Home | History | Annotate | Line # | Download | only in vi
      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
     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
     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