Skip to content

Commit da260dd

Browse files
feat: left padding for checkboxes
## Details Adds `checkbox.left_pad` option, makes things consistent as all other configurations that support one side also support the other. Required a bit of refactoring due to wanting to use `overlay` text when possible as `inline` does not behave super well with the cursor. To make this happen added `sub` function to the `Line` class which can get a subset of a line while correctly using utf code points rather than byte widths. Added a similar function to the `str` helper module which functions on individual strings and is what the `Line` class relies on. Currently only used for checkboxes but made available through helpers as the pattern is likely to come up again.
1 parent ea3678d commit da260dd

File tree

10 files changed

+79
-48
lines changed

10 files changed

+79
-48
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,8 @@ require('render-markdown').setup({
594594
render_modes = false,
595595
-- Render the bullet point before the checkbox.
596596
bullet = false,
597+
-- Padding to add to the left of checkboxes.
598+
left_pad = 0,
597599
-- Padding to add to the right of checkboxes.
598600
right_pad = 1,
599601
unchecked = {
@@ -1268,6 +1270,8 @@ require('render-markdown').setup({
12681270
render_modes = false,
12691271
-- Render the bullet point before the checkbox.
12701272
bullet = false,
1273+
-- Padding to add to the left of checkboxes.
1274+
left_pad = 0,
12711275
-- Padding to add to the right of checkboxes.
12721276
right_pad = 1,
12731277
unchecked = {

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 NVIM v0.11.4 Last change: 2025 October 06
1+
*render-markdown.txt* For NVIM v0.11.4 Last change: 2025 October 07
22

33
==============================================================================
44
Table of Contents *render-markdown-table-of-contents*
@@ -656,6 +656,8 @@ Default Configuration ~
656656
render_modes = false,
657657
-- Render the bullet point before the checkbox.
658658
bullet = false,
659+
-- Padding to add to the left of checkboxes.
660+
left_pad = 0,
659661
-- Padding to add to the right of checkboxes.
660662
right_pad = 1,
661663
unchecked = {
@@ -1318,6 +1320,8 @@ Checkbox Configuration ~
13181320
render_modes = false,
13191321
-- Render the bullet point before the checkbox.
13201322
bullet = false,
1323+
-- Padding to add to the left of checkboxes.
1324+
left_pad = 0,
13211325
-- Padding to add to the right of checkboxes.
13221326
right_pad = 1,
13231327
unchecked = {

lua/render-markdown/health.lua

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

88
---@private
9-
M.version = '8.9.0'
9+
M.version = '8.9.1'
1010

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

lua/render-markdown/lib/config.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ end
5656

5757
---@return render.md.Line
5858
function Config:line()
59-
return Line.new(self)
59+
return Line.new(self.padding.highlight)
6060
end
6161

6262
---@param destination string

lua/render-markdown/lib/line.lua

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
local interval = require('render-markdown.lib.interval')
12
local str = require('render-markdown.lib.str')
23

34
---@class render.md.Line
@@ -6,11 +7,11 @@ local str = require('render-markdown.lib.str')
67
local Line = {}
78
Line.__index = Line
89

9-
---@param config render.md.buf.Config
10+
---@param highlight string
1011
---@return render.md.Line
11-
function Line.new(config)
12+
function Line.new(highlight)
1213
local self = setmetatable({}, Line)
13-
self.highlight = config.padding.highlight
14+
self.highlight = highlight
1415
self.line = {}
1516
return self
1617
end
@@ -39,9 +40,24 @@ end
3940

4041
---@return render.md.Line
4142
function Line:copy()
42-
local result = setmetatable({}, Line)
43-
result.highlight = self.highlight
44-
result.line = vim.list_extend({}, self.line)
43+
return Line.new(self.highlight):extend(self)
44+
end
45+
46+
---@param i integer 1-based inclusive
47+
---@param j integer 1-based inclusive
48+
---@return render.md.Line
49+
function Line:sub(i, j)
50+
local result = Line.new(self.highlight)
51+
local position = 0
52+
for _, text in ipairs(self.line) do
53+
local length = str.width(text[1])
54+
local range = { i - position, j - position } ---@type render.md.Range
55+
local overlap = interval.overlap({ 1, length }, range)
56+
if overlap then
57+
result:add(str.sub(text[1], overlap[1], overlap[2]), text[2])
58+
end
59+
position = position + length
60+
end
4561
return result
4662
end
4763

lua/render-markdown/lib/str.lua

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ function M.split(s, sep, trimempty)
99
return vim.split(s, sep, { plain = true, trimempty = trimempty })
1010
end
1111

12+
---@param s string
13+
---@param i integer 1-based inclusive
14+
---@param j integer 1-based inclusive
15+
---@return string
16+
function M.sub(s, i, j)
17+
local bytes = vim.str_utf_pos(s)
18+
local start_byte = bytes[i]
19+
local end_byte = j < #bytes and bytes[j + 1] - 1 or #s
20+
return s:sub(start_byte, end_byte)
21+
end
22+
1223
---number of hashtags at the start of the string
1324
---@param s string
1425
---@return integer

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

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -79,49 +79,40 @@ end
7979

8080
---@private
8181
function Render:checkbox()
82+
-- add 1 to end to account for space after checkbox
8283
local node = self.data.checkbox
83-
local right = self.config.right_pad
84-
85-
-- add 1 to account for space after checkbox
84+
local row = node.start_row
85+
local start_col = node.start_col
86+
local end_col = node.end_col + 1
8687
local width = self.context:width(node) + 1
87-
local space = width - str.width(self.data.icon)
8888

89-
local line = self:line():text(self.data.icon, self.data.highlight)
90-
if space < 0 then
91-
-- not enough space to fit the icon in-place
92-
self.marks:over(self.config, 'check_icon', node, {
93-
virt_text = line:pad(right):get(),
94-
virt_text_pos = 'inline',
95-
conceal = '',
96-
}, { 0, 0, 0, 1 })
97-
else
98-
local fits = math.min(space, right) ---@type integer
99-
space = space - fits
100-
right = right - fits
101-
102-
local row = node.start_row
103-
local start_col = node.start_col
104-
local end_col = node.end_col + 1
89+
local line = self:line()
90+
:pad(self.config.left_pad)
91+
:text(self.data.icon, self.data.highlight)
92+
:pad(self.config.right_pad)
10593

94+
local overlay = line:sub(1, width)
95+
if not overlay:empty() then
10696
self.marks:add(self.config, 'check_icon', row, start_col, {
107-
end_col = end_col - space,
108-
virt_text = line:pad(fits):get(),
97+
virt_text = overlay:get(),
10998
virt_text_pos = 'overlay',
11099
})
111-
if space > 0 then
112-
-- remove extra space after the icon
113-
self.marks:add(self.config, 'check_icon', row, end_col - space, {
114-
end_col = end_col,
115-
conceal = '',
116-
})
117-
end
118-
if right > 0 then
119-
-- add padding
120-
self.marks:add(self.config, 'check_icon', row, end_col, {
121-
virt_text = self:line():pad(right):get(),
122-
virt_text_pos = 'inline',
123-
})
124-
end
100+
end
101+
102+
local inline = line:sub(width + 1, line:width())
103+
if not inline:empty() then
104+
self.marks:add(self.config, 'check_icon', row, end_col, {
105+
virt_text = inline:get(),
106+
virt_text_pos = 'inline',
107+
})
108+
end
109+
110+
local space = width - overlay:width()
111+
if space > 0 then
112+
self.marks:add(self.config, 'check_icon', row, end_col - space, {
113+
end_col = end_col,
114+
conceal = '',
115+
})
125116
end
126117
end
127118

lua/render-markdown/settings.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ M.checkbox = {}
275275

276276
---@class (exact) render.md.checkbox.Config: render.md.base.Config
277277
---@field bullet boolean
278+
---@field left_pad number
278279
---@field right_pad integer
279280
---@field unchecked render.md.checkbox.component.Config
280281
---@field checked render.md.checkbox.component.Config
@@ -302,6 +303,8 @@ M.checkbox.default = {
302303
render_modes = false,
303304
-- Render the bullet point before the checkbox.
304305
bullet = false,
306+
-- Padding to add to the left of checkboxes.
307+
left_pad = 0,
305308
-- Padding to add to the right of checkboxes.
306309
right_pad = 1,
307310
unchecked = {
@@ -354,6 +357,7 @@ function M.checkbox.schema()
354357
}
355358
return M.base.schema({
356359
bullet = { type = 'boolean' },
360+
left_pad = { type = 'number' },
357361
right_pad = { type = 'number' },
358362
unchecked = component,
359363
checked = component,

lua/render-markdown/types.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070

7171
---@class (exact) render.md.checkbox.UserConfig: render.md.base.UserConfig
7272
---@field bullet? boolean
73+
---@field left_pad? number
7374
---@field right_pad? integer
7475
---@field unchecked? render.md.checkbox.component.UserConfig
7576
---@field checked? render.md.checkbox.component.UserConfig

tests/demo/box_dash_quote_spec.lua

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,19 @@ 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, 0), { 2, 5 }, {
16+
marks:add(row:get(0), 2, {
1717
virt_text = { { '󰄱 ', 'RmUnchecked' }, { ' ', 'Normal' } },
1818
virt_text_pos = 'overlay',
1919
})
2020
marks:add(row:get(0, 0), { 5, 6 }, util.conceal())
2121
marks:add(row:get(1, 0), { 0, 2 }, util.conceal())
22-
marks:add(row:get(0, 0), { 2, 5 }, {
22+
marks:add(row:get(0), 2, {
2323
virt_text = { { '󰱒 ', 'RmChecked' }, { ' ', 'Normal' } },
2424
virt_text_pos = 'overlay',
2525
})
2626
marks:add(row:get(0, 0), { 5, 6 }, util.conceal())
2727
marks:add(row:get(1, 0), { 0, 2 }, util.conceal())
28-
marks:add(row:get(0, 0), { 2, 6 }, {
28+
marks:add(row:get(0), 2, {
2929
virt_text = { { '󰥔 ', 'RmTodo' } },
3030
virt_text_pos = 'overlay',
3131
})

0 commit comments

Comments
 (0)