Skip to content

Commit e3a0548

Browse files
committed
feat(markdown): Added support for Obsidian-style tags
Closes #491
1 parent e7db75d commit e3a0548

8 files changed

Lines changed: 201 additions & 1 deletion

File tree

doc/markview.nvim-markdown_inline.txt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
---@field inline_codes markview.config.markdown_inline.inline_codes Inline code/code span configuration.
2121
---@field internal_links markview.config.markdown_inline.internal_links Internal link configuration.
2222
---@field uri_autolinks markview.config.markdown_inline.uri_autolinks URI autolink configuration.
23+
---@field tags markview.config.markdown_inline.tags Obsidian-style tags configuration.
2324
<
2425

2526
--------------------------------------------------------------------------------
@@ -843,6 +844,39 @@ Each uri autolinks type has the following options.
843844
---@alias markview.config.markdown_inline.uri_autolinks.opts markview.config.__inline
844845
<
845846

847+
--------------------------------------------------------------------------------
848+
tags *markview.nvim-markdown_inline.tags*
849+
850+
>lua
851+
--- Configuration for emails.
852+
---@class markview.config.markdown_inline.tags
853+
---
854+
---@field enable boolean Enable rendering of Obsidian-style tags.
855+
---
856+
---@field default markview.config.markdown_inline.tags.opts Default configuration for tags.
857+
---@field [string] markview.config.markdown_inline.tags.opts Configuration for emails whose name(text after `#`) matches `string`.
858+
<
859+
860+
Changes how `#tags` are shown.
861+
862+
>lua
863+
require("markview.config.markdown_inline").tags = {
864+
default = {
865+
hl = "MarkviewPalette7",
866+
padding_left = " ",
867+
padding_right = " "
868+
},
869+
enable = true
870+
}
871+
<
872+
873+
Each tag type has the following options.
874+
875+
>lua
876+
--[[ Options for a specific email type. ]]
877+
---@alias markview.config.markdown_inline.tags.opts markview.config.__inline
878+
<
879+
846880
Links ~
847881

848882
1: https://github.com/ikatyang/emoji-cheat-sheet/blob/master/README.md

