Skip to content

Commit 275f289

Browse files
feat: position property for checkboxes
# Details Submitted as a bug: #140 When checkboxes are rendered they are padded with spaces to fill the width of the underlying text. This is because most icons have a width of 2, while the text forming the checkbox (like [x]) has a width of 3. If spaces were not added using overlay virtual text would result in one of the square brackets poking through depending on the side of the padding. We can use inline virtual text which was added in neovim 0.10.0 to fully hide the underlying text and insert the virtual text, shifting text as needed, however on its own this would remove the feature for users of older versions of neovim. To get around this I have added a position property to the checkbox config which defaults to the new value of 'inline' which results in nicer alignment of checkboxes. The value 'overlay' is available and will result in identical behavior to before this change if users prefer it. Additionally when initializing the config we check the neovim version and change configuration values that are known to not work with older versions to availble alternatives. Currently this is done for: - checkbox position -> 'overlay' so as not to remove the feature and require users to manually modify their config if they run neovim < 0.10.0 - code position -> 'right' since left alignment requires shifting text while right does not, adding a feature to users if they did not dive too deep into the options available for this plugin Overall this updates behavior for anyone that can use it and keeps the old behavior around either out of necessity (version) or personal preference.
1 parent d1cec33 commit 275f289

13 files changed

+93
-64
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
- wiki links nested in tables [72688ba](https://github.com/MeanderingProgrammer/render-markdown.nvim/commit/72688baea4ef0ed605033bf654b54d801b6a5f01)
1515
- code block background when indented in lists [#133](https://github.com/MeanderingProgrammer/render-markdown.nvim/issues/133)
1616
[4c823b1](https://github.com/MeanderingProgrammer/render-markdown.nvim/commit/4c823b1df151dbf1ed3ddaacac517be606b1e145)
17+
[d1cec33](https://github.com/MeanderingProgrammer/render-markdown.nvim/commit/d1cec33f0d59bac5c2854312d2ea0483b44dfd11)
1718
- do not set noref in vim.deepcopy [#139](https://github.com/MeanderingProgrammer/render-markdown.nvim/pull/139)
1819
- gate virt_text_repeat_linebreak to neovim >= 0.10.0 [98f9965](https://github.com/MeanderingProgrammer/render-markdown.nvim/commit/98f996563591b753470942165d2d5134df868529)
1920
- account for folds when computing visible range [#138](https://github.com/MeanderingProgrammer/render-markdown.nvim/issues/138)

README.md

+15-14
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,13 @@ Plugin to improve viewing Markdown files in Neovim
1818
> plugins = { markdown = true },
1919
> })
2020
> ```
21-
>
22-
> For `rocks.nvim` users migrate to the new [LuaRock](https://luarocks.org/modules/MeanderingProgrammer/render-markdown.nvim).
23-
>
24-
> ```vim
25-
> :Rocks prune markdown.nvim
26-
> :Rocks install render-markdown.nvim
27-
> ```
2821
2922
<!-- panvimdoc-ignore-start -->
3023
3124
| | |
3225
| --------- | ------- |
3326
| ![Heading](https://github.com/user-attachments/assets/663a34c5-0438-4688-8204-332065f65835) | ![Table](https://github.com/user-attachments/assets/162986e1-91f0-4e13-a83f-6183d58b0fcb) |
34-
| ![Quote](https://github.com/user-attachments/assets/3f76e73e-b3a0-4cd8-90c1-208c5070659c) | ![LaTeX](https://github.com/user-attachments/assets/9e8909e4-7256-45fc-b481-4aba8850ebc3) |
27+
| ![Quote](https://github.com/user-attachments/assets/5343ca45-d5e1-4de0-9065-46499e1ed919) | ![LaTeX](https://github.com/user-attachments/assets/9e8909e4-7256-45fc-b481-4aba8850ebc3) |
3528
| ![Callout](https://github.com/user-attachments/assets/4324ea72-a017-4175-9f9d-363da5e5f6ba) | |
3629
3730
<!-- panvimdoc-ignore-end -->
@@ -41,10 +34,10 @@ Plugin to improve viewing Markdown files in Neovim
4134
- Functions entirely inside of Neovim with no external windows
4235
- Changes between `rendered` view in normal mode and `raw` view in all other modes
4336
- Supports anti-conceal behavior, removing any virtual text added by this plugin
44-
on the line the cursor is on, this does have a performance penalty and can be disabled
37+
on the line the cursor is on, this can be disabled
4538
- Changes window options between `rendered` and `raw` view based on configuration
4639
- Effects `conceallevel` & `concealcursor` by default
47-
- Supports rendering `markdown` injected into other file types
40+
- Supports rendering `markdown` injected into any file type
4841
- Renders the following `markdown` components:
4942
- Headings: highlight depending on level and replaces `#` with icon
5043
- Horizontal breaks: replace with full-width lines
@@ -57,8 +50,8 @@ Plugin to improve viewing Markdown files in Neovim
5750
- Block quotes: replace leading `>` with provided icon
5851
- Tables: replace border characters, handles misaligned tables but does NOT align
5952
according to delimiter indicator
60-
- [Callouts](https://github.com/orgs/community/discussions/16925)
61-
- Github & Obsidian out of the box, supports user defined as well
53+
- [Callouts](https://github.com/orgs/community/discussions/16925): Github & Obsidian
54+
out of the box, supports user defined as well
6255
- Custom checkbox states [^1], function similar to `callouts`
6356
- Adds icon before images / links [^1]
6457
- `LaTeX` blocks: renders formulas if `latex` parser and `pylatexenc` are installed
@@ -241,7 +234,7 @@ require('render-markdown').setup({
241234
enabled = true,
242235
-- Turn on / off any sign column related rendering
243236
sign = true,
244-
-- Determines how the icon fills the available space:
237+
-- Determines how icons fill the available space:
245238
-- inline: underlying '#'s are concealed resulting in a left aligned icon
246239
-- overlay: result is left padded with spaces to hide any additional '#'
247240
position = 'overlay',
@@ -367,6 +360,10 @@ require('render-markdown').setup({
367360
checkbox = {
368361
-- Turn on / off checkbox state rendering
369362
enabled = true,
363+
-- Determines how icons fill the available space:
364+
-- inline: underlying text is concealed resulting in a left aligned icon
365+
-- overlay: result is left padded with spaces to hide any additional text
366+
position = 'inline',
370367
unchecked = {
371368
-- Replaces '[ ]' of 'task_list_marker_unchecked'
372369
icon = '󰄱 ',
@@ -546,7 +543,7 @@ require('render-markdown').setup({
546543
enabled = true,
547544
-- Turn on / off any sign column related rendering
548545
sign = true,
549-
-- Determines how the icon fills the available space:
546+
-- Determines how icons fill the available space:
550547
-- inline: underlying '#'s are concealed resulting in a left aligned icon
551548
-- overlay: result is left padded with spaces to hide any additional '#'
552549
position = 'overlay',
@@ -708,6 +705,10 @@ require('render-markdown').setup({
708705
checkbox = {
709706
-- Turn on / off checkbox state rendering
710707
enabled = true,
708+
-- Determines how icons fill the available space:
709+
-- inline: underlying text is concealed resulting in a left aligned icon
710+
-- overlay: result is left padded with spaces to hide any additional text
711+
position = 'inline',
711712
unchecked = {
712713
-- Replaces '[ ]' of 'task_list_marker_unchecked'
713714
icon = '󰄱 ',

benches/readme_spec.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ local util = require('benches.util')
44

55
describe('README.md', function()
66
it('default', function()
7-
local base_marks = 50
7+
local base_marks = 53
88
util.less_than(util.setup('README.md'), 50)
99
util.num_marks(base_marks)
1010

demo/box_dash_quote.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- [ ] Unchecked Checkbox
44
- [x] Checked Checkbox
55
- [-] Todo Checkbox
6+
- Regular List Item
67

78
---
89

doc/render-markdown.txt

+15-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*render-markdown.txt* For 0.10.0 Last change: 2024 August 19
1+
*render-markdown.txt* For 0.10.0 Last change: 2024 August 20
22

33
==============================================================================
44
Table of Contents *render-markdown-table-of-contents*
@@ -47,23 +47,17 @@ Plugin to improve viewing Markdown files in Neovim
4747
plugins = { markdown = true },
4848
})
4949
<
50-
For `rocks.nvim` users migrate to the new LuaRock
51-
<https://luarocks.org/modules/MeanderingProgrammer/render-markdown.nvim>.
52-
>vim
53-
:Rocks prune markdown.nvim
54-
:Rocks install render-markdown.nvim
55-
<
5650

5751
==============================================================================
5852
2. Features *render-markdown-features*
5953

6054
- Functions entirely inside of Neovim with no external windows
6155
- Changes between `rendered` view in normal mode and `raw` view in all other modes
6256
- Supports anti-conceal behavior, removing any virtual text added by this plugin
63-
on the line the cursor is on, this does have a performance penalty and can be disabled
57+
on the line the cursor is on, this can be disabled
6458
- Changes window options between `rendered` and `raw` view based on configuration
6559
- Effects `conceallevel` & `concealcursor` by default
66-
- Supports rendering `markdown` injected into other file types
60+
- Supports rendering `markdown` injected into any file type
6761
- Renders the following `markdown` components:
6862
- Headings: highlight depending on level and replaces `#` with icon
6963
- Horizontal breaks: replace with full-width lines
@@ -76,8 +70,8 @@ Plugin to improve viewing Markdown files in Neovim
7670
- Block quotes: replace leading `>` with provided icon
7771
- Tables: replace border characters, handles misaligned tables but does NOT align
7872
according to delimiter indicator
79-
- Callouts <https://github.com/orgs/community/discussions/16925>
80-
- Github & Obsidian out of the box, supports user defined as well
73+
- Callouts <https://github.com/orgs/community/discussions/16925>: Github & Obsidian
74+
out of the box, supports user defined as well
8175
- Custom checkbox states , function similar to `callouts`
8276
- Adds icon before images / links
8377
- `LaTeX` blocks: renders formulas if `latex` parser and `pylatexenc` are installed
@@ -270,7 +264,7 @@ Full Default Configuration ~
270264
enabled = true,
271265
-- Turn on / off any sign column related rendering
272266
sign = true,
273-
-- Determines how the icon fills the available space:
267+
-- Determines how icons fill the available space:
274268
-- inline: underlying '#'s are concealed resulting in a left aligned icon
275269
-- overlay: result is left padded with spaces to hide any additional '#'
276270
position = 'overlay',
@@ -396,6 +390,10 @@ Full Default Configuration ~
396390
checkbox = {
397391
-- Turn on / off checkbox state rendering
398392
enabled = true,
393+
-- Determines how icons fill the available space:
394+
-- inline: underlying text is concealed resulting in a left aligned icon
395+
-- overlay: result is left padded with spaces to hide any additional text
396+
position = 'inline',
399397
unchecked = {
400398
-- Replaces '[ ]' of 'task_list_marker_unchecked'
401399
icon = '󰄱 ',
@@ -575,7 +573,7 @@ Wiki Page
575573
enabled = true,
576574
-- Turn on / off any sign column related rendering
577575
sign = true,
578-
-- Determines how the icon fills the available space:
576+
-- Determines how icons fill the available space:
579577
-- inline: underlying '#'s are concealed resulting in a left aligned icon
580578
-- overlay: result is left padded with spaces to hide any additional '#'
581579
position = 'overlay',
@@ -745,6 +743,10 @@ Wiki Page
745743
checkbox = {
746744
-- Turn on / off checkbox state rendering
747745
enabled = true,
746+
-- Determines how icons fill the available space:
747+
-- inline: underlying text is concealed resulting in a left aligned icon
748+
-- overlay: result is left padded with spaces to hide any additional text
749+
position = 'inline',
748750
unchecked = {
749751
-- Replaces '[ ]' of 'task_list_marker_unchecked'
750752
icon = '󰄱 ',

lua/render-markdown/handler/markdown.lua

+15-12
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ function Handler:heading(info)
111111
-- Overwrite anything beyond width with Normal
112112
self:add(true, info.start_row, 0, {
113113
priority = 0,
114-
virt_text = { { str.pad(vim.o.columns * 2), 'Normal' } },
114+
virt_text = { { str.spaces(vim.o.columns * 2), 'Normal' } },
115115
virt_text_win_col = width,
116116
})
117117
end
@@ -123,7 +123,7 @@ function Handler:heading(info)
123123
if heading.left_pad > 0 then
124124
self:add(false, info.start_row, 0, {
125125
priority = 0,
126-
virt_text = { { str.pad(heading.left_pad), background } },
126+
virt_text = { { str.spaces(heading.left_pad), background } },
127127
virt_text_pos = 'inline',
128128
})
129129
end
@@ -346,7 +346,7 @@ function Handler:code_background(code_block, icon_added)
346346
end
347347
end
348348

349-
local padding = str.pad(vim.o.columns * 2)
349+
local padding = str.spaces(vim.o.columns * 2)
350350
for row = code_block.start_row, code_block.end_row - 1 do
351351
self:add(false, row, code_block.col, {
352352
end_row = row + 1,
@@ -375,8 +375,8 @@ function Handler:code_left_pad(code_block, add_background)
375375

376376
-- Use low priority to include other marks in padding when code block is at edge
377377
local priority = code_block.col == 0 and 0 or nil
378-
local outer_text = { str.pad(code_block.col), 'Normal' }
379-
local left_text = { str.pad(code.left_pad), add_background and code.highlight or 'Normal' }
378+
local outer_text = { str.spaces(code_block.col), 'Normal' }
379+
local left_text = { str.spaces(code.left_pad), add_background and code.highlight or 'Normal' }
380380

381381
for row = code_block.start_row, code_block.end_row - 1 do
382382
local virt_text = {}
@@ -446,13 +446,13 @@ function Handler:list_marker(info)
446446
if bullet.left_pad > 0 then
447447
self:add(false, info.start_row, 0, {
448448
priority = 0,
449-
virt_text = { { str.pad(bullet.left_pad), 'Normal' } },
449+
virt_text = { { str.spaces(bullet.left_pad), 'Normal' } },
450450
virt_text_pos = 'inline',
451451
})
452452
end
453453
if bullet.right_pad > 0 then
454454
self:add(true, info.start_row, info.end_col - 1, {
455-
virt_text = { { str.pad(bullet.right_pad), 'Normal' } },
455+
virt_text = { { str.spaces(bullet.right_pad), 'Normal' } },
456456
virt_text_pos = 'inline',
457457
})
458458
end
@@ -461,16 +461,19 @@ end
461461

462462
---@private
463463
---@param info render.md.NodeInfo
464-
---@param checkbox_state render.md.CheckboxComponent
465-
function Handler:checkbox(info, checkbox_state)
464+
---@param checkbox render.md.CheckboxComponent
465+
function Handler:checkbox(info, checkbox)
466466
if not self.config.checkbox.enabled then
467467
return
468468
end
469+
local inline = self.config.checkbox.position == 'inline'
470+
local icon, highlight = checkbox.icon, checkbox.highlight
469471
self:add(true, info.start_row, info.start_col, {
470472
end_row = info.end_row,
471473
end_col = info.end_col,
472-
virt_text = { { str.pad_to(info.text, checkbox_state.icon), checkbox_state.highlight } },
473-
virt_text_pos = 'overlay',
474+
virt_text = { { inline and icon or str.pad_to(info.text, icon), highlight } },
475+
virt_text_pos = inline and 'inline' or 'overlay',
476+
conceal = inline and '' or nil,
474477
})
475478
end
476479

@@ -583,7 +586,7 @@ function Handler:table_row(delim, row)
583586
local offset = delim.columns[i].width - column.width
584587
if offset > 0 then
585588
self:add(true, column.info.start_row, column.info.end_col - 1, {
586-
virt_text = { { str.pad(offset), pipe_table.filler } },
589+
virt_text = { { str.spaces(offset), pipe_table.filler } },
587590
virt_text_pos = 'inline',
588591
})
589592
end

lua/render-markdown/handler/markdown_inline.lua

+3-1
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,12 @@ function Handler:checkbox(info, checkbox)
128128
if not self.config.checkbox.enabled then
129129
return
130130
end
131+
local inline = self.config.checkbox.position == 'inline'
132+
local icon, highlight = checkbox.rendered, checkbox.highlight
131133
self:add(info.start_row, info.start_col, {
132134
end_row = info.end_row,
133135
end_col = info.end_col,
134-
virt_text = { { str.pad_to(info.text, checkbox.rendered), checkbox.highlight } },
136+
virt_text = { { inline and icon or str.pad_to(info.text, icon), highlight } },
135137
virt_text_pos = 'inline',
136138
conceal = '',
137139
})

lua/render-markdown/health.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ local M = {}
55

66
---@private
77
---@type string
8-
M.version = '6.1.8'
8+
M.version = '6.1.9'
99

1010
function M.check()
1111
vim.health.start('render-markdown.nvim [version]')

lua/render-markdown/init.lua

+8-1
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,11 @@ local M = {}
6868
---@field public icon? string
6969
---@field public highlight? string
7070

71+
---@alias render.md.checkbox.Position 'overlay'|'inline'
72+
7173
---@class (exact) render.md.UserCheckbox
7274
---@field public enabled? boolean
75+
---@field public position? render.md.checkbox.Position
7376
---@field public unchecked? render.md.UserCheckboxComponent
7477
---@field public checked? render.md.UserCheckboxComponent
7578
---@field public custom? table<string, render.md.CustomComponent>
@@ -264,7 +267,7 @@ M.default_config = {
264267
enabled = true,
265268
-- Turn on / off any sign column related rendering
266269
sign = true,
267-
-- Determines how the icon fills the available space:
270+
-- Determines how icons fill the available space:
268271
-- inline: underlying '#'s are concealed resulting in a left aligned icon
269272
-- overlay: result is left padded with spaces to hide any additional '#'
270273
position = 'overlay',
@@ -390,6 +393,10 @@ M.default_config = {
390393
checkbox = {
391394
-- Turn on / off checkbox state rendering
392395
enabled = true,
396+
-- Determines how icons fill the available space:
397+
-- inline: underlying text is concealed resulting in a left aligned icon
398+
-- overlay: result is left padded with spaces to hide any additional text
399+
position = 'inline',
393400
unchecked = {
394401
-- Replaces '[ ]' of 'task_list_marker_unchecked'
395402
icon = '󰄱 ',

lua/render-markdown/state.lua

+7
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ end
2626
---@param user_config render.md.UserConfig
2727
function M.setup(default_config, user_config)
2828
local config = vim.tbl_deep_extend('force', default_config, presets.get(user_config), user_config)
29+
-- Override settings that require neovim >= 0.10.0 and have compatible alternatives
30+
if not util.has_10 then
31+
config.code.position = 'right'
32+
config.checkbox.position = 'overlay'
33+
end
34+
2935
M.config = config
3036
M.enabled = config.enabled
3137
M.log_level = config.log_level
@@ -272,6 +278,7 @@ function M.validate()
272278
if checkbox ~= nil then
273279
append_errors(path .. '.checkbox', checkbox, {
274280
enabled = { checkbox.enabled, 'boolean', nilable },
281+
position = one_of(checkbox.position, { 'overlay', 'inline' }, {}, nilable),
275282
unchecked = { checkbox.unchecked, 'table', nilable },
276283
checked = { checkbox.checked, 'table', nilable },
277284
custom = { checkbox.custom, 'table', nilable },

lua/render-markdown/str.lua

+14-12
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,25 @@ function M.leading_spaces(s)
2424
return leading_spaces or 0
2525
end
2626

27-
---@param value string
27+
---@param n integer
28+
---@return string
29+
function M.spaces(n)
30+
return string.rep(' ', n)
31+
end
32+
33+
---@param target string
2834
---@param s string
2935
---@return string
30-
function M.pad_to(value, s)
31-
local padding = M.width(value) - M.width(s)
32-
return M.pad(padding, s)
36+
function M.pad_to(target, s)
37+
local n = M.width(target) - M.width(s)
38+
return M.pad(n, s)
3339
end
3440

35-
---@param padding integer
36-
---@param s? string
41+
---@param n integer
42+
---@param s string
3743
---@return string
38-
function M.pad(padding, s)
39-
local result = string.rep(' ', padding)
40-
if s ~= nil then
41-
result = result .. s
42-
end
43-
return result
44+
function M.pad(n, s)
45+
return M.spaces(n) .. s
4446
end
4547

4648
return M

0 commit comments

Comments
 (0)