vim-patch:9.2.0510: setline() mapping may trigger autoindent (#39961)

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:  vim/vim#19363
closes: vim/vim#20290

e3dedac77b

Co-authored-by: glepnir <glephunter@gmail.com>
This commit is contained in:
zeertzjq
2026-05-23 08:57:36 +08:00
committed by GitHub
parent 7a1bad27ca
commit c8dbb04da9
2 changed files with 58 additions and 25 deletions

View File

@@ -2295,33 +2295,42 @@ static void stop_insert(pos_T *end_insert_pos, int esc, int nomove)
curwin->w_cursor = *end_insert_pos;
check_cursor_col(curwin); // make sure it is not past the line
while (true) {
if (gchar_cursor() == NUL && curwin->w_cursor.col > 0) {
curwin->w_cursor.col--;
}
cc = gchar_cursor();
if (!ascii_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
}
// Where the loop would actually start (back up if NUL).
colnr_T strip_col = curwin->w_cursor.col;
if (gchar_cursor() == NUL && strip_col > 0) {
strip_col--;
}
// <C-S-Right> may have started Visual mode, adjust the position for
// deleted characters.
if (VIsual_active) {
check_visual_pos();
// Don't strip if non-whitespace follows: setline() from a <Cmd>
// mapping or CursorHoldI autocmd may have inserted content.
if (*skipwhite(get_cursor_line_ptr() + strip_col) == NUL) {
curwin->w_cursor.col = strip_col;
while (true) {
cc = gchar_cursor();
if (!ascii_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();
}
}
}
}

View File

@@ -2474,4 +2474,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