From 07fcc4c97320d08defba483fe01f87006cc2c78f Mon Sep 17 00:00:00 2001 From: Chris Allegretta Date: Thu, 10 Jul 2008 20:13:04 +0000 Subject: [PATCH] Add beginning undo feature, since I want to start fixing bugs from savannah and don't want to manager another checking, and the code basically works for some operations. git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@4271 35c25a1d-7b9e-4130-9fde-d3aeb78583b8 --- ChangeLog | 5 + src/files.c | 2 + src/global.c | 36 +++--- src/nano.c | 6 +- src/nano.h | 43 ++++--- src/proto.h | 11 +- src/rcfile.c | 2 +- src/text.c | 332 ++++++++++++++++++++++++++++++++++++++++++++++++++- 8 files changed, 391 insertions(+), 46 deletions(-) diff --git a/ChangeLog b/ChangeLog index 416cb2d0..fc99dd23 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,12 @@ +2008-07-09 Chris Allegretta + * 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 * 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 * rcfile.c: Added function check_bad_binding() to look for sequences which diff --git a/src/files.c b/src/files.c index 1c988c3c..10b8a3e1 100644 --- a/src/files.c +++ b/src/files.c @@ -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; diff --git a/src/global.c b/src/global.c index 4f2a291c..7daa9aff 100644 --- a/src/global.c +++ b/src/global.c @@ -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) diff --git a/src/nano.c b/src/nano.c index 950cffcb..bb19f116 100644 --- a/src/nano.c +++ b/src/nano.c @@ -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 < diff --git a/src/nano.h b/src/nano.h index 648d9a0e..734172c4 100644 --- a/src/nano.h +++ b/src/nano.h @@ -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; diff --git a/src/proto.h b/src/proto.h index c349c36d..6af1d462 100644 --- a/src/proto.h +++ b/src/proto.h @@ -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); diff --git a/src/rcfile.c b/src/rcfile.c index fd51c8c1..8e383b28 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -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 diff --git a/src/text.c b/src/text.c index 4578bb41..bdf82a1b 100644 --- a/src/text.c +++ b/src/text.c @@ -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); } + -- 2.39.5