Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit f7c669a

Browse files
feat(markup): Allow markup in link descriptions
1 parent 4e75c55 commit f7c669a

File tree

4 files changed

+238
-78
lines changed

4 files changed

+238
-78
lines changed

lua/orgmode/colors/highlighter/markup/_meta.lua

+2
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
---@field node TSNode
1313
---@field range OrgMarkupRange
1414
---@field self_contained? boolean
15+
---@field metadata? table<string, any>
1516

1617
---@class OrgMarkupHighlight
1718
---@field from OrgMarkupRange
1819
---@field to OrgMarkupRange
1920
---@field char string
21+
---@field metadata? table<string, any>
2022

2123
---@class OrgMarkupPreparedHighlight
2224
---@field start_line number

lua/orgmode/colors/highlighter/markup/init.lua

+3-2
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ function OrgMarkup:get_node_highlights(root_node, source, line)
132132
char = item.char,
133133
from = item.self_contained and item.range or from.range,
134134
to = item.range,
135+
metadata = item.metadata,
135136
})
136137

137138
if last_seek and last_seek.type == item.type then
@@ -171,9 +172,9 @@ function OrgMarkup:get_prepared_headline_highlights(headline)
171172
for type, highlight in pairs(highlights) do
172173
vim.list_extend(
173174
result,
174-
self.parsers[type]:prepare_highlights(highlight, function(markup_highlight)
175+
self.parsers[type]:prepare_highlights(highlight, function(start_col, end_col)
175176
local text = headline.file:get_node_text(headline:node())
176-
return text:sub(markup_highlight.from.start_col + 1, markup_highlight.to.end_col)
177+
return text:sub(start_col, end_col)
177178
end)
178179
)
179180
end

lua/orgmode/colors/highlighter/markup/link.lua

+200-59
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---@class OrgLinkHighlighter : OrgMarkupHighlighter
22
---@field private markup OrgMarkupHighlighter
33
---@field private has_extmark_url_support boolean
4+
---@field private last_start_node_id? string
45
local OrgLink = {
56
valid_capture_names = {
67
['link.start'] = true,
@@ -44,17 +45,42 @@ end
4445
function OrgLink:_parse_start_node(node)
4546
local node_type = node:type()
4647
local next_sibling = node:next_sibling()
48+
local prev_sibling = node:prev_sibling()
4749

50+
-- Start of link
4851
if next_sibling and next_sibling:type() == '[' then
49-
local id = table.concat({ 'link', node_type }, '_')
52+
local id = table.concat({ 'link', '[' }, '_')
5053
local seek_id = table.concat({ 'link', ']' }, '_')
54+
self.last_start_node_id = node:id()
5155
return {
5256
type = 'link',
5357
id = id,
5458
char = node_type,
5559
seek_id = seek_id,
5660
nestable = false,
5761
range = self.markup:node_to_range(node),
62+
metadata = {
63+
type = 'link_start',
64+
},
65+
node = node,
66+
}
67+
end
68+
69+
-- Start of link alias
70+
if prev_sibling and prev_sibling:type() == ']' then
71+
local id = table.concat({ 'link', '[' }, '_')
72+
local seek_id = table.concat({ 'link', ']' }, '_')
73+
return {
74+
type = 'link',
75+
id = id,
76+
char = node_type,
77+
seek_id = seek_id,
78+
nestable = true,
79+
range = self.markup:node_to_range(node),
80+
metadata = {
81+
type = 'link_alias_start',
82+
start_node_id = self.last_start_node_id,
83+
},
5884
node = node,
5985
}
6086
end
@@ -68,8 +94,11 @@ end
6894
function OrgLink:_parse_end_node(node)
6995
local node_type = node:type()
7096
local prev_sibling = node:prev_sibling()
71-
if prev_sibling and prev_sibling:type() == ']' then
72-
local id = table.concat({ 'link', node_type }, '_')
97+
local next_sibling = node:next_sibling()
98+
99+
-- End of link, start of alias
100+
if next_sibling and next_sibling:type() == '[' then
101+
local id = table.concat({ 'link', ']' }, '_')
73102
local seek_id = table.concat({ 'link', '[' }, '_')
74103
return {
75104
type = 'link',
@@ -79,9 +108,34 @@ function OrgLink:_parse_end_node(node)
79108
range = self.markup:node_to_range(node),
80109
nestable = false,
81110
node = node,
111+
metadata = {
112+
type = 'link_end_alias_start',
113+
start_node_id = self.last_start_node_id,
114+
},
82115
}
83116
end
84117

118+
-- End of link
119+
if prev_sibling and prev_sibling:type() == ']' then
120+
local id = table.concat({ 'link', ']' }, '_')
121+
local seek_id = table.concat({ 'link', '[' }, '_')
122+
local result = {
123+
type = 'link',
124+
id = id,
125+
char = node_type,
126+
seek_id = seek_id,
127+
range = self.markup:node_to_range(node),
128+
nestable = false,
129+
metadata = {
130+
type = 'link_end',
131+
},
132+
node = node,
133+
}
134+
result.metadata.start_node_id = self.last_start_node_id
135+
self.last_start_node_id = nil
136+
return result
137+
end
138+
85139
return false
86140
end
87141

@@ -97,17 +151,50 @@ function OrgLink:is_valid_end_node(entry)
97151
return entry.type == 'link' and entry.id == 'link_]'
98152
end
99153

154+
function OrgLink:_get_url(bufnr, line, start_col, end_col)
155+
if not self.has_extmark_url_support then
156+
return nil
157+
end
158+
159+
return vim.api.nvim_buf_get_text(bufnr, line, start_col, line, end_col, {})[1]
160+
end
161+
100162
---@param highlights OrgMarkupHighlight[]
101163
---@param bufnr number
102164
function OrgLink:highlight(highlights, bufnr)
103165
local namespace = self.markup.highlighter.namespace
104166
local ephemeral = self.markup:use_ephemeral()
105167

106-
for _, entry in ipairs(highlights) do
107-
local link =
108-
vim.api.nvim_buf_get_text(bufnr, entry.from.line, entry.from.start_col, entry.from.line, entry.to.end_col, {})[1]
109-
local alias = link:find('%]%[') or 1
110-
local link_end = link:find('%]%[') or (link:len() - 1)
168+
for i, entry in ipairs(highlights) do
169+
local prev_entry = highlights[i - 1]
170+
local next_entry = highlights[i + 1]
171+
if not entry.metadata.start_node_id then
172+
goto continue
173+
end
174+
175+
-- Alias without the valid end link
176+
if
177+
entry.metadata.type == 'link_end_alias_start'
178+
and (
179+
not next_entry
180+
or next_entry.metadata.type ~= 'link_end'
181+
or entry.metadata.start_node_id ~= next_entry.metadata.start_node_id
182+
)
183+
then
184+
goto continue
185+
end
186+
187+
-- End node without the valid alias
188+
if
189+
entry.metadata.type == 'link_end'
190+
and (
191+
prev_entry
192+
and prev_entry.metadata.type == 'link_end_alias_start'
193+
and prev_entry.metadata.start_node_id ~= entry.metadata.start_node_id
194+
)
195+
then
196+
goto continue
197+
end
111198

112199
local link_opts = {
113200
ephemeral = ephemeral,
@@ -116,43 +203,83 @@ function OrgLink:highlight(highlights, bufnr)
116203
priority = 110,
117204
}
118205

119-
if self.has_extmark_url_support then
120-
link_opts.url = alias > 1 and link:sub(3, alias - 1) or link:sub(3, -3)
206+
if entry.metadata.type == 'link_end_alias_start' then
207+
link_opts.url = self:_get_url(bufnr, entry.from.line, entry.from.start_col + 2, entry.to.end_col - 1)
208+
link_opts.spell = false
209+
entry.url = link_opts.url
210+
-- Conceal the whole target (marked with << and >>)
211+
-- <<[[https://neovim.io][>>Neovim]]
212+
vim.api.nvim_buf_set_extmark(bufnr, namespace, entry.from.line, entry.from.start_col, {
213+
ephemeral = ephemeral,
214+
end_col = entry.to.end_col + 1,
215+
conceal = '',
216+
})
121217
end
122218

123-
vim.api.nvim_buf_set_extmark(bufnr, namespace, entry.from.line, entry.from.start_col, link_opts)
124-
125-
vim.api.nvim_buf_set_extmark(bufnr, namespace, entry.from.line, entry.from.start_col, {
126-
ephemeral = ephemeral,
127-
end_col = entry.from.start_col + 1 + alias,
128-
conceal = '',
129-
})
130-
131-
vim.api.nvim_buf_set_extmark(bufnr, namespace, entry.from.line, entry.from.start_col + 2, {
132-
ephemeral = ephemeral,
133-
end_col = entry.from.start_col - 1 + link_end,
134-
spell = false,
135-
})
219+
if entry.metadata.type == 'link_end' then
220+
if prev_entry and prev_entry.metadata.type == 'link_end_alias_start' then
221+
link_opts.url = prev_entry.url
222+
else
223+
link_opts.url = self:_get_url(bufnr, entry.from.line, entry.from.start_col + 2, entry.to.end_col - 2)
224+
-- Conceal the start marker (marked with << and >>)
225+
-- <<[[>>https://neovim.io][Neovim]]
226+
vim.api.nvim_buf_set_extmark(bufnr, namespace, entry.from.line, entry.from.start_col, {
227+
ephemeral = ephemeral,
228+
end_col = entry.from.start_col + 2,
229+
conceal = '',
230+
})
231+
end
232+
-- Conceal the end marker (marked with << and >>)
233+
-- [[https://neovim.io][Neovim<<]]>>
234+
vim.api.nvim_buf_set_extmark(bufnr, namespace, entry.from.line, entry.to.end_col - 2, {
235+
ephemeral = ephemeral,
236+
end_col = entry.to.end_col,
237+
conceal = '',
238+
})
239+
end
136240

137-
vim.api.nvim_buf_set_extmark(bufnr, namespace, entry.from.line, entry.to.end_col - 2, {
138-
ephemeral = ephemeral,
139-
end_col = entry.to.end_col,
140-
conceal = '',
141-
})
241+
vim.api.nvim_buf_set_extmark(bufnr, namespace, entry.from.line, entry.from.start_col, link_opts)
242+
::continue::
142243
end
143244
end
144245

145246
---@param highlights OrgMarkupHighlight[]
146-
---@param source_getter_fn fun(highlight: OrgMarkupHighlight): string
247+
---@param source_getter_fn fun(start_col: number, end_col: number): string
147248
---@return OrgMarkupPreparedHighlight[]
148249
function OrgLink:prepare_highlights(highlights, source_getter_fn)
149250
local ephemeral = self.markup:use_ephemeral()
150251
local extmarks = {}
151252

152-
for _, entry in ipairs(highlights) do
153-
local link = source_getter_fn(entry)
154-
local alias = link:find('%]%[') or 1
155-
local link_end = link:find('%]%[') or (link:len() - 1)
253+
for i, entry in ipairs(highlights) do
254+
local prev_entry = highlights[i - 1]
255+
local next_entry = highlights[i + 1]
256+
if not entry.metadata.start_node_id then
257+
goto continue
258+
end
259+
260+
-- Alias without the valid end link
261+
if
262+
entry.metadata.type == 'link_end_alias_start'
263+
and (
264+
not next_entry
265+
or next_entry.metadata.type ~= 'link_end'
266+
or entry.metadata.start_node_id ~= next_entry.metadata.start_node_id
267+
)
268+
then
269+
goto continue
270+
end
271+
272+
-- End node without the valid alias
273+
if
274+
entry.metadata.type == 'link_end'
275+
and (
276+
prev_entry
277+
and prev_entry.metadata.type == 'link_end_alias_start'
278+
and prev_entry.metadata.start_node_id ~= entry.metadata.start_node_id
279+
)
280+
then
281+
goto continue
282+
end
156283

157284
local link_opts = {
158285
ephemeral = ephemeral,
@@ -161,8 +288,45 @@ function OrgLink:prepare_highlights(highlights, source_getter_fn)
161288
priority = 110,
162289
}
163290

164-
if self.has_extmark_url_support then
165-
link_opts.url = alias > 1 and link:sub(3, alias - 1) or link:sub(3, -3)
291+
if entry.metadata.type == 'link_end_alias_start' then
292+
link_opts.url = source_getter_fn(entry.from.end_col + 2, entry.to.end_col - 1)
293+
link_opts.spell = false
294+
entry.url = link_opts.url
295+
-- Conceal the whole target (marked with << and >>)
296+
-- <<[[https://neovim.io][>>Neovim]]
297+
table.insert(extmarks, {
298+
start_line = entry.from.line,
299+
start_col = entry.from.start_col,
300+
end_col = entry.to.end_col + 1,
301+
ephemeral = ephemeral,
302+
conceal = '',
303+
})
304+
end
305+
306+
if entry.metadata.type == 'link_end' then
307+
if prev_entry and prev_entry.metadata.type == 'link_end_alias_start' then
308+
link_opts.url = prev_entry.url
309+
else
310+
link_opts.url = source_getter_fn(entry.from.end_col + 2, entry.to.end_col - 2)
311+
-- Conceal the start marker (marked with << and >>)
312+
-- <<[[>>https://neovim.io][Neovim]]
313+
table.insert(extmarks, {
314+
start_line = entry.from.line,
315+
start_col = entry.from.start_col,
316+
end_col = entry.from.start_col + 2,
317+
ephemeral = ephemeral,
318+
conceal = '',
319+
})
320+
end
321+
-- Conceal the end marker (marked with << and >>)
322+
-- [[https://neovim.io][Neovim<<]]>>
323+
table.insert(extmarks, {
324+
start_line = entry.from.line,
325+
start_col = entry.to.end_col - 2,
326+
end_col = entry.to.end_col,
327+
ephemeral = ephemeral,
328+
conceal = '',
329+
})
166330
end
167331

168332
table.insert(extmarks, {
@@ -174,30 +338,7 @@ function OrgLink:prepare_highlights(highlights, source_getter_fn)
174338
priority = link_opts.priority,
175339
url = link_opts.url,
176340
})
177-
178-
table.insert(extmarks, {
179-
start_line = entry.from.line,
180-
start_col = entry.from.start_col,
181-
end_col = entry.from.start_col + 1 + alias,
182-
ephemeral = ephemeral,
183-
conceal = '',
184-
})
185-
186-
table.insert(extmarks, {
187-
start_line = entry.from.line,
188-
start_col = entry.from.start_col + 2,
189-
end_col = entry.from.start_col - 1 + link_end,
190-
ephemeral = ephemeral,
191-
spell = false,
192-
})
193-
194-
table.insert(extmarks, {
195-
start_line = entry.from.line,
196-
start_col = entry.to.end_col - 2,
197-
end_col = entry.to.end_col,
198-
ephemeral = ephemeral,
199-
conceal = '',
200-
})
341+
::continue::
201342
end
202343

203344
return extmarks

0 commit comments

Comments
 (0)