diff options
author | Ron Yorston <rmy@pobox.com> | 2021-04-06 13:40:23 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2021-04-11 00:18:55 +0200 |
commit | b7b1119d9f95cc956eb0161a59ee21a1daca8f52 (patch) | |
tree | 230b99719a414b668601bc75b083c54da413ef2a | |
parent | 7ce0e75c1f26d9cb3061c64aeab3204f62a5b55c (diff) | |
download | busybox-w32-b7b1119d9f95cc956eb0161a59ee21a1daca8f52.tar.gz busybox-w32-b7b1119d9f95cc956eb0161a59ee21a1daca8f52.tar.bz2 busybox-w32-b7b1119d9f95cc956eb0161a59ee21a1daca8f52.zip |
vi: improvements to range selection
Rewrite find_range(), pushing quite a bit of code from do_cmd()
down into it.
- The commands 'y', 'd', 'c', '<' and '>' can be given twice to
specify a whole-line range. BusyBox vi actually accepted any
second character from that group, e.g. 'dc' or '<y', with the
latter being accepted even if yank was disabled. Require the
two characters to match.
- '<' and '>' commands followed by ESC incorrectly issued an alert.
- Allow search commands and a marker (specified as "y'a", for example)
to define a range for those operators that support it.
function old new delta
find_range 518 707 +189
.rodata 105119 105133 +14
get_motion_char 68 - -68
do_cmd 4860 4695 -165
------------------------------------------------------------------------------
(add/remove: 0/1 grow/shrink: 2/1 up/down: 203/-233) Total: -30 bytes
Signed-off-by: Ron Yorston <rmy@pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | editors/vi.c | 83 |
1 files changed, 42 insertions, 41 deletions
diff --git a/editors/vi.c b/editors/vi.c index 5dc14f1ba..9a17f6527 100644 --- a/editors/vi.c +++ b/editors/vi.c | |||
@@ -3079,22 +3079,38 @@ static int at_eof(const char *s) | |||
3079 | return ((s == end - 2 && s[1] == '\n') || s == end - 1); | 3079 | return ((s == end - 2 && s[1] == '\n') || s == end - 1); |
3080 | } | 3080 | } |
3081 | 3081 | ||
3082 | static int find_range(char **start, char **stop, char c) | 3082 | static int find_range(char **start, char **stop, int cmd) |
3083 | { | 3083 | { |
3084 | char *save_dot, *p, *q, *t; | 3084 | char *save_dot, *p, *q, *t; |
3085 | int buftype = -1; | 3085 | int buftype = -1; |
3086 | int c; | ||
3086 | 3087 | ||
3087 | save_dot = dot; | 3088 | save_dot = dot; |
3088 | p = q = dot; | 3089 | p = q = dot; |
3089 | 3090 | ||
3090 | if (strchr("cdy><", c)) { | 3091 | #if ENABLE_FEATURE_VI_YANKMARK |
3092 | if (cmd == 'Y') { | ||
3093 | c = 'y'; | ||
3094 | } else | ||
3095 | #endif | ||
3096 | { | ||
3097 | c = get_motion_char(); | ||
3098 | } | ||
3099 | |||
3100 | #if ENABLE_FEATURE_VI_YANKMARK | ||
3101 | if ((cmd == 'Y' || cmd == c) && strchr("cdy><", c)) { | ||
3102 | #else | ||
3103 | if (cmd == c && strchr("cd><", c)) { | ||
3104 | #endif | ||
3091 | // these cmds operate on whole lines | 3105 | // these cmds operate on whole lines |
3092 | buftype = WHOLE; | 3106 | buftype = WHOLE; |
3093 | if (--cmdcnt > 0) | 3107 | if (--cmdcnt > 0) |
3094 | do_cmd('j'); | 3108 | do_cmd('j'); |
3095 | } else if (strchr("^%$0bBeEfFtTh|{}\b\177", c)) { | 3109 | } else if (strchr("^%$0bBeEfFtThnN/?|{}\b\177", c)) { |
3096 | // These cmds operate on char positions | 3110 | // Most operate on char positions within a line. Of those that |
3097 | buftype = PARTIAL; | 3111 | // don't '%' needs no special treatment, search commands are |
3112 | // marked as MULTI and "{}" are handled below. | ||
3113 | buftype = strchr("nN/?", c) ? MULTI : PARTIAL; | ||
3098 | do_cmd(c); // execute movement cmd | 3114 | do_cmd(c); // execute movement cmd |
3099 | if (p == dot) // no movement is an error | 3115 | if (p == dot) // no movement is an error |
3100 | buftype = -1; | 3116 | buftype = -1; |
@@ -3104,7 +3120,16 @@ static int find_range(char **start, char **stop, char c) | |||
3104 | // step back one char, but not if we're at end of file | 3120 | // step back one char, but not if we're at end of file |
3105 | if (dot > p && !at_eof(dot)) | 3121 | if (dot > p && !at_eof(dot)) |
3106 | dot--; | 3122 | dot--; |
3107 | } else if (strchr("GHL+-jk\r\n", c)) { | 3123 | t = dot; |
3124 | // don't include trailing WS as part of word | ||
3125 | while (dot > p && isspace(*dot)) { | ||
3126 | if (*dot-- == '\n') | ||
3127 | t = dot; | ||
3128 | } | ||
3129 | // for non-change operations WS after NL is not part of word | ||
3130 | if (cmd != 'c' && dot != p && *dot != '\n') | ||
3131 | dot = t; | ||
3132 | } else if (strchr("GHL+-jk'\r\n", c)) { | ||
3108 | // these operate on whole lines | 3133 | // these operate on whole lines |
3109 | buftype = WHOLE; | 3134 | buftype = WHOLE; |
3110 | do_cmd(c); // execute movement cmd | 3135 | do_cmd(c); // execute movement cmd |
@@ -3119,8 +3144,11 @@ static int find_range(char **start, char **stop, char c) | |||
3119 | dot--; | 3144 | dot--; |
3120 | } | 3145 | } |
3121 | 3146 | ||
3122 | if (buftype == -1) | 3147 | if (buftype == -1) { |
3148 | if (c != 27) | ||
3149 | indicate_error(); | ||
3123 | return buftype; | 3150 | return buftype; |
3151 | } | ||
3124 | 3152 | ||
3125 | q = dot; | 3153 | q = dot; |
3126 | if (q < p) { | 3154 | if (q < p) { |
@@ -3131,7 +3159,7 @@ static int find_range(char **start, char **stop, char c) | |||
3131 | 3159 | ||
3132 | // movements which don't include end of range | 3160 | // movements which don't include end of range |
3133 | if (q > p) { | 3161 | if (q > p) { |
3134 | if (strchr("^0bBFTh|\b\177", c)) { | 3162 | if (strchr("^0bBFThnN/?|\b\177", c)) { |
3135 | q--; | 3163 | q--; |
3136 | } else if (strchr("{}", c)) { | 3164 | } else if (strchr("{}", c)) { |
3137 | buftype = (p == begin_line(p) && (*q == '\n' || at_eof(q))) ? | 3165 | buftype = (p == begin_line(p) && (*q == '\n' || at_eof(q))) ? |
@@ -3144,7 +3172,7 @@ static int find_range(char **start, char **stop, char c) | |||
3144 | } | 3172 | } |
3145 | } | 3173 | } |
3146 | 3174 | ||
3147 | if (buftype == WHOLE) { | 3175 | if (buftype == WHOLE || cmd == '<' || cmd == '>') { |
3148 | p = begin_line(p); | 3176 | p = begin_line(p); |
3149 | q = end_line(q); | 3177 | q = end_line(q); |
3150 | } | 3178 | } |
@@ -3582,14 +3610,9 @@ static void do_cmd(int c) | |||
3582 | case '<': // <- Left shift something | 3610 | case '<': // <- Left shift something |
3583 | case '>': // >- Right shift something | 3611 | case '>': // >- Right shift something |
3584 | cnt = count_lines(text, dot); // remember what line we are on | 3612 | cnt = count_lines(text, dot); // remember what line we are on |
3585 | c1 = get_motion_char(); // get the type of thing to operate on | 3613 | if (find_range(&p, &q, c) == -1) |
3586 | if (find_range(&p, &q, c1) == -1) { | ||
3587 | indicate_error(); | ||
3588 | goto dc6; | 3614 | goto dc6; |
3589 | } | ||
3590 | yank_delete(p, q, WHOLE, YANKONLY, NO_UNDO); // save copy before change | 3615 | yank_delete(p, q, WHOLE, YANKONLY, NO_UNDO); // save copy before change |
3591 | p = begin_line(p); | ||
3592 | q = end_line(q); | ||
3593 | i = count_lines(p, q); // # of lines we are shifting | 3616 | i = count_lines(p, q); // # of lines we are shifting |
3594 | for ( ; i > 0; i--, p = next_line(p)) { | 3617 | for ( ; i > 0; i--, p = next_line(p)) { |
3595 | if (c == '<') { | 3618 | if (c == '<') { |
@@ -3820,39 +3843,17 @@ static void do_cmd(int c) | |||
3820 | case 'Y': // Y- Yank a line | 3843 | case 'Y': // Y- Yank a line |
3821 | #endif | 3844 | #endif |
3822 | { | 3845 | { |
3846 | int yf = YANKDEL; // assume either "c" or "d" | ||
3847 | int buftype; | ||
3823 | #if ENABLE_FEATURE_VI_YANKMARK | 3848 | #if ENABLE_FEATURE_VI_YANKMARK |
3824 | char *savereg = reg[YDreg]; | 3849 | char *savereg = reg[YDreg]; |
3825 | #endif | ||
3826 | int yf, buftype = 0; | ||
3827 | yf = YANKDEL; // assume either "c" or "d" | ||
3828 | #if ENABLE_FEATURE_VI_YANKMARK | ||
3829 | if (c == 'y' || c == 'Y') | 3850 | if (c == 'y' || c == 'Y') |
3830 | yf = YANKONLY; | 3851 | yf = YANKONLY; |
3831 | #endif | 3852 | #endif |
3832 | c1 = 'y'; | ||
3833 | if (c != 'Y') { | ||
3834 | c1 = get_motion_char(); // get the type of thing to operate on | ||
3835 | if (c1 == 27) // ESC- user changed mind and wants out | ||
3836 | goto dc6; | ||
3837 | } | ||
3838 | // determine range, and whether it spans lines | 3853 | // determine range, and whether it spans lines |
3839 | buftype = find_range(&p, &q, c1); | 3854 | buftype = find_range(&p, &q, c); |
3840 | place_cursor(0, 0); | 3855 | if (buftype == -1) // invalid range |
3841 | if (buftype == -1) { // invalid range | ||
3842 | indicate_error(); | ||
3843 | goto dc6; | 3856 | goto dc6; |
3844 | } | ||
3845 | if (c1 == 'w' || c1 == 'W') { | ||
3846 | char *q0 = q; | ||
3847 | // don't include trailing WS as part of word | ||
3848 | while (q > p && isspace(*q)) { | ||
3849 | if (*q-- == '\n') | ||
3850 | q0 = q; | ||
3851 | } | ||
3852 | // for non-change operations WS after NL is not part of word | ||
3853 | if (c != 'c' && q != p && *q != '\n') | ||
3854 | q = q0; | ||
3855 | } | ||
3856 | dot = yank_delete(p, q, buftype, yf, ALLOW_UNDO); // delete word | 3857 | dot = yank_delete(p, q, buftype, yf, ALLOW_UNDO); // delete word |
3857 | if (buftype == WHOLE) { | 3858 | if (buftype == WHOLE) { |
3858 | if (c == 'c') { | 3859 | if (c == 'c') { |