From 168a3b5b958191ec3695dc3cb1e60dce2b433ae2 Mon Sep 17 00:00:00 2001 From: Shawon Date: Mon, 12 Jan 2026 07:50:02 +0600 Subject: [PATCH 01/40] chore: Added parser template for `asciidoc` --- lua/markview/parsers/asciidoc.lua | 116 ++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 lua/markview/parsers/asciidoc.lua diff --git a/lua/markview/parsers/asciidoc.lua b/lua/markview/parsers/asciidoc.lua new file mode 100644 index 00000000..f564d909 --- /dev/null +++ b/lua/markview/parsers/asciidoc.lua @@ -0,0 +1,116 @@ +--- HTML parser for `markview.nvim`. +local asciidoc = {}; + +--- Queried contents +---@type table[] +asciidoc.content = {}; + +--- Queried contents, but sorted +---@type { [string]: table } +asciidoc.sorted = {} + +--- Wrapper for `table.insert()`. +---@param data table +asciidoc.insert = function (data) + table.insert(asciidoc.content, data); + + if not asciidoc.sorted[data.class] then + asciidoc.sorted[data.class] = {}; + end + + table.insert(asciidoc.sorted[data.class], data); +end + +--- Heading element parser +---@param buffer integer +---@param TSNode table +---@param text string[] +---@param range markview.parsed.range +asciidoc.heading = function (buffer, TSNode, text, range) + local tag = vim.treesitter.get_node_text(TSNode:named_child(0):named_child(0), buffer); + + asciidoc.insert({ + class = "asciidoc_heading", + level = tonumber(tag:match("^h(%d)$")), + + text = text, + range = range + }); +end + +--- HTML parser +---@param buffer integer +---@param TSTree table +---@param from integer? +---@param to integer? +---@return markview.parsed.asciidoc[] +---@return markview.parsed.asciidoc_sorted +asciidoc.parse = function (buffer, TSTree, from, to) + -- Clear the previous contents + asciidoc.sorted = {}; + asciidoc.content = {}; + + local scanned_queries = vim.treesitter.query.parse("asciidoc", [[ + ]]); + + for capture_id, capture_node, _, _ in scanned_queries:iter_captures(TSTree:root(), buffer, from, to) do + local capture_name = scanned_queries.captures[capture_id]; + + if not capture_name:match("^asciidoc%.") then + goto continue; + end + + ---@type string? + local capture_text = vim.treesitter.get_node_text(capture_node, buffer); + local r_start, c_start, r_end, c_end = capture_node:range(); + + if capture_text == nil then + goto continue; + end + + if not capture_text:match("\n$") then + capture_text = capture_text .. "\n"; + end + + local lines = {}; + + for line in capture_text:gmatch("(.-)\n") do + table.insert(lines, line); + end + + ---@type boolean, string + local success, error = pcall( + asciidoc[capture_name:gsub("^asciidoc%.", "")], + + buffer, + capture_node, + lines, + { + row_start = r_start, + col_start = c_start, + + row_end = r_end, + col_end = c_end + } + ); + + if success == false then + require("markview.health").print({ + kind = "ERR", + + from = "parsers/asciidoc.lua", + fn = "parse()", + + message = { + { tostring(error), "DiagnosticError" } + } + }); + end + + ::continue:: + end + + return asciidoc.content, asciidoc.sorted; +end + +return asciidoc; From 966a713f3d28cb59f6cb8defa7b101561bac4a89 Mon Sep 17 00:00:00 2001 From: Shawon Date: Mon, 12 Jan 2026 16:00:02 +0600 Subject: [PATCH 02/40] feat(asciidoc): Added `document title` support --- lua/markview/config/asciidoc.lua | 7 ++ lua/markview/parser.lua | 1 + lua/markview/parsers/asciidoc.lua | 13 ++-- lua/markview/renderer.lua | 5 ++ lua/markview/renderers/asciidoc.lua | 82 +++++++++++++++++++++++ lua/markview/spec.lua | 3 +- lua/markview/types/parsers/asciidoc.lua | 9 +++ lua/markview/types/renderers/asciidoc.lua | 13 ++++ test/asciidoc.adoc | 3 + 9 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 lua/markview/config/asciidoc.lua create mode 100644 lua/markview/renderers/asciidoc.lua create mode 100644 lua/markview/types/parsers/asciidoc.lua create mode 100644 lua/markview/types/renderers/asciidoc.lua create mode 100644 test/asciidoc.adoc diff --git a/lua/markview/config/asciidoc.lua b/lua/markview/config/asciidoc.lua new file mode 100644 index 00000000..64d76cb7 --- /dev/null +++ b/lua/markview/config/asciidoc.lua @@ -0,0 +1,7 @@ +return { + document_title = { + icon = "H ", + line_hl = "MarkviewHeading1", + }, + section_title = {}, +}; diff --git a/lua/markview/parser.lua b/lua/markview/parser.lua index aa7a82be..c39533aa 100644 --- a/lua/markview/parser.lua +++ b/lua/markview/parser.lua @@ -99,6 +99,7 @@ parser.init = function (buffer, from, to, cache) ---|fS local _parsers = { + asciidoc = require("markview.parsers.asciidoc"), comment = require("markview.parsers.comment"); markdown = require("markview.parsers.markdown"); markdown_inline = require("markview.parsers.markdown_inline"); diff --git a/lua/markview/parsers/asciidoc.lua b/lua/markview/parsers/asciidoc.lua index f564d909..2c6f98c8 100644 --- a/lua/markview/parsers/asciidoc.lua +++ b/lua/markview/parsers/asciidoc.lua @@ -1,6 +1,10 @@ --- HTML parser for `markview.nvim`. local asciidoc = {}; +asciidoc.data = { + document_title = nil, +}; + --- Queried contents ---@type table[] asciidoc.content = {}; @@ -21,17 +25,13 @@ asciidoc.insert = function (data) table.insert(asciidoc.sorted[data.class], data); end ---- Heading element parser ---@param buffer integer ---@param TSNode table ---@param text string[] ---@param range markview.parsed.range -asciidoc.heading = function (buffer, TSNode, text, range) - local tag = vim.treesitter.get_node_text(TSNode:named_child(0):named_child(0), buffer); - +asciidoc.doc_title = function (buffer, TSNode, text, range) asciidoc.insert({ - class = "asciidoc_heading", - level = tonumber(tag:match("^h(%d)$")), + class = "asciidoc_document_title", text = text, range = range @@ -51,6 +51,7 @@ asciidoc.parse = function (buffer, TSTree, from, to) asciidoc.content = {}; local scanned_queries = vim.treesitter.query.parse("asciidoc", [[ + (document_title) @asciidoc.doc_title ]]); for capture_id, capture_node, _, _ in scanned_queries:iter_captures(TSTree:root(), buffer, from, to) do diff --git a/lua/markview/renderer.lua b/lua/markview/renderer.lua index 3beaf54f..a7a62d39 100644 --- a/lua/markview/renderer.lua +++ b/lua/markview/renderer.lua @@ -15,6 +15,9 @@ renderer.__filter_cache = { renderer.option_maps = { ---|fS + asciidoc = { + document_title = { "asciidoc_document_title" }, + }, comment = { autolinks = { "comment_autolink" }, code_blocks = { "comment_code_block" }, @@ -366,6 +369,7 @@ renderer.render = function (buffer, parsed_content) ---|fS local _renderers = { + asciidoc = require("markview.renderers.asciidoc"), comment = require("markview.renderers.comment"), html = require("markview.renderers.html"), markdown = require("markview.renderers.markdown"), @@ -470,6 +474,7 @@ renderer.clear = function (buffer, from, to, hybrid_mode) ---|fS local _renderers = { + asciidoc = require("markview.renderers.asciidoc"), comment = require("markview.renderers.comment"); html = require("markview.renderers.html"); markdown = require("markview.renderers.markdown"); diff --git a/lua/markview/renderers/asciidoc.lua b/lua/markview/renderers/asciidoc.lua new file mode 100644 index 00000000..90f19b30 --- /dev/null +++ b/lua/markview/renderers/asciidoc.lua @@ -0,0 +1,82 @@ +local asciidoc = {}; + +local utils = require("markview.utils"); +local spec = require("markview.spec"); + +asciidoc.ns = vim.api.nvim_create_namespace("markview/asciidoc"); + +--- Renders void elements +---@param buffer integer +---@param item markview.parsed.asciidoc.document_titles +asciidoc.document_title = function (buffer, item) + ---@type markview.config.asciidoc.document_titles? + local config = spec.get({ "asciidoc", "document_titles" }, { eval_args = { buffer, item } }); + + if not config then + return; + end + + local range = item.range; + + utils.set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start, { + -- Remove `#+%s*` amount of characters. + end_col = range.col_start + 1, + conceal = "", + + sign_text = tostring(config.sign or ""), + sign_hl_group = utils.set_hl(config.sign_hl), + + virt_text = { + { config.icon, config.icon_hl or config.hl }, + }, + line_hl_group = utils.set_hl(config.hl), + }); +end + +--- Renders HTML elements +---@param buffer integer +---@param content markview.parsed.asciidoc[] +asciidoc.render = function (buffer, content) + asciidoc.cache = { + font_regions = {}, + style_regions = { + superscripts = {}, + subscripts = {} + }, + }; + + local custom = spec.get({ "renderers" }, { fallback = {} }); + + for _, item in ipairs(content or {}) do + local success, err; + + if custom[item.class] then + success, err = pcall(custom[item.class], asciidoc.ns, buffer, item); + else + success, err = pcall(asciidoc[item.class:gsub("^asciidoc_", "")], buffer, item); + end + + if success == false then + require("markview.health").print({ + kind = "ERR", + + from = "renderers/asciidoc.lua", + fn = "render() -> " .. item.class, + + message = { + { tostring(err), "DiagnosticError" } + } + }); + end + end +end + +--- Clears decorations of HTML elements +---@param buffer integer +---@param from integer +---@param to integer +asciidoc.clear = function (buffer, from, to) + vim.api.nvim_buf_clear_namespace(buffer, asciidoc.ns, from or 0, to or -1); +end + +return asciidoc; diff --git a/lua/markview/spec.lua b/lua/markview/spec.lua index d35690f9..a287bca4 100644 --- a/lua/markview/spec.lua +++ b/lua/markview/spec.lua @@ -189,7 +189,7 @@ spec.default = { debounce = 150, icon_provider = "internal", - filetypes = { "markdown", "quarto", "rmd", "typst", }, + filetypes = { "markdown", "quarto", "rmd", "typst", "asciidoc", }, ignore_buftypes = { "nofile" }, condition = function (buffer) local is_enabled = spec.get({ "experimental", "fancy_comments" }, { @@ -227,6 +227,7 @@ spec.default = { ---@type string[] Properties that should be sourced *externally*. spec.__external_config = { + "asciidoc", "comment", "html", "markdown", diff --git a/lua/markview/types/parsers/asciidoc.lua b/lua/markview/types/parsers/asciidoc.lua new file mode 100644 index 00000000..756e86e8 --- /dev/null +++ b/lua/markview/types/parsers/asciidoc.lua @@ -0,0 +1,9 @@ +---@meta + +---@class markview.parsed.asciidoc.document_titles +--- +---@field class_name "asciidoc_document_title" +--- +---@field text string[] +---@field range markview.parsed.range + diff --git a/lua/markview/types/renderers/asciidoc.lua b/lua/markview/types/renderers/asciidoc.lua new file mode 100644 index 00000000..cd9b2c23 --- /dev/null +++ b/lua/markview/types/renderers/asciidoc.lua @@ -0,0 +1,13 @@ +---@meta + +---@class markview.config.asciidoc.document_titles +--- +---@field enable boolean +--- +---@field sign? string +---@field sign_hl? string +--- +---@field icon? string +---@field icon_hl? string +--- +---@field hl? string diff --git a/test/asciidoc.adoc b/test/asciidoc.adoc new file mode 100644 index 00000000..6c39ed8a --- /dev/null +++ b/test/asciidoc.adoc @@ -0,0 +1,3 @@ += Hello world + + From 4f45dad306acecbaa637913f7765f5ba5944fd80 Mon Sep 17 00:00:00 2001 From: Shawon Date: Mon, 12 Jan 2026 22:30:26 +0600 Subject: [PATCH 03/40] fewt(config, asciidoc): Updated document title config --- lua/markview/config/asciidoc.lua | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lua/markview/config/asciidoc.lua b/lua/markview/config/asciidoc.lua index 64d76cb7..9e19db16 100644 --- a/lua/markview/config/asciidoc.lua +++ b/lua/markview/config/asciidoc.lua @@ -1,7 +1,13 @@ +---@type markview.config.asciidoc return { - document_title = { - icon = "H ", - line_hl = "MarkviewHeading1", + document_titles = { + enable = true, + + sign = "󰛓 ", + sign_hl = "MarkviewPalette7Sign", + + icon = "󰛓 ", + hl = "MarkviewPalette7", }, - section_title = {}, + section_titles = {}, }; From 33e897d8404b1fb97cb8aec3648a6bb36b7230be Mon Sep 17 00:00:00 2001 From: Shawon Date: Mon, 12 Jan 2026 22:30:49 +0600 Subject: [PATCH 04/40] fix(renderers, asciidoc, document_titles): Correctly handle conceal --- lua/markview/renderers/asciidoc.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/markview/renderers/asciidoc.lua b/lua/markview/renderers/asciidoc.lua index 90f19b30..9e49a3c8 100644 --- a/lua/markview/renderers/asciidoc.lua +++ b/lua/markview/renderers/asciidoc.lua @@ -19,8 +19,8 @@ asciidoc.document_title = function (buffer, item) local range = item.range; utils.set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start, { - -- Remove `#+%s*` amount of characters. - end_col = range.col_start + 1, + -- Remove `=%s*` amount of characters. + end_col = range.col_start + #string.match(item.text[1] or "", "=+%s*"), conceal = "", sign_text = tostring(config.sign or ""), From d313c1db77c86bc81ce815ca726568efc76065d5 Mon Sep 17 00:00:00 2001 From: Shawon Date: Mon, 12 Jan 2026 22:31:52 +0600 Subject: [PATCH 05/40] doc(asciidoc): Added missing types --- lua/markview/types/parsers/asciidoc.lua | 13 +++++++++++++ lua/markview/types/renderers/asciidoc.lua | 9 +++++++++ 2 files changed, 22 insertions(+) diff --git a/lua/markview/types/parsers/asciidoc.lua b/lua/markview/types/parsers/asciidoc.lua index 756e86e8..5e12778d 100644 --- a/lua/markview/types/parsers/asciidoc.lua +++ b/lua/markview/types/parsers/asciidoc.lua @@ -1,5 +1,7 @@ ---@meta +------------------------------------------------------------------------------ + ---@class markview.parsed.asciidoc.document_titles --- ---@field class_name "asciidoc_document_title" @@ -7,3 +9,14 @@ ---@field text string[] ---@field range markview.parsed.range +------------------------------------------------------------------------------ + +---@alias markview.parsed.asciidoc +---| markview.config.asciidoc.document_titles + +------------------------------------------------------------------------------ + +---@class markview.parsed.asciidoc_sorted +--- +---@field document_titles markview.config.asciidoc.document_titles[] + diff --git a/lua/markview/types/renderers/asciidoc.lua b/lua/markview/types/renderers/asciidoc.lua index cd9b2c23..dc9f4d22 100644 --- a/lua/markview/types/renderers/asciidoc.lua +++ b/lua/markview/types/renderers/asciidoc.lua @@ -1,5 +1,7 @@ ---@meta +------------------------------------------------------------------------------ + ---@class markview.config.asciidoc.document_titles --- ---@field enable boolean @@ -11,3 +13,10 @@ ---@field icon_hl? string --- ---@field hl? string + +------------------------------------------------------------------------------ + +---@class markview.config.asciidoc +--- +---@field document_titles markview.config.asciidoc.document_titles + From 7da3aea514199505e13c83bbc3a84200c614cacb Mon Sep 17 00:00:00 2001 From: Shawon Date: Mon, 12 Jan 2026 22:48:46 +0600 Subject: [PATCH 06/40] feat(asciidoc): Added document attributes --- lua/markview/config/asciidoc.lua | 4 +++- lua/markview/parsers/asciidoc.lua | 18 +++++++++++++++--- lua/markview/renderers/asciidoc.lua | 19 ++++++++++++++++++- lua/markview/types/parsers/asciidoc.lua | 11 +++++++++++ lua/markview/types/renderers/asciidoc.lua | 7 +++++++ test/asciidoc.adoc | 5 +++++ 6 files changed, 59 insertions(+), 5 deletions(-) diff --git a/lua/markview/config/asciidoc.lua b/lua/markview/config/asciidoc.lua index 9e19db16..e75bb724 100644 --- a/lua/markview/config/asciidoc.lua +++ b/lua/markview/config/asciidoc.lua @@ -9,5 +9,7 @@ return { icon = "󰛓 ", hl = "MarkviewPalette7", }, - section_titles = {}, + document_attributes = { + enable = true, + }, }; diff --git a/lua/markview/parsers/asciidoc.lua b/lua/markview/parsers/asciidoc.lua index 2c6f98c8..a87412d6 100644 --- a/lua/markview/parsers/asciidoc.lua +++ b/lua/markview/parsers/asciidoc.lua @@ -25,11 +25,11 @@ asciidoc.insert = function (data) table.insert(asciidoc.sorted[data.class], data); end ----@param buffer integer ----@param TSNode table ---@param text string[] ---@param range markview.parsed.range -asciidoc.doc_title = function (buffer, TSNode, text, range) +asciidoc.doc_title = function (_, _, text, range) + asciidoc.data.document_title = string.match(text[1] or "", "=%s+(.*)$") + asciidoc.insert({ class = "asciidoc_document_title", @@ -38,6 +38,17 @@ asciidoc.doc_title = function (buffer, TSNode, text, range) }); end +---@param text string[] +---@param range markview.parsed.range +asciidoc.doc_attr = function (_, _, text, range) + asciidoc.insert({ + class = "asciidoc_document_attribute", + + text = text, + range = range + }); +end + --- HTML parser ---@param buffer integer ---@param TSTree table @@ -52,6 +63,7 @@ asciidoc.parse = function (buffer, TSTree, from, to) local scanned_queries = vim.treesitter.query.parse("asciidoc", [[ (document_title) @asciidoc.doc_title + (document_attr) @asciidoc.doc_attr ]]); for capture_id, capture_node, _, _ in scanned_queries:iter_captures(TSTree:root(), buffer, from, to) do diff --git a/lua/markview/renderers/asciidoc.lua b/lua/markview/renderers/asciidoc.lua index 9e49a3c8..8790f559 100644 --- a/lua/markview/renderers/asciidoc.lua +++ b/lua/markview/renderers/asciidoc.lua @@ -5,7 +5,24 @@ local spec = require("markview.spec"); asciidoc.ns = vim.api.nvim_create_namespace("markview/asciidoc"); ---- Renders void elements +---@param buffer integer +---@param item markview.parsed.asciidoc.document_titles +asciidoc.document_attribute = function (buffer, item) + ---@type markview.config.asciidoc.document_titles? + local config = spec.get({ "asciidoc", "document_attributes" }, { eval_args = { buffer, item } }); + + if not config then + return; + end + + local range = item.range; + + utils.set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start, { + end_row = range.row_end - 1, + conceal_lines = "", + }); +end + ---@param buffer integer ---@param item markview.parsed.asciidoc.document_titles asciidoc.document_title = function (buffer, item) diff --git a/lua/markview/types/parsers/asciidoc.lua b/lua/markview/types/parsers/asciidoc.lua index 5e12778d..f8b80e2d 100644 --- a/lua/markview/types/parsers/asciidoc.lua +++ b/lua/markview/types/parsers/asciidoc.lua @@ -2,6 +2,15 @@ ------------------------------------------------------------------------------ +---@class markview.parsed.asciidoc.document_attributes +--- +---@field class_name "asciidoc_document_attribute" +--- +---@field text string[] +---@field range markview.parsed.range + +------------------------------------------------------------------------------ + ---@class markview.parsed.asciidoc.document_titles --- ---@field class_name "asciidoc_document_title" @@ -12,11 +21,13 @@ ------------------------------------------------------------------------------ ---@alias markview.parsed.asciidoc +---| markview.config.asciidoc.document_attributes ---| markview.config.asciidoc.document_titles ------------------------------------------------------------------------------ ---@class markview.parsed.asciidoc_sorted --- +---@field document_attributes markview.config.asciidoc.document_attributes[] ---@field document_titles markview.config.asciidoc.document_titles[] diff --git a/lua/markview/types/renderers/asciidoc.lua b/lua/markview/types/renderers/asciidoc.lua index dc9f4d22..16263211 100644 --- a/lua/markview/types/renderers/asciidoc.lua +++ b/lua/markview/types/renderers/asciidoc.lua @@ -2,6 +2,12 @@ ------------------------------------------------------------------------------ +---@class markview.config.asciidoc.document_attributes +--- +---@field enable boolean + +------------------------------------------------------------------------------ + ---@class markview.config.asciidoc.document_titles --- ---@field enable boolean @@ -18,5 +24,6 @@ ---@class markview.config.asciidoc --- +---@field document_attributes markview.config.asciidoc.document_attributes ---@field document_titles markview.config.asciidoc.document_titles diff --git a/test/asciidoc.adoc b/test/asciidoc.adoc index 6c39ed8a..91123af7 100644 --- a/test/asciidoc.adoc +++ b/test/asciidoc.adoc @@ -1,3 +1,8 @@ = Hello world +:bye: +hi +:hi: 123 + + From 75ae830af19853de5dacdd84f440be711c9003e0 Mon Sep 17 00:00:00 2001 From: Shawon Date: Tue, 13 Jan 2026 17:22:52 +0600 Subject: [PATCH 07/40] feat(asciidoc_inline): Added parser template --- lua/markview/config/asciidoc_inline.lua | 5 + lua/markview/parser.lua | 1 + lua/markview/parsers/asciidoc.lua | 12 +- lua/markview/parsers/asciidoc_inline.lua | 123 +++++++++++++++++++++ lua/markview/renderer.lua | 4 + lua/markview/renderers/asciidoc.lua | 1 - lua/markview/renderers/asciidoc_inline.lua | 108 ++++++++++++++++++ lua/markview/spec.lua | 1 + test/asciidoc.adoc | 33 ++++++ 9 files changed, 281 insertions(+), 7 deletions(-) create mode 100644 lua/markview/config/asciidoc_inline.lua create mode 100644 lua/markview/parsers/asciidoc_inline.lua create mode 100644 lua/markview/renderers/asciidoc_inline.lua diff --git a/lua/markview/config/asciidoc_inline.lua b/lua/markview/config/asciidoc_inline.lua new file mode 100644 index 00000000..35be288b --- /dev/null +++ b/lua/markview/config/asciidoc_inline.lua @@ -0,0 +1,5 @@ +---@type markview.config.asciidoc +return { + bolds = { enable = true }, + italics = { enable = true }, +}; diff --git a/lua/markview/parser.lua b/lua/markview/parser.lua index c39533aa..d9e75a12 100644 --- a/lua/markview/parser.lua +++ b/lua/markview/parser.lua @@ -100,6 +100,7 @@ parser.init = function (buffer, from, to, cache) local _parsers = { asciidoc = require("markview.parsers.asciidoc"), + asciidoc_inline = require("markview.parsers.asciidoc_inline"), comment = require("markview.parsers.comment"); markdown = require("markview.parsers.markdown"); markdown_inline = require("markview.parsers.markdown_inline"); diff --git a/lua/markview/parsers/asciidoc.lua b/lua/markview/parsers/asciidoc.lua index a87412d6..96e8caed 100644 --- a/lua/markview/parsers/asciidoc.lua +++ b/lua/markview/parsers/asciidoc.lua @@ -27,11 +27,9 @@ end ---@param text string[] ---@param range markview.parsed.range -asciidoc.doc_title = function (_, _, text, range) - asciidoc.data.document_title = string.match(text[1] or "", "=%s+(.*)$") - +asciidoc.doc_attr = function (_, _, text, range) asciidoc.insert({ - class = "asciidoc_document_title", + class = "asciidoc_document_attribute", text = text, range = range @@ -40,9 +38,11 @@ end ---@param text string[] ---@param range markview.parsed.range -asciidoc.doc_attr = function (_, _, text, range) +asciidoc.doc_title = function (_, _, text, range) + asciidoc.data.document_title = string.match(text[1] or "", "=%s+(.*)$") + asciidoc.insert({ - class = "asciidoc_document_attribute", + class = "asciidoc_document_title", text = text, range = range diff --git a/lua/markview/parsers/asciidoc_inline.lua b/lua/markview/parsers/asciidoc_inline.lua new file mode 100644 index 00000000..c85f826a --- /dev/null +++ b/lua/markview/parsers/asciidoc_inline.lua @@ -0,0 +1,123 @@ +--- HTML parser for `markview.nvim`. +local asciidoc_inline = {}; + +--- Queried contents +---@type table[] +asciidoc_inline.content = {}; + +--- Queried contents, but sorted +---@type { [string]: table } +asciidoc_inline.sorted = {} + +--- Wrapper for `table.insert()`. +---@param data table +asciidoc_inline.insert = function (data) + table.insert(asciidoc_inline.content, data); + + if not asciidoc_inline.sorted[data.class] then + asciidoc_inline.sorted[data.class] = {}; + end + + table.insert(asciidoc_inline.sorted[data.class], data); +end + +---@param text string[] +---@param range markview.parsed.range +asciidoc_inline.bold = function (_, _, text, range) + asciidoc_inline.insert({ + class = "asciidoc_inline_bold", + + text = text, + range = range + }); +end + +---@param text string[] +---@param range markview.parsed.range +asciidoc_inline.italic = function (_, _, text, range) + asciidoc_inline.insert({ + class = "asciidoc_inline_italic", + + text = text, + range = range + }); +end + +--- HTML parser +---@param buffer integer +---@param TSTree table +---@param from integer? +---@param to integer? +---@return markview.parsed.asciidoc_inline[] +---@return markview.parsed.asciidoc_inline_sorted +asciidoc_inline.parse = function (buffer, TSTree, from, to) + -- Clear the previous contents + asciidoc_inline.sorted = {}; + asciidoc_inline.content = {}; + + local scanned_queries = vim.treesitter.query.parse("asciidoc_inline", [[ + (emphasis) @asciidoc_inline.bold + (ltalic) @asciidoc_inline.italic + ]]); + + for capture_id, capture_node, _, _ in scanned_queries:iter_captures(TSTree:root(), buffer, from, to) do + local capture_name = scanned_queries.captures[capture_id]; + + if not capture_name:match("^asciidoc_inline%.") then + goto continue; + end + + ---@type string? + local capture_text = vim.treesitter.get_node_text(capture_node, buffer); + local r_start, c_start, r_end, c_end = capture_node:range(); + + if capture_text == nil then + goto continue; + end + + if not capture_text:match("\n$") then + capture_text = capture_text .. "\n"; + end + + local lines = {}; + + for line in capture_text:gmatch("(.-)\n") do + table.insert(lines, line); + end + + ---@type boolean, string + local success, error = pcall( + asciidoc_inline[capture_name:gsub("^asciidoc_inline%.", "")], + + buffer, + capture_node, + lines, + { + row_start = r_start, + col_start = c_start, + + row_end = r_end, + col_end = c_end + } + ); + + if success == false then + require("markview.health").print({ + kind = "ERR", + + from = "parsers/asciidoc_inline.lua", + fn = "parse()", + + message = { + { tostring(error), "DiagnosticError" } + } + }); + end + + ::continue:: + end + + return asciidoc_inline.content, asciidoc_inline.sorted; +end + +return asciidoc_inline; diff --git a/lua/markview/renderer.lua b/lua/markview/renderer.lua index a7a62d39..d6c73e10 100644 --- a/lua/markview/renderer.lua +++ b/lua/markview/renderer.lua @@ -18,6 +18,8 @@ renderer.option_maps = { asciidoc = { document_title = { "asciidoc_document_title" }, }, + asciidoc_inline = { + }, comment = { autolinks = { "comment_autolink" }, code_blocks = { "comment_code_block" }, @@ -370,6 +372,7 @@ renderer.render = function (buffer, parsed_content) local _renderers = { asciidoc = require("markview.renderers.asciidoc"), + asciidoc_inline = require("markview.renderers.asciidoc_inline"), comment = require("markview.renderers.comment"), html = require("markview.renderers.html"), markdown = require("markview.renderers.markdown"), @@ -475,6 +478,7 @@ renderer.clear = function (buffer, from, to, hybrid_mode) local _renderers = { asciidoc = require("markview.renderers.asciidoc"), + asciidoc_inline = require("markview.renderers.asciidoc_inline"), comment = require("markview.renderers.comment"); html = require("markview.renderers.html"); markdown = require("markview.renderers.markdown"); diff --git a/lua/markview/renderers/asciidoc.lua b/lua/markview/renderers/asciidoc.lua index 8790f559..b29788c9 100644 --- a/lua/markview/renderers/asciidoc.lua +++ b/lua/markview/renderers/asciidoc.lua @@ -50,7 +50,6 @@ asciidoc.document_title = function (buffer, item) }); end ---- Renders HTML elements ---@param buffer integer ---@param content markview.parsed.asciidoc[] asciidoc.render = function (buffer, content) diff --git a/lua/markview/renderers/asciidoc_inline.lua b/lua/markview/renderers/asciidoc_inline.lua new file mode 100644 index 00000000..9c4cff5b --- /dev/null +++ b/lua/markview/renderers/asciidoc_inline.lua @@ -0,0 +1,108 @@ +local asciidoc_inline = {}; + +local utils = require("markview.utils"); +local spec = require("markview.spec"); + +asciidoc_inline.ns = vim.api.nvim_create_namespace("markview/asciidoc_inline"); + +---@param buffer integer +---@param item markview.parsed.asciidoc_inline.document_titles +asciidoc_inline.bold = function (buffer, item) + ---@type markview.config.asciidoc_inline.bolds? + local config = spec.get({ "asciidoc_inline", "bolds" }, { eval_args = { buffer, item } }); + + if not config then + return; + end + + local range = item.range; + + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_start, range.col_start, { + end_col = range.col_start + 2, + conceal = "", + }); + + if not string.match(item.text[#item.text] or "", "%*%*$") then + return; + end + + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_end, range.col_end - 2, { + end_col = range.col_end, + conceal = "", + }); +end + +---@param buffer integer +---@param item markview.parsed.asciidoc_inline.document_titles +asciidoc_inline.italic = function (buffer, item) + ---@type markview.config.asciidoc_inline.document_titles? + local config = spec.get({ "asciidoc_inline", "italics" }, { eval_args = { buffer, item } }); + + if not config then + return; + end + + local range = item.range; + + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_start, range.col_start, { + end_col = range.col_start + 1, + conceal = "", + }); + + if not string.match(item.text[#item.text] or "", "%*$") then + return; + end + + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_end, range.col_end - 1, { + end_col = range.col_end, + conceal = "", + }); +end + +--- Renders HTML elements +---@param buffer integer +---@param content markview.parsed.asciidoc_inline[] +asciidoc_inline.render = function (buffer, content) + asciidoc_inline.cache = { + font_regions = {}, + style_regions = { + superscripts = {}, + subscripts = {} + }, + }; + + local custom = spec.get({ "renderers" }, { fallback = {} }); + + for _, item in ipairs(content or {}) do + local success, err; + + if custom[item.class] then + success, err = pcall(custom[item.class], asciidoc_inline.ns, buffer, item); + else + success, err = pcall(asciidoc_inline[item.class:gsub("^asciidoc_inline_", "")], buffer, item); + end + + if success == false then + require("markview.health").print({ + kind = "ERR", + + from = "renderers/asciidoc_inline.lua", + fn = "render() -> " .. item.class, + + message = { + { tostring(err), "DiagnosticError" } + } + }); + end + end +end + +--- Clears decorations of HTML elements +---@param buffer integer +---@param from integer +---@param to integer +asciidoc_inline.clear = function (buffer, from, to) + vim.api.nvim_buf_clear_namespace(buffer, asciidoc_inline.ns, from or 0, to or -1); +end + +return asciidoc_inline; diff --git a/lua/markview/spec.lua b/lua/markview/spec.lua index a287bca4..91ed55fd 100644 --- a/lua/markview/spec.lua +++ b/lua/markview/spec.lua @@ -228,6 +228,7 @@ spec.default = { ---@type string[] Properties that should be sourced *externally*. spec.__external_config = { "asciidoc", + "asciidoc_inline", "comment", "html", "markdown", diff --git a/test/asciidoc.adoc b/test/asciidoc.adoc index 91123af7..1c5472a2 100644 --- a/test/asciidoc.adoc +++ b/test/asciidoc.adoc @@ -4,5 +4,38 @@ hi :hi: 123 +It has *strong* significance to me. +I _cannot_ stress this enough. + +Type `OK` to accept. + +That *_really_* has to go. + +Can't pick one? Let's use them `*_all_*`. + + +**C**reate, **R**ead, **U**pdate, and **D**elete (CRUD) + +That's fan__freakin__tastic! + +Don't pass generic ``Object``s to methods that accept ``String``s! + +It was Beatle**__mania__**! + + +Mark my words, #automation is essential#. + +##Mark##up refers to text that contains formatting ##mark##s. + +Where did all the [.underline]#cores# go? + +We need [.line-through]#ten# twenty VMs. + +A [.myrole]#custom role# must be fulfilled by the theme. + + +^super^script + +~sub~script From 97a024bd37b8e0b13419b676971e9b7723d36be1 Mon Sep 17 00:00:00 2001 From: Shawon Date: Tue, 13 Jan 2026 23:35:47 +0600 Subject: [PATCH 08/40] fix(renderers, asciidoc_inline): Fixed conceal for `bold` & `italic`s --- lua/markview/parsers/asciidoc_inline.lua | 34 ++++++++++++++++++++-- lua/markview/renderers/asciidoc_inline.lua | 22 +++++--------- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/lua/markview/parsers/asciidoc_inline.lua b/lua/markview/parsers/asciidoc_inline.lua index c85f826a..77e6c833 100644 --- a/lua/markview/parsers/asciidoc_inline.lua +++ b/lua/markview/parsers/asciidoc_inline.lua @@ -21,22 +21,52 @@ asciidoc_inline.insert = function (data) table.insert(asciidoc_inline.sorted[data.class], data); end +---@param buffer integer +---@param TSNode TSNode ---@param text string[] ---@param range markview.parsed.range -asciidoc_inline.bold = function (_, _, text, range) +asciidoc_inline.bold = function (buffer, TSNode, text, range) + local delimiters = {}; + + for child in TSNode:iter_children() do + if child:named() == false then + if delimiters[1] then + delimiters[2] = vim.treesitter.get_node_text(child, buffer, {}); + else + delimiters[1] = vim.treesitter.get_node_text(child, buffer, {}); + end + end + end + asciidoc_inline.insert({ class = "asciidoc_inline_bold", + delimiters = delimiters, text = text, range = range }); end +---@param buffer integer +---@param TSNode TSNode ---@param text string[] ---@param range markview.parsed.range -asciidoc_inline.italic = function (_, _, text, range) +asciidoc_inline.italic = function (buffer, TSNode, text, range) + local delimiters = {}; + + for child in TSNode:iter_children() do + if child:named() == false then + if delimiters[1] then + delimiters[2] = vim.treesitter.get_node_text(child, buffer, {}); + else + delimiters[1] = vim.treesitter.get_node_text(child, buffer, {}); + end + end + end + asciidoc_inline.insert({ class = "asciidoc_inline_italic", + delimiters = delimiters, text = text, range = range diff --git a/lua/markview/renderers/asciidoc_inline.lua b/lua/markview/renderers/asciidoc_inline.lua index 9c4cff5b..10da342a 100644 --- a/lua/markview/renderers/asciidoc_inline.lua +++ b/lua/markview/renderers/asciidoc_inline.lua @@ -6,7 +6,7 @@ local spec = require("markview.spec"); asciidoc_inline.ns = vim.api.nvim_create_namespace("markview/asciidoc_inline"); ---@param buffer integer ----@param item markview.parsed.asciidoc_inline.document_titles +---@param item markview.parsed.asciidoc_inline.bolds asciidoc_inline.bold = function (buffer, item) ---@type markview.config.asciidoc_inline.bolds? local config = spec.get({ "asciidoc_inline", "bolds" }, { eval_args = { buffer, item } }); @@ -18,24 +18,20 @@ asciidoc_inline.bold = function (buffer, item) local range = item.range; utils.set_extmark(buffer, asciidoc_inline.ns, range.row_start, range.col_start, { - end_col = range.col_start + 2, + end_col = range.col_start + #(item.delimiters[1] or ""), conceal = "", }); - if not string.match(item.text[#item.text] or "", "%*%*$") then - return; - end - - utils.set_extmark(buffer, asciidoc_inline.ns, range.row_end, range.col_end - 2, { + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_end, range.col_end - #(item.delimiters[2] or ""), { end_col = range.col_end, conceal = "", }); end ---@param buffer integer ----@param item markview.parsed.asciidoc_inline.document_titles +---@param item markview.parsed.asciidoc_inline.italics asciidoc_inline.italic = function (buffer, item) - ---@type markview.config.asciidoc_inline.document_titles? + ---@type markview.config.asciidoc_inline.italics? local config = spec.get({ "asciidoc_inline", "italics" }, { eval_args = { buffer, item } }); if not config then @@ -45,15 +41,11 @@ asciidoc_inline.italic = function (buffer, item) local range = item.range; utils.set_extmark(buffer, asciidoc_inline.ns, range.row_start, range.col_start, { - end_col = range.col_start + 1, + end_col = range.col_start + #(item.delimiters[1] or ""), conceal = "", }); - if not string.match(item.text[#item.text] or "", "%*$") then - return; - end - - utils.set_extmark(buffer, asciidoc_inline.ns, range.row_end, range.col_end - 1, { + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_end, range.col_end - #(item.delimiters[2] or ""), { end_col = range.col_end, conceal = "", }); From aecd13821effb3328ebd853336ecb8cfcec837a7 Mon Sep 17 00:00:00 2001 From: Shawon Date: Tue, 13 Jan 2026 23:36:15 +0600 Subject: [PATCH 09/40] doc(asciidoc_inline): Added missing types for bold & italic --- lua/markview/config/asciidoc_inline.lua | 2 +- .../types/parsers/asciidoc_inline.lua | 31 +++++++++++++++++++ .../types/renderers/asciidoc_inline.lua | 18 +++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 lua/markview/types/parsers/asciidoc_inline.lua create mode 100644 lua/markview/types/renderers/asciidoc_inline.lua diff --git a/lua/markview/config/asciidoc_inline.lua b/lua/markview/config/asciidoc_inline.lua index 35be288b..c979f09b 100644 --- a/lua/markview/config/asciidoc_inline.lua +++ b/lua/markview/config/asciidoc_inline.lua @@ -1,4 +1,4 @@ ----@type markview.config.asciidoc +---@type markview.config.asciidoc_inline return { bolds = { enable = true }, italics = { enable = true }, diff --git a/lua/markview/types/parsers/asciidoc_inline.lua b/lua/markview/types/parsers/asciidoc_inline.lua new file mode 100644 index 00000000..1a342d17 --- /dev/null +++ b/lua/markview/types/parsers/asciidoc_inline.lua @@ -0,0 +1,31 @@ +---@meta + + +---@class markview.parsed.asciidoc_inline.bolds +--- +---@field class "asciidoc_inline_bold" +---@field delimiters [ string?, string? ] Delimiters +--- +---@field text string[] +---@field range markview.parsed.range + + +---@class markview.parsed.asciidoc_inline.italics +--- +---@field class "asciidoc_inline_italic" +---@field delimiters [ string?, string? ] Delimiters +--- +---@field text string[] +---@field range markview.parsed.range + + +---@alias markview.parsed.asciidoc_inline +---| markview.parsed.asciidoc_inline.bolds +---| markview.parsed.asciidoc_inline.italics + + +---@class markview.parsed.asciidoc_inline_sorted +--- +---@field bolds markview.parsed.asciidoc_inline.bolds +---@field italics markview.parsed.asciidoc_inline.italics + diff --git a/lua/markview/types/renderers/asciidoc_inline.lua b/lua/markview/types/renderers/asciidoc_inline.lua new file mode 100644 index 00000000..402cf8fc --- /dev/null +++ b/lua/markview/types/renderers/asciidoc_inline.lua @@ -0,0 +1,18 @@ +---@meta + + +---@class markview.config.asciidoc_inline.bolds +--- +---@field enable boolean + + +---@class markview.config.asciidoc_inline.italics +--- +---@field enable boolean + + +---@class markview.config.asciidoc_inline +--- +---@field bolds markview.config.asciidoc_inline.bolds +---@field italics markview.config.asciidoc_inline.italics + From c81472c16e2ac93d14517d59b9da386adafd4a15 Mon Sep 17 00:00:00 2001 From: Shawon Date: Wed, 14 Jan 2026 00:46:34 +0600 Subject: [PATCH 10/40] fix(parsers, asciidoc_inline): Reduce unnecessary recursion when parsing Fixes duplication of rendered output. --- lua/markview/parser.lua | 12 ++++++++++++ lua/markview/parsers/asciidoc_inline.lua | 15 ++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/lua/markview/parser.lua b/lua/markview/parser.lua index d9e75a12..3a42be88 100644 --- a/lua/markview/parser.lua +++ b/lua/markview/parser.lua @@ -128,6 +128,18 @@ parser.init = function (buffer, from, to, cache) return parser.content, parser.sorted; end + --[[ + WARN: Recursion when parsing `asciidoc_inline` trees + + `cathaysia/tree-sitter-asciidoc` uses `#injection.include-children` for it's inline parser. + This causes the same text to be parsed multiple times, + + FIX(asciidoc_inline): Check parse range + + Check if a parser range has been parsed before. If it has, do not parse again. + ]] + _parsers.asciidoc_inline.parsed_ranges = {}; + ---|fS "chore: Announce start of parsing" ---@type integer Start time local start = vim.uv.hrtime(); diff --git a/lua/markview/parsers/asciidoc_inline.lua b/lua/markview/parsers/asciidoc_inline.lua index 77e6c833..a5fd29be 100644 --- a/lua/markview/parsers/asciidoc_inline.lua +++ b/lua/markview/parsers/asciidoc_inline.lua @@ -1,6 +1,8 @@ --- HTML parser for `markview.nvim`. local asciidoc_inline = {}; +asciidoc_inline.parsed_ranges = {}; + --- Queried contents ---@type table[] asciidoc_inline.content = {}; @@ -75,7 +77,7 @@ end --- HTML parser ---@param buffer integer ----@param TSTree table +---@param TSTree TSTree ---@param from integer? ---@param to integer? ---@return markview.parsed.asciidoc_inline[] @@ -85,6 +87,17 @@ asciidoc_inline.parse = function (buffer, TSTree, from, to) asciidoc_inline.sorted = {}; asciidoc_inline.content = {}; + local root = TSTree:root(); + local r_range = { root:range() }; + + for _, entry in ipairs(asciidoc_inline.parsed_ranges) do + if vim.deep_equal(entry, r_range) then + return asciidoc_inline.content, asciidoc_inline.sorted; + end + end + + table.insert(asciidoc_inline.parsed_ranges, r_range); + local scanned_queries = vim.treesitter.query.parse("asciidoc_inline", [[ (emphasis) @asciidoc_inline.bold (ltalic) @asciidoc_inline.italic From a324d09c407232ed15c9b59e73293ae154efcdd4 Mon Sep 17 00:00:00 2001 From: Shawon Date: Wed, 14 Jan 2026 00:51:02 +0600 Subject: [PATCH 11/40] feat(asciidoc_inline): Added `monospce`s support --- lua/markview/config/asciidoc_inline.lua | 8 +++ lua/markview/parsers/asciidoc_inline.lua | 27 +++++++++ lua/markview/renderers/asciidoc_inline.lua | 55 ++++++++++++++++--- .../types/parsers/asciidoc_inline.lua | 11 ++++ .../types/renderers/asciidoc_inline.lua | 4 ++ 5 files changed, 96 insertions(+), 9 deletions(-) diff --git a/lua/markview/config/asciidoc_inline.lua b/lua/markview/config/asciidoc_inline.lua index c979f09b..1a09ca00 100644 --- a/lua/markview/config/asciidoc_inline.lua +++ b/lua/markview/config/asciidoc_inline.lua @@ -2,4 +2,12 @@ return { bolds = { enable = true }, italics = { enable = true }, + + monospaces = { + enable = true, + hl = "MarkviewInlineCode", + + padding_left = " ", + padding_right = " " + }, }; diff --git a/lua/markview/parsers/asciidoc_inline.lua b/lua/markview/parsers/asciidoc_inline.lua index a5fd29be..e0c436f2 100644 --- a/lua/markview/parsers/asciidoc_inline.lua +++ b/lua/markview/parsers/asciidoc_inline.lua @@ -75,6 +75,32 @@ asciidoc_inline.italic = function (buffer, TSNode, text, range) }); end +---@param buffer integer +---@param TSNode TSNode +---@param text string[] +---@param range markview.parsed.range +asciidoc_inline.monospace = function (buffer, TSNode, text, range) + local delimiters = {}; + + for child in TSNode:iter_children() do + if child:named() == false then + if delimiters[1] then + delimiters[2] = vim.treesitter.get_node_text(child, buffer, {}); + else + delimiters[1] = vim.treesitter.get_node_text(child, buffer, {}); + end + end + end + + asciidoc_inline.insert({ + class = "asciidoc_inline_monospace", + delimiters = delimiters, + + text = text, + range = range + }); +end + --- HTML parser ---@param buffer integer ---@param TSTree TSTree @@ -101,6 +127,7 @@ asciidoc_inline.parse = function (buffer, TSTree, from, to) local scanned_queries = vim.treesitter.query.parse("asciidoc_inline", [[ (emphasis) @asciidoc_inline.bold (ltalic) @asciidoc_inline.italic + (monospace) @asciidoc_inline.monospace ]]); for capture_id, capture_node, _, _ in scanned_queries:iter_captures(TSTree:root(), buffer, from, to) do diff --git a/lua/markview/renderers/asciidoc_inline.lua b/lua/markview/renderers/asciidoc_inline.lua index 10da342a..ddec2919 100644 --- a/lua/markview/renderers/asciidoc_inline.lua +++ b/lua/markview/renderers/asciidoc_inline.lua @@ -51,18 +51,55 @@ asciidoc_inline.italic = function (buffer, item) }); end ---- Renders HTML elements ---@param buffer integer ----@param content markview.parsed.asciidoc_inline[] -asciidoc_inline.render = function (buffer, content) - asciidoc_inline.cache = { - font_regions = {}, - style_regions = { - superscripts = {}, - subscripts = {} +---@param item markview.parsed.asciidoc_inline.monospaces +asciidoc_inline.monospace = function (buffer, item) + ---@type markview.config.asciidoc_inline.monospaces? + local config = spec.get({ "asciidoc_inline", "monospaces" }, { eval_args = { buffer, item } }); + + if not config then + return; + end + + local range = item.range; + + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_start, range.col_start, { + end_col = range.col_start + #(item.delimiters[1] or ""), + conceal = "", + + virt_text_pos = "inline", + virt_text = { + { config.corner_left or "", utils.set_hl(config.corner_left_hl or config.hl) }, + { config.padding_left or "", utils.set_hl(config.padding_left_hl or config.hl) } }, - }; + hl_mode = "combine" + }); + + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_start, range.col_start, { + end_col = range.col_end, end_row = range.row_end, + + hl_group = utils.set_hl(config.hl), + hl_mode = "combine" + }); + + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_end, range.col_end - #(item.delimiters[2] or ""), { + end_col = range.col_end, + conceal = "", + + virt_text_pos = "inline", + virt_text = { + { config.corner_right or "", utils.set_hl(config.corner_right_hl or config.hl) }, + { config.padding_right or "", utils.set_hl(config.padding_right_hl or config.hl) } + }, + + hl_mode = "combine" + }); +end + +---@param buffer integer +---@param content markview.parsed.asciidoc_inline[] +asciidoc_inline.render = function (buffer, content) local custom = spec.get({ "renderers" }, { fallback = {} }); for _, item in ipairs(content or {}) do diff --git a/lua/markview/types/parsers/asciidoc_inline.lua b/lua/markview/types/parsers/asciidoc_inline.lua index 1a342d17..bfa73969 100644 --- a/lua/markview/types/parsers/asciidoc_inline.lua +++ b/lua/markview/types/parsers/asciidoc_inline.lua @@ -19,13 +19,24 @@ ---@field range markview.parsed.range +---@class markview.parsed.asciidoc_inline.monospaces +--- +---@field class "asciidoc_inline_monospace" +---@field delimiters [ string?, string? ] Delimiters +--- +---@field text string[] +---@field range markview.parsed.range + + ---@alias markview.parsed.asciidoc_inline ---| markview.parsed.asciidoc_inline.bolds ---| markview.parsed.asciidoc_inline.italics +---| markview.parsed.asciidoc_inline.monospaces ---@class markview.parsed.asciidoc_inline_sorted --- ---@field bolds markview.parsed.asciidoc_inline.bolds ---@field italics markview.parsed.asciidoc_inline.italics +---@field monospaces markview.parsed.asciidoc_inline.monospaces diff --git a/lua/markview/types/renderers/asciidoc_inline.lua b/lua/markview/types/renderers/asciidoc_inline.lua index 402cf8fc..7b108f4f 100644 --- a/lua/markview/types/renderers/asciidoc_inline.lua +++ b/lua/markview/types/renderers/asciidoc_inline.lua @@ -11,8 +11,12 @@ ---@field enable boolean +---@alias markview.config.asciidoc_inline.monospaces markview.config.__inline + + ---@class markview.config.asciidoc_inline --- ---@field bolds markview.config.asciidoc_inline.bolds ---@field italics markview.config.asciidoc_inline.italics +---@field monospaces markview.config.asciidoc_inline.monospaces From 32c0ddd390ed3111b2ee18f38d3fc416ab9915db Mon Sep 17 00:00:00 2001 From: Shawon Date: Wed, 14 Jan 2026 20:32:40 +0600 Subject: [PATCH 12/40] feat(asciidoc_inline): Added `#highlight#` support --- lua/markview/config/asciidoc_inline.lua | 11 ++++ lua/markview/parsers/asciidoc_inline.lua | 27 +++++++++ lua/markview/renderers/asciidoc_inline.lua | 58 +++++++++++++++++++ .../types/parsers/asciidoc_inline.lua | 11 ++++ .../types/renderers/asciidoc_inline.lua | 15 +++++ 5 files changed, 122 insertions(+) diff --git a/lua/markview/config/asciidoc_inline.lua b/lua/markview/config/asciidoc_inline.lua index 1a09ca00..4796a316 100644 --- a/lua/markview/config/asciidoc_inline.lua +++ b/lua/markview/config/asciidoc_inline.lua @@ -10,4 +10,15 @@ return { padding_left = " ", padding_right = " " }, + + highlights = { + enable = true, + + default = { + padding_left = " ", + padding_right = " ", + + hl = "MarkviewPalette3" + } + }, }; diff --git a/lua/markview/parsers/asciidoc_inline.lua b/lua/markview/parsers/asciidoc_inline.lua index e0c436f2..3b90f4a5 100644 --- a/lua/markview/parsers/asciidoc_inline.lua +++ b/lua/markview/parsers/asciidoc_inline.lua @@ -49,6 +49,32 @@ asciidoc_inline.bold = function (buffer, TSNode, text, range) }); end +---@param buffer integer +---@param TSNode TSNode +---@param text string[] +---@param range markview.parsed.range +asciidoc_inline.highlight = function (buffer, TSNode, text, range) + local delimiters = {}; + + for child in TSNode:iter_children() do + if child:named() == false then + if delimiters[1] then + delimiters[2] = vim.treesitter.get_node_text(child, buffer, {}); + else + delimiters[1] = vim.treesitter.get_node_text(child, buffer, {}); + end + end + end + + asciidoc_inline.insert({ + class = "asciidoc_inline_highlight", + delimiters = delimiters, + + text = text, + range = range + }); +end + ---@param buffer integer ---@param TSNode TSNode ---@param text string[] @@ -128,6 +154,7 @@ asciidoc_inline.parse = function (buffer, TSTree, from, to) (emphasis) @asciidoc_inline.bold (ltalic) @asciidoc_inline.italic (monospace) @asciidoc_inline.monospace + (highlight) @asciidoc_inline.highlight ]]); for capture_id, capture_node, _, _ in scanned_queries:iter_captures(TSTree:root(), buffer, from, to) do diff --git a/lua/markview/renderers/asciidoc_inline.lua b/lua/markview/renderers/asciidoc_inline.lua index ddec2919..f05eab9f 100644 --- a/lua/markview/renderers/asciidoc_inline.lua +++ b/lua/markview/renderers/asciidoc_inline.lua @@ -28,6 +28,64 @@ asciidoc_inline.bold = function (buffer, item) }); end +---@param buffer integer +---@param item markview.parsed.asciidoc_inline.highlights +asciidoc_inline.highlight = function (buffer, item) + ---@type markview.config.asciidoc_inline.highlights? + local main_config = spec.get({ "asciidoc_inline", "highlights" }, { fallback = nil }); + local range = item.range; + + if not main_config then + return; + end + + ---@type markview.config.__inline? + local config = utils.match( + main_config, + table.concat(item.text, "\n"), + { + eval_args = { buffer, item } + } + ); + + if config == nil then + return; + end + + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_start, range.col_start, { + end_col = range.col_start + #(item.delimiters[1] or ""), + conceal = "", + + virt_text_pos = "inline", + virt_text = { + { config.corner_left or "", utils.set_hl(config.corner_left_hl or config.hl) }, + { config.padding_left or "", utils.set_hl(config.padding_left_hl or config.hl) } + }, + + hl_mode = "combine" + }); + + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_start, range.col_start, { + end_col = range.col_end, end_row = range.row_end, + + hl_group = utils.set_hl(config.hl), + hl_mode = "combine" + }); + + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_end, range.col_end - #(item.delimiters[2] or ""), { + end_col = range.col_end, + conceal = "", + + virt_text_pos = "inline", + virt_text = { + { config.corner_right or "", utils.set_hl(config.corner_right_hl or config.hl) }, + { config.padding_right or "", utils.set_hl(config.padding_right_hl or config.hl) } + }, + + hl_mode = "combine" + }); +end + ---@param buffer integer ---@param item markview.parsed.asciidoc_inline.italics asciidoc_inline.italic = function (buffer, item) diff --git a/lua/markview/types/parsers/asciidoc_inline.lua b/lua/markview/types/parsers/asciidoc_inline.lua index bfa73969..b7bf23ed 100644 --- a/lua/markview/types/parsers/asciidoc_inline.lua +++ b/lua/markview/types/parsers/asciidoc_inline.lua @@ -10,6 +10,15 @@ ---@field range markview.parsed.range +---@class markview.parsed.asciidoc_inline.highlights +--- +---@field class "asciidoc_inline_highlight" +---@field delimiters [ string?, string? ] Delimiters +--- +---@field text string[] +---@field range markview.parsed.range + + ---@class markview.parsed.asciidoc_inline.italics --- ---@field class "asciidoc_inline_italic" @@ -30,6 +39,7 @@ ---@alias markview.parsed.asciidoc_inline ---| markview.parsed.asciidoc_inline.bolds +---| markview.parsed.asciidoc_inline.highlights ---| markview.parsed.asciidoc_inline.italics ---| markview.parsed.asciidoc_inline.monospaces @@ -39,4 +49,5 @@ ---@field bolds markview.parsed.asciidoc_inline.bolds ---@field italics markview.parsed.asciidoc_inline.italics ---@field monospaces markview.parsed.asciidoc_inline.monospaces +---@field highlights markview.parsed.asciidoc_inline.highlights diff --git a/lua/markview/types/renderers/asciidoc_inline.lua b/lua/markview/types/renderers/asciidoc_inline.lua index 7b108f4f..6160635b 100644 --- a/lua/markview/types/renderers/asciidoc_inline.lua +++ b/lua/markview/types/renderers/asciidoc_inline.lua @@ -5,6 +5,20 @@ --- ---@field enable boolean +------------------------------------------------------------------------------ + +--- Configuration for Obsidian-style highlighted texts. +---@class markview.config.asciidoc_inline.highlights +--- +---@field enable boolean Enable rendering of highlighted text. +--- +---@field default markview.config.asciidoc_inline.highlights.opts Default configuration for highlighted text. +---@field [string] markview.config.asciidoc_inline.highlights.opts Configuration for highlighted text that matches `string`. + + +--[[ Options for a specific footnote type. ]] +---@alias markview.config.asciidoc_inline.highlights.opts markview.config.__inline + ---@class markview.config.asciidoc_inline.italics --- @@ -17,6 +31,7 @@ ---@class markview.config.asciidoc_inline --- ---@field bolds markview.config.asciidoc_inline.bolds +---@field highlights markview.config.asciidoc_inline.highlights Highlighted text configuration. ---@field italics markview.config.asciidoc_inline.italics ---@field monospaces markview.config.asciidoc_inline.monospaces From cead72e29b26c2502e4d3074bec9c7ef977189d4 Mon Sep 17 00:00:00 2001 From: Shawon Date: Thu, 15 Jan 2026 00:26:47 +0600 Subject: [PATCH 13/40] fix(renderers, asciidoc_inline): Fixed bug with icon not showing --- lua/markview/renderers/asciidoc_inline.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lua/markview/renderers/asciidoc_inline.lua b/lua/markview/renderers/asciidoc_inline.lua index f05eab9f..75f757ae 100644 --- a/lua/markview/renderers/asciidoc_inline.lua +++ b/lua/markview/renderers/asciidoc_inline.lua @@ -59,7 +59,8 @@ asciidoc_inline.highlight = function (buffer, item) virt_text_pos = "inline", virt_text = { { config.corner_left or "", utils.set_hl(config.corner_left_hl or config.hl) }, - { config.padding_left or "", utils.set_hl(config.padding_left_hl or config.hl) } + { config.padding_left or "", utils.set_hl(config.padding_left_hl or config.hl) }, + { config.icon or "", utils.set_hl(config.icon_hl or config.hl) } }, hl_mode = "combine" @@ -128,7 +129,8 @@ asciidoc_inline.monospace = function (buffer, item) virt_text_pos = "inline", virt_text = { { config.corner_left or "", utils.set_hl(config.corner_left_hl or config.hl) }, - { config.padding_left or "", utils.set_hl(config.padding_left_hl or config.hl) } + { config.padding_left or "", utils.set_hl(config.padding_left_hl or config.hl) }, + { config.icon or "", utils.set_hl(config.icon_hl or config.hl) } }, hl_mode = "combine" From 0c463e307cedb27b7135f33db200d941a8d3d26c Mon Sep 17 00:00:00 2001 From: Shawon Date: Thu, 15 Jan 2026 00:36:10 +0600 Subject: [PATCH 14/40] feat(asciidoc_inline): Added support for unlabeled URIs --- lua/markview/config/asciidoc_inline.lua | 320 ++++++++++++++++++ lua/markview/parsers/asciidoc_inline.lua | 36 ++ lua/markview/renderers/asciidoc_inline.lua | 71 ++++ .../types/parsers/asciidoc_inline.lua | 10 + .../types/renderers/asciidoc_inline.lua | 40 +++ test/asciidoc.adoc | 10 + 6 files changed, 487 insertions(+) diff --git a/lua/markview/config/asciidoc_inline.lua b/lua/markview/config/asciidoc_inline.lua index 4796a316..c4ca688c 100644 --- a/lua/markview/config/asciidoc_inline.lua +++ b/lua/markview/config/asciidoc_inline.lua @@ -1,3 +1,11 @@ +local function normalize_str(str) + if type(str) ~= "string" then + return ""; + end + + return string.lower(str):gsub("^%l", string.upper); +end + ---@type markview.config.asciidoc_inline return { bolds = { enable = true }, @@ -21,4 +29,316 @@ return { hl = "MarkviewPalette3" } }, + + + uris = { + enable = true, + + default = { + icon = "󰌷 ", + hl = "MarkviewHyperlink", + }, + + ---|fS + + --NOTE(@OXY2DEV): Github sites. + + ["github%.com/[%a%d%-%_%.]+%/?$"] = { + --- github.com/ + icon = " ", + hl = "MarkviewPalette0Fg", + + text = function (_, item) + return string.match(item.destination, "github%.com/([%a%d%-%_%.]+)%/?$"); + end + }, + ["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+%/?$"] = { + --- github.com// + icon = "󰳐 ", + hl = "MarkviewPalette0Fg", + + text = function (_, item) + return string.match(item.destination, "github%.com/([%a%d%-%_%.]+/[%a%d%-%_%.]+)%/?$"); + end + }, + ["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+/tree/[%a%d%-%_%.]+%/?$"] = { + --- github.com///tree/ + icon = " ", + hl = "MarkviewPalette0Fg", + + text = function (_, item) + local repo, branch = string.match(item.destination, "github%.com/([%a%d%-%_%.]+/[%a%d%-%_%.]+)/tree/([%a%d%-%_%.]+)%/?$"); + return repo .. " at " .. branch; + end + }, + ["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+/commits/[%a%d%-%_%.]+%/?$"] = { + --- github.com///commits/ + icon = " ", + hl = "MarkviewPalette0Fg", + + text = function (_, item) + return string.match(item.destination, "github%.com/([%a%d%-%_%.]+/[%a%d%-%_%.]+/commits/[%a%d%-%_%.]+)%/?$"); + end + }, + + ["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+%/releases$"] = { + --- github.com///releases + icon = " ", + hl = "MarkviewPalette0Fg", + + text = function (_, item) + return "Releases • " .. string.match(item.destination, "github%.com/([%a%d%-%_%.]+/[%a%d%-%_%.]+)%/releases$"); + end + }, + ["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+%/tags$"] = { + --- github.com///tags + icon = " ", + hl = "MarkviewPalette0Fg", + + text = function (_, item) + return "Tags • " .. string.match(item.destination, "github%.com/([%a%d%-%_%.]+/[%a%d%-%_%.]+)%/tags$"); + end + }, + ["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+%/issues$"] = { + --- github.com///issues + icon = " ", + hl = "MarkviewPalette0Fg", + + text = function (_, item) + return "Issues • " .. string.match(item.destination, "github%.com/([%a%d%-%_%.]+/[%a%d%-%_%.]+)%/issues$"); + end + }, + ["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+%/pulls$"] = { + --- github.com///pulls + icon = " ", + hl = "MarkviewPalette0Fg", + + text = function (_, item) + return "Pull requests • " .. string.match(item.destination, "github%.com/([%a%d%-%_%.]+/[%a%d%-%_%.]+)%/pulls$"); + end + }, + + ["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+%/wiki$"] = { + --- github.com///wiki + icon = " ", + hl = "MarkviewPalette0Fg", + + text = function (_, item) + return "Wiki • " .. string.match(item.destination, "github%.com/([%a%d%-%_%.]+/[%a%d%-%_%.]+)%/wiki$"); + end + }, + + --- NOTE(@OXY2DEV): Commonly used sites by programmers. + + ["developer%.mozilla%.org"] = { + priority = -9999, + + icon = "󰖟 ", + hl = "MarkviewPalette5Fg" + }, + + ["w3schools%.com"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette4Fg" + }, + + ["stackoverflow%.com"] = { + priority = -9999, + + icon = "󰓌 ", + hl = "MarkviewPalette2Fg" + }, + + ["reddit%.com"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette2Fg" + }, + + ["github%.com"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette6Fg" + }, + + ["gitlab%.com"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette2Fg" + }, + + ["dev%.to"] = { + priority = -9999, + + icon = "󱁴 ", + hl = "MarkviewPalette0Fg" + }, + + ["codepen%.io"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette6Fg" + }, + + ["replit%.com"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette2Fg" + }, + + ["jsfiddle%.net"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette5Fg" + }, + + ["npmjs%.com"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette0Fg" + }, + + ["pypi%.org"] = { + priority = -9999, + + icon = "󰆦 ", + hl = "MarkviewPalette0Fg" + }, + + ["mvnrepository%.com"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette1Fg" + }, + + ["medium%.com"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette6Fg" + }, + + ["linkedin%.com"] = { + priority = -9999, + + icon = "󰌻 ", + hl = "MarkviewPalette5Fg" + }, + + ["news%.ycombinator%.com"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette2Fg" + }, + + ["neovim%.io/doc/user/.*#%_?.*$"] = { + icon = " ", + hl = "MarkviewPalette4Fg", + + text = function (_, item) + local file, tag = string.match(item.destination, "neovim%.io/doc/user/(.*)#%_?(.*)$"); + --- The actual website seems to show + --- _ in the site name so, we won't + --- be replacing `_`s with ` `s. + file = string.gsub(file, "%.html$", ""); + + return string.format("%s(%s) - Neovim docs", normalize_str(file), tag); + end + }, + ["neovim%.io/doc/user/.*$"] = { + icon = " ", + hl = "MarkviewPalette4Fg", + + text = function (_, item) + local file = string.match(item.destination, "neovim%.io/doc/user/(.*)$"); + file = string.gsub(file, "%.html$", ""); + + return string.format("%s - Neovim docs", normalize_str(file)); + end + }, + + ["github%.com/vim/vim"] = { + priority = -100, + + icon = " ", + hl = "MarkviewPalette4Fg", + }, + + ["github%.com/neovim/neovim"] = { + priority = -100, + + icon = " ", + hl = "MarkviewPalette4Fg", + }, + + ["vim%.org"] = { + icon = " ", + hl = "MarkviewPalette4Fg", + }, + + ["luals%.github%.io/wiki/?.*$"] = { + icon = " ", + hl = "MarkviewPalette5Fg", + + text = function (_, item) + if string.match(item.destination, "luals%.github%.io/wiki/(.-)/#(.+)$") then + local page_mappings = { + annotations = { + ["as"] = "@as", + ["alias"] = "@alias", + ["async"] = "@async", + ["cast"] = "@cast", + ["class"] = "@class", + ["deprecated"] = "@deprecated", + ["diagnostic"] = "@diagnostic", + ["enum"] = "@enum", + ["field"] = "@field", + ["generic"] = "@generic", + ["meta"] = "@meta", + ["module"] = "@module", + ["nodiscard"] = "@nodiscard", + ["operator"] = "@operator", + ["overload"] = "@overload", + ["package"] = "@package", + ["param"] = "@param", + ["see"] = "@see", + ["source"] = "@source", + ["type"] = "@type", + ["vaarg"] = "@vaarg", + ["version"] = "@version" + } + }; + + local page, section = string.match(item.destination, "luals%.github%.io/wiki/(.-)/#(.+)$"); + + if page_mappings[page] and page_mappings[page][section] then + section = page_mappings[page][section]; + else + section = normalize_str(string.gsub(section, "%-", " ")); + end + + return string.format("%s(%s) | Lua Language Server", normalize_str(page), section); + elseif string.match(item.destination, "") then + local page = string.match(item.destination, "luals%.github%.io/wiki/(.-)/?$"); + + return string.format("%s | Lua Language Server", normalize_str(page)); + else + return item.destination; + end + end + }, + + ---|fE + }, }; diff --git a/lua/markview/parsers/asciidoc_inline.lua b/lua/markview/parsers/asciidoc_inline.lua index 3b90f4a5..8c35345e 100644 --- a/lua/markview/parsers/asciidoc_inline.lua +++ b/lua/markview/parsers/asciidoc_inline.lua @@ -127,6 +127,36 @@ asciidoc_inline.monospace = function (buffer, TSNode, text, range) }); end +---@param buffer integer +---@param TSNode TSNode +---@param text string[] +---@param range markview.parsed.range +asciidoc_inline.uri = function (buffer, TSNode, text, range) + local delimiters = {}; + local destination; + + for child in TSNode:iter_children() do + if child:named() == false then + if delimiters[1] then + delimiters[2] = vim.treesitter.get_node_text(child, buffer, {}); + else + delimiters[1] = vim.treesitter.get_node_text(child, buffer, {}); + end + else + destination = vim.treesitter.get_node_text(child, buffer, {}); + end + end + + asciidoc_inline.insert({ + class = "asciidoc_inline_uri", + delimiters = delimiters, + destination = destination, + + text = text, + range = range + }); +end + --- HTML parser ---@param buffer integer ---@param TSTree TSTree @@ -155,6 +185,12 @@ asciidoc_inline.parse = function (buffer, TSTree, from, to) (ltalic) @asciidoc_inline.italic (monospace) @asciidoc_inline.monospace (highlight) @asciidoc_inline.highlight + + (autolink + (uri)) @asciidoc_inline.uri + ; + ; (autolink + ; (labeled_uri)) @asciidoc_inline.labeled_uri ]]); for capture_id, capture_node, _, _ in scanned_queries:iter_captures(TSTree:root(), buffer, from, to) do diff --git a/lua/markview/renderers/asciidoc_inline.lua b/lua/markview/renderers/asciidoc_inline.lua index 75f757ae..9bfc8135 100644 --- a/lua/markview/renderers/asciidoc_inline.lua +++ b/lua/markview/renderers/asciidoc_inline.lua @@ -157,6 +157,77 @@ asciidoc_inline.monospace = function (buffer, item) }); end +---@param buffer integer +---@param item markview.parsed.asciidoc_inline.uris +asciidoc_inline.uri = function (buffer, item) + ---@type markview.config.asciidoc_inline.uris? + local main_config = spec.get({ "asciidoc_inline", "uris" }, { fallback = nil }); + local range = item.range; + + if not main_config then + return; + end + + ---@type markview.config.asciidoc_inline.uris.opts? + local config = utils.match( + main_config, + item.destination or "", + { + eval_args = { buffer, item } + } + ); + + if config == nil then + return; + end + + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_start, range.col_start, { + end_col = range.col_start + #(item.delimiters[1] or ""), + conceal = "", + + virt_text_pos = "inline", + virt_text = { + { config.corner_left or "", utils.set_hl(config.corner_left_hl or config.hl) }, + { config.padding_left or "", utils.set_hl(config.padding_left_hl or config.hl) }, + { config.icon or "", utils.set_hl(config.icon_hl or config.hl) } + }, + + hl_mode = "combine" + }); + + if config.text then + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_start, range.col_start + #(item.delimiters[1] or ""), { + end_col = range.col_end - #(item.delimiters[2] or ""), end_row = range.row_end, + + virt_text = { + { config.text or "", utils.set_hl(config.text_hl or config.hl) } + }, + + hl_mode = "combine" + }); + else + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_start, range.col_start, { + end_col = range.col_end, end_row = range.row_end, + + hl_group = utils.set_hl(config.hl), + hl_mode = "combine" + }); + end + + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_end, range.col_end - #(item.delimiters[2] or ""), { + end_col = range.col_end, + conceal = "", + + virt_text_pos = "inline", + virt_text = { + { config.corner_right or "", utils.set_hl(config.corner_right_hl or config.hl) }, + { config.padding_right or "", utils.set_hl(config.padding_right_hl or config.hl) } + }, + + hl_mode = "combine" + }); +end + ---@param buffer integer ---@param content markview.parsed.asciidoc_inline[] asciidoc_inline.render = function (buffer, content) diff --git a/lua/markview/types/parsers/asciidoc_inline.lua b/lua/markview/types/parsers/asciidoc_inline.lua index b7bf23ed..396847bc 100644 --- a/lua/markview/types/parsers/asciidoc_inline.lua +++ b/lua/markview/types/parsers/asciidoc_inline.lua @@ -37,6 +37,16 @@ ---@field range markview.parsed.range +---@class markview.parsed.asciidoc_inline.uris +--- +---@field class "asciidoc_inline_uri" +---@field delimiters [ string?, string? ] Delimiters +---@field destination string URL the node is pointing to. +--- +---@field text string[] +---@field range markview.parsed.range + + ---@alias markview.parsed.asciidoc_inline ---| markview.parsed.asciidoc_inline.bolds ---| markview.parsed.asciidoc_inline.highlights diff --git a/lua/markview/types/renderers/asciidoc_inline.lua b/lua/markview/types/renderers/asciidoc_inline.lua index 6160635b..f761df84 100644 --- a/lua/markview/types/renderers/asciidoc_inline.lua +++ b/lua/markview/types/renderers/asciidoc_inline.lua @@ -15,23 +15,63 @@ ---@field default markview.config.asciidoc_inline.highlights.opts Default configuration for highlighted text. ---@field [string] markview.config.asciidoc_inline.highlights.opts Configuration for highlighted text that matches `string`. +------------------------------------------------------------------------------ --[[ Options for a specific footnote type. ]] ---@alias markview.config.asciidoc_inline.highlights.opts markview.config.__inline +------------------------------------------------------------------------------ ---@class markview.config.asciidoc_inline.italics --- ---@field enable boolean +------------------------------------------------------------------------------ ---@alias markview.config.asciidoc_inline.monospaces markview.config.__inline +------------------------------------------------------------------------------ + +---@class markview.config.asciidoc_inline.uris +--- +---@field enable boolean Enable rendering of unlabeled URIs. +--- +---@field default markview.config.asciidoc_inline.uris.opts Default configuration for URIs. +---@field [string] markview.config.asciidoc_inline.uris.opts Configuration for URIs that matches `string`. + + +---@class markview.config.asciidoc_inline.uris.opts +--- +---@field corner_left? string Left corner. +---@field corner_left_hl? string Highlight group for the left corner. +--- +---@field padding_left? string Left padding(added after `corner_left`). +---@field padding_left_hl? string Highlight group for the left padding. +--- +---@field icon? string Icon(added after `padding_left`). +---@field icon_hl? string Highlight group for the icon. +--- +--[[ Text to show instead of the `URL`.]] +---@field text? +---| string +---| function(buffer: integer, item: markview.parsed.asciidoc_inline.uris): string +---@field text_hl? string Highlight group for the text. +--- +---@field hl? string Default highlight group(used by `*_hl` options when they are not set). +--- +---@field padding_right? string Right padding. +---@field padding_right_hl? string Highlight group for the right padding. +--- +---@field corner_right? string Right corner(added after `padding_right`). +---@field corner_right_hl? string Highlight group for the right corner. + +------------------------------------------------------------------------------ ---@class markview.config.asciidoc_inline --- ---@field bolds markview.config.asciidoc_inline.bolds ---@field highlights markview.config.asciidoc_inline.highlights Highlighted text configuration. ---@field italics markview.config.asciidoc_inline.italics ---@field monospaces markview.config.asciidoc_inline.monospaces +---@field uris markview.config.asciidoc_inline.uris diff --git a/test/asciidoc.adoc b/test/asciidoc.adoc index 1c5472a2..fdc12742 100644 --- a/test/asciidoc.adoc +++ b/test/asciidoc.adoc @@ -39,3 +39,13 @@ A [.myrole]#custom role# must be fulfilled by the theme. ~sub~script +https://asciidoctor.org - automatic! +"https://asciidoctor.org" - automatic! + +https://asciidoctor.org[Asciidoctor] + +devel@discuss.example.org + +mailto:devel@discuss.example.org[Discuss] + +mailto:join@discuss.example.org[Subscribe,Subscribe me,I want to join!] From b4a4eb2e8f34b375101f3337a64aadef298e6314 Mon Sep 17 00:00:00 2001 From: Shawon Date: Thu, 15 Jan 2026 18:43:46 +0600 Subject: [PATCH 15/40] feat(asciidoc_inline): Added `labeled uri` support --- lua/markview/parsers/asciidoc_inline.lua | 48 +++++++++++++-- lua/markview/renderers/asciidoc_inline.lua | 59 +++++++++++++++++++ .../types/parsers/asciidoc_inline.lua | 25 ++++++++ 3 files changed, 127 insertions(+), 5 deletions(-) diff --git a/lua/markview/parsers/asciidoc_inline.lua b/lua/markview/parsers/asciidoc_inline.lua index 8c35345e..1bd47530 100644 --- a/lua/markview/parsers/asciidoc_inline.lua +++ b/lua/markview/parsers/asciidoc_inline.lua @@ -127,6 +127,30 @@ asciidoc_inline.monospace = function (buffer, TSNode, text, range) }); end +---@param buffer integer +---@param TSNode TSNode +---@param text string[] +---@param range markview.parsed.range +asciidoc_inline.labeled_uri = function (buffer, TSNode, text, range) + local destination; + + for child in TSNode:iter_children() do + if child:type() == "uri_label" then + _, range.label_col_start, _, range.label_col_end = child:range(); + elseif child:type() == "uri" then + destination = vim.treesitter.get_node_text(child, buffer, {}); + end + end + + asciidoc_inline.insert({ + class = "asciidoc_inline_labeled_uri", + destination = destination, + + text = text, + range = range + }); +end + ---@param buffer integer ---@param TSNode TSNode ---@param text string[] @@ -157,7 +181,6 @@ asciidoc_inline.uri = function (buffer, TSNode, text, range) }); end ---- HTML parser ---@param buffer integer ---@param TSTree TSTree ---@param from integer? @@ -180,7 +203,7 @@ asciidoc_inline.parse = function (buffer, TSTree, from, to) table.insert(asciidoc_inline.parsed_ranges, r_range); - local scanned_queries = vim.treesitter.query.parse("asciidoc_inline", [[ + local can_scan, scanned_queries = pcall(vim.treesitter.query.parse, "asciidoc_inline", [[ (emphasis) @asciidoc_inline.bold (ltalic) @asciidoc_inline.italic (monospace) @asciidoc_inline.monospace @@ -188,11 +211,26 @@ asciidoc_inline.parse = function (buffer, TSTree, from, to) (autolink (uri)) @asciidoc_inline.uri - ; - ; (autolink - ; (labeled_uri)) @asciidoc_inline.labeled_uri + + (labled_uri + (uri)) @asciidoc_inline.labeled_uri ]]); + if not can_scan then + require("markview.health").print({ + kind = "ERR", + + from = "parsers/asciidoc_inline.lua", + fn = "parse() -> query", + + message = { + { tostring(error), "DiagnosticError" } + } + }); + + return asciidoc_inline.content, asciidoc_inline.sorted; + end + for capture_id, capture_node, _, _ in scanned_queries:iter_captures(TSTree:root(), buffer, from, to) do local capture_name = scanned_queries.captures[capture_id]; diff --git a/lua/markview/renderers/asciidoc_inline.lua b/lua/markview/renderers/asciidoc_inline.lua index 9bfc8135..8fae022e 100644 --- a/lua/markview/renderers/asciidoc_inline.lua +++ b/lua/markview/renderers/asciidoc_inline.lua @@ -157,6 +157,65 @@ asciidoc_inline.monospace = function (buffer, item) }); end +---@param buffer integer +---@param item markview.parsed.asciidoc_inline.labeled_uris +asciidoc_inline.labeled_uri = function (buffer, item) + ---@type markview.config.asciidoc_inline.uris? + local main_config = spec.get({ "asciidoc_inline", "uris" }, { fallback = nil }); + local range = item.range; + + if not main_config then + return; + end + + ---@type markview.config.asciidoc_inline.uris.opts? + local config = utils.match( + main_config, + item.destination or "", + { + eval_args = { buffer, item } + } + ); + + if config == nil then + return; + end + + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_start, range.col_start, { + end_col = range.label_col_start or range.col_start, + conceal = "", + + virt_text_pos = "inline", + virt_text = { + { config.corner_left or "", utils.set_hl(config.corner_left_hl or config.hl) }, + { config.padding_left or "", utils.set_hl(config.padding_left_hl or config.hl) }, + { config.icon or "", utils.set_hl(config.icon_hl or config.hl) } + }, + + hl_mode = "combine" + }); + + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_start, range.label_col_start or range.col_start, { + end_col = range.label_col_end or range.col_end, end_row = range.row_end, + + hl_group = utils.set_hl(config.hl), + hl_mode = "combine" + }); + + utils.set_extmark(buffer, asciidoc_inline.ns, range.row_end, range.label_col_end or range.col_end, { + end_col = range.col_end, + conceal = "", + + virt_text_pos = "inline", + virt_text = { + { config.corner_right or "", utils.set_hl(config.corner_right_hl or config.hl) }, + { config.padding_right or "", utils.set_hl(config.padding_right_hl or config.hl) } + }, + + hl_mode = "combine" + }); +end + ---@param buffer integer ---@param item markview.parsed.asciidoc_inline.uris asciidoc_inline.uri = function (buffer, item) diff --git a/lua/markview/types/parsers/asciidoc_inline.lua b/lua/markview/types/parsers/asciidoc_inline.lua index 396847bc..02a5d08c 100644 --- a/lua/markview/types/parsers/asciidoc_inline.lua +++ b/lua/markview/types/parsers/asciidoc_inline.lua @@ -37,6 +37,27 @@ ---@field range markview.parsed.range +---@class markview.parsed.asciidoc_inline.labeled_uris +--- +---@field class "asciidoc_inline_labeled_uri" +---@field destination string URL the node is pointing to. +--- +---@field text string[] +---@field range markview.parsed.asciidoc_inline.labeled_uris.range + + +---@class markview.parsed.asciidoc_inline.labeled_uris.range +--- +---@field row_start integer +---@field col_start integer +--- +---@field row_end integer +---@field col_end integer +--- +---@field label_col_start? integer Start column of the **label** of an URI(e.g. `foo` in `https://example.com[foo]`). +---@field label_col_end? integer End column of the **label** of an URI. + + ---@class markview.parsed.asciidoc_inline.uris --- ---@field class "asciidoc_inline_uri" @@ -52,6 +73,8 @@ ---| markview.parsed.asciidoc_inline.highlights ---| markview.parsed.asciidoc_inline.italics ---| markview.parsed.asciidoc_inline.monospaces +---| markview.parsed.asciidoc_inline.labeled_uris +---| markview.parsed.asciidoc_inline.uris ---@class markview.parsed.asciidoc_inline_sorted @@ -60,4 +83,6 @@ ---@field italics markview.parsed.asciidoc_inline.italics ---@field monospaces markview.parsed.asciidoc_inline.monospaces ---@field highlights markview.parsed.asciidoc_inline.highlights +---@field labeled_uris markview.parsed.asciidoc_inline.labeled_uris +---@field uris markview.parsed.asciidoc_inline.uris From f1f11678988fec1ba86122208bb9c2bf75a3fad2 Mon Sep 17 00:00:00 2001 From: Shawon Date: Thu, 15 Jan 2026 20:13:36 +0600 Subject: [PATCH 16/40] feat(asciidoc_inline): Added `uri macro` support --- lua/markview/parsers/asciidoc_inline.lua | 48 ++++++++++++++++++- .../types/parsers/asciidoc_inline.lua | 3 +- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/lua/markview/parsers/asciidoc_inline.lua b/lua/markview/parsers/asciidoc_inline.lua index 1bd47530..18cd7181 100644 --- a/lua/markview/parsers/asciidoc_inline.lua +++ b/lua/markview/parsers/asciidoc_inline.lua @@ -130,7 +130,7 @@ end ---@param buffer integer ---@param TSNode TSNode ---@param text string[] ----@param range markview.parsed.range +---@param range markview.parsed.asciidoc_inline.labeled_uris.range asciidoc_inline.labeled_uri = function (buffer, TSNode, text, range) local destination; @@ -181,6 +181,46 @@ asciidoc_inline.uri = function (buffer, TSNode, text, range) }); end +---@param buffer integer +---@param TSNode TSNode +---@param text string[] +---@param range markview.parsed.asciidoc_inline.labeled_uris.range +asciidoc_inline.uri_macro = function (buffer, TSNode, text, range) + local kind, destination; + + for child in TSNode:iter_children() do + if child:type() == "macro_name" then + kind = vim.treesitter.get_node_text(child, buffer, {}); + elseif child:type() == "target" then + destination = vim.treesitter.get_node_text(child, buffer, {}); + elseif child:type() == "attr" then + local attr = vim.treesitter.get_node_text(child, buffer, {}); + + if string.match(attr, '^".*"$') or not string.match(attr, "[,=]") then + _, range.label_col_start, _, range.label_col_end = child:range(); + else + local R = { child:range() }; + local target = string.match(attr, '^"[^"]*"') or string.match(attr, "^[^,]*") or ""; + target = string.gsub(target, "%s+$", ""); + + local spaces_before = #string.match(target, "^%s*"); + + range.label_col_start = R[2] + spaces_before; + range.label_col_end = R[2] + spaces_before + #target; + end + end + end + + asciidoc_inline.insert({ + class = "asciidoc_inline_labeled_uri", + kind = kind, + destination = destination, + + text = text, + range = range + }); +end + ---@param buffer integer ---@param TSTree TSTree ---@param from integer? @@ -214,6 +254,12 @@ asciidoc_inline.parse = function (buffer, TSTree, from, to) (labled_uri (uri)) @asciidoc_inline.labeled_uri + + (inline_macro + ((macro_name) @uri_macro_name + (#any-of? @uri_macro_name "link" "mailto")) + (target) + (attr)) @asciidoc_inline.uri_macro ]]); if not can_scan then diff --git a/lua/markview/types/parsers/asciidoc_inline.lua b/lua/markview/types/parsers/asciidoc_inline.lua index 02a5d08c..0f0ef309 100644 --- a/lua/markview/types/parsers/asciidoc_inline.lua +++ b/lua/markview/types/parsers/asciidoc_inline.lua @@ -40,6 +40,7 @@ ---@class markview.parsed.asciidoc_inline.labeled_uris --- ---@field class "asciidoc_inline_labeled_uri" +---@field kind? string URI type(e.g. `mailto`). Only if the node is a **macro**. ---@field destination string URL the node is pointing to. --- ---@field text string[] @@ -54,7 +55,7 @@ ---@field row_end integer ---@field col_end integer --- ----@field label_col_start? integer Start column of the **label** of an URI(e.g. `foo` in `https://example.com[foo]`). +---@field label_col_start? integer Start column of the **label** of an URI(e.g. `foo` in `https://example.com[foo]` or `https://example.com[foo,bar]`). ---@field label_col_end? integer End column of the **label** of an URI. From 6e228298c72a3b5f813c342a0f171fe2ac8d4450 Mon Sep 17 00:00:00 2001 From: Shawon Date: Fri, 16 Jan 2026 00:24:16 +0600 Subject: [PATCH 17/40] fix(asciidoc): Fixed incorrect type definitions --- lua/markview/types/parsers/asciidoc.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lua/markview/types/parsers/asciidoc.lua b/lua/markview/types/parsers/asciidoc.lua index f8b80e2d..7e788d97 100644 --- a/lua/markview/types/parsers/asciidoc.lua +++ b/lua/markview/types/parsers/asciidoc.lua @@ -4,7 +4,7 @@ ---@class markview.parsed.asciidoc.document_attributes --- ----@field class_name "asciidoc_document_attribute" +---@field class "asciidoc_document_attribute" --- ---@field text string[] ---@field range markview.parsed.range @@ -13,7 +13,7 @@ ---@class markview.parsed.asciidoc.document_titles --- ----@field class_name "asciidoc_document_title" +---@field class "asciidoc_document_title" --- ---@field text string[] ---@field range markview.parsed.range @@ -21,13 +21,13 @@ ------------------------------------------------------------------------------ ---@alias markview.parsed.asciidoc ----| markview.config.asciidoc.document_attributes ----| markview.config.asciidoc.document_titles +---| markview.parsed.asciidoc.document_attributes +---| markview.parsed.asciidoc.document_titles ------------------------------------------------------------------------------ ---@class markview.parsed.asciidoc_sorted --- ----@field document_attributes markview.config.asciidoc.document_attributes[] ----@field document_titles markview.config.asciidoc.document_titles[] +---@field document_attributes markview.parsed.asciidoc.document_attributes[] +---@field document_titles markview.parsed.asciidoc.document_titles[] From 99f66416f46db565c4061e3141102100c3315aa5 Mon Sep 17 00:00:00 2001 From: Shawon Date: Fri, 16 Jan 2026 00:25:13 +0600 Subject: [PATCH 18/40] feat(asciidoc): Added `section_title` support --- lua/markview/config/asciidoc.lua | 33 +++++++++++++++++ lua/markview/parsers/asciidoc.lua | 45 ++++++++++++++++++++++- lua/markview/renderers/asciidoc.lua | 37 +++++++++++++++++++ lua/markview/types/parsers/asciidoc.lua | 12 ++++++ lua/markview/types/renderers/asciidoc.lua | 25 +++++++++++++ test/asciidoc.adoc | 2 + 6 files changed, 153 insertions(+), 1 deletion(-) diff --git a/lua/markview/config/asciidoc.lua b/lua/markview/config/asciidoc.lua index e75bb724..2717a981 100644 --- a/lua/markview/config/asciidoc.lua +++ b/lua/markview/config/asciidoc.lua @@ -9,6 +9,39 @@ return { icon = "󰛓 ", hl = "MarkviewPalette7", }, + section_titles = { + enable = true, + + title_1 = { + sign = "󰌕 ", sign_hl = "MarkviewHeading1Sign", + + icon = "󰼏 ", hl = "MarkviewHeading1", + }, + title_2 = { + sign = "󰌖 ", sign_hl = "MarkviewHeading2Sign", + + icon = "󰎨 ", hl = "MarkviewHeading2", + }, + title_3 = { + + icon = "󰼑 ", hl = "MarkviewHeading3", + }, + title_4 = { + + icon = "󰎲 ", hl = "MarkviewHeading4", + }, + title_5 = { + + icon = "󰼓 ", hl = "MarkviewHeading5", + }, + title_6 = { + + icon = "󰎴 ", hl = "MarkviewHeading6", + }, + + shift_width = 1, + }, + document_attributes = { enable = true, }, diff --git a/lua/markview/parsers/asciidoc.lua b/lua/markview/parsers/asciidoc.lua index 96e8caed..eb6a05f6 100644 --- a/lua/markview/parsers/asciidoc.lua +++ b/lua/markview/parsers/asciidoc.lua @@ -49,6 +49,26 @@ asciidoc.doc_title = function (_, _, text, range) }); end +---@param buffer integer +---@param TSNode TSNode +---@param text string[] +---@param range markview.parsed.range +asciidoc.section_title = function (buffer, TSNode, text, range) + local marker = TSNode:child(0); + + if not marker then + return; + end + + asciidoc.insert({ + class = "asciidoc_section_title", + marker = vim.treesitter.get_node_text(marker, buffer, {}), + + text = text, + range = range + }); +end + --- HTML parser ---@param buffer integer ---@param TSTree table @@ -61,11 +81,34 @@ asciidoc.parse = function (buffer, TSTree, from, to) asciidoc.sorted = {}; asciidoc.content = {}; - local scanned_queries = vim.treesitter.query.parse("asciidoc", [[ + local can_scan, scanned_queries = pcall(vim.treesitter.query.parse, "asciidoc", [[ (document_title) @asciidoc.doc_title (document_attr) @asciidoc.doc_attr + + [ + (title1) + (title2) + (title3) + (title4) + (title5) + ] @asciidoc.section_title ]]); + if not can_scan then + require("markview.health").print({ + kind = "ERR", + + from = "parsers/asciidoc.lua", + fn = "parse() -> query", + + message = { + { tostring(error), "DiagnosticError" } + } + }); + + return asciidoc.content, asciidoc.sorted; + end + for capture_id, capture_node, _, _ in scanned_queries:iter_captures(TSTree:root(), buffer, from, to) do local capture_name = scanned_queries.captures[capture_id]; diff --git a/lua/markview/renderers/asciidoc.lua b/lua/markview/renderers/asciidoc.lua index b29788c9..71df6308 100644 --- a/lua/markview/renderers/asciidoc.lua +++ b/lua/markview/renderers/asciidoc.lua @@ -50,6 +50,43 @@ asciidoc.document_title = function (buffer, item) }); end +--- Renders atx headings. +---@param buffer integer +---@param item markview.parsed.asciidoc.section_titles +asciidoc.section_title = function (buffer, item) + ---@type markview.config.asciidoc.section_titles? + local main_config = spec.get({ "asciidoc", "section_titles" }, { fallback = nil, eval_args = { buffer, item } }); + + if not main_config then + return; + end + + ---@type markview.config.asciidoc.section_titles.opts? + local config = spec.get({ "title_" .. (#item.marker - 1) }, { source = main_config, eval_args = { buffer, item } }); + + if not config then + return; + end + + local shift_width = spec.get({ "shift_width" }, { source = main_config, fallback = 1, eval_args = { buffer, item } }); + + local range = item.range; + + utils.set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start, { + end_col = range.col_start + #item.marker, + conceal = "", + + sign_text = tostring(config.sign or ""), + sign_hl_group = utils.set_hl(config.sign_hl), + + virt_text = { + { string.rep(" ", (#item.marker - 1) * shift_width) }, + { config.icon, config.icon_hl or config.hl }, + }, + line_hl_group = utils.set_hl(config.hl), + }); +end + ---@param buffer integer ---@param content markview.parsed.asciidoc[] asciidoc.render = function (buffer, content) diff --git a/lua/markview/types/parsers/asciidoc.lua b/lua/markview/types/parsers/asciidoc.lua index 7e788d97..3991e690 100644 --- a/lua/markview/types/parsers/asciidoc.lua +++ b/lua/markview/types/parsers/asciidoc.lua @@ -20,9 +20,20 @@ ------------------------------------------------------------------------------ +---@class markview.parsed.asciidoc.section_titles +--- +---@field class "asciidoc_section_title" +---@field marker string The `=` part of the title. +--- +---@field text string[] +---@field range markview.parsed.range + +------------------------------------------------------------------------------ + ---@alias markview.parsed.asciidoc ---| markview.parsed.asciidoc.document_attributes ---| markview.parsed.asciidoc.document_titles +---| markview.parsed.asciidoc.section_titles ------------------------------------------------------------------------------ @@ -30,4 +41,5 @@ --- ---@field document_attributes markview.parsed.asciidoc.document_attributes[] ---@field document_titles markview.parsed.asciidoc.document_titles[] +---@field section_titles markview.parsed.asciidoc.section_titles[] diff --git a/lua/markview/types/renderers/asciidoc.lua b/lua/markview/types/renderers/asciidoc.lua index 16263211..af0838f7 100644 --- a/lua/markview/types/renderers/asciidoc.lua +++ b/lua/markview/types/renderers/asciidoc.lua @@ -22,8 +22,33 @@ ------------------------------------------------------------------------------ +---@class markview.config.asciidoc.section_titles +--- +---@field enable boolean +---@field shift_width integer +--- +---@field title_1 markview.config.asciidoc.section_titles.opts +---@field title_2 markview.config.asciidoc.section_titles.opts +---@field title_3 markview.config.asciidoc.section_titles.opts +---@field title_4 markview.config.asciidoc.section_titles.opts +---@field title_5 markview.config.asciidoc.section_titles.opts + + +---@class markview.config.asciidoc.section_titles.opts +--- +---@field icon? string +---@field icon_hl? string +--- +---@field sign? string +---@field sign_hl? string +--- +---@field hl? string + +------------------------------------------------------------------------------ + ---@class markview.config.asciidoc --- ---@field document_attributes markview.config.asciidoc.document_attributes ---@field document_titles markview.config.asciidoc.document_titles +---@field section_titles markview.config.asciidoc.section_titles diff --git a/test/asciidoc.adoc b/test/asciidoc.adoc index fdc12742..b08f8019 100644 --- a/test/asciidoc.adoc +++ b/test/asciidoc.adoc @@ -1,5 +1,7 @@ = Hello world +== hii + :bye: hi :hi: 123 From 69226e424188728e15c5504746f18bbdc20704a8 Mon Sep 17 00:00:00 2001 From: Shawon Date: Fri, 16 Jan 2026 14:21:57 +0600 Subject: [PATCH 19/40] feat(asciidoc): Added `TOC` support --- lua/markview/config/asciidoc.lua | 39 +++ lua/markview/parsers/asciidoc.lua | 213 ++++++++++++---- lua/markview/renderers/asciidoc.lua | 80 ++++++ lua/markview/renderers/asciidoc/tostring.lua | 241 +++++++++++++++++++ lua/markview/types/parsers/asciidoc.lua | 41 ++++ lua/markview/types/renderers/asciidoc.lua | 30 +++ test/asciidoc.adoc | 11 + 7 files changed, 607 insertions(+), 48 deletions(-) create mode 100644 lua/markview/renderers/asciidoc/tostring.lua diff --git a/lua/markview/config/asciidoc.lua b/lua/markview/config/asciidoc.lua index 2717a981..4fdd7e87 100644 --- a/lua/markview/config/asciidoc.lua +++ b/lua/markview/config/asciidoc.lua @@ -45,4 +45,43 @@ return { document_attributes = { enable = true, }, + + tocs = { + shift_width = 2, + hl = "MarkviewPalette2Fg", + + sign = "󰙅 ", + sign_hl = "MarkviewPalette2Sign", + + depth_1 = { + icon = "◆ ", + icon_hl = "Comment", + + hl = "MarkviewPalette5Fg", + }, + depth_2 = { + icon = "◇ ", + icon_hl = "Comment", + + hl = "MarkviewPalette5Fg", + }, + depth_3 = { + icon = "◆ ", + icon_hl = "Comment", + + hl = "MarkviewPalette5Fg", + }, + depth_4 = { + icon = "◇ ", + icon_hl = "Comment", + + hl = "MarkviewPalette5Fg", + }, + depth_5 = { + icon = "◆ ", + icon_hl = "Comment", + + hl = "MarkviewPalette5Fg", + }, + }, }; diff --git a/lua/markview/parsers/asciidoc.lua b/lua/markview/parsers/asciidoc.lua index eb6a05f6..3aafe9f3 100644 --- a/lua/markview/parsers/asciidoc.lua +++ b/lua/markview/parsers/asciidoc.lua @@ -1,9 +1,8 @@ --- HTML parser for `markview.nvim`. local asciidoc = {}; -asciidoc.data = { - document_title = nil, -}; +---@type markview.parser.asciidoc.data +asciidoc.data = {}; --- Queried contents ---@type table[] @@ -25,9 +24,32 @@ asciidoc.insert = function (data) table.insert(asciidoc.sorted[data.class], data); end +---@param buffer integer +---@param TSNode TSNode ---@param text string[] ---@param range markview.parsed.range -asciidoc.doc_attr = function (_, _, text, range) +asciidoc.doc_attr = function (buffer, TSNode, text, range) + local _name = TSNode:named_child(1) --[[@as TSNode]]; + local name = vim.treesitter.get_node_text(_name, buffer, {}); + + local _value = TSNode:named_child(3); + + if name == "toc" then + return; + elseif name == "toc-title" and _value then + asciidoc.data.toc_title = vim.treesitter.get_node_text(_value, buffer, {}); + elseif name == "toclevels" and _value then + asciidoc.data.toc_max_depth = math.max( + math.min( + tonumber( + vim.treesitter.get_node_text(_value, buffer, {}) + ) or 0, + 5 + ), + 0 + ); + end + asciidoc.insert({ class = "asciidoc_document_attribute", @@ -54,15 +76,72 @@ end ---@param text string[] ---@param range markview.parsed.range asciidoc.section_title = function (buffer, TSNode, text, range) - local marker = TSNode:child(0); + local _marker = TSNode:child(0); - if not marker then + if not _marker then return; end + local marker = vim.treesitter.get_node_text(_marker, buffer, {}); + local prev = TSNode:prev_named_sibling(); + + if prev then + local prev_text = vim.treesitter.get_node_text(prev, buffer, {}); + + if prev:type() == "element_attr" and prev_text == "[discrete]" then + goto dont_add_to_toc; + end + end + + if not asciidoc.data.toc_entries then + asciidoc.data.toc_entries = {}; + end + + table.insert(asciidoc.data.toc_entries, { + depth = (#marker or 1) - 1, + text = string.gsub(text[1] or "", "^[=%s]+", ""), + + range = vim.deepcopy(range, true), + } --[[@as markview.parser.asciidoc.data.toc_entry]]); + + ::dont_add_to_toc:: + asciidoc.insert({ class = "asciidoc_section_title", - marker = vim.treesitter.get_node_text(marker, buffer, {}), + marker = marker, + + text = text, + range = range + }); +end + +---@param text string[] +---@param range markview.parsed.range +asciidoc.toc_pos = function (_, _, text, range) + range.col_end = range.col_start + #(text[1] or ""); + asciidoc.data.toc_pos = range; +end + +---@param text string[] +---@param range markview.parsed.asciidoc.tocs.range +asciidoc.toc = function (_, _, text, range) + local validated = {}; + + for _, entry in ipairs(asciidoc.data.toc_entries or {}) do + if entry.depth < (asciidoc.data.toc_max_depth or 5) then + table.insert(validated, entry); + end + end + + range.col_end = range.col_start + #(text[1] or ""); + range.position = asciidoc.data.toc_pos; + + asciidoc.insert({ + class = "asciidoc_toc", + + title = asciidoc.data.toc_title, + max_depth = asciidoc.data.toc_max_depth, + entries = validated, text = text, range = range @@ -78,6 +157,7 @@ end ---@return markview.parsed.asciidoc_sorted asciidoc.parse = function (buffer, TSTree, from, to) -- Clear the previous contents + asciidoc.data = {}; asciidoc.sorted = {}; asciidoc.content = {}; @@ -92,6 +172,12 @@ asciidoc.parse = function (buffer, TSTree, from, to) (title4) (title5) ] @asciidoc.section_title + + (block_macro + ( + (block_macro_name) @toc_pos_name + (#eq? @toc_pos_name "toc") + )) @asciidoc.toc_pos ]]); if not can_scan then @@ -109,61 +195,92 @@ asciidoc.parse = function (buffer, TSTree, from, to) return asciidoc.content, asciidoc.sorted; end - for capture_id, capture_node, _, _ in scanned_queries:iter_captures(TSTree:root(), buffer, from, to) do - local capture_name = scanned_queries.captures[capture_id]; + local function iter (queries) + ---|fS - if not capture_name:match("^asciidoc%.") then - goto continue; - end + for capture_id, capture_node, _, _ in queries:iter_captures(TSTree:root(), buffer, from, to) do + local capture_name = queries.captures[capture_id]; - ---@type string? - local capture_text = vim.treesitter.get_node_text(capture_node, buffer); - local r_start, c_start, r_end, c_end = capture_node:range(); + if not capture_name:match("^asciidoc%.") then + goto continue; + end - if capture_text == nil then - goto continue; - end + ---@type string? + local capture_text = vim.treesitter.get_node_text(capture_node, buffer); + local r_start, c_start, r_end, c_end = capture_node:range(); - if not capture_text:match("\n$") then - capture_text = capture_text .. "\n"; - end + if capture_text == nil then + goto continue; + end - local lines = {}; + if not capture_text:match("\n$") then + capture_text = capture_text .. "\n"; + end - for line in capture_text:gmatch("(.-)\n") do - table.insert(lines, line); - end + local lines = {}; - ---@type boolean, string - local success, error = pcall( - asciidoc[capture_name:gsub("^asciidoc%.", "")], + for line in capture_text:gmatch("(.-)\n") do + table.insert(lines, line); + end - buffer, - capture_node, - lines, - { - row_start = r_start, - col_start = c_start, + ---@type boolean, string + local success, error = pcall( + asciidoc[capture_name:gsub("^asciidoc%.", "")], - row_end = r_end, - col_end = c_end - } - ); + buffer, + capture_node, + lines, + { + row_start = r_start, + col_start = c_start, - if success == false then - require("markview.health").print({ - kind = "ERR", + row_end = r_end, + col_end = c_end + } + ); - from = "parsers/asciidoc.lua", - fn = "parse()", + if success == false then + require("markview.health").print({ + kind = "ERR", - message = { - { tostring(error), "DiagnosticError" } - } - }); + from = "parsers/asciidoc.lua", + fn = "parse()", + + message = { + { tostring(error), "DiagnosticError" } + } + }); + end + + ::continue:: end - ::continue:: + ---|fE + end + + iter(scanned_queries); + + local can_scan_tquery, scanned_tqueries = pcall(vim.treesitter.query.parse, "asciidoc", [[ + (document_attr + ( + (attr_name) @toc_attr + (#eq? @toc_attr "toc") + )) @asciidoc.toc + ]]); + + if not can_scan_tquery then + require("markview.health").print({ + kind = "ERR", + + from = "parsers/asciidoc.lua", + fn = "parse() -> toc_query", + + message = { + { tostring(error), "DiagnosticError" } + } + }); + else + iter(scanned_tqueries); end return asciidoc.content, asciidoc.sorted; diff --git a/lua/markview/renderers/asciidoc.lua b/lua/markview/renderers/asciidoc.lua index 71df6308..84450871 100644 --- a/lua/markview/renderers/asciidoc.lua +++ b/lua/markview/renderers/asciidoc.lua @@ -87,6 +87,86 @@ asciidoc.section_title = function (buffer, item) }); end +---@param buffer integer +---@param item markview.parsed.asciidoc.tocs +asciidoc.toc = function (buffer, item) + ---@type markview.config.asciidoc.tocs? + local main_config = spec.get({ "asciidoc", "tocs" }, { fallback = nil, eval_args = { buffer, item } }); + + if not main_config then + return; + end + + local range = item.range; + local lines = {}; + + table.insert(lines, { + { main_config.icon or "", main_config.icon_hl or main_config.hl }, + { item.title or "Table of contents", main_config.hl }, + }); + + if item.entries and #item.entries > 0 then + table.insert(lines, { { "" } }); + end + + for _, entry in ipairs(item.entries or {}) do + ---@type markview.config.asciidoc.tocs.opts? + local config = spec.get({ "depth_" .. (entry.depth or 1) }, { source = main_config, eval_args = { buffer, item } }); + + if config then + local text = require("markview.renderers.asciidoc.tostring").tostring(buffer, entry.text, config.hl); + local shift_by = (main_config.shift_width or 1) * ( (entry.depth or 1) - 1 ); + + local line = { + { string.rep(config.shift_char or " ", shift_by), config.hl }, + { config.icon or "", config.icon_hl or config.hl }, + }; + + vim.list_extend(line, text); + table.insert(lines, line); + end + end + + local title = table.remove(lines, 1); + + if range.position then + utils.set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start, { + end_row = range.row_end - 1, + conceal_lines = "", + }); + + local r_pos = range.position --[[@as markview.parsed.range]]; + + utils.set_extmark(buffer, asciidoc.ns, r_pos.row_start, r_pos.col_start, { + end_col = r_pos.col_end, + conceal = "", + + virt_text = title, + virt_text_pos = "inline", + + sign_text = main_config.sign or "", + sign_hl_group = utils.set_hl(main_config.sign_hl), + + virt_lines = lines, + hl_mode = "combine", + }); + else + utils.set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start, { + end_col = range.col_end, + conceal = "", + + virt_text = title, + virt_text_pos = "inline", + + sign_text = main_config.sign or "", + sign_hl_group = utils.set_hl(main_config.sign_hl), + + virt_lines = lines, + hl_mode = "combine", + }); + end +end + ---@param buffer integer ---@param content markview.parsed.asciidoc[] asciidoc.render = function (buffer, content) diff --git a/lua/markview/renderers/asciidoc/tostring.lua b/lua/markview/renderers/asciidoc/tostring.lua new file mode 100644 index 00000000..510d214f --- /dev/null +++ b/lua/markview/renderers/asciidoc/tostring.lua @@ -0,0 +1,241 @@ +local adoc_str = {}; + +local utils = require("markview.utils"); +local spec = require("markview.spec"); + +adoc_str.buffer = -1; + +---@param match string +adoc_str.bold = function (match) + ---|fS + + ---@type markview.config.asciidoc_inline.bolds? + local config = spec.get({ + "asciidoc_inline", "bolds" + }, { + eval_args = { + adoc_str.buffer, { + class = "asciidoc_inline_bold", + delimiters = string.match(match, "^%*%*") and { "**", "**" } or { "*", "*" }, + + text = { match }, + range = { + row_start = -1, + col_start = -1, + + row_end = -1, + col_end = -1, + } + } --[[@as markview.parsed.asciidoc_inline.bolds]] + } + }); + + if not config then + return { match }; + else + local removed = string.gsub(match, "^%*+", ""):gsub("%*+$", ""); + return { removed }; + end + + ---|fE +end + +---@param match string +adoc_str.italic = function (match) + ---|fS + + ---@type markview.config.asciidoc_inline.italics? + local config = spec.get({ + "asciidoc_inline", "italics" + }, { + eval_args = { + adoc_str.buffer, { + class = "asciidoc_inline_italic", + delimiters = string.match(match, "^__") and { "__", "__" } or { "_", "_" }, + + text = { match }, + range = { + row_start = -1, + col_start = -1, + + row_end = -1, + col_end = -1, + } + } --[[@as markview.parsed.asciidoc_inline.italics]] + } + }); + + if not config then + return { match }; + else + local removed = string.gsub(match, "^[%*_]+", ""):gsub("[%*_]+$", ""); + return { removed }; + end + + ---|fE +end + +---@param match string +adoc_str.monospace = function (match) + ---|fS + + local delimiter = string.match(match, "^`+"); + + ---@type markview.config.asciidoc_inline.monospaces? + local config = spec.get({ + "asciidoc_inline", "monospaces" + }, { + eval_args = { + adoc_str.buffer, { + class = "asciidoc_inline_monospace", + delimiters = { delimiter, delimiter }, + + text = { match }, + range = { + row_start = -1, + col_start = -1, + + row_end = -1, + col_end = -1, + } + } --[[@as markview.parsed.asciidoc_inline.monospaces]] + } + }); + + if not config then + return { match }; + else + local removed = string.gsub(match, "^`+", ""):gsub("`+$", ""); + local output = {}; + + if config.corner_left then table.insert(output, { config.corner_left, config.corner_left_hl or config.hl }) end + if config.padding_left then table.insert(output, { config.padding_left, config.padding_left_hl or config.hl }) end + if config.icon then table.insert(output, { config.icon, config.icon_hl or config.hl }) end + + table.insert(output, { removed, config.hl }); + + if config.padding_right then table.insert(output, { config.padding_right, config.padding_right_hl or config.hl }) end + if config.corner_right then table.insert(output, { config.corner_right, config.corner_right_hl or config.hl }) end + + return output; + end + + ---|fE +end + +---@param match string +adoc_str.highlight = function (match) + ---|fS + + local delimiter = string.match(match, "#+"); + + ---@type markview.config.asciidoc_inline.highlights? + local main_config = spec.get({ "asciidoc_inline", "highlights" }, { fallback = nil }); + + if not main_config then + return { match }; + end + + ---@type markview.parsed.asciidoc_inline.highlights + local item = { + class = "asciidoc_inline_highlight", + delimiters = { delimiter, delimiter }, + + text = { match }, + range = { + row_start = -1, + col_start = -1, + + row_end = -1, + col_end = -1, + } + }; + + if not main_config then + return; + end + + ---@type markview.config.__inline? + local config = utils.match( + main_config, + table.concat(item.text, "\n"), + { + eval_args = { adoc_str.buffer, item } + } + ); + + if not config then + return { match }; + else + local removed = string.gsub(match, "^.-#+", ""):gsub("#+$", ""); + local output = {}; + + if config.corner_left then table.insert(output, { config.corner_left, config.corner_left_hl or config.hl }) end + if config.padding_left then table.insert(output, { config.padding_left, config.padding_left_hl or config.hl }) end + if config.icon then table.insert(output, { config.icon, config.icon_hl or config.hl }) end + + table.insert(output, { removed, config.hl }); + + if config.padding_right then table.insert(output, { config.padding_right, config.padding_right_hl or config.hl }) end + if config.corner_right then table.insert(output, { config.corner_right, config.corner_right_hl or config.hl }) end + + return output; + end + + ---|fE +end + +---@param char string +adoc_str.char = function (char) + return { char }; +end + +---@param buffer integer +---@param text string +---@param base_hl string +---@return [ string, string ][] +adoc_str.tostring = function (buffer, text, base_hl) + ---|fS + + local lpeg = vim.lpeg; + adoc_str.buffer = buffer; + + local strong_content = ( 1 - lpeg.P("*") ) + lpeg.P("\\*"); + local strong = lpeg.C( lpeg.P("*") * strong_content^1 * lpeg.P("*") ) / adoc_str.bold; + local ustrong = lpeg.C( lpeg.P("**") * strong_content^1 * lpeg.P("**") ) / adoc_str.bold; + + local italic_content = ( 1 - lpeg.P("_") ) + lpeg.P("\\_"); + local italic = lpeg.C( lpeg.P("_") * italic_content^1 * lpeg.P("_") ) / adoc_str.italic; + local uitalic = lpeg.C( lpeg.P("__") * italic_content^1 * lpeg.P("__") ) / adoc_str.italic; + + local mono_content = ( 1 - lpeg.P("`") ) + lpeg.P("\\`"); + local mono = lpeg.C( lpeg.P("`")^1 * mono_content^1 * lpeg.P("`")^1 ) / adoc_str.monospace; + + local hl_content = ( 1 - lpeg.P("##") ) + lpeg.P("\\#"); + local hl = lpeg.C( lpeg.P("##") * hl_content^1 * lpeg.P("##") ) / adoc_str.highlight; + local role_content = 1 - lpeg.P("]"); + local chl = lpeg.C( lpeg.P("[") * role_content^1 * lpeg.P("]") * lpeg.P("##") * hl_content^1 * lpeg.P("##") ) / adoc_str.highlight; + + local any = lpeg.C( lpeg.P(1) ) / adoc_str.char; + local token = ustrong + strong + uitalic + italic + mono + chl + hl + any; + + local inline = lpeg.Ct( token^0 ); + local result = {}; + + for _, item in ipairs(lpeg.match(inline, text or "")) do + local last = result[#result]; + item[2] = item[2] or base_hl; + + if (not item[2] or item[2] == base_hl) and last and (not last[2] or last[2] == base_hl) then + last[1] = last[1] .. item[1]; + else + table.insert(result, item) + end + end + + return result; + + ---|fE +end + +return adoc_str; diff --git a/lua/markview/types/parsers/asciidoc.lua b/lua/markview/types/parsers/asciidoc.lua index 3991e690..2e4854cf 100644 --- a/lua/markview/types/parsers/asciidoc.lua +++ b/lua/markview/types/parsers/asciidoc.lua @@ -30,6 +30,29 @@ ------------------------------------------------------------------------------ +---@class markview.parsed.asciidoc.tocs +--- +---@field class "asciidoc_toc" +---@field title? string +---@field max_depth? integer +---@field entries markview.parser.asciidoc.data.toc_entry[] +--- +---@field text string[] +---@field range markview.parsed.asciidoc.tocs.range + + +---@class markview.parsed.asciidoc.tocs.range +--- +---@field row_start integer +---@field col_start integer +--- +---@field row_end integer +---@field col_end integer +--- +---@field position? markview.parsed.range + +------------------------------------------------------------------------------ + ---@alias markview.parsed.asciidoc ---| markview.parsed.asciidoc.document_attributes ---| markview.parsed.asciidoc.document_titles @@ -43,3 +66,21 @@ ---@field document_titles markview.parsed.asciidoc.document_titles[] ---@field section_titles markview.parsed.asciidoc.section_titles[] +------------------------------------------------------------------------------ + +---@class markview.parser.asciidoc.data +--- +---@field document_title? string +---@field toc_title? string +---@field toc_max_depth? integer +---@field toc_entries? markview.parser.asciidoc.data.toc_entry[] +---@field toc_pos? markview.parsed.range + + +---@class markview.parser.asciidoc.data.toc_entry +--- +---@field depth integer +--- +---@field text string +---@field range markview.parsed.range + diff --git a/lua/markview/types/renderers/asciidoc.lua b/lua/markview/types/renderers/asciidoc.lua index af0838f7..1b5ce87c 100644 --- a/lua/markview/types/renderers/asciidoc.lua +++ b/lua/markview/types/renderers/asciidoc.lua @@ -46,6 +46,36 @@ ------------------------------------------------------------------------------ +---@class markview.config.asciidoc.tocs +--- +---@field enable boolean +---@field shift_width integer +--- +---@field icon? string Icon for the TOC title. +---@field icon_hl? string Highlight group for `icon`. +--- +---@field sign? string Sign for the TOC title. +---@field sign_hl? string Highlight group for `sign`. +--- +---@field hl? string +--- +---@field depth_1 markview.config.asciidoc.tocs.opts +---@field depth_2 markview.config.asciidoc.tocs.opts +---@field depth_3 markview.config.asciidoc.tocs.opts +---@field depth_4 markview.config.asciidoc.tocs.opts +---@field depth_5 markview.config.asciidoc.tocs.opts + + +---@class markview.config.asciidoc.tocs.opts +--- +---@field shift_char? string +---@field hl? string +--- +---@field icon? string Icon for the TOC title. +---@field icon_hl? string Highlight group for `icon`. + +------------------------------------------------------------------------------ + ---@class markview.config.asciidoc --- ---@field document_attributes markview.config.asciidoc.document_attributes diff --git a/test/asciidoc.adoc b/test/asciidoc.adoc index b08f8019..9f93ea38 100644 --- a/test/asciidoc.adoc +++ b/test/asciidoc.adoc @@ -1,11 +1,22 @@ = Hello world +:toc: +:toc-title: Hello + +[discrete] +== hii + + == hii +=== bye + :bye: hi :hi: 123 +toc::[] + It has *strong* significance to me. I _cannot_ stress this enough. From 318d47e02dbb8b50f2c0eb47ce0f7e782896d34b Mon Sep 17 00:00:00 2001 From: Shawon Date: Sat, 17 Jan 2026 14:42:42 +0600 Subject: [PATCH 20/40] feat(asciidoc): Added ordered & unordered list item support --- lua/markview/config/asciidoc.lua | 33 ++++++++ lua/markview/parsers/asciidoc.lua | 96 +++++++++++++++++++++++ lua/markview/renderers/asciidoc.lua | 55 +++++++++++++ lua/markview/types/parsers/asciidoc.lua | 26 ++++++ lua/markview/types/renderers/asciidoc.lua | 29 +++++++ test/asciidoc.adoc | 19 +++++ 6 files changed, 258 insertions(+) diff --git a/lua/markview/config/asciidoc.lua b/lua/markview/config/asciidoc.lua index 4fdd7e87..8ecc58a2 100644 --- a/lua/markview/config/asciidoc.lua +++ b/lua/markview/config/asciidoc.lua @@ -47,6 +47,8 @@ return { }, tocs = { + enable = true, + shift_width = 2, hl = "MarkviewPalette2Fg", @@ -84,4 +86,35 @@ return { hl = "MarkviewPalette5Fg", }, }, + + list_items = { + enable = true, + shift_width = 4, + + marker_dot = { + add_padding = true, + conceal_on_checkboxes = true, + + text = function (_, item) + return string.format("%d.", item.n); + end, + hl = "@markup.list.markdown", + }, + + marker_minus = { + add_padding = true, + conceal_on_checkboxes = true, + + text = "●", + hl = "MarkviewListItemMinus" + }, + + marker_star = { + add_padding = true, + conceal_on_checkboxes = true, + + text = "◇", + hl = "MarkviewListItemStar" + }, + }, }; diff --git a/lua/markview/parsers/asciidoc.lua b/lua/markview/parsers/asciidoc.lua index 3aafe9f3..88ab0784 100644 --- a/lua/markview/parsers/asciidoc.lua +++ b/lua/markview/parsers/asciidoc.lua @@ -148,6 +148,99 @@ asciidoc.toc = function (_, _, text, range) }); end +---@param buffer integer +---@param now string Current marker. +---@param last TSNode +---@return boolean +local function is_on_same_level(buffer, now, last) + local _marker = last:child(0); + + if not _marker then + return false; + end + + local marker = vim.treesitter.get_node_text(_marker, buffer, {}); + return marker == now; +end + +---@param buffer integer +---@param TSNode TSNode +---@param text string[] +---@param range markview.parsed.asciidoc.list_items.range +asciidoc.unordered_list_item = function (buffer, TSNode, text, range) + local _marker = TSNode:child(0); + + if not _marker then + return; + end + + local N = 1; + local prev = TSNode:prev_named_sibling(); + + local marker = vim.treesitter.get_node_text(_marker, buffer, {}); + _, _, _, range.marker_end = _marker:range(); + + while prev do + if prev:type() == "unordered_list_item" then + if is_on_same_level(buffer, marker, prev) then + N = N + 1; + else + break; + end + end + + prev = prev:prev_named_sibling(); + end + + asciidoc.insert({ + class = "asciidoc_list_item", + marker = marker, + n = N, + + text = text, + range = range + }); +end + +---@param buffer integer +---@param TSNode TSNode +---@param text string[] +---@param range markview.parsed.asciidoc.list_items.range +asciidoc.ordered_list_item = function (buffer, TSNode, text, range) + local _marker = TSNode:child(0); + + if not _marker then + return; + end + + local N = 1; + local prev = TSNode:prev_named_sibling(); + + local marker = vim.treesitter.get_node_text(_marker, buffer, {}); + _, _, _, range.marker_end = _marker:range(); + + while prev do + if prev:type() == "ordered_list_item" then + if is_on_same_level(buffer, marker, prev) then + N = N + 1; + else + break; + end + end + + prev = prev:prev_named_sibling(); + end + + asciidoc.insert({ + class = "asciidoc_list_item", + marker = marker, + n = N, + + text = text, + range = range + }); +end + --- HTML parser ---@param buffer integer ---@param TSTree table @@ -178,6 +271,9 @@ asciidoc.parse = function (buffer, TSTree, from, to) (block_macro_name) @toc_pos_name (#eq? @toc_pos_name "toc") )) @asciidoc.toc_pos + + (unordered_list_item) @asciidoc.unordered_list_item + (ordered_list_item) @asciidoc.ordered_list_item ]]); if not can_scan then diff --git a/lua/markview/renderers/asciidoc.lua b/lua/markview/renderers/asciidoc.lua index 84450871..dd5e648d 100644 --- a/lua/markview/renderers/asciidoc.lua +++ b/lua/markview/renderers/asciidoc.lua @@ -50,6 +50,61 @@ asciidoc.document_title = function (buffer, item) }); end +--- Renders atx headings. +---@param buffer integer +---@param item markview.parsed.asciidoc.list_items +asciidoc.list_item = function (buffer, item) + ---@type markview.config.asciidoc.list_items? + local main_config = spec.get({ "asciidoc", "list_items" }, { fallback = nil, eval_args = { buffer, item } }); + + if not main_config then + return; + end + + ---@type markview.config.asciidoc.list_items.opts? + local config; + + if string.match(item.marker, "%*") then + config = spec.get({ "marker_star" }, { source = main_config, eval_args = { buffer, item } }); + elseif string.match(item.marker, "%-") then + config = spec.get({ "marker_minus" }, { source = main_config, eval_args = { buffer, item } }); + else + config = spec.get({ "marker_dot" }, { source = main_config, eval_args = { buffer, item } }); + end + + if not config then + return; + end + + ---@cast config markview.config.asciidoc.list_items.opts + + local shift_width = main_config.shift_width or 2; + local range = item.range; + + for r = range.row_start, range.row_end - 1, 1 do + if r == range.row_start then + + utils.set_extmark(buffer, asciidoc.ns, r, range.col_start, { + end_col = range.marker_end, + conceal = "", + + virt_text = { + { config.add_padding and string.rep(" ", #item.marker * shift_width) or "" }, + { config.text or "", config.hl }, + }, + hl_mode = "combine", + }); + elseif config.add_padding then + utils.set_extmark(buffer, asciidoc.ns, r, 0, { + virt_text = { + { string.rep(" ", #item.marker * shift_width) }, + }, + hl_mode = "combine", + }); + end + end +end + --- Renders atx headings. ---@param buffer integer ---@param item markview.parsed.asciidoc.section_titles diff --git a/lua/markview/types/parsers/asciidoc.lua b/lua/markview/types/parsers/asciidoc.lua index 2e4854cf..0de7b7f9 100644 --- a/lua/markview/types/parsers/asciidoc.lua +++ b/lua/markview/types/parsers/asciidoc.lua @@ -20,6 +20,28 @@ ------------------------------------------------------------------------------ +---@class markview.parsed.asciidoc.list_items +--- +---@field class "asciidoc_list_item" +---@field marker string List item marker. +---@field n integer Item index. +--- +---@field text string[] +---@field range markview.parsed.asciidoc.list_items.range + + +---@class markview.parsed.asciidoc.list_items.range +--- +---@field row_start integer +---@field col_start integer +--- +---@field row_end integer +---@field col_end integer +--- +---@field marker_end integer End column for the list item marker(e.g. `*` or `.`). + +------------------------------------------------------------------------------ + ---@class markview.parsed.asciidoc.section_titles --- ---@field class "asciidoc_section_title" @@ -56,7 +78,9 @@ ---@alias markview.parsed.asciidoc ---| markview.parsed.asciidoc.document_attributes ---| markview.parsed.asciidoc.document_titles +---| markview.parsed.asciidoc.list_items ---| markview.parsed.asciidoc.section_titles +---| markview.parsed.asciidoc.tocs ------------------------------------------------------------------------------ @@ -64,7 +88,9 @@ --- ---@field document_attributes markview.parsed.asciidoc.document_attributes[] ---@field document_titles markview.parsed.asciidoc.document_titles[] +---@field list_items markview.parsed.asciidoc.list_items[] ---@field section_titles markview.parsed.asciidoc.section_titles[] +---@field tocs markview.parsed.asciidoc.tocs[] ------------------------------------------------------------------------------ diff --git a/lua/markview/types/renderers/asciidoc.lua b/lua/markview/types/renderers/asciidoc.lua index 1b5ce87c..84a6a744 100644 --- a/lua/markview/types/renderers/asciidoc.lua +++ b/lua/markview/types/renderers/asciidoc.lua @@ -22,6 +22,33 @@ ------------------------------------------------------------------------------ +--- Configuration for list items. +---@class markview.config.asciidoc.list_items +--- +---@field enable boolean +---@field shift_width integer | fun(buffer: integer, item: markview.parsed.markdown.list_items): integer Virtual indentation size for previewed list items. +--- +---@field marker_dot markview.config.asciidoc.list_items.opts Configuration for `.` list items. +---@field marker_minus markview.config.asciidoc.list_items.opts Configuration for `-` list items. +---@field marker_star markview.config.asciidoc.list_items.opts Configuration for `*` list items. +--- +---@field wrap? boolean Enables wrap support. + + +---@class markview.config.asciidoc.list_items.opts +--- +---@field add_padding boolean +---@field conceal_on_checkboxes? boolean +---@field enable? boolean +---@field hl? string +--- +---[[ Text used to replace the list item marker. ]] +---@field text? +---| string +---| fun(buffer: integer, item: markview.parsed.asciidoc.list_items): string + +------------------------------------------------------------------------------ + ---@class markview.config.asciidoc.section_titles --- ---@field enable boolean @@ -80,5 +107,7 @@ --- ---@field document_attributes markview.config.asciidoc.document_attributes ---@field document_titles markview.config.asciidoc.document_titles +---@field list_items markview.config.asciidoc.list_items ---@field section_titles markview.config.asciidoc.section_titles +---@field tocs markview.config.asciidoc.tocs diff --git a/test/asciidoc.adoc b/test/asciidoc.adoc index 9f93ea38..f83722f8 100644 --- a/test/asciidoc.adoc +++ b/test/asciidoc.adoc @@ -62,3 +62,22 @@ devel@discuss.example.org mailto:devel@discuss.example.org[Discuss] mailto:join@discuss.example.org[Subscribe,Subscribe me,I want to join!] + +* List item ++ +Hello + +* Bye +** Nested list item +*** Deeper nested list item +* List item +*** Another nested list item +* List item + +. Hi +.. Hello +.. Bye +. Bye +.. bye +.. hi + From 1658e34fdea9affad51a882eb73367f3c9909c6b Mon Sep 17 00:00:00 2001 From: Shawon Date: Sun, 18 Jan 2026 07:40:34 +0600 Subject: [PATCH 21/40] refactor(asciidoc): Unified parser for ordered & unordered list item types --- lua/markview/parsers/asciidoc.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lua/markview/parsers/asciidoc.lua b/lua/markview/parsers/asciidoc.lua index 88ab0784..8b93b3de 100644 --- a/lua/markview/parsers/asciidoc.lua +++ b/lua/markview/parsers/asciidoc.lua @@ -167,7 +167,7 @@ end ---@param TSNode TSNode ---@param text string[] ---@param range markview.parsed.asciidoc.list_items.range -asciidoc.unordered_list_item = function (buffer, TSNode, text, range) +asciidoc.list_item = function (buffer, TSNode, text, range) local _marker = TSNode:child(0); if not _marker then @@ -181,7 +181,8 @@ asciidoc.unordered_list_item = function (buffer, TSNode, text, range) _, _, _, range.marker_end = _marker:range(); while prev do - if prev:type() == "unordered_list_item" then + -- NOTE: Only one of these node types should appear. + if vim.list_contains({ "unordered_list_item", "ordered_list_item" }, prev:type()) then if is_on_same_level(buffer, marker, prev) then N = N + 1; else @@ -272,8 +273,8 @@ asciidoc.parse = function (buffer, TSTree, from, to) (#eq? @toc_pos_name "toc") )) @asciidoc.toc_pos - (unordered_list_item) @asciidoc.unordered_list_item - (ordered_list_item) @asciidoc.ordered_list_item + (unordered_list_item) @asciidoc.list_item + (ordered_list_item) @asciidoc.list_item ]]); if not can_scan then From 40ac165eda9c79f8821f0cf91cb06a04130eba2d Mon Sep 17 00:00:00 2001 From: Shawon Date: Sun, 18 Jan 2026 17:30:10 +0600 Subject: [PATCH 22/40] feat(asciidoc): Added checkboxes support --- lua/markview/config/asciidoc.lua | 29 +++++++++++ lua/markview/parsers/asciidoc.lua | 60 +++++++++++++++++++---- lua/markview/renderers/asciidoc.lua | 57 ++++++++++++++++----- lua/markview/types/parsers/asciidoc.lua | 4 ++ lua/markview/types/renderers/asciidoc.lua | 21 ++++++++ test/asciidoc.adoc | 6 +++ 6 files changed, 156 insertions(+), 21 deletions(-) diff --git a/lua/markview/config/asciidoc.lua b/lua/markview/config/asciidoc.lua index 8ecc58a2..2013c42b 100644 --- a/lua/markview/config/asciidoc.lua +++ b/lua/markview/config/asciidoc.lua @@ -1,5 +1,34 @@ ---@type markview.config.asciidoc return { + checkboxes = { + enable = true, + + checked = { text = "󰗠", hl = "MarkviewCheckboxChecked", scope_hl = "MarkviewCheckboxChecked" }, + unchecked = { text = "󰄰", hl = "MarkviewCheckboxUnchecked", scope_hl = "MarkviewCheckboxUnchecked" }, + + ["/"] = { text = "󱎖", hl = "MarkviewCheckboxPending" }, + [">"] = { text = "", hl = "MarkviewCheckboxCancelled" }, + ["<"] = { text = "󰃖", hl = "MarkviewCheckboxCancelled" }, + ["-"] = { text = "󰍶", hl = "MarkviewCheckboxCancelled", scope_hl = "MarkviewCheckboxStriked" }, + + ["?"] = { text = "󰋗", hl = "MarkviewCheckboxPending" }, + ["!"] = { text = "󰀦", hl = "MarkviewCheckboxUnchecked" }, + ["*"] = { text = "󰓎", hl = "MarkviewCheckboxPending" }, + ['"'] = { text = "󰸥", hl = "MarkviewCheckboxCancelled" }, + ["l"] = { text = "󰆋", hl = "MarkviewCheckboxProgress" }, + ["b"] = { text = "󰃀", hl = "MarkviewCheckboxProgress" }, + ["i"] = { text = "󰰄", hl = "MarkviewCheckboxChecked" }, + ["S"] = { text = "", hl = "MarkviewCheckboxChecked" }, + ["I"] = { text = "󰛨", hl = "MarkviewCheckboxPending" }, + ["p"] = { text = "", hl = "MarkviewCheckboxChecked" }, + ["c"] = { text = "", hl = "MarkviewCheckboxUnchecked" }, + ["f"] = { text = "󱠇", hl = "MarkviewCheckboxUnchecked" }, + ["k"] = { text = "", hl = "MarkviewCheckboxPending" }, + ["w"] = { text = "", hl = "MarkviewCheckboxProgress" }, + ["u"] = { text = "󰔵", hl = "MarkviewCheckboxChecked" }, + ["d"] = { text = "󰔳", hl = "MarkviewCheckboxUnchecked" }, + }, + document_titles = { enable = true, diff --git a/lua/markview/parsers/asciidoc.lua b/lua/markview/parsers/asciidoc.lua index 8b93b3de..df298ce1 100644 --- a/lua/markview/parsers/asciidoc.lua +++ b/lua/markview/parsers/asciidoc.lua @@ -167,7 +167,7 @@ end ---@param TSNode TSNode ---@param text string[] ---@param range markview.parsed.asciidoc.list_items.range -asciidoc.list_item = function (buffer, TSNode, text, range) +asciidoc._list_item = function (buffer, TSNode, text, range) local _marker = TSNode:child(0); if not _marker then @@ -207,18 +207,11 @@ end ---@param TSNode TSNode ---@param text string[] ---@param range markview.parsed.asciidoc.list_items.range -asciidoc.ordered_list_item = function (buffer, TSNode, text, range) - local _marker = TSNode:child(0); - - if not _marker then - return; - end - +asciidoc.list_item = function (buffer, TSNode, text, range) local N = 1; local prev = TSNode:prev_named_sibling(); - local marker = vim.treesitter.get_node_text(_marker, buffer, {}); - _, _, _, range.marker_end = _marker:range(); + local marker; while prev do if prev:type() == "ordered_list_item" then @@ -232,8 +225,53 @@ asciidoc.ordered_list_item = function (buffer, TSNode, text, range) prev = prev:prev_named_sibling(); end + local checkbox; + + for child in TSNode:iter_children() do + if child:type() == "checked_list_marker" then + local _marker = child:named_child(0):named_child(0) --[[@as TSNode]]; + local _checkbox = child:named_child(1) --[[@as TSNode]]; + + marker = vim.treesitter.get_node_text(_marker, buffer, {}) + _, _, _, range.marker_end = _marker:range(); + + if _checkbox then + if _checkbox:type() == "checked_list_marker_checked" then + checkbox = "*"; + _, range.checkbox_start, _, range.checkbox_end = _checkbox:range(); + + break; + elseif _checkbox:type() == "checked_list_marker_unchecked" then + checkbox = " "; + _, range.checkbox_start, _, range.checkbox_end = _checkbox:range(); + + break; + end + end + elseif vim.list_contains({ "ordered_list_marker", "unordered_list_marker" }, child:type()) then + local _marker = child:named_child(0) --[[@as TSNode]]; + + marker = vim.treesitter.get_node_text(_marker, buffer, {}) + _, _, _, range.marker_end = _marker:range(); + elseif child:type() == "line" then + local _text = vim.treesitter.get_node_text(child, buffer, {}); + checkbox = string.match(_text, "^%[(.)%]"); + + if checkbox then + local _, tmp = child:range(); + + range.checkbox_start = tmp; + range.checkbox_end = tmp + 3; + + break; + end + end + end + asciidoc.insert({ class = "asciidoc_list_item", + + checkbox = checkbox, marker = marker, n = N, @@ -275,6 +313,8 @@ asciidoc.parse = function (buffer, TSTree, from, to) (unordered_list_item) @asciidoc.list_item (ordered_list_item) @asciidoc.list_item + + (checked_list_item) @asciidoc.list_item ]]); if not can_scan then diff --git a/lua/markview/renderers/asciidoc.lua b/lua/markview/renderers/asciidoc.lua index dd5e648d..ff12d17a 100644 --- a/lua/markview/renderers/asciidoc.lua +++ b/lua/markview/renderers/asciidoc.lua @@ -78,22 +78,57 @@ asciidoc.list_item = function (buffer, item) ---@cast config markview.config.asciidoc.list_items.opts + local checkbox_config; + + if item.checkbox == "*" then + checkbox_config = spec.get({ "asciidoc", "checkboxes", "checked" }, { eval_args = { buffer, item } }); + elseif item.checkbox == " " then + checkbox_config = spec.get({ "asciidoc", "checkboxes", "unchecked" }, { eval_args = { buffer, item } }); + elseif item.checkbox then + local checkboxes = spec.get({ "asciidoc", "checkboxes" }, { eval_args = { buffer, item } }); + local _state = vim.pesc(tostring(item.checkbox)); + + checkbox_config = utils.match(checkboxes, "^" .. _state .. "$", { default = false, ignore_keys = { "checked", "unchecked", "enable" }, eval_args = { buffer, item } }); + end + local shift_width = main_config.shift_width or 2; local range = item.range; for r = range.row_start, range.row_end - 1, 1 do if r == range.row_start then - - utils.set_extmark(buffer, asciidoc.ns, r, range.col_start, { - end_col = range.marker_end, - conceal = "", - - virt_text = { - { config.add_padding and string.rep(" ", #item.marker * shift_width) or "" }, - { config.text or "", config.hl }, - }, - hl_mode = "combine", - }); + if checkbox_config and not vim.tbl_isempty(checkbox_config) then + utils.set_extmark(buffer, asciidoc.ns, r, range.col_start, { + end_col = config.conceal_on_checkboxes and range.checkbox_start or range.marker_end, + conceal = "", + + virt_text = { + { config.add_padding and string.rep(" ", #item.marker * shift_width) or "" }, + { not config.conceal_on_checkboxes and config.text or "", config.hl }, + }, + hl_mode = "combine", + }); + + utils.set_extmark(buffer, asciidoc.ns, r, range.checkbox_start, { + end_col = range.checkbox_end, + conceal = "", + + virt_text = { + { checkbox_config.text or "", checkbox_config.hl }, + }, + hl_mode = "combine", + }); + else + utils.set_extmark(buffer, asciidoc.ns, r, range.col_start, { + end_col = range.marker_end, + conceal = "", + + virt_text = { + { config.add_padding and string.rep(" ", #item.marker * shift_width) or "" }, + { config.text or "", config.hl }, + }, + hl_mode = "combine", + }); + end elseif config.add_padding then utils.set_extmark(buffer, asciidoc.ns, r, 0, { virt_text = { diff --git a/lua/markview/types/parsers/asciidoc.lua b/lua/markview/types/parsers/asciidoc.lua index 0de7b7f9..332a5a52 100644 --- a/lua/markview/types/parsers/asciidoc.lua +++ b/lua/markview/types/parsers/asciidoc.lua @@ -23,6 +23,7 @@ ---@class markview.parsed.asciidoc.list_items --- ---@field class "asciidoc_list_item" +---@field checkbox? string ---@field marker string List item marker. ---@field n integer Item index. --- @@ -39,6 +40,9 @@ ---@field col_end integer --- ---@field marker_end integer End column for the list item marker(e.g. `*` or `.`). +--- +---@field checkbox_start? integer Start column for the checkbox(e.g. `[*]` or `[ ]`). +---@field checkbox_end? integer End column for the checkbox(e.g. `[*]` or `[ ]`). ------------------------------------------------------------------------------ diff --git a/lua/markview/types/renderers/asciidoc.lua b/lua/markview/types/renderers/asciidoc.lua index 84a6a744..90bc1692 100644 --- a/lua/markview/types/renderers/asciidoc.lua +++ b/lua/markview/types/renderers/asciidoc.lua @@ -2,6 +2,26 @@ ------------------------------------------------------------------------------ +--- Configuration for checkboxes. +---@class markview.config.asciidoc.checkboxes +--- +---@field enable boolean Enable rendering of checkboxes. +--- +---@field checked markview.config.markdown_inline.checkboxes.opts Configuration for `[*]`. +---@field unchecked markview.config.markdown_inline.checkboxes.opts Configuration for `[ ]`. +--- +---@field [string] markview.config.markdown_inline.checkboxes.opts Configuration for `[string]` checkbox. + + +--[[ Options for a specific checkbox. ]] +---@class markview.config.asciidoc.checkboxes.opts +--- +---@field text string Text used to replace `[]` part. +---@field hl? string Highlight group for `text`. +---@field scope_hl? string Highlight group for the list item. + +------------------------------------------------------------------------------ + ---@class markview.config.asciidoc.document_attributes --- ---@field enable boolean @@ -105,6 +125,7 @@ ---@class markview.config.asciidoc --- +---@field checkboxes markview.config.asciidoc.checkboxes ---@field document_attributes markview.config.asciidoc.document_attributes ---@field document_titles markview.config.asciidoc.document_titles ---@field list_items markview.config.asciidoc.list_items diff --git a/test/asciidoc.adoc b/test/asciidoc.adoc index f83722f8..a60ddea4 100644 --- a/test/asciidoc.adoc +++ b/test/asciidoc.adoc @@ -73,6 +73,7 @@ Hello * List item *** Another nested list item * List item +- Bye . Hi .. Hello @@ -81,3 +82,8 @@ Hello .. bye .. hi +* [*] checked +* [ ] also checked +* [-] not checked +* normal list item + From d482fb5e2acd470604fa72da01ee5a2ff71724d4 Mon Sep 17 00:00:00 2001 From: Shawon Date: Tue, 20 Jan 2026 15:42:40 +0600 Subject: [PATCH 23/40] feat(asciidoc): Added Image macro support --- lua/markview/config/asciidoc.lua | 15 +++++ lua/markview/parsers/asciidoc.lua | 38 ++++++++++++ lua/markview/parsers/asciidoc_inline.lua | 7 +++ lua/markview/renderers/asciidoc.lua | 71 ++++++++++++++++++++++ lua/markview/renderers/asciidoc_inline.lua | 6 +- lua/markview/types/parsers/asciidoc.lua | 21 +++++++ lua/markview/types/renderers/asciidoc.lua | 37 +++++++++++ test/asciidoc.adoc | 10 +++ 8 files changed, 202 insertions(+), 3 deletions(-) diff --git a/lua/markview/config/asciidoc.lua b/lua/markview/config/asciidoc.lua index 2013c42b..c0455ad3 100644 --- a/lua/markview/config/asciidoc.lua +++ b/lua/markview/config/asciidoc.lua @@ -75,6 +75,21 @@ return { enable = true, }, + images = { + enable = true, + + default = { + icon = "󰥶 ", + hl = "MarkviewImage", + }, + + ["%.svg$"] = { icon = "󰜡 " }, + ["%.png$"] = { icon = "󰸭 " }, + ["%.jpg$"] = { icon = "󰈥 " }, + ["%.gif$"] = { icon = "󰵸 " }, + ["%.pdf$"] = { icon = " " } + }, + tocs = { enable = true, diff --git a/lua/markview/parsers/asciidoc.lua b/lua/markview/parsers/asciidoc.lua index df298ce1..3f6b47c1 100644 --- a/lua/markview/parsers/asciidoc.lua +++ b/lua/markview/parsers/asciidoc.lua @@ -115,6 +115,38 @@ asciidoc.section_title = function (buffer, TSNode, text, range) }); end +---@param buffer integer +---@param TSNode TSNode +---@param text string[] +---@param range markview.parsed.asciidoc.images.range +asciidoc.image = function (buffer, TSNode, text, range) + local _destination = TSNode:named_child(1); + + if not _destination then + return; + end + + local destination = vim.treesitter.get_node_text(_destination, buffer, {}); + range.destination = { _destination:range() }; + + --[[ + refactor: Range correction + + Block macros end at the start of the next line. + So, we correct the end column & end row. + ]] + range.row_end = range.row_end - 1; + range.col_end = range.col_start + #(text[#text] or ""); + + asciidoc.insert({ + class = "asciidoc_image", + destination = destination, + + text = text, + range = range + }); +end + ---@param text string[] ---@param range markview.parsed.range asciidoc.toc_pos = function (_, _, text, range) @@ -315,6 +347,12 @@ asciidoc.parse = function (buffer, TSTree, from, to) (ordered_list_item) @asciidoc.list_item (checked_list_item) @asciidoc.list_item + + (block_macro + ( + (block_macro_name) @image_keyword + (#eq? @image_keyword "image") + )) @asciidoc.image ]]); if not can_scan then diff --git a/lua/markview/parsers/asciidoc_inline.lua b/lua/markview/parsers/asciidoc_inline.lua index 18cd7181..2f3ce87d 100644 --- a/lua/markview/parsers/asciidoc_inline.lua +++ b/lua/markview/parsers/asciidoc_inline.lua @@ -156,6 +156,13 @@ end ---@param text string[] ---@param range markview.parsed.range asciidoc_inline.uri = function (buffer, TSNode, text, range) + local before = vim.api.nvim_buf_get_text(buffer, range.row_start, 0, range.row_start, range.col_start, {})[1]; + + -- NOTE: Do not parse a URI if it's part of an image macro. + if string.match(before, "image::$") then + return; + end + local delimiters = {}; local destination; diff --git a/lua/markview/renderers/asciidoc.lua b/lua/markview/renderers/asciidoc.lua index ff12d17a..b9f46515 100644 --- a/lua/markview/renderers/asciidoc.lua +++ b/lua/markview/renderers/asciidoc.lua @@ -23,6 +23,77 @@ asciidoc.document_attribute = function (buffer, item) }); end +---@param buffer integer +---@param item markview.parsed.asciidoc.images +asciidoc.image = function (buffer, item) + ---@type markview.config.asciidoc.images? + local main_config = spec.get({ "asciidoc", "images" }, { fallback = nil }); + local range = item.range; + + if not main_config then + return; + end + + ---@type markview.config.asciidoc.images.opts? + local config = utils.match( + main_config, + item.destination, + { + eval_args = { buffer, item } + } + ); + + if config == nil then + return; + end + + utils.set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start, { + end_col = range.destination[2], + conceal = "", + + virt_text_pos = "inline", + virt_text = { + { config.corner_left or "", utils.set_hl(config.corner_left_hl or config.hl) }, + { config.padding_left or "", utils.set_hl(config.padding_left_hl or config.hl) }, + { config.icon or "", utils.set_hl(config.icon_hl or config.hl) } + }, + + hl_mode = "combine" + }); + + if config.text then + utils.set_extmark(buffer, asciidoc.ns, range.destination[1], range.destination[2], { + end_col = range.destination[4], end_row = range.destination[3], + + virt_text = { + { config.text or "", utils.set_hl(config.text_hl or config.hl) } + }, + + hl_mode = "combine" + }); + else + utils.set_extmark(buffer, asciidoc.ns, range.destination[1], range.destination[2], { + end_col = range.destination[4], end_row = range.destination[3], + + hl_group = utils.set_hl(config.hl), + hl_mode = "combine" + }); + end + + utils.set_extmark(buffer, asciidoc.ns, range.row_end, range.destination[4], { + end_col = range.col_end, + conceal = "", + + virt_text_pos = "inline", + virt_text = { + { config.corner_right or "", utils.set_hl(config.corner_right_hl or config.hl) }, + { config.padding_right or "", utils.set_hl(config.padding_right_hl or config.hl) } + }, + + hl_mode = "combine" + }); +end + ---@param buffer integer ---@param item markview.parsed.asciidoc.document_titles asciidoc.document_title = function (buffer, item) diff --git a/lua/markview/renderers/asciidoc_inline.lua b/lua/markview/renderers/asciidoc_inline.lua index 8fae022e..b38d5b1b 100644 --- a/lua/markview/renderers/asciidoc_inline.lua +++ b/lua/markview/renderers/asciidoc_inline.lua @@ -258,9 +258,9 @@ asciidoc_inline.uri = function (buffer, item) utils.set_extmark(buffer, asciidoc_inline.ns, range.row_start, range.col_start + #(item.delimiters[1] or ""), { end_col = range.col_end - #(item.delimiters[2] or ""), end_row = range.row_end, - virt_text = { - { config.text or "", utils.set_hl(config.text_hl or config.hl) } - }, + virt_text = { + { config.text or "", utils.set_hl(config.text_hl or config.hl) } + }, hl_mode = "combine" }); diff --git a/lua/markview/types/parsers/asciidoc.lua b/lua/markview/types/parsers/asciidoc.lua index 332a5a52..bbc051f3 100644 --- a/lua/markview/types/parsers/asciidoc.lua +++ b/lua/markview/types/parsers/asciidoc.lua @@ -20,6 +20,27 @@ ------------------------------------------------------------------------------ +---@class markview.parsed.asciidoc.images +--- +---@field class "asciidoc_image" +---@field destination string Source of the image. +--- +---@field text string[] +---@field range markview.parsed.asciidoc.images.range + + +---@class markview.parsed.asciidoc.images.range +--- +---@field row_start integer +---@field col_start integer +--- +---@field row_end integer +---@field col_end integer +--- +---@field destination integer[] Range of the image destination(output of `{ TSNode:range() }`. + +------------------------------------------------------------------------------ + ---@class markview.parsed.asciidoc.list_items --- ---@field class "asciidoc_list_item" diff --git a/lua/markview/types/renderers/asciidoc.lua b/lua/markview/types/renderers/asciidoc.lua index 90bc1692..cd035c57 100644 --- a/lua/markview/types/renderers/asciidoc.lua +++ b/lua/markview/types/renderers/asciidoc.lua @@ -42,6 +42,43 @@ ------------------------------------------------------------------------------ +--- Configuration for image links. +---@class markview.config.asciidoc.images +--- +---@field enable boolean Enable rendering of image links +--- +---@field default markview.config.asciidoc.images.opts Default configuration for image links +---@field [string] markview.config.asciidoc.images.opts Configuration image links whose description matches `string`. + + +--[[ Options for a specific image link type. ]] +---@class markview.config.asciidoc.images.opts +--- +---@field corner_left? string Left corner. +---@field corner_left_hl? string Highlight group for the left corner. +--- +---@field padding_left? string Left padding(added after `corner_left`). +---@field padding_left_hl? string Highlight group for the left padding. +--- +---@field icon? string Icon(added after `padding_left`). +---@field icon_hl? string Highlight group for the icon. +--- +--[[ Text to show instead of the `URL`.]] +---@field text? +---| string +---| function(buffer: integer, item: markview.parsed.asciidoc_inline.uris): string +---@field text_hl? string Highlight group for the text. +--- +---@field hl? string Default highlight group(used by `*_hl` options when they are not set). +--- +---@field padding_right? string Right padding. +---@field padding_right_hl? string Highlight group for the right padding. +--- +---@field corner_right? string Right corner(added after `padding_right`). +---@field corner_right_hl? string Highlight group for the right corner. + +------------------------------------------------------------------------------ + --- Configuration for list items. ---@class markview.config.asciidoc.list_items --- diff --git a/test/asciidoc.adoc b/test/asciidoc.adoc index a60ddea4..5cdb8411 100644 --- a/test/asciidoc.adoc +++ b/test/asciidoc.adoc @@ -1,3 +1,13 @@ +image::sunset.jpg[] + +image::sunset.jpg[Sunset] + +.A mountain sunset +[#img-sunset,caption="Figure 1: ",link=https://www.flickr.com/photos/javh/5448336655] +image::macros:sunset.jpg[Sunset,200,100] + +image::https://asciidoctor.org/images/octocat.jpg[GitHub mascot] + = Hello world :toc: From f2452854cac5fce5fb79f3b9a95f0f603fcc7182 Mon Sep 17 00:00:00 2001 From: Shawon Date: Wed, 21 Jan 2026 16:09:09 +0600 Subject: [PATCH 24/40] feat(asciidoc): Added keycodes support --- lua/markview/config/asciidoc.lua | 85 +++++++++++++++++++++++ lua/markview/parsers/asciidoc.lua | 74 ++++++++++---------- lua/markview/renderers/asciidoc.lua | 65 ++++++++++++++++- lua/markview/types/parsers/asciidoc.lua | 21 ++++++ lua/markview/types/renderers/asciidoc.lua | 14 ++++ test/asciidoc.adoc | 2 + 6 files changed, 221 insertions(+), 40 deletions(-) diff --git a/lua/markview/config/asciidoc.lua b/lua/markview/config/asciidoc.lua index c0455ad3..0238b68a 100644 --- a/lua/markview/config/asciidoc.lua +++ b/lua/markview/config/asciidoc.lua @@ -1,3 +1,60 @@ +---@type [ string, markview.config.asciidoc.keycodes ] +local keycodes = { + default = { + padding_left = " ", + padding_right = " ", + + icon = "󰌌 ", + hl = "MarkviewPalette6", + }, + + ctrl = { + icon = " ", + hl = "MarkviewPalette3", + }, + + shift = { + icon = "󰘶 ", + hl = "MarkviewPalette4", + }, + + meta = { + icon = "󰘵 ", + hl = "MarkviewPalette2", + }, + + super = { + icon = "󰌽 ", + hl = "MarkviewPalette5", + }, + + command = { + icon = "󰘳 ", + hl = "MarkviewPalette5", + }, + + caps_lock = { + icon = "󰘲 ", + hl = "MarkviewPalette1", + }, + + space = { + icon = "󱁐 ", + hl = "MarkviewPalette5", + }, + + enter = { + icon = "󰌑 ", + hl = "MarkviewPalette2", + }, + + tab = { + icon = "󰌒 ", + hl = "MarkviewPalette3", + }, +}; + + ---@type markview.config.asciidoc return { checkboxes = { @@ -90,6 +147,34 @@ return { ["%.pdf$"] = { icon = " " } }, + keycodes = { + enable = true, + + default = keycodes.default, + + ["^CTRL"] = keycodes.ctrl, + ["^%<[cC]"] = keycodes.ctrl, + + ["^SHIFT"] = keycodes.shift, + ["^%<[sS]"] = keycodes.shift, + + ["^META"] = keycodes.meta, + ["^OPT"] = keycodes.meta, + ["^ALT"] = keycodes.meta, + ["^%<[mM]"] = keycodes.meta, + + ["^SUPER"] = keycodes.super, + ["^%<[dD]"] = keycodes.super, + + ["^CMD"] = keycodes.command, + ["^COMMAND"] = keycodes.command, + + ["CAPS.LOCK"] = keycodes.caps_lock, + ["SPACE"] = keycodes.space, + ["TAB"] = keycodes.tab, + ["ENTER"] = keycodes.enter, + }, + tocs = { enable = true, diff --git a/lua/markview/parsers/asciidoc.lua b/lua/markview/parsers/asciidoc.lua index 3f6b47c1..0a2a8bac 100644 --- a/lua/markview/parsers/asciidoc.lua +++ b/lua/markview/parsers/asciidoc.lua @@ -180,61 +180,53 @@ asciidoc.toc = function (_, _, text, range) }); end ----@param buffer integer ----@param now string Current marker. ----@param last TSNode ----@return boolean -local function is_on_same_level(buffer, now, last) - local _marker = last:child(0); - - if not _marker then - return false; - end - - local marker = vim.treesitter.get_node_text(_marker, buffer, {}); - return marker == now; -end - ---@param buffer integer ---@param TSNode TSNode ---@param text string[] ----@param range markview.parsed.asciidoc.list_items.range -asciidoc._list_item = function (buffer, TSNode, text, range) - local _marker = TSNode:child(0); +---@param range markview.parsed.asciidoc.keycodes.range +asciidoc.keycode = function (buffer, TSNode, text, range) + local _content = TSNode:named_child(1); - if not _marker then + if not _content then return; end - local N = 1; - local prev = TSNode:prev_named_sibling(); - - local marker = vim.treesitter.get_node_text(_marker, buffer, {}); - _, _, _, range.marker_end = _marker:range(); + local content = vim.treesitter.get_node_text(_content, buffer, {}); + range.content = { _content:range() }; - while prev do - -- NOTE: Only one of these node types should appear. - if vim.list_contains({ "unordered_list_item", "ordered_list_item" }, prev:type()) then - if is_on_same_level(buffer, marker, prev) then - N = N + 1; - else - break; - end - end + --[[ + refactor: Range correction - prev = prev:prev_named_sibling(); - end + Block macros end at the start of the next line. + So, we correct the end column & end row. + ]] + range.row_end = range.row_end - 1; + range.col_end = range.col_start + #(text[#text] or ""); asciidoc.insert({ - class = "asciidoc_list_item", - marker = marker, - n = N, + class = "asciidoc_keycode", + content = content, text = text, range = range }); end +---@param buffer integer +---@param now string Current marker. +---@param last TSNode +---@return boolean +local function is_on_same_level(buffer, now, last) + local _marker = last:child(0); + + if not _marker then + return false; + end + + local marker = vim.treesitter.get_node_text(_marker, buffer, {}); + return marker == now; +end + ---@param buffer integer ---@param TSNode TSNode ---@param text string[] @@ -353,6 +345,12 @@ asciidoc.parse = function (buffer, TSTree, from, to) (block_macro_name) @image_keyword (#eq? @image_keyword "image") )) @asciidoc.image + + (block_macro + ( + (block_macro_name) @kbd_keyword + (#eq? @kbd_keyword "kbd") + )) @asciidoc.keycode ]]); if not can_scan then diff --git a/lua/markview/renderers/asciidoc.lua b/lua/markview/renderers/asciidoc.lua index b9f46515..70c87b2f 100644 --- a/lua/markview/renderers/asciidoc.lua +++ b/lua/markview/renderers/asciidoc.lua @@ -86,8 +86,69 @@ asciidoc.image = function (buffer, item) virt_text_pos = "inline", virt_text = { - { config.corner_right or "", utils.set_hl(config.corner_right_hl or config.hl) }, - { config.padding_right or "", utils.set_hl(config.padding_right_hl or config.hl) } + { config.padding_right or "", utils.set_hl(config.padding_right_hl or config.hl) }, + { config.corner_right or "", utils.set_hl(config.corner_right_hl or config.hl) } + }, + + hl_mode = "combine" + }); +end + +---@param buffer integer +---@param item markview.parsed.asciidoc.keycodes +asciidoc.keycode = function (buffer, item) + ---@type markview.config.asciidoc.keycodes? + local main_config = spec.get({ "asciidoc", "keycodes" }, { fallback = nil }); + local range = item.range; + + if not main_config then + return; + end + + ---@type markview.config.asciidoc.images.opts? + local config = utils.match( + main_config, + string.upper(item.content or ""), + { + eval_args = { buffer, item } + } + ); + + if config == nil then + return; + end + + vim.print(config.corner_left == nil) + + utils.set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start, { + end_col = range.content[2], + conceal = "", + + virt_text_pos = "inline", + virt_text = { + { config.corner_left or "", utils.set_hl(config.corner_left_hl or config.hl) }, + { config.padding_left or "", utils.set_hl(config.padding_left_hl or config.hl) }, + { config.icon or "", utils.set_hl(config.icon_hl or config.hl) } + }, + + hl_mode = "combine" + }); + + utils.set_extmark(buffer, asciidoc.ns, range.content[1], range.content[2], { + end_col = range.content[4], end_row = range.content[3], + + hl_group = utils.set_hl(config.hl), + hl_mode = "combine" + }); + + utils.set_extmark(buffer, asciidoc.ns, range.row_end, range.content[4], { + end_col = range.col_end, + conceal = "", + + virt_text_pos = "inline", + virt_text = { + { config.padding_right or "", utils.set_hl(config.padding_right_hl or config.hl) }, + { config.corner_right or "", utils.set_hl(config.corner_right_hl or config.hl) } }, hl_mode = "combine" diff --git a/lua/markview/types/parsers/asciidoc.lua b/lua/markview/types/parsers/asciidoc.lua index bbc051f3..5b89f0b8 100644 --- a/lua/markview/types/parsers/asciidoc.lua +++ b/lua/markview/types/parsers/asciidoc.lua @@ -41,6 +41,27 @@ ------------------------------------------------------------------------------ +---@class markview.parsed.asciidoc.keycodes +--- +---@field class "asciidoc_keycode" +---@field destination string Source of the keycode. +--- +---@field text string[] +---@field range markview.parsed.asciidoc.keycodes.range + + +---@class markview.parsed.asciidoc.keycodes.range +--- +---@field row_start integer +---@field col_start integer +--- +---@field row_end integer +---@field col_end integer +--- +---@field destination integer[] Range of the keycode destination(output of `{ TSNode:range() }`. + +------------------------------------------------------------------------------ + ---@class markview.parsed.asciidoc.list_items --- ---@field class "asciidoc_list_item" diff --git a/lua/markview/types/renderers/asciidoc.lua b/lua/markview/types/renderers/asciidoc.lua index cd035c57..b518e6f2 100644 --- a/lua/markview/types/renderers/asciidoc.lua +++ b/lua/markview/types/renderers/asciidoc.lua @@ -79,6 +79,20 @@ ------------------------------------------------------------------------------ +--- Configuration for keycodes. +---@class markview.config.asciidoc.keycodes +--- +---@field enable boolean Enable rendering of keycodes +--- +---@field default markview.config.asciidoc.keycodes.opts Default configuration for keycodes +---@field [string] markview.config.asciidoc.keycodes.opts Configuration of keycodes whose content matches `string`. **NOTE:** Case-insensitive + + +--[[ Options for a specific keycode type. ]] +---@alias markview.config.asciidoc.keycodes.opts markview.config.__inline + +------------------------------------------------------------------------------ + --- Configuration for list items. ---@class markview.config.asciidoc.list_items --- diff --git a/test/asciidoc.adoc b/test/asciidoc.adoc index 5cdb8411..839644de 100644 --- a/test/asciidoc.adoc +++ b/test/asciidoc.adoc @@ -1,3 +1,5 @@ +kbd::space[] + image::sunset.jpg[] image::sunset.jpg[Sunset] From 5f2cc4681c7bfd1610d7e85c9db6e203db29a284 Mon Sep 17 00:00:00 2001 From: Shawon Date: Wed, 21 Jan 2026 18:47:55 +0600 Subject: [PATCH 25/40] feat(asciidoc): Added Literal block support --- lua/markview/config/asciidoc.lua | 18 ++ lua/markview/parsers/asciidoc.lua | 42 +++ lua/markview/renderers/asciidoc.lua | 303 +++++++++++++++++++++- lua/markview/types/parsers/asciidoc.lua | 28 +- lua/markview/types/renderers/asciidoc.lua | 21 ++ test/asciidoc.adoc | 17 ++ 6 files changed, 426 insertions(+), 3 deletions(-) diff --git a/lua/markview/config/asciidoc.lua b/lua/markview/config/asciidoc.lua index 0238b68a..709bfa86 100644 --- a/lua/markview/config/asciidoc.lua +++ b/lua/markview/config/asciidoc.lua @@ -86,6 +86,24 @@ return { ["d"] = { text = "󰔳", hl = "MarkviewCheckboxUnchecked" }, }, + literal_blocks = { + enable = true, + + hl = "MarkviewCode", + + sign = "hi", + + label = "Hello ", + label_direction = "right", + label_hl = nil, + + min_width = 60, + pad_amount = 2, + pad_char = " ", + + style = "block", + }, + document_titles = { enable = true, diff --git a/lua/markview/parsers/asciidoc.lua b/lua/markview/parsers/asciidoc.lua index 0a2a8bac..8496fada 100644 --- a/lua/markview/parsers/asciidoc.lua +++ b/lua/markview/parsers/asciidoc.lua @@ -24,6 +24,46 @@ asciidoc.insert = function (data) table.insert(asciidoc.sorted[data.class], data); end +---@param buffer integer +---@param TSNode TSNode +---@param text string[] +---@param range markview.parsed.asciidoc.literal_blocks.range +asciidoc.literal_block = function (buffer, TSNode, text, range) + local _delimiters = { + TSNode:named_child(0), + TSNode:named_child(2), + }; + + if _delimiters[1] then + range.start_delim = { _delimiters[1]:range(); }; + end + + if _delimiters[1] then + range.end_delim = { _delimiters[2]:range(); }; + end + + local uses_tab = false; + + for _, line in ipairs(text) do + if string.match(line, "\t") then + uses_tab = true; + break; + end + end + + asciidoc.insert({ + class = "asciidoc_literal_block", + delimiters = { + _delimiters[1] and vim.treesitter.get_node_text(_delimiters[1], buffer, {}) or "", + _delimiters[2] and vim.treesitter.get_node_text(_delimiters[2], buffer, {}) or "", + }, + uses_tab = uses_tab, + + text = text, + range = range + }); +end + ---@param buffer integer ---@param TSNode TSNode ---@param text string[] @@ -351,6 +391,8 @@ asciidoc.parse = function (buffer, TSTree, from, to) (block_macro_name) @kbd_keyword (#eq? @kbd_keyword "kbd") )) @asciidoc.keycode + + (literal_block) @asciidoc.literal_block ]]); if not can_scan then diff --git a/lua/markview/renderers/asciidoc.lua b/lua/markview/renderers/asciidoc.lua index 70c87b2f..8935b2a6 100644 --- a/lua/markview/renderers/asciidoc.lua +++ b/lua/markview/renderers/asciidoc.lua @@ -5,6 +5,307 @@ local spec = require("markview.spec"); asciidoc.ns = vim.api.nvim_create_namespace("markview/asciidoc"); +---@param buffer integer +---@param item markview.parsed.asciidoc.literal_blocks +asciidoc.literal_block = function (buffer, item) + ---@type markview.config.asciidoc.literal_blocks? + local config = spec.get({ "asciidoc", "literal_blocks" }, { fallback = nil, eval_args = { buffer, item } }); + + local delims = item.delimiters; + local range = item.range; + + if not config then + return; + end + + local label = { config.label, config.label_hl or config.hl }; + local win = utils.buf_getwin(buffer); + + --[[ *Basic* rendering of `code blocks`. ]] + local function render_simple() + ---|fS + + local conceal_from = range.start_delim[2] + #string.match(item.delimiters[1], "^%s*"); + local conceal_to = #delims[1]; + + if config.label_direction == nil or config.label_direction == "left" then + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, conceal_from, { + undo_restore = false, invalidate = true, + + end_col = conceal_to, + conceal = "", + + sign_text = config.sign, + sign_hl_group = utils.set_hl(config.sign_hl), + + virt_text_pos = "inline", + virt_text = { label }, + + line_hl_group = utils.set_hl(config.hl) + }); + else + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, conceal_from, { + undo_restore = false, invalidate = true, + + end_col = conceal_to, + conceal = "", + + sign_text = config.sign, + sign_hl_group = utils.set_hl(config.sign_hl), + + virt_text_pos = "right_align", + virt_text = { label }, + + line_hl_group = utils.set_hl(config.hl) + }); + end + + --- Background + for l = range.row_start + 1, range.row_end - 1 do + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, l, 0, { + undo_restore = false, invalidate = true, + end_row = l, + + line_hl_group = config.hl + }); + end + + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_end, (range.col_start + #item.text[#item.text]) - #delims[2], { + undo_restore = false, invalidate = true, + end_col = range.col_start + #item.text[#item.text], + conceal = "", + + line_hl_group = utils.set_hl(config.hl) + }); + + ---|fE + end + + --- Renders block style code blocks. + local function render_block () + ---|fS + + ---|fS "chunk: Calculate various widths" + + local pad_amount = config.pad_amount or 0; + local block_width = config.min_width or 60; + + local pad_char = config.pad_char or " "; + + ---@type integer[] Visual width of lines. + local line_widths = {}; + + for l, line in ipairs(item.text) do + if l ~= 1 and l ~= #item.text then + table.insert(line_widths, vim.fn.strdisplaywidth(line)); + + if vim.fn.strdisplaywidth(line) > (block_width - (2 * pad_amount)) then + block_width = vim.fn.strdisplaywidth(line) + (2 * pad_amount); + end + end + end + + local label_width = utils.virt_len({ label }); + + ---|fE + + local delim_conceal_from = range.start_delim[2] + #string.match(item.delimiters[1], "^%s*"); + local conceal_to = #delims[1]; + + ---|fS "chunk: Top border" + + local visible_info = string.sub(item.text[1], (conceal_to + 1) - range.col_start); + local left_padding = visible_info ~= "" and 1 or pad_amount; + + local pad_width = vim.fn.strdisplaywidth( + string.rep(pad_char, left_padding) + ); + + -- Hide the leading `backticks`s. + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, delim_conceal_from, { + undo_restore = false, invalidate = true, + + end_col = conceal_to, + conceal = "", + + sign_text = config.sign, + sign_hl_group = utils.set_hl(config.sign_hl), + }); + + if config.label_direction == "right" then + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, delim_conceal_from, { + virt_text_pos = "inline", + virt_text = { + { + string.rep(" " or pad_char, left_padding), + utils.set_hl(config.hl) + } + } + }); + else + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start + #item.text[1], { + virt_text_pos = "inline", + virt_text = { + { + string.rep(" " or pad_char, left_padding), + utils.set_hl(config.hl) + } + } + }); + end + + ---|fS "chunk: Prettify info" + + -- Calculating the amount of spacing to add, + -- 1. Used space = label width(`label_width`) + padding size(`pad_width`). + -- 2. Total block width - Used space + local spacing = block_width - (label_width + pad_width); + + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start + #item.text[1], { + virt_text_pos = "inline", + virt_text = { + { + string.rep(pad_char, spacing), + utils.set_hl(config.hl) + }, + } + }); + + ---|fE + + ---|fS "chunk: Place label" + + local top_border = { + }; + + if config.label_direction == "right" then + top_border.col_start = range.start_delim[2] + #item.text[1]; + else + top_border.col_start = range.start_delim[4]; + end + + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, top_border.col_start, { + virt_text_pos = "inline", + virt_text = { label } + }); + + ---|fE + + ---|fE + + --- Line padding + for l, width in ipairs(line_widths) do + local line = item.text[l + 1]; + + if width ~= 0 then + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start + l, line ~= "" and range.col_start or 0, { + undo_restore = false, invalidate = true, + + virt_text_pos = "inline", + virt_text = { + { + string.rep(" ", pad_amount), + utils.set_hl(config.hl --[[ @as string ]]) + } + }, + }); + + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start + l, range.col_start + #line, { + undo_restore = false, invalidate = true, + + virt_text_pos = "inline", + virt_text = { + { + string.rep(" ", math.max(0, block_width - (( 2 * pad_amount) + width))), + utils.set_hl(config.hl --[[ @as string ]]) + }, + { + string.rep(" ", pad_amount), + utils.set_hl(config.hl --[[ @as string ]]) + } + }, + }); + + --- Background + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start + l, range.col_start, { + undo_restore = false, invalidate = true, + end_col = range.col_start + #line, + + hl_group = utils.set_hl(config.hl --[[ @as string ]]) + }); + else + local buf_line = vim.api.nvim_buf_get_lines(buffer, range.row_start + l, range.row_start + l + 1, false)[1]; + + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start + l, #buf_line, { + undo_restore = false, invalidate = true, + + virt_text_pos = "inline", + virt_text = { + { + string.rep(" ", math.max(0, range.col_start - #buf_line)) + }, + { + string.rep(" ", pad_amount), + utils.set_hl(config.hl --[[ @as string ]]) + }, + { + string.rep(" ", math.max(0, block_width - (2 * pad_amount))), + utils.set_hl(config.hl --[[ @as string ]]) + }, + { + string.rep(" ", pad_amount), + utils.set_hl(config.hl --[[ @as string ]]) + }, + }, + }); + end + end + + --- Render bottom + if item.delimiters[2] then + local end_delim_conceal_from = range.end_delim[2] + #string.match(item.delimiters[2], "^%s*"); + + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_end, end_delim_conceal_from, { + undo_restore = false, invalidate = true, + end_col = range.col_start + #item.text[#item.text], + conceal = "" + }); + + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_end, end_delim_conceal_from, { + undo_restore = false, invalidate = true, + + virt_text_pos = "inline", + virt_text = { + { + string.rep(" ", block_width), + utils.set_hl(config.hl) + } + } + }); + else + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_end, range.col_start, { + undo_restore = false, invalidate = true, + + virt_text_pos = "inline", + virt_text = { + { + string.rep(" ", block_width), + utils.set_hl(config.hl) + } + } + }); + end + + ---|fE + end + + if not win or config.style == "simple" or item.uses_tab or ( vim.o.wrap == true or vim.wo[win].wrap == true ) then + render_simple(); + elseif config.style == "block" then + render_block() + end +end + ---@param buffer integer ---@param item markview.parsed.asciidoc.document_titles asciidoc.document_attribute = function (buffer, item) @@ -105,7 +406,7 @@ asciidoc.keycode = function (buffer, item) return; end - ---@type markview.config.asciidoc.images.opts? + ---@type markview.config.asciidoc.keycodes.opts? local config = utils.match( main_config, string.upper(item.content or ""), diff --git a/lua/markview/types/parsers/asciidoc.lua b/lua/markview/types/parsers/asciidoc.lua index 5b89f0b8..dc02fc36 100644 --- a/lua/markview/types/parsers/asciidoc.lua +++ b/lua/markview/types/parsers/asciidoc.lua @@ -39,12 +39,36 @@ --- ---@field destination integer[] Range of the image destination(output of `{ TSNode:range() }`. +------------------------------------------------------------------------------ + +---@class markview.parsed.asciidoc.literal_blocks +--- +---@field class "asciidoc_literal_block" +---@field delimiters [ string, string ] Block delimiters. +---@field uses_tab boolean Are there tabs in the text? +--- +---@field text string[] +---@field range markview.parsed.asciidoc.literal_blocks.range + + +---@class markview.parsed.asciidoc.literal_blocks.range +--- +---@field row_start integer +---@field col_start integer +--- +---@field row_end integer +---@field col_end integer +--- +---@field start_delim integer[] Range of the block start delimiter(output of `{ TSNode:range() }`. +---@field end_delim integer[] Range of the block end delimiter(output of `{ TSNode:range() }`. + + ------------------------------------------------------------------------------ ---@class markview.parsed.asciidoc.keycodes --- ---@field class "asciidoc_keycode" ----@field destination string Source of the keycode. +---@field content string Text inside the keycode. --- ---@field text string[] ---@field range markview.parsed.asciidoc.keycodes.range @@ -58,7 +82,7 @@ ---@field row_end integer ---@field col_end integer --- ----@field destination integer[] Range of the keycode destination(output of `{ TSNode:range() }`. +---@field content integer[] Range of the keycode destination(output of `{ TSNode:range() }`. ------------------------------------------------------------------------------ diff --git a/lua/markview/types/renderers/asciidoc.lua b/lua/markview/types/renderers/asciidoc.lua index b518e6f2..8b540487 100644 --- a/lua/markview/types/renderers/asciidoc.lua +++ b/lua/markview/types/renderers/asciidoc.lua @@ -79,6 +79,27 @@ ------------------------------------------------------------------------------ +--- Configuration for image links. +---@class markview.config.asciidoc.literal_blocks +--- +---@field enable boolean Enable rendering of image links +---@field style? "simple" | "block" +--- +---@field pad_amount? integer +---@field pad_char? string +---@field min_width? integer +--- +---@field label? string +---@field label_hl? string +---@field label_direction? "left" | "right" +--- +---@field sign? string +---@field sign_hl? string +--- +---@field hl? string + +------------------------------------------------------------------------------ + --- Configuration for keycodes. ---@class markview.config.asciidoc.keycodes --- diff --git a/test/asciidoc.adoc b/test/asciidoc.adoc index 839644de..381e5bfd 100644 --- a/test/asciidoc.adoc +++ b/test/asciidoc.adoc @@ -1,3 +1,20 @@ +.... +error: 1954 Forbidden search +absolutely fatal: operation lost in the dodecahedron of doom + +Would you like to try again? y/n +.... + +.Some Ruby code +[source,ruby] +---- +require 'sinatra' + +get '/hi' do + "Hello World!" +end +---- + kbd::space[] image::sunset.jpg[] From 8cc85d445a31feeb8bb0d792726d1fba1b761bb0 Mon Sep 17 00:00:00 2001 From: Shawon Date: Thu, 22 Jan 2026 21:46:11 +0600 Subject: [PATCH 26/40] feat(asciidoc): Added admonitions support --- lua/markview/config/asciidoc.lua | 53 +++++++++++++++ lua/markview/parsers/asciidoc.lua | 30 ++++++++- lua/markview/renderers/asciidoc.lua | 79 ++++++++++++++++++++++- lua/markview/types/parsers/asciidoc.lua | 23 +++++++ lua/markview/types/renderers/asciidoc.lua | 32 +++++++++ test/asciidoc.adoc | 26 ++++++++ 6 files changed, 240 insertions(+), 3 deletions(-) diff --git a/lua/markview/config/asciidoc.lua b/lua/markview/config/asciidoc.lua index 709bfa86..cc0d7dbe 100644 --- a/lua/markview/config/asciidoc.lua +++ b/lua/markview/config/asciidoc.lua @@ -1,5 +1,7 @@ ---@type [ string, markview.config.asciidoc.keycodes ] local keycodes = { + ---|fS + default = { padding_left = " ", padding_right = " ", @@ -52,11 +54,62 @@ local keycodes = { icon = "󰌒 ", hl = "MarkviewPalette3", }, + + ---|fE }; ---@type markview.config.asciidoc return { + admonitions = { + enable = true, + + default = { + padding_left = " ", + padding_right = " ", + + icon = " ", + + hl = "MarkviewPalette5", + }, + + important = { + padding_left = " ", + padding_right = " ", + + icon = " ", + + hl = "MarkviewPalette1", + }, + + tip = { + padding_left = " ", + padding_right = " ", + + icon = " ", + + hl = "MarkviewPalette4", + }, + + caution = { + padding_left = " ", + padding_right = " ", + + icon = " ", + + hl = "MarkviewPalette7", + }, + + warn = { + padding_left = " ", + padding_right = " ", + + icon = " ", + + hl = "MarkviewPalette3", + }, + }, + checkboxes = { enable = true, diff --git a/lua/markview/parsers/asciidoc.lua b/lua/markview/parsers/asciidoc.lua index 8496fada..92409ac7 100644 --- a/lua/markview/parsers/asciidoc.lua +++ b/lua/markview/parsers/asciidoc.lua @@ -24,6 +24,32 @@ asciidoc.insert = function (data) table.insert(asciidoc.sorted[data.class], data); end +---@param buffer integer +---@param _ TSNode +---@param text string[] +---@param range markview.parsed.asciidoc.admonitions.range +asciidoc.admonition = function (buffer, _, text, range) + local before = vim.api.nvim_buf_get_text(buffer, range.row_start, 0, range.row_start, range.col_start, {})[1] or ""; + local kind = string.match(before, "[A-Z]+$"); + + range.kind = { + range.row_start, + range.col_start - #(kind or ""), + + range.row_start, + range.col_start + 1, + }; + range.col_start = range.kind[2]; + + asciidoc.insert({ + class = "asciidoc_admonition", + kind = kind, + + text = text, + range = range + }); +end + ---@param buffer integer ---@param TSNode TSNode ---@param text string[] @@ -392,7 +418,9 @@ asciidoc.parse = function (buffer, TSTree, from, to) (#eq? @kbd_keyword "kbd") )) @asciidoc.keycode - (literal_block) @asciidoc.literal_block + (literal_block) @asciidoc.literal_block + + (admonition) @asciidoc.admonition ]]); if not can_scan then diff --git a/lua/markview/renderers/asciidoc.lua b/lua/markview/renderers/asciidoc.lua index 8935b2a6..0bc9fa14 100644 --- a/lua/markview/renderers/asciidoc.lua +++ b/lua/markview/renderers/asciidoc.lua @@ -5,6 +5,83 @@ local spec = require("markview.spec"); asciidoc.ns = vim.api.nvim_create_namespace("markview/asciidoc"); +---@param buffer integer +---@param item markview.parsed.asciidoc.admonitions +asciidoc.admonition = function (buffer, item) + ---|fS + + ---@type markview.config.asciidoc.admonitions? + local main_config = spec.get({ "asciidoc", "admonitions" }, { fallback = nil }); + + if not main_config then + return; + end + + ---@type markview.config.asciidoc.admonitions.opts? + local config = utils.match(main_config, item.kind, { + case_insensitive = true, + + ignore_keys = { "enable" }, + eval_args = { buffer, item } + }); + + if not config then + return; + end + + local range = item.range; + local row_end = range.kind[3]; + local col_end = range.kind[4]; + + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start, { + undo_restore = false, invalidate = true, + + virt_text_pos = "inline", + virt_text = { + { config.corner_left or "", utils.set_hl(config.corner_left_hl or config.hl) }, + { config.padding_left or "", utils.set_hl(config.padding_left_hl or config.hl) }, + + { config.icon or "", utils.set_hl(config.icon_hl or config.hl) } + }, + + hl_mode = "combine" + }); + + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.kind[1], range.kind[2], { + undo_restore = false, invalidate = true, + end_row = row_end, + end_col = col_end, + + hl_group = utils.set_hl(config.hl) + }); + + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, row_end, col_end - 1, { + undo_restore = false, invalidate = true, + end_col = col_end, + conceal = "", + + virt_text_pos = "inline", + virt_text = { + { config.padding_right or "", utils.set_hl(config.padding_right_hl or config.hl) }, + { config.corner_right or "", utils.set_hl(config.corner_right_hl or config.hl) } + }, + + hl_mode = "combine" + }); + + if config.desc_hl then + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, row_end, col_end, { + undo_restore = false, invalidate = true, + end_row = range.row_end, + end_col = range.col_end, + + hl_group = utils.set_hl(config.desc_hl) + }); + end + + ---|fE +end + ---@param buffer integer ---@param item markview.parsed.asciidoc.literal_blocks asciidoc.literal_block = function (buffer, item) @@ -419,8 +496,6 @@ asciidoc.keycode = function (buffer, item) return; end - vim.print(config.corner_left == nil) - utils.set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start, { end_col = range.content[2], conceal = "", diff --git a/lua/markview/types/parsers/asciidoc.lua b/lua/markview/types/parsers/asciidoc.lua index dc02fc36..75c96f9d 100644 --- a/lua/markview/types/parsers/asciidoc.lua +++ b/lua/markview/types/parsers/asciidoc.lua @@ -2,6 +2,27 @@ ------------------------------------------------------------------------------ +---@class markview.parsed.asciidoc.admonitions +--- +---@field class "asciidoc_admonition" +---@field kind string Type of admonition. +--- +---@field text string[] +---@field range markview.parsed.asciidoc.admonitions.range + + +---@class markview.parsed.asciidoc.admonitions.range +--- +---@field row_start integer +---@field col_start integer +--- +---@field row_end integer +---@field col_end integer +--- +---@field kind integer[] Range of the admonition kind(output of `{ TSNode:range() }`. + +------------------------------------------------------------------------------ + ---@class markview.parsed.asciidoc.document_attributes --- ---@field class "asciidoc_document_attribute" @@ -146,6 +167,7 @@ ------------------------------------------------------------------------------ ---@alias markview.parsed.asciidoc +---| markview.parsed.asciidoc.admonitions ---| markview.parsed.asciidoc.document_attributes ---| markview.parsed.asciidoc.document_titles ---| markview.parsed.asciidoc.list_items @@ -156,6 +178,7 @@ ---@class markview.parsed.asciidoc_sorted --- +---@field admonitions markview.parsed.asciidoc.admonitions[] ---@field document_attributes markview.parsed.asciidoc.document_attributes[] ---@field document_titles markview.parsed.asciidoc.document_titles[] ---@field list_items markview.parsed.asciidoc.list_items[] diff --git a/lua/markview/types/renderers/asciidoc.lua b/lua/markview/types/renderers/asciidoc.lua index 8b540487..cfb9b733 100644 --- a/lua/markview/types/renderers/asciidoc.lua +++ b/lua/markview/types/renderers/asciidoc.lua @@ -2,6 +2,37 @@ ------------------------------------------------------------------------------ +--- Configuration for checkboxes. +---@class markview.config.asciidoc.admonitions +--- +---@field enable boolean Enable rendering of admonitions. +--- +---@field default markview.config.asciidoc.admonitions.opts Default configuration for admonitions. +---@field [string] markview.config.asciidoc.admonitions.opts Configuration for `[string]` admonitions. + + +---@class markview.config.asciidoc.admonitions.opts +--- +---@field corner_left? string Left corner. +---@field corner_left_hl? string Highlight group for the left corner. +--- +---@field padding_left? string Left padding(added after `corner_left`). +---@field padding_left_hl? string Highlight group for the left padding. +--- +---@field icon? string Icon(added after `padding_left`). +---@field icon_hl? string Highlight group for the icon. +--- +---@field hl? string Default highlight group(used by `*_hl` options when they are not set). +---@field desc_hl? string Highlight group for the `description`. +--- +---@field padding_right? string Right padding. +---@field padding_right_hl? string Highlight group for the right padding. +--- +---@field corner_right? string Right corner(added after `padding_right`). +---@field corner_right_hl? string Highlight group for the right corner. + +------------------------------------------------------------------------------ + --- Configuration for checkboxes. ---@class markview.config.asciidoc.checkboxes --- @@ -197,6 +228,7 @@ ---@class markview.config.asciidoc --- +---@field admonitions markview.config.asciidoc.admonitions ---@field checkboxes markview.config.asciidoc.checkboxes ---@field document_attributes markview.config.asciidoc.document_attributes ---@field document_titles markview.config.asciidoc.document_titles diff --git a/test/asciidoc.adoc b/test/asciidoc.adoc index 381e5bfd..38f822a9 100644 --- a/test/asciidoc.adoc +++ b/test/asciidoc.adoc @@ -1,3 +1,29 @@ +NOTE: An admonition draws the reader's attention to auxiliary information. + +Here are the other built-in admonition types: + +IMPORTANT: Don't forget the children! + +TIP: Look for the warp zone under the bridge. + +CAUTION: Slippery when wet. + +WARNING: The software you're about to use is untested. + +IMPORTANT: Sign off before stepping away from your computer. + +[NOTE] +==== +An admonition block may contain compound content. + +.A list +- one +- two +- three + +Another paragraph. +==== + .... error: 1954 Forbidden search absolutely fatal: operation lost in the dodecahedron of doom From 30da3855fc6c264e79a413936c50ebdf06f767e8 Mon Sep 17 00:00:00 2001 From: Shawon Date: Thu, 22 Jan 2026 22:05:56 +0600 Subject: [PATCH 27/40] feat(asciidoc): Added horizontal rule support --- lua/markview/config/asciidoc.lua | 68 +++++++++++++++++++++++ lua/markview/parsers/asciidoc.lua | 12 ++++ lua/markview/renderers/asciidoc.lua | 63 +++++++++++++++++++++ lua/markview/types/parsers/asciidoc.lua | 9 +++ lua/markview/types/renderers/asciidoc.lua | 35 ++++++++++++ test/asciidoc.adoc | 3 + 6 files changed, 190 insertions(+) diff --git a/lua/markview/config/asciidoc.lua b/lua/markview/config/asciidoc.lua index cc0d7dbe..fa347b99 100644 --- a/lua/markview/config/asciidoc.lua +++ b/lua/markview/config/asciidoc.lua @@ -139,6 +139,74 @@ return { ["d"] = { text = "󰔳", hl = "MarkviewCheckboxUnchecked" }, }, + horizontal_rules = { + enable = true, + + parts = { + { + type = "repeating", + direction = "left", + + repeat_amount = function (buffer) + local utils = require("markview.utils"); + local window = utils.buf_getwin(buffer) + + local width = vim.api.nvim_win_get_width(window) + local textoff = vim.fn.getwininfo(window)[1].textoff; + + return math.floor((width - textoff - 3) / 2); + end, + + text = "─", + + hl = { + "MarkviewGradient1", "MarkviewGradient1", + "MarkviewGradient2", "MarkviewGradient2", + "MarkviewGradient3", "MarkviewGradient3", + "MarkviewGradient4", "MarkviewGradient4", + "MarkviewGradient5", "MarkviewGradient5", + "MarkviewGradient6", "MarkviewGradient6", + "MarkviewGradient7", "MarkviewGradient7", + "MarkviewGradient8", "MarkviewGradient8", + "MarkviewGradient9", "MarkviewGradient9" + } + }, + { + type = "text", + + text = "  ", + hl = "MarkviewIcon3Fg" + }, + { + type = "repeating", + direction = "right", + + repeat_amount = function (buffer) --[[@as function]] + local utils = require("markview.utils"); + local window = utils.buf_getwin(buffer) + + local width = vim.api.nvim_win_get_width(window) + local textoff = vim.fn.getwininfo(window)[1].textoff; + + return math.ceil((width - textoff - 3) / 2); + end, + + text = "─", + hl = { + "MarkviewGradient1", "MarkviewGradient1", + "MarkviewGradient2", "MarkviewGradient2", + "MarkviewGradient3", "MarkviewGradient3", + "MarkviewGradient4", "MarkviewGradient4", + "MarkviewGradient5", "MarkviewGradient5", + "MarkviewGradient6", "MarkviewGradient6", + "MarkviewGradient7", "MarkviewGradient7", + "MarkviewGradient8", "MarkviewGradient8", + "MarkviewGradient9", "MarkviewGradient9" + } + } + } + }, + literal_blocks = { enable = true, diff --git a/lua/markview/parsers/asciidoc.lua b/lua/markview/parsers/asciidoc.lua index 92409ac7..54261410 100644 --- a/lua/markview/parsers/asciidoc.lua +++ b/lua/markview/parsers/asciidoc.lua @@ -137,6 +137,17 @@ asciidoc.doc_title = function (_, _, text, range) }); end +---@param text string[] +---@param range markview.parsed.range +asciidoc.hr = function (_, _, text, range) + asciidoc.insert({ + class = "asciidoc_hr", + + text = text, + range = range + }); +end + ---@param buffer integer ---@param TSNode TSNode ---@param text string[] @@ -421,6 +432,7 @@ asciidoc.parse = function (buffer, TSTree, from, to) (literal_block) @asciidoc.literal_block (admonition) @asciidoc.admonition + (breaks) @asciidoc.hr ]]); if not can_scan then diff --git a/lua/markview/renderers/asciidoc.lua b/lua/markview/renderers/asciidoc.lua index 0bc9fa14..8f0e0d07 100644 --- a/lua/markview/renderers/asciidoc.lua +++ b/lua/markview/renderers/asciidoc.lua @@ -82,6 +82,69 @@ asciidoc.admonition = function (buffer, item) ---|fE end +--- Renders horizontal rules/line breaks. +---@param buffer integer +---@param item markview.parsed.asciidoc.hrs +asciidoc.hr = function (buffer, item) + ---@type markview.config.asciidoc.hrs? + local config = spec.get({ "asciidoc", "horizontal_rules" }, { fallback = nil, eval_args = { buffer, item } }); + local range = item.range; + + if not config then + return; + end + + local virt_text = {}; + local function val(opt, index, wrap) + if vim.islist(opt) == false then + return opt; + elseif #opt < index then + if wrap == true then + local mod = index % #opt; + return mod == 0 and opt[#opt] or opt[mod]; + else + return opt[#opt]; + end + elseif index < 0 then + return opt[1]; + end + + return opt[index]; + end + + for _, part in ipairs(config.parts) do + if part.type == "text" then + table.insert(virt_text, { part.text, utils.set_hl(part.hl --[[ @as string ]]) }); + elseif part.type == "repeating" then + local rep = spec.get({ "repeat_amount" }, { source = part, fallback = 1, eval_args = { buffer, item } }); + local hl_rep = spec.get({ "repeat_hl" }, { source = part, fallback = false, eval_args = { buffer, item } }); + local txt_rep = spec.get({ "repeat_text" }, { source = part, fallback = false, eval_args = { buffer, item } }); + + for r = 1, rep, 1 do + if part.direction == "right" then + table.insert(virt_text, { + val(part.text, (rep - r) + 1, txt_rep), + val(part.hl, (rep - r) + 1, hl_rep) + }); + else + table.insert(virt_text, { + val(part.text, r, txt_rep), + val(part.hl, r, hl_rep) + }); + end + end + end + end + + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, 0, { + undo_restore = false, invalidate = true, + virt_text_pos = "overlay", + virt_text = virt_text, + + hl_mode = "combine" + }); +end + ---@param buffer integer ---@param item markview.parsed.asciidoc.literal_blocks asciidoc.literal_block = function (buffer, item) diff --git a/lua/markview/types/parsers/asciidoc.lua b/lua/markview/types/parsers/asciidoc.lua index 75c96f9d..bd0d83b6 100644 --- a/lua/markview/types/parsers/asciidoc.lua +++ b/lua/markview/types/parsers/asciidoc.lua @@ -41,6 +41,15 @@ ------------------------------------------------------------------------------ +---@class markview.parsed.asciidoc.hrs +--- +---@field class "asciidoc_hr" +--- +---@field text string[] +---@field range markview.parsed.range + +------------------------------------------------------------------------------ + ---@class markview.parsed.asciidoc.images --- ---@field class "asciidoc_image" diff --git a/lua/markview/types/renderers/asciidoc.lua b/lua/markview/types/renderers/asciidoc.lua index cfb9b733..035ce7ba 100644 --- a/lua/markview/types/renderers/asciidoc.lua +++ b/lua/markview/types/renderers/asciidoc.lua @@ -73,6 +73,41 @@ ------------------------------------------------------------------------------ +--- Configuration for horizontal rules. +---@class markview.config.asciidoc.hrs +--- +---@field enable boolean Enable preview of horizontal rules. +---@field parts markview.config.asciidoc.hrs.part[] Parts for the horizontal rules. + + +---@alias markview.config.asciidoc.hrs.part +---| markview.config.asciidoc.hrs.text +---| markview.config.asciidoc.hrs.repeating + + +---@class markview.config.asciidoc.hrs.text +--- +---@field type "text" Part name. +--- +---@field hl? string Highlight group for this part. +---@field text string Text to show. + + +---@class markview.config.asciidoc.hrs.repeating +--- +---@field type "repeating" Part name. +--- +---@field direction "left" | "right" Direction from which the highlight groups are applied from. +--- +---@field repeat_amount integer | fun(buffer: integer, item: markview.parsed.asciidoc.hrs): integer How many times to repeat the text. +---@field repeat_hl? boolean Whether to repeat the highlight groups. +---@field repeat_text? boolean Whether to repeat the text. +--- +---@field text string | string[] Text to repeat. +---@field hl? string | string[] Highlight group for the text. + +------------------------------------------------------------------------------ + --- Configuration for image links. ---@class markview.config.asciidoc.images --- diff --git a/test/asciidoc.adoc b/test/asciidoc.adoc index 38f822a9..3ae10628 100644 --- a/test/asciidoc.adoc +++ b/test/asciidoc.adoc @@ -1,3 +1,6 @@ +''' + + NOTE: An admonition draws the reader's attention to auxiliary information. Here are the other built-in admonition types: From a96d7a6316fe6c00524675984d55e953956d2cba Mon Sep 17 00:00:00 2001 From: Shawon Date: Thu, 22 Jan 2026 23:32:08 +0600 Subject: [PATCH 28/40] refactor(asciidoc, asciidoc_inline): Clean up --- lua/markview/parsers/asciidoc.lua | 372 ++++--- lua/markview/parsers/asciidoc_inline.lua | 187 +++- lua/markview/renderers/asciidoc.lua | 913 +++++++++--------- lua/markview/renderers/asciidoc/tostring.lua | 5 + lua/markview/renderers/asciidoc_inline.lua | 29 +- lua/markview/types/parsers/asciidoc.lua | 57 +- .../types/parsers/asciidoc_inline.lua | 13 +- 7 files changed, 939 insertions(+), 637 deletions(-) diff --git a/lua/markview/parsers/asciidoc.lua b/lua/markview/parsers/asciidoc.lua index 54261410..4ff3d392 100644 --- a/lua/markview/parsers/asciidoc.lua +++ b/lua/markview/parsers/asciidoc.lua @@ -5,11 +5,12 @@ local asciidoc = {}; asciidoc.data = {}; --- Queried contents ----@type table[] +---@type markview.parsed.asciidoc[] asciidoc.content = {}; --- Queried contents, but sorted ----@type { [string]: table } +---@type markview.parsed.asciidoc_sorted +---@diagnostic disable-next-line: missing-fields asciidoc.sorted = {} --- Wrapper for `table.insert()`. @@ -24,11 +25,20 @@ asciidoc.insert = function (data) table.insert(asciidoc.sorted[data.class], data); end +--[[ +Admonitions. + +```asciidoc +NOTE: Some note. +``` +]] ---@param buffer integer ---@param _ TSNode ---@param text string[] ---@param range markview.parsed.asciidoc.admonitions.range asciidoc.admonition = function (buffer, _, text, range) + ---|fS + local before = vim.api.nvim_buf_get_text(buffer, range.row_start, 0, range.row_start, range.col_start, {})[1] or ""; local kind = string.match(before, "[A-Z]+$"); @@ -48,58 +58,30 @@ asciidoc.admonition = function (buffer, _, text, range) text = text, range = range }); -end - ----@param buffer integer ----@param TSNode TSNode ----@param text string[] ----@param range markview.parsed.asciidoc.literal_blocks.range -asciidoc.literal_block = function (buffer, TSNode, text, range) - local _delimiters = { - TSNode:named_child(0), - TSNode:named_child(2), - }; - - if _delimiters[1] then - range.start_delim = { _delimiters[1]:range(); }; - end - - if _delimiters[1] then - range.end_delim = { _delimiters[2]:range(); }; - end - - local uses_tab = false; - - for _, line in ipairs(text) do - if string.match(line, "\t") then - uses_tab = true; - break; - end - end - - asciidoc.insert({ - class = "asciidoc_literal_block", - delimiters = { - _delimiters[1] and vim.treesitter.get_node_text(_delimiters[1], buffer, {}) or "", - _delimiters[2] and vim.treesitter.get_node_text(_delimiters[2], buffer, {}) or "", - }, - uses_tab = uses_tab, - text = text, - range = range - }); + ---|fE end +--[[ +Document attributes. + +```asciidoc +:toc-title: Some title +``` +]] ---@param buffer integer ---@param TSNode TSNode ---@param text string[] ---@param range markview.parsed.range asciidoc.doc_attr = function (buffer, TSNode, text, range) + ---|fS + local _name = TSNode:named_child(1) --[[@as TSNode]]; local name = vim.treesitter.get_node_text(_name, buffer, {}); local _value = TSNode:named_child(3); + -- NOTE: Handle special attributes first. if name == "toc" then return; elseif name == "toc-title" and _value then @@ -122,8 +104,17 @@ asciidoc.doc_attr = function (buffer, TSNode, text, range) text = text, range = range }); + + ---|fE end +--[[ +Document title. + +```asciidoc += Some title +``` +]] ---@param text string[] ---@param range markview.parsed.range asciidoc.doc_title = function (_, _, text, range) @@ -137,6 +128,13 @@ asciidoc.doc_title = function (_, _, text, range) }); end +--[[ +Horizontal rule or thematic break + +```asciidoc +''' +``` +]] ---@param text string[] ---@param range markview.parsed.range asciidoc.hr = function (_, _, text, range) @@ -148,55 +146,22 @@ asciidoc.hr = function (_, _, text, range) }); end ----@param buffer integer ----@param TSNode TSNode ----@param text string[] ----@param range markview.parsed.range -asciidoc.section_title = function (buffer, TSNode, text, range) - local _marker = TSNode:child(0); - - if not _marker then - return; - end - - local marker = vim.treesitter.get_node_text(_marker, buffer, {}); - local prev = TSNode:prev_named_sibling(); - - if prev then - local prev_text = vim.treesitter.get_node_text(prev, buffer, {}); +--[[ +Image. - if prev:type() == "element_attr" and prev_text == "[discrete]" then - goto dont_add_to_toc; - end - end - - if not asciidoc.data.toc_entries then - asciidoc.data.toc_entries = {}; - end - - table.insert(asciidoc.data.toc_entries, { - depth = (#marker or 1) - 1, - text = string.gsub(text[1] or "", "^[=%s]+", ""), - - range = vim.deepcopy(range, true), - } --[[@as markview.parser.asciidoc.data.toc_entry]]); - - ::dont_add_to_toc:: - - asciidoc.insert({ - class = "asciidoc_section_title", - marker = marker, - - text = text, - range = range - }); -end +NOTE: Images aren't handled by the `asciidoc_inline` parser due to it being a **block macro**. +```asciidoc +image::markview.jpg[] +``` +]] ---@param buffer integer ---@param TSNode TSNode ---@param text string[] ---@param range markview.parsed.asciidoc.images.range asciidoc.image = function (buffer, TSNode, text, range) + ---|fS + local _destination = TSNode:named_child(1); if not _destination then @@ -222,46 +187,27 @@ asciidoc.image = function (buffer, TSNode, text, range) text = text, range = range }); -end ----@param text string[] ----@param range markview.parsed.range -asciidoc.toc_pos = function (_, _, text, range) - range.col_end = range.col_start + #(text[1] or ""); - asciidoc.data.toc_pos = range; + ---|fE end ----@param text string[] ----@param range markview.parsed.asciidoc.tocs.range -asciidoc.toc = function (_, _, text, range) - local validated = {}; +--[[ +Keycodes. - for _, entry in ipairs(asciidoc.data.toc_entries or {}) do - if entry.depth < (asciidoc.data.toc_max_depth or 5) then - table.insert(validated, entry); - end - end - - range.col_end = range.col_start + #(text[1] or ""); - range.position = asciidoc.data.toc_pos; - - asciidoc.insert({ - class = "asciidoc_toc", - - title = asciidoc.data.toc_title, - max_depth = asciidoc.data.toc_max_depth, - entries = validated, - - text = text, - range = range - }); -end +NOTE: Keycodes are **block macro**. +NIT: Should a separate function be used for `menus`? +```asciidoc +kbd::space[] +``` +]] ---@param buffer integer ---@param TSNode TSNode ---@param text string[] ---@param range markview.parsed.asciidoc.keycodes.range asciidoc.keycode = function (buffer, TSNode, text, range) + ---|fS + local _content = TSNode:named_child(1); if not _content then @@ -287,13 +233,18 @@ asciidoc.keycode = function (buffer, TSNode, text, range) text = text, range = range }); + + ---|fE end +--[[ Is the given `list item marker` on the same level as the **current** list item? ]] ---@param buffer integer ---@param now string Current marker. ---@param last TSNode ---@return boolean local function is_on_same_level(buffer, now, last) + ---|fS + local _marker = last:child(0); if not _marker then @@ -302,13 +253,29 @@ local function is_on_same_level(buffer, now, last) local marker = vim.treesitter.get_node_text(_marker, buffer, {}); return marker == now; + + ---|fE end +--[[ +List item(`ordered` & `unordered`) + +```asciidoc +* Unordered +* Unordered 2 + +. Ordered +. Ordered 2 +``` +]] ---@param buffer integer ---@param TSNode TSNode ---@param text string[] ---@param range markview.parsed.asciidoc.list_items.range asciidoc.list_item = function (buffer, TSNode, text, range) + ---|fS + + ---@type integer List item index local N = 1; local prev = TSNode:prev_named_sibling(); @@ -379,9 +346,175 @@ asciidoc.list_item = function (buffer, TSNode, text, range) text = text, range = range }); + + ---|fE +end + +--[[ +Literal block. + +```asciidoc +... +Some text literally +... +``` +]] +---@param buffer integer +---@param TSNode TSNode +---@param text string[] +---@param range markview.parsed.asciidoc.literal_blocks.range +asciidoc.literal_block = function (buffer, TSNode, text, range) + ---|fS + + local _delimiters = { + TSNode:named_child(0), + TSNode:named_child(2), + }; + + if _delimiters[1] then + range.start_delim = { _delimiters[1]:range(); }; + end + + if _delimiters[1] then + range.end_delim = { _delimiters[2]:range(); }; + end + + local uses_tab = false; + + for _, line in ipairs(text) do + if string.match(line, "\t") then + uses_tab = true; + break; + end + end + + asciidoc.insert({ + class = "asciidoc_literal_block", + delimiters = { + _delimiters[1] and vim.treesitter.get_node_text(_delimiters[1], buffer, {}) or "", + _delimiters[2] and vim.treesitter.get_node_text(_delimiters[2], buffer, {}) or "", + }, + uses_tab = uses_tab, + + text = text, + range = range + }); + + ---|fE end ---- HTML parser +--[[ +Section tiles. + +```asciidoc +... +== Section + +=== Sub-section + +=== Sub-section 2 +... +``` +]] +---@param buffer integer +---@param TSNode TSNode +---@param text string[] +---@param range markview.parsed.range +asciidoc.section_title = function (buffer, TSNode, text, range) + ---|fS + + local _marker = TSNode:child(0); + + if not _marker then + return; + end + + local marker = vim.treesitter.get_node_text(_marker, buffer, {}); + local prev = TSNode:prev_named_sibling(); + + if prev then + local prev_text = vim.treesitter.get_node_text(prev, buffer, {}); + + if prev:type() == "element_attr" and prev_text == "[discrete]" then + goto dont_add_to_toc; + end + end + + if not asciidoc.data.toc_entries then + asciidoc.data.toc_entries = {}; + end + + table.insert(asciidoc.data.toc_entries, { + depth = (#marker or 1) - 1, + text = string.gsub(text[1] or "", "^[=%s]+", ""), + + range = vim.deepcopy(range, true), + } --[[@as markview.parser.asciidoc.data.toc_entry]]); + + ::dont_add_to_toc:: + + asciidoc.insert({ + class = "asciidoc_section_title", + marker = marker, + + text = text, + range = range + }); + + ---|fE +end + +--[[ +Specified automated TOC position. + +NOTE: This needs to be parsed before parsing any TOC nodes! +]] +---@param text string[] +---@param range markview.parsed.range +asciidoc.toc_pos = function (_, _, text, range) + range.col_end = range.col_start + #(text[1] or ""); + asciidoc.data.toc_pos = range; +end + +--[[ +Automated Table of Content. + +```asciidoc +:toc: +``` +]] +---@param text string[] +---@param range markview.parsed.asciidoc.tocs.range +asciidoc.toc = function (_, _, text, range) + ---|fS + + ---@type markview.parser.asciidoc.data.toc_entry[] Validated TOC entries(matches heading depth). + local validated = {}; + + for _, entry in ipairs(asciidoc.data.toc_entries or {}) do + if entry.depth < (asciidoc.data.toc_max_depth or 5) then + table.insert(validated, entry); + end + end + + range.col_end = range.col_start + #(text[1] or ""); + range.position = asciidoc.data.toc_pos; + + asciidoc.insert({ + class = "asciidoc_toc", + + title = asciidoc.data.toc_title, + max_depth = asciidoc.data.toc_max_depth, + entries = validated, + + text = text, + range = range + }); + + ---|fE +end + +--- Asciidoc parser ---@param buffer integer ---@param TSTree table ---@param from integer? @@ -389,8 +522,11 @@ end ---@return markview.parsed.asciidoc[] ---@return markview.parsed.asciidoc_sorted asciidoc.parse = function (buffer, TSTree, from, to) + ---|fS + -- Clear the previous contents asciidoc.data = {}; + ---@diagnostic disable-next-line: missing-fields asciidoc.sorted = {}; asciidoc.content = {}; @@ -515,6 +651,12 @@ asciidoc.parse = function (buffer, TSTree, from, to) iter(scanned_queries); + --[[ + NOTE: We need to parse TOC nodes separately because certain document attributes changes the TOC + + Parsing them together will require manually finding TOC nodes every time a change needs to be applied. + ]] + local can_scan_tquery, scanned_tqueries = pcall(vim.treesitter.query.parse, "asciidoc", [[ (document_attr ( @@ -539,6 +681,8 @@ asciidoc.parse = function (buffer, TSTree, from, to) end return asciidoc.content, asciidoc.sorted; + + ---|fE end return asciidoc; diff --git a/lua/markview/parsers/asciidoc_inline.lua b/lua/markview/parsers/asciidoc_inline.lua index 2f3ce87d..4215446c 100644 --- a/lua/markview/parsers/asciidoc_inline.lua +++ b/lua/markview/parsers/asciidoc_inline.lua @@ -1,14 +1,16 @@ --- HTML parser for `markview.nvim`. local asciidoc_inline = {}; +---@type integer[][] Already parsed ranges. Used to prevent re-parsing already parsed regions. asciidoc_inline.parsed_ranges = {}; --- Queried contents ----@type table[] +---@type markview.parsed.asciidoc_inline[] asciidoc_inline.content = {}; --- Queried contents, but sorted ----@type { [string]: table } +---@type markview.parsed.asciidoc_inline_sorted +---@diagnostic disable-next-line: missing-fields asciidoc_inline.sorted = {} --- Wrapper for `table.insert()`. @@ -23,11 +25,20 @@ asciidoc_inline.insert = function (data) table.insert(asciidoc_inline.sorted[data.class], data); end +--[[ +Bold text. + +```asciidoc +*bold* +``` +]] ---@param buffer integer ---@param TSNode TSNode ---@param text string[] ---@param range markview.parsed.range asciidoc_inline.bold = function (buffer, TSNode, text, range) + ---|fS + local delimiters = {}; for child in TSNode:iter_children() do @@ -47,13 +58,24 @@ asciidoc_inline.bold = function (buffer, TSNode, text, range) text = text, range = range }); + + ---|fE end +--[[ +Highlighted text. + +```asciidoc +#highlighted# +``` +]] ---@param buffer integer ---@param TSNode TSNode ---@param text string[] ---@param range markview.parsed.range asciidoc_inline.highlight = function (buffer, TSNode, text, range) + ---|fS + local delimiters = {}; for child in TSNode:iter_children() do @@ -73,13 +95,24 @@ asciidoc_inline.highlight = function (buffer, TSNode, text, range) text = text, range = range }); + + ---|fE end +--[[ +Italic. + +```asciidoc +__italic__ +``` +]] ---@param buffer integer ---@param TSNode TSNode ---@param text string[] ---@param range markview.parsed.range asciidoc_inline.italic = function (buffer, TSNode, text, range) + ---|fS + local delimiters = {}; for child in TSNode:iter_children() do @@ -99,72 +132,111 @@ asciidoc_inline.italic = function (buffer, TSNode, text, range) text = text, range = range }); + + ---|fE end +--[[ +Labeled URI. + +```asciidoc +www.example.com[Example] +``` +]] ---@param buffer integer ---@param TSNode TSNode ---@param text string[] ----@param range markview.parsed.range -asciidoc_inline.monospace = function (buffer, TSNode, text, range) - local delimiters = {}; +---@param range markview.parsed.asciidoc_inline.labeled_uris.range +asciidoc_inline.labeled_uri = function (buffer, TSNode, text, range) + ---|fS + + local destination; for child in TSNode:iter_children() do - if child:named() == false then - if delimiters[1] then - delimiters[2] = vim.treesitter.get_node_text(child, buffer, {}); - else - delimiters[1] = vim.treesitter.get_node_text(child, buffer, {}); - end + if child:type() == "uri_label" then + _, range.label_col_start, _, range.label_col_end = child:range(); + elseif child:type() == "uri" then + destination = vim.treesitter.get_node_text(child, buffer, {}); end end asciidoc_inline.insert({ - class = "asciidoc_inline_monospace", - delimiters = delimiters, + class = "asciidoc_inline_labeled_uri", + destination = destination, text = text, range = range }); + + ---|fE end +--[[ +Inline macro for labeled URI. + +```asciidoc +link:www.example.com[Example] +``` +]] ---@param buffer integer ---@param TSNode TSNode ---@param text string[] ---@param range markview.parsed.asciidoc_inline.labeled_uris.range -asciidoc_inline.labeled_uri = function (buffer, TSNode, text, range) - local destination; +asciidoc_inline.uri_macro = function (buffer, TSNode, text, range) + ---|fS + + local kind, destination; for child in TSNode:iter_children() do - if child:type() == "uri_label" then - _, range.label_col_start, _, range.label_col_end = child:range(); - elseif child:type() == "uri" then + if child:type() == "macro_name" then + kind = vim.treesitter.get_node_text(child, buffer, {}); + elseif child:type() == "target" then destination = vim.treesitter.get_node_text(child, buffer, {}); + elseif child:type() == "attr" then + local attr = vim.treesitter.get_node_text(child, buffer, {}); + + if string.match(attr, '^".*"$') or not string.match(attr, "[,=]") then + _, range.label_col_start, _, range.label_col_end = child:range(); + else + local R = { child:range() }; + local target = string.match(attr, '^"[^"]*"') or string.match(attr, "^[^,]*") or ""; + target = string.gsub(target, "%s+$", ""); + + local spaces_before = #string.match(target, "^%s*"); + + range.label_col_start = R[2] + spaces_before; + range.label_col_end = R[2] + spaces_before + #target; + end end end asciidoc_inline.insert({ class = "asciidoc_inline_labeled_uri", + kind = kind, destination = destination, text = text, range = range }); + + ---|fE end +--[[ +Monospace text. + +```asciidoc +`mono` +``` +]] ---@param buffer integer ---@param TSNode TSNode ---@param text string[] ---@param range markview.parsed.range -asciidoc_inline.uri = function (buffer, TSNode, text, range) - local before = vim.api.nvim_buf_get_text(buffer, range.row_start, 0, range.row_start, range.col_start, {})[1]; - - -- NOTE: Do not parse a URI if it's part of an image macro. - if string.match(before, "image::$") then - return; - end +asciidoc_inline.monospace = function (buffer, TSNode, text, range) + ---|fS local delimiters = {}; - local destination; for child in TSNode:iter_children() do if child:named() == false then @@ -173,59 +245,66 @@ asciidoc_inline.uri = function (buffer, TSNode, text, range) else delimiters[1] = vim.treesitter.get_node_text(child, buffer, {}); end - else - destination = vim.treesitter.get_node_text(child, buffer, {}); end end asciidoc_inline.insert({ - class = "asciidoc_inline_uri", + class = "asciidoc_inline_monospace", delimiters = delimiters, - destination = destination, text = text, range = range }); + + ---|fE end +--[[ +Unlabeled URI. + +```asciidoc +www.example.com +``` +]] ---@param buffer integer ---@param TSNode TSNode ---@param text string[] ----@param range markview.parsed.asciidoc_inline.labeled_uris.range -asciidoc_inline.uri_macro = function (buffer, TSNode, text, range) - local kind, destination; +---@param range markview.parsed.range +asciidoc_inline.uri = function (buffer, TSNode, text, range) + ---|fS - for child in TSNode:iter_children() do - if child:type() == "macro_name" then - kind = vim.treesitter.get_node_text(child, buffer, {}); - elseif child:type() == "target" then - destination = vim.treesitter.get_node_text(child, buffer, {}); - elseif child:type() == "attr" then - local attr = vim.treesitter.get_node_text(child, buffer, {}); + local before = vim.api.nvim_buf_get_text(buffer, range.row_start, 0, range.row_start, range.col_start, {})[1]; - if string.match(attr, '^".*"$') or not string.match(attr, "[,=]") then - _, range.label_col_start, _, range.label_col_end = child:range(); - else - local R = { child:range() }; - local target = string.match(attr, '^"[^"]*"') or string.match(attr, "^[^,]*") or ""; - target = string.gsub(target, "%s+$", ""); + -- NOTE: Do not parse a URI if it's part of an image macro. + if string.match(before, "image::$") then + return; + end - local spaces_before = #string.match(target, "^%s*"); + local delimiters = {}; + local destination; - range.label_col_start = R[2] + spaces_before; - range.label_col_end = R[2] + spaces_before + #target; + for child in TSNode:iter_children() do + if child:named() == false then + if delimiters[1] then + delimiters[2] = vim.treesitter.get_node_text(child, buffer, {}); + else + delimiters[1] = vim.treesitter.get_node_text(child, buffer, {}); end + else + destination = vim.treesitter.get_node_text(child, buffer, {}); end end asciidoc_inline.insert({ - class = "asciidoc_inline_labeled_uri", - kind = kind, + class = "asciidoc_inline_uri", + delimiters = delimiters, destination = destination, text = text, range = range }); + + ---|fE end ---@param buffer integer @@ -235,7 +314,9 @@ end ---@return markview.parsed.asciidoc_inline[] ---@return markview.parsed.asciidoc_inline_sorted asciidoc_inline.parse = function (buffer, TSTree, from, to) - -- Clear the previous contents + ---|fS + + ---@diagnostic disable-next-line: missing-fields asciidoc_inline.sorted = {}; asciidoc_inline.content = {}; @@ -342,6 +423,8 @@ asciidoc_inline.parse = function (buffer, TSTree, from, to) end return asciidoc_inline.content, asciidoc_inline.sorted; + + ---|fE end return asciidoc_inline; diff --git a/lua/markview/renderers/asciidoc.lua b/lua/markview/renderers/asciidoc.lua index 8f0e0d07..dda770b4 100644 --- a/lua/markview/renderers/asciidoc.lua +++ b/lua/markview/renderers/asciidoc.lua @@ -82,391 +82,131 @@ asciidoc.admonition = function (buffer, item) ---|fE end ---- Renders horizontal rules/line breaks. ---@param buffer integer ----@param item markview.parsed.asciidoc.hrs -asciidoc.hr = function (buffer, item) - ---@type markview.config.asciidoc.hrs? - local config = spec.get({ "asciidoc", "horizontal_rules" }, { fallback = nil, eval_args = { buffer, item } }); - local range = item.range; +---@param item markview.parsed.asciidoc.document_titles +asciidoc.document_attribute = function (buffer, item) + ---|fS + + ---@type markview.config.asciidoc.document_titles? + local config = spec.get({ "asciidoc", "document_attributes" }, { eval_args = { buffer, item } }); if not config then return; end - local virt_text = {}; - local function val(opt, index, wrap) - if vim.islist(opt) == false then - return opt; - elseif #opt < index then - if wrap == true then - local mod = index % #opt; - return mod == 0 and opt[#opt] or opt[mod]; - else - return opt[#opt]; - end - elseif index < 0 then - return opt[1]; - end - - return opt[index]; - end - - for _, part in ipairs(config.parts) do - if part.type == "text" then - table.insert(virt_text, { part.text, utils.set_hl(part.hl --[[ @as string ]]) }); - elseif part.type == "repeating" then - local rep = spec.get({ "repeat_amount" }, { source = part, fallback = 1, eval_args = { buffer, item } }); - local hl_rep = spec.get({ "repeat_hl" }, { source = part, fallback = false, eval_args = { buffer, item } }); - local txt_rep = spec.get({ "repeat_text" }, { source = part, fallback = false, eval_args = { buffer, item } }); - - for r = 1, rep, 1 do - if part.direction == "right" then - table.insert(virt_text, { - val(part.text, (rep - r) + 1, txt_rep), - val(part.hl, (rep - r) + 1, hl_rep) - }); - else - table.insert(virt_text, { - val(part.text, r, txt_rep), - val(part.hl, r, hl_rep) - }); - end - end - end - end - - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, 0, { - undo_restore = false, invalidate = true, - virt_text_pos = "overlay", - virt_text = virt_text, + local range = item.range; - hl_mode = "combine" + utils.set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start, { + end_row = range.row_end - 1, + conceal_lines = "", }); + + ---|fE end ---@param buffer integer ----@param item markview.parsed.asciidoc.literal_blocks -asciidoc.literal_block = function (buffer, item) - ---@type markview.config.asciidoc.literal_blocks? - local config = spec.get({ "asciidoc", "literal_blocks" }, { fallback = nil, eval_args = { buffer, item } }); +---@param item markview.parsed.asciidoc.document_titles +asciidoc.document_title = function (buffer, item) + ---|fS - local delims = item.delimiters; - local range = item.range; + ---@type markview.config.asciidoc.document_titles? + local config = spec.get({ "asciidoc", "document_titles" }, { eval_args = { buffer, item } }); if not config then return; end - local label = { config.label, config.label_hl or config.hl }; - local win = utils.buf_getwin(buffer); - - --[[ *Basic* rendering of `code blocks`. ]] - local function render_simple() - ---|fS - - local conceal_from = range.start_delim[2] + #string.match(item.delimiters[1], "^%s*"); - local conceal_to = #delims[1]; - - if config.label_direction == nil or config.label_direction == "left" then - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, conceal_from, { - undo_restore = false, invalidate = true, - - end_col = conceal_to, - conceal = "", - - sign_text = config.sign, - sign_hl_group = utils.set_hl(config.sign_hl), - - virt_text_pos = "inline", - virt_text = { label }, - - line_hl_group = utils.set_hl(config.hl) - }); - else - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, conceal_from, { - undo_restore = false, invalidate = true, - - end_col = conceal_to, - conceal = "", - - sign_text = config.sign, - sign_hl_group = utils.set_hl(config.sign_hl), + local range = item.range; - virt_text_pos = "right_align", - virt_text = { label }, + utils.set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start, { + -- Remove `=%s*` amount of characters. + end_col = range.col_start + #string.match(item.text[1] or "", "=+%s*"), + conceal = "", - line_hl_group = utils.set_hl(config.hl) - }); - end + sign_text = tostring(config.sign or ""), + sign_hl_group = utils.set_hl(config.sign_hl), - --- Background - for l = range.row_start + 1, range.row_end - 1 do - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, l, 0, { - undo_restore = false, invalidate = true, - end_row = l, + virt_text = { + { config.icon, config.icon_hl or config.hl }, + }, + line_hl_group = utils.set_hl(config.hl), + }); - line_hl_group = config.hl - }); - end + ---|fE +end - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_end, (range.col_start + #item.text[#item.text]) - #delims[2], { - undo_restore = false, invalidate = true, - end_col = range.col_start + #item.text[#item.text], - conceal = "", +--- Renders horizontal rules/line breaks. +---@param buffer integer +---@param item markview.parsed.asciidoc.hrs +asciidoc.hr = function (buffer, item) + ---|fS - line_hl_group = utils.set_hl(config.hl) - }); + ---@type markview.config.asciidoc.hrs? + local config = spec.get({ "asciidoc", "horizontal_rules" }, { fallback = nil, eval_args = { buffer, item } }); + local range = item.range; - ---|fE + if not config then + return; end - --- Renders block style code blocks. - local function render_block () - ---|fS - - ---|fS "chunk: Calculate various widths" - - local pad_amount = config.pad_amount or 0; - local block_width = config.min_width or 60; - - local pad_char = config.pad_char or " "; - - ---@type integer[] Visual width of lines. - local line_widths = {}; - - for l, line in ipairs(item.text) do - if l ~= 1 and l ~= #item.text then - table.insert(line_widths, vim.fn.strdisplaywidth(line)); - - if vim.fn.strdisplaywidth(line) > (block_width - (2 * pad_amount)) then - block_width = vim.fn.strdisplaywidth(line) + (2 * pad_amount); - end - end - end - - local label_width = utils.virt_len({ label }); - - ---|fE - - local delim_conceal_from = range.start_delim[2] + #string.match(item.delimiters[1], "^%s*"); - local conceal_to = #delims[1]; - - ---|fS "chunk: Top border" - - local visible_info = string.sub(item.text[1], (conceal_to + 1) - range.col_start); - local left_padding = visible_info ~= "" and 1 or pad_amount; - - local pad_width = vim.fn.strdisplaywidth( - string.rep(pad_char, left_padding) - ); - - -- Hide the leading `backticks`s. - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, delim_conceal_from, { - undo_restore = false, invalidate = true, - - end_col = conceal_to, - conceal = "", - - sign_text = config.sign, - sign_hl_group = utils.set_hl(config.sign_hl), - }); - - if config.label_direction == "right" then - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, delim_conceal_from, { - virt_text_pos = "inline", - virt_text = { - { - string.rep(" " or pad_char, left_padding), - utils.set_hl(config.hl) - } - } - }); - else - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start + #item.text[1], { - virt_text_pos = "inline", - virt_text = { - { - string.rep(" " or pad_char, left_padding), - utils.set_hl(config.hl) - } - } - }); - end - - ---|fS "chunk: Prettify info" - - -- Calculating the amount of spacing to add, - -- 1. Used space = label width(`label_width`) + padding size(`pad_width`). - -- 2. Total block width - Used space - local spacing = block_width - (label_width + pad_width); - - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start + #item.text[1], { - virt_text_pos = "inline", - virt_text = { - { - string.rep(pad_char, spacing), - utils.set_hl(config.hl) - }, - } - }); - - ---|fE - - ---|fS "chunk: Place label" - - local top_border = { - }; - - if config.label_direction == "right" then - top_border.col_start = range.start_delim[2] + #item.text[1]; - else - top_border.col_start = range.start_delim[4]; - end - - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, top_border.col_start, { - virt_text_pos = "inline", - virt_text = { label } - }); - - ---|fE - - ---|fE - - --- Line padding - for l, width in ipairs(line_widths) do - local line = item.text[l + 1]; - - if width ~= 0 then - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start + l, line ~= "" and range.col_start or 0, { - undo_restore = false, invalidate = true, - - virt_text_pos = "inline", - virt_text = { - { - string.rep(" ", pad_amount), - utils.set_hl(config.hl --[[ @as string ]]) - } - }, - }); - - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start + l, range.col_start + #line, { - undo_restore = false, invalidate = true, - - virt_text_pos = "inline", - virt_text = { - { - string.rep(" ", math.max(0, block_width - (( 2 * pad_amount) + width))), - utils.set_hl(config.hl --[[ @as string ]]) - }, - { - string.rep(" ", pad_amount), - utils.set_hl(config.hl --[[ @as string ]]) - } - }, - }); - - --- Background - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start + l, range.col_start, { - undo_restore = false, invalidate = true, - end_col = range.col_start + #line, - - hl_group = utils.set_hl(config.hl --[[ @as string ]]) - }); + local virt_text = {}; + local function val(opt, index, wrap) + if vim.islist(opt) == false then + return opt; + elseif #opt < index then + if wrap == true then + local mod = index % #opt; + return mod == 0 and opt[#opt] or opt[mod]; else - local buf_line = vim.api.nvim_buf_get_lines(buffer, range.row_start + l, range.row_start + l + 1, false)[1]; - - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start + l, #buf_line, { - undo_restore = false, invalidate = true, - - virt_text_pos = "inline", - virt_text = { - { - string.rep(" ", math.max(0, range.col_start - #buf_line)) - }, - { - string.rep(" ", pad_amount), - utils.set_hl(config.hl --[[ @as string ]]) - }, - { - string.rep(" ", math.max(0, block_width - (2 * pad_amount))), - utils.set_hl(config.hl --[[ @as string ]]) - }, - { - string.rep(" ", pad_amount), - utils.set_hl(config.hl --[[ @as string ]]) - }, - }, - }); + return opt[#opt]; end + elseif index < 0 then + return opt[1]; end - --- Render bottom - if item.delimiters[2] then - local end_delim_conceal_from = range.end_delim[2] + #string.match(item.delimiters[2], "^%s*"); - - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_end, end_delim_conceal_from, { - undo_restore = false, invalidate = true, - end_col = range.col_start + #item.text[#item.text], - conceal = "" - }); - - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_end, end_delim_conceal_from, { - undo_restore = false, invalidate = true, - - virt_text_pos = "inline", - virt_text = { - { - string.rep(" ", block_width), - utils.set_hl(config.hl) - } - } - }); - else - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_end, range.col_start, { - undo_restore = false, invalidate = true, - - virt_text_pos = "inline", - virt_text = { - { - string.rep(" ", block_width), - utils.set_hl(config.hl) - } - } - }); - end - - ---|fE - end - - if not win or config.style == "simple" or item.uses_tab or ( vim.o.wrap == true or vim.wo[win].wrap == true ) then - render_simple(); - elseif config.style == "block" then - render_block() - end -end - ----@param buffer integer ----@param item markview.parsed.asciidoc.document_titles -asciidoc.document_attribute = function (buffer, item) - ---@type markview.config.asciidoc.document_titles? - local config = spec.get({ "asciidoc", "document_attributes" }, { eval_args = { buffer, item } }); + return opt[index]; + end - if not config then - return; + for _, part in ipairs(config.parts) do + if part.type == "text" then + table.insert(virt_text, { part.text, utils.set_hl(part.hl --[[ @as string ]]) }); + elseif part.type == "repeating" then + local rep = spec.get({ "repeat_amount" }, { source = part, fallback = 1, eval_args = { buffer, item } }); + local hl_rep = spec.get({ "repeat_hl" }, { source = part, fallback = false, eval_args = { buffer, item } }); + local txt_rep = spec.get({ "repeat_text" }, { source = part, fallback = false, eval_args = { buffer, item } }); + + for r = 1, rep, 1 do + if part.direction == "right" then + table.insert(virt_text, { + val(part.text, (rep - r) + 1, txt_rep), + val(part.hl, (rep - r) + 1, hl_rep) + }); + else + table.insert(virt_text, { + val(part.text, r, txt_rep), + val(part.hl, r, hl_rep) + }); + end + end + end end - local range = item.range; + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, 0, { + undo_restore = false, invalidate = true, + virt_text_pos = "overlay", + virt_text = virt_text, - utils.set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start, { - end_row = range.row_end - 1, - conceal_lines = "", + hl_mode = "combine" }); + + ---|fE end ---@param buffer integer ---@param item markview.parsed.asciidoc.images asciidoc.image = function (buffer, item) + ---|fS + ---@type markview.config.asciidoc.images? local main_config = spec.get({ "asciidoc", "images" }, { fallback = nil }); local range = item.range; @@ -533,11 +273,15 @@ asciidoc.image = function (buffer, item) hl_mode = "combine" }); + + ---|fE end ---@param buffer integer ---@param item markview.parsed.asciidoc.keycodes asciidoc.keycode = function (buffer, item) + ---|fS + ---@type markview.config.asciidoc.keycodes? local main_config = spec.get({ "asciidoc", "keycodes" }, { fallback = nil }); local range = item.range; @@ -584,137 +328,421 @@ asciidoc.keycode = function (buffer, item) end_col = range.col_end, conceal = "", - virt_text_pos = "inline", - virt_text = { - { config.padding_right or "", utils.set_hl(config.padding_right_hl or config.hl) }, - { config.corner_right or "", utils.set_hl(config.corner_right_hl or config.hl) } - }, + virt_text_pos = "inline", + virt_text = { + { config.padding_right or "", utils.set_hl(config.padding_right_hl or config.hl) }, + { config.corner_right or "", utils.set_hl(config.corner_right_hl or config.hl) } + }, + + hl_mode = "combine" + }); + + ---|fE +end + +---@param buffer integer +---@param item markview.parsed.asciidoc.list_items +asciidoc.list_item = function (buffer, item) + ---|fS + + ---@type markview.config.asciidoc.list_items? + local main_config = spec.get({ "asciidoc", "list_items" }, { fallback = nil, eval_args = { buffer, item } }); + + if not main_config then + return; + end + + ---@type markview.config.asciidoc.list_items.opts? + local config; + + if string.match(item.marker, "%*") then + config = spec.get({ "marker_star" }, { source = main_config, eval_args = { buffer, item } }); + elseif string.match(item.marker, "%-") then + config = spec.get({ "marker_minus" }, { source = main_config, eval_args = { buffer, item } }); + else + config = spec.get({ "marker_dot" }, { source = main_config, eval_args = { buffer, item } }); + end + + if not config then + return; + end + + ---@cast config markview.config.asciidoc.list_items.opts + + local checkbox_config; + + if item.checkbox == "*" then + checkbox_config = spec.get({ "asciidoc", "checkboxes", "checked" }, { eval_args = { buffer, item } }); + elseif item.checkbox == " " then + checkbox_config = spec.get({ "asciidoc", "checkboxes", "unchecked" }, { eval_args = { buffer, item } }); + elseif item.checkbox then + local checkboxes = spec.get({ "asciidoc", "checkboxes" }, { eval_args = { buffer, item } }); + local _state = vim.pesc(tostring(item.checkbox)); + + checkbox_config = utils.match(checkboxes, "^" .. _state .. "$", { default = false, ignore_keys = { "checked", "unchecked", "enable" }, eval_args = { buffer, item } }); + end + + local shift_width = main_config.shift_width or 2; + local range = item.range; + + for r = range.row_start, range.row_end - 1, 1 do + if r == range.row_start then + if checkbox_config and not vim.tbl_isempty(checkbox_config) then + utils.set_extmark(buffer, asciidoc.ns, r, range.col_start, { + end_col = config.conceal_on_checkboxes and range.checkbox_start or range.marker_end, + conceal = "", + + virt_text = { + { config.add_padding and string.rep(" ", #item.marker * shift_width) or "" }, + { not config.conceal_on_checkboxes and config.text or "", config.hl }, + }, + hl_mode = "combine", + }); + + utils.set_extmark(buffer, asciidoc.ns, r, range.checkbox_start, { + end_col = range.checkbox_end, + conceal = "", + + virt_text = { + { checkbox_config.text or "", checkbox_config.hl }, + }, + hl_mode = "combine", + }); + else + utils.set_extmark(buffer, asciidoc.ns, r, range.col_start, { + end_col = range.marker_end, + conceal = "", + + virt_text = { + { config.add_padding and string.rep(" ", #item.marker * shift_width) or "" }, + { config.text or "", config.hl }, + }, + hl_mode = "combine", + }); + end + elseif config.add_padding then + utils.set_extmark(buffer, asciidoc.ns, r, 0, { + virt_text = { + { string.rep(" ", #item.marker * shift_width) }, + }, + hl_mode = "combine", + }); + end + end + + ---|fE +end + +---@param buffer integer +---@param item markview.parsed.asciidoc.literal_blocks +asciidoc.literal_block = function (buffer, item) + ---|fS + + ---@type markview.config.asciidoc.literal_blocks? + local config = spec.get({ "asciidoc", "literal_blocks" }, { fallback = nil, eval_args = { buffer, item } }); + + local delims = item.delimiters; + local range = item.range; + + if not config then + return; + end + + local label = { config.label, config.label_hl or config.hl }; + local win = utils.buf_getwin(buffer); + + --[[ *Basic* rendering of `code blocks`. ]] + local function render_simple() + ---|fS + + local conceal_from = range.start_delim[2] + #string.match(item.delimiters[1], "^%s*"); + local conceal_to = #delims[1]; + + if config.label_direction == nil or config.label_direction == "left" then + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, conceal_from, { + undo_restore = false, invalidate = true, + + end_col = conceal_to, + conceal = "", + + sign_text = config.sign, + sign_hl_group = utils.set_hl(config.sign_hl), + + virt_text_pos = "inline", + virt_text = { label }, + + line_hl_group = utils.set_hl(config.hl) + }); + else + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, conceal_from, { + undo_restore = false, invalidate = true, + + end_col = conceal_to, + conceal = "", + + sign_text = config.sign, + sign_hl_group = utils.set_hl(config.sign_hl), + + virt_text_pos = "right_align", + virt_text = { label }, + + line_hl_group = utils.set_hl(config.hl) + }); + end + + --- Background + for l = range.row_start + 1, range.row_end - 1 do + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, l, 0, { + undo_restore = false, invalidate = true, + end_row = l, + + line_hl_group = config.hl + }); + end + + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_end, (range.col_start + #item.text[#item.text]) - #delims[2], { + undo_restore = false, invalidate = true, + end_col = range.col_start + #item.text[#item.text], + conceal = "", + + line_hl_group = utils.set_hl(config.hl) + }); + + ---|fE + end + + --- Renders block style code blocks. + local function render_block () + ---|fS + + ---|fS "chunk: Calculate various widths" + + local pad_amount = config.pad_amount or 0; + local block_width = config.min_width or 60; + + local pad_char = config.pad_char or " "; + + ---@type integer[] Visual width of lines. + local line_widths = {}; + + for l, line in ipairs(item.text) do + if l ~= 1 and l ~= #item.text then + table.insert(line_widths, vim.fn.strdisplaywidth(line)); + + if vim.fn.strdisplaywidth(line) > (block_width - (2 * pad_amount)) then + block_width = vim.fn.strdisplaywidth(line) + (2 * pad_amount); + end + end + end + + local label_width = utils.virt_len({ label }); + + ---|fE + + local delim_conceal_from = range.start_delim[2] + #string.match(item.delimiters[1], "^%s*"); + local conceal_to = #delims[1]; + + ---|fS "chunk: Top border" - hl_mode = "combine" - }); -end + local visible_info = string.sub(item.text[1], (conceal_to + 1) - range.col_start); + local left_padding = visible_info ~= "" and 1 or pad_amount; ----@param buffer integer ----@param item markview.parsed.asciidoc.document_titles -asciidoc.document_title = function (buffer, item) - ---@type markview.config.asciidoc.document_titles? - local config = spec.get({ "asciidoc", "document_titles" }, { eval_args = { buffer, item } }); + local pad_width = vim.fn.strdisplaywidth( + string.rep(pad_char, left_padding) + ); - if not config then - return; - end + -- Hide the leading `backticks`s. + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, delim_conceal_from, { + undo_restore = false, invalidate = true, - local range = item.range; + end_col = conceal_to, + conceal = "", - utils.set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start, { - -- Remove `=%s*` amount of characters. - end_col = range.col_start + #string.match(item.text[1] or "", "=+%s*"), - conceal = "", + sign_text = config.sign, + sign_hl_group = utils.set_hl(config.sign_hl), + }); - sign_text = tostring(config.sign or ""), - sign_hl_group = utils.set_hl(config.sign_hl), + if config.label_direction == "right" then + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, delim_conceal_from, { + virt_text_pos = "inline", + virt_text = { + { + string.rep(" " or pad_char, left_padding), + utils.set_hl(config.hl) + } + } + }); + else + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start + #item.text[1], { + virt_text_pos = "inline", + virt_text = { + { + string.rep(" " or pad_char, left_padding), + utils.set_hl(config.hl) + } + } + }); + end - virt_text = { - { config.icon, config.icon_hl or config.hl }, - }, - line_hl_group = utils.set_hl(config.hl), - }); -end + ---|fS "chunk: Prettify info" ---- Renders atx headings. ----@param buffer integer ----@param item markview.parsed.asciidoc.list_items -asciidoc.list_item = function (buffer, item) - ---@type markview.config.asciidoc.list_items? - local main_config = spec.get({ "asciidoc", "list_items" }, { fallback = nil, eval_args = { buffer, item } }); + -- Calculating the amount of spacing to add, + -- 1. Used space = label width(`label_width`) + padding size(`pad_width`). + -- 2. Total block width - Used space + local spacing = block_width - (label_width + pad_width); - if not main_config then - return; - end + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start + #item.text[1], { + virt_text_pos = "inline", + virt_text = { + { + string.rep(pad_char, spacing), + utils.set_hl(config.hl) + }, + } + }); - ---@type markview.config.asciidoc.list_items.opts? - local config; + ---|fE - if string.match(item.marker, "%*") then - config = spec.get({ "marker_star" }, { source = main_config, eval_args = { buffer, item } }); - elseif string.match(item.marker, "%-") then - config = spec.get({ "marker_minus" }, { source = main_config, eval_args = { buffer, item } }); - else - config = spec.get({ "marker_dot" }, { source = main_config, eval_args = { buffer, item } }); - end + ---|fS "chunk: Place label" - if not config then - return; - end + local top_border = { + }; - ---@cast config markview.config.asciidoc.list_items.opts + if config.label_direction == "right" then + top_border.col_start = range.start_delim[2] + #item.text[1]; + else + top_border.col_start = range.start_delim[4]; + end - local checkbox_config; + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, top_border.col_start, { + virt_text_pos = "inline", + virt_text = { label } + }); - if item.checkbox == "*" then - checkbox_config = spec.get({ "asciidoc", "checkboxes", "checked" }, { eval_args = { buffer, item } }); - elseif item.checkbox == " " then - checkbox_config = spec.get({ "asciidoc", "checkboxes", "unchecked" }, { eval_args = { buffer, item } }); - elseif item.checkbox then - local checkboxes = spec.get({ "asciidoc", "checkboxes" }, { eval_args = { buffer, item } }); - local _state = vim.pesc(tostring(item.checkbox)); + ---|fE - checkbox_config = utils.match(checkboxes, "^" .. _state .. "$", { default = false, ignore_keys = { "checked", "unchecked", "enable" }, eval_args = { buffer, item } }); - end + ---|fE - local shift_width = main_config.shift_width or 2; - local range = item.range; + --- Line padding + for l, width in ipairs(line_widths) do + local line = item.text[l + 1]; - for r = range.row_start, range.row_end - 1, 1 do - if r == range.row_start then - if checkbox_config and not vim.tbl_isempty(checkbox_config) then - utils.set_extmark(buffer, asciidoc.ns, r, range.col_start, { - end_col = config.conceal_on_checkboxes and range.checkbox_start or range.marker_end, - conceal = "", + if width ~= 0 then + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start + l, line ~= "" and range.col_start or 0, { + undo_restore = false, invalidate = true, + virt_text_pos = "inline", virt_text = { - { config.add_padding and string.rep(" ", #item.marker * shift_width) or "" }, - { not config.conceal_on_checkboxes and config.text or "", config.hl }, + { + string.rep(" ", pad_amount), + utils.set_hl(config.hl --[[ @as string ]]) + } }, - hl_mode = "combine", }); - utils.set_extmark(buffer, asciidoc.ns, r, range.checkbox_start, { - end_col = range.checkbox_end, - conceal = "", + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start + l, range.col_start + #line, { + undo_restore = false, invalidate = true, + virt_text_pos = "inline", virt_text = { - { checkbox_config.text or "", checkbox_config.hl }, + { + string.rep(" ", math.max(0, block_width - (( 2 * pad_amount) + width))), + utils.set_hl(config.hl --[[ @as string ]]) + }, + { + string.rep(" ", pad_amount), + utils.set_hl(config.hl --[[ @as string ]]) + } }, - hl_mode = "combine", + }); + + --- Background + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start + l, range.col_start, { + undo_restore = false, invalidate = true, + end_col = range.col_start + #line, + + hl_group = utils.set_hl(config.hl --[[ @as string ]]) }); else - utils.set_extmark(buffer, asciidoc.ns, r, range.col_start, { - end_col = range.marker_end, - conceal = "", + local buf_line = vim.api.nvim_buf_get_lines(buffer, range.row_start + l, range.row_start + l + 1, false)[1]; + + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start + l, #buf_line, { + undo_restore = false, invalidate = true, + virt_text_pos = "inline", virt_text = { - { config.add_padding and string.rep(" ", #item.marker * shift_width) or "" }, - { config.text or "", config.hl }, + { + string.rep(" ", math.max(0, range.col_start - #buf_line)) + }, + { + string.rep(" ", pad_amount), + utils.set_hl(config.hl --[[ @as string ]]) + }, + { + string.rep(" ", math.max(0, block_width - (2 * pad_amount))), + utils.set_hl(config.hl --[[ @as string ]]) + }, + { + string.rep(" ", pad_amount), + utils.set_hl(config.hl --[[ @as string ]]) + }, }, - hl_mode = "combine", }); end - elseif config.add_padding then - utils.set_extmark(buffer, asciidoc.ns, r, 0, { + end + + --- Render bottom + if item.delimiters[2] then + local end_delim_conceal_from = range.end_delim[2] + #string.match(item.delimiters[2], "^%s*"); + + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_end, end_delim_conceal_from, { + undo_restore = false, invalidate = true, + end_col = range.col_start + #item.text[#item.text], + conceal = "" + }); + + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_end, end_delim_conceal_from, { + undo_restore = false, invalidate = true, + + virt_text_pos = "inline", virt_text = { - { string.rep(" ", #item.marker * shift_width) }, - }, - hl_mode = "combine", + { + string.rep(" ", block_width), + utils.set_hl(config.hl) + } + } + }); + else + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_end, range.col_start, { + undo_restore = false, invalidate = true, + + virt_text_pos = "inline", + virt_text = { + { + string.rep(" ", block_width), + utils.set_hl(config.hl) + } + } }); end + + ---|fE + end + + if not win or config.style == "simple" or item.uses_tab or ( vim.o.wrap == true or vim.wo[win].wrap == true ) then + render_simple(); + elseif config.style == "block" then + render_block() end + + ---|fE end ---- Renders atx headings. ---@param buffer integer ---@param item markview.parsed.asciidoc.section_titles asciidoc.section_title = function (buffer, item) + ---|fS + ---@type markview.config.asciidoc.section_titles? local main_config = spec.get({ "asciidoc", "section_titles" }, { fallback = nil, eval_args = { buffer, item } }); @@ -746,11 +774,15 @@ asciidoc.section_title = function (buffer, item) }, line_hl_group = utils.set_hl(config.hl), }); + + ---|fE end ---@param buffer integer ---@param item markview.parsed.asciidoc.tocs asciidoc.toc = function (buffer, item) + ---|fS + ---@type markview.config.asciidoc.tocs? local main_config = spec.get({ "asciidoc", "tocs" }, { fallback = nil, eval_args = { buffer, item } }); @@ -826,18 +858,14 @@ asciidoc.toc = function (buffer, item) hl_mode = "combine", }); end + + ---|fE end ---@param buffer integer ---@param content markview.parsed.asciidoc[] asciidoc.render = function (buffer, content) - asciidoc.cache = { - font_regions = {}, - style_regions = { - superscripts = {}, - subscripts = {} - }, - }; + ---|fS local custom = spec.get({ "renderers" }, { fallback = {} }); @@ -863,9 +891,10 @@ asciidoc.render = function (buffer, content) }); end end + + ---|fE end ---- Clears decorations of HTML elements ---@param buffer integer ---@param from integer ---@param to integer diff --git a/lua/markview/renderers/asciidoc/tostring.lua b/lua/markview/renderers/asciidoc/tostring.lua index 510d214f..9bcbf813 100644 --- a/lua/markview/renderers/asciidoc/tostring.lua +++ b/lua/markview/renderers/asciidoc/tostring.lua @@ -1,3 +1,8 @@ +--[[ +Basic renderer for `asciidoc`. Used to convert regular strings to preview strings. + +Used for width calculations. +]] local adoc_str = {}; local utils = require("markview.utils"); diff --git a/lua/markview/renderers/asciidoc_inline.lua b/lua/markview/renderers/asciidoc_inline.lua index b38d5b1b..7164e507 100644 --- a/lua/markview/renderers/asciidoc_inline.lua +++ b/lua/markview/renderers/asciidoc_inline.lua @@ -8,6 +8,8 @@ asciidoc_inline.ns = vim.api.nvim_create_namespace("markview/asciidoc_inline"); ---@param buffer integer ---@param item markview.parsed.asciidoc_inline.bolds asciidoc_inline.bold = function (buffer, item) + ---|fS + ---@type markview.config.asciidoc_inline.bolds? local config = spec.get({ "asciidoc_inline", "bolds" }, { eval_args = { buffer, item } }); @@ -26,11 +28,15 @@ asciidoc_inline.bold = function (buffer, item) end_col = range.col_end, conceal = "", }); + + ---|fE end ---@param buffer integer ---@param item markview.parsed.asciidoc_inline.highlights asciidoc_inline.highlight = function (buffer, item) + ---|fS + ---@type markview.config.asciidoc_inline.highlights? local main_config = spec.get({ "asciidoc_inline", "highlights" }, { fallback = nil }); local range = item.range; @@ -85,11 +91,15 @@ asciidoc_inline.highlight = function (buffer, item) hl_mode = "combine" }); + + ---|fE end ---@param buffer integer ---@param item markview.parsed.asciidoc_inline.italics asciidoc_inline.italic = function (buffer, item) + ---|fS + ---@type markview.config.asciidoc_inline.italics? local config = spec.get({ "asciidoc_inline", "italics" }, { eval_args = { buffer, item } }); @@ -108,11 +118,15 @@ asciidoc_inline.italic = function (buffer, item) end_col = range.col_end, conceal = "", }); + + ---|fE end ---@param buffer integer ---@param item markview.parsed.asciidoc_inline.monospaces asciidoc_inline.monospace = function (buffer, item) + ---|fS + ---@type markview.config.asciidoc_inline.monospaces? local config = spec.get({ "asciidoc_inline", "monospaces" }, { eval_args = { buffer, item } }); @@ -155,11 +169,15 @@ asciidoc_inline.monospace = function (buffer, item) hl_mode = "combine" }); + + ---|fE end ---@param buffer integer ---@param item markview.parsed.asciidoc_inline.labeled_uris asciidoc_inline.labeled_uri = function (buffer, item) + ---|fS + ---@type markview.config.asciidoc_inline.uris? local main_config = spec.get({ "asciidoc_inline", "uris" }, { fallback = nil }); local range = item.range; @@ -214,11 +232,15 @@ asciidoc_inline.labeled_uri = function (buffer, item) hl_mode = "combine" }); + + ---|fE end ---@param buffer integer ---@param item markview.parsed.asciidoc_inline.uris asciidoc_inline.uri = function (buffer, item) + ---|fS + ---@type markview.config.asciidoc_inline.uris? local main_config = spec.get({ "asciidoc_inline", "uris" }, { fallback = nil }); local range = item.range; @@ -285,11 +307,15 @@ asciidoc_inline.uri = function (buffer, item) hl_mode = "combine" }); + + ---|fE end ---@param buffer integer ---@param content markview.parsed.asciidoc_inline[] asciidoc_inline.render = function (buffer, content) + ---|fS + local custom = spec.get({ "renderers" }, { fallback = {} }); for _, item in ipairs(content or {}) do @@ -314,9 +340,10 @@ asciidoc_inline.render = function (buffer, content) }); end end + + ---|fE end ---- Clears decorations of HTML elements ---@param buffer integer ---@param from integer ---@param to integer diff --git a/lua/markview/types/parsers/asciidoc.lua b/lua/markview/types/parsers/asciidoc.lua index bd0d83b6..92499f3c 100644 --- a/lua/markview/types/parsers/asciidoc.lua +++ b/lua/markview/types/parsers/asciidoc.lua @@ -5,7 +5,7 @@ ---@class markview.parsed.asciidoc.admonitions --- ---@field class "asciidoc_admonition" ----@field kind string Type of admonition. +---@field kind string Type of admonition(e.g. `NOTE`). --- ---@field text string[] ---@field range markview.parsed.asciidoc.admonitions.range @@ -69,30 +69,6 @@ --- ---@field destination integer[] Range of the image destination(output of `{ TSNode:range() }`. ------------------------------------------------------------------------------- - ----@class markview.parsed.asciidoc.literal_blocks ---- ----@field class "asciidoc_literal_block" ----@field delimiters [ string, string ] Block delimiters. ----@field uses_tab boolean Are there tabs in the text? ---- ----@field text string[] ----@field range markview.parsed.asciidoc.literal_blocks.range - - ----@class markview.parsed.asciidoc.literal_blocks.range ---- ----@field row_start integer ----@field col_start integer ---- ----@field row_end integer ----@field col_end integer ---- ----@field start_delim integer[] Range of the block start delimiter(output of `{ TSNode:range() }`. ----@field end_delim integer[] Range of the block end delimiter(output of `{ TSNode:range() }`. - - ------------------------------------------------------------------------------ ---@class markview.parsed.asciidoc.keycodes @@ -142,6 +118,29 @@ ------------------------------------------------------------------------------ +---@class markview.parsed.asciidoc.literal_blocks +--- +---@field class "asciidoc_literal_block" +---@field delimiters [ string, string ] Block delimiters. +---@field uses_tab boolean Are there tabs in the text? +--- +---@field text string[] +---@field range markview.parsed.asciidoc.literal_blocks.range + + +---@class markview.parsed.asciidoc.literal_blocks.range +--- +---@field row_start integer +---@field col_start integer +--- +---@field row_end integer +---@field col_end integer +--- +---@field start_delim integer[] Range of the block start delimiter(output of `{ TSNode:range() }`. +---@field end_delim integer[] Range of the block end delimiter(output of `{ TSNode:range() }`. + +------------------------------------------------------------------------------ + ---@class markview.parsed.asciidoc.section_titles --- ---@field class "asciidoc_section_title" @@ -179,7 +178,11 @@ ---| markview.parsed.asciidoc.admonitions ---| markview.parsed.asciidoc.document_attributes ---| markview.parsed.asciidoc.document_titles +---| markview.parsed.asciidoc.hrs +---| markview.parsed.asciidoc.images +---| markview.parsed.asciidoc.keycodes ---| markview.parsed.asciidoc.list_items +---| markview.parsed.asciidoc.literal_blocks ---| markview.parsed.asciidoc.section_titles ---| markview.parsed.asciidoc.tocs @@ -190,7 +193,11 @@ ---@field admonitions markview.parsed.asciidoc.admonitions[] ---@field document_attributes markview.parsed.asciidoc.document_attributes[] ---@field document_titles markview.parsed.asciidoc.document_titles[] +---@field hrs markview.parsed.asciidoc.hrs[] +---@field images markview.parsed.asciidoc.images[] +---@field keycodes markview.parsed.asciidoc.keycodes[] ---@field list_items markview.parsed.asciidoc.list_items[] +---@field literal_blocks markview.parsed.asciidoc.literal_blocks[] ---@field section_titles markview.parsed.asciidoc.section_titles[] ---@field tocs markview.parsed.asciidoc.tocs[] diff --git a/lua/markview/types/parsers/asciidoc_inline.lua b/lua/markview/types/parsers/asciidoc_inline.lua index 0f0ef309..a9e7ed28 100644 --- a/lua/markview/types/parsers/asciidoc_inline.lua +++ b/lua/markview/types/parsers/asciidoc_inline.lua @@ -1,5 +1,6 @@ ---@meta +------------------------------------------------------------------------------ ---@class markview.parsed.asciidoc_inline.bolds --- @@ -9,6 +10,7 @@ ---@field text string[] ---@field range markview.parsed.range +------------------------------------------------------------------------------ ---@class markview.parsed.asciidoc_inline.highlights --- @@ -18,6 +20,7 @@ ---@field text string[] ---@field range markview.parsed.range +------------------------------------------------------------------------------ ---@class markview.parsed.asciidoc_inline.italics --- @@ -27,6 +30,7 @@ ---@field text string[] ---@field range markview.parsed.range +------------------------------------------------------------------------------ ---@class markview.parsed.asciidoc_inline.monospaces --- @@ -36,6 +40,7 @@ ---@field text string[] ---@field range markview.parsed.range +------------------------------------------------------------------------------ ---@class markview.parsed.asciidoc_inline.labeled_uris --- @@ -58,6 +63,7 @@ ---@field label_col_start? integer Start column of the **label** of an URI(e.g. `foo` in `https://example.com[foo]` or `https://example.com[foo,bar]`). ---@field label_col_end? integer End column of the **label** of an URI. +------------------------------------------------------------------------------ ---@class markview.parsed.asciidoc_inline.uris --- @@ -68,22 +74,23 @@ ---@field text string[] ---@field range markview.parsed.range +------------------------------------------------------------------------------ ---@alias markview.parsed.asciidoc_inline ---| markview.parsed.asciidoc_inline.bolds ---| markview.parsed.asciidoc_inline.highlights ---| markview.parsed.asciidoc_inline.italics ----| markview.parsed.asciidoc_inline.monospaces ---| markview.parsed.asciidoc_inline.labeled_uris +---| markview.parsed.asciidoc_inline.monospaces ---| markview.parsed.asciidoc_inline.uris ---@class markview.parsed.asciidoc_inline_sorted --- ---@field bolds markview.parsed.asciidoc_inline.bolds ----@field italics markview.parsed.asciidoc_inline.italics ----@field monospaces markview.parsed.asciidoc_inline.monospaces ---@field highlights markview.parsed.asciidoc_inline.highlights +---@field italics markview.parsed.asciidoc_inline.italics ---@field labeled_uris markview.parsed.asciidoc_inline.labeled_uris +---@field monospaces markview.parsed.asciidoc_inline.monospaces ---@field uris markview.parsed.asciidoc_inline.uris From 50d672a188d4a07ac00f6a155571160dcf6e3032 Mon Sep 17 00:00:00 2001 From: Shawon Date: Fri, 23 Jan 2026 08:13:45 +0600 Subject: [PATCH 29/40] fix(asciidoc, checkboxes): `scope_hl` is implemented --- lua/markview/config/asciidoc.lua | 3 ++- lua/markview/renderers/asciidoc.lua | 25 +++++++++++++++++++++++ lua/markview/types/renderers/asciidoc.lua | 10 +++++---- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/lua/markview/config/asciidoc.lua b/lua/markview/config/asciidoc.lua index fa347b99..ed02cf0c 100644 --- a/lua/markview/config/asciidoc.lua +++ b/lua/markview/config/asciidoc.lua @@ -61,6 +61,8 @@ local keycodes = { ---@type markview.config.asciidoc return { + enable = true, + admonitions = { enable = true, @@ -123,7 +125,6 @@ return { ["?"] = { text = "󰋗", hl = "MarkviewCheckboxPending" }, ["!"] = { text = "󰀦", hl = "MarkviewCheckboxUnchecked" }, - ["*"] = { text = "󰓎", hl = "MarkviewCheckboxPending" }, ['"'] = { text = "󰸥", hl = "MarkviewCheckboxCancelled" }, ["l"] = { text = "󰆋", hl = "MarkviewCheckboxProgress" }, ["b"] = { text = "󰃀", hl = "MarkviewCheckboxProgress" }, diff --git a/lua/markview/renderers/asciidoc.lua b/lua/markview/renderers/asciidoc.lua index dda770b4..c94641e6 100644 --- a/lua/markview/renderers/asciidoc.lua +++ b/lua/markview/renderers/asciidoc.lua @@ -369,6 +369,7 @@ asciidoc.list_item = function (buffer, item) ---@cast config markview.config.asciidoc.list_items.opts + ---@type markview.config.asciidoc.checkboxes.opts? local checkbox_config; if item.checkbox == "*" then @@ -385,7 +386,11 @@ asciidoc.list_item = function (buffer, item) local shift_width = main_config.shift_width or 2; local range = item.range; + local scope_hl = checkbox_config and checkbox_config.scope_hl or nil; + for r = range.row_start, range.row_end - 1, 1 do + local line = item.text[(r - range.row_start) + 1]; + if r == range.row_start then if checkbox_config and not vim.tbl_isempty(checkbox_config) then utils.set_extmark(buffer, asciidoc.ns, r, range.col_start, { @@ -428,6 +433,26 @@ asciidoc.list_item = function (buffer, item) hl_mode = "combine", }); end + + if scope_hl then + if r == range.row_start then + utils.set_extmark(buffer, asciidoc.ns, r, range.col_start, { + undo_restore = false, invalidate = true, + end_col = #item.text[1], + + hl_group = utils.set_hl(scope_hl) + }); + elseif line ~= "" then + local spaces = line:match("^([%>%s]*)"); + + vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start + (r - 1), #spaces, { + undo_restore = false, invalidate = true, + end_col = #line, + + hl_group = utils.set_hl(scope_hl) + }); + end + end end ---|fE diff --git a/lua/markview/types/renderers/asciidoc.lua b/lua/markview/types/renderers/asciidoc.lua index 035ce7ba..2a9d4e80 100644 --- a/lua/markview/types/renderers/asciidoc.lua +++ b/lua/markview/types/renderers/asciidoc.lua @@ -2,7 +2,7 @@ ------------------------------------------------------------------------------ ---- Configuration for checkboxes. +--- Configuration for admonitions. ---@class markview.config.asciidoc.admonitions --- ---@field enable boolean Enable rendering of admonitions. @@ -38,10 +38,10 @@ --- ---@field enable boolean Enable rendering of checkboxes. --- ----@field checked markview.config.markdown_inline.checkboxes.opts Configuration for `[*]`. ----@field unchecked markview.config.markdown_inline.checkboxes.opts Configuration for `[ ]`. +---@field checked markview.config.asciidoc.checkboxes.opts Configuration for `[*]`. +---@field unchecked markview.config.asciidoc.checkboxes.opts Configuration for `[ ]`. --- ----@field [string] markview.config.markdown_inline.checkboxes.opts Configuration for `[string]` checkbox. +---@field [string] markview.config.asciidoc.checkboxes.opts Configuration for `[string]` checkbox. --[[ Options for a specific checkbox. ]] @@ -263,6 +263,8 @@ ---@class markview.config.asciidoc --- +---@field enable boolean Enable rendering of Asciidoc files? +--- ---@field admonitions markview.config.asciidoc.admonitions ---@field checkboxes markview.config.asciidoc.checkboxes ---@field document_attributes markview.config.asciidoc.document_attributes From 86e2643106a29d71403f0fad6a93b1f18df510db Mon Sep 17 00:00:00 2001 From: Shawon Date: Fri, 23 Jan 2026 08:51:12 +0600 Subject: [PATCH 30/40] fix(asciidoc): Typo fixes and corrected `hl` handling --- lua/markview/config/asciidoc.lua | 1 - lua/markview/renderers/asciidoc.lua | 26 +++++++++++------------ lua/markview/types/renderers/asciidoc.lua | 16 ++++++++------ 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/lua/markview/config/asciidoc.lua b/lua/markview/config/asciidoc.lua index ed02cf0c..31731e61 100644 --- a/lua/markview/config/asciidoc.lua +++ b/lua/markview/config/asciidoc.lua @@ -230,7 +230,6 @@ return { enable = true, sign = "󰛓 ", - sign_hl = "MarkviewPalette7Sign", icon = "󰛓 ", hl = "MarkviewPalette7", diff --git a/lua/markview/renderers/asciidoc.lua b/lua/markview/renderers/asciidoc.lua index c94641e6..049a59e9 100644 --- a/lua/markview/renderers/asciidoc.lua +++ b/lua/markview/renderers/asciidoc.lua @@ -124,10 +124,10 @@ asciidoc.document_title = function (buffer, item) conceal = "", sign_text = tostring(config.sign or ""), - sign_hl_group = utils.set_hl(config.sign_hl), + sign_hl_group = utils.set_hl(config.sign_hl or config.hl), virt_text = { - { config.icon, config.icon_hl or config.hl }, + { config.icon, utils.set_hl(config.icon_hl or config.hl) }, }, line_hl_group = utils.set_hl(config.hl), }); @@ -399,7 +399,7 @@ asciidoc.list_item = function (buffer, item) virt_text = { { config.add_padding and string.rep(" ", #item.marker * shift_width) or "" }, - { not config.conceal_on_checkboxes and config.text or "", config.hl }, + { not config.conceal_on_checkboxes and config.text or "", utils.set_hl(config.hl) }, }, hl_mode = "combine", }); @@ -409,7 +409,7 @@ asciidoc.list_item = function (buffer, item) conceal = "", virt_text = { - { checkbox_config.text or "", checkbox_config.hl }, + { checkbox_config.text or "", utils.set_hl(checkbox_config.hl) }, }, hl_mode = "combine", }); @@ -420,7 +420,7 @@ asciidoc.list_item = function (buffer, item) virt_text = { { config.add_padding and string.rep(" ", #item.marker * shift_width) or "" }, - { config.text or "", config.hl }, + { config.text or "", utils.set_hl(config.hl) }, }, hl_mode = "combine", }); @@ -473,7 +473,7 @@ asciidoc.literal_block = function (buffer, item) return; end - local label = { config.label, config.label_hl or config.hl }; + local label = { config.label, utils.set_hl(config.label_hl or config.hl) }; local win = utils.buf_getwin(buffer); --[[ *Basic* rendering of `code blocks`. ]] @@ -521,7 +521,7 @@ asciidoc.literal_block = function (buffer, item) undo_restore = false, invalidate = true, end_row = l, - line_hl_group = config.hl + line_hl_group = utils.set_hl(config.hl) }); end @@ -795,7 +795,7 @@ asciidoc.section_title = function (buffer, item) virt_text = { { string.rep(" ", (#item.marker - 1) * shift_width) }, - { config.icon, config.icon_hl or config.hl }, + { config.icon, utils.set_hl(config.icon_hl or config.hl) }, }, line_hl_group = utils.set_hl(config.hl), }); @@ -819,8 +819,8 @@ asciidoc.toc = function (buffer, item) local lines = {}; table.insert(lines, { - { main_config.icon or "", main_config.icon_hl or main_config.hl }, - { item.title or "Table of contents", main_config.hl }, + { main_config.icon or "", utils.set_hl(main_config.icon_hl or main_config.hl) }, + { item.title or "Table of contents", utils.set_hl(main_config.hl) }, }); if item.entries and #item.entries > 0 then @@ -832,12 +832,12 @@ asciidoc.toc = function (buffer, item) local config = spec.get({ "depth_" .. (entry.depth or 1) }, { source = main_config, eval_args = { buffer, item } }); if config then - local text = require("markview.renderers.asciidoc.tostring").tostring(buffer, entry.text, config.hl); + local text = require("markview.renderers.asciidoc.tostring").tostring(buffer, entry.text, utils.set_hl(config.hl) --[[@as string]]); local shift_by = (main_config.shift_width or 1) * ( (entry.depth or 1) - 1 ); local line = { - { string.rep(config.shift_char or " ", shift_by), config.hl }, - { config.icon or "", config.icon_hl or config.hl }, + { string.rep(config.shift_char or " ", shift_by), utils.set_hl(config.hl) }, + { config.icon or "", utils.set_hl(config.icon_hl or config.hl) }, }; vim.list_extend(line, text); diff --git a/lua/markview/types/renderers/asciidoc.lua b/lua/markview/types/renderers/asciidoc.lua index 2a9d4e80..341f51b2 100644 --- a/lua/markview/types/renderers/asciidoc.lua +++ b/lua/markview/types/renderers/asciidoc.lua @@ -53,23 +53,24 @@ ------------------------------------------------------------------------------ +--[[ Options for document attributes. ]] ---@class markview.config.asciidoc.document_attributes --- ----@field enable boolean +---@field enable boolean Enable concealing of document attributes? Requires `conceal_lines` support. ------------------------------------------------------------------------------ ---@class markview.config.asciidoc.document_titles --- ----@field enable boolean +---@field enable boolean Enable rendering of document titles. --- ----@field sign? string ----@field sign_hl? string +---@field sign? string Text to add to the sign column. +---@field sign_hl? string Highlight group for the sign. --- ----@field icon? string ----@field icon_hl? string +---@field icon? string Icon added before the title. +---@field icon_hl? string Highlight group for `icon`. --- ----@field hl? string +---@field hl? string Fallback for all `*_hl` options. ------------------------------------------------------------------------------ @@ -269,6 +270,7 @@ ---@field checkboxes markview.config.asciidoc.checkboxes ---@field document_attributes markview.config.asciidoc.document_attributes ---@field document_titles markview.config.asciidoc.document_titles +---@field horizontal_rules markview.config.asciidoc.hrs ---@field list_items markview.config.asciidoc.list_items ---@field section_titles markview.config.asciidoc.section_titles ---@field tocs markview.config.asciidoc.tocs From 9d961ae327d47bdd2f610e34043d12480174d88b Mon Sep 17 00:00:00 2001 From: Shawon Date: Fri, 23 Jan 2026 11:10:34 +0600 Subject: [PATCH 31/40] doc(asciidoc): Added docs --- doc/markview.nvim-asciidoc.txt | 480 ++++++++++++++++++++++ doc/tags | 1 + lua/markview/types/renderers/asciidoc.lua | 39 +- 3 files changed, 503 insertions(+), 17 deletions(-) create mode 100644 doc/markview.nvim-asciidoc.txt diff --git a/doc/markview.nvim-asciidoc.txt b/doc/markview.nvim-asciidoc.txt new file mode 100644 index 00000000..e59d22ab --- /dev/null +++ b/doc/markview.nvim-asciidoc.txt @@ -0,0 +1,480 @@ +*markview.nvim-asciidoc* 🧩 Asciidoc options for `markview.nvim` + +>lua + ---@class markview.config.asciidoc + --- + ---@field enable boolean Enable rendering of Asciidoc files? + --- + ---@field admonitions markview.config.asciidoc.admonitions + ---@field checkboxes markview.config.asciidoc.checkboxes + ---@field document_attributes markview.config.asciidoc.document_attributes + ---@field document_titles markview.config.asciidoc.document_titles + ---@field horizontal_rules markview.config.asciidoc.hrs + ---@field list_items markview.config.asciidoc.list_items + ---@field section_titles markview.config.asciidoc.section_titles + ---@field tocs markview.config.asciidoc.tocs +< + +-------------------------------------------------------------------------------- +enable + +Enables previewing asciidoc files. + +>lua + enable = true +< + +-------------------------------------------------------------------------------- +admonitions + +>lua + --- Configuration for admonitions. + ---@class markview.config.asciidoc.admonitions + --- + ---@field enable boolean Enable rendering of admonitions. + --- + ---@field default markview.config.asciidoc.admonitions.opts Default configuration for admonitions. + ---@field [string] markview.config.asciidoc.admonitions.opts Configuration for `[string]` admonitions. +< + +Changes how admonitions are shown. + +>lua + admonitions = { + enable = true, + + default = { + padding_left = " ", + padding_right = " ", + + icon = " ", + + hl = "MarkviewPalette5", + }, + + important = { + padding_left = " ", + padding_right = " ", + + icon = " ", + + hl = "MarkviewPalette1", + }, + + tip = { + padding_left = " ", + padding_right = " ", + + icon = " ", + + hl = "MarkviewPalette4", + }, + + caution = { + padding_left = " ", + padding_right = " ", + + icon = " ", + + hl = "MarkviewPalette7", + }, + + warn = { + padding_left = " ", + padding_right = " ", + + icon = " ", + + hl = "MarkviewPalette3", + }, + }, +< + +Each admonition type has the following options. + +▋  Important +▋ The key names are case-insensitive. So, `foo` & `FOO` refer to the same type. +▋ However, modifying `FOO` instead of `foo` can lead to undefined behavior. Use +▋ lower case for type names! + +>lua + ---@class markview.config.asciidoc.admonitions.opts + --- + ---@field corner_left? string Left corner. + ---@field corner_left_hl? string Highlight group for the left corner. + --- + ---@field padding_left? string Left padding(added after `corner_left`). + ---@field padding_left_hl? string Highlight group for the left padding. + --- + ---@field icon? string Icon(added after `padding_left`). + ---@field icon_hl? string Highlight group for the icon. + --- + ---@field hl? string Default highlight group(used by `*_hl` options when they are not set). + ---@field desc_hl? string Highlight group for the `description`. + --- + ---@field padding_right? string Right padding. + ---@field padding_right_hl? string Highlight group for the right padding. + --- + ---@field corner_right? string Right corner(added after `padding_right`). + ---@field corner_right_hl? string Highlight group for the right corner. +< + +-------------------------------------------------------------------------------- +document_attributes + +>lua + --[[ Options for document attributes. ]] + ---@class markview.config.asciidoc.document_attributes + --- + ---@field enable boolean Enable concealing of document attributes? Requires `conceal_lines` support. +< + +Hides document attributes + +>lua + document_attributes = { + enable = true, + }, +< + +-------------------------------------------------------------------------------- +hrs + +>lua + --- Configuration for horizontal rules. + ---@class markview.config.asciidoc.hrs + --- + ---@field enable boolean Enable preview of horizontal rules. + ---@field parts markview.config.asciidoc.hrs.part[] Parts for the horizontal rules. +< + +Changes how horizontal rules/thematic breaks are shown. + +>lua + horizontal_rules = { + enable = true, + + parts = { + { + type = "repeating", + direction = "left", + + repeat_amount = function (buffer) + local utils = require("markview.utils"); + local window = utils.buf_getwin(buffer) + + local width = vim.api.nvim_win_get_width(window) + local textoff = vim.fn.getwininfo(window)[1].textoff; + + return math.floor((width - textoff - 3) / 2); + end, + + text = "─", + + hl = { + "MarkviewGradient1", "MarkviewGradient1", + "MarkviewGradient2", "MarkviewGradient2", + "MarkviewGradient3", "MarkviewGradient3", + "MarkviewGradient4", "MarkviewGradient4", + "MarkviewGradient5", "MarkviewGradient5", + "MarkviewGradient6", "MarkviewGradient6", + "MarkviewGradient7", "MarkviewGradient7", + "MarkviewGradient8", "MarkviewGradient8", + "MarkviewGradient9", "MarkviewGradient9" + } + }, + { + type = "text", + + text = "  ", + hl = "MarkviewIcon3Fg" + }, + { + type = "repeating", + direction = "right", + + repeat_amount = function (buffer) --[[@as function]] + local utils = require("markview.utils"); + local window = utils.buf_getwin(buffer) + + local width = vim.api.nvim_win_get_width(window) + local textoff = vim.fn.getwininfo(window)[1].textoff; + + return math.ceil((width - textoff - 3) / 2); + end, + + text = "─", + hl = { + "MarkviewGradient1", "MarkviewGradient1", + "MarkviewGradient2", "MarkviewGradient2", + "MarkviewGradient3", "MarkviewGradient3", + "MarkviewGradient4", "MarkviewGradient4", + "MarkviewGradient5", "MarkviewGradient5", + "MarkviewGradient6", "MarkviewGradient6", + "MarkviewGradient7", "MarkviewGradient7", + "MarkviewGradient8", "MarkviewGradient8", + "MarkviewGradient9", "MarkviewGradient9" + } + } + } + }, +< + +parts ~ + +You can have any of the following parts. + +▋ Text + +Shows some text literally. + +>lua + ---@class markview.config.asciidoc.hrs.text + --- + ---@field type "text" Part name. + --- + ---@field hl? string Highlight group for this part. + ---@field text string Text to show. +< + +▋ Repeating + +Repeats given text by an amount. + +>lua + ---@class markview.config.asciidoc.hrs.repeating + --- + ---@field type "repeating" Part name. + --- + ---@field direction "left" | "right" Direction from which the highlight groups are applied from. + --- + ---@field repeat_amount integer | fun(buffer: integer, item: markview.parsed.asciidoc.hrs): integer How many times to repeat the text. + ---@field repeat_hl? boolean Whether to repeat the highlight groups. + ---@field repeat_text? boolean Whether to repeat the text. + --- + ---@field text string | string[] Text to repeat. + ---@field hl? string | string[] Highlight group for the text. +< + +-------------------------------------------------------------------------------- +list_items + +>lua + --- Configuration for list items. + ---@class markview.config.asciidoc.list_items + --- + ---@field enable boolean + ---@field shift_width integer | fun(buffer: integer, item: markview.parsed.markdown.list_items): integer Virtual indentation size for previewed list items. + --- + ---@field marker_dot markview.config.asciidoc.list_items.opts Configuration for `.` list items. + ---@field marker_minus markview.config.asciidoc.list_items.opts Configuration for `-` list items. + ---@field marker_star markview.config.asciidoc.list_items.opts Configuration for `*` list items. + --- + ---@field wrap? boolean Enables wrap support. +< + +Changes how list items are shown. + +>lua + list_items = { + enable = true, + shift_width = 4, + + marker_dot = { + add_padding = true, + conceal_on_checkboxes = true, + + text = function (_, item) + return string.format("%d.", item.n); + end, + hl = "@markup.list.markdown", + }, + + marker_minus = { + add_padding = true, + conceal_on_checkboxes = true, + + text = "●", + hl = "MarkviewListItemMinus" + }, + + marker_star = { + add_padding = true, + conceal_on_checkboxes = true, + + text = "◇", + hl = "MarkviewListItemStar" + }, + }, +< + +Each list item type has the following options. + +>lua + ---@class markview.config.asciidoc.list_items.opts + --- + ---@field enable? boolean Enable rendering of this list item type? + --- + ---@field add_padding boolean When `true`, Add padding before the list item. + ---@field conceal_on_checkboxes? boolean Should the list item marker be hidden if the item contains a `checkbox`? + --- + ---[[ Text used to replace the list item marker. ]] + ---@field text? + ---| string + ---| fun(buffer: integer, item: markview.parsed.asciidoc.list_items): string Dynamic marker. Used for stuff like adding list index to `ordered list items`. + --- + ---@field hl? string Highlight group for the `text`. +< + +-------------------------------------------------------------------------------- +section_titles + +>lua + --- Configuration for section titles. + ---@class markview.config.asciidoc.section_titles + --- + ---@field enable boolean Enable rendering of section titles. + ---@field shift_width integer Amount of spaces to add to the start for each title level. Useful to visualize nesting of sections. + --- + ---@field title_1 markview.config.asciidoc.section_titles.opts + ---@field title_2 markview.config.asciidoc.section_titles.opts + ---@field title_3 markview.config.asciidoc.section_titles.opts + ---@field title_4 markview.config.asciidoc.section_titles.opts + ---@field title_5 markview.config.asciidoc.section_titles.opts +< + +Changes how section titles are shown. + +>lua + section_titles = { + enable = true, + + title_1 = { + sign = "󰌕 ", sign_hl = "MarkviewHeading1Sign", + + icon = "󰼏 ", hl = "MarkviewHeading1", + }, + title_2 = { + sign = "󰌖 ", sign_hl = "MarkviewHeading2Sign", + + icon = "󰎨 ", hl = "MarkviewHeading2", + }, + title_3 = { + + icon = "󰼑 ", hl = "MarkviewHeading3", + }, + title_4 = { + + icon = "󰎲 ", hl = "MarkviewHeading4", + }, + title_5 = { + + icon = "󰼓 ", hl = "MarkviewHeading5", + }, + title_6 = { + + icon = "󰎴 ", hl = "MarkviewHeading6", + }, + + shift_width = 1, + }, +< + +Each section title level has the following options. + +>lua + ---@class markview.config.asciidoc.section_titles.opts + --- + ---@field icon? string Icon added before the title. + ---@field icon_hl? string Highlight group for `icon`. + --- + ---@field sign? string Sign to show in the sign column. + ---@field sign_hl? string Highlight group for the `sign`. + --- + ---@field hl? string Fallback highlight group for the `*_hl` options. +< + +-------------------------------------------------------------------------------- +tocs + +>lua + --- Configuration for generated Table of contents section.. + ---@class markview.config.asciidoc.tocs + --- + ---@field enable boolean Enable rendering of automated TOC. + ---@field shift_width integer Amount if `shift_char` to add per item depth level. + --- + ---@field icon? string Icon for the TOC title. + ---@field icon_hl? string Highlight group for `icon`. + --- + ---@field sign? string Sign for the TOC title. + ---@field sign_hl? string Highlight group for `sign`. + --- + ---@field hl? string Highlight group for the TOC title. + --- + ---@field depth_1 markview.config.asciidoc.tocs.opts + ---@field depth_2 markview.config.asciidoc.tocs.opts + ---@field depth_3 markview.config.asciidoc.tocs.opts + ---@field depth_4 markview.config.asciidoc.tocs.opts + ---@field depth_5 markview.config.asciidoc.tocs.opts +< + +Changes how automatic TOCs are shown. + +>lua + tocs = { + enable = true, + + shift_width = 2, + hl = "MarkviewPalette2Fg", + + sign = "󰙅 ", + sign_hl = "MarkviewPalette2Sign", + + depth_1 = { + icon = "◆ ", + icon_hl = "Comment", + + hl = "MarkviewPalette5Fg", + }, + depth_2 = { + icon = "◇ ", + icon_hl = "Comment", + + hl = "MarkviewPalette5Fg", + }, + depth_3 = { + icon = "◆ ", + icon_hl = "Comment", + + hl = "MarkviewPalette5Fg", + }, + depth_4 = { + icon = "◇ ", + icon_hl = "Comment", + + hl = "MarkviewPalette5Fg", + }, + depth_5 = { + icon = "◆ ", + icon_hl = "Comment", + + hl = "MarkviewPalette5Fg", + }, + }, +< + +Each item depth has the following options. + +>lua + --- Options for a specific item depth. + ---@class markview.config.asciidoc.tocs.opts + --- + ---@field shift_char? string The character used to shift the entry(helps visualize nesting, document structure). + ---@field hl? string Highlight group for the text. + --- + ---@field icon? string Icon for the TOC title. + ---@field icon_hl? string Highlight group for `icon`. +< + +vim:ft=help:textwidth=80:tabstop=4:noexpandtab: diff --git a/doc/tags b/doc/tags index 959bcade..846b177f 100644 --- a/doc/tags +++ b/doc/tags @@ -1,6 +1,7 @@ !_TAG_FILE_ENCODING utf-8 // markview.nvim markview.nvim.txt /*markview.nvim* markview.nvim-advanced markview.nvim-advanced.txt /*markview.nvim-advanced* +markview.nvim-asciidoc markview.nvim-asciidoc.txt /*markview.nvim-asciidoc* markview.nvim-autocmds markview.nvim-autocmds.txt /*markview.nvim-autocmds* markview.nvim-commands markview.nvim.txt /*markview.nvim-commands* markview.nvim-comment markview.nvim-comment.txt /*markview.nvim-comment* diff --git a/lua/markview/types/renderers/asciidoc.lua b/lua/markview/types/renderers/asciidoc.lua index 341f51b2..7e6cf3b9 100644 --- a/lua/markview/types/renderers/asciidoc.lua +++ b/lua/markview/types/renderers/asciidoc.lua @@ -196,22 +196,25 @@ ---@class markview.config.asciidoc.list_items.opts --- ----@field add_padding boolean ----@field conceal_on_checkboxes? boolean ----@field enable? boolean ----@field hl? string +---@field enable? boolean Enable rendering of this list item type? +--- +---@field add_padding boolean When `true`, Add padding before the list item. +---@field conceal_on_checkboxes? boolean Should the list item marker be hidden if the item contains a `checkbox`? --- ---[[ Text used to replace the list item marker. ]] ---@field text? ---| string ----| fun(buffer: integer, item: markview.parsed.asciidoc.list_items): string +---| fun(buffer: integer, item: markview.parsed.asciidoc.list_items): string Dynamic marker. Used for stuff like adding list index to `ordered list items`. +--- +---@field hl? string Highlight group for the `text`. ------------------------------------------------------------------------------ +--- Configuration for section titles. ---@class markview.config.asciidoc.section_titles --- ----@field enable boolean ----@field shift_width integer +---@field enable boolean Enable rendering of section titles. +---@field shift_width integer Amount of spaces to add to the start for each title level. Useful to visualize nesting of sections. --- ---@field title_1 markview.config.asciidoc.section_titles.opts ---@field title_2 markview.config.asciidoc.section_titles.opts @@ -222,20 +225,21 @@ ---@class markview.config.asciidoc.section_titles.opts --- ----@field icon? string ----@field icon_hl? string +---@field icon? string Icon added before the title. +---@field icon_hl? string Highlight group for `icon`. --- ----@field sign? string ----@field sign_hl? string +---@field sign? string Sign to show in the sign column. +---@field sign_hl? string Highlight group for the `sign`. --- ----@field hl? string +---@field hl? string Fallback highlight group for the `*_hl` options. ------------------------------------------------------------------------------ +--- Configuration for generated Table of contents section.. ---@class markview.config.asciidoc.tocs --- ----@field enable boolean ----@field shift_width integer +---@field enable boolean Enable rendering of automated TOC. +---@field shift_width integer Amount if `shift_char` to add per item depth level. --- ---@field icon? string Icon for the TOC title. ---@field icon_hl? string Highlight group for `icon`. @@ -243,7 +247,7 @@ ---@field sign? string Sign for the TOC title. ---@field sign_hl? string Highlight group for `sign`. --- ----@field hl? string +---@field hl? string Highlight group for the TOC title. --- ---@field depth_1 markview.config.asciidoc.tocs.opts ---@field depth_2 markview.config.asciidoc.tocs.opts @@ -252,10 +256,11 @@ ---@field depth_5 markview.config.asciidoc.tocs.opts +--- Options for a specific item depth. ---@class markview.config.asciidoc.tocs.opts --- ----@field shift_char? string ----@field hl? string +---@field shift_char? string The character used to shift the entry(helps visualize nesting, document structure). +---@field hl? string Highlight group for the text. --- ---@field icon? string Icon for the TOC title. ---@field icon_hl? string Highlight group for `icon`. From 27c17741a89096b96327a8fa706b7661597064d0 Mon Sep 17 00:00:00 2001 From: Shawon Date: Fri, 23 Jan 2026 11:52:24 +0600 Subject: [PATCH 32/40] doc(asciidoc_inline): Added docs --- doc/markview.nvim-asciidoc_inline.txt | 444 ++++++++++++++++++ doc/tags | 1 + .../types/renderers/asciidoc_inline.lua | 7 +- 3 files changed, 450 insertions(+), 2 deletions(-) create mode 100644 doc/markview.nvim-asciidoc_inline.txt diff --git a/doc/markview.nvim-asciidoc_inline.txt b/doc/markview.nvim-asciidoc_inline.txt new file mode 100644 index 00000000..25632de6 --- /dev/null +++ b/doc/markview.nvim-asciidoc_inline.txt @@ -0,0 +1,444 @@ +*markview.nvim-asciidoc_inline* 🧩 Inline asciidoc options for `markview.nvim` + +>lua + ---@class markview.config.asciidoc_inline + --- + ---@field enable boolean Enable rendering of inline asciidoc. + --- + ---@field bolds markview.config.asciidoc_inline.bolds + ---@field highlights markview.config.asciidoc_inline.highlights + ---@field italics markview.config.asciidoc_inline.italics + ---@field monospaces markview.config.asciidoc_inline.monospaces + ---@field uris markview.config.asciidoc_inline.uris +< + +-------------------------------------------------------------------------------- +enable + +Enables previewing inline asciidoc. + +>lua + enable = true +< + +-------------------------------------------------------------------------------- +bolds + +>lua + ---@class markview.config.asciidoc_inline.bolds + --- + ---@field enable boolean +< + +Hides the delimiters surrounding bold text. + +>lua + bolds = { enable = true }, +< + +-------------------------------------------------------------------------------- +highlights + +>lua + --- Configuration for Obsidian-style highlighted texts. + ---@class markview.config.asciidoc_inline.highlights + --- + ---@field enable boolean Enable rendering of highlighted text. + --- + ---@field default markview.config.asciidoc_inline.highlights.opts Default configuration for highlighted text. + ---@field [string] markview.config.asciidoc_inline.highlights.opts Configuration for highlighted text that matches `string`. +< + +Changes how specific highlights are shown. + +>lua + highlights = { + enable = true, + + default = { + padding_left = " ", + padding_right = " ", + + hl = "MarkviewPalette3" + } + }, +< + +Highlights can be made to look different based on lua patterns matching the +content(`default` also works the same way but acts as the default style). These +can have the following options. + +>lua + --[[ Options for a specific highlight type. ]] + ---@alias markview.config.asciidoc_inline.highlights.opts markview.config.__inline +< + +-------------------------------------------------------------------------------- +italics + +>lua + ---@class markview.config.asciidoc_inline.italics + --- + ---@field enable boolean +< + +Hides the delimiters surrounding italic text. + +>lua + italics = { enable = true }, +< + +-------------------------------------------------------------------------------- +monospaces + +>lua + ---@alias markview.config.asciidoc_inline.monospaces markview.config.__inline +< + +Hides the delimiters surrounding `monospace text`. + +>lua + monospaces = { + enable = true, + hl = "MarkviewInlineCode", + + padding_left = " ", + padding_right = " " + }, +< + +-------------------------------------------------------------------------------- +uris + +>lua + ---@class markview.config.asciidoc_inline.uris + --- + ---@field enable boolean Enable rendering of unlabeled URIs. + --- + ---@field default markview.config.asciidoc_inline.uris.opts Default configuration for URIs. + ---@field [string] markview.config.asciidoc_inline.uris.opts Configuration for URIs that matches `string`. +< + +Changes how specific uris are shown. + +>lua + uris = { + enable = true, + + default = { + icon = "󰌷 ", + hl = "MarkviewHyperlink", + }, + + ---|fS + + --NOTE(@OXY2DEV): Github sites. + + ["github%.com/[%a%d%-%_%.]+%/?$"] = { + --- github.com/ + icon = " ", + hl = "MarkviewPalette0Fg", + + text = function (_, item) + return string.match(item.destination, "github%.com/([%a%d%-%_%.]+)%/?$"); + end + }, + ["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+%/?$"] = { + --- github.com// + icon = "󰳐 ", + hl = "MarkviewPalette0Fg", + + text = function (_, item) + return string.match(item.destination, "github%.com/([%a%d%-%_%.]+/[%a%d%-%_%.]+)%/?$"); + end + }, + ["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+/tree/[%a%d%-%_%.]+%/?$"] = { + --- github.com///tree/ + icon = " ", + hl = "MarkviewPalette0Fg", + + text = function (_, item) + local repo, branch = string.match(item.destination, "github%.com/([%a%d%-%_%.]+/[%a%d%-%_%.]+)/tree/([%a%d%-%_%.]+)%/?$"); + return repo .. " at " .. branch; + end + }, + ["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+/commits/[%a%d%-%_%.]+%/?$"] = { + --- github.com///commits/ + icon = " ", + hl = "MarkviewPalette0Fg", + + text = function (_, item) + return string.match(item.destination, "github%.com/([%a%d%-%_%.]+/[%a%d%-%_%.]+/commits/[%a%d%-%_%.]+)%/?$"); + end + }, + + ["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+%/releases$"] = { + --- github.com///releases + icon = " ", + hl = "MarkviewPalette0Fg", + + text = function (_, item) + return "Releases • " .. string.match(item.destination, "github%.com/([%a%d%-%_%.]+/[%a%d%-%_%.]+)%/releases$"); + end + }, + ["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+%/tags$"] = { + --- github.com///tags + icon = " ", + hl = "MarkviewPalette0Fg", + + text = function (_, item) + return "Tags • " .. string.match(item.destination, "github%.com/([%a%d%-%_%.]+/[%a%d%-%_%.]+)%/tags$"); + end + }, + ["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+%/issues$"] = { + --- github.com///issues + icon = " ", + hl = "MarkviewPalette0Fg", + + text = function (_, item) + return "Issues • " .. string.match(item.destination, "github%.com/([%a%d%-%_%.]+/[%a%d%-%_%.]+)%/issues$"); + end + }, + ["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+%/pulls$"] = { + --- github.com///pulls + icon = " ", + hl = "MarkviewPalette0Fg", + + text = function (_, item) + return "Pull requests • " .. string.match(item.destination, "github%.com/([%a%d%-%_%.]+/[%a%d%-%_%.]+)%/pulls$"); + end + }, + + ["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+%/wiki$"] = { + --- github.com///wiki + icon = " ", + hl = "MarkviewPalette0Fg", + + text = function (_, item) + return "Wiki • " .. string.match(item.destination, "github%.com/([%a%d%-%_%.]+/[%a%d%-%_%.]+)%/wiki$"); + end + }, + + --- NOTE(@OXY2DEV): Commonly used sites by programmers. + + ["developer%.mozilla%.org"] = { + priority = -9999, + + icon = "󰖟 ", + hl = "MarkviewPalette5Fg" + }, + + ["w3schools%.com"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette4Fg" + }, + + ["stackoverflow%.com"] = { + priority = -9999, + + icon = "󰓌 ", + hl = "MarkviewPalette2Fg" + }, + + ["reddit%.com"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette2Fg" + }, + + ["github%.com"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette6Fg" + }, + + ["gitlab%.com"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette2Fg" + }, + + ["dev%.to"] = { + priority = -9999, + + icon = "󱁴 ", + hl = "MarkviewPalette0Fg" + }, + + ["codepen%.io"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette6Fg" + }, + + ["replit%.com"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette2Fg" + }, + + ["jsfiddle%.net"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette5Fg" + }, + + ["npmjs%.com"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette0Fg" + }, + + ["pypi%.org"] = { + priority = -9999, + + icon = "󰆦 ", + hl = "MarkviewPalette0Fg" + }, + + ["mvnrepository%.com"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette1Fg" + }, + + ["medium%.com"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette6Fg" + }, + + ["linkedin%.com"] = { + priority = -9999, + + icon = "󰌻 ", + hl = "MarkviewPalette5Fg" + }, + + ["news%.ycombinator%.com"] = { + priority = -9999, + + icon = " ", + hl = "MarkviewPalette2Fg" + }, + + ["neovim%.io/doc/user/.*#%_?.*$"] = { + icon = " ", + hl = "MarkviewPalette4Fg", + + text = function (_, item) + local file, tag = string.match(item.destination, "neovim%.io/doc/user/(.*)#%_?(.*)$"); + --- The actual website seems to show + --- _ in the site name so, we won't + --- be replacing `_`s with ` `s. + file = string.gsub(file, "%.html$", ""); + + return string.format("%s(%s) - Neovim docs", normalize_str(file), tag); + end + }, + ["neovim%.io/doc/user/.*$"] = { + icon = " ", + hl = "MarkviewPalette4Fg", + + text = function (_, item) + local file = string.match(item.destination, "neovim%.io/doc/user/(.*)$"); + file = string.gsub(file, "%.html$", ""); + + return string.format("%s - Neovim docs", normalize_str(file)); + end + }, + + ["github%.com/vim/vim"] = { + priority = -100, + + icon = " ", + hl = "MarkviewPalette4Fg", + }, + + ["github%.com/neovim/neovim"] = { + priority = -100, + + icon = " ", + hl = "MarkviewPalette4Fg", + }, + + ["vim%.org"] = { + icon = " ", + hl = "MarkviewPalette4Fg", + }, + + ["luals%.github%.io/wiki/?.*$"] = { + icon = " ", + hl = "MarkviewPalette5Fg", + + text = function (_, item) + if string.match(item.destination, "luals%.github%.io/wiki/(.-)/#(.+)$") then + local page_mappings = { + annotations = { + ["as"] = "@as", + ["alias"] = "@alias", + ["async"] = "@async", + ["cast"] = "@cast", + ["class"] = "@class", + ["deprecated"] = "@deprecated", + ["diagnostic"] = "@diagnostic", + ["enum"] = "@enum", + ["field"] = "@field", + ["generic"] = "@generic", + ["meta"] = "@meta", + ["module"] = "@module", + ["nodiscard"] = "@nodiscard", + ["operator"] = "@operator", + ["overload"] = "@overload", + ["package"] = "@package", + ["param"] = "@param", + ["see"] = "@see", + ["source"] = "@source", + ["type"] = "@type", + ["vaarg"] = "@vaarg", + ["version"] = "@version" + } + }; + + local page, section = string.match(item.destination, "luals%.github%.io/wiki/(.-)/#(.+)$"); + + if page_mappings[page] and page_mappings[page][section] then + section = page_mappings[page][section]; + else + section = normalize_str(string.gsub(section, "%-", " ")); + end + + return string.format("%s(%s) | Lua Language Server", normalize_str(page), section); + elseif string.match(item.destination, "") then + local page = string.match(item.destination, "luals%.github%.io/wiki/(.-)/?$"); + + return string.format("%s | Lua Language Server", normalize_str(page)); + else + return item.destination; + end + end + }, + + ---|fE + }, +< + +Each uri type has the following options. + +>lua + --[[ Options for a specific highlight type. ]] + ---@alias markview.config.asciidoc_inline.uris.opts markview.config.__inline +< + +vim:ft=help:textwidth=80:tabstop=4:noexpandtab: diff --git a/doc/tags b/doc/tags index 846b177f..a56b77ad 100644 --- a/doc/tags +++ b/doc/tags @@ -2,6 +2,7 @@ markview.nvim markview.nvim.txt /*markview.nvim* markview.nvim-advanced markview.nvim-advanced.txt /*markview.nvim-advanced* markview.nvim-asciidoc markview.nvim-asciidoc.txt /*markview.nvim-asciidoc* +markview.nvim-asciidoc_inline markview.nvim-asciidoc_inline.txt /*markview.nvim-asciidoc_inline* markview.nvim-autocmds markview.nvim-autocmds.txt /*markview.nvim-autocmds* markview.nvim-commands markview.nvim.txt /*markview.nvim-commands* markview.nvim-comment markview.nvim-comment.txt /*markview.nvim-comment* diff --git a/lua/markview/types/renderers/asciidoc_inline.lua b/lua/markview/types/renderers/asciidoc_inline.lua index f761df84..aae40c9a 100644 --- a/lua/markview/types/renderers/asciidoc_inline.lua +++ b/lua/markview/types/renderers/asciidoc_inline.lua @@ -17,7 +17,7 @@ ------------------------------------------------------------------------------ ---[[ Options for a specific footnote type. ]] +--[[ Options for a specific highlight type. ]] ---@alias markview.config.asciidoc_inline.highlights.opts markview.config.__inline ------------------------------------------------------------------------------ @@ -67,10 +67,13 @@ ------------------------------------------------------------------------------ + ---@class markview.config.asciidoc_inline --- +---@field enable boolean Enable rendering of inline asciidoc. +--- ---@field bolds markview.config.asciidoc_inline.bolds ----@field highlights markview.config.asciidoc_inline.highlights Highlighted text configuration. +---@field highlights markview.config.asciidoc_inline.highlights ---@field italics markview.config.asciidoc_inline.italics ---@field monospaces markview.config.asciidoc_inline.monospaces ---@field uris markview.config.asciidoc_inline.uris From afe02adb1bba682c0887bae9dfac309ac9d52d79 Mon Sep 17 00:00:00 2001 From: Shawon Date: Fri, 23 Jan 2026 14:54:32 +0600 Subject: [PATCH 33/40] fix(asciidoc): Updated base config --- lua/markview/config/asciidoc.lua | 7 ++++--- lua/markview/config/asciidoc_inline.lua | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lua/markview/config/asciidoc.lua b/lua/markview/config/asciidoc.lua index 31731e61..df2f2c4c 100644 --- a/lua/markview/config/asciidoc.lua +++ b/lua/markview/config/asciidoc.lua @@ -213,11 +213,12 @@ return { hl = "MarkviewCode", - sign = "hi", + sign = "󱨏 ", + sign_hl = "MarkviewPalette6Sign", - label = "Hello ", + label = "󱨏 Raw ", label_direction = "right", - label_hl = nil, + label_hl = "MarkviewIcon6", min_width = 60, pad_amount = 2, diff --git a/lua/markview/config/asciidoc_inline.lua b/lua/markview/config/asciidoc_inline.lua index c4ca688c..033692ce 100644 --- a/lua/markview/config/asciidoc_inline.lua +++ b/lua/markview/config/asciidoc_inline.lua @@ -8,6 +8,8 @@ end ---@type markview.config.asciidoc_inline return { + enable = true, + bolds = { enable = true }, italics = { enable = true }, From 4107360939e69730b4ab508b7c2bf0d275114a4a Mon Sep 17 00:00:00 2001 From: Shawon Date: Fri, 23 Jan 2026 14:55:51 +0600 Subject: [PATCH 34/40] refactor(asciidoc): Removed boilerplate --- lua/markview/renderers/asciidoc.lua | 78 ++++++++++------------------- 1 file changed, 27 insertions(+), 51 deletions(-) diff --git a/lua/markview/renderers/asciidoc.lua b/lua/markview/renderers/asciidoc.lua index 049a59e9..a4d1a176 100644 --- a/lua/markview/renderers/asciidoc.lua +++ b/lua/markview/renderers/asciidoc.lua @@ -33,9 +33,7 @@ asciidoc.admonition = function (buffer, item) local row_end = range.kind[3]; local col_end = range.kind[4]; - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start, { - undo_restore = false, invalidate = true, - + utils.set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start, { virt_text_pos = "inline", virt_text = { { config.corner_left or "", utils.set_hl(config.corner_left_hl or config.hl) }, @@ -47,16 +45,14 @@ asciidoc.admonition = function (buffer, item) hl_mode = "combine" }); - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.kind[1], range.kind[2], { - undo_restore = false, invalidate = true, + utils.set_extmark(buffer, asciidoc.ns, range.kind[1], range.kind[2], { end_row = row_end, end_col = col_end, hl_group = utils.set_hl(config.hl) }); - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, row_end, col_end - 1, { - undo_restore = false, invalidate = true, + utils.set_extmark(buffer, asciidoc.ns, row_end, col_end - 1, { end_col = col_end, conceal = "", @@ -70,8 +66,7 @@ asciidoc.admonition = function (buffer, item) }); if config.desc_hl then - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, row_end, col_end, { - undo_restore = false, invalidate = true, + utils.set_extmark(buffer, asciidoc.ns, row_end, col_end, { end_row = range.row_end, end_col = range.col_end, @@ -191,8 +186,7 @@ asciidoc.hr = function (buffer, item) end end - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, 0, { - undo_restore = false, invalidate = true, + utils.set_extmark(buffer, asciidoc.ns, range.row_start, 0, { virt_text_pos = "overlay", virt_text = virt_text, @@ -437,7 +431,6 @@ asciidoc.list_item = function (buffer, item) if scope_hl then if r == range.row_start then utils.set_extmark(buffer, asciidoc.ns, r, range.col_start, { - undo_restore = false, invalidate = true, end_col = #item.text[1], hl_group = utils.set_hl(scope_hl) @@ -445,8 +438,7 @@ asciidoc.list_item = function (buffer, item) elseif line ~= "" then local spaces = line:match("^([%>%s]*)"); - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start + (r - 1), #spaces, { - undo_restore = false, invalidate = true, + utils.set_extmark(buffer, asciidoc.ns, range.row_start + (r - 1), #spaces, { end_col = #line, hl_group = utils.set_hl(scope_hl) @@ -484,9 +476,7 @@ asciidoc.literal_block = function (buffer, item) local conceal_to = #delims[1]; if config.label_direction == nil or config.label_direction == "left" then - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, conceal_from, { - undo_restore = false, invalidate = true, - + utils.set_extmark(buffer, asciidoc.ns, range.row_start, conceal_from, { end_col = conceal_to, conceal = "", @@ -499,9 +489,7 @@ asciidoc.literal_block = function (buffer, item) line_hl_group = utils.set_hl(config.hl) }); else - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, conceal_from, { - undo_restore = false, invalidate = true, - + utils.set_extmark(buffer, asciidoc.ns, range.row_start, conceal_from, { end_col = conceal_to, conceal = "", @@ -517,16 +505,14 @@ asciidoc.literal_block = function (buffer, item) --- Background for l = range.row_start + 1, range.row_end - 1 do - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, l, 0, { - undo_restore = false, invalidate = true, + utils.set_extmark(buffer, asciidoc.ns, l, 0, { end_row = l, line_hl_group = utils.set_hl(config.hl) }); end - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_end, (range.col_start + #item.text[#item.text]) - #delims[2], { - undo_restore = false, invalidate = true, + utils.set_extmark(buffer, asciidoc.ns, range.row_end, (range.col_start + #item.text[#item.text]) - #delims[2], { end_col = range.col_start + #item.text[#item.text], conceal = "", @@ -576,10 +562,8 @@ asciidoc.literal_block = function (buffer, item) string.rep(pad_char, left_padding) ); - -- Hide the leading `backticks`s. - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, delim_conceal_from, { - undo_restore = false, invalidate = true, - + -- Hide the leading `dot`s. + utils.set_extmark(buffer, asciidoc.ns, range.row_start, delim_conceal_from, { end_col = conceal_to, conceal = "", @@ -588,7 +572,7 @@ asciidoc.literal_block = function (buffer, item) }); if config.label_direction == "right" then - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, delim_conceal_from, { + utils.set_extmark(buffer, asciidoc.ns, range.row_start, delim_conceal_from, { virt_text_pos = "inline", virt_text = { { @@ -598,7 +582,7 @@ asciidoc.literal_block = function (buffer, item) } }); else - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start + #item.text[1], { + utils.set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start + #item.text[1], { virt_text_pos = "inline", virt_text = { { @@ -616,7 +600,9 @@ asciidoc.literal_block = function (buffer, item) -- 2. Total block width - Used space local spacing = block_width - (label_width + pad_width); - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start + #item.text[1], { + utils.set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start + #item.text[1], { + right_gravity = config.label_direction ~= "right", + virt_text_pos = "inline", virt_text = { { @@ -639,7 +625,9 @@ asciidoc.literal_block = function (buffer, item) top_border.col_start = range.start_delim[4]; end - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start, top_border.col_start, { + utils.set_extmark(buffer, asciidoc.ns, range.row_start, top_border.col_start, { + right_gravity = config.label_direction == "right", + virt_text_pos = "inline", virt_text = { label } }); @@ -653,9 +641,7 @@ asciidoc.literal_block = function (buffer, item) local line = item.text[l + 1]; if width ~= 0 then - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start + l, line ~= "" and range.col_start or 0, { - undo_restore = false, invalidate = true, - + utils.set_extmark(buffer, asciidoc.ns, range.row_start + l, line ~= "" and range.col_start or 0, { virt_text_pos = "inline", virt_text = { { @@ -665,9 +651,7 @@ asciidoc.literal_block = function (buffer, item) }, }); - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start + l, range.col_start + #line, { - undo_restore = false, invalidate = true, - + utils.set_extmark(buffer, asciidoc.ns, range.row_start + l, range.col_start + #line, { virt_text_pos = "inline", virt_text = { { @@ -682,8 +666,7 @@ asciidoc.literal_block = function (buffer, item) }); --- Background - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start + l, range.col_start, { - undo_restore = false, invalidate = true, + utils.set_extmark(buffer, asciidoc.ns, range.row_start + l, range.col_start, { end_col = range.col_start + #line, hl_group = utils.set_hl(config.hl --[[ @as string ]]) @@ -691,9 +674,7 @@ asciidoc.literal_block = function (buffer, item) else local buf_line = vim.api.nvim_buf_get_lines(buffer, range.row_start + l, range.row_start + l + 1, false)[1]; - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_start + l, #buf_line, { - undo_restore = false, invalidate = true, - + utils.set_extmark(buffer, asciidoc.ns, range.row_start + l, #buf_line, { virt_text_pos = "inline", virt_text = { { @@ -720,15 +701,12 @@ asciidoc.literal_block = function (buffer, item) if item.delimiters[2] then local end_delim_conceal_from = range.end_delim[2] + #string.match(item.delimiters[2], "^%s*"); - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_end, end_delim_conceal_from, { - undo_restore = false, invalidate = true, + utils.set_extmark(buffer, asciidoc.ns, range.row_end, end_delim_conceal_from, { end_col = range.col_start + #item.text[#item.text], conceal = "" }); - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_end, end_delim_conceal_from, { - undo_restore = false, invalidate = true, - + utils.set_extmark(buffer, asciidoc.ns, range.row_end, end_delim_conceal_from, { virt_text_pos = "inline", virt_text = { { @@ -738,9 +716,7 @@ asciidoc.literal_block = function (buffer, item) } }); else - vim.api.nvim_buf_set_extmark(buffer, asciidoc.ns, range.row_end, range.col_start, { - undo_restore = false, invalidate = true, - + utils.set_extmark(buffer, asciidoc.ns, range.row_end, range.col_start, { virt_text_pos = "inline", virt_text = { { From 508b6b969f74972cee181592004ddba2cf5f5a6b Mon Sep 17 00:00:00 2001 From: Shawon Date: Fri, 23 Jan 2026 15:21:30 +0600 Subject: [PATCH 35/40] fix(asciidoc, list_items): Item index is now correctly parsed --- lua/markview/parsers/asciidoc.lua | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lua/markview/parsers/asciidoc.lua b/lua/markview/parsers/asciidoc.lua index 4ff3d392..8e0cd967 100644 --- a/lua/markview/parsers/asciidoc.lua +++ b/lua/markview/parsers/asciidoc.lua @@ -281,18 +281,6 @@ asciidoc.list_item = function (buffer, TSNode, text, range) local marker; - while prev do - if prev:type() == "ordered_list_item" then - if is_on_same_level(buffer, marker, prev) then - N = N + 1; - else - break; - end - end - - prev = prev:prev_named_sibling(); - end - local checkbox; for child in TSNode:iter_children() do @@ -336,6 +324,19 @@ asciidoc.list_item = function (buffer, TSNode, text, range) end end + -- NOTE: Check list index after getting the list marker. + while prev do + if prev:type() == "ordered_list_item" then + if is_on_same_level(buffer, marker, prev) then + N = N + 1; + else + break; + end + end + + prev = prev:prev_named_sibling(); + end + asciidoc.insert({ class = "asciidoc_list_item", From b097da24a42264129b060535dbbe164b5006c3a3 Mon Sep 17 00:00:00 2001 From: Shawon Date: Fri, 23 Jan 2026 15:21:48 +0600 Subject: [PATCH 36/40] refactor(asciidoc): Final cleanup --- lua/markview/config/asciidoc.lua | 62 ++++++++--------- test/asciidoc.adoc | 114 ++++++++----------------------- 2 files changed, 60 insertions(+), 116 deletions(-) diff --git a/lua/markview/config/asciidoc.lua b/lua/markview/config/asciidoc.lua index df2f2c4c..8fafbb62 100644 --- a/lua/markview/config/asciidoc.lua +++ b/lua/markview/config/asciidoc.lua @@ -315,6 +315,37 @@ return { ["ENTER"] = keycodes.enter, }, + list_items = { + enable = true, + shift_width = 4, + + marker_dot = { + add_padding = true, + conceal_on_checkboxes = true, + + text = function (_, item) + return string.format("%d.", item.n); + end, + hl = "@markup.list.markdown", + }, + + marker_minus = { + add_padding = true, + conceal_on_checkboxes = true, + + text = "●", + hl = "MarkviewListItemMinus" + }, + + marker_star = { + add_padding = true, + conceal_on_checkboxes = true, + + text = "◇", + hl = "MarkviewListItemStar" + }, + }, + tocs = { enable = true, @@ -355,35 +386,4 @@ return { hl = "MarkviewPalette5Fg", }, }, - - list_items = { - enable = true, - shift_width = 4, - - marker_dot = { - add_padding = true, - conceal_on_checkboxes = true, - - text = function (_, item) - return string.format("%d.", item.n); - end, - hl = "@markup.list.markdown", - }, - - marker_minus = { - add_padding = true, - conceal_on_checkboxes = true, - - text = "●", - hl = "MarkviewListItemMinus" - }, - - marker_star = { - add_padding = true, - conceal_on_checkboxes = true, - - text = "◇", - hl = "MarkviewListItemStar" - }, - }, }; diff --git a/test/asciidoc.adoc b/test/asciidoc.adoc index 3ae10628..c760a812 100644 --- a/test/asciidoc.adoc +++ b/test/asciidoc.adoc @@ -1,5 +1,6 @@ -''' += Asciidoc examples +== Admonitions NOTE: An admonition draws the reader's attention to auxiliary information. @@ -15,17 +16,25 @@ WARNING: The software you're about to use is untested. IMPORTANT: Sign off before stepping away from your computer. -[NOTE] -==== -An admonition block may contain compound content. +== Checkboxes + +=== Main + +- [*] Checked +- [ ] Unchecked -.A list -- one -- two -- three +=== Custom states -Another paragraph. -==== +- [/] Pending +- [>] Reschedule +- [-] Cancelled + + +== horizontal rules + +''' + +== Literal blocks .... error: 1954 Forbidden search @@ -34,17 +43,11 @@ absolutely fatal: operation lost in the dodecahedron of doom Would you like to try again? y/n .... -.Some Ruby code -[source,ruby] ----- -require 'sinatra' +== Document attributes -get '/hi' do - "Hello World!" -end ----- +:attribute: value -kbd::space[] +== Images image::sunset.jpg[] @@ -56,70 +59,12 @@ image::macros:sunset.jpg[Sunset,200,100] image::https://asciidoctor.org/images/octocat.jpg[GitHub mascot] -= Hello world - -:toc: -:toc-title: Hello - -[discrete] -== hii - - -== hii - -=== bye - -:bye: -hi -:hi: 123 - -toc::[] - -It has *strong* significance to me. - -I _cannot_ stress this enough. - -Type `OK` to accept. - -That *_really_* has to go. +== Keycodes -Can't pick one? Let's use them `*_all_*`. - - -**C**reate, **R**ead, **U**pdate, and **D**elete (CRUD) - -That's fan__freakin__tastic! - -Don't pass generic ``Object``s to methods that accept ``String``s! - -It was Beatle**__mania__**! - - -Mark my words, #automation is essential#. - -##Mark##up refers to text that contains formatting ##mark##s. - -Where did all the [.underline]#cores# go? - -We need [.line-through]#ten# twenty VMs. - -A [.myrole]#custom role# must be fulfilled by the theme. - - -^super^script - -~sub~script - -https://asciidoctor.org - automatic! -"https://asciidoctor.org" - automatic! - -https://asciidoctor.org[Asciidoctor] - -devel@discuss.example.org - -mailto:devel@discuss.example.org[Discuss] +kbd::space[] +kbd::tab[] -mailto:join@discuss.example.org[Subscribe,Subscribe me,I want to join!] +== List items * List item + @@ -140,8 +85,7 @@ Hello .. bye .. hi -* [*] checked -* [ ] also checked -* [-] not checked -* normal list item +== TOCs + +toc::[] From d34fe9fca84be2e57d2e61f4c3b23fd587a74ec5 Mon Sep 17 00:00:00 2001 From: Shawon Date: Fri, 23 Jan 2026 15:51:25 +0600 Subject: [PATCH 37/40] refactor(asciidoc_inline): Final cleanup --- lua/markview/config/asciidoc_inline.lua | 18 +++++++-------- test/asciidoc_inline.adoc | 30 +++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 test/asciidoc_inline.adoc diff --git a/lua/markview/config/asciidoc_inline.lua b/lua/markview/config/asciidoc_inline.lua index 033692ce..af85e431 100644 --- a/lua/markview/config/asciidoc_inline.lua +++ b/lua/markview/config/asciidoc_inline.lua @@ -11,15 +11,6 @@ return { enable = true, bolds = { enable = true }, - italics = { enable = true }, - - monospaces = { - enable = true, - hl = "MarkviewInlineCode", - - padding_left = " ", - padding_right = " " - }, highlights = { enable = true, @@ -32,6 +23,15 @@ return { } }, + italics = { enable = true }, + + monospaces = { + enable = true, + hl = "MarkviewInlineCode", + + padding_left = " ", + padding_right = " " + }, uris = { enable = true, diff --git a/test/asciidoc_inline.adoc b/test/asciidoc_inline.adoc new file mode 100644 index 00000000..7c60af33 --- /dev/null +++ b/test/asciidoc_inline.adoc @@ -0,0 +1,30 @@ += Inline asciidoc syntax + +== Bold + +*Bold* + +== Highlight + +#Highlight# + +== Italic + +__Italic__ + +== Monospace + +`Monospace` + +== URI + +https://asciidoctor.org - automatic! + +https://asciidoctor.org[Asciidoctor] + +devel@discuss.example.org + +mailto:devel@discuss.example.org[Discuss] + +mailto:join@discuss.example.org[Subscribe,Subscribe me,I want to join!] + From 5c5b4b7f8150d918251cc923026b1d0f5f8fdc0d Mon Sep 17 00:00:00 2001 From: Shawon Date: Fri, 23 Jan 2026 15:51:47 +0600 Subject: [PATCH 38/40] fix(asciidoc, asciidoc_inline): Added option maps --- lua/markview/renderer.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lua/markview/renderer.lua b/lua/markview/renderer.lua index d6c73e10..c6634fbd 100644 --- a/lua/markview/renderer.lua +++ b/lua/markview/renderer.lua @@ -16,9 +16,24 @@ renderer.option_maps = { ---|fS asciidoc = { + admonitions = { "asciidoc_admonitiono" }, + document_attribute = { "asciidoc_document_attribute" }, document_title = { "asciidoc_document_title" }, + horizontal_rules = { "asciidoc_hr" }, + images = { "asciidoc_image" }, + keycode = { "asciidoc_keycode" }, + list_item = { "asciidoc_list_item" }, + literal_block = { "asciidoc_literal_block" }, + section_title = { "asciidoc_section_title" }, + toc = { "asciidoc_toc" }, }, asciidoc_inline = { + bold = { "asciidoc_inline_bold" }, + highlight = { "asciidoc_inline_highlight" }, + italic = { "asciidoc_inline_italic" }, + labeled_uri = { "asciidoc_inline_labeled_uri" }, + monospace = { "asciidoc_inline_monospace" }, + uri = { "asciidoc_inline_uri" }, }, comment = { autolinks = { "comment_autolink" }, From d0e43d41fe917bcee5d47bae1132b6a8bb88cf19 Mon Sep 17 00:00:00 2001 From: Shawon Date: Fri, 23 Jan 2026 16:14:17 +0600 Subject: [PATCH 39/40] chore: Updated test file --- test/asciidoc.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/test/asciidoc.adoc b/test/asciidoc.adoc index c760a812..fa9a26d8 100644 --- a/test/asciidoc.adoc +++ b/test/asciidoc.adoc @@ -1,4 +1,5 @@ = Asciidoc examples +:toc: == Admonitions From 429e56e4c81d977f3a24873899ee97863c75cb19 Mon Sep 17 00:00:00 2001 From: Shawon Date: Fri, 23 Jan 2026 16:51:10 +0600 Subject: [PATCH 40/40] doc: Updated README.md --- README.md | 31 +++++++++++++++++++- doc/markview.nvim-integrations.txt | 45 ++++++++++++++++++++++++++++++ doc/tags | 1 + markview.nvim.wiki | 2 +- test/asciidoc.adoc | 2 +- 5 files changed, 78 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c07afa7c..a75166e3 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ +
@@ -74,7 +75,7 @@ Core features, -+ Preview `Markdown`, HTML, $LaTeX$, `Typst` & `YAML` within Neovim. ++ Preview `Markdown`, HTML, $LaTeX$, `Typst` & `Asciidoc`(See [integrations#Asciidoc](https://github.com/OXY2DEV/markview.nvim/wiki/Usage#Asciidoc)) within Neovim. + *Hybrid* editing mode! Allowing *editing* & *previewing* at the same time. + *Splitview*! Allows editing & previewing *side-by-side*. + `Wrap` support(markdown only, at the moment)! Allows using text wrapping while not losing *most* rendering features! See [integrations#wrap](https://github.com/OXY2DEV/markview.nvim/wiki/Integrations#-wrap) for fixing visual glitches or [integrations#nowrap](https://github.com/OXY2DEV/markview.nvim/wiki/Integrations#-nowrap) for disabling it. @@ -106,6 +107,32 @@ Also see, Expand to see complete feature list +#### Asciidoc + + + +Supported syntax, + ++ Admonitions ++ Checkboxes(also supports custom checkbox states). ++ Horizontal rules ++ Literal blocks ++ Hiding document attributes ++ Image macros ++ Keycode macros ++ List items(ordered & unordered) ++ Automated TOC(Table of Contents) + +#### Asciidoc inline + +Supported syntax, + ++ Bold ++ Highlights ++ Italic ++ Monospace ++ URI + #### Fancy comments, @@ -405,6 +432,8 @@ Parsers, - `latex`(optional) - `typst`(optional) - `yaml`(optional) +- [tree-sitter-asciidoc](https://github.com/cathaysia/tree-sitter-asciidoc)(optional) + See [integrations#Asciidoc](https://github.com/OXY2DEV/markview.nvim/wiki/Usage#Asciidoc) to see how to get started. Fonts, diff --git a/doc/markview.nvim-integrations.txt b/doc/markview.nvim-integrations.txt index 0dfd29db..399d5ed0 100644 --- a/doc/markview.nvim-integrations.txt +++ b/doc/markview.nvim-integrations.txt @@ -130,6 +130,51 @@ For this you would need a `comment` parser. You can use either, ◆ OXY2DEV/tree-sitter-comment {10}, which also supports a subset of `markdown` & `vimdoc`. See installation {9}. +-------------------------------------------------------------------------------- +📦 Asciidoc *markview.nvim-integrations.nvim-asciidoc* + +`markview.nvim` provides support for `asciidoc` via a third party tree-sitter +parser. + +To get started, add this to your configuration table for `nvim-treesitter`. + +▋ 󰋽 Note +▋ This is only for the `main` branch of `nvim-treesitter`. `master` branch isn't +▋ supported. + +>lua + vim.api.nvim_create_autocmd("User", { + pattern = "TSUpdate", + callback = function () + require("nvim-treesitter.parsers").asciidoc = { + install_info = { + branch = "master", + location = "tree-sitter-asciidoc", + queries = "queries/asciidoc/", + requires = { "asciidoc_inline" }, + url = "https://github.com/cathaysia/tree-sitter-asciidoc" + } + }; + require("nvim-treesitter.parsers").asciidoc_inline = { + install_info = { + branch = "master", + location = "tree-sitter-asciidoc_inline", + queries = "queries/asciidoc_inline", + url = "https://github.com/cathaysia/tree-sitter-asciidoc" + } + }; + end + }); +< + +Exit and open `Neovim` and run, + +>vim + :TSInstall asciidoc asciidoc_inline +< + +Markview should now preview `asciidoc` files. + Links ~ 1: https://github.com/OXY2DEV/markview.nvim/wiki/Integrations diff --git a/doc/tags b/doc/tags index a56b77ad..5e1ce036 100644 --- a/doc/tags +++ b/doc/tags @@ -44,6 +44,7 @@ markview.nvim-integrations.blink-cmp markview.nvim-integrations.txt /*markview.n markview.nvim-integrations.color_blending markview.nvim-integrations.txt /*markview.nvim-integrations.color_blending* markview.nvim-integrations.gx markview.nvim-integrations.txt /*markview.nvim-integrations.gx* markview.nvim-integrations.nowrap markview.nvim-integrations.txt /*markview.nvim-integrations.nowrap* +markview.nvim-integrations.nvim-asciidoc markview.nvim-integrations.txt /*markview.nvim-integrations.nvim-asciidoc* markview.nvim-integrations.nvim-fancy_comments markview.nvim-integrations.txt /*markview.nvim-integrations.nvim-fancy_comments* markview.nvim-integrations.transparent_colorschemes markview.nvim-integrations.txt /*markview.nvim-integrations.transparent_colorschemes* markview.nvim-integrations.wrap markview.nvim-integrations.txt /*markview.nvim-integrations.wrap* diff --git a/markview.nvim.wiki b/markview.nvim.wiki index af5a5769..9c8e5467 160000 --- a/markview.nvim.wiki +++ b/markview.nvim.wiki @@ -1 +1 @@ -Subproject commit af5a576914fbf01fdb17d5e05f3c0083b65aa357 +Subproject commit 9c8e5467da865590bac6cea5a089a978202c7c8e diff --git a/test/asciidoc.adoc b/test/asciidoc.adoc index fa9a26d8..2fb5358f 100644 --- a/test/asciidoc.adoc +++ b/test/asciidoc.adoc @@ -31,7 +31,7 @@ IMPORTANT: Sign off before stepping away from your computer. - [-] Cancelled -== horizontal rules +== Horizontal rules '''