Configurable tools for working with Markdown in Neovim.
markdown.nvim
Configurable tools for working with markdown files in Neovim.
Contents
- Planned features -<Plug> mappings
- Inline surround
- Table of contents
- Lists
- Links
- Navigation
Features
- Inline-style
- Table of contents
- Lists
- Links
- Navigation
- nvim-treesitter module support
Planned features
- Tables (GFM)
Requirements
- Neovim >=0.10.0
- markdown and markdowninline tree-sitter parsers. The easiest way to install these is with nvim-treesitter.
Installation
Install markdown.nvim with your preferred plugin manager.
{
"tadmccorkle/markdown.nvim",
ft = "markdown", -- or 'event = "VeryLazy"'
opts = {
-- configuration here or empty for defaults
},
}
use({
"tadmccorkle/markdown.nvim",
config = function()
require("markdown").setup({
-- configuration here or empty for defaults
})
end,
})
Plug 'tadmccorkle/markdown.nvim'
" after plug#end() " provide setup() configuration options or leave empty for defaults lua require('markdown').setup()
{ "tadmccorkle/markdown.nvim",
config = function()
require("markdown").setup({
-- configuration here or empty for defaults
})
end,
};
Getting help
markdown.nvim provides help docs that can be accessed by running :help markdown.nvim.
Configuration
Detailed plugin configuration information can be found in the help doc (:h markdown.configuration).
A call to require("markdown").setup() is necessary for commands and keybindings to be registered in markdown buffers.
A table of configuration options can optionally be passed to the setup() function. Any fields in the table will overwrite the corresponding default. markdown.nvim uses the following defaults:
<pre><code class="lang-lua">{ "nvim-treesitter/nvim-treesitter", dependencies = { "tadmccorkle/markdown.nvim" }, config = function() require("nvim-treesitter.configs").setup({ ensureinstalled = { "markdown", "markdowninline", --[[ other parsers you need ]] }, markdown = { enable = true, -- configuration here or nothing for defaults }, }) end, }</code></pre> <pre><code class="lang-lua">use({ "nvim-treesitter/nvim-treesitter", requires = { { "tadmccorkle/markdown.nvim" } }, config = function() require("nvim-treesitter.configs").setup({ ensureinstalled = { "markdown", "markdowninline", --[[ other parsers you need ]] }, markdown = { enable = true, -- configuration here or nothing for defaults }, }) end, })</code></pre> <pre><code class="lang-vim">Plug 'nvim-treesitter/nvim-treesitter' Plug 'tadmccorkle/markdown.nvim'{ -- Disable all keymaps by setting mappings field to 'false'. -- Selectively disable keymaps by setting corresponding field to 'false'. mappings = { inlinesurroundtoggle = "gs", -- (string|boolean) toggle inline style inlinesurroundtoggle_line = "gss", -- (string|boolean) line-wise toggle inline style inlinesurrounddelete = "ds", -- (string|boolean) delete emphasis surrounding cursor inlinesurroundchange = "cs", -- (string|boolean) change emphasis surrounding cursor link_add = "gl", -- (string|boolean) add link link_follow = "gx", -- (string|boolean) follow link gocurrheading = "]c", -- (string|boolean) set cursor to current section heading goparentheading = "]p", -- (string|boolean) set cursor to parent section heading gonextheading = "]]", -- (string|boolean) set cursor to next section heading goprevheading = "[", -- (string|boolean) set cursor to previous section heading }, inline_surround = { -- For the emphasis, strong, strikethrough, and code fields: -- * 'key': used to specify an inline style in toggle, delete, and change operations -- * 'txt': text inserted when toggling or changing to the corresponding inline style emphasis = { key = "i", txt = "*", }, strong = { key = "b", txt = "**", }, strikethrough = { key = "s", txt = "~~", }, code = { key = "c", txt = ":h markdown.usage", }, }, link = { paste = { enable = true, -- whether to convert URLs to links on paste }, }, toc = { -- Comment text to flag headings/sections for omission in table of contents. omit_heading = "toc omit heading", omit_section = "toc omit section", -- Cycling list markers to use in table of contents. -- Use '.' and ')' for ordered lists. markers = { "-" }, }, -- Hook functions allow for overriding or extending default behavior. -- Called with a table of options and a fallback function with default behavior. -- Signature: fun(opts: table, fallback: fun()) hooks = { -- Called when following links. Provided the following options: -- * 'dest' (string): the link destination -- * 'usedefaultapp' (boolean|nil): whether to open the destination with default application -- (refer to documentation on <Plug> mappings for explanation of when this option is used) follow_link = nil, }, on_attach = nil, -- (fun(bufnr: integer)) callback when plugin attaches to a buffer }</code></pre>on_attachis useful for creating additional buffer-only keymaps:<pre><code class="lang-lua">on_attach = function(bufnr) local map = vim.keymap.set local opts = { buffer = bufnr } map({ 'n', 'i' }, '<M-l><M-o>', '<Cmd>MDListItemBelow<CR>', opts) map({ 'n', 'i' }, '<M-L><M-O>', '<Cmd>MDListItemAbove<CR>', opts) map('n', '<M-c>', '<Cmd>MDTaskToggle<CR>', opts) map('x', '<M-c>', ':MDTaskToggle<CR>', opts) end,</code></pre>
markdown.nvim can even be configured to support standard/typical inline style keybindings in visual mode like
for strong/bold andfor emphasis/italic:<pre><code class="lang-lua">on_attach = function(bufnr) local function toggle(key) return "<Esc>gv<Cmd>lua require'markdown.inline'" .. ".toggleemphasisvisual'" .. key .. "'<CR>" end
vim.keymap.set("x", "<C-b>", toggle("b"), { buffer = bufnr }) vim.keymap.set("x", "<C-i>", toggle("i"), { buffer = bufnr }) end,</code></pre>
mappingsmarkdown.nvim sets up
mappings regardless of configuration. Most are used by the configuration mappings described above:|
Mapping | Configuration Mapping | | ------------------------------------------ | ----------------------------- | |(markdowntoggleemphasis) |inlinesurroundtoggle| |(markdowntoggleemphasisline) |inlinesurroundtoggleline| |(markdowntoggleemphasisvisual) |inlinesurround_toggle| |(markdowndeleteemphasis) |inlinesurrounddelete| |(markdownchangeemphasis) |inlinesurroundchange| |(markdownaddlink) |link_add| |(markdownaddlinkvisual) |linkadd| |(markdownfollowlink) |link_follow| |(markdowngocurrentheading) |gocurr_heading| |(markdowngoparentheading) |goparent_heading| |(markdowngonextheading) |gonext_heading| |(markdowngoprevheading) |goprev_heading|The following
mappings are not used by markdown.nvim's configuration:|
Mapping | Description | | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |(markdownfollowlinkdefaultapp) | Like(markdownfollowlink) but opens links to non-markdown files in the default application for the link destination. The hookfollowlinkis called before following the link with the option 'usedefault_app' set to 'true'. |Usage
Detailed usage instructions can be found in the help doc (
).^some textmarkdown.nvim is broken up into different feature categories:
- [Inline Surround
- Table of Contents
- Lists
- Links
- Navigation
Inline surround
markdown.nvim provides keymaps to toggle, delete, and change emphasis and code spans, referred to in this section as "styles". The supported styles and the default keys used to refer to them are:
| Style | Key | |:----------------------------------------|:---:| | emphasis (typically rendered in italic) | "i" | | strong (typically rendered in bold) | "b" | | strikethrough | "s" | | code span | "c" |
Inline styles can be toggled over vim motions in normal and visual mode. Toggled styles are only applied to appropriate markdown elements (i.e., not blank lines, list markers, etc.). For example, a motion that includes a list marker and multiple blocks will only apply the style to inline content:
- #### Toggle
<pre><code class="lang-txt">toggle strong over five lines ----------------------------------------------- paragraph block paragraph block - list item - list item ----> another paragraph **another pargraph over two lines over two lines**</code></pre>
In normal mode this is done with gs{motion}{style}, where {style} is the key corresponding to the style to toggle. Like other vim motions, a [count] can be specified before and after the gs. Styles can also be toggled over the current line using gss{style}. A [count] can be specified to toggle over multiple lines.
| Before | Command | After | |:-------------------|:-------:|:----------------| |
| gs2es |some text| |some t^ext| gsiwb |some text| |some t^ext| gsiwi |some text| |some^ text| gssb |some text|^denotes cursor position^some text$Styles can be toggled in visual mode based on a visual selection using gs{style}.
| Before | Command | After | |:--------------------|:-------:|:----------------| |
| gss |some text| |some ^text$| gsb |some text| |some ^text$| gsi |some text| |^some text$| gsb |some text|^and$denote selection start and end, respectively^Styles can also be toggled in visual block mode.
<pre><code class="lang-txt">Before | Command | After ----------------|---------|---------------- - list ^item$ 1 | | - list item 1 - li2 | | - li2 | gsi | - list ^item$ 3 | | - list item 3 - list ^item$ 4 | | - list item 4
and$denote block selection start and end on each line, respectively</code></pre>some^ textInline styles around the cursor can be deleted in normal mode using ds{style}, where {style} is the key corresponding to the style to delete. Only the style directly surrounding the cursor will be deleted.
- #### Delete
| Before | Command | After | |:---------------------|:-------:|:----------------| |
| dsb |some text| |some t^ext| dsb |some text| |some t^ext*| dsb |some text*|^denotes cursor positionsome^ textInline styles around the cursor can be changed in normal mode using cs{from}{to}, where {from} and {to} are the keys corresponding to the current style ({from}) and the new style ({to}). Only the matching {from} style directly surrounding the cursor will be changed.
- #### Change
| Before | Command | After | |:---------------------|:-------:|:--------------------| |
| csbi |some text**| |some t^ext| csbi |some text**| |some t^ext*| csbs |sometext*|^denotes cursor position:MDInsertToc [max_level] [markers]Table of contents
A table of contents (TOC) is created from the top-level ATX and setext headings of markdown buffers.
The
command adds a TOC by inserting (normal mode) or replacing selected lines (visual mode). Optional arguments can be provided to set the max heading level to include and the list markers to alternate through for each heading level.:MDToc [maxlevel] [size]The
and:MDTocAll [maxlevel] [size]commands show a TOC for the current buffer in the current window's location list.:MDTocomits flagged headings and:MDTocAllincludes all headings. The optional[max_level]argument specifies the max heading level to include, and the optional[size]argument specifies the desired size of the location list. These commands also forward command modifiers to:lopen.Omit sections and headings <!-- toc omit heading -->
Headings and entire sections can be omitted from the TOC by flagging them with
and, respectively. The flag can either be placed directly above (i.e., on the line immediately preceding) or within the heading content. For example, the following headings would be omitted:on_attach<pre><code class="lang-md"># heading 1 <!-- toc omit heading -->
<!-- toc omit heading -->
heading 2
<!-- toc omit section -->
section heading omitted
subsection heading also omitted</code></pre>
Lists
Most list editing commands are intended to be invoked by custom keymaps (see notes on the
field under configuration).:MDListItemBelowUse the
- #### Inserting items
and:MDListItemAbovecommands to insert a new list item below and above the current cursor position, respectively. Both commands maintain the same indentation and list marker as the item under the cursor. The commands do nothing if the cursor is not within an existing list.:MDResetListNumberingWhen inserting an item in an ordered list, numbering is reset automatically for that list.
The
- #### Reset numbering
command resets the numbering of all ordered lists in the current buffer (normal mode) or under the current visual selection (visual mode).:MDTaskToggleThe
- #### Toggle tasks
command toggles the task(s) on the current cursor line (normal mode) or under the current visual selection (visual mode).#destinationLinks
Links can be added over vim motions in normal and visual mode. Links are only added when the motion is within one inline block (i.e., not over list markers, blank lines, etc.). In normal mode this is done with gl{motion} and over a visual selection with gl.
- #### Add
Follow links under the cursor in normal mode with gx. Supported in-editor navigation:
- #### Follow
-
: headings in the current buffer -./destination: files and directories relative to the current buffer -/destination: files and directories relative to the working directory - Other absolute path destinations are opened if they exist]cURL destinations are opened in the browser.
URLs can be pasted over a visual selection (not a visual block selection) from the system clipboard as markdown links. The visual selection must be contained by one inline block (i.e., conversion to a link will not occur if the visual selection includes blank lines, list markers, etc.).
- #### Paste URLs
Navigation
Markdown buffers can be navigated with the following keymaps:
: go to the current section heading]p: go to the parent section heading]]: go to the next section heading[: go to the previous section headingmodule.nvim-treesitter module
markdown.nvim can also be configured as an [nvim-treesitter
> [!CAUTION] > This feature is only supported when using nvim-treesitter's master
branch, which has been locked and remains available for backward compatibility.Module installation <!-- toc omit heading -->
The following code snippets show how to install markdown.nvim as an nvim-treesitter module. Refer to nvim-treesitter for the appropriate way to install and manage parsers.
" after plug#end() lua << EOF require("nvim-treesitter.configs").setup({ ensureinstalled = { "markdown", "markdowninline", --[[ other parsers you need ]] }, markdown = { enable = true, -- configuration here or nothing for defaults }, }) EOF</code></pre>
<pre><code class="lang-lua">{ "nvim-treesitter/nvim-treesitter", requires = { "tadmccorkle/markdown.nvim" }, config = function() require("nvim-treesitter.configs").setup({ ensureinstalled = { "markdown", "markdowninline", --[[ other parsers you need ]] }, markdown = { enable = true, -- configuration here or nothing for defaults }, }) end, };</code></pre>Module configuration <!-- toc omit heading -->
When markdown.nvim is configured as an nvim-treesitter module, configuration options are passed to the require("nvim-treesitter.configs").setup()` function. All configuration options are the same as described in the configuration section.
local configs = require("nvim-treesitter.configs")
configs.setup({ ensureinstalled = { "markdown", "markdowninline", --[[ other parsers you need ]] }, markdown = { enable = true, -- must be specified to enable markdown.nvim as a module -- configuration here or nothing for defaults }, })