From b18c7bf702b2d7d967b2baf7d70fd8c408191eb4 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Sat, 10 Apr 2021 10:20:31 +0100 Subject: vi: improvements to undo The left shift operator ('<') didn't support undo at all; right shift ('>') required changes to be undone separately for each line. Allow both types of shift to be undone as a single operation. Also, neither traditional vi nor vim yank the lines being shifted by the '<' and '>' commands, so remove that call to yank_delete(); When a repetition count was specified for the '~', 'x', 'X' or 's' commands the changes had to be undone one character at a time. Allow undo as a single operation (though the delete and change parts of the 's' command still have to be undone separately). function old new delta undo_push_insert 37 40 +3 do_cmd 4695 4663 -32 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 3/-32) Total: -29 bytes v2: Don't break build when FEATURE_VI_UNDO is disabled. Don't reset 'undo_del' too early in '~' handling code. Code shrink '~'. Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- editors/vi.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) (limited to 'editors') diff --git a/editors/vi.c b/editors/vi.c index 7e89f58..e752685 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -3213,6 +3213,10 @@ static void do_cmd(int c) int dir; int cnt, i, j; int c1; +#if ENABLE_FEATURE_VI_UNDO + int allow_undo = ALLOW_UNDO; + int undo_del = UNDO_DEL; +#endif // c1 = c; // quiet the compiler // cnt = yf = 0; // quiet the compiler @@ -3614,24 +3618,29 @@ static void do_cmd(int c) cnt = count_lines(text, dot); // remember what line we are on if (find_range(&p, &q, c) == -1) goto dc6; - yank_delete(p, q, WHOLE, YANKONLY, NO_UNDO); // save copy before change i = count_lines(p, q); // # of lines we are shifting for ( ; i > 0; i--, p = next_line(p)) { if (c == '<') { // shift left- remove tab or 8 spaces if (*p == '\t') { // shrink buffer 1 char - text_hole_delete(p, p, NO_UNDO); + text_hole_delete(p, p, allow_undo); } else if (*p == ' ') { // we should be calculating columns, not just SPACE for (j = 0; *p == ' ' && j < tabstop; j++) { - text_hole_delete(p, p, NO_UNDO); + text_hole_delete(p, p, allow_undo); +#if ENABLE_FEATURE_VI_UNDO + allow_undo = ALLOW_UNDO_CHAIN; +#endif } } - } else if (c == '>') { + } else /* if (c == '>') */ { // shift right -- add tab or 8 spaces - char_insert(p, '\t', ALLOW_UNDO); + char_insert(p, '\t', allow_undo); } +#if ENABLE_FEATURE_VI_UNDO + allow_undo = ALLOW_UNDO_CHAIN; +#endif } dot = find_line(cnt); // what line were we on dot_skip_over_ws(); @@ -3785,7 +3794,10 @@ static void do_cmd(int c) if (dot[dir] != '\n') { if (c == 'X') dot--; // delete prev char - dot = yank_delete(dot, dot, PARTIAL, YANKDEL, ALLOW_UNDO); // delete char + dot = yank_delete(dot, dot, PARTIAL, YANKDEL, allow_undo); // delete char +#if ENABLE_FEATURE_VI_UNDO + allow_undo = ALLOW_UNDO_CHAIN; +#endif } } while (--cmdcnt > 0); end_cmd_q(); // stop adding to q @@ -3945,14 +3957,11 @@ static void do_cmd(int c) case '~': // ~- flip the case of letters a-z -> A-Z do { #if ENABLE_FEATURE_VI_UNDO - if (islower(*dot)) { - undo_push(dot, 1, UNDO_DEL); - *dot = toupper(*dot); - undo_push(dot, 1, UNDO_INS_CHAIN); - } else if (isupper(*dot)) { - undo_push(dot, 1, UNDO_DEL); - *dot = tolower(*dot); + if (isalpha(*dot)) { + undo_push(dot, 1, undo_del); + *dot = islower(*dot) ? toupper(*dot) : tolower(*dot); undo_push(dot, 1, UNDO_INS_CHAIN); + undo_del = UNDO_DEL_CHAIN; } #else if (islower(*dot)) { -- cgit v1.1