vi: handle autoindent in 'cc' command

When the 'cc' command is invoked with autoindent enabled it
should use the indent of the first line being changed.

The size of the indent has to be established before char_insert()
is called as the lines being changed are deleted.  Introduce a
new global variable, newindent, to handle this.  The indentcol
global is now effectively a static variable in char_insert().

function                                             old     new   delta
do_cmd                                              4247    4308     +61
vi_main                                              416     422      +6
char_insert                                          891     875     -16
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/1 up/down: 67/-16)             Total: 51 bytes

Signed-off-by: Ron Yorston <rmy@pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Ron Yorston 2022-03-18 11:30:38 +00:00 committed by Denys Vlasenko
parent af3b585815
commit 2617a5e4c6

View File

@ -380,7 +380,9 @@ struct globals {
char *last_search_pattern; // last pattern from a '/' or '?' search char *last_search_pattern; // last pattern from a '/' or '?' search
#endif #endif
#if ENABLE_FEATURE_VI_SETOPTS #if ENABLE_FEATURE_VI_SETOPTS
int indentcol; // column of recently autoindent, 0 or -1 int char_insert__indentcol; // column of recent autoindent or 0
int newindent; // autoindent value for 'O'/'cc' commands
// or -1 to use indent from previous line
#endif #endif
smallint cmd_error; smallint cmd_error;
@ -507,7 +509,8 @@ struct globals {
#define ioq_start (G.ioq_start ) #define ioq_start (G.ioq_start )
#define dotcnt (G.dotcnt ) #define dotcnt (G.dotcnt )
#define last_search_pattern (G.last_search_pattern) #define last_search_pattern (G.last_search_pattern)
#define indentcol (G.indentcol ) #define char_insert__indentcol (G.char_insert__indentcol)
#define newindent (G.newindent )
#define cmd_error (G.cmd_error ) #define cmd_error (G.cmd_error )
#define edit_file__cur_line (G.edit_file__cur_line) #define edit_file__cur_line (G.edit_file__cur_line)
@ -540,10 +543,11 @@ struct globals {
#define INIT_G() do { \ #define INIT_G() do { \
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
last_modified_count = -1; \ last_modified_count--; \
/* "" but has space for 2 chars: */ \ /* "" but has space for 2 chars: */ \
IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \ IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \
tabstop = 8; \ tabstop = 8; \
IF_FEATURE_VI_SETOPTS(newindent--;) \
} while (0) } while (0)
#if ENABLE_FEATURE_VI_CRASHME #if ENABLE_FEATURE_VI_CRASHME
@ -2113,6 +2117,7 @@ static size_t indent_len(char *p)
static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
{ {
#if ENABLE_FEATURE_VI_SETOPTS #if ENABLE_FEATURE_VI_SETOPTS
# define indentcol char_insert__indentcol
size_t len; size_t len;
int col, ntab, nspc; int col, ntab, nspc;
#endif #endif
@ -2141,7 +2146,8 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
#if ENABLE_FEATURE_VI_SETOPTS #if ENABLE_FEATURE_VI_SETOPTS
if (autoindent) { if (autoindent) {
len = indent_len(bol); len = indent_len(bol);
if (len && get_column(bol + len) == indentcol && bol[len] == '\n') { col = get_column(bol + len);
if (len && col == indentcol && bol[len] == '\n') {
// remove autoindent from otherwise empty line // remove autoindent from otherwise empty line
text_hole_delete(bol, bol + len - 1, undo); text_hole_delete(bol, bol + len - 1, undo);
p = bol; p = bol;
@ -2210,26 +2216,30 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
showmatching(p - 1); showmatching(p - 1);
} }
if (autoindent && c == '\n') { // auto indent the new line if (autoindent && c == '\n') { // auto indent the new line
// use indent of current/previous line if (newindent < 0) {
bol = indentcol < 0 ? p : prev_line(p); // use indent of previous line
len = indent_len(bol); bol = prev_line(p);
col = get_column(bol + len); len = indent_len(bol);
col = get_column(bol + len);
if (len && col == indentcol) { if (len && col == indentcol) {
// previous line was empty except for autoindent // previous line was empty except for autoindent
// move the indent to the current line // move the indent to the current line
memmove(bol + 1, bol, len); memmove(bol + 1, bol, len);
*bol = '\n'; *bol = '\n';
return p; return p;
}
} else {
// for 'O'/'cc' commands add indent before newly inserted NL
if (p != end - 1) // but not for 'cc' at EOF
p--;
col = newindent;
} }
if (indentcol < 0) if (col) {
p--; // open above, indent before newly inserted NL
if (len) {
// only record indent if in insert/replace mode or for // only record indent if in insert/replace mode or for
// the 'o'/'O' commands, which are switched to insert // the 'o'/'O'/'cc' commands, which are switched to
// mode early. // insert mode early.
indentcol = cmd_mode != 0 ? col : 0; indentcol = cmd_mode != 0 ? col : 0;
if (expandtab) { if (expandtab) {
ntab = 0; ntab = 0;
@ -2252,6 +2262,7 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
} }
#if ENABLE_FEATURE_VI_SETOPTS #if ENABLE_FEATURE_VI_SETOPTS
indentcol = 0; indentcol = 0;
# undef indentcol
#endif #endif
return p; return p;
} }
@ -4220,6 +4231,9 @@ static void do_cmd(int c)
case 'i': // i- insert before current char case 'i': // i- insert before current char
case KEYCODE_INSERT: // Cursor Key Insert case KEYCODE_INSERT: // Cursor Key Insert
dc_i: dc_i:
#if ENABLE_FEATURE_VI_SETOPTS
newindent = -1;
#endif
cmd_mode = 1; // start inserting cmd_mode = 1; // start inserting
undo_queue_commit(); // commit queue when cmd_mode changes undo_queue_commit(); // commit queue when cmd_mode changes
break; break;
@ -4262,7 +4276,8 @@ static void do_cmd(int c)
case 'O': // O- open an empty line above case 'O': // O- open an empty line above
dot_begin(); dot_begin();
#if ENABLE_FEATURE_VI_SETOPTS #if ENABLE_FEATURE_VI_SETOPTS
indentcol = -1; // special case: use indent of current line
newindent = get_column(dot + indent_len(dot));
#endif #endif
goto dc3; goto dc3;
case 'o': // o- open an empty line below case 'o': // o- open an empty line below
@ -4385,14 +4400,22 @@ static void do_cmd(int c)
if (buftype == WHOLE) { if (buftype == WHOLE) {
save_dot = p; // final cursor position is start of range save_dot = p; // final cursor position is start of range
p = begin_line(p); p = begin_line(p);
#if ENABLE_FEATURE_VI_SETOPTS
if (c == 'c') // special case: use indent of current line
newindent = get_column(p + indent_len(p));
#endif
q = end_line(q); q = end_line(q);
} }
dot = yank_delete(p, q, buftype, yf, ALLOW_UNDO); // delete word dot = yank_delete(p, q, buftype, yf, ALLOW_UNDO); // delete word
if (buftype == WHOLE) { if (buftype == WHOLE) {
if (c == 'c') { if (c == 'c') {
#if ENABLE_FEATURE_VI_SETOPTS
cmd_mode = 1; // switch to insert mode early
#endif
dot = char_insert(dot, '\n', ALLOW_UNDO_CHAIN); dot = char_insert(dot, '\n', ALLOW_UNDO_CHAIN);
// on the last line of file don't move to prev line // on the last line of file don't move to prev line,
if (dot != (end-1)) { // handled in char_insert() if autoindent is enabled
if (dot != (end-1) && !autoindent) {
dot_prev(); dot_prev();
} }
} else if (c == 'd') { } else if (c == 'd') {