Skip to content

Commit 6910fe1

Browse files
feat: support wiki style media links, improve custom links
## Details Request: #410 Avoids rendering links if they contain a nested `shortcut_link`. This avoid the double placement of icons and instead only uses the `shortcut_link` based rendering. A link in a link will have the same structure as a `wikilink` so really that is the rendering logic that will ultimately get used. To then allow a larger variety of icons to be configured have also expanded custom link functionality. They now have 2 additional optional properties, `kind` & `priority`. - kind: changes how the pattern is matched against link destinations - pattern: uses the existing lua pattern mechanism, is also the default behavior if no value is set to avoid any breaking changes - suffix: when set does a simple `vim.endswith` check, can be used to easier support file extension matching - because of how this is setup adding more kinds should be trivial - priority: used when multiple patterns match a destination to decide which to use, highest value wins, allows users more control rather than being stuck with current behavior, uses the length of the pattern if no value is set to avoid any changes to behavior out of the box
1 parent 6531aa7 commit 6910fe1

File tree

13 files changed

+107
-50
lines changed

13 files changed

+107
-50
lines changed

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -721,8 +721,12 @@ require('render-markdown').setup({
721721
-- contains. Applies to 'inline_link', 'uri_autolink', and wikilink nodes. When multiple
722722
-- patterns match a link the one with the longer pattern is used.
723723
-- The key is for healthcheck and to allow users to change its values, value type below.
724-
-- | pattern | matched against the destination text, @see :h lua-patterns |
724+
-- | pattern | matched against the destination text |
725725
-- | icon | gets inlined before the link text |
726+
-- | kind | optional determines how pattern is checked |
727+
-- | | pattern | @see :h lua-patterns, is the default if not set |
728+
-- | | suffix | @see :h vim.endswith() |
729+
-- | priority | optional used when multiple match, uses pattern length if empty |
726730
-- | highlight | optional highlight for 'icon', uses fallback highlight if empty |
727731
custom = {
728732
web = { pattern = '^http', icon = '󰖟 ' },
@@ -1394,8 +1398,12 @@ require('render-markdown').setup({
13941398
-- contains. Applies to 'inline_link', 'uri_autolink', and wikilink nodes. When multiple
13951399
-- patterns match a link the one with the longer pattern is used.
13961400
-- The key is for healthcheck and to allow users to change its values, value type below.
1397-
-- | pattern | matched against the destination text, @see :h lua-patterns |
1401+
-- | pattern | matched against the destination text |
13981402
-- | icon | gets inlined before the link text |
1403+
-- | kind | optional determines how pattern is checked |
1404+
-- | | pattern | @see :h lua-patterns, is the default if not set |
1405+
-- | | suffix | @see :h vim.endswith() |
1406+
-- | priority | optional used when multiple match, uses pattern length if empty |
13991407
-- | highlight | optional highlight for 'icon', uses fallback highlight if empty |
14001408
custom = {
14011409
web = { pattern = '^http', icon = '󰖟 ' },

doc/render-markdown.txt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -786,8 +786,12 @@ Default Configuration ~
786786
-- contains. Applies to 'inline_link', 'uri_autolink', and wikilink nodes. When multiple
787787
-- patterns match a link the one with the longer pattern is used.
788788
-- The key is for healthcheck and to allow users to change its values, value type below.
789-
-- | pattern | matched against the destination text, @see :h lua-patterns |
789+
-- | pattern | matched against the destination text |
790790
-- | icon | gets inlined before the link text |
791+
-- | kind | optional determines how pattern is checked |
792+
-- | | pattern | @see :h lua-patterns, is the default if not set |
793+
-- | | suffix | @see :h vim.endswith() |
794+
-- | priority | optional used when multiple match, uses pattern length if empty |
791795
-- | highlight | optional highlight for 'icon', uses fallback highlight if empty |
792796
custom = {
793797
web = { pattern = '^http', icon = '󰖟 ' },
@@ -1439,8 +1443,12 @@ Link Configuration ~
14391443
-- contains. Applies to 'inline_link', 'uri_autolink', and wikilink nodes. When multiple
14401444
-- patterns match a link the one with the longer pattern is used.
14411445
-- The key is for healthcheck and to allow users to change its values, value type below.
1442-
-- | pattern | matched against the destination text, @see :h lua-patterns |
1446+
-- | pattern | matched against the destination text |
14431447
-- | icon | gets inlined before the link text |
1448+
-- | kind | optional determines how pattern is checked |
1449+
-- | | pattern | @see :h lua-patterns, is the default if not set |
1450+
-- | | suffix | @see :h vim.endswith() |
1451+
-- | priority | optional used when multiple match, uses pattern length if empty |
14441452
-- | highlight | optional highlight for 'icon', uses fallback highlight if empty |
14451453
custom = {
14461454
web = { pattern = '^http', icon = '󰖟 ' },

lua/render-markdown/config/link.lua

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,16 @@
2929
---@class (exact) render.md.link.custom.Config
3030
---@field pattern string
3131
---@field icon string
32+
---@field kind? render.md.link.custom.Kind
33+
---@field priority? integer
3234
---@field highlight? string
3335

36+
---@enum (key) render.md.link.custom.Kind
37+
local Kind = {
38+
pattern = 'pattern',
39+
suffix = 'suffix',
40+
}
41+
3442
local M = {}
3543

3644
---@param spec render.md.debug.ValidatorSpec
@@ -57,6 +65,8 @@ function M.validate(spec)
5765
patterns:each(function(pattern)
5866
pattern:type('pattern', 'string')
5967
pattern:type('icon', 'string')
68+
pattern:one_of('kind', vim.tbl_keys(Kind), 'nil')
69+
pattern:type('priority', { 'number', 'nil' })
6070
pattern:type('highlight', { 'string', 'nil' })
6171
pattern:check()
6272
end, false)

lua/render-markdown/health.lua

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

77
---@private
8-
M.version = '8.3.10'
8+
M.version = '8.3.11'
99

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

lua/render-markdown/init.lua

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,8 +587,12 @@ M.default = {
587587
-- contains. Applies to 'inline_link', 'uri_autolink', and wikilink nodes. When multiple
588588
-- patterns match a link the one with the longer pattern is used.
589589
-- The key is for healthcheck and to allow users to change its values, value type below.
590-
-- | pattern | matched against the destination text, @see :h lua-patterns |
590+
-- | pattern | matched against the destination text |
591591
-- | icon | gets inlined before the link text |
592+
-- | kind | optional determines how pattern is checked |
593+
-- | | pattern | @see :h lua-patterns, is the default if not set |
594+
-- | | suffix | @see :h vim.endswith() |
595+
-- | priority | optional used when multiple match, uses pattern length if empty |
592596
-- | highlight | optional highlight for 'icon', uses fallback highlight if empty |
593597
custom = {
594598
web = { pattern = '^http', icon = '󰖟 ' },

lua/render-markdown/lib/iter.lua

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,22 @@ function M.list.map(values, f)
1616
return result
1717
end
1818

19+
---@generic T
20+
---@param values T[]
21+
---@param f fun(value: T): integer
22+
function M.list.sort(values, f)
23+
table.sort(values, function(a, b)
24+
return f(a) < f(b)
25+
end)
26+
end
27+
1928
---@class render.md.iter.Table
2029
M.table = {}
2130

22-
---@generic V
23-
---@param values table<any, V>
24-
---@param f fun(value: V): boolean
25-
---@return V[]
31+
---@generic T
32+
---@param values { [any]: T }
33+
---@param f fun(value: T): boolean
34+
---@return T[]
2635
function M.table.filter(values, f)
2736
local result = {}
2837
for _, value in pairs(values) do

lua/render-markdown/lib/node.lua

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,13 @@ function Node:level(parent)
7878
if section == nil then
7979
return 0
8080
end
81-
assert(section.type == 'section', 'Node must be a section')
81+
assert(section.type == 'section', 'must be a section')
8282
local heading = section:child('atx_heading')
8383
local node = heading ~= nil and heading.node:child(0) or nil
84-
-- Counts the number of hashtags in the heading marker
84+
-- counts the number of hashtags in the heading marker
8585
return node ~= nil and #vim.treesitter.get_node_text(node, self.buf) or 0
8686
end
8787

88-
---Walk through parent nodes, count the number of target nodes
8988
---@param target string
9089
---@return integer, render.md.Node?
9190
function Node:level_in_section(target)
@@ -105,12 +104,12 @@ end
105104
---@param target string
106105
---@return render.md.Node?
107106
function Node:parent(target)
108-
local parent = self.node:parent()
109-
while parent ~= nil do
110-
if parent:type() == target then
111-
return self:create(parent)
107+
local node = self.node:parent()
108+
while node ~= nil do
109+
if node:type() == target then
110+
return self:create(node)
112111
end
113-
parent = parent:parent()
112+
node = node:parent()
114113
end
115114
return nil
116115
end
@@ -152,13 +151,13 @@ function Node:child_at(index)
152151
return node ~= nil and self:create(node) or nil
153152
end
154153

155-
---@param target_type string
156-
---@param target_row? integer
154+
---@param target string
155+
---@param row? integer
157156
---@return render.md.Node?
158-
function Node:child(target_type, target_row)
157+
function Node:child(target, row)
159158
for node in self.node:iter_children() do
160-
if node:type() == target_type then
161-
if target_row == nil or node:range() == target_row then
159+
if node:type() == target then
160+
if row == nil or node:range() == row then
162161
return self:create(node)
163162
end
164163
end
@@ -168,9 +167,25 @@ end
168167

169168
---@param callback fun(node: render.md.Node)
170169
function Node:for_each_child(callback)
171-
for child in self.node:iter_children() do
172-
callback(self:create(child))
170+
for node in self.node:iter_children() do
171+
callback(self:create(node))
172+
end
173+
end
174+
175+
---@param target string
176+
---@return render.md.Node?
177+
function Node:descendant(target)
178+
for node in self.node:iter_children() do
179+
local child = self:create(node)
180+
if child.type == target then
181+
return child
182+
end
183+
local nested = child:descendant(target)
184+
if nested ~= nil then
185+
return nested
186+
end
173187
end
188+
return nil
174189
end
175190

176191
---@return string?
@@ -183,15 +198,16 @@ end
183198
---@param by integer
184199
---@return string?
185200
function Node:line(position, by)
186-
local one_line, row = self.start_row == self.end_row, nil
201+
local row = nil
202+
local single = self.start_row == self.end_row
187203
if position == 'above' then
188204
row = self.start_row - by
189205
elseif position == 'first' then
190206
row = self.start_row + by
191207
elseif position == 'below' then
192-
row = self.end_row - (one_line and 0 or 1) + by
208+
row = self.end_row - (single and 0 or 1) + by
193209
elseif position == 'last' then
194-
row = self.end_row - (one_line and 0 or 1) - by
210+
row = self.end_row - (single and 0 or 1) - by
195211
end
196212
if row == nil then
197213
return nil

lua/render-markdown/render/base.lua

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ function Base:check_icon(icon, highlight)
5353
local space = self.context:width(self.node) + 1 - Str.width(icon)
5454
local right_pad = self.config.checkbox.right_pad
5555
if space < 0 then
56-
-- Not enough space to fit the icon in-place
56+
-- not enough space to fit the icon in-place
5757
return self.marks:over('check_icon', self.node, {
5858
virt_text = self:append(line, right_pad),
5959
virt_text_pos = 'inline',
@@ -62,23 +62,25 @@ function Base:check_icon(icon, highlight)
6262
else
6363
local fits = math.min(space, right_pad)
6464
self:append(line, fits)
65-
space, right_pad = space - fits, right_pad - fits
65+
space = space - fits
66+
right_pad = right_pad - fits
6667
local row = self.node.start_row
67-
local start_col, end_col = self.node.start_col, self.node.end_col + 1
68+
local start_col = self.node.start_col
69+
local end_col = self.node.end_col + 1
6870
self.marks:add('check_icon', row, start_col, {
6971
end_col = end_col - space,
7072
virt_text = line,
7173
virt_text_pos = 'overlay',
7274
})
7375
if space > 0 then
74-
-- Hide extra space after the icon
76+
-- hide extra space after the icon
7577
self.marks:add('check_icon', row, end_col - space, {
7678
end_col = end_col,
7779
conceal = '',
7880
})
7981
end
8082
if right_pad > 0 then
81-
-- Add padding
83+
-- add padding
8284
self.marks:add('check_icon', row, end_col, {
8385
virt_text = self:append({}, right_pad),
8486
virt_text_pos = 'inline',
@@ -106,10 +108,14 @@ end
106108
---@return string, string
107109
function Base:dest(icon, highlight, destination)
108110
local options = Iter.table.filter(self.config.link.custom, function(custom)
109-
return destination:find(custom.pattern) ~= nil
111+
if custom.kind == 'suffix' then
112+
return vim.endswith(destination, custom.pattern)
113+
else
114+
return destination:find(custom.pattern) ~= nil
115+
end
110116
end)
111-
table.sort(options, function(a, b)
112-
return Str.width(a.pattern) < Str.width(b.pattern)
117+
Iter.list.sort(options, function(custom)
118+
return custom.priority or Str.width(custom.pattern)
113119
end)
114120
local result = options[#options] or {}
115121
return result.icon or icon, result.highlight or highlight

lua/render-markdown/render/link.lua

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ function Render:setup()
1616
if self.context:skip(link) then
1717
return false
1818
end
19-
19+
if self.node:descendant('shortcut_link') ~= nil then
20+
return false
21+
end
2022
local icon, highlight, autolink = link.hyperlink, link.highlight, false
2123
if self.node.type == 'email_autolink' then
2224
icon = link.email
@@ -34,7 +36,6 @@ function Render:setup()
3436
autolink = true
3537
end
3638
self.data = { icon = icon, highlight = highlight, autolink = autolink }
37-
3839
return true
3940
end
4041

lua/render-markdown/render/shortcut.lua

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,8 @@ function Render:render()
2525
end
2626

2727
local line = self.node:line('first', 0)
28-
if
29-
line ~= nil
30-
and line:find('[' .. self.node.text .. ']', 1, true) ~= nil
31-
then
28+
local wiki_pattern = '[' .. self.node.text .. ']'
29+
if line ~= nil and line:find(wiki_pattern, 1, true) ~= nil then
3230
self:wiki_link()
3331
return
3432
end

lua/render-markdown/render/table.lua

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,8 @@ function Render:setup()
6464
if row.type == 'pipe_table_delimiter_row' then
6565
delim = self:parse_delim(row)
6666
elseif self.context:overlaps(row:get()) then
67-
if
68-
vim.tbl_contains(
69-
{ 'pipe_table_header', 'pipe_table_row' },
70-
row.type
71-
)
72-
then
67+
local row_types = { 'pipe_table_header', 'pipe_table_row' }
68+
if vim.tbl_contains(row_types, row.type) then
7369
table_rows[#table_rows + 1] = row
7470
else
7571
log.unhandled_type('markdown', 'row', row.type)

lua/render-markdown/types.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@
204204
---@class (exact) render.md.link.custom.UserConfig
205205
---@field pattern? string
206206
---@field icon? string
207+
---@field kind? render.md.link.custom.Kind
208+
---@field priority? integer
207209
---@field highlight? string
208210

209211
---@class (exact) render.md.callback.UserConfig

tests/ad_hoc_spec.lua

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,10 @@ describe('ad hoc', function()
6969
it('wikilink media', function()
7070
util.setup.text({ '![[test.png]]' })
7171
local marks = util.marks()
72-
marks:add(0, nil, 0, nil, util.link('image'))
7372
marks:add(0, 0, 1, 2, util.conceal())
7473
marks:add(0, nil, 2, nil, util.link('wiki'))
7574
marks:add(0, 0, 12, 13, util.conceal())
76-
util.assert_view(marks, { '󰥶 󱗖 test.png' })
75+
util.assert_view(marks, { '󱗖 test.png' })
7776
end)
7877

7978
it('email', function()

0 commit comments

Comments
 (0)