diff options
author | Ron Yorston <rmy@pobox.com> | 2021-04-06 13:44:05 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2021-04-11 00:18:56 +0200 |
commit | f4a9908b4c21e5e8f544ab9d2bc3770b35942a6b (patch) | |
tree | 672a0715fd51c2523d8db3894dc53b640f885220 | |
parent | 99fb5f2144fc30337ec2ece3d1f59ba92ebed5fb (diff) | |
download | busybox-w32-f4a9908b4c21e5e8f544ab9d2bc3770b35942a6b.tar.gz busybox-w32-f4a9908b4c21e5e8f544ab9d2bc3770b35942a6b.tar.bz2 busybox-w32-f4a9908b4c21e5e8f544ab9d2bc3770b35942a6b.zip |
vi: improvements to reporting of changes
Traditional vi is mostly silent about the results of yank, delete,
change, undo or substitution commands. Vim reports some details
about undo and substitution. BusyBox vi is positively verbose in
comparison.
Make some improvements to BusyBox vi:
- Add vim-like reporting of changes caused by substitutions, of
the form '64 substitutions on 53 lines'. This replaces a fairly
useless report of the result of the last change made.
- Ensure that the report about put operations correctly reflects the
newly introduced repetition count.
- Commit 25d2592640 tried to limit status updates for delete and
yank operations by detecting whether the register had changed.
This didn't always work because the previously allocated memory
could be reused for the new register contents. Fix this by
delaying freeing the old register until after the new one has
been allocated.
- Add a configuration option to control verbose status reporting.
This is on by default. Turning it off make BusyBox vi as taciturn
as traditional vi and saves 435 bytes.
function old new delta
colon 3212 3292 +80
yank_status - 74 +74
static.text_yank 99 86 -13
string_insert 130 76 -54
do_cmd 4842 4776 -66
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 1/3 up/down: 154/-133) Total: 21 bytes
Signed-off-by: Ron Yorston <rmy@pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | editors/vi.c | 90 |
1 files changed, 59 insertions, 31 deletions
diff --git a/editors/vi.c b/editors/vi.c index 96e6af318..fb46dc381 100644 --- a/editors/vi.c +++ b/editors/vi.c | |||
@@ -159,6 +159,14 @@ | |||
159 | //config: and will generally malloc() larger objects and less frequently. | 159 | //config: and will generally malloc() larger objects and less frequently. |
160 | //config: Unless you want more (or less) frequent "undo points" while typing, | 160 | //config: Unless you want more (or less) frequent "undo points" while typing, |
161 | //config: you should probably leave this unchanged. | 161 | //config: you should probably leave this unchanged. |
162 | //config: | ||
163 | //config:config FEATURE_VI_VERBOSE_STATUS | ||
164 | //config: bool "Enable verbose status reporting" | ||
165 | //config: default y | ||
166 | //config: depends on VI | ||
167 | //config: help | ||
168 | //config: Enable more verbose reporting of the results of yank, change, | ||
169 | //config: delete, undo and substitution commands. | ||
162 | 170 | ||
163 | //applet:IF_VI(APPLET(vi, BB_DIR_BIN, BB_SUID_DROP)) | 171 | //applet:IF_VI(APPLET(vi, BB_DIR_BIN, BB_SUID_DROP)) |
164 | 172 | ||
@@ -1357,14 +1365,17 @@ static void not_implemented(const char *s) | |||
1357 | // copy text into a register | 1365 | // copy text into a register |
1358 | static char *text_yank(char *p, char *q, int dest, int buftype) | 1366 | static char *text_yank(char *p, char *q, int dest, int buftype) |
1359 | { | 1367 | { |
1368 | char *oldreg = reg[dest]; | ||
1360 | int cnt = q - p; | 1369 | int cnt = q - p; |
1361 | if (cnt < 0) { // they are backwards- reverse them | 1370 | if (cnt < 0) { // they are backwards- reverse them |
1362 | p = q; | 1371 | p = q; |
1363 | cnt = -cnt; | 1372 | cnt = -cnt; |
1364 | } | 1373 | } |
1365 | free(reg[dest]); // if already a yank register, free it | 1374 | // Don't free register yet. This prevents the memory allocator |
1375 | // from reusing the free block so we can detect if it's changed. | ||
1366 | reg[dest] = xstrndup(p, cnt + 1); | 1376 | reg[dest] = xstrndup(p, cnt + 1); |
1367 | regtype[dest] = buftype; | 1377 | regtype[dest] = buftype; |
1378 | free(oldreg); | ||
1368 | return p; | 1379 | return p; |
1369 | } | 1380 | } |
1370 | 1381 | ||
@@ -1415,6 +1426,22 @@ static char *swap_context(char *p) // goto new context for '' command make this | |||
1415 | } | 1426 | } |
1416 | return p; | 1427 | return p; |
1417 | } | 1428 | } |
1429 | |||
1430 | # if ENABLE_FEATURE_VI_VERBOSE_STATUS | ||
1431 | static void yank_status(const char *op, const char *p, int cnt) | ||
1432 | { | ||
1433 | int lines, chars; | ||
1434 | |||
1435 | lines = chars = 0; | ||
1436 | while (*p) { | ||
1437 | ++chars; | ||
1438 | if (*p++ == '\n') | ||
1439 | ++lines; | ||
1440 | } | ||
1441 | status_line("%s %d lines (%d chars) from [%c]", | ||
1442 | op, lines * cnt, chars * cnt, what_reg()); | ||
1443 | } | ||
1444 | # endif | ||
1418 | #endif /* FEATURE_VI_YANKMARK */ | 1445 | #endif /* FEATURE_VI_YANKMARK */ |
1419 | 1446 | ||
1420 | #if ENABLE_FEATURE_VI_UNDO | 1447 | #if ENABLE_FEATURE_VI_UNDO |
@@ -1687,10 +1714,12 @@ static void undo_pop(void) | |||
1687 | u_start = text + undo_entry->start; | 1714 | u_start = text + undo_entry->start; |
1688 | text_hole_make(u_start, undo_entry->length); | 1715 | text_hole_make(u_start, undo_entry->length); |
1689 | memcpy(u_start, undo_entry->undo_text, undo_entry->length); | 1716 | memcpy(u_start, undo_entry->undo_text, undo_entry->length); |
1717 | # if ENABLE_FEATURE_VI_VERBOSE_STATUS | ||
1690 | status_line("Undo [%d] %s %d chars at position %d", | 1718 | status_line("Undo [%d] %s %d chars at position %d", |
1691 | modified_count, "restored", | 1719 | modified_count, "restored", |
1692 | undo_entry->length, undo_entry->start | 1720 | undo_entry->length, undo_entry->start |
1693 | ); | 1721 | ); |
1722 | # endif | ||
1694 | break; | 1723 | break; |
1695 | case UNDO_INS: | 1724 | case UNDO_INS: |
1696 | case UNDO_INS_CHAIN: | 1725 | case UNDO_INS_CHAIN: |
@@ -1698,10 +1727,12 @@ static void undo_pop(void) | |||
1698 | u_start = undo_entry->start + text; | 1727 | u_start = undo_entry->start + text; |
1699 | u_end = u_start - 1 + undo_entry->length; | 1728 | u_end = u_start - 1 + undo_entry->length; |
1700 | text_hole_delete(u_start, u_end, NO_UNDO); | 1729 | text_hole_delete(u_start, u_end, NO_UNDO); |
1730 | # if ENABLE_FEATURE_VI_VERBOSE_STATUS | ||
1701 | status_line("Undo [%d] %s %d chars at position %d", | 1731 | status_line("Undo [%d] %s %d chars at position %d", |
1702 | modified_count, "deleted", | 1732 | modified_count, "deleted", |
1703 | undo_entry->length, undo_entry->start | 1733 | undo_entry->length, undo_entry->start |
1704 | ); | 1734 | ); |
1735 | # endif | ||
1705 | break; | 1736 | break; |
1706 | } | 1737 | } |
1707 | repeat = 0; | 1738 | repeat = 0; |
@@ -2150,16 +2181,6 @@ static uintptr_t string_insert(char *p, const char *s, int undo) // insert the s | |||
2150 | bias = text_hole_make(p, i); | 2181 | bias = text_hole_make(p, i); |
2151 | p += bias; | 2182 | p += bias; |
2152 | memcpy(p, s, i); | 2183 | memcpy(p, s, i); |
2153 | #if ENABLE_FEATURE_VI_YANKMARK | ||
2154 | { | ||
2155 | int cnt; | ||
2156 | for (cnt = 0; *s != '\0'; s++) { | ||
2157 | if (*s == '\n') | ||
2158 | cnt++; | ||
2159 | } | ||
2160 | status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg()); | ||
2161 | } | ||
2162 | #endif | ||
2163 | return bias; | 2184 | return bias; |
2164 | } | 2185 | } |
2165 | #endif | 2186 | #endif |
@@ -2821,6 +2842,9 @@ static void colon(char *buf) | |||
2821 | size_t len_F, len_R; | 2842 | size_t len_F, len_R; |
2822 | int gflag = 0; // global replace flag | 2843 | int gflag = 0; // global replace flag |
2823 | int subs = 0; // number of substitutions | 2844 | int subs = 0; // number of substitutions |
2845 | # if ENABLE_FEATURE_VI_VERBOSE_STATUS | ||
2846 | int last_line = 0, lines = 0; | ||
2847 | # endif | ||
2824 | 2848 | ||
2825 | // F points to the "find" pattern | 2849 | // F points to the "find" pattern |
2826 | // R points to the "replace" pattern | 2850 | // R points to the "replace" pattern |
@@ -2860,6 +2884,12 @@ static void colon(char *buf) | |||
2860 | subs ? ALLOW_UNDO_CHAIN: ALLOW_UNDO); | 2884 | subs ? ALLOW_UNDO_CHAIN: ALLOW_UNDO); |
2861 | // can't do this above, no undo => no third argument | 2885 | // can't do this above, no undo => no third argument |
2862 | subs++; | 2886 | subs++; |
2887 | # if ENABLE_FEATURE_VI_VERBOSE_STATUS | ||
2888 | if (last_line != i) { | ||
2889 | last_line = i; | ||
2890 | ++lines; | ||
2891 | } | ||
2892 | # endif | ||
2863 | // insert the "replace" patern | 2893 | // insert the "replace" patern |
2864 | bias = string_insert(found, R, ALLOW_UNDO_CHAIN); | 2894 | bias = string_insert(found, R, ALLOW_UNDO_CHAIN); |
2865 | found += bias; | 2895 | found += bias; |
@@ -2880,6 +2910,10 @@ static void colon(char *buf) | |||
2880 | status_line_bold("No match"); | 2910 | status_line_bold("No match"); |
2881 | } else { | 2911 | } else { |
2882 | dot_skip_over_ws(); | 2912 | dot_skip_over_ws(); |
2913 | # if ENABLE_FEATURE_VI_VERBOSE_STATUS | ||
2914 | if (subs > 1) | ||
2915 | status_line("%d substitutions on %d lines", subs, lines); | ||
2916 | # endif | ||
2883 | } | 2917 | } |
2884 | # endif /* FEATURE_VI_SEARCH */ | 2918 | # endif /* FEATURE_VI_SEARCH */ |
2885 | } else if (strncmp(cmd, "version", i) == 0) { // show software version | 2919 | } else if (strncmp(cmd, "version", i) == 0) { // show software version |
@@ -3434,8 +3468,9 @@ static void do_cmd(int c) | |||
3434 | status_line_bold("Nothing in register %c", what_reg()); | 3468 | status_line_bold("Nothing in register %c", what_reg()); |
3435 | break; | 3469 | break; |
3436 | } | 3470 | } |
3437 | // are we putting whole lines or strings | ||
3438 | cnt = 0; | 3471 | cnt = 0; |
3472 | i = cmdcnt ?: 1; | ||
3473 | // are we putting whole lines or strings | ||
3439 | if (regtype[YDreg] == WHOLE) { | 3474 | if (regtype[YDreg] == WHOLE) { |
3440 | if (c == 'P') { | 3475 | if (c == 'P') { |
3441 | dot_begin(); // putting lines- Put above | 3476 | dot_begin(); // putting lines- Put above |
@@ -3453,7 +3488,7 @@ static void do_cmd(int c) | |||
3453 | dot_right(); // move to right, can move to NL | 3488 | dot_right(); // move to right, can move to NL |
3454 | // how far to move cursor if register doesn't have a NL | 3489 | // how far to move cursor if register doesn't have a NL |
3455 | if (strchr(p, '\n') == NULL) | 3490 | if (strchr(p, '\n') == NULL) |
3456 | cnt = (cmdcnt ?: 1) * strlen(p) - 1; | 3491 | cnt = i * strlen(p) - 1; |
3457 | } | 3492 | } |
3458 | do { | 3493 | do { |
3459 | // dot is adjusted if text[] is reallocated so we don't have to | 3494 | // dot is adjusted if text[] is reallocated so we don't have to |
@@ -3463,6 +3498,9 @@ static void do_cmd(int c) | |||
3463 | # endif | 3498 | # endif |
3464 | } while (--cmdcnt > 0); | 3499 | } while (--cmdcnt > 0); |
3465 | dot += cnt; | 3500 | dot += cnt; |
3501 | # if ENABLE_FEATURE_VI_YANKMARK && ENABLE_FEATURE_VI_VERBOSE_STATUS | ||
3502 | yank_status("Put", p, i); | ||
3503 | # endif | ||
3466 | end_cmd_q(); // stop adding to q | 3504 | end_cmd_q(); // stop adding to q |
3467 | break; | 3505 | break; |
3468 | case 'U': // U- Undo; replace current line with original version | 3506 | case 'U': // U- Undo; replace current line with original version |
@@ -3473,6 +3511,9 @@ static void do_cmd(int c) | |||
3473 | p += string_insert(p, reg[Ureg], ALLOW_UNDO_CHAIN); // insert orig line | 3511 | p += string_insert(p, reg[Ureg], ALLOW_UNDO_CHAIN); // insert orig line |
3474 | dot = p; | 3512 | dot = p; |
3475 | dot_skip_over_ws(); | 3513 | dot_skip_over_ws(); |
3514 | # if ENABLE_FEATURE_VI_YANKMARK && ENABLE_FEATURE_VI_VERBOSE_STATUS | ||
3515 | yank_status("Undo", reg[Ureg], 1); | ||
3516 | # endif | ||
3476 | } | 3517 | } |
3477 | break; | 3518 | break; |
3478 | #endif /* FEATURE_VI_YANKMARK */ | 3519 | #endif /* FEATURE_VI_YANKMARK */ |
@@ -3878,7 +3919,9 @@ static void do_cmd(int c) | |||
3878 | int yf = YANKDEL; // assume either "c" or "d" | 3919 | int yf = YANKDEL; // assume either "c" or "d" |
3879 | int buftype; | 3920 | int buftype; |
3880 | #if ENABLE_FEATURE_VI_YANKMARK | 3921 | #if ENABLE_FEATURE_VI_YANKMARK |
3922 | # if ENABLE_FEATURE_VI_VERBOSE_STATUS | ||
3881 | char *savereg = reg[YDreg]; | 3923 | char *savereg = reg[YDreg]; |
3924 | # endif | ||
3882 | if (c == 'y' || c == 'Y') | 3925 | if (c == 'y' || c == 'Y') |
3883 | yf = YANKONLY; | 3926 | yf = YANKONLY; |
3884 | #endif | 3927 | #endif |
@@ -3901,27 +3944,12 @@ static void do_cmd(int c) | |||
3901 | } | 3944 | } |
3902 | // if CHANGING, not deleting, start inserting after the delete | 3945 | // if CHANGING, not deleting, start inserting after the delete |
3903 | if (c == 'c') { | 3946 | if (c == 'c') { |
3904 | //strcpy(buf, "Change"); | ||
3905 | goto dc_i; // start inserting | 3947 | goto dc_i; // start inserting |
3906 | } | 3948 | } |
3907 | #if ENABLE_FEATURE_VI_YANKMARK | 3949 | #if ENABLE_FEATURE_VI_YANKMARK && ENABLE_FEATURE_VI_VERBOSE_STATUS |
3908 | // only update status if a yank has actually happened | 3950 | // only update status if a yank has actually happened |
3909 | if (reg[YDreg] != savereg) { | 3951 | if (reg[YDreg] != savereg) |
3910 | if (c == 'd') { | 3952 | yank_status(c == 'd' ? "Delete" : "Yank", reg[YDreg], 1); |
3911 | strcpy(buf, "Delete"); | ||
3912 | } | ||
3913 | if (c == 'y' || c == 'Y') { | ||
3914 | strcpy(buf, "Yank"); | ||
3915 | } | ||
3916 | p = reg[YDreg]; | ||
3917 | q = p + strlen(p); | ||
3918 | for (cnt = 0; p <= q; p++) { | ||
3919 | if (*p == '\n') | ||
3920 | cnt++; | ||
3921 | } | ||
3922 | status_line("%s %u lines (%u chars) using [%c]", | ||
3923 | buf, cnt, (unsigned)strlen(reg[YDreg]), what_reg()); | ||
3924 | } | ||
3925 | #endif | 3953 | #endif |
3926 | dc6: | 3954 | dc6: |
3927 | end_cmd_q(); // stop adding to q | 3955 | end_cmd_q(); // stop adding to q |