]> git.wh0rd.org Git - nano.git/commitdiff
Add beginning undo feature, since I want to start fixing bugs from savannah and don...
authorChris Allegretta <chrisa@asty.org>
Thu, 10 Jul 2008 20:13:04 +0000 (20:13 +0000)
committerChris Allegretta <chrisa@asty.org>
Thu, 10 Jul 2008 20:13:04 +0000 (20:13 +0000)
git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@4271 35c25a1d-7b9e-4130-9fde-d3aeb78583b8

ChangeLog
src/files.c
src/global.c
src/nano.c
src/nano.h
src/proto.h
src/rcfile.c
src/text.c

index 416cb2d040e04752c436988df0d2f05bc2e00c59..fc99dd23b8ccb663e03b4d225d3552f63c35094a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,12 @@
+2008-07-09 Chris Allegretta <chrisa@asty.org>
+        * nano.c/nano.h/global.c/text.c: New generalized undo code, currently
+         just works for adding and deleting text and splitting and unsplitting lines.
+
 2008-06-29 Chris Allegretta <chrisa@asty.org>
        * global.c: Fix for not having search history when --disable-justify is used
          (Savannah bug 23733)
 
+
 GNU nano 2.1.2 - 2008.06.24
 2008-06-24 Chris Allegretta <chrisa@asty.org>
        * rcfile.c: Added function check_bad_binding() to look for sequences which
index 1c988c3c8e83c857b3160472fe662c5a2612bc1b..10b8a3e1fa626aa12c8be5ab884b23933feb95b0 100644 (file)
@@ -75,6 +75,8 @@ void initialize_buffer(void)
     openfile->fmt = NIX_FILE;
 
     openfile->current_stat = NULL;
+    openfile->undotop = NULL;
+    openfile->current_undo = NULL;
 #endif
 #ifdef ENABLE_COLOR
     openfile->colorstrings = NULL;
index 4f2a291c92b5a163b163ed6b86baf58708b2355b..7daa9aff5105ec80290bc791c7d13c5c6f8e2810 100644 (file)
@@ -1,5 +1,4 @@
-/* $Id$ 
-*/
+/* $Id$ */
 /**************************************************************************
  *   global.c                                                             *
  *                                                                        *
@@ -150,11 +149,6 @@ const shortcut *currshortcut;
 int currmenu;
        /* The currently loaded menu */
 
-#ifndef NANO_TINY
-toggle *toggles = NULL;
-       /* The global toggle list. */
-#endif
-
 sc *sclist = NULL;
        /* New shortcut key struct */
 subnfunc *allfuncs = NULL;
@@ -486,6 +480,8 @@ void shortcut_init(bool unjustify)
     const char *refresh_msg = N_("Refresh");
     const char *insert_file_msg =  N_("Insert File");
     const char *go_to_line_msg = N_("Go To Line");
+    const char *prev_undo_msg = N_("Prev Undo");
+    const char *next_undo_msg = N_("Next Undo");
 
 #ifndef DISABLE_HELP
     /* TRANSLATORS: The next long series of strings are shortcut descriptions;
@@ -528,6 +524,8 @@ void shortcut_init(bool unjustify)
        N_("Copy the current line and store it in the cutbuffer");
     const char *nano_indent_msg = N_("Indent the current line");
     const char *nano_unindent_msg = N_("Unindent the current line");
+    const char *nano_undo_msg = N_("Undo the last operation");
+    const char *nano_redo_msg = N_("Redo the last undone operation");
 #endif
     const char *nano_forward_msg = N_("Go forward one character");
     const char *nano_back_msg = N_("Go back one character");
@@ -602,6 +600,10 @@ void shortcut_init(bool unjustify)
        N_("Recall the previous search/replace string");
     const char *nano_next_history_msg =
        N_("Recall the next search/replace string");
+    const char *nano_prev_undo_msg =
+       N_("Recall the previous undo action");
+    const char *nano_next_undo_msg =
+       N_("Recall the next undo action");
 #endif
 #ifndef DISABLE_BROWSER
     const char *nano_tofiles_msg = N_("Go to file browser");
@@ -698,6 +700,7 @@ void shortcut_init(bool unjustify)
     add_to_funcs(do_page_down, MMAIN|MHELP,
        next_page_msg, IFSCHELP(nano_nextpage_msg), TRUE, VIEW);
 
+
     /* TRANSLATORS: Try to keep this at most 10 characters. */
     add_to_funcs(do_cut_text_void, MMAIN, N_("Cut Text"), IFSCHELP(nano_cut_msg),
        FALSE, NOVIEW);
