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-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/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/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 959bcade..5e1ce036 100644
--- a/doc/tags
+++ b/doc/tags
@@ -1,6 +1,8 @@
!_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-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*
@@ -42,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/lua/markview/config/asciidoc.lua b/lua/markview/config/asciidoc.lua
new file mode 100644
index 00000000..8fafbb62
--- /dev/null
+++ b/lua/markview/config/asciidoc.lua
@@ -0,0 +1,389 @@
+---@type [ string, markview.config.asciidoc.keycodes ]
+local keycodes = {
+ ---|fS
+
+ 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",
+ },
+
+ ---|fE
+};
+
+
+---@type markview.config.asciidoc
+return {
+ enable = true,
+
+ 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,
+
+ 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 = "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" },
+ },
+
+ 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,
+
+ hl = "MarkviewCode",
+
+ sign = "σ±¨ ",
+ sign_hl = "MarkviewPalette6Sign",
+
+ label = "σ±¨ Raw ",
+ label_direction = "right",
+ label_hl = "MarkviewIcon6",
+
+ min_width = 60,
+ pad_amount = 2,
+ pad_char = " ",
+
+ style = "block",
+ },
+
+ document_titles = {
+ enable = true,
+
+ sign = "σ° ",
+
+ 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,
+ },
+
+ images = {
+ enable = true,
+
+ default = {
+ icon = "σ°₯Ά ",
+ hl = "MarkviewImage",
+ },
+
+ ["%.svg$"] = { icon = "σ°‘ " },
+ ["%.png$"] = { icon = "σ°Έ " },
+ ["%.jpg$"] = { icon = "σ°₯ " },
+ ["%.gif$"] = { icon = "σ°΅Έ " },
+ ["%.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,
+ },
+
+ 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,
+
+ 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/config/asciidoc_inline.lua b/lua/markview/config/asciidoc_inline.lua
new file mode 100644
index 00000000..af85e431
--- /dev/null
+++ b/lua/markview/config/asciidoc_inline.lua
@@ -0,0 +1,346 @@
+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 {
+ enable = true,
+
+ bolds = { enable = true },
+
+ highlights = {
+ enable = true,
+
+ default = {
+ padding_left = " ",
+ padding_right = " ",
+
+ hl = "MarkviewPalette3"
+ }
+ },
+
+ italics = { enable = true },
+
+ monospaces = {
+ enable = true,
+ hl = "MarkviewInlineCode",
+
+ padding_left = " ",
+ padding_right = " "
+ },
+
+ 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/parser.lua b/lua/markview/parser.lua
index aa7a82be..3a42be88 100644
--- a/lua/markview/parser.lua
+++ b/lua/markview/parser.lua
@@ -99,6 +99,8 @@ parser.init = function (buffer, from, to, cache)
---|fS
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");
@@ -126,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.lua b/lua/markview/parsers/asciidoc.lua
new file mode 100644
index 00000000..8e0cd967
--- /dev/null
+++ b/lua/markview/parsers/asciidoc.lua
@@ -0,0 +1,689 @@
+--- HTML parser for `markview.nvim`.
+local asciidoc = {};
+
+---@type markview.parser.asciidoc.data
+asciidoc.data = {};
+
+--- Queried contents
+---@type markview.parsed.asciidoc[]
+asciidoc.content = {};
+
+--- Queried contents, but sorted
+---@type markview.parsed.asciidoc_sorted
+---@diagnostic disable-next-line: missing-fields
+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
+
+--[[
+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]+$");
+
+ 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
+ });
+
+ ---|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
+ 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",
+
+ 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)
+ asciidoc.data.document_title = string.match(text[1] or "", "=%s+(.*)$")
+
+ asciidoc.insert({
+ class = "asciidoc_document_title",
+
+ text = text,
+ range = range
+ });
+end
+
+--[[
+Horizontal rule or thematic break
+
+```asciidoc
+'''
+```
+]]
+---@param text string[]
+---@param range markview.parsed.range
+asciidoc.hr = function (_, _, text, range)
+ asciidoc.insert({
+ class = "asciidoc_hr",
+
+ text = text,
+ range = range
+ });
+end
+
+--[[
+Image.
+
+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
+ 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
+ });
+
+ ---|fE
+end
+
+--[[
+Keycodes.
+
+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
+ return;
+ end
+
+ local content = vim.treesitter.get_node_text(_content, buffer, {});
+ range.content = { _content: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_keycode",
+ content = content,
+
+ 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
+ return false;
+ end
+
+ 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();
+
+ local marker;
+
+ 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
+
+ -- 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",
+
+ checkbox = checkbox,
+ marker = marker,
+ n = N,
+
+ 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
+
+--[[
+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?
+---@param to integer?
+---@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 = {};
+
+ 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
+
+ (block_macro
+ (
+ (block_macro_name) @toc_pos_name
+ (#eq? @toc_pos_name "toc")
+ )) @asciidoc.toc_pos
+
+ (unordered_list_item) @asciidoc.list_item
+ (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
+
+ (block_macro
+ (
+ (block_macro_name) @kbd_keyword
+ (#eq? @kbd_keyword "kbd")
+ )) @asciidoc.keycode
+
+ (literal_block) @asciidoc.literal_block
+
+ (admonition) @asciidoc.admonition
+ (breaks) @asciidoc.hr
+ ]]);
+
+ 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
+
+ local function iter (queries)
+ ---|fS
+
+ for capture_id, capture_node, _, _ in queries:iter_captures(TSTree:root(), buffer, from, to) do
+ local capture_name = 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
+
+ ---|fE
+ end
+
+ 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
+ (
+ (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;
+
+ ---|fE
+end
+
+return asciidoc;
diff --git a/lua/markview/parsers/asciidoc_inline.lua b/lua/markview/parsers/asciidoc_inline.lua
new file mode 100644
index 00000000..4215446c
--- /dev/null
+++ b/lua/markview/parsers/asciidoc_inline.lua
@@ -0,0 +1,430 @@
+--- 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 markview.parsed.asciidoc_inline[]
+asciidoc_inline.content = {};
+
+--- Queried contents, but sorted
+---@type markview.parsed.asciidoc_inline_sorted
+---@diagnostic disable-next-line: missing-fields
+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
+
+--[[
+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
+ 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
+ });
+
+ ---|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
+ 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
+ });
+
+ ---|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
+ 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
+ });
+
+ ---|fE
+end
+
+--[[
+Labeled URI.
+
+```asciidoc
+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)
+ ---|fS
+
+ 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
+ });
+
+ ---|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.uri_macro = function (buffer, TSNode, text, range)
+ ---|fS
+
+ 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
+ });
+
+ ---|fE
+end
+
+--[[
+Monospace text.
+
+```asciidoc
+`mono`
+```
+]]
+---@param buffer integer
+---@param TSNode TSNode
+---@param text string[]
+---@param range markview.parsed.range
+asciidoc_inline.monospace = function (buffer, TSNode, text, range)
+ ---|fS
+
+ 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
+ });
+
+ ---|fE
+end
+
+--[[
+Unlabeled URI.
+
+```asciidoc
+www.example.com
+```
+]]
+---@param buffer integer
+---@param TSNode TSNode
+---@param text string[]
+---@param range markview.parsed.range
+asciidoc_inline.uri = function (buffer, TSNode, text, range)
+ ---|fS
+
+ 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;
+
+ 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
+ });
+
+ ---|fE
+end
+
+---@param buffer integer
+---@param TSTree TSTree
+---@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)
+ ---|fS
+
+ ---@diagnostic disable-next-line: missing-fields
+ 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 can_scan, scanned_queries = pcall(vim.treesitter.query.parse, "asciidoc_inline", [[
+ (emphasis) @asciidoc_inline.bold
+ (ltalic) @asciidoc_inline.italic
+ (monospace) @asciidoc_inline.monospace
+ (highlight) @asciidoc_inline.highlight
+
+ (autolink
+ (uri)) @asciidoc_inline.uri
+
+ (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
+ 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];
+
+ 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;
+
+ ---|fE
+end
+
+return asciidoc_inline;
diff --git a/lua/markview/renderer.lua b/lua/markview/renderer.lua
index 3beaf54f..c6634fbd 100644
--- a/lua/markview/renderer.lua
+++ b/lua/markview/renderer.lua
@@ -15,6 +15,26 @@ renderer.__filter_cache = {
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" },
code_blocks = { "comment_code_block" },
@@ -366,6 +386,8 @@ renderer.render = function (buffer, parsed_content)
---|fS
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"),
@@ -470,6 +492,8 @@ renderer.clear = function (buffer, from, to, hybrid_mode)
---|fS
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
new file mode 100644
index 00000000..a4d1a176
--- /dev/null
+++ b/lua/markview/renderers/asciidoc.lua
@@ -0,0 +1,906 @@
+local asciidoc = {};
+
+local utils = require("markview.utils");
+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];
+
+ 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) },
+ { 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.kind[1], range.kind[2], {
+ end_row = row_end,
+ end_col = col_end,
+
+ hl_group = utils.set_hl(config.hl)
+ });
+
+ utils.set_extmark(buffer, asciidoc.ns, row_end, col_end - 1, {
+ 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
+ utils.set_extmark(buffer, asciidoc.ns, row_end, col_end, {
+ 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.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 range = item.range;
+
+ 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.document_titles
+asciidoc.document_title = function (buffer, item)
+ ---|fS
+
+ ---@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 + #string.match(item.text[1] or "", "=+%s*"),
+ conceal = "",
+
+ sign_text = tostring(config.sign or ""),
+ sign_hl_group = utils.set_hl(config.sign_hl or config.hl),
+
+ virt_text = {
+ { config.icon, utils.set_hl(config.icon_hl or config.hl) },
+ },
+ line_hl_group = utils.set_hl(config.hl),
+ });
+
+ ---|fE
+end
+
+--- Renders horizontal rules/line breaks.
+---@param buffer integer
+---@param item markview.parsed.asciidoc.hrs
+asciidoc.hr = function (buffer, item)
+ ---|fS
+
+ ---@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
+
+ utils.set_extmark(buffer, asciidoc.ns, range.row_start, 0, {
+ virt_text_pos = "overlay",
+ virt_text = virt_text,
+
+ 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;
+
+ 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.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.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;
+
+ if not main_config then
+ return;
+ end
+
+ ---@type markview.config.asciidoc.keycodes.opts?
+ local config = utils.match(
+ main_config,
+ string.upper(item.content or ""),
+ {
+ 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.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"
+ });
+
+ ---|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
+
+ ---@type markview.config.asciidoc.checkboxes.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;
+
+ 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, {
+ 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 "", utils.set_hl(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 "", utils.set_hl(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 "", utils.set_hl(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
+
+ if scope_hl then
+ if r == range.row_start then
+ utils.set_extmark(buffer, asciidoc.ns, r, range.col_start, {
+ end_col = #item.text[1],
+
+ hl_group = utils.set_hl(scope_hl)
+ });
+ elseif line ~= "" then
+ local spaces = line:match("^([%>%s]*)");
+
+ utils.set_extmark(buffer, asciidoc.ns, range.row_start + (r - 1), #spaces, {
+ end_col = #line,
+
+ hl_group = utils.set_hl(scope_hl)
+ });
+ end
+ 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, utils.set_hl(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
+ utils.set_extmark(buffer, asciidoc.ns, range.row_start, conceal_from, {
+ 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
+ utils.set_extmark(buffer, asciidoc.ns, range.row_start, conceal_from, {
+ 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
+ utils.set_extmark(buffer, asciidoc.ns, l, 0, {
+ end_row = l,
+
+ line_hl_group = utils.set_hl(config.hl)
+ });
+ end
+
+ 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 = "",
+
+ 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 `dot`s.
+ utils.set_extmark(buffer, asciidoc.ns, range.row_start, delim_conceal_from, {
+ end_col = conceal_to,
+ conceal = "",
+
+ sign_text = config.sign,
+ sign_hl_group = utils.set_hl(config.sign_hl),
+ });
+
+ if config.label_direction == "right" then
+ utils.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
+ utils.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);
+
+ 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 = {
+ {
+ 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
+
+ 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 }
+ });
+
+ ---|fE
+
+ ---|fE
+
+ --- Line padding
+ for l, width in ipairs(line_widths) do
+ local line = item.text[l + 1];
+
+ if width ~= 0 then
+ utils.set_extmark(buffer, asciidoc.ns, range.row_start + l, line ~= "" and range.col_start or 0, {
+ virt_text_pos = "inline",
+ virt_text = {
+ {
+ string.rep(" ", pad_amount),
+ utils.set_hl(config.hl --[[ @as string ]])
+ }
+ },
+ });
+
+ utils.set_extmark(buffer, asciidoc.ns, range.row_start + l, range.col_start + #line, {
+ 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
+ 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 ]])
+ });
+ else
+ local buf_line = vim.api.nvim_buf_get_lines(buffer, range.row_start + l, range.row_start + l + 1, false)[1];
+
+ utils.set_extmark(buffer, asciidoc.ns, range.row_start + l, #buf_line, {
+ 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*");
+
+ utils.set_extmark(buffer, asciidoc.ns, range.row_end, end_delim_conceal_from, {
+ end_col = range.col_start + #item.text[#item.text],
+ conceal = ""
+ });
+
+ utils.set_extmark(buffer, asciidoc.ns, range.row_end, end_delim_conceal_from, {
+ virt_text_pos = "inline",
+ virt_text = {
+ {
+ string.rep(" ", block_width),
+ utils.set_hl(config.hl)
+ }
+ }
+ });
+ else
+ utils.set_extmark(buffer, asciidoc.ns, range.row_end, range.col_start, {
+ 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
+
+---@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 } });
+
+ 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, utils.set_hl(config.icon_hl or config.hl) },
+ },
+ 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 } });
+
+ if not main_config then
+ return;
+ end
+
+ local range = item.range;
+ local lines = {};
+
+ table.insert(lines, {
+ { 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
+ 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, 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), utils.set_hl(config.hl) },
+ { config.icon or "", utils.set_hl(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
+
+ ---|fE
+end
+
+---@param buffer integer
+---@param content markview.parsed.asciidoc[]
+asciidoc.render = function (buffer, content)
+ ---|fS
+
+ 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
+
+ ---|fE
+end
+
+---@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/renderers/asciidoc/tostring.lua b/lua/markview/renderers/asciidoc/tostring.lua
new file mode 100644
index 00000000..9bcbf813
--- /dev/null
+++ b/lua/markview/renderers/asciidoc/tostring.lua
@@ -0,0 +1,246 @@
+--[[
+Basic renderer for `asciidoc`. Used to convert regular strings to preview strings.
+
+Used for width calculations.
+]]
+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/renderers/asciidoc_inline.lua b/lua/markview/renderers/asciidoc_inline.lua
new file mode 100644
index 00000000..7164e507
--- /dev/null
+++ b/lua/markview/renderers/asciidoc_inline.lua
@@ -0,0 +1,354 @@
+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.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 } });
+
+ 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 = "",
+ });
+
+ utils.set_extmark(buffer, asciidoc_inline.ns, range.row_end, range.col_end - #(item.delimiters[2] or ""), {
+ 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;
+
+ 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) },
+ { 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.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"
+ });
+
+ ---|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 } });
+
+ 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 = "",
+ });
+
+ utils.set_extmark(buffer, asciidoc_inline.ns, range.row_end, range.col_end - #(item.delimiters[2] or ""), {
+ 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 } });
+
+ 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) },
+ { 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.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"
+ });
+
+ ---|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;
+
+ 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"
+ });
+
+ ---|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;
+
+ 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"
+ });
+
+ ---|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
+ 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
+
+ ---|fE
+end
+
+---@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 d35690f9..91ed55fd 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,8 @@ spec.default = {
---@type string[] Properties that should be sourced *externally*.
spec.__external_config = {
+ "asciidoc",
+ "asciidoc_inline",
"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..92499f3c
--- /dev/null
+++ b/lua/markview/types/parsers/asciidoc.lua
@@ -0,0 +1,221 @@
+---@meta
+
+------------------------------------------------------------------------------
+
+---@class markview.parsed.asciidoc.admonitions
+---
+---@field class "asciidoc_admonition"
+---@field kind string Type of admonition(e.g. `NOTE`).
+---
+---@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"
+---
+---@field text string[]
+---@field range markview.parsed.range
+
+------------------------------------------------------------------------------
+
+---@class markview.parsed.asciidoc.document_titles
+---
+---@field class "asciidoc_document_title"
+---
+---@field text string[]
+---@field range markview.parsed.range
+
+------------------------------------------------------------------------------
+
+---@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"
+---@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.keycodes
+---
+---@field class "asciidoc_keycode"
+---@field content string Text inside 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 content integer[] Range of the keycode destination(output of `{ TSNode:range() }`.
+
+------------------------------------------------------------------------------
+
+---@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.
+---
+---@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 `.`).
+---
+---@field checkbox_start? integer Start column for the checkbox(e.g. `[*]` or `[ ]`).
+---@field checkbox_end? integer End column for the checkbox(e.g. `[*]` or `[ ]`).
+
+------------------------------------------------------------------------------
+
+---@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"
+---@field marker string The `=` part of the title.
+---
+---@field text string[]
+---@field range markview.parsed.range
+
+------------------------------------------------------------------------------
+
+---@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.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
+
+------------------------------------------------------------------------------
+
+---@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 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[]
+
+------------------------------------------------------------------------------
+
+---@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/parsers/asciidoc_inline.lua b/lua/markview/types/parsers/asciidoc_inline.lua
new file mode 100644
index 00000000..a9e7ed28
--- /dev/null
+++ b/lua/markview/types/parsers/asciidoc_inline.lua
@@ -0,0 +1,96 @@
+---@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.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"
+---@field delimiters [ string?, string? ] Delimiters
+---
+---@field text string[]
+---@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
+
+------------------------------------------------------------------------------
+
+---@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[]
+---@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]` 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
+---
+---@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
+---| markview.parsed.asciidoc_inline.italics
+---| 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 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
+
diff --git a/lua/markview/types/renderers/asciidoc.lua b/lua/markview/types/renderers/asciidoc.lua
new file mode 100644
index 00000000..7e6cf3b9
--- /dev/null
+++ b/lua/markview/types/renderers/asciidoc.lua
@@ -0,0 +1,282 @@
+---@meta
+
+------------------------------------------------------------------------------
+
+--- 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.
+
+
+---@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
+---
+---@field enable boolean Enable rendering of checkboxes.
+---
+---@field checked markview.config.asciidoc.checkboxes.opts Configuration for `[*]`.
+---@field unchecked markview.config.asciidoc.checkboxes.opts Configuration for `[ ]`.
+---
+---@field [string] markview.config.asciidoc.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.
+
+------------------------------------------------------------------------------
+
+--[[ Options for document attributes. ]]
+---@class markview.config.asciidoc.document_attributes
+---
+---@field enable boolean Enable concealing of document attributes? Requires `conceal_lines` support.
+
+------------------------------------------------------------------------------
+
+---@class markview.config.asciidoc.document_titles
+---
+---@field enable boolean Enable rendering of document titles.
+---
+---@field sign? string Text to add to the sign column.
+---@field sign_hl? string Highlight group for the sign.
+---
+---@field icon? string Icon added before the title.
+---@field icon_hl? string Highlight group for `icon`.
+---
+---@field hl? string Fallback for all `*_hl` options.
+
+------------------------------------------------------------------------------
+
+--- 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
+---
+---@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 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
+---
+---@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
+---
+---@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 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`.
+
+------------------------------------------------------------------------------
+
+--- 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
+
+
+---@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.
+
+------------------------------------------------------------------------------
+
+--- 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
+
+
+--- 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`.
+
+------------------------------------------------------------------------------
+
+---@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
+
diff --git a/lua/markview/types/renderers/asciidoc_inline.lua b/lua/markview/types/renderers/asciidoc_inline.lua
new file mode 100644
index 00000000..aae40c9a
--- /dev/null
+++ b/lua/markview/types/renderers/asciidoc_inline.lua
@@ -0,0 +1,80 @@
+---@meta
+
+
+---@class markview.config.asciidoc_inline.bolds
+---
+---@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 highlight 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 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
+
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
new file mode 100644
index 00000000..2fb5358f
--- /dev/null
+++ b/test/asciidoc.adoc
@@ -0,0 +1,92 @@
+= Asciidoc examples
+:toc:
+
+== Admonitions
+
+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.
+
+== Checkboxes
+
+=== Main
+
+- [*] Checked
+- [ ] Unchecked
+
+=== Custom states
+
+- [/] Pending
+- [>] Reschedule
+- [-] Cancelled
+
+
+== Horizontal rules
+
+'''
+
+== Literal blocks
+
+....
+error: 1954 Forbidden search
+absolutely fatal: operation lost in the dodecahedron of doom
+
+Would you like to try again? y/n
+....
+
+== Document attributes
+
+:attribute: value
+
+== Images
+
+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]
+
+== Keycodes
+
+kbd::space[]
+kbd::tab[]
+
+== List items
+
+* List item
++
+Hello
+
+* Bye
+** Nested list item
+*** Deeper nested list item
+* List item
+*** Another nested list item
+* List item
+- Bye
+
+. Hi
+.. Hello
+.. Bye
+. Bye
+.. bye
+.. hi
+
+== TOCs
+
+toc::[]
+
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!]
+