On 3/18/2014 6:15 PM, Jody Bruchon wrote:
This code is in a "works for me" state but needs testing and refinement.

D'oh, I didn't test building without the undo feature enabled and missed an #if/#endif. Apologies. Revised patch attached.

Also, there is an "unused parameter" warning if undo is disabled; I'm not sure if there is an elegant way to get rid of this since it'll require every invocation of text_hole_delete to have an #if/#else/#endif around it. Suggestions welcome.

Signed-off-by: Jody Bruchon <j...@jodybruchon.com>


diff -Naurw a/editors/vi.c b/editors/vi.c
--- a/editors/vi.c      2014-01-09 13:15:44.000000000 -0500
+++ b/editors/vi.c      2014-03-18 18:23:37.863250314 -0400
@@ -17,7 +17,6 @@
  *      it would be easier to change the mark when add/delete lines
  *     More intelligence in refresh()
  *     ":r !cmd"  and  "!cmd"  to filter text through an external command
- *     A true "undo" facility
  *     An "ex" line oriented mode- maybe using "cmdedit"
  */
 
@@ -136,6 +135,13 @@
 //config:        cursor position using "ESC [ 6 n" escape sequence, then read 
stdin.
 //config:
 //config:        This is not clean but helps a lot on serial lines and such.
+//config:config FEATURE_VI_UNDO
+//config:      bool "Support undo command 'u'"
+//config:      default y
+//config:      depends on VI
+//config:      help
+//config:        Support the 'u' command to undo insertion, deletion, and 
replacement
+//config:        of text.
 
 //applet:IF_VI(APPLET(vi, BB_DIR_BIN, BB_SUID_DROP))
 
@@ -347,6 +353,16 @@
        char get_input_line__buf[MAX_INPUT_LEN]; /* former static */
 
        char scr_out_buf[MAX_SCR_COLS + MAX_TABSTOP * 2];
+#if ENABLE_FEATURE_VI_UNDO
+       struct undo_object {
+               struct undo_object *prev;       // Linking back avoids list 
traversal (LIFO)
+               int type;               // 0=deleted, 1=inserted, 2=swapped
+               int start;              // Offset where the data should be 
restored/deleted
+               int length;             // total data size
+               char *undo_text;        // ptr to text that will be inserted
+       } *undo_stack_tail;
+#endif
+
 };
 #define G (*ptr_to_globals)
 #define text           (G.text          )
@@ -408,6 +424,10 @@
 #define last_modifying_cmd  (G.last_modifying_cmd )
 #define get_input_line__buf (G.get_input_line__buf)
 
+#if ENABLE_FEATURE_VI_UNDO
+#define undo_stack_tail (G.undo_stack_tail)
+#endif
+
 #define INIT_G() do { \
        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
        last_file_modified = -1; \
@@ -448,7 +468,7 @@
 static int st_test(char *, int, int, char *);  // helper for skip_thing()
 static char *skip_thing(char *, int, int, int);        // skip some object
 static char *find_pair(char *, char);  // find matching pair ()  []  {}
-static char *text_hole_delete(char *, char *); // at "p", delete a 'size' byte 
hole
+static char *text_hole_delete(char *, char *, int);    // at "p", delete a 
'size' byte hole
 // might reallocate text[]! use p += text_hole_make(p, ...),
 // and be careful to not use pointers into potentially freed text[]!
 static uintptr_t text_hole_make(char *, int);  // at "p", make a 'size' byte 
hole
@@ -526,7 +546,10 @@
 static void crash_test();
 static int crashme = 0;
 #endif
