diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 42188b52fb..a903c148a9 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -486,26 +486,37 @@ 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); - // 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); + bool is_virt = wlv->filler_todo > 0; + fill_foldcolumn(wp, wlv->foldinfo, wlv->lnum, attr, fdc, is_virt, &wlv->off, NULL, NULL); + } +} + +/// Get foldcolumn char based on line fold level and column, either `foldsep`, `foldinner`, +/// or an overflow indicator. `foldopen` and `foldclosed` are set in `fill_foldcolumn`. +/// @param first_level Lowest fold level displayed on line +/// @param i Column index +static inline schar_T foldcolumn_sep_char(int first_level, int i, win_T *wp) +{ + if (first_level == 1) { + return wp->w_p_fcs_chars.foldsep; + } else if (wp->w_p_fcs_chars.foldinner != NUL) { + return wp->w_p_fcs_chars.foldinner; + } else if (first_level + i <= 9) { + return schar_from_ascii('0' + first_level + i); + } else { + return schar_from_ascii('>'); } } /// Draw the foldcolumn or fill "out_buffer". Assume monocell characters. /// /// @param fdc Current width of the foldcolumn +/// @param is_virt Whether the line is a filler line (diff or virtual) /// @param[out] wlv_off Pointer to linebuf offset, incremented for default column /// @param[out] out_buffer Char array to fill, only used for 'statuscolumn' /// @param[out] out_vcol vcol array to fill, only used for 'statuscolumn' -void fill_foldcolumn(win_T *wp, foldinfo_T foldinfo, linenr_T lnum, int attr, int fdc, int *wlv_off, - colnr_T *out_vcol, schar_T *out_buffer) +void fill_foldcolumn(win_T *wp, foldinfo_T foldinfo, linenr_T lnum, int attr, int fdc, bool is_virt, + int *wlv_off, colnr_T *out_vcol, schar_T *out_buffer) { bool closed = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0; int level = foldinfo.fi_level; @@ -523,14 +534,20 @@ void fill_foldcolumn(win_T *wp, foldinfo_T foldinfo, linenr_T lnum, int attr, in symbol = wp->w_p_fcs_chars.foldclosed; } else if (foldinfo.fi_lnum == lnum && first_level + i >= foldinfo.fi_low_level) { symbol = wp->w_p_fcs_chars.foldopen; - } else if (first_level == 1) { - symbol = wp->w_p_fcs_chars.foldsep; - } else if (wp->w_p_fcs_chars.foldinner != NUL) { - symbol = wp->w_p_fcs_chars.foldinner; - } else if (first_level + i <= 9) { - symbol = schar_from_ascii('0' + first_level + i); } else { - symbol = schar_from_ascii('>'); + symbol = foldcolumn_sep_char(first_level, i, wp); + } + + // We don't want to show `foldopen` or `foldclose` twice, so we compute + // the fold level of `lnum - 1` and reuse the logic from above. + if (is_virt && foldinfo.fi_level != 0 && foldinfo.fi_lnum == lnum) { + int outer_level = MAX(foldinfo.fi_low_level - 1, 0); + int outer_first_level = MAX(outer_level - fdc + 1, 1); + if (i >= outer_level) { + symbol = schar_from_ascii(' '); + } else { + symbol = foldcolumn_sep_char(outer_first_level, i, wp); + } } int vcol = i >= level ? -1 : (i == closedcol - 1 && closed) ? -2 : -3; @@ -1354,6 +1371,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b // Draw the 'statuscolumn' if option is set. statuscol.draw = true; statuscol.sattrs = wlv.sattrs; + statuscol.lnum = lnum; statuscol.foldinfo = foldinfo; statuscol.width = win_col_off(wp) - (wp == cmdwin_win); statuscol.sign_cul_id = use_cursor_line_highlight(wp, lnum) ? wlv.sign_cul_attr : 0; diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 9e5361927f..1ac013f43b 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -19,6 +19,7 @@ #include "nvim/eval.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/vars.h" +#include "nvim/eval_defs.h" #include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" @@ -1595,7 +1596,8 @@ stcsign: if (fdc > 0) { schar_T fold_buf[9]; - fill_foldcolumn(wp, stcp->foldinfo, lnum, 0, fdc, NULL, stcp->fold_vcol, fold_buf); + fill_foldcolumn(wp, stcp->foldinfo, stcp->lnum, 0, fdc, get_vim_var_nr(VV_VIRTNUM) < 0, + NULL, stcp->fold_vcol, fold_buf); stl_items[curitem].minwid = -(use_cursor_line_highlight(wp, lnum) ? HLF_CLF : HLF_FC); size_t buflen = 0; // TODO(bfredl): this is very backwards. we must support schar_T diff --git a/src/nvim/statusline_defs.h b/src/nvim/statusline_defs.h index ff55845669..c76dbfb3df 100644 --- a/src/nvim/statusline_defs.h +++ b/src/nvim/statusline_defs.h @@ -3,6 +3,7 @@ #include #include "nvim/fold_defs.h" +#include "nvim/pos_defs.h" #include "nvim/sign_defs.h" /// 'statusline' item flags @@ -101,6 +102,7 @@ struct stl_item { /// Struct to hold info for 'statuscolumn' typedef struct { int width; ///< width of the status column + linenr_T lnum; ///< buffer line being drawn int sign_cul_id; ///< cursorline sign highlight id bool draw; ///< whether to draw the statuscolumn stl_hlrec_t *hlrec; ///< highlight groups diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua index 3b25f7cb7f..70e005f37f 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -2304,6 +2304,10 @@ describe('folded lines', function() | ]]) end + + -- `foldcolumn` in `statuscolumn` should be identical + command('set statuscolumn=%C') + screen:expect_unchanged() end) it('foldcolumn is not interrupted when virt_lines are inside a fold', function() @@ -2353,6 +2357,129 @@ describe('folded lines', function() | ]]) end + + command('set statuscolumn=%C') + screen:expect_unchanged() + end) + + it('foldcolumn for a virt_line above a nested fold shows correct fold level', 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('1,4fold | 1,4foldopen') + command('3,4fold | 3,4foldopen ') + command('set foldcolumn=1') + 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:2}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:2}line 4 | + {1:~ }| + | + ]]) + end + + command('set statuscolumn=%C') + screen:expect_unchanged() + command('set statuscolumn=') + + command('set foldcolumn=2') + 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 + + command('set statuscolumn=%C') + screen:expect_unchanged() + command('set statuscolumn=') + + command('2,4fold | 2,4foldopen') + 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:2-}line 3 | + {7:23}line 4 | + {1:~ }| + ## grid 3 + | + ]]) + else + screen:expect([[ + {7:- }^line 1 | + {7:│-}line 2 | + {7:││}below line 2 | + {7:││}above line 3 | + {7:2-}line 3 | + {7:23}line 4 | + {1:~ }| + | + ]]) + end + + command('set statuscolumn=%C') + screen:expect_unchanged() end) it('Folded and Visual highlights are combined #19691', function()