Home

Awesome

markdown-toggle.nvim

A simple and useful set of toggle commands for Markdown. Similar to Obsidian

Features

Installation

<details> <summary>lazy.nvim</summary>
{
  "roodolv/markdown-toggle.nvim",
  config = function()
    require("markdown-toggle").setup()
  end,
},
</details> <details> <summary>packer.nvim</summary>
use {
  "roodolv/markdown-toggle.nvim",
  config = function()
    require("markdown-toggle").setup()
  end,
}
</details> <details> <summary>vim-plug</summary>
Plug "roodolv/markdown-toggle.nvim"
</details>

Other Plugin Managers

For specific installation instructions, please refer to the documentation of your preferred plugin manager.

Configuration

Minimal Setup

Include this single line in your init.lua or config:

require("markdown-toggle").setup()

Default Config

The default settings are as follows:

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" },

  -- Cycle the marks in user-defined table when toggling lists
  enable_list_cycle = false,
  -- 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 checkboxes
  enable_box_cycle = false,
  -- The checkbox marks table used in cycle-mode (box_table[1] is used as the default checked-state)
  box_table = { "x", "~", "!", ">" },

  -- Mimic the behavior of Obsidian's "Toggle bullet list" on `list()`
  mimic_obsidian_list = true,
  -- Mimic the behavior of Obsidian's "Cycle bullet/checkbox" on `checkbox()`
  mimic_obsidian_cycle = true,

  -- The heading marks table used in `markdown-toggle.heading`
  heading_table = { "#", "##", "###", "####", "#####" },

  -- Skip blank lines and headings in Visual mode (except for quotes)
  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,
})

Keymaps

Auto-setup

For a quick start with default keymaps, add this to your setup:

require("markdown-toggle").setup({
  use_default_keymaps = true,
})

Manual-setup Examples

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,
})

Dot-repeat

If you set 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", "<C-n>", toggle.olist_dot, opts)
vim.keymap.set("n", "<Leader><C-x>", toggle.checkbox_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", "<C-n>", toggle.olist, opts)
vim.keymap.set("x", "<Leader><C-x>", toggle.checkbox, opts)
vim.keymap.set("x", "<C-h>", toggle.heading, opts)

If you set 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" }, "<C-n>", toggle.olist, opts)
vim.keymap.set({ "n", "x" }, "<Leader><C-x>", toggle.checkbox, opts)
vim.keymap.set({ "n", "x" }, "<C-h>", toggle.heading, opts)

Autolist

If you set 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)

Config-switch

You can switch various options in the comfort of your active buffer, without the need to restart or reload Neovim.

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_list_cycle, opts)
vim.keymap.set("n", "<Leader>mX", toggle.switch_box_cycle, opts)

API

This plugin provides the following set of API functions:

typefunctionvim-mode
Quotesquote()Normal, Visual
quote_dot()Normal
Listslist()Normal, Visual
list_dot()Normal
Ordered Listsolist()Normal, Visual
olist_dot()Normal
Checkboxescheckbox()Normal, Visual
checkbox_dot()Normal
Headingsheading()Normal, Visual
heading_dot()Normal
Autolistautolist_up()<br>autolist_down()Normal
autolist_cr()Insert
Config-switchswitch_unmarked_only()<br>switch_blankhead_skip()<br>switch_inner_indent()<br>switch_auto_samestate()<br>switch_list_cycle()<br>switch_box_cycle()<br>switch_mimic_obsidian_list()<br>switch_mimic_obsidian_cycle()Normal

Tips

Mimicking Obsidian

If you don't want to mimic Obsidian's behavior, set mimic_obsidian_list and mimic_obsidian_cycle to false in your init.lua or config.

These options are set to true by default.

API functions are available, allowing you to set keymaps for switching them.

mimic_obsidian_list

- [ ] foo
↓ execute `list()`
foo
↓
- foo
↓
foo
↓
- [ ] foo
↓ execute `list()`
- foo
↓
foo
↓
- foo
↓

mimic_obsidian_cycle

foo
↓ execute `checkbox()` with `enable_box_cycle = true`
- foo
↓
- [ ] foo
↓
- [x] foo
↓
- [~] foo
↓
- foo
↓
- [ ] foo
↓
foo
↓ execute `checkbox()` with `enable_box_cycle = true`
- [ ] foo
↓
- [x] foo
↓
- [~] foo
↓
- [ ] foo
↓

Related Plugins

References

Todo