From f5ac8c1ee1422c4a61b4173ec40b9c784218b542 Mon Sep 17 00:00:00 2001 From: Benno Schulenberg Date: Sun, 25 May 2014 19:41:49 +0000 Subject: [PATCH] Placing the cursor after an undo there where it was before the do, and handling multibyte characters correctly. *Patch by Mark Majeres.* git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@4908 35c25a1d-7b9e-4130-9fde-d3aeb78583b8 --- ChangeLog | 8 ++++ src/chars.c | 11 ++++++ src/cut.c | 4 ++ src/nano.h | 1 + src/proto.h | 1 + src/text.c | 112 +++++++++++++++++++++------------------------------- 6 files changed, 70 insertions(+), 67 deletions(-) diff --git a/ChangeLog b/ChangeLog index a1255631..4a549bcd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2014-05-25 Mark Majeres + * src/chars.c (addstrings): New function, concatenates two allocated + strings, tacking the second onto the first and freeing the second. + * src/cut.c (do_uncut_text): Update the undo structure for a paste. + * src/text.c (undo_cut, redo_cut, add_undo, update_undo): Place the + cursor after an undo there where it was before the do, and handle + multibyte characters correctly. + 2014-05-23 Benno Schulenberg * src/winio.c (edit_draw): Finally, the proper fix for bug #31743; telling ncurses to really redraw the line, without optimization, so diff --git a/src/chars.c b/src/chars.c index b09a6d33..65c340eb 100644 --- a/src/chars.c +++ b/src/chars.c @@ -43,6 +43,17 @@ static const wchar_t bad_wchar = 0xFFFD; static const char *const bad_mbchar = "\xEF\xBF\xBD"; static const int bad_mbchar_len = 3; +/* Concatenate two allocated strings. */ +char* addstrings(char* str1, size_t len1, char* str2, size_t len2) +{ + str1 = charealloc(str1, len1 + len2 + 1); + str1[len1] = '\0'; + strncat(&str1[len1], str2, len2); + free(str2); + + return str1; +} + /* Enable UTF-8 support. */ void utf8_init(void) { diff --git a/src/cut.c b/src/cut.c index 6f98e743..e766d92a 100644 --- a/src/cut.c +++ b/src/cut.c @@ -274,6 +274,10 @@ void do_uncut_text(void) * at the current cursor position. */ copy_from_filestruct(cutbuffer); +#ifndef NANO_TINY + update_undo(PASTE); +#endif + /* Set the current place we want to where the text from the * cutbuffer ends. */ openfile->placewewant = xplustabs(); diff --git a/src/nano.h b/src/nano.h index c97faad6..f338b95a 100644 --- a/src/nano.h +++ b/src/nano.h @@ -572,6 +572,7 @@ enum #define UNdel_del (1<<0) #define UNdel_backspace (1<<1) #define UNsplit_madenew (1<<2) +#define UNcut_cutline (1<<3) #endif /* !NANO_TINY */ #define VIEW TRUE diff --git a/src/proto.h b/src/proto.h index c1f17b9b..de10f57f 100644 --- a/src/proto.h +++ b/src/proto.h @@ -162,6 +162,7 @@ char *striponedir(const char *path); void utf8_init(void); bool using_utf8(void); #endif +char *addstrings(char* str1, size_t len1, char* str2, size_t len2); #ifndef HAVE_ISBLANK bool nisblank(int c); #endif diff --git a/src/text.c b/src/text.c index 662b3fd9..b49c29f0 100644 --- a/src/text.c +++ b/src/text.c @@ -375,11 +375,17 @@ void undo_cut(undo *u) ; /* Get to where we need to uncut from. */ - goto_line_posx(u->mark_begin_lineno, u->mark_begin_x); + if (u->xflags == UNcut_cutline) + goto_line_posx(u->mark_begin_lineno, 0); + else + goto_line_posx(u->mark_begin_lineno, u->mark_begin_x); copy_from_filestruct(cutbuffer); free_filestruct(cutbuffer); cutbuffer = NULL; + + if (u->xflags == UNcut_cutline) + goto_line_posx(u->mark_begin_lineno, u->mark_begin_x); } /* Redo a cut, or undo an uncut. */ @@ -406,7 +412,7 @@ void redo_cut(undo *u) if (!ISSET(CUT_TO_END)) openfile->mark_set = TRUE; - openfile->mark_begin_x = u->mark_begin_x; + openfile->mark_begin_x = (u->xflags == UNcut_cutline) ? 0 : u->mark_begin_x; do_cut_text(FALSE, u->to_end, TRUE); openfile->mark_set = FALSE; openfile->mark_begin = NULL; @@ -460,9 +466,7 @@ void do_undo(void) strcpy(&data[u->begin + strlen(u->strdata)], &f->data[u->begin]); free(f->data); f->data = data; - if (u->xflags == UNdel_backspace) - openfile->current_x += strlen(u->strdata); - goto_line_posx(u->mark_begin_lineno, u->mark_begin_x + 1); + goto_line_posx(u->mark_begin_lineno, u->mark_begin_x); break; #ifndef DISABLE_WRAPPING case SPLIT: @@ -489,7 +493,7 @@ void do_undo(void) free(f->data); f->data = data; splice_node(f, t, f->next); - gotolinecolumn = TRUE; + goto_line_posx(u->lineno, u->begin); break; case CUT_EOF: case CUT: @@ -504,8 +508,8 @@ void do_undo(void) undidmsg = _("line break"); if (f->next) { filestruct *foo = f->next; - f->data = (char *) nrealloc(f->data, strlen(f->data) + strlen(f->next->data) + 1); - strcat(f->data, f->next->data); + f->data = (char *) nrealloc(f->data, strlen(f->data) + strlen(&f->next->data[u->mark_begin_x]) + 1); + strcat(f->data, &f->next->data[u->mark_begin_x]); unlink_node(foo); delete_node(foo); } @@ -629,7 +633,7 @@ void do_redo(void) delete_node(tmp); } renumber(f); - gotolinecolumn = TRUE; + goto_line_posx(u->lineno, u->begin); break; case CUT_EOF: case CUT: @@ -723,6 +727,11 @@ void do_enter(bool undoing) openfile->placewewant = xplustabs(); +#ifndef NANO_TINY + if (!undoing) + update_undo(ENTER); +#endif + edit_refresh_needed = TRUE; } @@ -879,15 +888,14 @@ void add_undo(undo_type current_action) /* We need to start copying data into the undo buffer * or we won't be able to restore it later. */ case ADD: - data = charalloc(2); - data[0] = '\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; + char *char_buf = charalloc(mb_cur_max() + 1); + int char_buf_len = parse_mbchar(&fs->current->data[u->begin], char_buf, NULL); + char_buf[char_buf_len] = '\0'; + u->strdata = char_buf; /* Note: there is likely more memory allocated than necessary. */ + u->mark_begin_x += char_buf_len; break; } /* Else purposely fall into unsplit code. */ @@ -929,7 +937,7 @@ void add_undo(undo_type current_action) else if (!ISSET(CUT_TO_END) && !u->to_end) { /* The entire line is being cut regardless of the cursor position. */ u->begin = 0; - u->mark_begin_x = 0; + u->xflags = UNcut_cutline; } break; case PASTE: @@ -942,18 +950,6 @@ void add_undo(undo_type current_action) u->mark_begin_lineno = fs->current->lineno; u->mark_begin_x = fs->current_x; u->lineno = fs->current->lineno + cutbottom->lineno - cutbuffer->lineno; - - filestruct *fs_buff = cutbuffer; - if (fs_buff->lineno == cutbottom->lineno) - u->begin = fs->current_x + get_totsize(fs_buff, cutbottom); - else { - /* Advance fs_buff to the last line in the cutbuffer. */ - while (fs_buff->lineno != cutbottom->lineno && fs_buff->next != NULL) - fs_buff = fs_buff->next; - assert(fs_buff->next != NULL); - u->begin = get_totsize(fs_buff, cutbottom); - } - u->mark_set = TRUE; } break; @@ -979,8 +975,6 @@ void add_undo(undo_type current_action) void update_undo(undo_type action) { undo *u; - char *data; - int len = 0; openfilestruct *fs = openfile; if (!ISSET(UNDOABLE)) @@ -998,7 +992,7 @@ void update_undo(undo_type action) /* Change to an add if we're not using the same undo struct * that we should be using. */ if (action != fs->last_action - || (action != CUT && action != INSERT && action != SPLIT + || (action != ENTER && action != CUT && action != INSERT && action != SPLIT && openfile->current->lineno != fs->current_undo->lineno)) { add_undo(action); return; @@ -1008,55 +1002,35 @@ void update_undo(undo_type action) u = fs->undotop; switch (u->type) { - case ADD: + case ADD: { #ifdef DEBUG fprintf(stderr, "fs->current->data = \"%s\", current_x = %lu, u->begin = %d\n", fs->current->data, (unsigned long) fs->current_x, u->begin); #endif - len = strlen(u->strdata) + 2; - data = (char *) nrealloc((void *) u->strdata, len * sizeof(char *)); - data[len-2] = fs->current->data[fs->current_x - 1]; - data[len-1] = '\0'; - u->strdata = (char *) data; + char *char_buf = charalloc(mb_cur_max()); + size_t char_buf_len = parse_mbchar(&fs->current->data[u->mark_begin_x], char_buf, NULL); + u->strdata = addstrings(u->strdata, u->strdata?strlen(u->strdata):0, char_buf, char_buf_len); #ifdef DEBUG fprintf(stderr, "current undo data now \"%s\"\n", u->strdata); #endif u->mark_begin_lineno = fs->current->lineno; u->mark_begin_x = fs->current_x; break; - case DEL: - len = strlen(u->strdata) + 2; - assert(len > 2); + } + case DEL: { + char *char_buf = charalloc(mb_cur_max()); + size_t char_buf_len = parse_mbchar(&fs->current->data[fs->current_x], char_buf, NULL); if (fs->current_x == u->begin) { /* They're deleting. */ - if (!u->xflags) - u->xflags = UNdel_del; - else if (u->xflags != UNdel_del) { - add_undo(action); - return; - } - 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) { + u->strdata = addstrings(u->strdata, strlen(u->strdata), char_buf, char_buf_len); + u->mark_begin_x = fs->current_x; + } else if (fs->current_x == u->begin - char_buf_len){ /* They're backspacing. */ - if (!u->xflags) - u->xflags = UNdel_backspace; - else if (u->xflags != UNdel_backspace) { - add_undo(action); - return; - } - data = charalloc(len); - data[0] = fs->current->data[fs->current_x]; - strcpy(&data[1], u->strdata); - free(u->strdata); - u->strdata = data; - u->begin--; + u->strdata = addstrings(char_buf, char_buf_len, u->strdata, strlen(u->strdata)); + u->begin = fs->current_x; } else { /* They deleted something else on the line. */ + free(char_buf); add_undo(DEL); return; } @@ -1064,6 +1038,7 @@ void update_undo(undo_type action) fprintf(stderr, "current undo data now \"%s\"\nu->begin = %d\n", u->strdata, u->begin); #endif break; + } case CUT_EOF: case CUT: if (!cutbuffer) @@ -1095,11 +1070,15 @@ void update_undo(undo_type action) break; case REPLACE: case PASTE: - add_undo(action); + u->begin = fs->current_x; + u->lineno = openfile->current->lineno; break; case INSERT: u->mark_begin_lineno = openfile->current->lineno; break; + case ENTER: + u->mark_begin_x = fs->current_x; + break; #ifndef DISABLE_WRAPPING case SPLIT: /* This will only be called if we made a completely new line, @@ -1109,7 +1088,6 @@ void update_undo(undo_type action) #endif /* !DISABLE_WRAPPING */ case UNSPLIT: /* These cases are handled by the earlier check for a new line and action. */ - case ENTER: case OTHER: break; } -- 2.39.5