summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorJaehwang Jung <tomtomjhj@gmail.com>2026-04-19 02:16:31 +0900
committergithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>2026-04-20 02:17:54 +0000
commit898ccbc68a6e5c1983a565abcc87dceec01325bb (patch)
tree2170fc3e774ad5d7e5f053540ac2ecada69ac901
parent9966afbc9df4631ec3ed9464cf0d3c52e59d1702 (diff)
fix(smoothscroll): crash when resizing to textoff with showbreak
vim-patch:9.2.0362: division by zero with smoothscroll and small windows Problem: Resizing a smoothscrolled wrapped window to its textoff width with 'showbreak' can leave wrapped continuation lines with zero text width. win_lbr_chartabsize() still runs the partial max_head_vcol calculation in that state and divides by width2, crashing during redraw. Solution: Skip that partial head calculation when the wrapped continuation width is zero, matching the other width2 guards in charset.c (Jaehwang Jung) closes: vim/vim#20012 AI-assisted: Codex https://github.com/vim/vim/commit/0e31fb024c846e36bb0d26d01ff179a0d1b3eae4 (cherry picked from commit 79a7a4abe11f9b678cbf7073a4464fbe8631d57a)
-rw-r--r--src/nvim/plines.c2
-rw-r--r--test/functional/legacy/scroll_opt_spec.lua18
-rw-r--r--test/old/testdir/test_scroll_opt.vim38
3 files changed, 57 insertions, 1 deletions
diff --git a/src/nvim/plines.c b/src/nvim/plines.c
index 389af69849..3af6bf6c1c 100644
--- a/src/nvim/plines.c
+++ b/src/nvim/plines.c
@@ -269,7 +269,7 @@ CharSize charsize_regular(CharsizeArg *csarg, char *const cur, colnr_T const vco
if (max_head_vcol == 0 || vcol + size + added < max_head_vcol) {
head += cnt * head_mid;
- } else if (max_head_vcol > vcol + head_prev + prev_rem) {
+ } else if (width2 > 0 && max_head_vcol > vcol + head_prev + prev_rem) {
head += (max_head_vcol - (vcol + head_prev + prev_rem)
+ width2 - 1) / width2 * head_mid;
} else if (max_head_vcol < 0) {
diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua
index 427a69ca01..16f061a5c7 100644
--- a/test/functional/legacy/scroll_opt_spec.lua
+++ b/test/functional/legacy/scroll_opt_spec.lua
@@ -1127,6 +1127,24 @@ describe('smoothscroll', function()
screen:expect(screen_l_top)
end)
+ -- oldtest: Test_smoothscroll_textoff_showbreak()
+ it('does not crash when resizing to textoff with showbreak', function()
+ exec([[
+ set noswapfile scrolloff=0
+
+ call setline(1, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
+ set number wrap smoothscroll showbreak=>
+ vsplit
+
+ let textoff = getwininfo(win_getid())[0].textoff
+ execute "normal! 0\<C-E>"
+ redraw
+ execute 'vertical resize' textoff
+ redraw
+ ]])
+ assert_alive()
+ end)
+
it('works with virt_lines above and below', function()
screen:try_resize(55, 7)
exec([=[
diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim
index 4db7db2a49..7c65afa38b 100644
--- a/test/old/testdir/test_scroll_opt.vim
+++ b/test/old/testdir/test_scroll_opt.vim
@@ -1443,4 +1443,42 @@ func Test_smoothscroll_listchars_eol()
bwipe!
endfunc
+
+" Resizing to "textoff" after 'smoothscroll' skips part of a wrapped line must
+" not crash.
+func Test_smoothscroll_textoff_showbreak()
+ CheckOption smoothscroll
+
+ let donefile = 'XTest_crash_textoff_showbreak_done'
+ defer delete(donefile)
+ let lines =<< trim END
+ set noswapfile
+
+ set scrolloff=0
+ set lines=12 columns=40
+
+ call setline(1, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
+ set number wrap smoothscroll showbreak=>
+ vsplit
+
+ let textoff = getwininfo(win_getid())[0].textoff
+ execute "normal! 0\<C-E>"
+ redraw
+ execute 'vertical resize' textoff
+ redraw
+ call writefile(['done'], 'XTest_crash_textoff_showbreak_done')
+ END
+ call writefile(lines, 'XTest_crash_textoff_showbreak', 'D')
+
+ let buf = 0
+ let buf = term_start([GetVimProg(), '--clean'], #{term_rows: 24, term_cols: 80})
+ call TermWait(buf, 200)
+ call term_sendkeys(buf, ":source XTest_crash_textoff_showbreak\<CR>")
+ call WaitForAssert({-> assert_true(filereadable(donefile))})
+ let status = term_getstatus(buf)
+ call assert_equal('running', status)
+ call assert_true(filereadable(donefile))
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab