From b7b1119d9f95cc956eb0161a59ee21a1daca8f52 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Tue, 6 Apr 2021 13:40:23 +0100 Subject: 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 '' 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 Signed-off-by: Denys Vlasenko --- editors/vi.c | 83 ++++++++++++++++++++++++++++++------------------------------ 1 file changed, 42 insertions(+), 41 deletions(-) (limited to 'editors') diff --git a/editors/vi.c b/editors/vi.c index 5dc14f1..9a17f65 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -3079,22 +3079,38 @@ static int at_eof(const char *s) return ((s == end - 2 && s[1] == '\n') || s == end - 1); } -static int find_range(char **start, char **stop, char c) +static int find_range(char **start, char **stop, int cmd) { char *save_dot, *p, *q, *t; int buftype = -1; + int c; save_dot = dot; p = q = dot; - if (strchr("cdy><", c)) { +#if ENABLE_FEATURE_VI_YANKMARK + if (cmd == 'Y') { + c = 'y'; + } else +#endif + { + c = get_motion_char(); + } + +#if ENABLE_FEATURE_VI_YANKMARK + if ((cmd == 'Y' || cmd == c) && strchr("cdy><", c)) { +#else + if (cmd == c && strchr("cd><", c)) { +#endif // these cmds operate on whole lines buftype = WHOLE; if (--cmdcnt > 0) do_cmd('j'); - } else if (strchr("^%$0bBeEfFtTh|{}\b\177", c)) { - // These cmds operate on char positions - buftype = PARTIAL; + } else if (strchr("^%$0bBeEfFtThnN/?|{}\b\177", c)) { + // Most operate on char positions within a line. Of those that + // don't '%' needs no special treatment, search commands are + // marked as MULTI and "{}" are handled below. + buftype = strchr("nN/?", c) ? MULTI : PARTIAL; do_cmd(c); // execute movement cmd if (p == dot) // no movement is an error buftype = -1; @@ -3104,7 +3120,16 @@ static int find_range(char **start, char **stop, char c) // step back one char, but not if we're at end of file if (dot > p && !at_eof(dot)) dot--; - } else if (strchr("GHL+-jk\r\n", c)) { + t = dot; + // don't include trailing WS as part of word + while (dot > p && isspace(*dot)) { + if (*dot-- == '\n') + t = dot; + } + // for non-change operations WS after NL is not part of word + if (cmd != 'c' && dot != p && *dot != '\n') + dot = t; + } else if (strchr("GHL+-jk'\r\n", c)) { // these operate on whole lines buftype = WHOLE; do_cmd(c); // execute movement cmd @@ -3119,8 +3144,11 @@ static int find_range(char **start, char **stop, char c) dot--; } - if (buftype == -1) + if (buftype == -1) { + if (c != 27) + indicate_error(); return buftype; + } q = dot; if (q < p) { @@ -3131,7 +3159,7 @@ static int find_range(char **start, char **stop, char c) // movements which don't include end of range if (q > p) { - if (strchr("^0bBFTh|\b\177", c)) { + if (strchr("^0bBFThnN/?|\b\177", c)) { q--; } else if (strchr("{}", c)) { buftype = (p == begin_line(p) && (*q == '\n' || at_eof(q))) ? @@ -3144,7 +3172,7 @@ static int find_range(char **start, char **stop, char c) } } - if (buftype == WHOLE) { + if (buftype == WHOLE || cmd == '<' || cmd == '>') { p = begin_line(p); q = end_line(q); } @@ -3582,14 +3610,9 @@ static void do_cmd(int c) case '<': // <- Left shift something case '>': // >- Right shift something cnt = count_lines(text, dot); // remember what line we are on - c1 = get_motion_char(); // get the type of thing to operate on - if (find_range(&p, &q, c1) == -1) { - indicate_error(); + if (find_range(&p, &q, c) == -1) goto dc6; - } yank_delete(p, q, WHOLE, YANKONLY, NO_UNDO); // save copy before change - p = begin_line(p); - q = end_line(q); i = count_lines(p, q); // # of lines we are shifting for ( ; i > 0; i--, p = next_line(p)) { if (c == '<') { @@ -3820,39 +3843,17 @@ static void do_cmd(int c) case 'Y': // Y- Yank a line #endif { + int yf = YANKDEL; // assume either "c" or "d" + int buftype; #if ENABLE_FEATURE_VI_YANKMARK char *savereg = reg[YDreg]; -#endif - int yf, buftype = 0; - yf = YANKDEL; // assume either "c" or "d" -#if ENABLE_FEATURE_VI_YANKMARK if (c == 'y' || c == 'Y') yf = YANKONLY; #endif - c1 = 'y'; - if (c != 'Y') { - c1 = get_motion_char(); // get the type of thing to operate on - if (c1 == 27) // ESC- user changed mind and wants out - goto dc6; - } // determine range, and whether it spans lines - buftype = find_range(&p, &q, c1); - place_cursor(0, 0); - if (buftype == -1) { // invalid range - indicate_error(); + buftype = find_range(&p, &q, c); + if (buftype == -1) // invalid range goto dc6; - } - if (c1 == 'w' || c1 == 'W') { - char *q0 = q; - // don't include trailing WS as part of word - while (q > p && isspace(*q)) { - if (*q-- == '\n') - q0 = q; - } - // for non-change operations WS after NL is not part of word - if (c != 'c' && q != p && *q != '\n') - q = q0; - } dot = yank_delete(p, q, buftype, yf, ALLOW_UNDO); // delete word if (buftype == WHOLE) { if (c == 'c') { -- cgit v1.1