https://github.com/roodolv/markdown-toggle.nvim
A Neovim plugin that provides a simple and useful set of toggle commands for Markdown.
https://github.com/roodolv/markdown-toggle.nvim
document editing markdown neovim neovim-plugin obsidian toggle
Last synced: 12 days ago
JSON representation
A Neovim plugin that provides a simple and useful set of toggle commands for Markdown.
- Host: GitHub
- URL: https://github.com/roodolv/markdown-toggle.nvim
- Owner: roodolv
- License: mit
- Created: 2024-07-11T10:27:16.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2024-12-22T11:13:08.000Z (about 1 year ago)
- Last Synced: 2025-05-31T09:43:09.969Z (7 months ago)
- Topics: document, editing, markdown, neovim, neovim-plugin, obsidian, toggle
- Language: Lua
- Homepage:
- Size: 27.3 KB
- Stars: 32
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# markdown-toggle.nvim
Smart and customizable markdown toggling for Neovim. Provides intuitive commands for quotes, headings, lists, and checkboxes.
[](https://github.com/roodolv/markdown-toggle.nvim/releases)
[](https://www.lua.org)
[](https://neovim.io)
[](LICENSE)
## Table of Contents
- [Features](#features)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Configuration](#configuration)
- [Default Settings](#default-settings)
- [Keymaps Setup](#keymaps-setup)
- [Cycling Behavior](#cycling-behavior)
- [API Reference](#api-reference)
- [Tips](#tips)
- [Related Plugins](#related-plugins)
- [Roadmap](#roadmap)
## Features
- 🧠 **Smart Toggling** - Cycle through quotes, headings, lists, and checkboxes
- 🎨 **Highly Customizable** - Configure marks, keymaps, and behaviors
- 🚀 **On-the-fly Configuration** - Toggle settings without restarting
- ♻️ **Dot Repeat** - Full support for Vim's `.` command
- 🔢 **Count Prefix** - Use `3` to create `### heading`
- 📝 **Auto-list** - Automatic list continuation
- 🧮 **Auto-recalculation** - Ordered lists renumber automatically
- 🪨 **Obsidian-friendly** - Works great for Obsidian users
📸 See it in action



## Installation
lazy.nvim
```lua
{
"roodolv/markdown-toggle.nvim",
config = function()
require("markdown-toggle").setup()
end,
}
```
packer.nvim
```lua
use {
"roodolv/markdown-toggle.nvim",
config = function()
require("markdown-toggle").setup()
end,
}
```
vim-plug
```vim
Plug "roodolv/markdown-toggle.nvim"
```
## Quick Start
### Minimal Setup
```lua
require("markdown-toggle").setup()
```
### With Default Keymaps
```lua
require("markdown-toggle").setup({
use_default_keymaps = true,
})
```
### Custom Keymaps (Recommended)
```lua
require("markdown-toggle").setup({
keymaps = {
toggle = {
[""] = "quote",
[""] = "list",
[""] = "list_cycle",
[""] = "olist",
[""] = "checkbox",
[""] = "checkbox_cycle",
[""] = "heading",
[""] = "heading_toggle",
},
},
})
```
## Configuration
### Default Settings
View all default settings
```lua
{
use_default_keymaps = false,
filetypes = { "markdown", "markdown.mdx" },
keymaps = nil,
list_table = { "-", "+", "*", "=" },
cycle_list_table = false,
box_table = { "x", "~", "!", ">" },
cycle_box_table = false,
list_before_box = false,
obox_as_olist = true,
heading_table = { "#", "##", "###", "####", "#####" },
enable_blankline_skip = true,
enable_heading_skip = true,
enable_unmarked_only = true,
enable_autolist = true,
enable_auto_samestate = false,
enable_olist_recalc = true,
enable_dot_repeat = true,
}
```
### Default Keymaps
View all default keymaps
```lua
keymaps = {
toggle = {
[""] = "quote",
[""] = "list",
[""] = "list_cycle",
[""] = "olist",
[""] = "checkbox",
[""] = "checkbox_cycle",
[""] = "heading",
[""] = "heading_toggle",
},
switch = {
["mU"] = "switch_unmarked_only",
["mB"] = "switch_blankline_skip",
["mH"] = "switch_heading_skip",
["mS"] = "switch_auto_samestate",
["mL"] = "switch_cycle_list_table",
["mX"] = "switch_cycle_box_table",
["mC"] = "switch_list_before_box",
["mO"] = "switch_obox_as_olist",
},
autolist = {
["O"] = "autolist_up",
["o"] = "autolist_down",
[""] = "autolist_cr",
},
},
```
### Keymaps Setup
The keymaps table structure:
```lua
keymaps = {
toggle = { [""] = "function_name" },
switch = { [""] = "function_name" },
autolist = { [""] = "function_name" },
}
```
Available functions
**Toggle functions:**
- `quote`
- `list`, `list_cycle`, `olist`
- `checkbox`, `checkbox_cycle`
- `heading`, `heading_toggle`
> **Note**: `XXX_dot` functions are automatically configured if `enable_dot_repeat = true`.
**Switch functions:**
- `switch_blankline_skip`, `switch_heading_skip`
- `switch_unmarked_only`
- `switch_auto_samestate`
- `switch_cycle_list_table`, `switch_cycle_box_table`
- `switch_list_before_box`, `switch_obox_as_olist`
**Autolist functions:**
- `autolist_up`, `autolist_down`, `autolist_cr`
### Cycling Behavior
#### List Cycling
**Default** (`cycle_list_table = false`):
`foo` → `- foo` → `foo`
**Enabled** (`cycle_list_table = true` with `list_table = { "-", "+", "*" }`):
`foo` → `- foo` → `+ foo` → `* foo` → `foo`
Vertical view
```
foo
↓
- foo
↓
+ foo
↓
* foo
↓
foo
```
#### Checkbox Cycling
**Default** (`cycle_box_table = false`):
`foo` → `- [ ] foo` → `- [x] foo` → `- [ ] foo`
**Enabled** (`cycle_box_table = true` with `box_table = { "x", "~" }`):
`foo` → `- [ ] foo` → `- [x] foo` → `- [~] foo` → `- [ ] foo`
Vertical view
```
foo
↓
- [ ] foo
↓
- [x] foo
↓
- [~] foo
↓
- [ ] foo
```
## API Reference
| API Function | Vim Mode | Description |
|----------|----------|-------------|
| `quote()` | Normal, Visual | Toggle blockquote |
| `list()` | Normal, Visual | Toggle bullet list |
| `list_cycle()` | Normal, Visual | Cycle bullet list marks |
| `olist()` | Normal, Visual | Toggle ordered list |
| `checkbox()` | Normal, Visual | Toggle checkbox |
| `checkbox_cycle()` | Normal, Visual | Cycle checkbox states |
| `heading()` | Normal, Visual | Cycle heading levels |
| `heading_toggle()` | Normal, Visual | Toggle heading on/off |
| `XXX_dot()` | Normal | Dot-repeatable version |
| `autolist_up/down()` | Normal | Auto-list (`O`/`o`) |
| `autolist_cr()` | Insert | Auto-list (``) |
### Config-switch functions
Config-switch functions have corresponding commands like `:MarkdownToggleSwitchXXX`.
Config-switch functions
| API Function | Commands | Description |
|----------|----------|-------------|
| `switch_unmarked_only()` | `:MarkdownToggleSwitchUnmarked` | Toggle unmarked-line-only mode |
| `switch_blankline_skip()` | `:MarkdownToggleSwitchBlankline` | Toggle blankline-skip mode |
| `switch_heading_skip()` | `:MarkdownToggleSwitchHeading` | Toggle heading-skip mode |
| `switch_auto_samestate()` | `:MarkdownToggleSwitchSamestate` | Toggle same-state mode for autolist |
| `switch_cycle_list_table()` | `:MarkdownToggleSwitchCycleList` | Toggle list cycling mode |
| `switch_cycle_box_table()` | `:MarkdownToggleSwitchCycleBox` | Toggle checkbox cycling mode |
| `switch_list_before_box()` | `:MarkdownToggleSwitchListBeforeBox` | Toggle `list_before_box` |
| `switch_obox_as_olist()` | `:MarkdownToggleSwitchOboxAsOlist` | Toggle `obox_as_olist` |
> **Tip**: Try typing `:mkdtlist`, `:mkdtbox`, or `:mdtlist` for faster command completion.
> **Note**: See [For Obsidian Users](#for-obsidian-users) for `list_before_box`.
> **Note**: See [Ordered Checkbox Configuration](#ordered-checkbox-configuration) for `obox_as_olist`.
### Dot-repeatable Functions
Dot-repeatable functions have names like `XXX_dot()`. For example:
- `quote_dot()` for blockquote
- `checkbox_dot()` for checkbox
These functions support Vim's `.` (dot) command for repeating actions.
### Cycle-only Functions
The cycle-only functions (`list_cycle()`, `checkbox_cycle()`) **only perform mark-cycling** every time you call them, regardless of the `cycle_XXX_table` setting.
This allows you to have separate keymaps for toggling and cycling:
```lua
-- list() performs toggling/cycling (can be switched with option)
vim.keymap.set({ "n", "x" }, "", toggle.list, opts)
-- list_cycle() performs cycling only
vim.keymap.set({ "n", "x" }, "", toggle.list_cycle, opts)
```
## Tips
### Autolist Configuration
> **Note:** For best experience, set `autoindent = true` for Markdown buffers.
Example
```lua
vim.api.nvim_create_autocmd("FileType", {
pattern = { "markdown", "markdown.mdx" },
callback = function()
vim.opt_local.autoindent = true
end,
})
```
### For Obsidian Users
If you'd like this plugin to behave like Obsidian, use the following configuration:
| Obsidian Command | API Function | 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 = false` |
| Cycle bullet/checkbox | `checkbox()`, `checkbox_dot()` | `list_before_box = true` |
> **Note:** `list_before_box` can be toggled with `switch_list_before_box()`.
Obsidian-like setup example
```lua
require("markdown-toggle").setup({
list_before_box = true, -- Cycle between list and checkbox
keymaps = {
toggle = {
[""] = "quote",
[""] = "list",
[""] = "olist",
[""] = "checkbox", -- Cycles: foo → - foo → - [ ] foo → - [x] foo
},
autolist = {
-- ["O"] = "autolist_up", -- Obsidian does not have `autolist_up`
-- ["o"] = "autolist_down", -- Obsidian does not have `autolist_down`
[""] = "autolist_cr",
},
},
})
```
### Ordered Checkbox Configuration
You can control how ordered checkboxes are treated with the `obox_as_olist` config.
**When `obox_as_olist = true` (default):**
```md
1. [ ] foo
↓ `olist()`
foo
```
**When `obox_as_olist = false`:**
```md
1. [ ] foo
↓ `olist()`
1. foo
```
## Related Plugins
- [markdowny.nvim](https://github.com/antonk52/markdowny.nvim) - Code, link, bold, italic formatting
- [nvim-surround](https://github.com/kylechui/nvim-surround) - Surround text objects
- [Surrounds Showcase](https://github.com/kylechui/nvim-surround/discussions/53)
- [obsidian.nvim](https://github.com/epwalsh/obsidian.nvim) - Obsidian integration
- [mder.nvim](https://github.com/phanen/mder.nvim) - Markdown editing utilities
- [markdown-togglecheck](https://github.com/nfrid/markdown-togglecheck) - Checkbox toggling
## Roadmap
- [x] Smart toggling for quotes, lists, headings, checkboxes
- [x] Cyclic toggling support
- [x] On-the-fly configuration
- [x] Dot-repeat support
- [x] Auto-list continuation
- [x] Ordered list auto-recalculation
- [x] Plugin commands (`:MarkdownToggleSwitchXXX`)
- [x] `v:count` (`vim.v.count`) support
- [x] Smart continuation for empty list items (contributed by [@Dieal](https://github.com/Dieal) in [#37](https://github.com/roodolv/markdown-toggle.nvim/pull/37))
- [x] Comprehensive codebase refactoring ([#43](https://github.com/roodolv/markdown-toggle.nvim/pull/43))
- [x] Add `mini.test` test framework ([#44](https://github.com/roodolv/markdown-toggle.nvim/pull/44))
- [ ] Improved autolist behavior
- [ ] Grouped configuration structure
- [ ] Tab indentation for quoted text
- [ ] Additional functions: `link()`, `code()`, `codeblock()`, `bold()`, `italic()`, and `strikethrough()`
- [ ] Rename `heading()` → `heading_cycle()`
## License
MIT License - see [LICENSE](LICENSE) for details