Skip to content

Commit 14b3a01

Browse files
Attach most auto commands at buffer level
## Details This functions as a performance improvement by removing most of the global auto commands setup by this plugin. Primarily common events like TextChanged / ModeChanged. Instead of having these at a global level and having internal logic to avoid running on certain buffers keep only the FileType auto command at a global level, use the input file types as a pattern directly. From there attach the other auto commands to the buffer scope. Add a manager module to keep track of buffers we've attached to. This is to enable performing global actions on only buffers we care about. In this case for WinResized events and for the toggle command. Users should see no visible change from this other than improved background performance of this plugin.
1 parent 90072fd commit 14b3a01

File tree

4 files changed

+145
-107
lines changed

4 files changed

+145
-107
lines changed

lua/render-markdown/init.lua

Lines changed: 8 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1+
local manager = require('render-markdown.manager')
12
local state = require('render-markdown.state')
2-
local ui = require('render-markdown.ui')
3-
local util = require('render-markdown.util')
43

54
local M = {}
65

@@ -240,52 +239,17 @@ function M.setup(opts)
240239
state.markdown_query = vim.treesitter.query.parse('markdown', state.config.markdown_query)
241240
state.inline_query = vim.treesitter.query.parse('markdown_inline', state.config.inline_query)
242241

243-
-- Call immediately to re-render on LazyReload
244-
ui.schedule_refresh(vim.api.nvim_get_current_buf())
242+
manager.setup()
245243

246-
local group = vim.api.nvim_create_augroup('RenderMarkdown', { clear = true })
247-
vim.api.nvim_create_autocmd({ 'ModeChanged' }, {
248-
group = group,
249-
callback = function(event)
250-
local render_modes = state.config.render_modes
251-
local prev_rendered = vim.tbl_contains(render_modes, vim.v.event.old_mode)
252-
local should_render = vim.tbl_contains(render_modes, vim.v.event.new_mode)
253-
-- Only need to re-render if render state is changing. I.e. going from normal mode to
254-
-- command mode with the default config, both are rendered, so no point re-rendering
255-
if prev_rendered ~= should_render then
256-
ui.schedule_refresh(event.buf)
257-
end
258-
end,
259-
})
260-
vim.api.nvim_create_autocmd({ 'WinResized' }, {
261-
group = group,
262-
callback = function()
263-
for _, win in ipairs(vim.v.event.windows) do
264-
local buf = util.win_to_buf(win)
265-
ui.schedule_refresh(buf)
266-
end
267-
end,
268-
})
269-
vim.api.nvim_create_autocmd({ 'FileChangedShellPost', 'FileType', 'TextChanged' }, {
270-
group = group,
271-
callback = function(event)
272-
ui.schedule_refresh(event.buf)
273-
end,
274-
})
275-
276-
local description = 'Switch between enabling & disabling render markdown plugin'
277-
vim.api.nvim_create_user_command('RenderMarkdownToggle', M.toggle, { desc = description })
244+
vim.api.nvim_create_user_command(
245+
'RenderMarkdownToggle',
246+
M.toggle,
247+
{ desc = 'Switch between enabling & disabling render markdown plugin' }
248+
)
278249
end
279250

280251
M.toggle = function()
281-
state.enabled = not state.enabled
282-
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
283-
if state.enabled then
284-
ui.schedule_refresh(buf)
285-
else
286-
ui.schedule_clear(buf)
287-
end
288-
end
252+
manager.toggle()
289253
end
290254

291255
return M