-
+#if ENABLE_FEATURE_VI_UNDO
+static char undo_push(char *, int, int);       // Push an operation on the 
undo stack
+static char undo_pop(void);    // Undo the last operation
+#endif
 
 static void write1(const char *out)
 {
@@ -540,6 +563,9 @@
 
        INIT_G();
 
+#if ENABLE_FEATURE_VI_UNDO
+       undo_stack_tail = NULL;
+#endif
 #if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
        my_pid = getpid();
 #endif
@@ -1269,7 +1295,7 @@
                        if (found) {
                                uintptr_t bias;
                                // we found the "find" pattern - delete it
-                               text_hole_delete(found, found + len_F - 1);
+                               text_hole_delete(found, found + len_F - 1, 1);
                                // inset the "replace" patern
                                bias = string_insert(found, R); // insert the 
string
                                found += bias;
@@ -1655,7 +1681,7 @@
 
 static void dot_delete(void)   // delete the char at 'dot'
 {
-       text_hole_delete(dot, dot);
+       text_hole_delete(dot, dot, 0);
 }
 
 static char *bound_dot(char *p) // make sure  text[0] <= P < "end"
@@ -1811,6 +1837,9 @@
                refresh(FALSE); // show the ^
                c = get_one_char();
                *p = c;
+#if ENABLE_FEATURE_VI_UNDO
+               undo_push(p, 1, 1);
+#endif
                p++;
                file_modified++;
        } else if (c == 27) {   // Is this an ESC?
@@ -1825,7 +1854,7 @@
                //     123456789
                if ((p[-1] != '\n') && (dot>text)) {
                        p--;
-                       p = text_hole_delete(p, p);     // shrink buffer 1 char
+                       p = text_hole_delete(p, p, 0);  // shrink buffer 1 char
                }
        } else {
 #if ENABLE_FEATURE_VI_SETOPTS
@@ -1838,6 +1867,9 @@
 #if ENABLE_FEATURE_VI_SETOPTS
                sp = p;                 // remember addr of insert
 #endif
+#if ENABLE_FEATURE_VI_UNDO
+               undo_push(p, 1, 1);
+#endif
                p += 1 + stupid_insert(p, c);   // insert the char
 #if ENABLE_FEATURE_VI_SETOPTS
                if (showmatch && strchr(")]}", *sp) != NULL) {
@@ -1853,6 +1885,9 @@
                                bias = text_hole_make(p, len);
                                p += bias;
                                q += bias;
+#if ENABLE_FEATURE_VI_UNDO
+                               undo_push(p, len, 1);
+#endif
                                memcpy(p, q, len);
                                p += len;
                        }
@@ -2051,6 +2086,76 @@
 }
 #endif /* FEATURE_VI_SETOPTS */
 
+#if ENABLE_FEATURE_VI_UNDO
+// Undo functions added by Jody Bruchon (j...@jodybruchon.com)
+static char undo_push(char *src, int length, int type) // Add to the undo stack
+{
+       struct undo_object *undo_temp;
+       // "type" values
+       // 0: deleted text, undo will restore to buffer
+       // 1: insertion, undo will remove from buffer
+       // 2: swap undo will perform the remove and insert operations in one 
shot
+       // Swap undo pushing is a two-operation process (push with 0, then with 
2.)
+
+       if ((type > 2) || (type < 0)) return 1; // only types 0,1,2 are valid
+       // Allocate a new undo object and use it as the stack tail
+       if (type == 2) {
+               undo_stack_tail->type = 2;      // Previous insert operation is 
part of a replace
+       } else {
+               undo_temp = undo_stack_tail;
+               undo_stack_tail = (struct undo_object *) malloc(sizeof(struct 
undo_object));
+               undo_stack_tail->prev = undo_temp;
+               undo_stack_tail->start = src - text;    // use offset from 
start of text buffer
+               undo_stack_tail->length = length;
+               undo_stack_tail->type = type;
+               if (type == 0) {
+                               undo_stack_tail->undo_text = (char *) 
malloc(length);
+                               memcpy(undo_stack_tail->undo_text, src, length);
+               }
+       }
+       return 0;
+}
+
+static char undo_pop(void)     // Undo the last operation
+{
+       int repeat = 0;
+       char *u_start, *u_end;
+       struct undo_object *undo_temp;
+
+       // Check for an empty undo stack first
+       if (undo_stack_tail != NULL) {
+               switch (undo_stack_tail->type) {
+                       case 0:
+                               // make hole and put in text that was deleted; 
deallocate text
+                               u_start = text + undo_stack_tail->start;
+                               text_hole_make(u_start, 
undo_stack_tail->length);
+                               memcpy(u_start, undo_stack_tail->undo_text, 
undo_stack_tail->length);
+                               free(undo_stack_tail->undo_text);
+                               break;
+                       case 1:
+                       case 2:
+                               // delete what was inserted
+                               u_start = undo_stack_tail->start + text;
+                               u_end = u_start - 1 + undo_stack_tail->length;
+                               text_hole_delete(u_start, u_end, 1);
+                               break;
+               }
+               if (undo_stack_tail->type == 2) repeat = 1;     // handle swap 
undo
+               // Lower modification count and deallocate undo object
+               file_modified--;
+               undo_temp = undo_stack_tail->prev;
+               free(undo_stack_tail);
+               undo_stack_tail = undo_temp;
+               if (repeat == 1) undo_pop();    // swap undo requires two runs
+       } else {
+               status_line("Already at oldest change");
+               return 1;
+       }
+       refresh(FALSE);
+       return 0;
+}
+#endif
+    
 // open a hole in text[]
 // might reallocate text[]! use p += text_hole_make(p, ...),
 // and be careful to not use pointers into potentially freed text[]!
