From ee986ee0449b828ca64bf7d4c69624596aab8319 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 25 May 2023 22:14:12 +0800 Subject: [PATCH] fix(folds): combined Folded and Visual highlights (#23752) Also combine high-priority CursorLine with Folded. --- src/nvim/drawline.c | 117 ++++++++++++----------- test/functional/ui/decorations_spec.lua | 39 +++++++- test/functional/ui/fold_spec.lua | 37 +++++-- test/functional/ui/statuscolumn_spec.lua | 6 +- 4 files changed, 129 insertions(+), 70 deletions(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 54da38a6ff..f3332dc8a2 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -1049,7 +1049,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, static char *at_end_str = ""; // used for p_extra when displaying curwin->w_p_lcs_chars.eol // at end-of-line - bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0; + const bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0; int saved_attr2 = 0; // char_attr saved for n_attr int n_attr3 = 0; // chars with overruling special attr @@ -1075,6 +1075,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, int vcol_save_attr = 0; // saved attr for 'cursorcolumn' int syntax_attr = 0; // attributes desired by syntax bool has_syntax = false; // this buffer has syntax highl. + int folded_attr = 0; // attributes for folded line int save_did_emsg; int eol_hl_off = 0; // 1 if highlighted char after EOL bool draw_color_col = false; // highlight colorcolumn @@ -1159,7 +1160,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.vcol_sbr = -1; buf_T *buf = wp->w_buffer; - bool end_fill = (lnum == buf->b_ml.ml_line_count + 1); + const bool end_fill = (lnum == buf->b_ml.ml_line_count + 1); if (!number_only) { // To speed up the loop below, set extra_check when there is linebreak, @@ -1760,7 +1761,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, && wlv.col == win_col_offset && wlv.n_extra == 0 && wlv.row == startrow + wlv.filler_lines) { - wlv.char_attr = win_hl_attr(wp, HLF_FL); + wlv.char_attr = folded_attr = win_hl_attr(wp, HLF_FL); linenr_T lnume = lnum + foldinfo.fi_lines - 1; memset(buf_fold, ' ', FOLD_TEXT_LEN); @@ -1802,7 +1803,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } int extmark_attr = 0; - if (wlv.draw_state == WL_LINE && !has_fold + if (wlv.draw_state == WL_LINE && (area_highlighting || has_spell || extra_check)) { // handle Visual or match highlighting in this line if (wlv.vcol == wlv.fromcol @@ -1821,63 +1822,65 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, area_active = false; } - if (has_decor && v >= 0) { - bool selected = (area_active || (area_highlighting && noinvcur - && wlv.vcol == wp->w_virtcol)); - extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, - selected, &decor_state); + if (!has_fold) { + if (has_decor && v >= 0) { + bool selected = (area_active || (area_highlighting && noinvcur + && wlv.vcol == wp->w_virtcol)); + extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, + selected, &decor_state); - bool do_save = false; - handle_inline_virtual_text(wp, &wlv, v, &do_save); - if (do_save) { - // restore search_attr and area_attr when n_extra is down to zero - // TODO(bfredl): this is ugly as fuck. look if we can do this some other way. - saved_search_attr = search_attr; - saved_area_attr = area_attr; - saved_search_attr_from_match = search_attr_from_match; - search_attr_from_match = false; - search_attr = 0; - area_attr = 0; - extmark_attr = 0; - n_skip = 0; + bool do_save = false; + handle_inline_virtual_text(wp, &wlv, v, &do_save); + if (do_save) { + // restore search_attr and area_attr when n_extra is down to zero + // TODO(bfredl): this is ugly as fuck. look if we can do this some other way. + saved_search_attr = search_attr; + saved_area_attr = area_attr; + saved_search_attr_from_match = search_attr_from_match; + search_attr_from_match = false; + search_attr = 0; + area_attr = 0; + extmark_attr = 0; + n_skip = 0; + } } - } - if (wlv.n_extra == 0) { - // Check for start/end of 'hlsearch' and other matches. - // After end, check for start/end of next match. - // When another match, have to check for start again. - v = (ptr - line); - search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &screen_search_hl, - &has_match_conc, &match_conc, lcs_eol_one, - &on_last_col, &search_attr_from_match); - ptr = line + v; // "line" may have been changed + if (wlv.n_extra == 0) { + // Check for start/end of 'hlsearch' and other matches. + // After end, check for start/end of next match. + // When another match, have to check for start again. + v = (ptr - line); + search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &screen_search_hl, + &has_match_conc, &match_conc, lcs_eol_one, + &on_last_col, &search_attr_from_match); + ptr = line + v; // "line" may have been changed - // Do not allow a conceal over EOL otherwise EOL will be missed - // and bad things happen. - if (*ptr == NUL) { - has_match_conc = 0; + // Do not allow a conceal over EOL otherwise EOL will be missed + // and bad things happen. + if (*ptr == NUL) { + has_match_conc = 0; + } } - } - if (wlv.diff_hlf != (hlf_T)0) { - // When there is extra text (eg: virtual text) it gets the - // diff highlighting for the line, but not for changed text. - if (wlv.diff_hlf == HLF_CHD && ptr - line >= change_start - && wlv.n_extra == 0) { - wlv.diff_hlf = HLF_TXD; // changed text - } - if (wlv.diff_hlf == HLF_TXD && ((ptr - line > change_end && wlv.n_extra == 0) - || (wlv.n_extra > 0 && wlv.extra_for_extmark))) { - wlv.diff_hlf = HLF_CHD; // changed line - } - wlv.line_attr = win_hl_attr(wp, (int)wlv.diff_hlf); - // Overlay CursorLine onto diff-mode highlight. - if (wlv.cul_attr) { - wlv.line_attr = 0 != wlv.line_attr_lowprio // Low-priority CursorLine - ? hl_combine_attr(hl_combine_attr(wlv.cul_attr, wlv.line_attr), - hl_get_underline()) - : hl_combine_attr(wlv.line_attr, wlv.cul_attr); + if (wlv.diff_hlf != (hlf_T)0) { + // When there is extra text (eg: virtual text) it gets the + // diff highlighting for the line, but not for changed text. + if (wlv.diff_hlf == HLF_CHD && ptr - line >= change_start + && wlv.n_extra == 0) { + wlv.diff_hlf = HLF_TXD; // changed text + } + if (wlv.diff_hlf == HLF_TXD && ((ptr - line > change_end && wlv.n_extra == 0) + || (wlv.n_extra > 0 && wlv.extra_for_extmark))) { + wlv.diff_hlf = HLF_CHD; // changed line + } + wlv.line_attr = win_hl_attr(wp, (int)wlv.diff_hlf); + // Overlay CursorLine onto diff-mode highlight. + if (wlv.cul_attr) { + wlv.line_attr = 0 != wlv.line_attr_lowprio // Low-priority CursorLine + ? hl_combine_attr(hl_combine_attr(wlv.cul_attr, wlv.line_attr), + hl_get_underline()) + : hl_combine_attr(wlv.line_attr, wlv.cul_attr); + } } } @@ -1908,6 +1911,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.char_attr = 0; } } + + if (folded_attr != 0) { + wlv.char_attr = hl_combine_attr(folded_attr, wlv.char_attr); + } } // Get the next character to put on the screen. diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index e430865df6..b301ed8353 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -10,6 +10,7 @@ local expect_events = helpers.expect_events local meths = helpers.meths local curbufmeths = helpers.curbufmeths local command = helpers.command +local assert_alive = helpers.assert_alive describe('decorations providers', function() local screen @@ -80,7 +81,7 @@ describe('decorations providers', function() local ns2 = api.nvim_create_namespace "ns2" api.nvim_set_decoration_provider(ns2, {}) ]]) - helpers.assert_alive() + assert_alive() end) it('leave a trace', function() @@ -1092,7 +1093,7 @@ describe('extmark decorations', function() {1:~ }| | ]]} - helpers.assert_alive() + assert_alive() end) it('conceal #19007', function() @@ -1258,6 +1259,9 @@ describe('decorations: inline virtual text', function() [13] = {reverse = true}; [14] = {foreground = Screen.colors.SlateBlue, background = Screen.colors.LightMagenta}; [15] = {bold = true, reverse = true}; + [16] = {foreground = Screen.colors.Red}; + [17] = {background = Screen.colors.LightGrey, foreground = Screen.colors.DarkBlue}; + [18] = {background = Screen.colors.LightGrey, foreground = Screen.colors.Red}; } ns = meths.create_namespace 'test' @@ -1971,6 +1975,37 @@ bbbbbbb]]) ]], }) end) + + it('does not crash at column 0 when folded in a wide window', function() + screen:try_resize(82, 4) + command('hi! CursorLine guibg=NONE guifg=Red gui=NONE') + command('set cursorline') + insert([[ + aaaaa + bbbbb + ccccc]]) + meths.buf_set_extmark(0, ns, 0, 0, { virt_text = {{'foo'}}, virt_text_pos = 'inline' }) + screen:expect{grid=[[ + fooaaaaa | + bbbbb | + {16:cccc^c }| + | + ]]} + command('1,2fold') + screen:expect{grid=[[ + {17:+-- 2 lines: aaaaa·······························································}| + {16:cccc^c }| + {1:~ }| + | + ]]} + feed('k') + screen:expect{grid=[[ + {18:^+-- 2 lines: aaaaa·······························································}| + ccccc | + {1:~ }| + | + ]]} + end) end) describe('decorations: virtual lines', function() diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua index 2afe27ecc7..520979a2c2 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -42,9 +42,10 @@ describe("folded lines", function() [9] = {bold = true, foreground = Screen.colors.Brown}, [10] = {background = Screen.colors.LightGrey, underline = true}, [11] = {bold=true}, - [12] = {background = Screen.colors.Grey90, underline = true}, - [13] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey, underline = true}, - [14] = {background = Screen.colors.LightGray}, + [12] = {foreground = Screen.colors.Red}, + [13] = {foreground = Screen.colors.Red, background = Screen.colors.LightGrey}, + [14] = {background = Screen.colors.Red}, + [15] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.Red}, }) end) @@ -88,10 +89,9 @@ describe("folded lines", function() end end) - it("foldcolumn highlighted with CursorLineFold when 'cursorline' is set", function() + local function test_folded_cursorline() command("set number cursorline foldcolumn=2") command("hi link CursorLineFold Search") - command("hi! CursorLine gui=underline guibg=Grey90") insert(content1) feed("ggzf3jj") if multigrid then @@ -239,6 +239,22 @@ describe("folded lines", function() | ]]) end + end + + describe("when 'cursorline' is set", function() + it('with high-priority CursorLine', function() + command("hi! CursorLine guibg=NONE guifg=Red gui=NONE") + test_folded_cursorline() + end) + + it('with low-priority CursorLine', function() + command("hi! CursorLine guibg=NONE guifg=NONE gui=underline") + local attrs = screen:get_default_attr_ids() + attrs[12] = {underline = true} + attrs[13] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey, underline = true} + screen:set_default_attr_ids(attrs) + test_folded_cursorline() + end) end) it("work with spell", function() @@ -2017,7 +2033,8 @@ describe("folded lines", function() end end) - it('Folded highlight does not disappear in Visual selection #19691', function() + it('Folded and Visual highlights are combined #19691', function() + command('hi! Visual guibg=Red') insert([[ " foo " {{{1 @@ -2044,9 +2061,9 @@ describe("folded lines", function() [3:---------------------------------------------]| ## grid 2 {14:" fo}o | - {5:+-- 3 lines: "······························}| + {15:+-- }{5: 3 lines: "······························}| {14:" ba}r | - {5:+-- 3 lines: "······························}| + {15:+-- }{5: 3 lines: "······························}| {14:" b}^az | {1:~ }| {1:~ }| @@ -2056,9 +2073,9 @@ describe("folded lines", function() else screen:expect([[ {14:" fo}o | - {5:+-- 3 lines: "······························}| + {15:+-- }{5: 3 lines: "······························}| {14:" ba}r | - {5:+-- 3 lines: "······························}| + {15:+-- }{5: 3 lines: "······························}| {14:" b}^az | {1:~ }| {1:~ }| diff --git a/test/functional/ui/statuscolumn_spec.lua b/test/functional/ui/statuscolumn_spec.lua index f349b182c9..c218bd8fd6 100644 --- a/test/functional/ui/statuscolumn_spec.lua +++ b/test/functional/ui/statuscolumn_spec.lua @@ -196,10 +196,10 @@ describe('statuscolumn', function() [2] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGrey}, [3] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey}, [4] = {bold = true, foreground = Screen.colors.Brown}, - [5] = {background = Screen.colors.Grey90, underline = true}, - [6] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey, underline = true}, + [5] = {foreground = Screen.colors.Red}, + [6] = {foreground = Screen.colors.Red, background = Screen.colors.LightGrey}, }) - command('hi! CursorLine gui=underline guibg=Grey90') + command('hi! CursorLine guifg=Red guibg=NONE') screen:expect([[ {1: 4│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {1: │ }a |