patch 9.2.0510: setline() mapping may trigger autoindent
Commit:
https://github.com/vim/vim/commit/e3dedac77b2076210e7027b14a5927622434f014
Author: glepnir <
gleph...@gmail.com>
Date: Fri May 22 17:59:23 2026 +0000
patch 9.2.0510: setline() mapping may trigger autoindent
Problem: setline() insert mode mapping may trigger autoindent,
corrupting the newly inserted line content (Evgeni Chasnovski)
Solution: Only strip autoindent whitespace when the rest of the line is
all whitespace (glepnir).
fixes: #19363
closes: #20290
Signed-off-by: glepnir <
gleph...@gmail.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/src/edit.c b/src/edit.c
index 1db9c1307..512c0b23c 100644
--- a/src/edit.c
+++ b/src/edit.c
@@ -2613,34 +2613,45 @@ stop_insert(
{
pos_T tpos = curwin->w_cursor;
colnr_T prev_col = end_insert_pos->col;
+ colnr_T strip_col;
curwin->w_cursor = *end_insert_pos;
check_cursor_col(); // make sure it is not past the line
- for (;;)
- {
- if (gchar_cursor() == NUL && curwin->w_cursor.col > 0)
- --curwin->w_cursor.col;
- cc = gchar_cursor();
- if (!VIM_ISWHITE(cc))
- break;
- if (del_char(TRUE) == FAIL)
- break; // should not happen
- }
- if (curwin->w_cursor.lnum != tpos.lnum)
- curwin->w_cursor = tpos;
- else if (curwin->w_cursor.col < prev_col)
+
+ // Where the loop would actually start (back up if on NUL).
+ strip_col = curwin->w_cursor.col;
+ if (gchar_cursor() == NUL && strip_col > 0)
+ --strip_col;
+
+ // Don't strip if non-whitespace follows: setline() from a <Cmd>
+ // mapping or CursorHoldI autocmd may have inserted content.
+ if (*skipwhite(ml_get_curline() + strip_col) == NUL)
{
- // reset tpos, could have been invalidated in the loop above
- tpos = curwin->w_cursor;
- tpos.col++;
- if (cc != NUL && gchar_pos(&tpos) == NUL)
- ++curwin->w_cursor.col; // put cursor back on the NUL
- }
+ curwin->w_cursor.col = strip_col;
+ for (;;)
+ {
+ cc = gchar_cursor();
+ if (!VIM_ISWHITE(cc))
+ break;
+ if (del_char(TRUE) == FAIL)
+ break; // should not happen
+ }
+ if (curwin->w_cursor.lnum != tpos.lnum)
+ curwin->w_cursor = tpos;
+ else if (curwin->w_cursor.col < prev_col)
+ {
+ // reset tpos, could have been invalidated in the loop above
+ tpos = curwin->w_cursor;
+ tpos.col++;
+ if (cc != NUL && gchar_pos(&tpos) == NUL)
+ ++curwin->w_cursor.col; // put cursor back on the NUL
+ }
- // <C-S-Right> may have started Visual mode, adjust the position for
- // deleted characters.
- if (VIsual_active)
- check_visual_pos();
+ // <C-S-Right> may have started Visual mode, adjust the position for
+ // deleted characters.
+ if (VIsual_active)
+ check_visual_pos();
+ }
}
}
did_ai = FALSE;
diff --git a/src/testdir/test_edit.vim b/src/testdir/test_edit.vim
index 196933f7d..6511e178c 100644
--- a/src/testdir/test_edit.vim
+++ b/src/testdir/test_edit.vim
@@ -2458,4 +2458,28 @@ func Test_edit_CAR_with_completion()
bw!
endfunc
+func Test_autoindent_no_strip_after_cmd_setline()
+ new
+ setlocal autoindent
+ inoremap <buffer> <F2> <Cmd>call setline('.', 'v v')<CR><Cmd>call cursor(line('.'), 2)<CR>
+ call feedkeys("Go\<F2>\<Esc>", 'tx')
+ call assert_equal('v v', getline(2))
+ bwipe!
+endfunc
+
+func Test_autoindent_no_strip_after_cursorholdi()
+ CheckFeature timers
+ new
+ setlocal autoindent
+ set updatetime=50
+ au CursorHoldI <buffer> call setline('.', 'v v')
+ call setline(1, ' x')
+ call cursor(1, 2)
+ call timer_start(120, {-> feedkeys("\<Esc>", 't')})
+ call feedkeys("o", 'tx!')
+ call assert_equal('v v', getline(2))
+ set updatetime&
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index 7b80b546f..7b6e33045 100644
--- a/src/version.c
+++ b/src/version.c
@@ -729,6 +729,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 510,
/**/
509,
/**/