๐ markdoc.nvim
November 14, 2025 ยท View on GitHub
Tree-sitter based markdown to vimdoc converter for Neovim.
๐ช Features
- Common
markdownsyntax support. - Common
inline HTMLsupport. - Aligned paragraph(via
<p align=""></p>or<div align=""></div>) - Preserves whitespace.
- Extended syntax support(e.g. Callouts).
- Table converter with column alignment & text wrap support.
- Custom Table of contents generator.
- Custom file header support(with support for
version,author,date modified). - Fully customizable.
- Ability to configure per file(using
JSON). - Ability to remove parts of the file when converting to Vimdoc(e.g. Top heading in
README.md).
And much more!
๐งญ Requirements
tree-sitter-markdown.tree-sitter-markdown_inline.tree-sitter-html(for inlineHTML& comments in markdown).
Important
By default, callouts use nerd font characters. You can change this in the config to use normal text instead.
๐ฆ Installation
๐งฉ Vim-plug
Plug "OXY2DEV/markdoc.nvim"
๐ค lazy.nvim
{
"OXY2DEV/markdoc.nvim"
},
return {
"OXY2DEV/markdoc.nvim"
};
๐ฆ mini.deps
local MiniDeps = require("mini.deps");
MiniDeps.add({
source = "OXY2DEV/markdoc.nvim",
})
๐ rocks.nvim
Warning
luarocks package may sometimes be a bit behind main.
:Rocks install markdoc.nvim
๐ฅ GitHub release
Tagged releases can be found in the release page.
Note
Github releases may sometimes be slightly behind main.
๐ Usage
There is a single command,
:Doc
When called without arguments, it runs on the current buffer.
When called with an argument,
:Doc README.md
It converts given file.
:Doc 0
Or buffer.
It can be called with multiple files/buffers.
:Doc README.md test.md
๐ฌ API functions
There are 2 available API functions.
๐งฉ File conversion API
--[[ Converts **file** with `path` into `vimdoc`. ]]
---@param path string Path to file.
---@param user_config? markdoc.config Custom configuration. Overrides the file specific configuration.
---@param use? integer Buffer to dump the preview into. Has no effect if `generic.filename` is set.
markdoc.convert_file = function (path, user_config, use)
-- ...
end
This function can be used to convert a file in path into vimdoc.
You can use it like so,
require("markdoc").convert_file("README.md", {
generic = { filename = "doc/markdoc.nvim.txt" }
});
๐งฉ Buffer conversion API
--[[ Converts `buffer` into `vimdoc`. ]]
---@param buffer? integer Buffer ID. Defaults to *current* buffer.
---@param user_config? markdoc.config Custom configuration. Overrides the file specific configuration.
---@param use? integer Buffer to dump the preview into. Has no effect if `generic.filename` is set.
markdoc.convert_buffer = function (buffer, user_config, use)
-- ...
end
This function can be used to convert a buffer into vimdoc.
You can use it like so,
require("markdoc").convert_buffer(0, {
generic = { filename = "doc/markdoc.nvim.txt" }
});
This will convert current buffer.
โจ Special syntax
markdoc.nvim uses special comments to modify the file. These can be,
โจ File-specific configuration
Use <!--markdoc {}--> syntax for adding file specific configuration.
<!--markdoc
{
"generic": {
"filename": "doc/markdoc.nvim.txt",
"force_write": true,
"header": {
"desc": "Tree-sitter based `Markdown -> Vimdoc` converter for Neovim",
"tag": "markdoc.nvim"
}
},
"markdown": {
"tags": {
"Features$": [ "markdoc.nvim-features" ],
"^generic$": [ "markdoc.nvim-generic" ],
"^filename$": [ "markdoc.nvim-filename", "markdoc.nvim-generic.filename" ],
"^force_write$": [ "markdoc.nvim-force_write", "markdoc.nvim-generic.force_write" ],
"^winopts$": [ "markdoc.nvim-winopts", "markdoc.nvim-generic.winopts" ]
}
}
}
-->
โจ Ignoring parts of the document
Use <!--markdoc_ignore_start--> & <!--markdoc_ignore_end--> syntax for specifying parts of the document that will be removed in the converted document.
<!--markdoc_ignore_start-->
# Invisible text
<!--markdoc_ignore_end-->
Tree-sitter based `markdown` to `vimdoc` converter for Neovim.
Some visible text
This will result in
Some visible text
โจ Table of contents
Table of contents can be generated by adding <TOC/> to a line.
<TOC/>
๐ง Configuration
The plugin can be set-up by using the setup() function.
require("markdoc").setup({
markdown = {
code_blocks = {
fallback_language = "vim",
indentation = "\t"
},
}
});
The following options are supported,
generic
Type: markdoc.config.generic
-- Generic options for controlling help file generation.
---@class markdoc.config.generic
---
---@field preserve_whitespace? boolean Should whitespace be preserved?
---
---@field filename? string Name of help file.
---@field relative_path? boolean Is the file oath relative to the `source` file? Gets set to `false`, if `filename` starts with `~`.
---@field force_write? boolean Should `:write!` be used instead of `:write`?
---@field winopts? table Window options for the previewer.
---
---@field textwidth? integer Text width of the help file.
---@field indent? integer Number of spaces to use for indentation.
---
---@field header markdoc.config.generic.header
---@field links markdoc.config.generic.links
---@field images markdoc.config.generic.links
---@field toc markdoc.config.generic.toc
Generic configuration options. These are,
preserve_whitespace
Type: boolean
When false, Repeating empty lines will be collapsed into a single empty line.
The default value is false.
filename
Type: string
Name of the file to save to.
Note
This is relative to the current file being concerted!
Important
markdoc doesn't create non-existing directories! You have to make sure the directories exist before using it!
relative_path
Type: string
When true(default value), generic.filename will be relative to the source file's location.
When false, the filename will be relative to the current working directory.
Note
This is useful for automation if you wish to have some files be created relative to their source and others relative to current directory.
force_write
Type: boolean
When true, use :write! instead of :write.
winopts
Type: vim.api.keyset.win_config
Options passed to nvim_open_win() for showing file preview.
textwidth
Type: integer
Same as 'textwidth'(:h 'textwidth'). Used for text wrapping & setting the modeline.
Used by options that use ratios.
Note
This will fallback to 80 by default.
indent
Type: integer
Unused.
header
Type: markdoc.config.generic.header
Configuration for file header.
---@class markdoc.config.generic.header
---
---@field enabled? boolean
---
---@field tag? string Name of `help tag` for this file..
---@field desc? string Short description to show at top.
---
---@field author? string[] Author(s) of the help file.
---@field version? string Version string.
---@field last_modified? boolean Should the last modification date be shown?
links
Type: markdoc.config.generic.links
Configuration for link reference section.
---@class markdoc.config.generic.links
---
---@field enabled? boolean
---@field desc? string Short description to show at top of link section.
---
---@field list_marker? string
---@field url_format? string
images
Type: markdoc.config.generic.links
Configuration for image reference section. Same as above.
toc
Type: markdoc.config.generic.toc
Configuration for table of contents section.
---@class markdoc.config.generic.toc
---
---@field enabled? boolean
---
---@field heading? string
---@field heading_level? integer
---
---@field entries generic.toc.entry[]
Each entry in entries has the following structure.
---@class generic.toc.entry
---
---@field text string
---@field tag string
markdown
Markdown options.
-- Configuration for `markdown`
---@class markdoc.config.markdown
---
---@field use_link_refs markdown.use_link_refs Should *references* be used instead of `URLs`
---@field link_ref_format? string `Format string` used for the link references. Default: `{%d}`.
---@field link_url_modifiers markdown.url_modifier.entry[] Changes the url based on a pattern
---
---@field heading_ratio integer[] Ratio for the amount of space a heading text & it's tags should take. Default: `{ 6, 4 }`.
---
---@field block_quotes markdoc.config.markdown.block_quotes
---@field code_blocks markdoc.config.markdown.code_blocks
---@field hr? string Text used to show `horizontal rules`
---@field list_items markdoc.config.markdown.list_items
---@field tables markdoc.config.markdown.tables
---@field tags markdoc.config.markdown.tags
use_link_refs
Type: markdown.use_link_refs
---@alias markdown.use_link_refs
---| boolean
---| fun (description: string, destination: string): boolean
Controls whether references be used instead of the URL.
With `use_link_refs = true`.
Link {1}
With `use_link_refs = true`.
Link www.example.com
link_ref_format
Type: string
Text used for formatting the reference of a link. Default is {%d}.
link_url_modifiers
Type: markdown.url_modifier.entry[]
---@class markdown.url_modifier.entry
---
---@field [1] string `Lua-pattern` to match.
---@field [2] markdown.url_modifier
Modifies URLs. Useful for section links(e.g. #link_url_modifiers).
Each entry has the following pattern.
---@alias markdown.url_modifier
---| string
---| fun (description: string, destination: string): string
heading_ratio
Type: [ integer, integer ]
Ratio of spaces taken by heading text & the tags. Default is { 6, 4 }.
Note
This may not be respected if a tag is too long.
block_quotes
Type: markdoc.config.markdown.block_quotes
---@class markdoc.config.markdown.block_quotes
---
---@field default markdown.block_quotes.opts
---@field [string] markdown.block_quotes.opts
Configuration for block quotes & callouts.
Each block quote type has the following options.
---@class markdown.block_quotes.opts
---
---@field border? string
---@field icon? string
---@field preview? string
code_blocks
Type: markdoc.config.markdown.code_blocks
---@class markdoc.config.markdown.code_blocks
---
---@field indentation? string Text used for indenting code block.
---@field fallback_language? string Fallback language for `code_blocks` without a language.
Configuration for code blocks.
hr
Type: string
Text representing a horizontal rule.
markdown = {
hr = " โถ" .. string.rep("โ", 76) .. "โด ",
}
list_items
Type: markdoc.config.markdown.list_items
---@class markdoc.config.markdown.list_items
---
---@field marker_plus? string Text used to replace `+` markers.
---@field marker_minus? string Text used to replace `-` markers.
---@field marker_star? string Text used to replace `*` markers.
---
---@field marker_dot? string Text used to replace `%d+.` markers. May contain `%d` to add the marker number.
---@field marker_parenthesis? string Text used to replace `%d+)` markers. May contain `%d` to add the marker number.
tables
Type: markdoc.config.markdown.tables
---@class markdoc.config.markdown.tables
---
---@field max_col_size? integer Maximum width of a table `column`.
---@field preserve_whitespace? boolean Should **leading** & **trailing** whitespaces be preserved?
---@field default_alignment? "left" | "center" | "right" Default *text alignment* of table cells.
---@field borders? markdown.tables.border Table border.
Changes how table borders are shown.
Tables have the following options for border drawing.
-- Border elements for the table.
---@class markdown.tables.border
---
---@field top markdown.tables.border.decoration
---@field bottom markdown.tables.border.decoration
---
---@field separator markdown.tables.border.decoration
---@field row_separator markdown.tables.border.decoration
---
---@field header markdown.tables.border.row
---@field row markdown.tables.border.row
top, bottom, separator & row_separator have the following options.
---@class markdown.tables.border.decoration
---
---@field [1] string Left border
---@field [2] string Character used to make creating columns.
---@field [3] string Right border
---
---@field [4] string Column separator.
header & row have the following options.
---@class markdown.tables.border.row
---
---@field [1] string Left border
---@field [2] string Column separator.
---@field [3] string Right border
markdown = {
tables = {
max_col_size = 20,
preserve_whitespace = true,
default_alignment = "left",
borders = {
header = { "โ", "โ", "โ" },
row = { "โ", "โ", "โ" },
separator = { "โ", "โ", "โค", "โผ" },
row_separator = { "โ", "โ", "โค", "โผ" },
top = { "โญ", "โ", "โฎ", "โฌ" },
bottom = { "โฐ", "โ", "โฏ", "โด" },
},
},
}
tags
Type: markdoc.config.markdown.tags
---@class markdoc.config.markdown.tags
---
---@field default? string[]
---@field [string] string[]
Maps heading text to a list of tags.
markdown = {
tags = {
["^textwidth$"]: { "markdoc.nvim-textwidth", "markdoc.nvim-generic.textwidth" },
},
}