Skip to content

Commit 5adf089

Browse files
fix: handle checkbox rendering in single line nested lists
## Details Issue: #659 We render custom checkboxes by tracking the row in which they appear on and if that matches the start of a list item a checkbox is shown. However this does not properly handle the ability to nest list items all in one line, i.e. `- - - [-] triply nested custom checkbox`. Currently if this happens all of the marker nodes end up hidden, which is not the correct behavior. The fix is to add a check that the checkbox node starts immediately after the list item's marker node. Since the marker node absorbs additional space we don't need to do anything special to handle different amounts of whitespace. This should make the behavior between custom and regular checkboxes consistent as regular checkboxes must be at the start of a list according to the parser.
1 parent dcb7751 commit 5adf089

8 files changed

Lines changed: 71 additions & 21 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
[3f3eea9](https://github.com/MeanderingProgrammer/render-markdown.nvim/commit/3f3eea97b80839f629c951ca660ffd125bfa5b34)
1212
- allow rendering when window is in diff mode [#645](https://github.com/MeanderingProgrammer/render-markdown.nvim/issues/645)
1313
[629eb95](https://github.com/MeanderingProgrammer/render-markdown.nvim/commit/629eb9533ec989d9d5c6cab8f3ad5372422c24e0)
14+
- add snacks.nvim image as conflict if latex is enabled [dcb7751](https://github.com/MeanderingProgrammer/render-markdown.nvim/commit/dcb77511efb1665b5073d8396a0033b3e4d680f6)
1415

1516
### Bug Fixes
1617

doc/render-markdown.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
*render-markdown.txt* Improve viewing Markdown in Neovim
2-
For NVIM v0.12.2 Last change: 2026 May 24
2+
For NVIM v0.12.2 Last change: 2026 May 25
33

44
==============================================================================
55
Table of Contents *render-markdown-table-of-contents*

lua/render-markdown/handler/latex.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ function Handler:render(row, nodes)
195195
local _, line = node:line('below', 1)
196196
col = line and str.spaces('start', line) or 0
197197
else
198-
local _, line = node:line('above', 0)
198+
local _, line = node:line('first', 0)
199199
col = self.context:width({
200200
text = line and line:sub(1, node.start_col) or '',
201201
start_row = node.start_row,

lua/render-markdown/health.lua

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

1515
---@private
16-
M.version = '8.12.16'
16+
M.version = '8.12.17'
1717

1818
function M.check()
1919
M.start('versions')

lua/render-markdown/render/markdown/checkbox.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ function Render:setup()
3030
if not value then
3131
return false
3232
end
33+
if value.node.start_col ~= marker.end_col then
34+
return false
35+
end
3336
self.data = {
3437
marker = marker,
3538
checkbox = value.node,

tests/checkbox_spec.lua

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---@module 'luassert'
2+
3+
local util = require('tests.util')
4+
5+
describe('checkbox', function()
6+
it('handle nested list items', function()
7+
util.setup.text({
8+
'1. - [x] Checked Checkbox',
9+
'1. - [-] Todo Checkbox',
10+
})
11+
local marks = util.marks()
12+
marks:add({ 0, 0 }, { 0, 4 }, util.ordered(1))
13+
marks:add({ 0, 0 }, { 4, 7 }, util.conceal())
14+
marks:add(0, 7, util.checkbox('checked', 1))
15+
marks:add({ 0, 0 }, { 10, 11 }, util.conceal())
16+
marks:add({ 1, 1 }, { 0, 4 }, util.ordered(2))
17+
marks:add({ 1, 1 }, { 4, 7 }, util.conceal())
18+
marks:add(1, 7, util.checkbox('todo', 0))
19+
marks:add(1, 11, util.padding(1, false))
20+
util.assert_view(marks, {
21+
'1. 󰱒 Checked Checkbox',
22+
'2. 󰥔 Todo Checkbox',
23+
})
24+
end)
25+
26+
it('ignores invalid position', function()
27+
util.setup.text({
28+
'- Todo [-] Checkbox',
29+
})
30+
local marks = util.marks()
31+
marks:add({ 0, 0 }, { 0, 2 }, util.bullet(1))
32+
util.assert_view(marks, {
33+
'● Todo - Checkbox',
34+
})
35+
end)
36+
end)

tests/demo/box_dash_quote_spec.lua

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,14 @@ describe('demo/box_dash_quote.md', function()
1313
marks:add(row:get(0, 1), { 0, 0 }, util.heading.bg(1))
1414

1515
marks:add(row:get(1, 0), { 0, 2 }, util.conceal())
16-
marks:add(row:get(0), 2, {
17-
virt_text = { { '󰄱 ', 'RmUnchecked' }, { ' ', 'Normal' } },
18-
virt_text_pos = 'overlay',
19-
})
16+
marks:add(row:get(0), 2, util.checkbox('unchecked', 1))
2017
marks:add(row:get(0, 0), { 5, 6 }, util.conceal())
2118
marks:add(row:get(1, 0), { 0, 2 }, util.conceal())
22-
marks:add(row:get(0), 2, {
23-
virt_text = { { '󰱒 ', 'RmChecked' }, { ' ', 'Normal' } },
24-
virt_text_pos = 'overlay',
25-
})
19+
marks:add(row:get(0), 2, util.checkbox('checked', 1))
2620
marks:add(row:get(0, 0), { 5, 6 }, util.conceal())
2721
marks:add(row:get(1, 0), { 0, 2 }, util.conceal())
28-
marks:add(row:get(0), 2, {
29-
virt_text = { { '󰥔 ', 'RmTodo' } },
30-
virt_text_pos = 'overlay',
31-
})
32-
marks:add(row:get(0), 6, {
33-
virt_text = { { ' ', 'Normal' } },
34-
virt_text_pos = 'inline',
35-
})
22+
marks:add(row:get(0), 2, util.checkbox('todo', 0))
23+
marks:add(row:get(0), 6, util.padding(1, false))
3624
marks:add(row:get(1, 0), { 0, 2 }, util.bullet(1))
3725

3826
marks:add(row:get(2), 0, {

tests/util.lua

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,28 @@ function M.highlight(kind)
9898
}
9999
end
100100

101+
---@param kind 'checked'|'unchecked'|'todo'
102+
---@param space integer
103+
---@return vim.api.keyset.set_extmark
104+
function M.checkbox(kind, space)
105+
local line = {} ---@type render.md.mark.Line
106+
if kind == 'checked' then
107+
line[#line + 1] = { '󰱒 ', 'RmChecked' }
108+
elseif kind == 'unchecked' then
109+
line[#line + 1] = { '󰄱 ', 'RmUnchecked' }
110+
elseif kind == 'todo' then
111+
line[#line + 1] = { '󰥔 ', 'RmTodo' }
112+
end
113+
if space > 0 then
114+
line[#line + 1] = { (' '):rep(space), 'Normal' }
115+
end
116+
---@type vim.api.keyset.set_extmark
117+
return {
118+
virt_text = line,
119+
virt_text_pos = 'overlay',
120+
}
121+
end
122+
101123
---@param level integer
102124
---@param spaces? integer
103125
---@return vim.api.keyset.set_extmark
@@ -155,13 +177,13 @@ function M.quote(highlight)
155177
end
156178

157179
---@param spaces integer
158-
---@param priority? integer
180+
---@param priority? integer|false
159181
---@param highlight? string
160182
---@return vim.api.keyset.set_extmark
161183
function M.padding(spaces, priority, highlight)
162184
---@type vim.api.keyset.set_extmark
163185
return {
164-
priority = priority or 100,
186+
priority = priority or (priority ~= false and 100 or nil),
165187
virt_text = { { (' '):rep(spaces), highlight or 'Normal' } },
166188
virt_text_pos = 'inline',
167189
}

0 commit comments

Comments
 (0)