@@ -771,7 +774,13 @@ void shortcut_init(bool unjustify)
        nano_indent_msg, FALSE, NOVIEW);
 
     add_to_funcs(do_unindent, MMAIN, N_("Unindent Text"),
-       nano_unindent_msg, TRUE, NOVIEW);
+       nano_unindent_msg, FALSE, NOVIEW);
+
+    add_to_funcs(do_undo, MMAIN, N_("Undo"),
+       nano_undo_msg, FALSE, NOVIEW);
+
+    add_to_funcs(do_redo, MMAIN, N_("Redo"),
+       nano_redo_msg, TRUE, NOVIEW);
 
 #endif
 
@@ -1055,6 +1064,8 @@ void shortcut_init(bool unjustify)
     add_to_sclist(MMAIN, "M-6", do_copy_text, 0, TRUE);
     add_to_sclist(MMAIN, "M-}", do_indent_void, 0, TRUE);
     add_to_sclist(MMAIN, "M-{", do_unindent, 0, TRUE);
+    add_to_sclist(MMAIN, "M-U", do_undo, 0, TRUE);
+    add_to_sclist(MMAIN, "M-E", do_redo, 0, TRUE);
     add_to_sclist(MALL, "^F", do_right, 0, TRUE);
     add_to_sclist(MALL, "^B", do_left, 0, TRUE);
     add_to_sclist(MMAIN, "^Space", do_next_word_void, 0, TRUE);
@@ -1576,15 +1587,6 @@ void thanks_for_all_the_fish(void)
 #ifndef DISABLE_JUSTIFY
     if (jusbuffer != NULL)
        free_filestruct(jusbuffer);
-#endif
-#ifndef NANO_TINY
-    /* Free the memory associated with each toggle. */
-    while (toggles != NULL) {
-       toggle *t = toggles;
-
-       toggles = toggles->next;
-       free(t);
-    }
 #endif
     /* Free the memory associated with each open file buffer. */
     if (openfile != NULL)
