Skip to content

Commit aa9e871

Browse files
Refactor: split off rendering logic into ui module
1 parent 35573d0 commit aa9e871

File tree

2 files changed

+137
-131
lines changed

2 files changed

+137
-131
lines changed

lua/render-markdown/init.lua

Lines changed: 5 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
local list = require('render-markdown.list')
21
local state = require('render-markdown.state')
2+
local ui = require('render-markdown.ui')
33

44
local M = {}
55

@@ -80,7 +80,7 @@ function M.setup(opts)
8080
state.markdown_query = vim.treesitter.query.parse('markdown', state.config.markdown_query)
8181

8282
-- Call immediately to re-render on LazyReload
83-
vim.schedule(M.refresh)
83+
vim.schedule(ui.refresh)
8484

8585
vim.api.nvim_create_autocmd({
8686
'FileChangedShellPost',
@@ -91,7 +91,7 @@ function M.setup(opts)
9191
}, {
9292
group = vim.api.nvim_create_augroup('RenderMarkdown', { clear = true }),
9393
callback = function()
94-
vim.schedule(M.refresh)
94+
vim.schedule(ui.refresh)
9595
end,
9696
})
9797

@@ -102,141 +102,15 @@ function M.setup(opts)
102102
)
103103
end
104104

105-
M.namespace = vim.api.nvim_create_namespace('render-markdown.nvim')
106-
107105
M.toggle = function()
108106
if state.enabled then
109107
state.enabled = false
110-
vim.schedule(M.clear)
108+
vim.schedule(ui.clear)
111109
else
112110
state.enabled = true
113111
-- Call to refresh must happen after state change
114-
vim.schedule(M.refresh)
115-
end
116-
end
117-
118-
M.clear = function()
119-
-- Remove existing highlights / virtual text
120-
vim.api.nvim_buf_clear_namespace(0, M.namespace, 0, -1)
121-
end
122-
123-
M.refresh = function()
124-
if not state.enabled then
125-
return
112+
vim.schedule(ui.refresh)
126113
end
127-
if not vim.tbl_contains(state.config.file_types, vim.bo.filetype) then
128-
return
129-
end
130-
-- Needs to happen after file_type check and before mode check
131-
M.clear()
132-
if not vim.tbl_contains(state.config.render_modes, vim.fn.mode()) then
133-
return
134-
end
135-
136-
vim.treesitter.get_parser():for_each_tree(function(tree, language_tree)
137-
local language = language_tree:lang()
138-
if language == 'markdown' then
139-
M.handle_markdown(tree:root())
140-
elseif language == 'latex' then
141-
M.handle_latex(tree:root())
142-
end
143-
end)
144-
end
145-
146-
---@param root TSNode
147-
M.handle_markdown = function(root)
148-
local highlights = state.config.highlights
149-
---@diagnostic disable-next-line: missing-parameter
150-
for id, node in state.markdown_query:iter_captures(root, 0) do
151-
local capture = state.markdown_query.captures[id]
152-
local value = vim.treesitter.get_node_text(node, 0)
153-
local start_row, start_col, end_row, end_col = node:range()
154-
155-
if capture == 'heading' then
156-
local level = #value
157-
local heading = list.cycle(state.config.headings, level)
158-
local background = list.clamp_last(highlights.heading.backgrounds, level)
159-
local foreground = list.clamp_last(highlights.heading.foregrounds, level)
160-
161-
local virt_text = { string.rep(' ', level - 1) .. heading, { foreground, background } }
162-
vim.api.nvim_buf_set_extmark(0, M.namespace, start_row, 0, {
163-
end_row = end_row + 1,
164-
end_col = 0,
165-
hl_group = background,
166-
virt_text = { virt_text },
167-
virt_text_pos = 'overlay',
168-
hl_eol = true,
169-
})
170-
elseif capture == 'code' then
171-
vim.api.nvim_buf_set_extmark(0, M.namespace, start_row, 0, {
172-
end_row = end_row,
173-
end_col = 0,
174-
hl_group = highlights.code,
175-
hl_eol = true,
176-
})
177-
elseif capture == 'list_marker' then
178-
-- List markers from tree-sitter should have leading spaces removed, however there are known
179-
-- edge cases in the parser: https://github.com/tree-sitter-grammars/tree-sitter-markdown/issues/127
180-
-- As a result we handle leading spaces here, can remove if this gets fixed upstream
181-
local _, leading_spaces = value:find('^%s*')
182-
local virt_text = { string.rep(' ', leading_spaces or 0) .. state.config.bullet, highlights.bullet }
183-
vim.api.nvim_buf_set_extmark(0, M.namespace, start_row, start_col, {
184-
end_row = end_row,
185-
end_col = end_col,
186-
virt_text = { virt_text },
187-
virt_text_pos = 'overlay',
188-
})
189-
elseif vim.tbl_contains({ 'table_head', 'table_delim', 'table_row' }, capture) then
190-
local row = value:gsub('|', '')
191-
if capture == 'table_delim' then
192-
-- Order matters here, in particular handling inner intersections before left & right
193-
row = row:gsub('-', '')
194-
:gsub(' ', '')
195-
:gsub('─│─', '─┼─')
196-
:gsub('│─', '├─')
197-
:gsub('─│', '─┤')
198-
end
199-
200-
local highlight = highlights.table.head
201-
if capture == 'table_row' then
202-
highlight = highlights.table.row
203-
end
204-
205-
local virt_text = { row, highlight }
206-
vim.api.nvim_buf_set_extmark(0, M.namespace, start_row, start_col, {
207-
end_row = end_row,
208-
end_col = end_col,
209-
virt_text = { virt_text },
210-
virt_text_pos = 'overlay',
211-
})
212-
else
213-
-- Should only get here if user provides custom capture, currently unhandled
214-
vim.print('Unhandled capture: ' .. capture)
215-
end
216-
end
217-
end
218-
219-
---@param root TSNode
220-
M.handle_latex = function(root)
221-
if vim.fn.executable('latex2text') ~= 1 then
222-
return
223-
end
224-
225-
local latex = vim.treesitter.get_node_text(root, 0)
226-
local expression = vim.trim(vim.fn.system('latex2text', latex))
227-
local extra_space = vim.fn.strdisplaywidth(latex) - vim.fn.strdisplaywidth(expression)
228-
if extra_space < 0 then
229-
return
230-
end
231-
232-
local start_row, start_col, end_row, end_col = root:range()
233-
local virt_text = { expression .. string.rep(' ', extra_space), state.config.highlights.latex }
234-
vim.api.nvim_buf_set_extmark(0, M.namespace, start_row, start_col, {
235-
end_row = end_row,
236-
end_col = end_col,
237-
virt_text = { virt_text },
238-
virt_text_pos = 'overlay',
239-
})
240114
end
241115

