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