index 950cffcb6d4dd985f12804c454becf77f67a4a74..bb19f116133fc9af2d59f7b817765137eed71e79 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id$ */
+/* $Id$ */ 
 /**************************************************************************
  *   nano.c                                                               *
  *                                                                        *
@@ -502,6 +502,7 @@ openfilestruct *make_new_opennode(void)
     newnode->filebot = NULL;
     newnode->edittop = NULL;
     newnode->current = NULL;
+    newnode->last_action = OTHER;
 
     return newnode;
 }
@@ -1642,6 +1643,9 @@ void do_output(char *output, size_t output_len, bool allow_cntrls)
        set_modified();
 
 #ifndef NANO_TINY
+       update_undo(ADD, openfile);
+
+
        /* Note that current_x has not yet been incremented. */
        if (openfile->mark_set && openfile->current ==
                openfile->mark_begin && openfile->current_x <
index 648d9a0e3e1a1679e574e112d39c5c8b51807d07..734172c42ae826c9012cebce815566f3711e7dca 100644 (file)
@@ -169,6 +169,10 @@ typedef enum {
     CONTROL, META, FKEY, RAW
 }  function_type;
 
+typedef enum {
+    ADD, DEL, REPLACE, SPLIT, UNSPLIT, CUT, UNCUT, OTHER
+} undo_type;
+
 /* Structure types. */
 typedef struct filestruct {
     char *data;
@@ -245,6 +249,21 @@ typedef struct syntaxtype {
 } syntaxtype;
 #endif /* ENABLE_COLOR */
 
+#ifndef NANO_TINY
+typedef struct undo {
+    undo_type type;
+    filestruct *fs;
+    int begin;
+       /* Where did this  action begin or end */
+    char *strdata;
+       /* Generic pointer for data regardless of what type it is */
+    filestruct *fsdata;
+       /* Generic pointer for data regardless of what type it is */
+    struct undo *next;
+    ssize_t lineno;
+} undo;
+#endif /* NANO_TINY */
+
 typedef struct openfilestruct {
     char *filename;
        /* The current file's name. */
@@ -278,6 +297,11 @@ typedef struct openfilestruct {
        /* The current file's format. */
     struct stat *current_stat;
        /* The current file's stat. */
+    undo *undotop;
+       /* Top of the undo list */
+    undo *current_undo;
+       /* The current (i.e. n ext) level of undo */
+    undo_type last_action;
 #endif
 #ifdef ENABLE_COLOR
     colortype *colorstrings;
@@ -318,25 +342,6 @@ typedef struct shortcut {
        /* Next shortcut. */
 } shortcut;
 
-#ifndef NANO_TINY
-typedef struct toggle {
-   int val;
-       /* The sequence to toggle the key.  We should only need one. */
-   const char *desc;
-       /* The description of the toggle, e.g. "Cut to end"; we'll
-        * append Enabled or Disabled to it. */
-#ifndef DISABLE_HELP
-   bool blank_after;
-       /* Whether there should be a blank line after the description of
-        * the toggle. */
-#endif
-   long flag;
-       /* Which flag actually gets toggled. */
-   struct toggle *next;
-       /* Next toggle. */
-} toggle;
-#endif
-
 #ifdef ENABLE_NANORC
 typedef struct rcoption {
    const char *name;
index c349c36dda8843ac53db76b4b9ab90df765d39c8..6af1d46207c7c6c2331b0c9079baaff4b0095328 100644 (file)
@@ -60,6 +60,7 @@ extern char *matchbrackets;
 #if !defined(NANO_TINY) && defined(ENABLE_NANORC)
 extern char *whitespace;
 extern int whitespace_len[2];
+extern undo_type last_action;
 #endif
 
 #ifndef DISABLE_JUSTIFY
@@ -100,9 +101,6 @@ extern char *syntaxstr;
 
 extern const shortcut *currshortcut;
 extern int currmenu;
-#ifndef NANO_TINY
-extern toggle *toggles;
-#endif
 
 #ifndef NANO_TINY
 extern filestruct *search_history;
@@ -619,6 +617,8 @@ void do_tab(void);
 void do_indent(ssize_t cols);
 void do_indent_void(void);
 void do_unindent(void);
+void do_undo(void);
+void do_redo(void);
 #endif
 void do_enter(void);
 #ifndef NANO_TINY
@@ -706,6 +706,8 @@ void new_magicline(void);
 void remove_magicline(void);
 void mark_order(const filestruct **top, size_t *top_x, const filestruct
        **bot, size_t *bot_x, bool *right_side_up);
+void add_undo(undo_type current_action, openfilestruct *fs);
+void update_undo(undo_type action, openfilestruct *fs);
 #endif
 size_t get_totsize(const filestruct *begin, const filestruct *end);
 #ifdef DEBUG
@@ -739,9 +741,6 @@ int get_mouseinput(int *mouse_x, int *mouse_y, bool allow_shortcuts);
 const sc *get_shortcut(int menu, int *kbinput, bool
        *meta_key, bool *func_key);
 const sc *first_sc_for(int menu, void *func);
-#ifndef NANO_TINY
-const toggle *get_toggle(int kbinput, bool meta_key);
-#endif
 void blank_line(WINDOW *win, int y, int x, int n);
 void blank_titlebar(void);
 void blank_topbar(void);
index fd51c8c156c67c654fdb0c866b1bb1109600649a..8e383b28fd0911fabcaabfdb9168f6b282bc1815 100644 (file)
@@ -423,7 +423,7 @@ void parse_keybinding(char *ptr)
 
 #ifdef DEBUG
     fprintf(stderr, "newsc now address %d, menu func assigned = %d, menu = %d\n",
-       (int) newsc, (int) newsc->scfunc, menu);
+       &newsc, &newsc->scfunc, menu);
 #endif
 
 
index 4578bb4165ad1c70d20f6ad67afde3eacaf1224d..bdf82a1bedc0196b9df888d5c89bd5328313c68a 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id$ */
+/* $Id$ */ 
 /**************************************************************************
  *   text.c                                                               *
  *                                                                        *
@@ -70,6 +70,10 @@ void do_delete(void)
        /* Do we have to call edit_refresh(), or can we get away with
         * just update_line()? */
 
+#ifndef NANO_TINY
+    update_undo(DEL, openfile);
+#endif
+
     assert(openfile->current != NULL && openfile->current->data != NULL && openfile->current_x <= strlen(openfile->current->data));
 
     openfile->placewewant = xplustabs();
@@ -359,6 +363,179 @@ void do_unindent(void)
 {
     do_indent(-tabsize);
 }
+
+/* Undo the last thing(s) we did */
+void do_undo(void)
+{
+    undo *u = openfile->current_undo;
+    filestruct *f = openfile->current, *t;
+    int len = 0;
+    char *action, *data, *uu;
+
+    if (!u) {
+       statusbar(_("Nothing in undo buffer!"));
+       return;
+    }
+
+
+    if (u->lineno <= f->lineno)
+        for (; f->prev != NULL && f->lineno != u->lineno; f = f->prev)
+           ;
+    else
+        for (; f->next != NULL && f->lineno != u->lineno; f = f->next)
+           ;
+    if (f->lineno != u->lineno) {
+        statusbar(_("Couldnt match current undo line"));
+       return;
+    }
+#ifdef DEBUG
+    fprintf(stderr, "data we're about to undo = \"%s\"\n", f->data);
+    fprintf(stderr, "Undo running for type %d\n", u->type);
+#endif
+
+    switch(u->type) {
+    case ADD:
+       action = _("text add");
+       len = strlen(f->data) - strlen(u->strdata) + 1;
+        data = charalloc(len);
+        strncpy(data, f->data, u->begin);
+       strcpy(&data[u->begin], &f->data[u->begin + strlen(u->strdata)]);
+       free(f->data);
+       f->data = data;
+       break;
+    case DEL:
+       action = _("text delete");
+       len = strlen(f->data) + strlen(u->strdata) + 1;
+       data = charalloc(len);
+
+       strncpy(data, f->data, u->begin);
+       strcpy(&data[u->begin], u->strdata);
+       strcpy(&data[u->begin + strlen(u->strdata)], &f->data[u->begin]);
+       free(f->data);
+       f->data = data;
+       break;
+    case SPLIT:
+       action = _("line split");
+       free(f->data);
+       f->data = mallocstrcpy(NULL, u->strdata);
+       if (f->next != NULL) {
+           filestruct *tmp = f->next;
+           unlink_node(tmp);
+           delete_node(tmp);
+       }
+       renumber(openfile->current->prev);
+       break;
+    case UNSPLIT:
+       action = _("line join");
+       t = make_new_node(f);
+       t->data = mallocstrcpy(NULL, u->strdata);
+       data = mallocstrncpy(NULL, f->data, u->begin);
+       data[u->begin] = '\0';
+       free(f->data);
+       f->data = data;
+       splice_node(f, t, f->next);
+       renumber(openfile->current->prev);
+       break;
+    default:
+       action = _("wtf?");
+       break;
+
+    }
+    openfile->current = f;
+    openfile->current_x = u->begin;
+    edit_refresh();
+    statusbar(_("Undid action (%s)"), action);
+    openfile->current_undo = openfile->current_undo->next;
+
+}
+
+void do_redo(void)
+{
+    undo *u = openfile->undotop;
+    filestruct *f = openfile->current, *t;
+    int len = 0;
+    char *action, *data, *uu;
+
+    for (; u != NULL && u->next != openfile->current_undo; u = u->next)
+       ;
+    if (!u) {
+       statusbar(_("Nothing to re-do!"));
+       return;
+    }
+    if (u->next != openfile->current_undo) {
+       statusbar(_("Can't find previous undo to re-do, argh"));
+       return;
+    }
+
+    if (u->lineno <= f->lineno)
+        for (; f->prev != NULL && f->lineno != u->lineno; f = f->prev)
+           ;
+    else
+        for (; f->next != NULL && f->lineno != u->lineno; f = f->next)
+           ;
+    if (f->lineno != u->lineno) {
+        statusbar(_("Couldnt match current undo line"));
+       return;
+    }
+#ifdef DEBUG
+    fprintf(stderr, "data we're about to redo = \"%s\"\n", f->data);
+    fprintf(stderr, "Redo running for type %d\n", u->type);
+#endif
+
+    switch(u->type) {
+    case ADD:
+       action = _("text add");
+       len = strlen(f->data) + strlen(u->strdata) + 1;
+        data = charalloc(len);
+       strcpy(&data[u->begin], u->strdata);
+       strcpy(&data[u->begin + strlen(u->strdata)], &f->data[u->begin]);
+       free(f->data);
+       f->data = data;
+       break;
+    case DEL:
+       action = _("text delete");
+       len = strlen(f->data) + strlen(u->strdata) + 1;
+       data = charalloc(len);
+        strncpy(data, f->data, u->begin);
+       strcpy(&data[u->begin], &f->data[u->begin + strlen(u->strdata)]);
+       free(f->data);
+       f->data = data;
+       break;
+    case SPLIT:
+       action = _("line split");
+       t = make_new_node(f);
+       t->data = mallocstrcpy(NULL, u->strdata);
+       data = mallocstrncpy(NULL, f->data, u->begin);
+       data[u->begin] = '\0';
+       free(f->data);
+       f->data = data;
+       splice_node(f, t, f->next);
+       renumber(openfile->current->prev);
+       break;
+    case UNSPLIT:
+       action = _("line join");
+       free(f->data);
+       f->data = mallocstrcpy(NULL, u->strdata);
+       if (f->next != NULL) {
+           filestruct *tmp = f->next;
+           unlink_node(tmp);
+           delete_node(tmp);
+       }
+       renumber(openfile->current->prev);
+       break;
+    default:
+       action = _("wtf?");
+       break;
+
+    }
+    openfile->current = f;
+    openfile->current_x = u->begin;
+    edit_refresh();
+    statusbar(_("Redid action (%s)"), action);
+
+    openfile->current_undo = u;
+
+}
 #endif /* !NANO_TINY */
 
 /* Someone hits Enter *gasp!* */
@@ -367,9 +544,12 @@ void do_enter(void)
     filestruct *newnode = make_new_node(openfile->current);
     size_t extra = 0;
 
-    assert(openfile->current != NULL && openfile->current->data != NULL);
+    assert(openfile->current != NULL && xopenfile->current->data != NULL);
 
 #ifndef NANO_TINY
+    update_undo(SPLIT, openfile);
+
+
     /* Do auto-indenting, like the neolithic Turbo Pascal editor. */
     if (ISSET(AUTOINDENT)) {
        /* If we are breaking the line in the indentation, the new
@@ -510,6 +690,153 @@ bool execute_command(const char *command)
 
     return TRUE;
 }
+
+void add_undo(undo_type current_action, openfilestruct *fs)
+{
+    int i;
+    undo *u = nmalloc(sizeof(undo));
+    char *data;
+
+    /* Blow away the old undo stack if we are starting from the middle */
+    while (fs->undotop != fs->current_undo) {
+       undo *tmp = fs->undotop;
+       fs->undotop = fs->undotop->next;
+       free(tmp->strdata);
+       free(tmp);
+    }
+
+    u->type = current_action;
+    u->lineno = fs->current->lineno;
+    u->begin = fs->current_x;
+    u->fs = fs->current;
+    u->next = fs->undotop;
+    fs->undotop = u;
+    fs->current_undo = u;
+
+    switch (u->type) {
+    /* We need to start copying data into the undo buffer or we wont be able
+       to restore it later */
+    case ADD:
+        data = charalloc(2);
+        data[0] = fs->current->data[fs->current_x];
+        data[1] = '\0';
+        u->strdata = data;
+       break;
+    case DEL:
+       if (u->begin != strlen(fs->current->data)) {
+            data = mallocstrncpy(NULL, &fs->current->data[u->begin], 2);
+            data[1] = '\0';
+            u->strdata = data;
+           break;
+       }
+       /* Else purposely fall into unsplit code */
+       current_action = u->type = UNSPLIT;
+    case UNSPLIT:
+       if (fs->current->next) {
+           data = mallocstrcpy(NULL, fs->current->next->data);
+           u->strdata = data;
+       }
+       u->begin = fs->current_x;
+       break;
+    case SPLIT:
+       data = mallocstrcpy(NULL, fs->current->data);
+       u->strdata = data;
+        u->begin = fs->current_x;
+       break;
+    }
+
+#ifdef DEBUG
+    fprintf(stderr, "fs->current->data = \"%s\", current_x = %d, u->begin = %d, type = %d\n",
+                       fs->current->data,  fs->current_x, u->begin, current_action);
+    fprintf(stderr, "u->strdata = \"%s\"\n", u->strdata);
+    fprintf(stderr, "left update_add...\n");
+#endif
+    fs->last_action = current_action;
+}
+
+void update_undo(undo_type action, openfilestruct *fs)
+{
+    undo *u;
+    char *data;
+    int len = 0;
+
+    if (action != fs->last_action) {
+        add_undo(action, fs);
+       return;
+    }
+
+    assert(fs->undotop != NULL);
+    u = fs->undotop;
+
+    if (u->fs->data != openfile->current->data) {
+        add_undo(action, fs);
+       return;
+    }
+
+
+
+    switch (u->type) {
+    case ADD:
+#ifdef DEBUG
+        fprintf(stderr, "fs->current->data = \"%s\", current_x = %d, u->begin = %d\n",
+                       fs->current->data,  fs->current_x, u->begin);
+#endif
+        len = strlen(u->strdata) + 2;
+        data = nrealloc((void *) u->strdata, len * sizeof(char *));
+        data[len-2] = fs->current->data[fs->current_x];
+        data[len-1] = '\0';
+        u->strdata = (char *) data;
+#ifdef DEBUG
+       fprintf(stderr, "current undo data now \"%s\"\n", u->strdata);
+#endif
+       break;
+    case DEL:
+       len = strlen(u->strdata) + 2;
+       assert(len > 2);
+        if (fs->current_x == u->begin) {
+           /* They're deleting */
+           data = charalloc(len);
+           strcpy(data, u->strdata);
+           data[len-2] = fs->current->data[fs->current_x];;
+           data[len-1] = '\0';
+           free(u->strdata);
+           u->strdata = data;
+       } else if (fs->current_x == u->begin - 1) {
+           /* They're backspacing */
+           data = charalloc(len);
+           data[0] = fs->current->data[fs->current_x];
+           strcpy(&data[1], u->strdata);
+           free(u->strdata);
+           u->strdata = data;
+           u->begin--;
+       } else {
+           /* They deleted something else on the line */
+           add_undo(DEL, fs);
+           return;
+       }
+#ifdef DEBUG
+       fprintf(stderr, "current undo data now \"%s\"\n", u->strdata);
+#endif
+       break;
+    case SPLIT:
+    case UNSPLIT:
+       /* We don't really ever update an enter key press, treat it as a new */
+//     add_undo(action, fs);
+       break;
+    }
+
+#ifdef DEBUG
+    fprintf(stderr, "Done in udpate_undo (type was %d)\n", action);
+#endif
+    if (fs->last_action != action) {
+#ifdef DEBUG
+       fprintf(stderr, "Starting add_undo for new action as it does not match last_action\n");
+#endif
+       add_undo(action, openfile);
+    }
+    fs->last_action = action;
+}
+
 #endif /* !NANO_TINY */
 
 #ifndef DISABLE_WRAPPING
@@ -2464,3 +2791,4 @@ void do_verbatim_input(void)
 
     free(output);
 }
+