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
v_Undo(SCR * sp,VICMD * vp)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
v_undo(SCR * sp,VICMD * vp)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 }
134