[14301] | 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 | } |
---|