doc/tags

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ markview.nvim-markdown_inline.hyperlinks markview.nvim-markdown_inline.txt /*mar
9595
markview.nvim-markdown_inline.images markview.nvim-markdown_inline.txt /*markview.nvim-markdown_inline.images*
9696
markview.nvim-markdown_inline.inline_codes markview.nvim-markdown_inline.txt /*markview.nvim-markdown_inline.inline_codes*
9797
markview.nvim-markdown_inline.internal_links markview.nvim-markdown_inline.txt /*markview.nvim-markdown_inline.internal_links*
98+
markview.nvim-markdown_inline.tags markview.nvim-markdown_inline.txt /*markview.nvim-markdown_inline.tags*
9899
markview.nvim-markdown_inline.uri_autolinks markview.nvim-markdown_inline.txt /*markview.nvim-markdown_inline.uri_autolinks*
99100
markview.nvim-presets markview.nvim-presets.txt /*markview.nvim-presets*
100101
markview.nvim-preview markview.nvim-preview.txt /*markview.nvim-preview*

lua/markview/config/markdown_inline.lua

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,4 +515,15 @@ return {
515515
hl = "MarkviewPalette2Fg"
516516
},
517517
},
518+
519+
tags = {
520+
enable = true,
521+
522+
default = {
523+
padding_left = " ",
524+
padding_right = " ",
525+
526+
hl = "MarkviewPalette7"
527+
}
528+
},
518529
};

lua/markview/parsers/markdown_inline.lua

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
local inline = {};
2+
local lpeg = vim.lpeg;
23

34
--- Queried contents
45
---@type table[]
@@ -486,6 +487,62 @@ inline.shortcut_link = function (buffer, TSNode, text, range)
486487
});
487488
end
488489

490+
---|fS "code: LPeg grammar for matching `tags`"
491+
492+
-- NOTE: A tag must not be followed by a `]]`
493+
--
494+
-- This is because `internal links` can have `#^section` parts in them which aren't tags.
495+
-- Tags also don't have a dedicated `TSNode` and must rely in patterns to match.
496+
497+
-- NOTE: Valid characters are `0-9`, `a-z`, `A-Z`, `_` & `-`.
498+
--
499+
-- Special cases such as `#1` are not considered tags in **Obsidian**,
500+
-- but the extra matching required isn't worth the effort in my opinion.
501+
local tag_text = lpeg.R("az", "AZ", "09") + lpeg.S("_-");
502+
local tag = lpeg.C(
503+
lpeg.Cp() * -- Start byte
504+
lpeg.P("#") * tag_text^1 *
505+
lpeg.Cp() -- End byte
506+
) * -lpeg.P("]]");
507+
local tag_pattern = lpeg.Ct( ( tag + 1 )^1 );
508+
509+
---|fE
510+
511+
--- Obsidian-style tags
512+
---@param text string[]
513+
---@param range markview.parsed.range
514+
inline.tag = function (_, _, text, range)
515+
for l, line in ipairs(text) do
516+
local _col_start = l == 1 and range.col_start or 0;
517+
local index = 1;
518+
local tags = tag_pattern:match(line);
519+
520+
while index < #tags do
521+
local match = tags[index];
522+
local col_start = _col_start + tags[index + 1] - 1;
523+
local col_end = tags[index + 2] - 1;
524+
525+
if match and col_start and col_end then
526+
inline.insert({
527+
class = "inline_tag",
528+
label = match:gsub("^#", ""),
529+
text = { match },
530+
531+
range = {
532+
row_start = range.row_start + (l - 1),
533+
col_start = col_start,
534+
535+
row_end = range.row_start + (l - 1),
536+
col_end = col_end
537+
}
538+
});
539+
end
540+
541+
index = index + 3;
542+
end
543+
end
544+
end
545+
489546
--- Uri autolink parser.
490547
---@param TSNode table
491548
---@param text string[]
@@ -543,6 +600,9 @@ inline.parse = function (buffer, TSTree, from, to)
543600
((inline) @markdown_inline.emoji
544601
(#lua-match? @markdown_inline.emoji ":.+:"))
545602
603+
((inline) @markdown_inline.tag
604+
(#lua-match? @markdown_inline.tag "#[a-zA-Z0-9_-]+"))
605+
546606
((email_autolink) @markdown_inline.email)
547607
548608
((image) @markdown_inline.image)

lua/markview/renderers/markdown_inline.lua

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,74 @@ inline.link_uri_autolink = function (buffer, item)
10351035
});
10361036
end
10371037

1038+
--- Render ==tag==.
1039+
---@param buffer integer
1040+
---@param item markview.parsed.markdown_inline.tags
1041+
inline.tag = function (buffer, item)
1042+
---@type markview.config.markdown_inline.tags?
1043+
local main_config = spec.get({ "markdown_inline", "tags" }, { fallback = nil });
1044+
local range = item.range;
1045+
1046+
if not main_config then
1047+
return;
1048+
end
1049+
1050+
---@type markview.config.__inline?
1051+
local config = utils.match(
1052+
main_config,
1053+
item.label,
1054+
{
1055+
eval_args = { buffer, item }
1056+
}
1057+
);
1058+
1059+
if config == nil then
1060+
return;
1061+
end
1062+
1063+
--[[
1064+
NOTE: `hl_mode` shouldn't be "combine".
1065+
1066+
As tags don't have delimiter surrounding it,
1067+
using "combine" prevents adding `paddings`/`margins`
1068+
to the right side.
1069+
]]
1070+
1071+
vim.api.nvim_buf_set_extmark(buffer, inline.ns, range.row_start, range.col_start, {
1072+
undo_restore = false, invalidate = true,
1073+
end_col = range.col_start + 1,
1074+
conceal = "",
1075+
1076+
virt_text_pos = "inline",
1077+
virt_text = {
1078+
{ config.corner_left or "", utils.set_hl(config.corner_left_hl or config.hl) },
1079+
{ config.padding_left or "", utils.set_hl(config.padding_left_hl or config.hl) },
1080+
1081+
{ config.icon or "", utils.set_hl(config.icon_hl or config.hl) }
1082+
},
1083+
1084+
-- hl_mode = "combine"
1085+
});
1086+
1087+
vim.api.nvim_buf_set_extmark(buffer, inline.ns, range.row_start, range.col_start + 1, {
1088+
undo_restore = false, invalidate = true,
1089+
end_col = range.col_end,
1090+
hl_group = utils.set_hl(config.hl)
1091+
});
1092+
1093+
vim.api.nvim_buf_set_extmark(buffer, inline.ns, range.row_start, range.col_end, {
1094+
undo_restore = false, invalidate = true,
1095+
1096+
virt_text_pos = "inline",
1097+
virt_text = {
1098+
{ config.corner_right or "", utils.set_hl(config.corner_right_hl or config.hl) },
1099+
{ config.padding_right or "", utils.set_hl(config.padding_right_hl or config.hl) }
1100+
},
1101+
1102+
-- hl_mode = "combine"
1103+
});
1104+
end
1105+
10381106
--- Renders inline markdown.
10391107
---@param buffer integer
10401108
---@param content markview.parsed.markdown_inline[]

lua/markview/types/parsers/markdown_inline.lua

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,17 @@
208208

209209
------------------------------------------------------------------------------
210210

211+
---@class markview.parsed.markdown_inline.tags
212+
---
213+
---@field class "inline_tag"
214+
---
215+
---@field label string Tag label(text after `#`).
216+
---
217+
---@field text string[]
218+
---@field range markview.parsed.range
219+
220+
------------------------------------------------------------------------------
221+
211222
---@class markview.parsed.markdown_inline.uri_autolinks
212223
---
213224
---@field class "inline_link_uri_autolinks"

lua/markview/types/renderers/markdown_inline.lua

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
---@field inline_codes markview.config.markdown_inline.inline_codes Inline code/code span configuration.
2020
---@field internal_links markview.config.markdown_inline.internal_links Internal link configuration.
2121
---@field uri_autolinks markview.config.markdown_inline.uri_autolinks URI autolink configuration.
22+
---@field tags markview.config.markdown_inline.tags Obsidian-style tags configuration.
2223

2324
------------------------------------------------------------------------------
2425

@@ -190,3 +191,17 @@
190191
--[[ Options for a specific URI autolink type. ]]
191192
---@alias markview.config.markdown_inline.uri_autolinks.opts markview.config.__inline
192193

194+
------------------------------------------------------------------------------
195+
196+
--- Configuration for emails.
197+
---@class markview.config.markdown_inline.tags
198+
---
199+
---@field enable boolean Enable rendering of Obsidian-style tags.
200+
---
201+
---@field default markview.config.markdown_inline.tags.opts Default configuration for tags.
202+
---@field [string] markview.config.markdown_inline.tags.opts Configuration for emails whose name(text after `#`) matches `string`.
203+
204+
205+
--[[ Options for a specific email type. ]]
206+
---@alias markview.config.markdown_inline.tags.opts markview.config.__inline
207+

markview.nvim.wiki

0 commit comments

Comments
 (0)