@@ -2087,7 +2192,8 @@
 }
 
 //  close a hole in text[]
-static char *text_hole_delete(char *p, char *q) // delete "p" through "q", 
inclusive
+//  "undo" value indicates if this operation should be undo-able (0 = yes, 1 = 
no)
+static char *text_hole_delete(char *p, char *q, int undo) // delete "p" 
through "q", inclusive
 {
        char *src, *dest;
        int cnt, hole_size;
@@ -2102,6 +2208,9 @@
        }
        hole_size = q - p + 1;
        cnt = end - src;
+#if ENABLE_FEATURE_VI_UNDO
+       if (undo == 0) undo_push(p, hole_size, 0);      // UNDO support
+#endif
        if (src < text || src > end)
                goto thd0;
        if (dest < text || dest >= end)
@@ -2152,7 +2261,7 @@
        text_yank(start, stop, YDreg);
 #endif
        if (yf == YANKDEL) {
-               p = text_hole_delete(start, stop);
+               p = text_hole_delete(start, stop, 0);
        }                                       // delete lines
        return p;
 }
@@ -2224,6 +2333,9 @@
        int i;
 
        i = strlen(s);
+#if ENABLE_FEATURE_VI_UNDO
+       undo_push(p, i, 1);
+#endif
        bias = text_hole_make(p, i);
        p += bias;
        memcpy(p, s, i);
@@ -2516,10 +2628,10 @@
        cnt = safe_read(fd, p, size);
        if (cnt < 0) {
                status_line_bold_errno(fn);
-               p = text_hole_delete(p, p + size - 1);  // un-do buffer insert
+               p = text_hole_delete(p, p + size - 1, 1);       // un-do buffer 
insert
        } else if (cnt < size) {
                // There was a partial read, shrink unused space text[]
-               p = text_hole_delete(p + cnt, p + size - 1);    // un-do buffer 
insert
+               p = text_hole_delete(p + cnt, p + size - 1, 1); // un-do buffer 
insert
                status_line_bold("can't read '%s'", fn);
        }
        if (cnt >= size)
@@ -3053,6 +3165,9 @@
                                if (c != 27)
                                        dot = yank_delete(dot, dot, 0, 
YANKDEL);        // delete char
                                dot = char_insert(dot, c);      // insert new 
char
+#if ENABLE_FEATURE_VI_UNDO
+                                       undo_push(dot, 1, 2);
+#endif
                        }
                        goto dc1;
                }
@@ -3108,7 +3223,6 @@
                //case ']':     // ]-
                //case '_':     // _-
                //case '`':     // `-
-               //case 'u':     // u- FIXME- there is no undo
                //case 'v':     // v-
        default:                        // unrecognized command
                buf[0] = c;
@@ -3254,11 +3368,16 @@
                string_insert(dot, p);  // insert the string
                end_cmd_q();    // stop adding to q
                break;
+#if ENABLE_FEATURE_VI_UNDO
+       case 'u':       // u- undo last operation
+               undo_pop();
+               break;
+#endif
        case 'U':                       // U- Undo; replace current line with 
original version
                if (reg[Ureg] != NULL) {
                        p = begin_line(dot);
                        q = end_line(dot);
-                       p = text_hole_delete(p, q);     // delete cur line
+                       p = text_hole_delete(p, q, 1);  // delete cur line
                        p += string_insert(p, reg[Ureg]);       // insert orig 
line
                        dot = p;
                        dot_skip_over_ws();
@@ -3494,11 +3613,11 @@
                                // shift left- remove tab or 8 spaces
                                if (*p == '\t') {
                                        // shrink buffer 1 char
-                                       text_hole_delete(p, p);
+                                       text_hole_delete(p, p, 1);
                                } else if (*p == ' ') {
                                        // we should be calculating columns, 
not just SPACE
                                        for (j = 0; *p == ' ' && j < tabstop; 
j++) {
-                                               text_hole_delete(p, p);
+                                               text_hole_delete(p, p, 1);
                                        }
                                }
                        } else if (c == '>') {
_______________________________________________
busybox mailing list
busybox@busybox.net
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to