commit ba47cb7edab3cf90c171e7ef5a37e7ece46cd5e3
parent 236d831d77d61e8de6bb7191fc0a7fb0743a5021
Author: zeertzjq <zeertzjq@outlook.com>
Date: Wed, 8 Oct 2025 06:51:15 +0800
vim-patch:9.1.1836: 'culopt' "screenline" not redrawn with line("w0") and :retab
Problem: 'cursorlineopt' "screenline" isn't redrawn when moving cursor
and then using line("w0") and :retab that does nothing.
Solution: Call redraw_for_cursorcolumn() when setting a valid w_virtcol
(zeertzjq).
closes: vim/vim#18506
https://github.com/vim/vim/commit/a0849143614e687a305b6195dd8724840786e372
Diffstat:
6 files changed, 70 insertions(+), 25 deletions(-)
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
@@ -62,8 +62,7 @@ int coladvance_force(colnr_T wcol)
curwin->w_valid &= ~VALID_VIRTCOL;
} else {
// Virtcol is valid
- curwin->w_valid |= VALID_VIRTCOL;
- curwin->w_virtcol = wcol;
+ set_valid_virtcol(curwin, wcol);
}
return rc;
}
@@ -83,8 +82,7 @@ int coladvance(win_T *wp, colnr_T wcol)
wp->w_valid &= ~VALID_VIRTCOL;
} else if (*(ml_get_buf(wp->w_buffer, wp->w_cursor.lnum) + wp->w_cursor.col) != TAB) {
// Virtcol is valid when not on a TAB
- wp->w_valid |= VALID_VIRTCOL;
- wp->w_virtcol = wcol;
+ set_valid_virtcol(curwin, wcol);
}
return rc;
}
diff --git a/src/nvim/move.c b/src/nvim/move.c
@@ -179,6 +179,15 @@ static void redraw_for_cursorcolumn(win_T *wp)
}
}
+/// Set wp->w_virtcol to a value ("vcol") that is already valid.
+/// Handles redrawing if wp->w_virtcol was previously invalid.
+void set_valid_virtcol(win_T *wp, colnr_T vcol)
+{
+ wp->w_virtcol = vcol;
+ redraw_for_cursorcolumn(wp);
+ wp->w_valid |= VALID_VIRTCOL;
+}
+
/// Calculates how much the 'listchars' "precedes" or 'smoothscroll' "<<<"
/// marker overlaps with buffer text for window "wp".
/// Parameter "extra2" should be the padding on the 2nd line, not the first
diff --git a/src/nvim/window.c b/src/nvim/window.c
@@ -6498,7 +6498,7 @@ void set_fraction(win_T *wp)
/// calculate the new scroll position.
/// TODO(vim): Ensure this also works with wrapped lines.
/// Requires a not fully visible cursor line to be allowed at the bottom of
-/// a window("zb"), probably only when 'smoothscroll' is also set.
+/// a window ("zb"), probably only when 'smoothscroll' is also set.
void win_fix_scroll(bool resize)
{
if (*p_spk == 'c') {
diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua
@@ -1065,25 +1065,6 @@ describe('CursorLine and CursorLineNr highlights', function()
{101: }{100:>>>}aaaaaaaaaaaa |
|
]])
-
- command('inoremap <F2> <Cmd>call cursor(1, 1)<CR>')
- feed('A')
- screen:expect([[
- {103:0 }øøøøøøøøøøøøøøøøøø|
- {101: }{100:>>>}{102:øøøøøøøøøøøø^ }|
- {101:1 }aaaaaaaaaaaaaaaaaa|
- {101: }{100:>>>}aaaaaaaaaaaa |
- {5:-- INSERT --} |
- ]])
-
- feed('<F2>')
- screen:expect([[
- {103:0 }{102:^øøøøøøøøøøøøøøøøøø}|
- {101: }{100:>>>}øøøøøøøøøøøø |
- {101:1 }aaaaaaaaaaaaaaaaaa|
- {101: }{100:>>>}aaaaaaaaaaaa |
- {5:-- INSERT --} |
- ]])
end)
-- oldtest: Test_cursorline_screenline_resize()
@@ -1123,6 +1104,47 @@ describe('CursorLine and CursorLineNr highlights', function()
]])
end)
+ -- oldtest: Test_cursorline_screenline_update()
+ it("'cursorlineopt' screenline is updated on various movements", function()
+ local screen = Screen.new(75, 8)
+ exec([[
+ func TestRetab()
+ let w = winwidth(0)
+ call cursor([1, w + 1, 0, w + 1])
+ call line('w0')
+ retab 8
+ endfunc
+
+ call setline(1, repeat('xyz ', 30))
+ set cursorline cursorlineopt=screenline tabstop=8
+ inoremap <F2> <Cmd>call cursor(1, 1)<CR>
+ inoremap <F3> <Cmd>call TestRetab()<CR>
+ ]])
+
+ feed('A')
+ screen:expect([[
+ xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz|
+ {21: xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz ^ }|
+ {1:~ }|*5
+ {5:-- INSERT --} |
+ ]])
+ feed('<F2>')
+ screen:expect([[
+ {21:^xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz}|
+ xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz |
+ {1:~ }|*5
+ {5:-- INSERT --} |
+ ]])
+ feed('<F3>')
+ screen:expect([[
+ xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz|
+ {21:^ xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz }|
+ {1:~ }|*5
+ {5:-- INSERT --} |
+ ]])
+ feed('<Esc>')
+ end)
+
-- oldtest: Test_cursorline_after_yank()
it('always updated. vim-patch:8.1.0849', function()
local screen = Screen.new(50, 5)
diff --git a/test/old/testdir/test_conceal.vim b/test/old/testdir/test_conceal.vim
@@ -394,6 +394,10 @@ func Test_conceal_mouse_click()
syn match Concealed "this" conceal
hi link Concealed Search
+ " Nvim: need to redraw before processing every key, because mouse clicks set
+ " w_redr_type, which prevent using vcols[].
+ lua _G.NS = vim.on_key(function() vim.cmd.redraw() end)
+
" Test with both 'nocursorline' and 'cursorline', as they use two different
" code paths to set virtual columns for the cells to clear.
for cul in [v:false, v:true]
@@ -550,6 +554,8 @@ func Test_conceal_mouse_click()
setlocal number& signcolumn&
endfor
+ lua vim.on_key(nil, _G.NS); _G.NS = nil
+
call CloseWindow()
set mouse&
endfunc
diff --git a/test/old/testdir/test_cursorline.vim b/test/old/testdir/test_cursorline.vim
@@ -296,9 +296,17 @@ func Test_cursorline_screenline_update()
CheckScreendump
let lines =<< trim END
+ func TestRetab()
+ let w = winwidth(0)
+ call cursor([1, w + 1, 0, w + 1])
+ call line('w0')
+ retab 8
+ endfunc
+
call setline(1, repeat('xyz ', 30))
- set cursorline cursorlineopt=screenline
+ set cursorline cursorlineopt=screenline tabstop=8
inoremap <F2> <Cmd>call cursor(1, 1)<CR>
+ inoremap <F3> <Cmd>call TestRetab()<CR>
END
call writefile(lines, 'Xcul_screenline', 'D')
@@ -307,6 +315,8 @@ func Test_cursorline_screenline_update()
call VerifyScreenDump(buf, 'Test_cursorline_screenline_1', {})
call term_sendkeys(buf, "\<F2>")
call VerifyScreenDump(buf, 'Test_cursorline_screenline_2', {})
+ call term_sendkeys(buf, "\<F3>")
+ call VerifyScreenDump(buf, 'Test_cursorline_screenline_3', {})
call term_sendkeys(buf, "\<Esc>")
call StopVimInTerminal(buf)