lua/render-markdown/manager.lua

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
local state = require('render-markdown.state')
2+
local ui = require('render-markdown.ui')
3+
local util = require('render-markdown.util')
4+
5+
---@class render.md.manager.Data
6+
---@field buffers integer[]
7+
8+
---@type render.md.manager.Data
9+
local data = {
10+
buffers = {},
11+
}
12+
13+
---@class render.md.Manager
14+
local M = {}
15+
16+
function M.setup()
17+
local group = vim.api.nvim_create_augroup('RenderMarkdown', { clear = true })
18+
-- Attach to buffers based on matching filetype, this will add additional events
19+
vim.api.nvim_create_autocmd({ 'FileType' }, {
20+
group = group,
21+
pattern = state.config.file_types,
22+
callback = function(event)
23+
M.attach(group, event.buf)
24+
end,
25+
})
26+
-- Window resizing is not buffer specific so is managed more globablly
27+
vim.api.nvim_create_autocmd({ 'WinResized' }, {
28+
group = group,
29+
callback = function()
30+
for _, win in ipairs(vim.v.event.windows) do
31+
local buf = util.win_to_buf(win)
32+
if vim.tbl_contains(data.buffers, buf) then
33+
ui.schedule_refresh(buf)
34+
end
35+
end
36+
end,
37+
})
38+
end
39+
40+
---@private
41+
---@param group integer
42+
---@param buf integer
43+
M.attach = function(group, buf)
44+
if not vim.tbl_contains(data.buffers, buf) then
45+
table.insert(data.buffers, buf)
46+
end
47+
vim.api.nvim_create_autocmd({ 'BufWinEnter', 'TextChanged' }, {
48+
group = group,
49+
buffer = buf,
50+
callback = function()
51+
ui.schedule_refresh(buf)
52+
end,
53+
})
54+
vim.api.nvim_create_autocmd({ 'ModeChanged' }, {
55+
group = group,
56+
buffer = buf,
57+
callback = function()
58+
local render_modes = state.config.render_modes
59+
local prev_rendered = vim.tbl_contains(render_modes, vim.v.event.old_mode)
60+
local should_render = vim.tbl_contains(render_modes, vim.v.event.new_mode)
61+
-- Only need to re-render if render state is changing. I.e. going from normal mode to
62+
-- command mode with the default config, both are rendered, so no point re-rendering
63+
if prev_rendered ~= should_render then
64+
ui.schedule_refresh(buf)
65+
end
66+
end,
67+
})
68+
end
69+
70+
M.toggle = function()
71+
state.enabled = not state.enabled
72+
for _, buf in ipairs(data.buffers) do
73+
ui.schedule_refresh(buf)
74+
end
75+
end
76+
77+
return M

lua/render-markdown/ui.lua

