Markdown improvements that don't move your text around.
Most markdown plugins make notes prettier by hiding things: URLs collapse, multiple heading symbols turn into single icons, ** markers vanish until the cursor lands on them. touchup goes the other way. Icons, backgrounds and colors are drawn on top of the text you typed, so the buffer never reflows and nothing jumps when you move the cursor.
- List bullets get icons that change with nesting depth (✸ ✿ ✦ ✧). "We have org mode at home".jpg
- Checkboxes show obsidian-style state icons inside the brackets:
[x],[ ],[!],[>]and more. - Code blocks, block quotes and HTML comment blocks get a subtle background. Quotes also render in cursive. A closed fold over one of them keeps the background: the fold line is painted with the block's own group (
line_hl_group), not left to the theme'sFolded. - Comment blocks with a type word (
<!-- pitel:thinking,<!-- pitel:error,<!-- note) get the word colored per kind; apitel:toolregion is painted as one span up to its closer. **,~~and backtick markers are dimmed, not hidden.- Link brackets, parens and URLs are dimmed so labels read like prose.
- GFI admonitions (
[!NOTE],[!WARNING], etc.) get type-colored labels. - H1 and H2 get underlines that start after the
#marker; the marker itself stays plain. - Yap role headings (
# @user,# @assistant) get distinct colors; recognizable success/error lines insideyap:toolresults are colored too (confirmations, errors and exit codes). No text is hidden. - Enter continues a list item (a checkbox item continues unchecked). Enter on an empty item exits the list.
- Conceal URLs, syntax markers or anything else you typed.
- Format tables. Alignment is a formatter's job.
- Completions, diagnostics or wiki links. This is for a LSP server.
- Support anything other than Markdown
Needs the markdown and markdown_inline treesitter parsers.
{ "noisesfromspace/touchup.nvim", opts = {} }Everything below is the default; pass only what you want to change.
require("touchup").setup({
filetypes = { "markdown" },
bullets = { enabled = true, icons = { "✸", "✿", "✦", "✧" } },
checkboxes = {
enabled = true,
-- Obsidian-style checkbox states. Standard `[ ]`/`[x]` are handled via
-- treesitter; every other key is matched by a regex fallback against
-- `- [<char>]` / `* [<char>]` / `+ [<char>]`.
icons = {
["x"] = { text = "", hl = "TouchupCheckboxChecked" },
["X"] = { text = "", hl = "TouchupCheckboxChecked" },
["/"] = { text = "", hl = "TouchupCheckboxPending" },
[">"] = { text = "", hl = "TouchupCheckboxCancelled" },
["<"] = { text = "", hl = "TouchupCheckboxCancelled" },
["-"] = { text = "", hl = "TouchupCheckboxCancelled" },
["?"] = { text = "", hl = "TouchupCheckboxPending" },
["!"] = { text = "", hl = "TouchupCheckboxImportant" },
["*"] = { text = "", hl = "TouchupCheckboxPending" },
['"'] = { text = "", hl = "TouchupCheckboxCancelled" },
["l"] = { text = "", hl = "TouchupCheckboxProgress" },
["b"] = { text = "", hl = "TouchupCheckboxProgress" },
["i"] = { text = "", hl = "TouchupCheckboxChecked" },
["S"] = { text = "", hl = "TouchupCheckboxChecked" },
["I"] = { text = "", hl = "TouchupCheckboxPending" },
["p"] = { text = "", hl = "TouchupCheckboxChecked" },
["c"] = { text = "", hl = "TouchupCheckboxUnchecked" },
["f"] = { text = "", hl = "TouchupCheckboxUnchecked" },
["k"] = { text = "", hl = "TouchupCheckboxPending" },
["w"] = { text = "", hl = "TouchupCheckboxProgress" },
["u"] = { text = "", hl = "TouchupCheckboxChecked" },
["d"] = { text = "", hl = "TouchupCheckboxUnchecked" },
},
},
code_blocks = { enabled = true },
comment_blocks = { enabled = true },
yap = { enabled = true }, -- role headings and tool result status lines
markers = { enabled = true },
quotes = { enabled = true },
enter = { enabled = true },
})checkboxes.icons is merged per key: pass only the states you want to change or
add and the rest keep their default icon.
require("touchup").setup({
checkboxes = {
icons = {
["x"] = { text = "✔", hl = "MyChecked" }, -- restyle an existing state
["z"] = { text = "💤", hl = "MyCustom" }, -- add a brand-new one: [z]
},
},
})Unchecked ([ ]) always renders as a plain space and isn't customizable.
All highlight groups are defined with default = true, so your colorscheme wins. Override any Touchup* group if you want different colors. The block backgrounds (TouchupCommentBlock, TouchupCodeBlock, TouchupQuote) copy CursorLine's attributes rather than linking to it, because a link is resolved in the window's highlight namespace and a plugin that remaps CursorLine for one window would blank them there; they re-copy on ColorScheme. A Touchup* group you define yourself is never overwritten.
:TouchupDisable stops rendering and removes touchup's buffer-local Enter/indent
mappings and autocmds. Run require("touchup").setup() to enable it again
(including already-open Markdown buffers).
touchup only decorates. This is the setup it is built to sit next to:
| Tool | Does |
|---|---|
| markdown-oxide | LSP: completions, diagnostics, symbol navigation |
| mdformat | Formats markdown, including tables |
| conform.nvim | Runs mdformat on save |
