Skip to content

10.Lua API

github-actions[bot] edited this page Jul 31, 2026 · 1 revision

Lua API

markdown-plus exposes a Lua API for plugin authors and for anyone scripting their own mappings, commands or autocommands.

Everything here is also in the help file — :help markdown-plus-api.

Entry Points

local mp = require("markdown-plus")
Function Description
mp.setup(opts) Validate and merge config, load enabled features, register <Plug> mappings and autocommands
mp.teardown() Undo everything setup() did and restore the default config
mp.config The merged, validated config table (read-only in practice)
mp.in_list_context(kind) Whether the cursor is in list context — see Customizing Keymaps
mp.enable_features_for_buffer() Enable features for the current buffer manually
mp.setup_autocmds() Re-register the FileType autocommands

setup() is the only function most users ever call.

teardown()

Resets plugin-managed state so setup() can be re-run cleanly:

require("markdown-plus").teardown()

It disables per-buffer features across loaded buffers, clears the augroup, removes the buffer-local default keymaps it installed, resets the <Plug> registration cache and the keymap-fallback state, drops the module handles, and restores mp.config to the shipped defaults.

Important

This is not a full unload. The global <Plug> mappings created by an earlier setup() are not deleted — only the cache that tracks them is cleared, so the next setup() re-registers them. Invoking a <Plug> mapping in between still runs the handler it captured.

This makes setup() safely re-runnable, which is what the test suite relies on. It is also handy while tuning your config:

vim.keymap.set("n", "<leader>mr", function()
  require("markdown-plus").teardown()
  package.loaded["markdown-plus"] = nil
  require("markdown-plus").setup({ --[[ new options ]] })
end, { desc = "Reload markdown-plus" })

Reaching the Feature Modules

Each feature is exposed as a field on the root module:

require("markdown-plus").headers.generate_toc()
require("markdown-plus").footnotes.list()

Important

These handles are nil until setup() has run, and they are only assigned when the matching features.* flag is enabled. If you disable a feature, its handle stays nil.

local mp = require("markdown-plus")
if mp.headers then
  mp.headers.generate_toc()
end

require() the module directly if you want it regardless of the feature flags:

require("markdown-plus.headers").generate_toc()
Handle Module path
mp.list markdown-plus.list
mp.format markdown-plus.format
mp.thematic_break markdown-plus.thematic_break
mp.headers markdown-plus.headers
mp.links markdown-plus.links
mp.images markdown-plus.images
mp.quotes markdown-plus.quote
mp.callouts markdown-plus.callouts
mp.code_block markdown-plus.code_block
mp.table markdown-plus.table
mp.footnotes markdown-plus.footnotes

Note

The quotes handle is plural (mp.quotes) but the module path is singular (markdown-plus.quote). Easy to trip over.

Function Reference

List

Function Description
in_list_context(kind) true if the cursor is in list context for 'enter', 'backspace' or 'indent'
handle_enter() Insert-mode <CR> handler
continue_list_content() Continue content without starting a new bullet
handle_tab() / handle_shift_tab() Indent / outdent the item
handle_backspace() Insert-mode <BS> handler
handle_normal_o() / handle_normal_O() Normal-mode o / O handlers
renumber_ordered_lists() Renumber every ordered list in the buffer
debug_list_groups() Print the parsed list groups — useful for bug reports

The handlers are not predicates and do not report whether they acted. When the cursor is not in list context they emulate the native behavior for that key themselves and return nothing. If you need to branch before calling one, use in_list_context(kind).

Format

Function Description
toggle_format(format_type) Toggle formatting over the visual selection
toggle_format_word(format_type) Toggle formatting on the word under the cursor
strip_all_formatting(text) Return text with all markup removed
toggle_escape_selection() Escape / unescape Markdown punctuation in the selection
convert_to_code_block() Convert the visual selection into a fenced block
clear_formatting() / clear_formatting_word() Clear formatting on selection / word
get_lines_in_range(start_row, end_row) Fetch a line range

Headers & TOC

Function Description
promote_header() / demote_header() Add / remove a #
set_header_level(level) Set the line to a specific level (1–6)
toggle_atx_setext() Switch an H1/H2 between ATX and setext style
next_header() / prev_header() Jump between headers
generate_toc() / update_toc() Create / refresh the table of contents
follow_link() Follow the TOC link under the cursor — returns boolean
open_toc_window(window_type) Interactive TOC: 'vertical' (default), 'horizontal' or 'tab'
parse_header(line, next_line) Parse an ATX or setext heading

