Skip to content

Commit 14673b0

Browse files
feat: support adding icons to ordered lists
## Details Adjusts query to be for list_item rather than specific markers. Modifies render logic to work with list_item rather than the marker. Adds new config parameter `bullet.ordered_icons` which are the icons used for ordered list items. Ordered lists are defined by the node type of `list_marker_dot` or `list_marker_parenthesis`. By default it is empty which essentially means do not add an icon for ordered lists. Unordered lists continue to function as before. It probably makes sense to rename the `bullet` top level config to something more appropriate like `list`, but I don't want to add the breaking change, at least for the time being.
1 parent 04e75a3 commit 14673b0

File tree

11 files changed

+74
-49
lines changed

11 files changed

+74
-49
lines changed

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,10 @@ require('render-markdown').setup({
358358
-- The 'level' is used to index into the list using a cycle
359359
-- If the item is a 'checkbox' a conceal is used to hide the bullet instead
360360
icons = { '', '', '', '' },
361+
-- Replaces 'n.'|'n)' of 'list_item'
362+
-- How deeply nested the list is determines the 'level'
363+
-- The 'level' is used to index into the list using a cycle
364+
ordered_icons = {},
361365
-- Padding to add to the left of bullet point
362366
left_pad = 0,
363367
-- Padding to add to the right of bullet point
@@ -810,6 +814,10 @@ require('render-markdown').setup({
810814
-- The 'level' is used to index into the list using a cycle
811815
-- If the item is a 'checkbox' a conceal is used to hide the bullet instead
812816
icons = { '', '', '', '' },
817+
-- Replaces 'n.'|'n)' of 'list_item'
818+
-- How deeply nested the list is determines the 'level'
819+
-- The 'level' is used to index into the list using a cycle
820+
ordered_icons = {},
813821
-- Padding to add to the left of bullet point
814822
left_pad = 0,
815823
-- Padding to add to the right of bullet point

doc/render-markdown.txt

+8
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,10 @@ Default Configuration ~
405405
-- The 'level' is used to index into the list using a cycle
406406
-- If the item is a 'checkbox' a conceal is used to hide the bullet instead
407407
icons = { '●', '○', '◆', '◇' },
408+
-- Replaces 'n.'|'n)' of 'list_item'
409+
-- How deeply nested the list is determines the 'level'
410+
-- The 'level' is used to index into the list using a cycle
411+
ordered_icons = {},
408412
-- Padding to add to the left of bullet point
409413
left_pad = 0,
410414
-- Padding to add to the right of bullet point
@@ -847,6 +851,10 @@ Bullet Point Configuration ~
847851
-- The 'level' is used to index into the list using a cycle
848852
-- If the item is a 'checkbox' a conceal is used to hide the bullet instead
849853
icons = { '●', '○', '◆', '◇' },
854+
-- Replaces 'n.'|'n)' of 'list_item'
855+
-- How deeply nested the list is determines the 'level'
856+
-- The 'level' is used to index into the list using a cycle
857+
ordered_icons = {},
850858
-- Padding to add to the left of bullet point
851859
left_pad = 0,
852860
-- Padding to add to the right of bullet point

lua/render-markdown/handler/markdown.lua

+2-6
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,7 @@ function Handler.new(buf)
4444
(plus_metadata)
4545
] @dash
4646
47-
[
48-
(list_marker_plus)
49-
(list_marker_minus)
50-
(list_marker_star)
51-
] @list_marker
47+
(list_item) @list_item
5248
5349
[
5450
(task_list_marker_unchecked)
@@ -65,7 +61,7 @@ function Handler.new(buf)
6561
code = require('render-markdown.render.code'),
6662
dash = require('render-markdown.render.dash'),
6763
heading = require('render-markdown.render.heading'),
68-
list_marker = require('render-markdown.render.list_marker'),
64+
list_item = require('render-markdown.render.list_item'),
6965
paragraph = require('render-markdown.render.paragraph'),
7066
quote = require('render-markdown.render.quote'),
7167
section = require('render-markdown.render.section'),

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.4.8'
7+
M.version = '7.4.9'
88

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

lua/render-markdown/init.lua

+5
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ local M = {}
111111
---@class (exact) render.md.UserBullet
112112
---@field public enabled? boolean
113113
---@field public icons? (string|string[])[]
114+
---@field public ordered_icons? (string|string[])[]
114115
---@field public left_pad? integer
115116
---@field public right_pad? integer
116117
---@field public highlight? string
@@ -471,6 +472,10 @@ M.default_config = {
471472
-- The 'level' is used to index into the list using a cycle
472473
-- If the item is a 'checkbox' a conceal is used to hide the bullet instead
473474
icons = { '', '', '', '' },
475+
-- Replaces 'n.'|'n)' of 'list_item'
476+
-- How deeply nested the list is determines the 'level'
477+
-- The 'level' is used to index into the list using a cycle
478+
ordered_icons = {},
474479
-- Padding to add to the left of bullet point
475480
left_pad = 0,
476481
-- Padding to add to the right of bullet point

lua/render-markdown/lib/node.lua

+8-2
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,7 @@ function Node:level_in_section(target)
6969
local parent, level, root = self.node:parent(), 0, nil
7070
while parent ~= nil and parent:type() ~= 'section' do
7171
if parent:type() == target then
72-
level = level + 1
73-
root = parent
72+
level, root = level + 1, parent
7473
end
7574
parent = parent:parent()
7675
end
@@ -114,6 +113,13 @@ function Node:sibling_count(target)
114113
return count
115114
end
116115

116+
---@param index integer
117+
---@return render.md.Node?
118+
function Node:child_at(index)
119+
local node = self.node:named_child(index)
120+
return node ~= nil and Node.new(self.buf, node) or nil
121+
end
122+
117123
---@param target_type string
118124
---@param target_row? integer
119125
---@return render.md.Node?

lua/render-markdown/render/base.lua

+3-6
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,10 @@ function Base:sign(text, highlight)
4545
end
4646

4747
---@protected
48+
---@param paragraph render.md.Node?
4849
---@param highlight? string
49-
function Base:checkbox_scope(highlight)
50-
if highlight == nil then
51-
return
52-
end
53-
local paragraph = self.node:sibling('paragraph')
54-
if paragraph == nil then
50+
function Base:checkbox_scope(paragraph, highlight)
51+
if paragraph == nil or highlight == nil then
5552
return
5653
end
5754
paragraph = paragraph:child('inline')

lua/render-markdown/render/checkbox.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ end
3030

3131
function Render:render()
3232
self:icon()
33-
self:checkbox_scope(self.checkbox.scope_highlight)
33+
self:checkbox_scope(self.node:sibling('paragraph'), self.checkbox.scope_highlight)
3434
end
3535

3636
---@private

lua/render-markdown/render/list_marker.lua lua/render-markdown/render/list_item.lua

+36-32
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ local List = require('render-markdown.lib.list')
33
local Str = require('render-markdown.lib.str')
44

55
---@class render.md.data.ListMarker
6+
---@field marker render.md.Node
7+
---@field ordered boolean
68
---@field spaces integer
79
---@field checkbox? render.md.CustomCheckbox
810

@@ -16,55 +18,63 @@ Render.__index = Render
1618
function Render:setup()
1719
self.bullet = self.config.bullet
1820

21+
local marker = self.node:child_at(0)
22+
if marker == nil then
23+
return false
24+
end
25+
1926
self.data = {
27+
marker = marker,
28+
ordered = vim.tbl_contains({ 'list_marker_dot', 'list_marker_parenthesis' }, marker.type),
2029
-- List markers from tree-sitter should have leading spaces removed, however there are edge
2130
-- cases in the parser: https://github.com/tree-sitter-grammars/tree-sitter-markdown/issues/127
2231
-- As a result we account for leading spaces here, can remove if this gets fixed upstream
23-
spaces = Str.spaces('start', self.node.text),
32+
spaces = Str.spaces('start', marker.text),
2433
checkbox = self.context:get_checkbox(self.node),
2534
}
2635

2736
return true
2837
end
2938

3039
function Render:render()
31-
if self:sibling_checkbox() then
40+
if self:has_checkbox() then
3241
-- Hide the list marker for checkboxes rather than replacing with a bullet point
3342
self:hide_marker()
3443
self:highlight_scope()
3544
else
3645
if not self.bullet.enabled then
3746
return
3847
end
39-
local level, root_list = self.node:level_in_section('list')
48+
local level, root = self.node:level_in_section('list')
4049
self:icon(level)
41-
self:padding(root_list)
50+
self:padding(root)
4251
end
4352
end
4453

4554
---@private
4655
---@return boolean
47-
function Render:sibling_checkbox()
56+
function Render:has_checkbox()
4857
if not self.config.checkbox.enabled then
4958
return false
5059
end
5160
if self.data.checkbox ~= nil then
5261
return true
5362
end
54-
if self.node:sibling('task_list_marker_unchecked') ~= nil then
63+
if self.data.marker:sibling('task_list_marker_unchecked') ~= nil then
5564
return true
5665
end
57-
if self.node:sibling('task_list_marker_checked') ~= nil then
66+
if self.data.marker:sibling('task_list_marker_checked') ~= nil then
5867
return true
5968
end
6069
return false
6170
end
6271

6372
---@private
6473
function Render:hide_marker()
65-
self.marks:add('check_icon', self.node.start_row, self.node.start_col + self.data.spaces, {
66-
end_row = self.node.end_row,
67-
end_col = self.node.end_col,
74+
local node = self.data.marker
75+
self.marks:add('check_icon', node.start_row, node.start_col + self.data.spaces, {
76+
end_row = node.end_row,
77+
end_col = node.end_col,
6878
conceal = '',
6979
})
7080
end
@@ -74,54 +84,48 @@ function Render:highlight_scope()
7484
if self.data.checkbox == nil then
7585
return
7686
end
77-
self:checkbox_scope(self.data.checkbox.scope_highlight)
87+
self:checkbox_scope(self.node:child('paragraph'), self.data.checkbox.scope_highlight)
7888
end
7989

8090
---@private
8191
---@param level integer
8292
function Render:icon(level)
83-
local icon = List.cycle(self.bullet.icons, level)
93+
local icons = self.data.ordered and self.bullet.ordered_icons or self.bullet.icons
94+
local icon = List.cycle(icons, level)
8495
if type(icon) == 'table' then
85-
local item = self.node:parent('list_item')
86-
if item == nil then
87-
return
88-
end
89-
icon = List.clamp(icon, item:sibling_count('list_item'))
96+
icon = List.clamp(icon, self.node:sibling_count('list_item'))
9097
end
9198
if icon == nil then
9299
return
93100
end
101+
local node = self.data.marker
94102
local text = Str.pad(self.data.spaces) .. icon
95103
local position, conceal = 'overlay', nil
96-
if Str.width(text) > Str.width(self.node.text) then
104+
if Str.width(text) > Str.width(node.text) then
97105
position, conceal = 'inline', ''
98106
end
99-
self.marks:add('bullet', self.node.start_row, self.node.start_col, {
100-
end_row = self.node.end_row,
101-
end_col = self.node.end_col,
107+
self.marks:add('bullet', node.start_row, node.start_col, {
108+
end_row = node.end_row,
109+
end_col = node.end_col,
102110
virt_text = { { text, self.bullet.highlight } },
103111
virt_text_pos = position,
104112
conceal = conceal,
105113
})
106114
end
107115

108116
---@private
109-
---@param root_list? render.md.Node
110-
function Render:padding(root_list)
117+
---@param root? render.md.Node
118+
function Render:padding(root)
111119
if self.bullet.left_pad <= 0 and self.bullet.right_pad <= 0 then
112120
return
113121
end
114-
local list_item = self.node:parent('list_item')
115-
if list_item == nil then
116-
return
117-
end
118-
local left_col = root_list ~= nil and root_list.start_col or list_item.start_col
122+
local left_col = root ~= nil and root.start_col or self.node.start_col
119123

120-
local next_list = list_item:child('list')
121-
local end_row = next_list ~= nil and next_list.start_row or list_item.end_row
124+
local next_list = self.node:child('list')
125+
local end_row = next_list ~= nil and next_list.start_row or self.node.end_row
122126

123-
for row = list_item.start_row, end_row - 1 do
124-
local right_col = row == list_item.start_row and self.node.end_col - 1 or left_col
127+
for row = self.node.start_row, end_row - 1 do
128+
local right_col = row == self.node.start_row and self.data.marker.end_col - 1 or left_col
125129
self:padding_mark(row, left_col, self.bullet.left_pad)
126130
self:padding_mark(row, right_col, self.bullet.right_pad)
127131
end

lua/render-markdown/state.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ function M.validate()
183183
:type('enabled', 'boolean')
184184
:type({ 'left_pad', 'right_pad' }, 'number')
185185
:type('highlight', 'string')
186-
:list_or_list_of_list('icons', 'string')
186+
:list_or_list_of_list({ 'icons', 'ordered_icons' }, 'string')
187187
:check()
188188

189189
get_spec('checkbox')

lua/render-markdown/types.lua

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
---@class (exact) render.md.Bullet
9393
---@field public enabled boolean
9494
---@field public icons (string|string[])[]
95+
---@field public ordered_icons (string|string[])[]
9596
---@field public left_pad integer
9697
---@field public right_pad integer
9798
---@field public highlight string

0 commit comments

Comments
 (0)