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