diff options
-rw-r--r-- | runtime/lua/vim/treesitter/highlighter.lua | 3 | ||||
-rw-r--r-- | src/nvim/api/extmark.c | 16 | ||||
-rw-r--r-- | src/nvim/api/keysets.lua | 1 | ||||
-rw-r--r-- | src/nvim/decoration.c | 15 | ||||
-rw-r--r-- | src/nvim/decoration.h | 13 | ||||
-rw-r--r-- | src/nvim/screen.c | 17 | ||||
-rw-r--r-- | test/functional/treesitter/highlight_spec.lua | 40 |
7 files changed, 100 insertions, 5 deletions
diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index b6f61cfb2e..e1ffd42a7f 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -274,7 +274,8 @@ local function on_line_impl(self, buf, line) { end_line = end_row, end_col = end_col, hl_group = hl, ephemeral = true, - priority = tonumber(metadata.priority) or 100 -- Low but leaves room below + priority = tonumber(metadata.priority) or 100, -- Low but leaves room below + conceal = metadata.conceal, }) end if start_row > line then diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index c02688a815..797b64e2af 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -467,6 +467,11 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// as the mark and 'cursorline' is enabled. /// Note: ranges are unsupported and decorations are only /// applied to start_row +/// - conceal: string which should be either empty or a single +/// character. Enable concealing similar to |:syn-conceal|. +/// When a character is supplied it is used as |:syn-cchar|. +/// "hl_group" is used as highlight for the cchar if provided, +/// otherwise it defaults to |hl-Conceal|. /// /// @param[out] err Error details, if any /// @return Id of the created/updated extmark @@ -563,6 +568,17 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } } + if (opts->conceal.type == kObjectTypeString) { + String c = opts->conceal.data.string; + decor.conceal = true; + if (c.size) { + decor.conceal_char = utf_ptr2char((const char_u *)c.data); + } + } else if (HAS_KEY(opts->conceal)) { + api_set_error(err, kErrorTypeValidation, "conceal is not a String"); + goto error; + } + if (opts->virt_text.type == kObjectTypeArray) { decor.virt_text = parse_virt_text(opts->virt_text.data.array, err, &decor.virt_text_width); diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index 435e8195dd..b6264cdfab 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -27,6 +27,7 @@ return { "number_hl_group"; "line_hl_group"; "cursorline_hl_group"; + "conceal"; }; keymap = { "noremap"; diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index fb709d12ff..a0f189ca38 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -312,6 +312,10 @@ next_mark: int attr = 0; size_t j = 0; + bool conceal = 0; + int conceal_char = 0; + int conceal_attr = 0; + for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange item = kv_A(state->active, i); bool active = false, keep = true; @@ -336,6 +340,14 @@ next_mark: if (active && item.attr_id > 0) { attr = hl_combine_attr(attr, item.attr_id); } + if (active && item.decor.conceal) { + conceal = true; + if (item.start_row == state->row && item.start_col == col && item.decor.conceal_char) { + conceal_char = item.decor.conceal_char; + state->col_until = MIN(state->col_until, item.start_col); + conceal_attr = item.attr_id; + } + } if ((item.start_row == state->row && item.start_col <= col) && kv_size(item.decor.virt_text) && item.decor.virt_text_pos == kVTOverlay && item.win_col == -1) { @@ -349,6 +361,9 @@ next_mark: } kv_size(state->active) = j; state->current = attr; + state->conceal = conceal; + state->conceal_char = conceal_char; + state->conceal_attr = conceal_attr; return attr; } diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 8df53caa0a..97ab218f86 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -47,6 +47,7 @@ struct Decoration { bool virt_text_hide; bool hl_eol; bool virt_lines_above; + bool conceal; // TODO(bfredl): style, etc DecorPriority priority; int col; // fixed col value, like win_col @@ -56,9 +57,13 @@ struct Decoration { int number_hl_id; int line_hl_id; int cursorline_hl_id; + // TODO(bfredl): in principle this should be a schar_T, but we + // probably want some kind of glyph cache for that.. + int conceal_char; }; -#define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, kHlModeUnknown, \ - false, false, false, DECOR_PRIORITY_BASE, 0, 0, NULL, 0, 0, 0, 0 } +#define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, \ + kHlModeUnknown, false, false, false, false, DECOR_PRIORITY_BASE, \ + 0, 0, NULL, 0, 0, 0, 0, 0 } typedef struct { int start_row; @@ -80,6 +85,10 @@ typedef struct { int col_until; int current; int eol_col; + + bool conceal; + int conceal_char; + int conceal_attr; } DecorState; EXTERN DecorState decor_state INIT(= { 0 }); diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 60de4e479e..0e1f469a4b 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2682,6 +2682,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // Repeat for the whole displayed line. for (;;) { int has_match_conc = 0; ///< match wants to conceal + int decor_conceal = 0; + bool did_decrement_ptr = false; // Skip this quickly when working on the text. @@ -3506,6 +3508,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc char_attr = hl_combine_attr(extmark_attr, char_attr); } } + + decor_conceal = decor_state.conceal; + if (decor_conceal && decor_state.conceal_char) { + decor_conceal = 2; // really?? + } } // Found last space before word: check for line break. @@ -3809,19 +3816,25 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (wp->w_p_cole > 0 && (wp != curwin || lnum != wp->w_cursor.lnum || conceal_cursor_line(wp)) - && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0) + && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0 || decor_conceal > 0) && !(lnum_in_visual_area && vim_strchr(wp->w_p_cocu, 'v') == NULL)) { char_attr = conceal_attr; - if ((prev_syntax_id != syntax_seqnr || has_match_conc > 1) + if ((prev_syntax_id != syntax_seqnr || has_match_conc > 1 || decor_conceal > 1) && (syn_get_sub_char() != NUL || (has_match_conc && match_conc) + || (decor_conceal && decor_state.conceal_char) || wp->w_p_cole == 1) && wp->w_p_cole != 3) { // First time at this concealed item: display one // character. if (has_match_conc && match_conc) { c = match_conc; + } else if (decor_conceal && decor_state.conceal_char) { + c = decor_state.conceal_char; + if (decor_state.conceal_attr) { + char_attr = decor_state.conceal_attr; + } } else if (syn_get_sub_char() != NUL) { c = syn_get_sub_char(); } else if (wp->w_p_lcs_chars.conceal != NUL) { diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua index ed28d8a37d..5ec0a8a060 100644 --- a/test/functional/treesitter/highlight_spec.lua +++ b/test/functional/treesitter/highlight_spec.lua @@ -672,6 +672,46 @@ describe('treesitter highlighting', function() ]]} end) + it("supports conceal attribute", function() + if pending_c_parser(pending) then return end + insert(hl_text) + + -- conceal can be empty or a single cchar. + exec_lua [=[ + vim.opt.cole = 2 + local parser = vim.treesitter.get_parser(0, "c") + test_hl = vim.treesitter.highlighter.new(parser, {queries = {c = [[ + ("static" @keyword + (set! conceal "R")) + + ((identifier) @Identifier + (set! conceal "") + (eq? @Identifier "lstate")) + ]]}}) + ]=] + + screen:expect{grid=[[ + /// Schedule Lua callback on main loop's event queue | + {4:R} int nlua_schedule(lua_State *const ) | + { | + if (lua_type(, 1) != LUA_TFUNCTION | + || != ) { | + lua_pushliteral(, "vim.schedule: expected function"); | + return lua_error(); | + } | + | + LuaRef cb = nlua_ref(, 1); | + | + multiqueue_put(main_loop.events, nlua_schedule_event, | + 1, (void *)(ptrdiff_t)cb); | + return 0; | + ^} | + {1:~ }| + {1:~ }| + | + ]]} + end) + it("hl_map has the correct fallback behavior", function() exec_lua [[ local hl_map = vim.treesitter.highlighter.hl_map |