mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #17776 from bfredl/tsconceal
feat(ui): allow conceal to be defined in decorations
This commit is contained in:
commit
e7391191e2
@ -274,7 +274,8 @@ local function on_line_impl(self, buf, line)
|
|||||||
{ end_line = end_row, end_col = end_col,
|
{ end_line = end_row, end_col = end_col,
|
||||||
hl_group = hl,
|
hl_group = hl,
|
||||||
ephemeral = true,
|
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
|
end
|
||||||
if start_row > line then
|
if start_row > line then
|
||||||
|
@ -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.
|
/// as the mark and 'cursorline' is enabled.
|
||||||
/// Note: ranges are unsupported and decorations are only
|
/// Note: ranges are unsupported and decorations are only
|
||||||
/// applied to start_row
|
/// 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
|
/// @param[out] err Error details, if any
|
||||||
/// @return Id of the created/updated extmark
|
/// @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) {
|
if (opts->virt_text.type == kObjectTypeArray) {
|
||||||
decor.virt_text = parse_virt_text(opts->virt_text.data.array, err,
|
decor.virt_text = parse_virt_text(opts->virt_text.data.array, err,
|
||||||
&decor.virt_text_width);
|
&decor.virt_text_width);
|
||||||
|
@ -27,6 +27,7 @@ return {
|
|||||||
"number_hl_group";
|
"number_hl_group";
|
||||||
"line_hl_group";
|
"line_hl_group";
|
||||||
"cursorline_hl_group";
|
"cursorline_hl_group";
|
||||||
|
"conceal";
|
||||||
};
|
};
|
||||||
keymap = {
|
keymap = {
|
||||||
"noremap";
|
"noremap";
|
||||||
|
@ -312,6 +312,10 @@ next_mark:
|
|||||||
|
|
||||||
int attr = 0;
|
int attr = 0;
|
||||||
size_t j = 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++) {
|
for (size_t i = 0; i < kv_size(state->active); i++) {
|
||||||
DecorRange item = kv_A(state->active, i);
|
DecorRange item = kv_A(state->active, i);
|
||||||
bool active = false, keep = true;
|
bool active = false, keep = true;
|
||||||
@ -336,6 +340,14 @@ next_mark:
|
|||||||
if (active && item.attr_id > 0) {
|
if (active && item.attr_id > 0) {
|
||||||
attr = hl_combine_attr(attr, item.attr_id);
|
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)
|
if ((item.start_row == state->row && item.start_col <= col)
|
||||||
&& kv_size(item.decor.virt_text)
|
&& kv_size(item.decor.virt_text)
|
||||||
&& item.decor.virt_text_pos == kVTOverlay && item.win_col == -1) {
|
&& item.decor.virt_text_pos == kVTOverlay && item.win_col == -1) {
|
||||||
@ -349,6 +361,9 @@ next_mark:
|
|||||||
}
|
}
|
||||||
kv_size(state->active) = j;
|
kv_size(state->active) = j;
|
||||||
state->current = attr;
|
state->current = attr;
|
||||||
|
state->conceal = conceal;
|
||||||
|
state->conceal_char = conceal_char;
|
||||||
|
state->conceal_attr = conceal_attr;
|
||||||
return attr;
|
return attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ struct Decoration {
|
|||||||
bool virt_text_hide;
|
bool virt_text_hide;
|
||||||
bool hl_eol;
|
bool hl_eol;
|
||||||
bool virt_lines_above;
|
bool virt_lines_above;
|
||||||
|
bool conceal;
|
||||||
// TODO(bfredl): style, etc
|
// TODO(bfredl): style, etc
|
||||||
DecorPriority priority;
|
DecorPriority priority;
|
||||||
int col; // fixed col value, like win_col
|
int col; // fixed col value, like win_col
|
||||||
@ -56,9 +57,13 @@ struct Decoration {
|
|||||||
int number_hl_id;
|
int number_hl_id;
|
||||||
int line_hl_id;
|
int line_hl_id;
|
||||||
int cursorline_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, \
|
#define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, \
|
||||||
false, false, false, DECOR_PRIORITY_BASE, 0, 0, NULL, 0, 0, 0, 0 }
|
kHlModeUnknown, false, false, false, false, DECOR_PRIORITY_BASE, \
|
||||||
|
0, 0, NULL, 0, 0, 0, 0, 0 }
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int start_row;
|
int start_row;
|
||||||
@ -80,6 +85,10 @@ typedef struct {
|
|||||||
int col_until;
|
int col_until;
|
||||||
int current;
|
int current;
|
||||||
int eol_col;
|
int eol_col;
|
||||||
|
|
||||||
|
bool conceal;
|
||||||
|
int conceal_char;
|
||||||
|
int conceal_attr;
|
||||||
} DecorState;
|
} DecorState;
|
||||||
|
|
||||||
EXTERN DecorState decor_state INIT(= { 0 });
|
EXTERN DecorState decor_state INIT(= { 0 });
|
||||||
|
@ -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.
|
// Repeat for the whole displayed line.
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int has_match_conc = 0; ///< match wants to conceal
|
int has_match_conc = 0; ///< match wants to conceal
|
||||||
|
int decor_conceal = 0;
|
||||||
|
|
||||||
bool did_decrement_ptr = false;
|
bool did_decrement_ptr = false;
|
||||||
|
|
||||||
// Skip this quickly when working on the text.
|
// 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);
|
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.
|
// 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
|
if (wp->w_p_cole > 0
|
||||||
&& (wp != curwin || lnum != wp->w_cursor.lnum
|
&& (wp != curwin || lnum != wp->w_cursor.lnum
|
||||||
|| conceal_cursor_line(wp))
|
|| 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
|
&& !(lnum_in_visual_area
|
||||||
&& vim_strchr(wp->w_p_cocu, 'v') == NULL)) {
|
&& vim_strchr(wp->w_p_cocu, 'v') == NULL)) {
|
||||||
char_attr = conceal_attr;
|
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
|
&& (syn_get_sub_char() != NUL
|
||||||
|| (has_match_conc && match_conc)
|
|| (has_match_conc && match_conc)
|
||||||
|
|| (decor_conceal && decor_state.conceal_char)
|
||||||
|| wp->w_p_cole == 1)
|
|| wp->w_p_cole == 1)
|
||||||
&& wp->w_p_cole != 3) {
|
&& wp->w_p_cole != 3) {
|
||||||
// First time at this concealed item: display one
|
// First time at this concealed item: display one
|
||||||
// character.
|
// character.
|
||||||
if (has_match_conc && match_conc) {
|
if (has_match_conc && match_conc) {
|
||||||
c = 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) {
|
} else if (syn_get_sub_char() != NUL) {
|
||||||
c = syn_get_sub_char();
|
c = syn_get_sub_char();
|
||||||
} else if (wp->w_p_lcs_chars.conceal != NUL) {
|
} else if (wp->w_p_lcs_chars.conceal != NUL) {
|
||||||
|
@ -672,6 +672,46 @@ describe('treesitter highlighting', function()
|
|||||||
]]}
|
]]}
|
||||||
end)
|
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()
|
it("hl_map has the correct fallback behavior", function()
|
||||||
exec_lua [[
|
exec_lua [[
|
||||||
local hl_map = vim.treesitter.highlighter.hl_map
|
local hl_map = vim.treesitter.highlighter.hl_map
|
||||||
|
Loading…
Reference in New Issue
Block a user