A simple and useful set of toggle commands for Markdown. Similar to Obsidian
-
Handles quotes, headings, lists (unordered and ordered), and checkboxes
-
Cycle through different levels of headings, types of lists, and states of checkboxes
-
Automatically continue quotes, lists, and checkboxes when starting a new line
-
Use Vim's dot (
.
) command to repeat toggle actions (only in Normal mode) -
Change plugin settings on-the-fly
lazy.nvim
{
"roodolv/markdown-toggle.nvim",
config = function()
require("markdown-toggle").setup()
end,
},
packer.nvim
use {
"roodolv/markdown-toggle.nvim",
config = function()
require("markdown-toggle").setup()
end,
}
vim-plug
Plug "roodolv/markdown-toggle.nvim"
For specific installation instructions, please refer to the documentation of your preferred plugin manager.
Include this single line in your init.lua
or config:
require("markdown-toggle").setup()
The default settings are as follows:
Default Config
require("markdown-toggle").setup({
-- If true, the auto-setup for the default keymaps is enabled
use_default_keymaps = false,
-- The keymaps are valid only for these filetypes
filetypes = { "markdown", "markdown.mdx" },
-- The list marks table used in cycle-mode (list_table[1] is used as the default list-mark)
list_table = { "-", "+", "*", "=" },
-- Cycle the marks in user-defined table when toggling lists
cycle_list_table = false,
-- The checkbox marks table used in cycle-mode (box_table[1] is used as the default checked-state)
box_table = { "x", "~", "!", ">" },
-- Cycle the marks in user-defined table when toggling checkboxes
cycle_box_table = false,
-- A bullet list is toggled before turning into a checkbox (similar to how it works in Obsidian).
list_before_box = false,
-- The heading marks table used in `markdown-toggle.heading`
heading_table = { "#", "##", "###", "####", "#####" },
-- Skip blank lines and headings in Visual mode (except for `quote()`)
enable_blankhead_skip = true,
-- Insert an indented quote for new lines within quoted text
enable_inner_indent = false,
-- Toggle only unmarked lines first
enable_unmarked_only = true,
-- Automatically continue lists on new lines
enable_autolist = true,
-- Maintain checkbox state when continuing lists
enable_auto_samestate = false,
-- Dot-repeat for toggle functions in Normal mode
enable_dot_repeat = true,
})
List-Cycling
cycle_list_table = false
(default):
foo
β call `list()`
- foo
β
foo
β
cycle_list_table = true
andlist_table = { "-", "+" }
:
foo
β call `list()`
- foo
β
+ foo
β
foo
β
Checkbox-Cycling
cycle_box_table = false
(default):
foo
β call `checkbox()`
- foo
β
- [ ] foo
β
- [x] foo
β
- foo
β
cycle_box_table = true
andbox_table = { "x", "~" }
:
foo
β call `checkbox()`
- foo
β
- [ ] foo
β
- [x] foo
β
- [~] foo
β
- foo
β
List-Before-Checkbox
list_before_box = false
(default):
foo
β call `checkbox()`
- [ ] foo
β
- [x] foo
β
- [ ] foo
β
list_before_box = true
:
foo
β call `checkbox()`
- foo
β
- [ ] foo
β
- [x] foo
β
- foo
β
For a quick start with default keymaps, add this to your setup:
require("markdown-toggle").setup({
use_default_keymaps = true,
})
First, set up the common autocmd structure:
vim.api.nvim_create_autocmd("FileType", {
desc = "markdown-toggle.nvim keymaps",
pattern = { "markdown", "markdown.mdx" },
callback = function(args)
local opts = { silent = true, noremap = true, buffer = args.buf }
local toggle = require("markdown-toggle")
-- Keymap configurations will be added here for each feature
end,
})
Common keymap examples for toggle functions.
Examples
If enable_dot_repeat = true
(default):
opts.expr = true -- required for dot-repeat in Normal mode
vim.keymap.set("n", "<C-q>", toggle.quote_dot, opts)
vim.keymap.set("n", "<C-l>", toggle.list_dot, opts)
vim.keymap.set("n", "<Leader><C-l>", toggle.list_cycle_dot, opts)
vim.keymap.set("n", "<C-n>", toggle.olist_dot, opts)
vim.keymap.set("n", "<M-x>", toggle.checkbox_dot, opts)
vim.keymap.set("n", "<Leader><M-x>", toggle.checkbox_cycle_dot, opts)
vim.keymap.set("n", "<C-h>", toggle.heading_dot, opts)
opts.expr = false -- required for Visual mode
vim.keymap.set("x", "<C-q>", toggle.quote, opts)
vim.keymap.set("x", "<C-l>", toggle.list, opts)
vim.keymap.set("x", "<Leader><C-l>", toggle.list_cycle, opts)
vim.keymap.set("x", "<C-n>", toggle.olist, opts)
vim.keymap.set("x", "<M-x>", toggle.checkbox, opts)
vim.keymap.set("x", "<Leader><M-x>", toggle.checkbox_cycle, opts)
vim.keymap.set("x", "<C-h>", toggle.heading, opts)
If enable_dot_repeat = false
:
vim.keymap.set({ "n", "x" }, "<C-q>", toggle.quote, opts)
vim.keymap.set({ "n", "x" }, "<C-l>", toggle.list, opts)
vim.keymap.set({ "n", "x" }, "<Leader><C-l>", toggle.list_cycle, opts)
vim.keymap.set({ "n", "x" }, "<C-n>", toggle.olist, opts)
vim.keymap.set({ "n", "x" }, "<M-x>", toggle.checkbox, opts)
vim.keymap.set({ "n", "x" }, "<Leader><M-x>", toggle.checkbox_cycle, opts)
vim.keymap.set({ "n", "x" }, "<C-h>", toggle.heading, opts)
Examples
If enable_autolist = true
(default):
vim.keymap.set("n", "O", toggle.autolist_up, opts)
vim.keymap.set("n", "o", toggle.autolist_down, opts)
vim.keymap.set("i", "<CR>", toggle.autolist_cr, opts)
You can switch various options in the comfort of your active buffer, without the need to restart or reload Neovim.
Examples
vim.keymap.set("n", "<Leader>mU", toggle.switch_unmarked_only, opts)
vim.keymap.set("n", "<Leader>mB", toggle.switch_blankhead_skip, opts)
vim.keymap.set("n", "<Leader>mI", toggle.switch_inner_indent, opts)
vim.keymap.set("n", "<Leader>mS", toggle.switch_auto_samestate, opts)
vim.keymap.set("n", "<Leader>mL", toggle.switch_cycle_list_table, opts)
vim.keymap.set("n", "<Leader>mX", toggle.switch_cycle_box_table, opts)
vim.keymap.set("n", "<Leader>mC", toggle.switch_list_before_box, opts)
This plugin provides the following set of API functions:
type | function | vim-mode |
---|---|---|
Quotes | quote() |
Normal, Visual |
Lists | list() |
Normal, Visual |
Lists(cycle-only) | list_cycle() |
Normal, Visual |
Ordered Lists | olist() |
Normal, Visual |
Checkboxes | checkbox() |
Normal, Visual |
Checkboxes(cycle-only) | checkbox_cycle() |
Normal, Visual |
Headings | heading() |
Normal, Visual |
Dot-repeatable | XXX_dot() |
Normal |
Autolist | autolist_up() autolist_down() |
Normal |
autolist_cr() |
Insert | |
Config-switch | switch_unmarked_only() switch_blankhead_skip() switch_inner_indent() switch_auto_samestate() switch_cycle_list_table() switch_cycle_box_table() switch_list_before_box |
Normal |
Dot-repeatable functions have names like XXX_dot()
.
For example:
- Dot-repeatable function for block-quote is
quote_dot()
- Dot-repeatable function for checkbox is
checkbox_dot()
Cycle-only Functions
The cycle-only functions are like:
list_cycle()
,list_cycle_dot()
checkbox_cycle()
,checkbox_cycle_dot()
These funcs only perform mark-cycling every time you call them, regardless of whether cycle_XXX_table
is true
or not.
So if you'd like to have TWO separate keymaps for both toggling and cycling functions, you no longer need to set or switch cycle_XXX_table
:
-- list() performs toggling/cycling (can be switched with option)
vim.keymap.set({ "n", "x" }, "<C-l>", toggle.list, opts)
-- list_cycle() performs cycling only
vim.keymap.set({ "n", "x" }, "<Leader><C-l>", toggle.list_cycle, opts)
If you'd like this plugin to behave like Obsidian, take a look at this:
How to use like Obsidian
Obsidian commands | API | config |
---|---|---|
Toggle blockquote | quote() , quote_dot() |
any |
Toggle bullet list | list() , list_dot() |
any |
Toggle numbered list | olist() , olist_dot() |
any |
Toggle checkbox status | checkbox() , checkbox_dot() |
list_before_box is false |
Cycle bullet/checkbox | checkbox() , checkbox_dot() |
list_before_box is true |
NOTE: list_before_box
can be toggled with switch_list_before_box()
.
- markdowny.nvim
- If you want to easily apply or toggle code, codeblock, link, bold, or italic formatting on Markdown text, this may be ideal for you.
- nvim-surround
- For more information and implementation details, check out this:
- obsidian.nvim
NOTE: This is just a provisional plan.
- Rename and consolidate options
- Use more generic config names
- Allow
quote()
to be used without whitespace at the beginning of a line - Recalculate ordered lists automatically
- Implement various autolist behaviors triggered by consecutive
<CR>
presses - Enable
heading()
to directly replace list, olist, or checkbox items - Add an option to toggle
1.
inside headings like### hoge
to### 1. hoge
- Indent text in block quotes with
Tab
, changing> hoge
to> ____hoge
- Add plugin commands (e.g.,
:MarkdownToggleQuote
) to call API functions - Integrate
v:count
(vim.v.count
) support to handle repeated actions- Example:
2<C-h>
should call theheading()
function twice
- Example: