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