aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2021-05-20 08:27:48 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2021-06-02 06:29:34 +0200
commit9659a8db1dd28bdf8659fdae5d097b6f48bd2736 (patch)
tree7a984a8efdadb26fad56ef6cdcd0b5da7ef14808
parent16e2fa9049d5ddd7d4c8aea875dad91e07868685 (diff)
downloadbusybox-w32-9659a8db1dd28bdf8659fdae5d097b6f48bd2736.tar.gz
busybox-w32-9659a8db1dd28bdf8659fdae5d097b6f48bd2736.tar.bz2
busybox-w32-9659a8db1dd28bdf8659fdae5d097b6f48bd2736.zip
vi: remove autoindent from otherwise empty lines
Lines that have no content apart from automatic indentation should be treated as empty when the user hits return or ESC. The implementation uses the global variable 'indentcol'. Usually this is zero. It can also be -1 to indicate an 'O' (open above) command, replacing the overloading of the tabstop option bit. A value greater than zero indicates that the current line has been autoindented to the given column (or that the autoindent has been adjusted with ctrl-D). Any other change to the line resets 'indentcol' to zero. Replace strspn() with ident_len(). The latter handles the unlikely case that it's called on the last line of a file which doesn't have a terminating newline. function old new delta char_insert 741 912 +171 indent_len - 42 +42 do_cmd 4781 4785 +4 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 2/0 up/down: 217/0) Total: 217 bytes Signed-off-by: Ron Yorston <rmy@pobox.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--editors/vi.c85
1 files changed, 59 insertions, 26 deletions
diff --git a/editors/vi.c b/editors/vi.c
index 36116e677..d85cdd98d 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -307,7 +307,6 @@ struct globals {
307#define err_method (vi_setops & VI_ERR_METHOD) // indicate error with beep or flash 307#define err_method (vi_setops & VI_ERR_METHOD) // indicate error with beep or flash
308#define ignorecase (vi_setops & VI_IGNORECASE) 308#define ignorecase (vi_setops & VI_IGNORECASE)
309#define showmatch (vi_setops & VI_SHOWMATCH ) 309#define showmatch (vi_setops & VI_SHOWMATCH )
310#define openabove (vi_setops & VI_TABSTOP )
311// order of constants and strings must match 310// order of constants and strings must match
312#define OPTS_STR \ 311#define OPTS_STR \
313 "ai\0""autoindent\0" \ 312 "ai\0""autoindent\0" \
@@ -316,15 +315,10 @@ struct globals {
316 "ic\0""ignorecase\0" \ 315 "ic\0""ignorecase\0" \
317 "sm\0""showmatch\0" \ 316 "sm\0""showmatch\0" \
318 "ts\0""tabstop\0" 317 "ts\0""tabstop\0"
319#define set_openabove() (vi_setops |= VI_TABSTOP)
320#define clear_openabove() (vi_setops &= ~VI_TABSTOP)
321#else 318#else
322#define autoindent (0) 319#define autoindent (0)
323#define expandtab (0) 320#define expandtab (0)
324#define err_method (0) 321#define err_method (0)
325#define openabove (0)
326#define set_openabove() ((void)0)
327#define clear_openabove() ((void)0)
328#endif 322#endif
329 323
330#if ENABLE_FEATURE_VI_READONLY 324#if ENABLE_FEATURE_VI_READONLY
@@ -380,6 +374,9 @@ struct globals {
380#if ENABLE_FEATURE_VI_SEARCH 374#if ENABLE_FEATURE_VI_SEARCH
381 char *last_search_pattern; // last pattern from a '/' or '?' search 375 char *last_search_pattern; // last pattern from a '/' or '?' search
382#endif 376#endif
377#if ENABLE_FEATURE_VI_SETOPTS
378 int indentcol; // column of recently autoindent, 0 or -1
379#endif
383 380
384 // former statics 381 // former statics
385#if ENABLE_FEATURE_VI_YANKMARK 382#if ENABLE_FEATURE_VI_YANKMARK
@@ -503,6 +500,7 @@ struct globals {
503#define ioq_start (G.ioq_start ) 500#define ioq_start (G.ioq_start )
504#define dotcnt (G.dotcnt ) 501#define dotcnt (G.dotcnt )
505#define last_search_pattern (G.last_search_pattern) 502#define last_search_pattern (G.last_search_pattern)
503#define indentcol (G.indentcol )
506 504
507#define edit_file__cur_line (G.edit_file__cur_line) 505#define edit_file__cur_line (G.edit_file__cur_line)
508#define refresh__old_offset (G.refresh__old_offset) 506#define refresh__old_offset (G.refresh__old_offset)
@@ -2103,16 +2101,26 @@ static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at
2103 return bias; 2101 return bias;
2104} 2102}
2105 2103
2104// find number of characters in indent, p must be at beginning of line
2105static size_t indent_len(char *p)
2106{
2107 char *r = p;
2108
2109 while (r < (end - 1) && isblank(*r))
2110 r++;
2111 return r - p;
2112}
2113
2106#if !ENABLE_FEATURE_VI_UNDO 2114#if !ENABLE_FEATURE_VI_UNDO
2107#define char_insert(a,b,c) char_insert(a,b) 2115#define char_insert(a,b,c) char_insert(a,b)
2108#endif 2116#endif
2109static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' 2117static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2110{ 2118{
2111#if ENABLE_FEATURE_VI_SETOPTS 2119#if ENABLE_FEATURE_VI_SETOPTS
2112 char *q;
2113 size_t len; 2120 size_t len;
2114 int col, ntab, nspc; 2121 int col, ntab, nspc;
2115#endif 2122#endif
2123 char *bol = begin_line(p);
2116 2124
2117 if (c == 22) { // Is this an ctrl-V? 2125 if (c == 22) { // Is this an ctrl-V?
2118 p += stupid_insert(p, '^'); // use ^ to indicate literal next 2126 p += stupid_insert(p, '^'); // use ^ to indicate literal next
@@ -2134,22 +2142,33 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2134 if ((p[-1] != '\n') && (dot > text)) { 2142 if ((p[-1] != '\n') && (dot > text)) {
2135 p--; 2143 p--;
2136 } 2144 }
2137 } else if (c == 4) { // ctrl-D reduces indentation 2145#if ENABLE_FEATURE_VI_SETOPTS
2138 int prev; 2146 if (autoindent) {
2139 char *r, *bol; 2147 len = indent_len(bol);
2140 bol = begin_line(p); 2148 if (len && get_column(bol + len) == indentcol) {
2141 for (r = bol; r < end_line(p); ++r) { 2149 // remove autoindent from otherwise empty line
2142 if (!isblank(*r)) 2150 text_hole_delete(bol, bol + len - 1, undo);
2143 break; 2151 p = bol;
2152 }
2144 } 2153 }
2145 2154#endif
2146 prev = prev_tabstop(get_column(r)); 2155 } else if (c == 4) { // ctrl-D reduces indentation
2156 char *r = bol + indent_len(bol);
2157 int prev = prev_tabstop(get_column(r));
2147 while (r > bol && get_column(r) > prev) { 2158 while (r > bol && get_column(r) > prev) {
2148 if (p > bol) 2159 if (p > bol)
2149 p--; 2160 p--;
2150 r--; 2161 r--;
2151 r = text_hole_delete(r, r, ALLOW_UNDO_QUEUED); 2162 r = text_hole_delete(r, r, ALLOW_UNDO_QUEUED);
2152 } 2163 }
2164
2165#if ENABLE_FEATURE_VI_SETOPTS
2166 if (autoindent && indentcol && r == end_line(p)) {
2167 // record changed size of autoindent
2168 indentcol = get_column(p);
2169 return p;
2170 }
2171#endif
2153#if ENABLE_FEATURE_VI_SETOPTS 2172#if ENABLE_FEATURE_VI_SETOPTS
2154 } else if (c == '\t' && expandtab) { // expand tab 2173 } else if (c == '\t' && expandtab) { // expand tab
2155 col = get_column(p); 2174 col = get_column(p);
@@ -2188,12 +2207,23 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2188 } 2207 }
2189 if (autoindent && c == '\n') { // auto indent the new line 2208 if (autoindent && c == '\n') { // auto indent the new line
2190 // use indent of current/previous line 2209 // use indent of current/previous line
2191 q = openabove ? p : prev_line(p); 2210 bol = indentcol < 0 ? p : prev_line(p);
2192 len = strspn(q, " \t"); // space or tab 2211 len = indent_len(bol);
2193 if (openabove) 2212 col = get_column(bol + len);
2194 p--; // indent goes before newly inserted NL 2213
2214 if (len && col == indentcol) {
2215 // previous line was empty except for autoindent
2216 // move the indent to the current line
2217 memmove(bol + 1, bol, len);
2218 *bol = '\n';
2219 return p;
2220 }
2221
2222 if (indentcol < 0)
2223 p--; // open above, indent before newly inserted NL
2224
2195 if (len) { 2225 if (len) {
2196 col = get_column(q + len); 2226 indentcol = col;
2197 if (expandtab) { 2227 if (expandtab) {
2198 ntab = 0; 2228 ntab = 0;
2199 nspc = col; 2229 nspc = col;
@@ -2208,11 +2238,14 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2208 memset(p, '\t', ntab); 2238 memset(p, '\t', ntab);
2209 p += ntab; 2239 p += ntab;
2210 memset(p, ' ', nspc); 2240 memset(p, ' ', nspc);
2211 p += nspc; 2241 return p + nspc;
2212 } 2242 }
2213 } 2243 }
2214#endif 2244#endif
2215 } 2245 }
2246#if ENABLE_FEATURE_VI_SETOPTS
2247 indentcol = 0;
2248#endif
2216 return p; 2249 return p;
2217} 2250}
2218 2251
@@ -2587,7 +2620,6 @@ static void setops(char *args, int flg_no)
2587 index = 1 << (index >> 1); // convert to VI_bit 2620 index = 1 << (index >> 1); // convert to VI_bit
2588 2621
2589 if (index & VI_TABSTOP) { 2622 if (index & VI_TABSTOP) {
2590 // don't set this bit in vi_setops, it's reused as 'openabove'
2591 int t; 2623 int t;
2592 if (!eq || flg_no) // no "=NNN" or it is "notabstop"? 2624 if (!eq || flg_no) // no "=NNN" or it is "notabstop"?
2593 goto bad; 2625 goto bad;
@@ -4050,17 +4082,18 @@ static void do_cmd(int c)
4050 break; 4082 break;
4051 case 'O': // O- open an empty line above 4083 case 'O': // O- open an empty line above
4052 dot_begin(); 4084 dot_begin();
4053 set_openabove(); 4085#if ENABLE_FEATURE_VI_SETOPTS
4086 indentcol = -1;
4087#endif
4054 goto dc3; 4088 goto dc3;
4055 case 'o': // o- open an empty line below 4089 case 'o': // o- open an empty line below
4056 dot_end(); 4090 dot_end();
4057 dc3: 4091 dc3:
4058 dot = char_insert(dot, '\n', ALLOW_UNDO); 4092 dot = char_insert(dot, '\n', ALLOW_UNDO);
4059 if (c == 'O' && !autoindent) { 4093 if (c == 'O' && !autoindent) {
4060 // done in char_insert() for openabove+autoindent 4094 // done in char_insert() for 'O'+autoindent
4061 dot_prev(); 4095 dot_prev();
4062 } 4096 }
4063 clear_openabove();
4064 goto dc_i; 4097 goto dc_i;
4065 break; 4098 break;
4066 case 'R': // R- continuous Replace char 4099 case 'R': // R- continuous Replace char