diff options
-rw-r--r-- | runtime/doc/treesitter.txt | 3 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter/highlighter.lua | 19 | ||||
-rw-r--r-- | src/nvim/api/extmark.c | 8 | ||||
-rw-r--r-- | src/nvim/decoration.c | 12 | ||||
-rw-r--r-- | src/nvim/decoration.h | 6 | ||||
-rw-r--r-- | src/nvim/drawline.c | 5 | ||||
-rw-r--r-- | src/nvim/extmark.c | 2 | ||||
-rw-r--r-- | src/nvim/spell.c | 21 | ||||
-rw-r--r-- | src/nvim/types.h | 3 | ||||
-rw-r--r-- | test/functional/ui/decorations_spec.lua | 38 |
10 files changed, 88 insertions, 29 deletions
diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index 992e5ebe69..d6cd370ec7 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -432,6 +432,9 @@ capture marks comments as to be checked: > (comment) @spell < + +There is also `@nospell` which disables spellchecking regions with `@spell`. + *treesitter-highlight-conceal* Treesitter highlighting supports |conceal| via the `conceal` metadata. By convention, nodes to be concealed are captured as `@conceal`, but any capture diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 83a26aff13..f5e5ca1988 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -164,7 +164,7 @@ function TSHighlighter:get_query(lang) end ---@private -local function on_line_impl(self, buf, line, spell) +local function on_line_impl(self, buf, line, is_spell_nav) self.tree:for_each_tree(function(tstree, tree) if not tstree then return @@ -201,17 +201,26 @@ local function on_line_impl(self, buf, line, spell) local start_row, start_col, end_row, end_col = node:range() local hl = highlighter_query.hl_cache[capture] - local is_spell = highlighter_query:query().captures[capture] == 'spell' + local capture_name = highlighter_query:query().captures[capture] + local spell = nil + if capture_name == 'spell' then + spell = true + elseif capture_name == 'nospell' then + spell = false + end + + -- Give nospell a higher priority so it always overrides spell captures. + local spell_pri_offset = capture_name == 'nospell' and 1 or 0 - if hl and end_row >= line and (not spell or is_spell) then + if hl and end_row >= line and (not is_spell_nav or spell ~= nil) then a.nvim_buf_set_extmark(buf, ns, start_row, start_col, { 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) + spell_pri_offset, -- Low but leaves room below conceal = metadata.conceal, - spell = is_spell, + spell = spell, }) end if start_row > line then diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index fee6876469..54eb7d9b6b 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -721,8 +721,12 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer bool ephemeral = false; OPTION_TO_BOOL(ephemeral, ephemeral, false); - OPTION_TO_BOOL(decor.spell, spell, false); - if (decor.spell) { + if (opts->spell.type == kObjectTypeNil) { + decor.spell = kNone; + } else { + bool spell = false; + OPTION_TO_BOOL(spell, spell, false); + decor.spell = spell ? kTrue : kFalse; has_decor = true; } diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 19e99fa7a6..230d96a15f 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -69,7 +69,11 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor) { if (row2 >= row1) { - if (!decor || decor->hl_id || decor_has_sign(decor) || decor->conceal || decor->spell) { + if (!decor + || decor->hl_id + || decor_has_sign(decor) + || decor->conceal + || decor->spell != kNone) { redraw_buf_range_later(buf, row1 + 1, row2 + 1); } } @@ -309,7 +313,7 @@ next_mark: bool conceal = 0; int conceal_char = 0; int conceal_attr = 0; - bool spell = false; + TriState spell = kNone; for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange item = kv_A(state->active, i); @@ -343,8 +347,8 @@ next_mark: conceal_attr = item.attr_id; } } - if (active && item.decor.spell) { - spell = true; + if (active && item.decor.spell != kNone) { + spell = item.decor.spell; } if ((item.start_row == state->row && item.start_col <= col) && decor_virt_pos(item.decor) diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 9ba621d7a4..8f016c103b 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -45,7 +45,7 @@ struct Decoration { bool hl_eol; bool virt_lines_above; bool conceal; - bool spell; + TriState spell; // TODO(bfredl): style, etc DecorPriority priority; int col; // fixed col value, like win_col @@ -61,7 +61,7 @@ struct Decoration { bool ui_watched; // watched for win_extmark }; #define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, \ - kHlModeUnknown, false, false, false, false, false, \ + kHlModeUnknown, false, false, false, false, kNone, \ DECOR_PRIORITY_BASE, 0, 0, NULL, 0, 0, 0, 0, 0, false } typedef struct { @@ -91,7 +91,7 @@ typedef struct { int conceal_char; int conceal_attr; - bool spell; + TriState spell; } DecorState; EXTERN DecorState decor_state INIT(= { 0 }); diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index cb7d85a467..6d42b91507 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -33,6 +33,7 @@ #include "nvim/spell.h" #include "nvim/state.h" #include "nvim/syntax.h" +#include "nvim/types.h" #include "nvim/undo.h" #include "nvim/window.h" @@ -1722,9 +1723,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, decor_conceal = 2; // really?? } - if (decor_state.spell) { - can_spell = true; - } + can_spell = TRISTATE_TO_BOOL(decor_state.spell, can_spell); } // Check spelling (unless at the end of the line). diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index df87cc8ab6..015799be06 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -71,7 +71,7 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col || decor->conceal || decor_has_sign(decor) || decor->ui_watched - || decor->spell) { + || decor->spell != kNone) { decor_full = true; decor = xmemdup(decor, sizeof *decor); } diff --git a/src/nvim/spell.c b/src/nvim/spell.c index f24af5c3bf..b1daf4ed23 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -1216,7 +1216,14 @@ static bool decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_lnum, *decor_lnum = lnum; } decor_redraw_col(wp->w_buffer, col, col, false, &decor_state); - return decor_state.spell; + return decor_state.spell == kTrue; +} + +static inline bool can_syn_spell(win_T *wp, linenr_T lnum, int col) +{ + bool can_spell; + (void)syn_get_id(wp, lnum, col, false, &can_spell, false); + return can_spell; } /// Moves to the next spell error. @@ -1346,15 +1353,9 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att : p - buf) > wp->w_cursor.col)) { col = (colnr_T)(p - buf); - bool can_spell = decor_spell_nav_col(wp, lnum, &decor_lnum, col, &decor_error); - - if (!can_spell) { - if (has_syntax) { - (void)syn_get_id(wp, lnum, col, false, &can_spell, false); - } else { - can_spell = (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) == 0; - } - } + bool can_spell = (!has_syntax && (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) == 0) + || decor_spell_nav_col(wp, lnum, &decor_lnum, col, &decor_error) + || (has_syntax && can_syn_spell(wp, lnum, col)); if (!can_spell) { attr = HLF_COUNT; diff --git a/src/nvim/types.h b/src/nvim/types.h index fb10bf21d9..d90c623955 100644 --- a/src/nvim/types.h +++ b/src/nvim/types.h @@ -45,6 +45,9 @@ typedef enum { kTrue = 1, } TriState; +#define TRISTATE_TO_BOOL(val, \ + default) ((val) == kTrue ? true : ((val) == kFalse ? false : (default))) + typedef struct Decoration Decoration; #endif // NVIM_TYPES_H diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 5b62f5b3e1..489c33d8b1 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -176,7 +176,13 @@ describe('decorations providers', function() beamtrace = {} local function on_do(kind, ...) if kind == 'win' or kind == 'spell' then - a.nvim_buf_set_extmark(0, ns, 0, 0, { end_row = 2, end_col = 23, spell = true, ephemeral = true }) + a.nvim_buf_set_extmark(0, ns, 0, 0, { + end_row = 2, + end_col = 23, + spell = true, + priority = 20, + ephemeral = true + }) end table.insert(beamtrace, {kind, ...}) end @@ -234,6 +240,36 @@ describe('decorations providers', function() {1:~ }| | ]]} + + -- spell=false with lower priority doesn't disable spell + local ns = meths.create_namespace "spell" + local id = helpers.curbufmeths.set_extmark(ns, 0, 0, { priority = 30, end_row = 2, end_col = 23, spell = false }) + + screen:expect{grid=[[ + I am well written text. | + i am not capitalized. | + I am a ^speling mistakke. | + | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + -- spell=false with higher priority does disable spell + helpers.curbufmeths.set_extmark(ns, 0, 0, { id = id, priority = 10, end_row = 2, end_col = 23, spell = false }) + + screen:expect{grid=[[ + I am well written text. | + {15:i} am not capitalized. | + I am a {16:^speling} {16:mistakke}. | + | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) it('can predefine highlights', function() |