A Neovim plugin for generating and managing table of contents in markdown files. Designed to work seamlessly with Telekasten but works independently as well.
- Generate table of contents from markdown headings
- Support for multiple markdown flavors:
- GFM (GitHub Flavored Markdown)
- GitLab
- Redcarpet
- Marked
- Pleasant looking anchor links for internal file references
- Optional Telescope integration for TOC navigation
- Auto-update TOC on save (optional)
- Works with both ATX (
# Heading) and Setext heading styles - Respects code blocks (won't include headings from code)
- Configurable indentation, list markers, and heading levels
use {
'D3alWyth1T/md_toc',
config = function()
require('md_toc').setup({
-- Optional configuration (these are the defaults)
style = 'gfm', -- gfm, gitlab, redcarpet, marked
list_marker = '*', -- Character for list items
indent_size = 4, -- Spaces per indentation level
min_level = 1, -- Minimum heading level to include
max_level = 6, -- Maximum heading level to include
auto_update = false, -- Auto-update TOC on save
fence_text = 'md_toc', -- Text to use in fence comments
telescope_navigation = true, -- Use Telescope for navigation
telekasten_integration = true, -- Enable Telekasten integration
})
end
}{
'D3alWyth1T/md_toc',
ft = 'markdown',
config = function()
require('md_toc').setup()
end
}Plug 'D3alWyth1T/md_toc'
lua << EOF
require('md_toc').setup()
EOF:MdTocGenerate- Generate a new table of contents at cursor position:MdTocUpdate- Update existing table of contents:MdTocRemove- Remove table of contents from buffer:MdTocGoto- Jump to heading under cursor (when cursor is on TOC entry):MdTocNav- Open Telescope picker to navigate headings (requires Telescope)
- Open a markdown file
- Position cursor where you want the TOC
- Run
:MdTocGenerate
The plugin will generate a TOC like this:
<!-- md_toc GFM -->
* [md_toc](#md_toc)
* [Features](#features)
* [Installation](#installation)
* [Using Packer](#using-packer)
* [Using lazy.nvim](#using-lazynvim)
* [Using vim-plug](#using-vim-plug)
* [Usage](#usage)
* [Commands](#commands)
* [Example Workflow](#example-workflow)
* [Auto-Update](#auto-update)
* [Telescope Navigation](#telescope-navigation)
* [Configuration](#configuration)
* [Complete Configuration Example](#complete-configuration-example)
* [Markdown Styles](#markdown-styles)
* [Telekasten Integration](#telekasten-integration)
* [Recommended Telekasten Configuration](#recommended-telekasten-configuration)
* [Advanced Usage](#advanced-usage)
* [Programmatic API](#programmatic-api)
* [Custom Keybindings](#custom-keybindings)
* [Comparison with vim-markdown-toc](#comparison-with-vim-markdown-toc)
* [Requirements](#requirements)
* [License](#license)
* [Contributing](#contributing)
* [Acknowledgments](#acknowledgments)
<!-- md_toc -->Enable auto-update to automatically refresh the TOC when you save:
require('md_toc').setup({
auto_update = true,
})If Telescope is installed, use :MdTocNav to open a searchable list of all headings:
-- Optional: Add a keybinding
vim.keymap.set('n', '<leader>mt', '<cmd>MdTocNav<cr>', { desc = 'Navigate TOC' })require('md_toc').setup({
-- Anchor link style
style = 'gfm', -- 'gfm', 'gitlab', 'redcarpet', 'marked'
-- List formatting
list_marker = '*', -- Character for list items
cycle_list_markers = false, -- Use *, -, + for different levels
indent_size = 4, -- Spaces per indentation level
-- Heading level filtering
min_level = 1, -- Include headings from level 1...
max_level = 6, -- ...through level 6
-- Fence markers
fence_text = 'md_toc', -- Text in fence comments
dont_insert_fence = false, -- Set true to omit fence markers
-- Auto-update behavior
auto_update = false, -- Auto-update on save
-- Integration
telekasten_integration = true, -- Enable Telekasten integration
telescope_navigation = true, -- Enable Telescope navigation
})Different markdown processors generate anchors differently:
GFM (GitHub Flavored Markdown)
- Default style, works with GitHub, Obsidian, and most modern parsers
"Chapter One"→#chapter-one- Preserves Unicode characters:
"第三章"→#第三章
GitLab
- Similar to GFM with slightly different special character handling
- Consolidates multiple hyphens
- Strips leading/trailing underscores
Redcarpet
- Ruby markdown processor
- HTML entity encoding for some characters
"Q & A"→#q-&-a
Marked
- JavaScript markdown processor
- Minimal character transformation
- Simpler anchor generation
The plugin works great with Telekasten! It automatically detects when Telekasten is installed and can integrate with it.
-- In your Telekasten setup
require('telekasten').setup({
home = vim.fn.expand("~/zettelkasten"),
-- ... other Telekasten config
})
-- Setup md_toc
require('md_toc').setup({
telekasten_integration = true,
})
-- Recommended keybindings for Telekasten workflow
vim.keymap.set('n', '<leader>zc', '<cmd>MdTocGenerate<cr>', { desc = 'Generate TOC' })
vim.keymap.set('n', '<leader>zu', '<cmd>MdTocUpdate<cr>', { desc = 'Update TOC' })
vim.keymap.set('n', '<leader>zh', '<cmd>MdTocGoto<cr>', { desc = 'Jump to heading' })You can also use the plugin programmatically in Lua:
local md_toc = require('md_toc')
-- Generate TOC
md_toc.generate()
-- Update TOC
md_toc.update()
-- Remove TOC
md_toc.remove()
-- Navigate to heading
md_toc.goto()
-- Access internal modules
local headings = md_toc.headings.extract_headings(0, config)
local anchor = md_toc.anchors.generate("My Heading", "gfm", {})Standalone usage (without Telekasten):
-- Basic TOC operations
vim.keymap.set('n', '<leader>tg', '<cmd>MdTocGenerate<cr>', { desc = 'Generate TOC' })
vim.keymap.set('n', '<leader>tu', '<cmd>MdTocUpdate<cr>', { desc = 'Update TOC' })
vim.keymap.set('n', '<leader>tr', '<cmd>MdTocRemove<cr>', { desc = 'Remove TOC' })
vim.keymap.set('n', '<leader>th', '<cmd>MdTocGoto<cr>', { desc = 'Jump to heading' })
vim.keymap.set('n', '<leader>tn', '<cmd>MdTocNav<cr>', { desc = 'Navigate TOC with Telescope' })With Telekasten (recommended for Telekasten users):
-- Telekasten-style keybindings
vim.keymap.set('n', '<leader>nc', '<cmd>MdTocGenerate<cr>', { desc = 'Generate TOC' })
vim.keymap.set('n', '<leader>nu', '<cmd>MdTocUpdate<cr>', { desc = 'Update TOC' })
vim.keymap.set('n', '<leader>nh', '<cmd>MdTocGoto<cr>', { desc = 'Jump to heading' })Advanced: Override gf to handle TOC links:
-- Make gf work with TOC entries (falls back to normal gf for other links)
vim.keymap.set('n', 'gf', function()
local line = vim.api.nvim_get_current_line()
if line:match('%[.-%]%(#.-%)') then
vim.cmd('MdTocGoto')
else
vim.cmd('normal! gf')
end
end, { buffer = true, desc = 'Follow link or TOC entry' })This plugin is inspired by vim-markdown-toc but offers:
- Written in Lua for modern Neovim
- Better integration with Neovim ecosystem (Telescope, Lua API)
- Built-in Telekasten support
- Simplified configuration using Lua tables
- Active development for Neovim-specific features
- Neovim 0.7.0 or later
- Optional: Telescope for enhanced navigation
- Optional: Telekasten for note-taking integration
MIT License - see LICENSE file for details.
Contributions welcome! Please feel free to submit issues or pull requests.
- Designed to complement Telekasten