1 |
|
|
/* $OpenBSD: v_undo.c,v 1.6 2014/11/12 04:28:41 bentley Exp $ */ |
2 |
|
|
|
3 |
|
|
/*- |
4 |
|
|
* Copyright (c) 1992, 1993, 1994 |
5 |
|
|
* The Regents of the University of California. All rights reserved. |
6 |
|
|
* Copyright (c) 1992, 1993, 1994, 1995, 1996 |
7 |
|
|
* Keith Bostic. All rights reserved. |
8 |
|
|
* |
9 |
|
|
* See the LICENSE file for redistribution information. |
10 |
|
|
*/ |
11 |
|
|
|
12 |
|
|
#include "config.h" |
13 |
|
|
|
14 |
|
|
#include <sys/types.h> |
15 |
|
|
#include <sys/queue.h> |
16 |
|
|
#include <sys/time.h> |
17 |
|
|
|
18 |
|
|
#include <bitstring.h> |
19 |
|
|
#include <errno.h> |
20 |
|
|
#include <limits.h> |
21 |
|
|
#include <stdio.h> |
22 |
|
|
#include <stdlib.h> |
23 |
|
|
#include <string.h> |
24 |
|
|
|
25 |
|
|
#include "../common/common.h" |
26 |
|
|
#include "vi.h" |
27 |
|
|
|
28 |
|
|
/* |
29 |
|
|
* v_Undo -- U |
30 |
|
|
* Undo changes to this line. |
31 |
|
|
* |
32 |
|
|
* PUBLIC: int v_Undo(SCR *, VICMD *); |
33 |
|
|
*/ |
34 |
|
|
int |
35 |
|
|
v_Undo(SCR *sp, VICMD *vp) |
36 |
|
|
{ |
37 |
|
|
/* |
38 |
|
|
* Historically, U reset the cursor to the first column in the line |
39 |
|
|
* (not the first non-blank). This seems a bit non-intuitive, but, |
40 |
|
|
* considering that we may have undone multiple changes, anything |
41 |
|
|
* else (including the cursor position stored in the logging records) |
42 |
|
|
* is going to appear random. |
43 |
|
|
*/ |
44 |
|
|
vp->m_final.cno = 0; |
45 |
|
|
|
46 |
|
|
/* |
47 |
|
|
* !!! |
48 |
|
|
* Set up the flags so that an immediately subsequent 'u' will roll |
49 |
|
|
* forward, instead of backward. In historic vi, a 'u' following a |
50 |
|
|
* 'U' redid all of the changes to the line. Given that the user has |
51 |
|
|
* explicitly discarded those changes by entering 'U', it seems likely |
52 |
|
|
* that the user wants something between the original and end forms of |
53 |
|
|
* the line, so starting to replay the changes seems the best way to |
54 |
|
|
* get to there. |
55 |
|
|
*/ |
56 |
|
|
F_SET(sp->ep, F_UNDO); |
57 |
|
|
sp->ep->lundo = BACKWARD; |
58 |
|
|
|
59 |
|
|
return (log_setline(sp)); |
60 |
|
|
} |
61 |
|
|
|
62 |
|
|
/* |
63 |
|
|
* v_undo -- u |
64 |
|
|
* Undo the last change. |
65 |
|
|
* |
66 |
|
|
* PUBLIC: int v_undo(SCR *, VICMD *); |
67 |
|
|
*/ |
68 |
|
|
int |
69 |
|
|
v_undo(SCR *sp, VICMD *vp) |
70 |
|
|
{ |
71 |
|
|
EXF *ep; |
72 |
|
|
|
73 |
|
|
/* Set the command count. */ |
74 |
|
|
VIP(sp)->u_ccnt = sp->ccnt; |
75 |
|
|
|
76 |
|
|
/* |
77 |
|
|
* !!! |
78 |
|
|
* In historic vi, 'u' toggled between "undo" and "redo", i.e. 'u' |
79 |
|
|
* undid the last undo. However, if there has been a change since |
80 |
|
|
* the last undo/redo, we always do an undo. To make this work when |
81 |
|
|
* the user can undo multiple operations, we leave the old semantic |
82 |
|
|
* unchanged, but make '.' after a 'u' do another undo/redo operation. |
83 |
|
|
* This has two problems. |
84 |
|
|
* |
85 |
|
|
* The first is that 'u' didn't set '.' in historic vi. So, if a |
86 |
|
|
* user made a change, realized it was in the wrong place, does a |
87 |
|
|
* 'u' to undo it, moves to the right place and then does '.', the |
88 |
|
|
* change was reapplied. To make this work, we only apply the '.' |
89 |
|
|
* to the undo command if it's the command immediately following an |
90 |
|
|
* undo command. See vi/vi.c:getcmd() for the details. |
91 |
|
|
* |
92 |
|
|
* The second is that the traditional way to view the numbered cut |
93 |
|
|
* buffers in vi was to enter the commands "1pu.u.u.u. which will |
94 |
|
|
* no longer work because the '.' immediately follows the 'u' command. |
95 |
|
|
* Since we provide a much better method of viewing buffers, and |
96 |
|
|
* nobody can think of a better way of adding in multiple undo, this |
97 |
|
|
* remains broken. |
98 |
|
|
* |
99 |
|
|
* !!! |
100 |
|
|
* There is change to historic practice for the final cursor position |
101 |
|
|
* in this implementation. In historic vi, if an undo was isolated to |
102 |
|
|
* a single line, the cursor moved to the start of the change, and |
103 |
|
|
* then, subsequent 'u' commands would not move it again. (It has been |
104 |
|
|
* pointed out that users used multiple undo commands to get the cursor |
105 |
|
|
* to the start of the changed text.) Nvi toggles between the cursor |
106 |
|
|
* position before and after the change was made. One final issue is |
107 |
|
|
* that historic vi only did this if the user had not moved off of the |
108 |
|
|
* line before entering the undo command; otherwise, vi would move the |
109 |
|
|
* cursor to the most attractive position on the changed line. |
110 |
|
|
* |
111 |
|
|
* It would be difficult to match historic practice in this area. You |
112 |
|
|
* not only have to know that the changes were isolated to one line, |
113 |
|
|
* but whether it was the first or second undo command as well. And, |
114 |
|
|
* to completely match historic practice, we'd have to track users line |
115 |
|
|
* changes, too. This isn't worth the effort. |
116 |
|
|
*/ |
117 |
|
|
ep = sp->ep; |
118 |
|
|
if (!F_ISSET(ep, F_UNDO)) { |
119 |
|
|
F_SET(ep, F_UNDO); |
120 |
|
|
ep->lundo = BACKWARD; |
121 |
|
|
} else if (!F_ISSET(vp, VC_ISDOT)) |
122 |
|
|
ep->lundo = ep->lundo == BACKWARD ? FORWARD : BACKWARD; |
123 |
|
|
|
124 |
|
|
switch (ep->lundo) { |
125 |
|
|
case BACKWARD: |
126 |
|
|
return (log_backward(sp, &vp->m_final)); |
127 |
|
|
case FORWARD: |
128 |
|
|
return (log_forward(sp, &vp->m_final)); |
129 |
|
|
default: |
130 |
|
|
abort(); |
131 |
|
|
} |
132 |
|
|
/* NOTREACHED */ |
133 |
|
|
} |