Skip to content

A Neovim plugin that provides a simple and useful set of toggle commands for Markdown.

License

Notifications You must be signed in to change notification settings

roodolv/markdown-toggle.nvim

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

11 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

markdown-toggle.nvim

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

Features

  • Handles quotes, headings, lists (unordered and ordered), and checkboxes markdown_common

  • Cycle through different levels of headings, types of lists, and states of checkboxes cyclic_toggling01 cyclic_toggling02

  • 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 config_switch01 config_switch02

Installation

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"

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:

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

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:

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

Toggle Functions

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)

Autolist

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)

Config-switch

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)

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:

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

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

References

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 <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 the heading() function twice

About

A Neovim plugin that provides a simple and useful set of toggle commands for Markdown.

Topics

Resources

License

Stars

Watchers

Forks

Languages