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