Code Block

Function Description
insert_with_language() Insert a fenced block, prompting for the language
wrap_selection() Wrap the visual selection in a fence
change_language() Change the language of the block under the cursor
toggle_fence_style() Switch between backtick and tilde fences
find_all_blocks() Every fenced block in the buffer
find_block_at_cursor() The block containing the cursor, or nil

find_block_at_cursor() is the one to reach for when you need to make your own mapping defer inside code blocks.

Thematic Break

Function Description
insert() Insert a break below the cursor, adding blank lines as needed
cycle_style() Rotate ---***___---

Links

Function Description
insert_link() Prompt for text and URL, insert an inline link
selection_to_link() Turn the visual selection into the link text
edit_link() Edit the link under the cursor
auto_link_url() Wrap a bare URL in <>
convert_to_reference() / convert_to_inline() Convert between link styles
find_reference_url(ref) Resolve a reference label to its URL

Smart paste lives in a submodule rather than on the links handle:

require("markdown-plus.links.smart_paste").smart_paste()

It is a no-op unless links.smart_paste.enabled is true in your config.

Images

Function Description
insert_image() Prompt for alt text and path, insert an image
selection_to_image() Turn the visual selection into the alt text
edit_image() Edit the image under the cursor
get_image_at_cursor() Return the image under the cursor, or nil
toggle_image_link() Switch between ![alt](src) and [alt](src)

Quotes & Callouts

Function Description
quote.toggle_quote() Toggle blockquote on the line or selection
callouts.insert_callout(type) Insert a callout of a given type
callouts.insert_callout_prompt() Insert a callout, prompting for the type
callouts.wrap_selection_in_callout() Wrap the selection in a callout
callouts.toggle_callout_type() Cycle the type of the callout under the cursor
callouts.convert_to_callout() / convert_to_blockquote() Convert between the two
callouts.get_callout_at_cursor() The callout under the cursor, or nil
callouts.is_valid_callout_type(type) Validate a callout type string

Tables

The largest module. Everything below operates on the table under the cursor; most are no-ops outside one.

Function Description
is_in_table() true if the cursor is inside a table
create_table(rows, cols) Create a rows × cols table — both arguments are required
format_table() / normalize_table() Realign columns / normalize structure
move_left() / move_right() / move_up() / move_down() Move between cells
edit_cell() / clear_cell() Edit / empty the current cell
wrap_cell() / unwrap_cell() Toggle soft wrapping in a cell
insert_break() Insert a line break inside a cell
insert_row_above() / insert_row_below() Insert a row
delete_row() Delete the current row
move_row_up() / move_row_down() Reorder rows
insert_column_left() / insert_column_right() Insert a column
delete_column() Delete the current column
move_column_left() / move_column_right() Reorder columns
toggle_cell_alignment() Cycle the column's alignment marker
sort_ascending() / sort_descending() Sort rows by the current column
transpose_table() Swap rows and columns
csv_to_table() / table_to_csv() Convert between CSV and Markdown tables

Footnotes

Function Description
insert() Insert a reference and, if needed, its definition
edit() Jump to the definition and enter insert mode
delete() Delete every reference plus the definition
goto_definition() / goto_reference() Jump between the two
next_footnote() / prev_footnote() Walk references in document order
list() Picker showing every footnote and its health
get_all_footnotes() Every footnote with its references and definition
get_next_id() The next unused numeric ID
get_footnote_at_cursor() The footnote under the cursor, or nil

get_all_footnotes() is what you want for a lint-style check — each entry carries its definition and reference list, so orphans and missing definitions are easy to spot.

Utils

Function Description
get_lines_in_range(start_row, end_row) Fetch a line range
get_code_block_lines(lines) Map of which lines fall inside fenced blocks
build_markdown_link(text, url, title) Build [text](url "title")
build_markdown_image(alt, url, title) Build ![alt](url "title")

Commands

Three buffer-local commands are available in Markdown buffers when the headers_toc feature is enabled:

Command Equivalent
:Toc headers.open_toc_window("vertical")
:Toch headers.open_toc_window("horizontal")
:Toct headers.open_toc_window("tab")

They are only created if a command of that name does not already exist on the buffer, so they will not clobber a command from another plugin.

See Also

  • Customizing Keymapsin_list_context recipes and plugin interop
  • Plug Mappings — the full <Plug> reference
  • :help markdown-plus-api — the same reference inside Neovim

Clone this wiki locally