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