242116
return M

lua/render-markdown/ui.lua

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
local list = require('render-markdown.list')
2+
local state = require('render-markdown.state')
3+
4+
local M = {}
5+
6+
M.namespace = vim.api.nvim_create_namespace('render-markdown.nvim')
7+
8+
M.clear = function()
9+
-- Remove existing highlights / virtual text
10+
vim.api.nvim_buf_clear_namespace(0, M.namespace, 0, -1)
11+
end
12+
13+
M.refresh = function()
14+
if not state.enabled then
15+
return
16+
end
17+
if not vim.tbl_contains(state.config.file_types, vim.bo.filetype) then
18+
return
19+
end
20+
-- Needs to happen after file_type check and before mode check
21+
M.clear()
22+
if not vim.tbl_contains(state.config.render_modes, vim.fn.mode()) then
23+
return
24+
end
25+
26+
vim.treesitter.get_parser():for_each_tree(function(tree, language_tree)
27+
local language = language_tree:lang()
28+
if language == 'markdown' then
29+
M.markdown(tree:root())
30+
elseif language == 'latex' then
31+
M.latex(tree:root())
32+
end
33+
end)
34+
end
35+
36+
---@param root TSNode
37+
M.markdown = function(root)
38+
local highlights = state.config.highlights
39+
---@diagnostic disable-next-line: missing-parameter
40+
for id, node in state.markdown_query:iter_captures(root, 0) do
41+
local capture = state.markdown_query.captures[id]
42+
local value = vim.treesitter.get_node_text(node, 0)
43+
local start_row, start_col, end_row, end_col = node:range()
44+
45+
if capture == 'heading' then
46+
local level = #value
47+
local heading = list.cycle(state.config.headings, level)
48+
local background = list.clamp_last(highlights.heading.backgrounds, level)
49+
local foreground = list.clamp_last(highlights.heading.foregrounds, level)
50+
51+
local virt_text = { string.rep(' ', level - 1) .. heading, { foreground, background } }
52+
vim.api.nvim_buf_set_extmark(0, M.namespace, start_row, 0, {
53+
end_row = end_row + 1,
54+
end_col = 0,
55+
hl_group = background,
56+
virt_text = { virt_text },
57+
virt_text_pos = 'overlay',
58+
hl_eol = true,
59+
})
60+
elseif capture == 'code' then
61+
vim.api.nvim_buf_set_extmark(0, M.namespace, start_row, 0, {
62+
end_row = end_row,
63+
end_col = 0,
64+
hl_group = highlights.code,
65+
hl_eol = true,
66+
})
67+
elseif capture == 'list_marker' then
68+
-- List markers from tree-sitter should have leading spaces removed, however there are known
69+
-- edge cases in the parser: https://github.com/tree-sitter-grammars/tree-sitter-markdown/issues/127
70+
-- As a result we handle leading spaces here, can remove if this gets fixed upstream
71+
local _, leading_spaces = value:find('^%s*')
72+
local virt_text = { string.rep(' ', leading_spaces or 0) .. state.config.bullet, highlights.bullet }
73+
vim.api.nvim_buf_set_extmark(0, M.namespace, start_row, start_col, {
74+
end_row = end_row,
75+
end_col = end_col,
76+
virt_text = { virt_text },
77+
virt_text_pos = 'overlay',
78+
})
79+
elseif vim.tbl_contains({ 'table_head', 'table_delim', 'table_row' }, capture) then
80+
local row = value:gsub('|', '')
81+
if capture == 'table_delim' then
82+
-- Order matters here, in particular handling inner intersections before left & right
83+
row = row:gsub('-', '')
84+
:gsub(' ', '')
85+
:gsub('─│─', '─┼─')
86+
:gsub('│─', '├─')
87+
:gsub('─│', '─┤')
88+
end
89+
90+
local highlight = highlights.table.head
91+
if capture == 'table_row' then
92+
highlight = highlights.table.row
93+
end
94+
95+
local virt_text = { row, highlight }
96+
vim.api.nvim_buf_set_extmark(0, M.namespace, start_row, start_col, {
97+
end_row = end_row,
98+
end_col = end_col,
99+
virt_text = { virt_text },
100+
virt_text_pos = 'overlay',
101+
})
102+
else
103+
-- Should only get here if user provides custom capture, currently unhandled
104+
vim.print('Unhandled capture: ' .. capture)
105+
end
106+
end
107+
end
108+
109+
---@param root TSNode
110+
M.latex = function(root)
111+
if vim.fn.executable('latex2text') ~= 1 then
112+
return
113+
end
114+
115+
local latex = vim.treesitter.get_node_text(root, 0)
116+
local expression = vim.trim(vim.fn.system('latex2text', latex))
117+
local extra_space = vim.fn.strdisplaywidth(latex) - vim.fn.strdisplaywidth(expression)
118+
if extra_space < 0 then
119+
return
120+
end
121+
122+
local start_row, start_col, end_row, end_col = root:range()
123+
local virt_text = { expression .. string.rep(' ', extra_space), state.config.highlights.latex }
124+
vim.api.nvim_buf_set_extmark(0, M.namespace, start_row, start_col, {
125+
end_row = end_row,
126+
end_col = end_col,
127+
virt_text = { virt_text },
128+
virt_text_pos = 'overlay',
129+
})
130+
end
131+
132+
return M

0 commit comments

Comments
 (0)