Skip to content

Commit 3adb9d5

Browse files
feat: apply bullet left & right pad to all lines of list items
## Details Issue: #181 Currently list item padding only works in and around a bullet point which causes wrapped lines to be ignored and as a result they end up offset. To fix this we need to get the range of the parent list item and apply padding over its range. However the ranges of list items encompass all their children. This means a simple approach would stack the padding resulting in overly indented text. To deal with this we need to base our end row off of both the list item as well the start of the next list if one exist. There was also some prioritization and column handling that needed to happen to support good interp with indent blank lines. To make this work the starting column needed to be based off of the starting column of the top most list for the current item. Then the left padding needs a low priority to move indent blank lines marks whereas the right padding uses the default priority so it does not further skew these marks. As part of this change handling list markers was moved into its own handler class like we've done to many other components.
1 parent 67288fe commit 3adb9d5

File tree

5 files changed

+132
-70
lines changed

5 files changed

+132
-70
lines changed

Diff for: CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
- pad setext header lines [75a0a95](https://github.com/MeanderingProgrammer/render-markdown.nvim/commit/75a0a9596a91130fae43d3b7c0d6c651645ef1df)
1212
- center headings and code blocks [#179](https://github.com/MeanderingProgrammer/render-markdown.nvim/issues/179)
1313
[0986638](https://github.com/MeanderingProgrammer/render-markdown.nvim/commit/0986638b381a4b01eb108bb946f3a67a9eb3d0ec)
14+
[67288fe](https://github.com/MeanderingProgrammer/render-markdown.nvim/commit/67288febca78b7aac8fae9543ef8980237e27d2a)
1415
- integrate with lazy.nvim filetypes [cb9a5e2](https://github.com/MeanderingProgrammer/render-markdown.nvim/commit/cb9a5e2412d21c7a89627e0d6da5459acbc0eb9c)
1516

1617
### Bug Fixes

Diff for: lua/render-markdown/core/node_info.lua

+4-3
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,17 @@ end
6464

6565
---Walk through parent nodes, count the number of target nodes
6666
---@param target string
67-
---@return integer
67+
---@return integer, render.md.NodeInfo?
6868
function NodeInfo:level_in_section(target)
69-
local level, parent = 0, self.node:parent()
69+
local parent, level, root = self.node:parent(), 0, nil
7070
while parent ~= nil and parent:type() ~= 'section' do
7171
if parent:type() == target then
7272
level = level + 1
73+
root = parent
7374
end
7475
parent = parent:parent()
7576
end
76-
return level
77+
return level, root ~= nil and NodeInfo.new(self.buf, root) or nil
7778
end
7879

7980
---@param target string

Diff for: lua/render-markdown/handler/markdown.lua

+1-66
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ function Handler.new(buf)
2222
self.renderers = {
2323
code = require('render-markdown.render.code'),
2424
heading = require('render-markdown.render.heading'),
25+
list_marker = require('render-markdown.render.list_marker'),
2526
quote = require('render-markdown.render.quote'),
2627
section = require('render-markdown.render.section'),
2728
table = require('render-markdown.render.table'),
@@ -41,8 +42,6 @@ function Handler:parse(root)
4142
end
4243
elseif capture == 'dash' then
4344
self:dash(info)
44-
elseif capture == 'list_marker' then
45-
self:list_marker(info)
4645
elseif capture == 'checkbox_unchecked' then
4746
self:checkbox(info, self.config.checkbox.unchecked)
4847
elseif capture == 'checkbox_checked' then
@@ -71,70 +70,6 @@ function Handler:dash(info)
7170
})
7271
end
7372

74-
---@private
75-
---@param info render.md.NodeInfo
76-
function Handler:list_marker(info)
77-
---@return boolean
78-
local function sibling_checkbox()
79-
if not self.config.checkbox.enabled then
80-
return false
81-
end
82-
if self.context:get_component(info) ~= nil then
83-
return true
84-
end
85-
if info:sibling('task_list_marker_unchecked') ~= nil then
86-
return true
87-
end
88-
if info:sibling('task_list_marker_checked') ~= nil then
89-
return true
90-
end
91-
return false
92-
end
93-
94-
-- List markers from tree-sitter should have leading spaces removed, however there are known
95-
-- edge cases in the parser: https://github.com/tree-sitter-grammars/tree-sitter-markdown/issues/127
96-
-- As a result we account for leading spaces here, can remove if this gets fixed upstream
97-
local leading_spaces = str.spaces('start', info.text)
98-
99-
if sibling_checkbox() then
100-
-- Hide the list marker for checkboxes rather than replacing with a bullet point
101-
self.marks:add(true, info.start_row, info.start_col + leading_spaces, {
102-
end_row = info.end_row,
103-
end_col = info.end_col,
104-
conceal = '',
105-
})
106-
else
107-
local bullet = self.config.bullet
108-
if not bullet.enabled then
109-
return
110-
end
111-
local level = info:level_in_section('list')
112-
local icon = list.cycle(bullet.icons, level)
113-
if icon == nil then
114-
return
115-
end
116-
self.marks:add(true, info.start_row, info.start_col, {
117-
end_row = info.end_row,
118-
end_col = info.end_col,
119-
virt_text = { { str.pad(leading_spaces) .. icon, bullet.highlight } },
120-
virt_text_pos = 'overlay',
121-
})
122-
if bullet.left_pad > 0 then
123-
self.marks:add(false, info.start_row, 0, {
124-
priority = 0,
125-
virt_text = { { str.pad(bullet.left_pad), self.config.padding.highlight } },
126-
virt_text_pos = 'inline',
127-
})
128-
end
129-
if bullet.right_pad > 0 then
130-
self.marks:add(true, info.start_row, info.end_col - 1, {
131-
virt_text = { { str.pad(bullet.right_pad), self.config.padding.highlight } },
132-
virt_text_pos = 'inline',
133-
})
134-
end
135-
end
136-
end
137-
13873
---@private
13974
---@param info render.md.NodeInfo
14075
---@param checkbox render.md.CheckboxComponent

Diff for: lua/render-markdown/health.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ local state = require('render-markdown.state')
44
local M = {}
55

66
---@private
7-
M.version = '7.1.9'
7+
M.version = '7.1.10'
88

99
function M.check()
1010
M.start('version')

Diff for: lua/render-markdown/render/list_marker.lua

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
local Base = require('render-markdown.render.base')
2+
local list = require('render-markdown.core.list')
3+
local str = require('render-markdown.core.str')
4+
5+
---@class render.md.render.ListMarker: render.md.Renderer
6+
---@field private bullet render.md.Bullet
7+
---@field private leading_spaces integer
8+
local Render = setmetatable({}, Base)
9+
Render.__index = Render
10+
11+
---@param marks render.md.Marks
12+
---@param config render.md.buffer.Config
13+
---@param context render.md.Context
14+
---@param info render.md.NodeInfo
15+
---@return render.md.Renderer
16+
function Render:new(marks, config, context, info)
17+
return Base.new(self, marks, config, context, info)
18+
end
19+
20+
---@return boolean
21+
function Render:setup()
22+
self.bullet = self.config.bullet
23+
24+
-- List markers from tree-sitter should have leading spaces removed, however there are edge
25+
-- cases in the parser: https://github.com/tree-sitter-grammars/tree-sitter-markdown/issues/127
26+
-- As a result we account for leading spaces here, can remove if this gets fixed upstream
27+
self.leading_spaces = str.spaces('start', self.info.text)
28+
29+
return true
30+
end
31+
32+
function Render:render()
33+
if self:sibling_checkbox() then
34+
-- Hide the list marker for checkboxes rather than replacing with a bullet point
35+
self:hide_marker()
36+
else
37+
if not self.bullet.enabled then
38+
return
39+
end
40+
local level, root_list = self.info:level_in_section('list')
41+
self:icon(level)
42+
self:padding(root_list)
43+
end
44+
end
45+
46+
---@private
47+
---@return boolean
48+
function Render:sibling_checkbox()
49+
if not self.config.checkbox.enabled then
50+
return false
51+
end
52+
if self.context:get_component(self.info) ~= nil then
53+
return true
54+
end
55+
if self.info:sibling('task_list_marker_unchecked') ~= nil then
56+
return true
57+
end
58+
if self.info:sibling('task_list_marker_checked') ~= nil then
59+
return true
60+
end
61+
return false
62+
end
63+
64+
---@private
65+
function Render:hide_marker()
66+
self.marks:add(true, self.info.start_row, self.info.start_col + self.leading_spaces, {
67+
end_row = self.info.end_row,
68+
end_col = self.info.end_col,
69+
conceal = '',
70+
})
71+
end
72+
73+
---@private
74+
---@param level integer
75+
function Render:icon(level)
76+
local icon = list.cycle(self.bullet.icons, level)
77+
if icon == nil then
78+
return
79+
end
80+
self.marks:add(true, self.info.start_row, self.info.start_col, {
81+
end_row = self.info.end_row,
82+
end_col = self.info.end_col,
83+
virt_text = { { str.pad(self.leading_spaces) .. icon, self.bullet.highlight } },
84+
virt_text_pos = 'overlay',
85+
})
86+
end
87+
88+
---@private
89+
---@param root_list? render.md.NodeInfo
90+
function Render:padding(root_list)
91+
if self.bullet.left_pad <= 0 and self.bullet.right_pad <= 0 then
92+
return
93+
end
94+
local list_item = self.info:parent('list_item')
95+
if list_item == nil then
96+
return
97+
end
98+
local left_col = root_list ~= nil and root_list.start_col or list_item.start_col
99+
100+
local next_list = list_item:child('list')
101+
local end_row = next_list ~= nil and next_list.start_row or list_item.end_row
102+
103+
for row = list_item.start_row, end_row - 1 do
104+
local right_col = row == list_item.start_row and self.info.end_col - 1 or left_col
105+
self:padding_mark(row, left_col, 0, self.bullet.left_pad)
106+
self:padding_mark(row, right_col, nil, self.bullet.right_pad)
107+
end
108+
end
109+
110+
---@private
111+
---@param row integer
112+
---@param col integer
113+
---@param priority? integer
114+
---@param amount integer
115+
function Render:padding_mark(row, col, priority, amount)
116+
if amount > 0 then
117+
self.marks:add(false, row, col, {
118+
priority = priority,
119+
virt_text = { { str.pad(amount), self.config.padding.highlight } },
120+
virt_text_pos = 'inline',
121+
})
122+
end
123+
end
124+
125+
return Render

0 commit comments

Comments
 (0)