Custom handlers allow users to integrate custom rendering for either unsupported languages or to override / extend builtin implementations.
Custom handlers are ran identically to builtin ones, so by returning extmark data
this plugin will handle clearing the extmarks on mode changes, re-rendering when
needed, and concealing when the cursor enters.
Each handler must conform to the following interface:
---@class (exact) render.md.Handler
---@field extends? boolean
---@field parse fun(ctx: render.md.handler.Context): render.md.Mark[]
---@class (exact) render.md.handler.Context
---@field buf integer
---@field root TSNode
---@field last boolean
---@class (exact) render.md.Mark
---@field modes? render.md.Modes
---@field conceal render.md.mark.Conceal
---@field start_row integer
---@field start_col integer
---@field opts render.md.mark.Opts
---@alias render.md.mark.Conceal boolean|render.md.Element
---@class render.md.mark.Opts: vim.api.keyset.set_extmark
---@field virt_text? render.md.mark.Line
---@field virt_lines? render.md.mark.Line[]
---@alias render.md.mark.Line render.md.mark.Text[]
---@class (exact) render.md.mark.Text
---@field [1] string text
---@field [2] render.md.mark.Hl highlight
---@alias render.md.mark.Hl string|string[]The parse function takes a ctx parameter whose fields are:
buf: The buffer containing the root noderoot: The root treesitter node for the specified languagelast: Whether this is the last treesitter root for the specified language
The extends parameter defines whether the builtin handler should still be run in
conjunction with this one. Defaults to false.
This is a high level interface, as such creating, parsing, and iterating through a treesitter query is entirely up to the user if the functionality they want needs this. We do not provide any convenience functions, but you are more than welcome to use patterns from the builtin handlers.
For each mark in the return value the fields mean:
modes: additional modes themarkshould be shown inconceal: determines if the mark should be hidden when cursor entersstart_row: only value used to check whether cursor is inside themarkstart_col: passed tonvim_buf_set_extmarkas the 3rd argumentopts: passed directly tonvim_buf_set_extmark, no special handling
By not specifying the extends field and having the parse implementation return
an empty table we can disable a builtin handler. Though this has little benefit and
can be accomplished in other ways like setting { latex = { enabled = false } }
for latex.
Still as a toy example disabling the latex handler can be done with:
require('render-markdown').setup({
custom_handlers = {
latex = {
parse = function()
return {}
end,
},
},
})This will require a treesitter query and using the range values of nodes.
-- Parse query outside of the function to avoid doing it for each call
local query = vim.treesitter.query.parse('python', '(function_definition) @def')
local function parse_python(ctx)
local marks = {} ---@type render.md.Mark[]
for id, node in query:iter_captures(ctx.root, ctx.buf) do
local capture = query.captures[id]
local start_row = node:range()
if capture == 'def' then
marks[#marks + 1] = {
conceal = true,
start_row = start_row,
start_col = 0,
opts = {
end_row = start_row + 1,
end_col = 0,
hl_group = 'DiffDelete',
hl_eol = true,
},
})
end
end
return marks
end
require('render-markdown').setup({
file_types = { 'markdown', 'python' },
custom_handlers = {
python = { parse = parse_python },
},
})