Skip to content

Commit 84d4928

Browse files
fix: component level render modes
## Details Similar to the problem around anti-conceal ignore modes. Individual component render modes are checked once at creation time and used until a recompute is necessary. This can potentially include various mode changes, and since the results are not updated the rendering will be incorrect. To fix this add an optional `modes` field to the `render.md.Mark` class. This gets populated based on the `render_modes` value for the relevant component and stored as part of the mark metadata. We then use this information to check for each `mark` if it should be rendered based on either being in the top level mode or the mark level mode. Custom handlers can use this feature as well, though with some edge cases like potentially not identifying the specified mode as a render mode. This does have a performance drawback compared to the old approach. Before we would avoid creating / processing nodes if they didn't match any of the mode values. Now if any mode value is matched in any component we will compute the marks for all components, even if they're not needed right now. That way if a mode change happens without a text change we'll have all the marks we might potentially need to show, and show the ones relevant to the current mode. For most users who do not use this feature this should essentially have no impact on performance.
1 parent 8074a9c commit 84d4928

32 files changed

+228
-206
lines changed

doc/custom-handlers.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Each handler must conform to the following interface:
2121
---@field root TSNode
2222

2323
---@class (exact) render.md.Mark
24+
---@field modes? render.md.Modes
2425
---@field conceal render.md.mark.Conceal
2526
---@field start_row integer
2627
---@field start_col integer
@@ -58,6 +59,7 @@ to use patterns from the builtin handlers.
5859

5960
For each `mark` in the return value the fields mean:
6061

62+
- `modes`: additional modes the `mark` should be shown in
6163
- `conceal`: determines if the mark should be hidden when cursor enters
6264
- `start_row`: only value used to check whether cursor is inside the `mark`
6365
- `start_col`: passed to `nvim_buf_set_extmark` as the 3rd argument
@@ -92,7 +94,7 @@ This will require a treesitter query and using the range values of nodes.
9294
-- Parse query outside of the function to avoid doing it for each call
9395
local query = vim.treesitter.query.parse('python', '(function_definition) @def')
9496
local function parse_python(ctx)
95-
local marks = {}
97+
local marks = {} ---@type render.md.Mark[]
9698
for id, node in query:iter_captures(ctx.root, ctx.buf) do
9799
local capture = query.captures[id]
98100
local start_row = node:range()

lua/render-markdown/core/ui.lua

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ function Updater:parse(callback)
163163
local ok, parser = pcall(vim.treesitter.get_parser, self.buf)
164164
if ok and parser then
165165
-- reset buffer context
166-
local context = Context.new(self.buf, self.win, self.config, self.mode)
166+
local context = Context.new(self.buf, self.win, self.config)
167167
if context then
168168
-- make sure injections are processed
169169
context.view:parse(parser, function()
@@ -185,7 +185,7 @@ function Updater:display()
185185
local range = self:hidden()
186186
local extmarks = self.decorator:get()
187187
for _, extmark in ipairs(extmarks) do
188-
if self:conceal(extmark, range) then
188+
if self:hide(extmark, range) then
189189
extmark:hide(M.ns, self.buf)
190190
else
191191
extmark:show(M.ns, self.buf)
@@ -222,19 +222,29 @@ end
222222
---@param extmark render.md.Extmark
223223
---@param range? render.md.Range
224224
---@return boolean
225-
function Updater:conceal(extmark, range)
226-
local conceal = extmark:get().conceal
225+
function Updater:hide(extmark, range)
226+
local mark = extmark:get()
227+
228+
-- not in top level or mark level modes -> hide
229+
local show = env.mode.join(self.config.render_modes, mark.modes)
230+
if not env.mode.is(self.mode, show) then
231+
return true
232+
end
233+
234+
-- does not overlap with hidden range -> show
235+
if not extmark:overlaps(range) then
236+
return false
237+
end
238+
239+
local conceal = mark.conceal
227240
if type(conceal) == 'boolean' then
228-
if not conceal then
229-
return false
230-
end
241+
-- mark has conceal value -> respect
242+
return conceal
231243
else
232-
local modes = self.config.anti_conceal.ignore[conceal]
233-
if modes and env.mode.is(self.mode, modes) then
234-
return false
235-
end
244+
-- mark has conceal element -> show if anti-conceal is ignored
245+
local ignore = self.config.anti_conceal.ignore[conceal]
246+
return not (ignore and env.mode.is(self.mode, ignore))
236247
end
237-
return extmark:overlaps(range)
238248
end
239249

240250
---@private

lua/render-markdown/debug/marks.lua

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,23 @@
33
---@field [2]? integer
44

55
---@class render.md.debug.Mark
6+
---@field modes? render.md.Modes
67
---@field conceal render.md.mark.Conceal
7-
---@field opts render.md.mark.Opts
88
---@field row render.md.debug.Range
99
---@field col render.md.debug.Range
10+
---@field opts render.md.mark.Opts
1011
local Mark = {}
1112
Mark.__index = Mark
1213

1314
---@param mark render.md.Mark
1415
---@return render.md.debug.Mark
1516
function Mark.new(mark)
1617
local self = setmetatable({}, Mark)
17-
self.conceal, self.opts = mark.conceal, mark.opts
18+
self.modes = mark.modes
19+
self.conceal = mark.conceal
1820
self.row = { mark.start_row, mark.opts.end_row }
1921
self.col = { mark.start_col, mark.opts.end_col }
22+
self.opts = mark.opts
2023
return self
2124
end
2225

@@ -57,9 +60,10 @@ end
5760
function Mark:__tostring()
5861
local lines = {} ---@type string[]
5962
lines[#lines + 1] = ('='):rep(vim.o.columns - 1)
63+
lines[#lines + 1] = ('modes: %s'):format(vim.inspect(self.modes))
64+
lines[#lines + 1] = ('conceal: %s'):format(vim.inspect(self.conceal))
6065
lines[#lines + 1] = ('row: %s'):format(Mark.collapse(self.row))
6166
lines[#lines + 1] = ('column: %s'):format(Mark.collapse(self.col))
62-
lines[#lines + 1] = ('hide: %s'):format(vim.inspect(self.conceal))
6367

6468
---@param name string
6569
---@param f fun(value: any): string

lua/render-markdown/handler/html.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function M.parse(ctx)
2121
tag = require('render-markdown.render.html.tag'),
2222
}
2323
local context = Context.get(ctx.buf)
24-
if context:skip(context.config.html) then
24+
if not context.config.html.enabled then
2525
return {}
2626
end
2727
local marks = Marks.new(context, true)

lua/render-markdown/handler/latex.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ end
2828
---@param root TSNode
2929
---@return render.md.Mark[]
3030
function Handler:run(root)
31-
if self.context:skip(self.config) then
31+
if not self.config.enabled then
3232
return {}
3333
end
3434
if vim.fn.executable(self.config.converter) ~= 1 then
@@ -50,7 +50,7 @@ function Handler:run(root)
5050
local row = above and node.start_row or node.end_row
5151

5252
local marks = Marks.new(self.context, true)
53-
marks:add('virtual_lines', row, 0, {
53+
marks:add(self.config, 'virtual_lines', row, 0, {
5454
virt_lines = lines,
5555
virt_lines_above = above,
5656
})

lua/render-markdown/handler/yaml.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function M.parse(ctx)
2323
link = require('render-markdown.render.common.wiki'),
2424
}
2525
local context = Context.get(ctx.buf)
26-
if context:skip(context.config.yaml) then
26+
if not context.config.yaml.enabled then
2727
return {}
2828
end
2929
local marks = Marks.new(context, true)

lua/render-markdown/health.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ local state = require('render-markdown.state')
55
local M = {}
66

77
---@private
8-
M.version = '8.7.11'
8+
M.version = '8.7.12'
99

1010
function M.check()
1111
M.start('versions')

lua/render-markdown/lib/env.lua

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,34 @@ function M.mode.get()
9898
return vim.fn.mode(true)
9999
end
100100

101+
---@param v1 render.md.Modes
102+
---@param v2? render.md.Modes
103+
---@return render.md.Modes
104+
function M.mode.join(v1, v2)
105+
if type(v1) == 'boolean' and type(v2) == 'boolean' then
106+
return v1 or v2
107+
elseif type(v1) == 'boolean' and type(v2) == 'table' then
108+
return v1 or v2
109+
elseif type(v1) == 'table' and type(v2) == 'boolean' then
110+
return v2 or v1
111+
elseif type(v1) == 'table' and type(v2) == 'table' then
112+
-- copy to avoid modifying inputs
113+
local result = {} ---@type string[]
114+
vim.list_extend(result, v1)
115+
vim.list_extend(result, v2)
116+
return result
117+
else
118+
return v1 -- should only occur if v2 is nil, keep v1
119+
end
120+
end
121+
101122
---@param mode string
102-
---@param modes render.md.Modes
123+
---@param modes? render.md.Modes
103124
---@return boolean
104125
function M.mode.is(mode, modes)
105-
if type(modes) == 'boolean' then
126+
if modes == nil then
127+
return false
128+
elseif type(modes) == 'boolean' then
106129
return modes
107130
else
108131
return vim.tbl_contains(modes, mode)

lua/render-markdown/lib/indent.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ end
5252
---@param level? integer
5353
---@return integer
5454
function Indent:level(level)
55-
if self.context:skip(self.config) then
55+
if not self.config.enabled then
5656
return 0
5757
end
5858
if not level then

lua/render-markdown/lib/marks.lua

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ local log = require('render-markdown.core.log')
33
local str = require('render-markdown.lib.str')
44

55
---@class (exact) render.md.Mark
6+
---@field modes? render.md.Modes
67
---@field conceal render.md.mark.Conceal
78
---@field start_row integer
89
---@field start_col integer
@@ -47,20 +48,22 @@ function Marks:get()
4748
return self.marks
4849
end
4950

51+
---@param config render.md.base.Config
5052
---@param conceal render.md.mark.Conceal
5153
---@param node render.md.Node
5254
---@param opts render.md.mark.Opts
5355
---@return boolean
54-
function Marks:start(conceal, node, opts)
55-
return self:add(conceal, node.start_row, node.start_col, opts)
56+
function Marks:start(config, conceal, node, opts)
57+
return self:add(config, conceal, node.start_row, node.start_col, opts)
5658
end
5759

60+
---@param config render.md.base.Config
5861
---@param conceal render.md.mark.Conceal
5962
---@param node? render.md.Node
6063
---@param opts render.md.mark.Opts
6164
---@param offset? Range4
6265
---@return boolean
63-
function Marks:over(conceal, node, opts, offset)
66+
function Marks:over(config, conceal, node, opts, offset)
6467
if not node then
6568
return false
6669
end
@@ -69,23 +72,25 @@ function Marks:over(conceal, node, opts, offset)
6972
local start_col = node.start_col + offset[2]
7073
opts.end_row = node.end_row + offset[3]
7174
opts.end_col = node.end_col + offset[4]
72-
return self:add(conceal, start_row, start_col, opts)
75+
return self:add(config, conceal, start_row, start_col, opts)
7376
end
7477

78+
---@param config render.md.base.Config
7579
---@param conceal render.md.mark.Conceal
7680
---@param start_row integer
7781
---@param start_col integer
7882
---@param opts render.md.mark.Opts
7983
---@return boolean
80-
function Marks:add(conceal, start_row, start_col, opts)
84+
function Marks:add(config, conceal, start_row, start_col, opts)
8185
---@type render.md.Mark
8286
local mark = {
87+
modes = config.render_modes,
8388
conceal = conceal,
8489
start_row = start_row,
8590
start_col = start_col,
8691
opts = opts,
8792
}
88-
local valid, feature, min_version = self:validate(opts)
93+
local valid, feature, min_version = self:validate(mark.opts)
8994
if not valid then
9095
local message = feature .. ' requires neovim >= ' .. min_version
9196
log.add('error', 'Mark', message, mark)

0 commit comments

Comments
 (0)