Skip to content

Commit 973a5ac

Browse files
Add cell_style overlay / raw options
resolves: #40 ## Details Previously tables were always overlayed with text to keep the rendered look the same between mode changes. A couple of feature requests have come up to allow the underlying table highlights / conceals to still be visible. The cell_style 'overlay', which is the default value should result in no change to the rendering logic. The other option of 'raw' will overlay only the pipe icons rather than entire rows, allowing concealing and highlighting to still be visible. Other changes made around this are largely refactors: - Split unit tests into separate files, each file itself tests the rendering for a single file. Add a test utility module. - Split rendering into `render` and `render_node` methods to allow early returns when useful. - Move various methods in `handler/markdown.lua` into new `ts.lua` module to contain helpers for interacting with treesitter.
1 parent e353f1f commit 973a5ac

18 files changed

+1280
-790
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,10 @@ require('render-markdown').setup({
176176
-- normal: renders the rows of tables
177177
-- none: disables rendering, use this if you prefer having cell highlights
178178
table_style = 'full',
179+
-- Determines how table cells are rendered
180+
-- overlay: writes over the top of cells removing conealing and highlighting
181+
-- raw: will leave the cells as they and only replace table related symbols
182+
cell_style = 'overlay',
179183
-- Mapping from treesitter language to user defined handlers
180184
-- See 'Custom Handlers' section for more info
181185
custom_handlers = {},

doc/render-markdown.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*render-markdown.txt* For 0.10.0 Last change: 2024 June 13
1+
*render-markdown.txt* For 0.10.0 Last change: 2024 June 17
22

33
==============================================================================
44
Table of Contents *render-markdown-table-of-contents*
@@ -213,6 +213,10 @@ modified by the user.
213213
-- normal: renders the rows of tables
214214
-- none: disables rendering, use this if you prefer having cell highlights
215215
table_style = 'full',
216+
-- Determines how table cells are rendered
217+
-- overlay: writes over the top of cells removing conealing and highlighting
218+
-- raw: will leave the cells as they and only replace table related symbols
219+
cell_style = 'overlay',
216220
-- Mapping from treesitter language to user defined handlers
217221
-- See 'Custom Handlers' section for more info
218222
custom_handlers = {},

lua/render-markdown/handler/latex.lua

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,20 @@ M.render = function(namespace, root, buf)
1818
local converter = state.config.latex_converter
1919
if vim.fn.executable(converter) ~= 1 then
2020
logger.debug('Executable not found: ' .. converter)
21-
return
21+
else
22+
logger.debug_node('latex', root, buf)
23+
M.render_node(namespace, buf, root, converter)
2224
end
25+
end
2326

24-
local value = vim.treesitter.get_node_text(root, buf)
25-
local start_row, start_col, end_row, end_col = root:range()
26-
logger.debug_node('latex', root, buf)
27+
---@param namespace integer
28+
---@param buf integer
29+
---@param node TSNode
30+
---@param converter string
31+
M.render_node = function(namespace, buf, node, converter)
32+
local highlights = state.config.highlights
33+
local value = vim.treesitter.get_node_text(node, buf)
34+
local start_row, start_col, end_row, end_col = node:range()
2735

2836
local expressions = cache.expressions[value]
2937
if expressions == nil then
@@ -34,7 +42,7 @@ M.render = function(namespace, root, buf)
3442
end
3543

3644
local latex_lines = vim.tbl_map(function(expression)
37-
return { { expression, state.config.highlights.latex } }
45+
return { { expression, highlights.latex } }
3846
end, expressions)
3947
vim.api.nvim_buf_set_extmark(buf, namespace, start_row, start_col, {
4048
end_row = end_row,

lua/render-markdown/handler/markdown.lua

Lines changed: 186 additions & 179 deletions
Large diffs are not rendered by default.

lua/render-markdown/handler/markdown_inline.lua

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,42 @@ local M = {}
88
---@param root TSNode
99
---@param buf integer
1010
M.render = function(namespace, root, buf)
11-
local highlights = state.config.highlights
1211
for id, node in state.inline_query:iter_captures(root, buf) do
1312
local capture = state.inline_query.captures[id]
14-
local value = vim.treesitter.get_node_text(node, buf)
15-
local start_row, start_col, end_row, end_col = node:range()
1613
logger.debug_node(capture, node, buf)
14+
M.render_node(namespace, buf, capture, node)
15+
end
16+
end
17+
18+
---@param namespace integer
19+
---@param buf integer
20+
---@param capture string
21+
---@param node TSNode
22+
M.render_node = function(namespace, buf, capture, node)
23+
local highlights = state.config.highlights
24+
local value = vim.treesitter.get_node_text(node, buf)
25+
local start_row, start_col, end_row, end_col = node:range()
1726

18-
if capture == 'code' then
27+
if capture == 'code' then
28+
vim.api.nvim_buf_set_extmark(buf, namespace, start_row, start_col, {
29+
end_row = end_row,
30+
end_col = end_col,
31+
hl_group = highlights.code,
32+
})
33+
elseif capture == 'callout' then
34+
local key = callout.get_key_exact(value)
35+
if key ~= nil then
36+
local callout_text = { state.config.callout[key], highlights.callout[key] }
1937
vim.api.nvim_buf_set_extmark(buf, namespace, start_row, start_col, {
2038
end_row = end_row,
2139
end_col = end_col,
22-
hl_group = highlights.code,
40+
virt_text = { callout_text },
41+
virt_text_pos = 'overlay',
2342
})
24-
elseif capture == 'callout' then
25-
local key = callout.get_key_exact(value)
26-
if key ~= nil then
27-
local callout_text = { state.config.callout[key], highlights.callout[key] }
28-
vim.api.nvim_buf_set_extmark(buf, namespace, start_row, start_col, {
29-
end_row = end_row,
30-
end_col = end_col,
31-
virt_text = { callout_text },
32-
virt_text_pos = 'overlay',
33-
})
34-
end
35-
else
36-
-- Should only get here if user provides custom capture, currently unhandled
37-
logger.error('Unhandled inline capture: ' .. capture)
3843
end
44+
else
45+
-- Should only get here if user provides custom capture, currently unhandled
46+
logger.error('Unhandled inline capture: ' .. capture)
3947
end
4048
end
4149

lua/render-markdown/init.lua

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ local M = {}
6363
---@field public callout? render.md.UserCallout
6464
---@field public win_options? table<string, render.md.WindowOption>
6565
---@field public table_style? 'full'|'normal'|'none'
66+
---@field public cell_style? 'overlay'|'raw'
6667
---@field public custom_handlers? table<string, render.md.Handler>
6768
---@field public highlights? render.md.UserHighlights
6869

@@ -167,6 +168,10 @@ M.default_config = {
167168
-- normal: renders the rows of tables
168169
-- none: disables rendering, use this if you prefer having cell highlights
169170
table_style = 'full',
171+
-- Determines how table cells are rendered
172+
-- overlay: writes over the top of cells removing conealing and highlighting
173+
-- raw: will leave the cells as they and only replace table related symbols
174+
cell_style = 'overlay',
170175
-- Mapping from treesitter language to user defined handlers
171176
-- See 'Custom Handlers' section for more info
172177
custom_handlers = {},

lua/render-markdown/ts.lua

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
local M = {}
2+
3+
---@param node TSNode
4+
---@param targets string[]
5+
---@return boolean
6+
M.is_sibling = function(node, targets)
7+
local sibling = node:next_sibling()
8+
if sibling == nil then
9+
return false
10+
end
11+
return vim.tbl_contains(targets, sibling:type())
12+
end
13+
14+
--- Walk through all parent nodes and count the number of list nodes
15+
---@param node TSNode
16+
---@return integer
17+
M.get_list_level = function(node)
18+
local level = 0
19+
local parent = node:parent()
20+
while parent ~= nil do
21+
local parent_type = parent:type()
22+
if vim.tbl_contains({ 'section', 'document' }, parent_type) then
23+
-- reaching a section or document means we are clearly at the top of the list
24+
break
25+
elseif parent_type == 'list' then
26+
-- found a list increase the level and continue
27+
level = level + 1
28+
end
29+
parent = parent:parent()
30+
end
31+
return level
32+
end
33+
34+
--- Walk through parent nodes until target, return first found
35+
---@param node TSNode
36+
---@param target string
37+
---@return TSNode?
38+
M.get_parent = function(node, target)
39+
local parent = node:parent()
40+
while parent ~= nil do
41+
local parent_type = parent:type()
42+
if vim.tbl_contains({ 'section', 'document' }, parent_type) then
43+
-- reaching a section or document means we are clearly outside our target
44+
break
45+
elseif parent_type == target then
46+
return parent
47+
end
48+
parent = parent:parent()
49+
end
50+
return nil
51+
end
52+
53+
---@param node TSNode
54+
---@param target string
55+
---@return TSNode?
56+
M.get_child = function(node, target)
57+
for child in node:iter_children() do
58+
if child:type() == target then
59+
return child
60+
end
61+
end
62+
return nil
63+
end
64+
65+
---@param buf integer
66+
---@param row integer
67+
---@param s string
68+
---@return integer
69+
M.concealed = function(buf, row, s)
70+
local result = 0
71+
local col = 0
72+
for _, index in ipairs(vim.fn.str2list(s)) do
73+
local ch = vim.fn.nr2char(index)
74+
local captures = vim.treesitter.get_captures_at_pos(buf, row, col)
75+
for _, capture in ipairs(captures) do
76+
if capture.metadata.conceal ~= nil then
77+
result = result + vim.fn.strdisplaywidth(ch)
78+
end
79+
end
80+
col = col + #ch
81+
end
82+
return result
83+
end
84+
85+
return M

lua/render-markdown/types.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,6 @@
5050
---@field public callout render.md.Callout
5151
---@field public win_options table<string, render.md.WindowOption>
5252
---@field public table_style 'full'|'normal'|'none'
53+
---@field public cell_style 'overlay'|'raw'
5354
---@field public custom_handlers table<string, render.md.Handler>
5455
---@field public highlights render.md.Highlights

tests/box_dash_quote_spec.lua

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
local async_tests = require('plenary.async.tests')
2+
local util = require('tests.util')
3+
4+
async_tests.describe('box_dash_quote.md', function()
5+
async_tests.it('default', function()
6+
util.setup('demo/box_dash_quote.md')
7+
8+
local expected = {}
9+
10+
-- Heading
11+
vim.list_extend(expected, {
12+
{
13+
row = { 0, 1 },
14+
col = { 0, 0 },
15+
hl_eol = true,
16+
hl_group = 'DiffAdd',
17+
virt_text = { { '󰲡 ', { 'markdownH1', 'DiffAdd' } } },
18+
virt_text_pos = 'overlay',
19+
},
20+
})
21+
22+
-- Checkboxes
23+
vim.list_extend(expected, {
24+
-- Unchecked, conceal list marker
25+
{
26+
row = { 2, 2 },
27+
col = { 0, 2 },
28+
conceal = '',
29+
},
30+
-- Unchecked, checkbox
31+
{
32+
row = { 2, 2 },
33+
col = { 2, 5 },
34+
virt_text = { { ' 󰄱 ', '@markup.list.unchecked' } },
35+
virt_text_pos = 'overlay',
36+
},
37+
-- Checked, conceal list marker
38+
{
39+
row = { 3, 3 },
40+
col = { 0, 2 },
41+
conceal = '',
42+
},
43+
-- Checked, checkbox
44+
{
45+
row = { 3, 3 },
46+
col = { 2, 5 },
47+
virt_text = { { ' 󰱒 ', '@markup.heading' } },
48+
virt_text_pos = 'overlay',
49+
},
50+
})
51+
52+
-- Line break
53+
vim.list_extend(expected, {
54+
{
55+
row = { 5 },
56+
col = { 0 },
57+
virt_text = { { string.rep('', vim.opt.columns:get()), 'LineNr' } },
58+
virt_text_pos = 'overlay',
59+
},
60+
})
61+
62+
-- Quote lines
63+
vim.list_extend(expected, {
64+
{
65+
row = { 7, 7 },
66+
col = { 0, 4 },
67+
virt_text = { { '', '@markup.quote' } },
68+
virt_text_pos = 'overlay',
69+
},
70+
{
71+
row = { 8, 8 },
72+
col = { 0, 4 },
73+
virt_text = { { '', '@markup.quote' } },
74+
virt_text_pos = 'overlay',
75+
},
76+
})
77+
78+
local actual = util.get_actual_marks()
79+
util.marks_are_equal(expected, actual)
80+
end)
81+
end)

0 commit comments

Comments
 (0)