Lines changed: 49 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -12,54 +12,73 @@ local builtin_handlers = {
1212
---@class render.md.Ui
1313
local M = {}
1414

15+
---@type integer
1516
M.namespace = vim.api.nvim_create_namespace('render-markdown.nvim')
1617

1718
---@param buf integer
1819
M.schedule_refresh = function(buf)
1920
local mode = vim.fn.mode(true)
2021
vim.schedule(function()
22+
logger.start()
2123
M.refresh(buf, mode)
24+
logger.flush()
2225
end)
2326
end
2427

28+
---@private
2529
---@param buf integer
26-
M.schedule_clear = function(buf)
27-
vim.schedule(function()
28-
M.clear_valid(buf)
29-
end)
30+
---@param mode string
31+
M.refresh = function(buf, mode)
32+
-- Remove any existing marks if buffer is valid
33+
if not vim.api.nvim_buf_is_valid(buf) then
34+
return
35+
end
36+
vim.api.nvim_buf_clear_namespace(buf, M.namespace, 0, -1)
37+
38+
-- Check that buffer is associated with a valid window before window operations
39+
local win = util.buf_to_win(buf)
40+
if not vim.api.nvim_win_is_valid(win) then
41+
return
42+
end
43+
44+
if not M.should_render(buf, win, mode) then
45+
-- Set window options back to default
46+
for name, value in pairs(state.config.win_options) do
47+
util.set_win_option(win, name, value.default)
48+
end
49+
else
50+
-- Set window options to rendered & perform render
51+
for name, value in pairs(state.config.win_options) do
52+
util.set_win_option(win, name, value.rendered)
53+
end
54+
-- Make sure injections are processed
55+
local parser = vim.treesitter.get_parser(buf)
56+
parser:parse(true)
57+
parser:for_each_tree(function(tree, language_tree)
58+
M.render(buf, language_tree:lang(), tree:root())
59+
end)
60+
end
3061
end
3162

3263
---@private
3364
---@param buf integer
65+
---@param win integer
3466
---@param mode string
35-
M.refresh = function(buf, mode)
67+
---@return boolean
68+
M.should_render = function(buf, win, mode)
3669
if not state.enabled then
37-
return
70+
return false
3871
end
39-
if not M.clear_valid(buf) then
40-
return
72+
if not util.get_leftcol(win) == 0 then
73+
return false
4174
end
4275
if not vim.tbl_contains(state.config.render_modes, mode) then
43-
return
76+
return false
4477
end
4578
if util.file_size_mb(buf) > state.config.max_file_size then
46-
return
47-
end
48-
49-
logger.start()
50-
for name, value in pairs(state.config.win_options) do
51-
util.set_win_option(buf, name, value.rendered)
79+
return false
5280
end
53-
-- Make sure injections are processed
54-
local parser = vim.treesitter.get_parser(buf)
55-
parser:parse(true)
56-
parser:for_each_tree(function(tree, language_tree)
57-
local language = language_tree:lang()
58-
logger.debug('Language: ' .. language)
59-
local executed = M.render(buf, language, tree:root())
60-
logger.debug({ executed_handlers = executed })
61-
end)
62-
logger.flush()
81+
return true
6382
end
6483

6584
---Run user & builtin handlers when available. User handler is always executed,
@@ -68,51 +87,21 @@ end
6887
---@param buf integer
6988
---@param language string
7089
---@param root TSNode
71-
---@return string[]
7290
M.render = function(buf, language, root)
73-
local result = {}
91+
logger.debug('Language: ' .. language)
7492
local user_handler = state.config.custom_handlers[language]
7593
if user_handler ~= nil then
94+
logger.debug('Running user handler')
7695
user_handler.render(M.namespace, root, buf)
77-
table.insert(result, 'user')
78-
if user_handler.extends ~= true then
79-
return result
96+
if not user_handler.extends then
97+
return
8098
end
8199
end
82100
local builtin_handler = builtin_handlers[language]
83101
if builtin_handler ~= nil then
102+
logger.debug('Running builtin handler')
84103
builtin_handler.render(M.namespace, root, buf)
85-
table.insert(result, 'builtin')
86-
end
87-
return result
88-
end
89-
90-
---Remove existing highlights / virtual text for valid buffers
91-
---@private
92-
---@param buf integer
93-
---@return boolean
94-
M.clear_valid = function(buf)
95-
if not vim.api.nvim_buf_is_valid(buf) then
96-
return false
97-
end
98-
if not vim.tbl_contains(state.config.file_types, vim.bo[buf].filetype) then
99-
return false
100104
end
101-
vim.api.nvim_buf_clear_namespace(buf, M.namespace, 0, -1)
102-
local win = util.buf_to_win(buf)
103-
if not vim.api.nvim_win_is_valid(win) then
104-
return false
105-
end
106-
for name, value in pairs(state.config.win_options) do
107-
util.set_win_option(buf, name, value.default)
108-
end
109-
local leftcol = vim.api.nvim_win_call(win, function()
110-
return vim.fn.winsaveview().leftcol
111-
end)
112-
if leftcol > 0 then
113-
return false
114-
end
115-
return true
116105
end
117106

118107
return M

lua/render-markdown/util.lua

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,24 @@ M.buf_to_win = function(buf)
1414
return vim.fn.bufwinid(buf)
1515
end
1616

17-
---@param buf integer
17+
---@param win integer
1818
---@param name string
1919
---@param value any
20-
M.set_win_option = function(buf, name, value)
21-
local opts = { scope = 'local', win = M.buf_to_win(buf) }
20+
M.set_win_option = function(win, name, value)
21+
local opts = { scope = 'local', win = win }
2222
local before = vim.api.nvim_get_option_value(name, opts)
2323
vim.api.nvim_set_option_value(name, value, opts)
2424
logger.debug({ option = name, opts = opts, before = before, after = value })
2525
end
2626

27+
---@param win integer
28+
---@return integer
29+
M.get_leftcol = function(win)
30+
return vim.api.nvim_win_call(win, function()
31+
return vim.fn.winsaveview().leftcol
32+
end)
33+
end
34+
2735
---@param buf integer
2836
---@return number
2937
M.file_size_mb = function(buf)

0 commit comments

Comments
 (0)