From 8fccb26cd3beccaf2f9664a928b99a8574f1cbd3 Mon Sep 17 00:00:00 2001 From: Matei Stroia Date: Wed, 20 May 2026 14:56:39 +0300 Subject: [PATCH] fix(fold): virtual lines duplicate foldopen (#39891) Problem: Virtual lines above a line where a fold starts show `foldopen` in `foldcolumn`. Solution: Check if the line below the virtual one is inside a fold that starts higher up or if it's the start of a fold. In the latter case, don't show anything in `foldcolumn` for the virtual line. refactor: lint (cherry picked from commit 526ae1cc1b432d7f1d9a412be86ac28c809f77eb) --- src/nvim/drawline.c | 10 +++- test/functional/ui/fold_spec.lua | 98 ++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index c8a0349e94..42188b52fb 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -486,7 +486,15 @@ static void draw_foldcolumn(win_T *wp, winlinevars_T *wlv) int fdc = compute_foldcolumn(wp, 0); if (fdc > 0) { int attr = win_hl_attr(wp, use_cursor_line_highlight(wp, wlv->lnum) ? HLF_CLF : HLF_FC); - fill_foldcolumn(wp, wlv->foldinfo, wlv->lnum, attr, fdc, &wlv->off, NULL, NULL); + // Only draw 'foldcolumn' for filler line if lnum is inside a fold that + // starts higher up. We don't want to show 'foldopen' or 'foldclose' twice. + foldinfo_T fi; + if (wlv->filler_todo <= 0 || wlv->foldinfo.fi_lnum < wlv->lnum) { + fi = wlv->foldinfo; + } else { + fi = (foldinfo_T){ 0 }; + } + fill_foldcolumn(wp, fi, wlv->lnum, attr, fdc, &wlv->off, NULL, NULL); } } diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua index aaff97fbe4..3b25f7cb7f 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -2257,6 +2257,104 @@ describe('folded lines', function() end end) + it('virt_lines above line where fold begins do not duplicate foldcolumn', function() + fn.setline(1, 'line 1') + fn.setline(2, 'line 2') + fn.setline(3, 'line 3') + fn.setline(4, 'line 4') + + local ns = api.nvim_create_namespace('ns') + api.nvim_buf_set_extmark( + 0, + ns, + 1, + 0, + { virt_lines_above = true, virt_lines = { { { 'above line 2' } } } } + ) + api.nvim_buf_set_extmark(0, ns, 2, 0, { virt_lines = { { { 'below line 3' } } } }) + + command('set foldcolumn=1') + feed('jzfl') + feed('2jzfl') + if multigrid then + screen:expect([[ + ## grid 1 + [2:---------------------------------------------]|*7 + [3:---------------------------------------------]| + ## grid 2 + {7: }line 1 | + {7: }above line 2 | + {7:-}line 2 | + {7: }line 3 | + {7: }below line 3 | + {7:-}^line 4 | + {1:~ }| + ## grid 3 + | + ]]) + else + screen:expect([[ + {7: }line 1 | + {7: }above line 2 | + {7:-}line 2 | + {7: }line 3 | + {7: }below line 3 | + {7:-}^line 4 | + {1:~ }| + | + ]]) + end + end) + + it('foldcolumn is not interrupted when virt_lines are inside a fold', function() + fn.setline(1, 'line 1') + fn.setline(2, 'line 2') + fn.setline(3, 'line 3') + fn.setline(4, 'line 4') + + local ns = api.nvim_create_namespace('ns') + api.nvim_buf_set_extmark(0, ns, 1, 0, { virt_lines = { { { 'below line 2', '' } } } }) + api.nvim_buf_set_extmark( + 0, + ns, + 2, + 0, + { virt_lines_above = true, virt_lines = { { { 'above line 3', '' } } } } + ) + + command('set foldcolumn=1') + feed('zf2j') + feed('zo') + if multigrid then + screen:expect([[ + ## grid 1 + [2:---------------------------------------------]|*7 + [3:---------------------------------------------]| + ## grid 2 + {7:-}^line 1 | + {7:│}line 2 | + {7:│}below line 2 | + {7:│}above line 3 | + {7:│}line 3 | + {7: }line 4 | + {1:~ }| + ## grid 3 + | + ]]) + else + screen:expect([[ + {7:-}^line 1 | + {7:│}line 2 | + {7:│}below line 2 | + {7:│}above line 3 | + {7:│}line 3 | + {7: }line 4 | + {1:~ }| + | + ]]) + end + end) + it('Folded and Visual highlights are combined #19691', function() command('hi! Visual guifg=NONE guibg=Red') insert([[