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 (about 1 year ago)
- Default Branch: main
- Last Pushed: 2024-12-22T11:13:08.000Z (7 months ago)
- Last Synced: 2025-05-31T09:43:09.969Z (about 1 month 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
A simple and useful set of toggle commands for Markdown. Similar to [Obsidian](https://obsidian.md)
### Features
- 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**
- Unmarked Only: Toggle only unmarked lines first
- Blankhead Skip: Skip blank lines and headings in Visual mode (except for `quote()`)
- Inner Indent: Insert an indent for new lines within quoted text
- Autolist Same-state: Maintain checkbox state when continuing lists

## 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
```lua
Plug "roodolv/markdown-toggle.nvim"
```### 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:
```lua
require("markdown-toggle").setup()
```### Default Config
The default settings are as follows:Default Config
```lua
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,
})
```### Config for Cycling
List-Cycling
- `cycle_list_table = false` (default):
```
foo
↓ call `list()`
- foo
↓
foo
↓
```- `cycle_list_table = true` and `list_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` and `box_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
↓
```## Keymaps
### Auto-setup
For a quick start with default keymaps, add this to your setup:
```lua
require("markdown-toggle").setup({
use_default_keymaps = true,
})
```### Manual-setup Examples
First, set up the common autocmd structure:
```lua
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,
})
```#### Toggle Functions
Common keymap examples for toggle functions.Examples
If `enable_dot_repeat = true` (default):
```lua
opts.expr = true -- required for dot-repeat in Normal mode
vim.keymap.set("n", "", toggle.quote_dot, opts)
vim.keymap.set("n", "", toggle.list_dot, opts)
vim.keymap.set("n", "", toggle.list_cycle_dot, opts)
vim.keymap.set("n", "", toggle.olist_dot, opts)
vim.keymap.set("n", "", toggle.checkbox_dot, opts)
vim.keymap.set("n", "", toggle.checkbox_cycle_dot, opts)
vim.keymap.set("n", "", toggle.heading_dot, opts)opts.expr = false -- required for Visual mode
vim.keymap.set("x", "", toggle.quote, opts)
vim.keymap.set("x", "", toggle.list, opts)
vim.keymap.set("x", "", toggle.list_cycle, opts)
vim.keymap.set("x", "", toggle.olist, opts)
vim.keymap.set("x", "", toggle.checkbox, opts)
vim.keymap.set("x", "", toggle.checkbox_cycle, opts)
vim.keymap.set("x", "", toggle.heading, opts)
```If `enable_dot_repeat = false`:
```lua
vim.keymap.set({ "n", "x" }, "", toggle.quote, opts)
vim.keymap.set({ "n", "x" }, "", toggle.list, opts)
vim.keymap.set({ "n", "x" }, "", toggle.list_cycle, opts)
vim.keymap.set({ "n", "x" }, "", toggle.olist, opts)
vim.keymap.set({ "n", "x" }, "", toggle.checkbox, opts)
vim.keymap.set({ "n", "x" }, "", toggle.checkbox_cycle, opts)
vim.keymap.set({ "n", "x" }, "", toggle.heading, opts)
```#### Autolist
Examples
If `enable_autolist = true` (default):
```lua
vim.keymap.set("n", "O", toggle.autolist_up, opts)
vim.keymap.set("n", "o", toggle.autolist_down, opts)
vim.keymap.set("i", "", 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.Examples
```lua
vim.keymap.set("n", "mU", toggle.switch_unmarked_only, opts)
vim.keymap.set("n", "mB", toggle.switch_blankhead_skip, opts)
vim.keymap.set("n", "mI", toggle.switch_inner_indent, opts)
vim.keymap.set("n", "mS", toggle.switch_auto_samestate, opts)
vim.keymap.set("n", "mL", toggle.switch_cycle_list_table, opts)
vim.keymap.set("n", "mX", toggle.switch_cycle_box_table, opts)
vim.keymap.set("n", "mC", toggle.switch_list_before_box, opts)
```## API
### API: Functions
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 |### API: Dot-repeatable
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()`### API: Cycle-only
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`:
```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)
```## Etc
### For Obsidian Users
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()`.
## Related Plugins
- [markdowny.nvim](https://github.com/antonk52/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](https://github.com/kylechui/nvim-surround)
- For more information and implementation details, check out this:
- [Surrounds Showcase](https://github.com/kylechui/nvim-surround/discussions/53)
- [obsidian.nvim](https://github.com/epwalsh/obsidian.nvim)## References
- [mder.nvim](https://github.com/phanen/mder.nvim)
- [markdown-togglecheck](https://github.com/nfrid/markdown-togglecheck)## Todo
**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 `` 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` should call the `heading()` function twice