color_my_ascii.nvim · View & render · vimdoc
:help color_my_ascii
Highlight ASCII art in markdown code blocks
doc/color_my_ascii.txt — rendered from the plugin's own vimdoc
*color_my_ascii.txt* Highlight ASCII art in markdown code blocks *color_my_ascii.nvim* COLOR MY ASCII~ ASCII Art Highlighter for Neovim Version: 1.1.0
CONTENTS
1. Introduction ........................... |color_my_ascii-intro| 2. Requirements ........................... |color_my_ascii-requirements| 3. Installation ........................... |color_my_ascii-installation| 4. Quick Start ............................ |color_my_ascii-quickstart| 5. Configuration .......................... |color_my_ascii-config| 6. Features ............................... |color_my_ascii-features| 7. Treesitter Integration ................. |color_my_ascii-treesitter| 8. Commands ................................ |color_my_ascii-commands| 9. Keybindings ............................. |color_my_ascii-keybindings| 10. Color Schemes ......................... |color_my_ascii-schemes| 11. API .................................... |color_my_ascii-api| 12. Troubleshooting ....................... |color_my_ascii-troubleshooting| 13. Development ........................... |color_my_ascii-development|
1. INTRODUCTION
color_my_ascii.nvim is a Neovim plugin that provides syntax highlighting for ASCII art in markdown code blocks. It supports automatic language detection, custom color schemes, optional treesitter integration, and various highlighting features.
Features:
• Highlight box-drawing characters, blocks, arrows, and symbols
• Language-specific keyword highlighting (31 languages: C, C++, C#, Lua,
Go, Rust, TypeScript, JavaScript, Python, Bash, Zig, LLVM IR, Vimscript,
Java, PHP, Ruby, Kotlin, Swift, Scala, Dart, Elixir, Haskell, Perl, R,
Clojure, Groovy, PowerShell, SQL, JSON, HTML, CSS)
• Automatic language detection using heuristics
• Standard markdown fence tags via fence_language_map (e.g., ```vim)
• Custom color schemes with RGB/hex colors
• Function name detection
• Bracket highlighting
• Inline code highlighting
• Fence validation
• Scheme switcher with live preview
• Code block formatting
• Optional treesitter-based block detection and real syntax highlighting
• Optional, disableable default keymaps with which-key support
2. REQUIREMENTS
• Neovim >= 0.10
• lib.nvim (github.com/StefanBartl/lib.nvim) - required, not optional:
the commands, keymaps, autocommands and notifications are built on it,
and every module requires it at the top level
• Markdown files (filetype=markdown)
• Optional: telescope.nvim for the scheme picker
• Optional: nvim-treesitter with the "markdown" parser (and a parser for
the relevant language) for treesitter integration
3. INSTALLATION
The plugin is loaded via ft = 'markdown', i.e. only once a markdown buffer is
opened. This is the recommended trigger - more precise than a blanket
event = "VeryLazy", since there is nothing to do until a markdown file is
actually being edited.
Using lazy.nvim:
{
'StefanBartl/color_my_ascii.nvim',
ft = 'markdown',
dependencies = { 'StefanBartl/lib.nvim' }, -- required
opts = {}
}
Using packer.nvim:
use {
'StefanBartl/color_my_ascii.nvim',
ft = 'markdown',
requires = { 'StefanBartl/lib.nvim' }, -- required
config = function()
require('color_my_ascii').setup()
end
}
4. QUICK START
After installation, the plugin activates automatically for markdown files.
Basic usage:
```ascii
┌─────────────┐
│ Hello World │
└─────────────┘
```
<
With explicit language:
```ascii-c
┌──────────────┐
│ int x = 42; │
└──────────────┘
```
<
With a standard fence tag (via fence_language_map):
```vim
nnoremap <leader>w :w<CR>
augroup MyGroup
autocmd!
augroup END
```
<
See |color_my_ascii-config| for configuration options.
5. CONFIGURATION
Setup with defaults:
require('color_my_ascii').setup()
Full default configuration:
require('color_my_ascii').setup({
debug_enabled = false,
debug_verbose = false,
scheme = 'default',
-- Character-specific overrides (highest priority)
overrides = {},
-- Your own language(s), merged into the built-in set.
-- See |color_my_ascii-config-languages|.
languages = {},
-- Default highlight for unmatched characters
default_hl = 'Normal',
-- Optional: default highlight for normal text in blocks
default_text_hl = nil, -- e.g. 'Comment' for dimmed display
-- Feature toggles
enable_keywords = true,
enable_language_detection = true,
language_detection_threshold = 2,
enable_function_names = true,
enable_bracket_highlighting = true,
treat_empty_fence_as_ascii = true,
enable_inline_code = true,
-- Map standard markdown fence tags to plugin language names
fence_language_map = {
vim = 'vim',
vimscript = 'vim',
viml = 'vim',
},
-- Optional default keymaps (false = disabled). See |color_my_ascii-keybindings|
keymaps = false,
-- Optional overrides for cache_manager/debounce_manager internals.
-- nil = use the plugin's built-in defaults.
cache = nil,
debounce = nil,
-- Optional treesitter integration, on by default, see |color_my_ascii-treesitter|
treesitter = {
enabled = true,
block_detection = true,
syntax_highlight = true,
},
})
Configuration options:
*color_my_ascii-config-overrides* overrides Character-specific highlight overrides Type: table<string, string|table> *color_my_ascii-config-languages* languages Extension point for adding your own language(s) from setup(), without a languages/*.lua file in the plugin itself. Same entry shape as the built-in language files. Type: table<string, table> Default: {} Each entry:~ words string[] words highlighted withhlwhen this language is active unique_words string[]|nil optional subset unique enough to drive heuristic language detection hl string|table highlight-group name or an attribute table Merged into the built-in language set at setup() time. An entry whose name matches a built-in language (e.g. "lua") replaces that language's entry wholesale (words/unique_words/hl together) - not a field-by-field merge. A malformed entry (missingwordsorhl) is skipped with a warning; the rest still load. Calling setup() again (e.g. after editing alanguagesentry) re-highlights every already- open, plugin-managed buffer immediately - no need to touch the buffer or restart Neovim. To also recognize a markdown fence tag as your language, add it to |color_my_ascii-config-fence_language_map| too. Example:~
require('color_my_ascii').setup({
languages = {
mylang = {
words = { 'foo', 'bar', 'baz' },
unique_words = { 'foo' },
hl = 'Function',
},
},
fence_language_map = {
mylang = 'mylang',
},
})
*color_my_ascii-config-default_hl*
default_hl Default highlight for unmatched characters
Type: string or table
Default: 'Normal'
*color_my_ascii-config-default_text_hl*
default_text_hl Highlight for normal text in blocks
Type: string, table, or nil
Default: nil (no change)
*color_my_ascii-config-enable_keywords*
enable_keywords Enable keyword highlighting
Type: boolean
Default: true
*color_my_ascii-config-enable_language_detection*
enable_language_detection
Enable automatic language detection
Type: boolean
Default: true
*color_my_ascii-config-language_detection_threshold*
language_detection_threshold
Minimum unique keyword matches for detection
Type: integer
Default: 2
*color_my_ascii-config-enable_function_names*
enable_function_names Enable heuristic function name detection
Type: boolean
Default: true
*color_my_ascii-config-enable_bracket_highlighting*
enable_bracket_highlighting
Highlight brackets/parentheses
Type: boolean
Default: true
*color_my_ascii-config-treat_empty_fence_as_ascii*
treat_empty_fence_as_ascii
Treat ``` without language as ASCII
Type: boolean
Default: true
*color_my_ascii-config-enable_inline_code*
enable_inline_code Highlight inline code (...)
Type: boolean
Default: true
*color_my_ascii-config-debug_enabled*
debug_enabled Enable debug mode (verbose logging)
Type: boolean
Default: false
*color_my_ascii-config-fence_language_map*
fence_language_map Map of standard markdown fence language identifiers
to plugin language names. Fences whose tag appears
in this map are treated as ASCII blocks and
highlighted with the corresponding language - not
just ```ascii-prefixed ones.
Type: table<string, string>
Default: covers every language in
|color_my_ascii-language-detection| under its
common tag(s) (e.g. go, js/javascript, py/python,
rb/ruby, cs/csharp, ...). See
lua/color_my_ascii/config/DEFAULTS.lua for the
full list.
Example (adding a custom tag on top of the
defaults):~
require('color_my_ascii').setup({
fence_language_map = {
myasciitag = 'python',
},
})
*color_my_ascii-config-fence_line_highlight* fence_line_highlight Full-line highlight of fenced-block delimiter lines (the ``lang opening line and its closing`` line), as a visual boundary. On by default. Type: table Fields: enable boolean (default true) preset string (default "auto") one of: "auto" | "subtle" | "accent" | "underline" | "bar" | <theme-name> open string|table|nil per-delimiter override close string|table|nil per-delimiter override apply_to "all" | "ascii" (default "all") respect_indent boolean (default true) start the highlight at an indented block's own indent column (the opening fence's first backtick) instead of column 0. false paints the whole screen line from column 0 right_pad integer (default 1, clamp 0-20) screen columns held off the window's right edge when respect_indent is on; needs the buffer visible in a window, recomputed on resize preset = "auto" matches vim.g.colors_name against a set of bundled theme palettes (catppuccin, tokyonight, gruvbox, gruvbox-material, nord, onedark, dracula, kanagawa, rose-pine, everforest, nightfox, material, sonokai, monokai, solarized, github, oxocarbon), re-matching on :colorscheme. It falls back to "subtle" on a light background or an unknown theme. You can also name a theme directly. The generic looks link to theme-adaptive built-ins: subtle -> CursorLine, accent -> Visual, underline, bar -> ColorColumn. open/close accept an existing highlight-group name (string, linked) or an attribute table forwarded to nvim_set_hl. Example:~
require('color_my_ascii').setup({
fence_line_highlight = {
enable = true,
preset = 'auto',
apply_to = 'all',
},
})
*color_my_ascii-config-fence_content_highlight* fence_content_highlight Full-width background highlight of a fenced block's interior (every line between the delimiters), painted via the same line_hl_group mechanism as |color_my_ascii-config-fence_line_highlight| - covers trailing whitespace and blank lines too, not just characters. On by default (opt-out). Type: table Fields: enable boolean (default true) preset string|nil (default nil) base look to shade from; nil follows fence_line_highlight.preset. Same values as that field's preset. hl string|table|nil explicit override (hl-group name or attr table); bypasses shading entirely shade "auto"|"darken"|"lighten"|"none" (default "auto") shade direction relative to the resolved preset color; "auto" darkens on dark backgrounds and lightens on light ones; "none" uses the resolved color unshaded amount integer 0-100 (default 6) blend strength toward black/white apply_to "all" | "ascii" (default "all") respect_indent boolean (default true) as fence_line_highlight.respect_indent, for the interior rows right_pad integer (default 1) as fence_line_highlight.right_pad, for the interior rows The interior color is derived by resolving the same preset/theme lookup as fence_line_highlight (see above) to a "#rrggbb" background, then blending it toward black or white byamountpercent - so the block interior reads as a related-but-distinguishable tint of its own delimiter lines, without hand-tuning a second palette per theme.hlskips all of that and sets an explicit look instead. Example:~
require('color_my_ascii').setup({
fence_content_highlight = {
enable = true,
shade = 'auto',
amount = 6,
},
})
*color_my_ascii-config-fence_export*
fence_export Behaviour of the |:Fence| export command.
Type: table
Fields:
default_dir "buffer" | "cwd" (default "buffer")
open_after boolean (default false)
open_cmd string ("edit"|"split"|"vsplit"|
"tabedit", default "vsplit")
replace boolean (default false)
replace_format string string.format template;
args are (filename, relpath)
(default "[%s](%s)")
ext_map table<string,string> language-tag
-> extension overrides
*color_my_ascii-config-fence_run*
fence_run Interpreters for |:Fence| run.
Type: table
Fields:
runners table<string, string|string[]>
language tag -> command (temp file with
the block content is appended). Merged on
top of the built-ins (python3, node, lua,
bash, ruby, "go run", Rscript, ...).
*color_my_ascii-config-fence_format*
fence_format Formatters for |:Fence| format.
Type: table
Fields:
formatters table<string, string[]>
language tag -> stdin/stdout formatter
command. Merged on top of the built-ins
(stylua, black, prettier, gofmt, shfmt,
rustfmt).
*color_my_ascii-config-keymaps*
keymaps Optional default keymaps (action name -> key
sequence). false disables all keymaps.
Type: false | table<string, string>
Default: false
See |color_my_ascii-keybindings|
*color_my_ascii-config-cache*
cache Optional override for cache_manager defaults
({ timeout, max_size, enable_stats }).
Type: table or nil
Default: nil (use built-in defaults)
*color_my_ascii-config-debounce*
debounce Optional override for debounce_manager defaults
({ small_file_threshold, medium_file_threshold,
small_delay, medium_delay, large_delay }).
Type: table or nil
Default: nil (use built-in defaults)
*color_my_ascii-config-treesitter*
treesitter Optional treesitter-based block detection and
syntax highlighting.
Type: table { enabled, block_detection, syntax_highlight }
Default: { enabled = true, block_detection = true,
syntax_highlight = true }
See |color_my_ascii-treesitter|
*color_my_ascii-config-comment_ascii*
comment_ascii Optional detection/highlighting of explicitly-
marked ASCII blocks inside code comments, outside
markdown. Off by default - unlike most other
features, enabling this activates the plugin on
non-markdown filetypes.
Type: table { enable, filetypes }
Fields:
enable boolean (default false)
filetypes string[] filetypes to activate on
when enabled (default: a broad
built-in list, see
lua/color_my_ascii/config/DEFAULTS.lua)
Marker: a comment line whose trimmed, comment-
prefix-stripped text is "ascii" (or "ascii-lang",
same tag syntax as a markdown fence tag), up to a
matching "/ascii" line - the buffer's own line-
comment prefix (vim.bo.commentstring) instead of
backticks:
local function foo()
-- ascii
-- ┌────┐
-- │ hi │
-- └────┘
-- /ascii
return 1
end
Highlighting only - the |:Fence| toolkit and fence-line/fence-content background highlighting remain markdown-only. Only single-line comment syntax is supported (the commentstring prefix before "%s"), not block comments. Algorithm in lua/color_my_ascii/comment_ascii.lua. Example:~
require('color_my_ascii').setup({
comment_ascii = {
enable = true,
filetypes = { 'lua', 'python' },
},
})
Custom highlight specification:
Highlights can be specified as strings (built-in highlight groups) or tables with custom colors:
{
fg = '#ff0000', -- Hex color
bg = '#000000', -- Background
bold = true, -- Bold text
italic = true, -- Italic text
underline = true, -- Underline
undercurl = true, -- Undercurl
strikethrough = true, -- Strikethrough
}
6. FEATURES
Character Groups
The plugin highlights different types of characters:
• Box drawing: ┌─┐│└┘├┤┬┴┼╔═╗║╚╝╠╣╦╩╬
• Blocks: █▓▒░▀▄▌▐■□▪▫
• Arrows: ←→↑↓⇐⇒⇑⇓↔⇔➔➡
• Symbols: •★☆✓✔✗✘♠♣♥♦
• Operators: +-*/%=<>!&|^~()[]{}
Language Detection~ *color_my_ascii-language-detection*
The plugin can detect programming languages using four strategies:
1. Explicit marker: ``ascii-c, `ascii lua, ``ascii:python
2. fence_language_map: Standard fence tag mapped in config (e.g., ```vim)
3. Heuristic analysis: Counts unique keywords per language
4. Buffer context: Uses buffer filetype as fallback
Supported languages:
C, C++, C#, Lua, Go, Rust, TypeScript, JavaScript, Python, Bash, Zig, LLVM IR, Vimscript, Java, PHP, Ruby, Kotlin, Swift, Scala, Dart, Elixir, Haskell, Perl, R, Clojure, Groovy, PowerShell, SQL, JSON, HTML, CSS (31 languages total) See |color_my_ascii-config-fence_language_map| for mapping additional fence tags. See |color_my_ascii-config-enable_language_detection| to enable/disable. Function Name Detection~ *color_my_ascii-function-detection* When enabled, highlights function names using heuristics:
result = calculate(x); // "calculate" highlighted
See |color_my_ascii-config-enable_function_names|. Bracket Highlighting~ *color_my_ascii-bracket-highlighting* When enabled, highlights parentheses, square brackets, and curly braces:
if (x > 0) { } // (), {} highlighted
See |color_my_ascii-config-enable_bracket_highlighting|. Inline Code Highlighting~ *color_my_ascii-inline-code* When enabled, highlights keywords and symbols in inline code:
Use `func` for functions and `→` for arrows.
See |color_my_ascii-config-enable_inline_code|. Empty Fence Support~ *color_my_ascii-empty-fence* When enabled, treats empty fences (``` without language) as ASCII blocks:
```
┌────┐
│ OK │
└────┘
```
<
See |color_my_ascii-config-treat_empty_fence_as_ascii|.
Comment ASCII Blocks~ *color_my_ascii-comment-ascii*
Opt-in (config.comment_ascii): explicitly-marked ASCII blocks inside code
comments, outside markdown:
-- ascii
-- +--+
-- |ok|
-- +--+
-- /ascii
See |color_my_ascii-config-comment_ascii|.
7. TREESITTER INTEGRATION
On by default. Both sub-features fall back silently to heuristic-only behavior when the relevant parser isn't installed, so there's no downside to leaving this enabled even without treesitter set up at all. Set treesitter.enabled = false to fully disable and behave exactly as without treesitter. Two independently toggleable sub-features: *color_my_ascii-treesitter-block_detection* block_detection Use Neovim's markdown treesitter grammar to find fenced code blocks instead of the built-in line scanner. More robust for edge cases (nested fences, unusual indentation). Requires a "markdown" parser (:TSInstall markdown); falls back silently to the heuristic scanner if unavailable or if detection errors. *color_my_ascii-treesitter-syntax_highlight* syntax_highlight Additionally highlights a block's content using the real grammar of its detected language (e.g. real Lua/Python/C syntax via @-prefixed highlight groups), on top of the existing character/keyword highlighting. Best-effort: ASCII art is usually not valid syntax, so this only has a visible effect on blocks (or portions of blocks) that happen to contain real, parseable code. Requires a parser for that language (:TSInstall <language>); silently does nothing where unavailable or unparseable.
Example (disabling):
-- Disable entirely
require('color_my_ascii').setup({
treesitter = { enabled = false },
})
-- Or keep block detection but skip the syntax highlighting pass
require('color_my_ascii').setup({
treesitter = { syntax_highlight = false },
})
:checkhealth color_my_ascii reports whether the required parsers are
installed.
8. COMMANDS
One command, :ColorMyAscii <subcommand> (built via lib.nvim.bindings.usercmd.composer, with <Tab> completion) — distinct from the separate buffer-local |:Fence| toolkit below.
Core Commands
*:ColorMyAscii* Manually trigger highlighting for the current buffer (bare form). :ColorMyAscii toggle *:ColorMyAscii-toggle* Toggle the plugin on/off. :ColorMyAscii debug *:ColorMyAscii-debug* Show basic debug information (languages, groups, features). :ColorMyAscii show-config *:ColorMyAscii-show-config* Show detailed configuration information including: • All feature flags • Loaded languages and groups • Lookup table statistics • Highlight configuration
Debug Commands (only when config.debug_enabled is true)
:ColorMyAscii inspect char {char} *:ColorMyAscii-inspect-char*
Show which groups and highlights {char} belongs to.
:ColorMyAscii inspect group {group} *:ColorMyAscii-inspect-group*
List every character in {group} (tab-completed from config.groups).
:ColorMyAscii inspect inline *:ColorMyAscii-inspect-inline*
Inspect inline code segments on the current line.
:ColorMyAscii inspect highlight {hl} *:ColorMyAscii-inspect-highlight*
Show every group using highlight {hl}.
:ColorMyAscii stats *:ColorMyAscii-stats*
Show comprehensive plugin statistics (groups, languages, lookups).
Fence Management
:ColorMyAscii check-fences *:ColorMyAscii-check-fences* Check current buffer for unmatched fenced code blocks. Reports opening fences without closing, and vice versa. :ColorMyAscii ensure-blank-lines *:ColorMyAscii-ensure-blank-lines* Ensure blank lines before and after all fenced code blocks. Adds missing blank lines automatically. Reports number of changes made. :ColorMyAscii fence-jump *:ColorMyAscii-fence-jump* Jump from a fence delimiter line (the ```lang opening line or its closing ``) to the matching one - the same mental model%` already applies to ()/{}/[] pairs, extended to fenced blocks. Falls back to the built-in%(bracket-pair matching) when the cursor isn't on a delimiter. Meant to be bound to%itself via thefence_jumpkeymap action - see |color_my_ascii-keybindings|. :ColorMyAscii hover *:ColorMyAscii-hover* Show a float with the applied highlight/group/keyword info for the character under the cursor; also copies the same text to the unnamed register (+ system clipboard where available). Combines the live answer (the actualhl_groupcolor_my_ascii's own extmarks painted at this exact position right now, with resolved fg/bg) and the config answer (which character groups it belongs to per config, and whether it's an override - independent of whether anything is painted right now). If the cursor sits on a recognized keyword, also shows which language(s) it maps to. Uses ui.nvim'sui.kitnotepopup when available, else a plain floating window (q/<Esc>/<C-c> to close). *:Fence* *color_my_ascii-fence-command* Buffer-local (markdown) dispatcher for actions on the fenced block under the cursor. Argument completion suggests subcommands, flags, file paths and language tags. :Fence export [path] [--open] [--replace] [--html] Extract the block's content into a standalone file. - path: quoted or bare ("src/a.js", 'a b.py', a.lua). Omitted -> a prompt with a suggested filename (extension derived from the fence language, or "html" with --html) and file-path completion. - --open: open the exported file afterwards (config open_cmd). - --replace: replace the fenced block with a link reference to the new file (literate-tangle style; config replace_format). - --html: export the block's applied color_my_ascii highlighting instead of plain text - a standalone HTML document with<span class="cma-<Group>">runs and a stylesheet covering only the groups the block actually uses. See |color_my_ascii-highlight-export|. See |color_my_ascii-config-fence_export|. :Fence yank [register] [--ansi] Copy the block content (without delimiters) to a register. Default: unnamed " and system + . - --ansi: copy the block's applied color_my_ascii highlighting instead of plain text, as 24-bit truecolor ANSI escape codes - paste-ready for a terminal or a chat that renders ANSI. See |color_my_ascii-highlight-export|. :Fence open [--split|--vsplit|--tab|--edit] Edit the block in a real split buffer with the correct filetype (so the language server and formatters attach), writing changes back into the fence on :w. The interior is anchored with extmarks; the temp file is removed when its buffer is wiped. :Fence run Run the block with its language's interpreter and show stdout/stderr in a scratch split. Interpreters via |color_my_ascii-config-fence_run|. :Fence format Format the block in place with the language's stdin/stdout formatter. Formatters via |color_my_ascii-config-fence_format|. :Fence import <file> Replace the block's content with the content of <file> (inverse of export). :Fence lang <language> Change the language tag of the opening fence. :Fence select Visually (linewise) select the block interior. :[range]Fence wrap [language] Wrap the current line or the [range] (e.g. :'<,'>) in a fenced block. :Fence unwrap Remove the fence delimiters around the block under the cursor. :Fence align Straighten simple box-drawing boxes (corner+horizontal+corner top and bottom, vertical-edge interior rows - light, heavy, rounded, or ASCII +-| style) whose right edge has drifted after hand-editing. Each box is widened to its own widest row, so content is only ever padded, never cut off - a normal buffer edit,uundoes it like any other change. Narrow scope: only genuine 4-sided boxes; directory- tree connectors (e.g. tree-branch glyphs) and anything that isn't a clean rectangle are left untouched. Algorithm in lua/color_my_ascii/box_align.lua. *color_my_ascii-highlight-export*
Highlight export (`:Fence export --html` / `:Fence yank --ansi`)
lua/color_my_ascii/highlight_export.luare-reads a block's applied color_my_ascii highlighting (the extmarks it already painted, following any links to their effective colors) and reconstructs the same colors as HTML or ANSI, so a colored ASCII block survives being copied out of the buffer - into a chat, a terminal, or a web page. - HTML:M.to_html(bufnr, block)returns a standalone HTML document - a<pre>block of<span class="cma-<Group>">runs plus a<style>block with a rule only for each group the block actually uses (not the whole plugin palette). - ANSI:M.to_ansi(bufnr, block)returns 24-bit truecolor escape codes (\x1b[38;2;r;g;bm/\x1b[48;2;r;g;bm, plus bold/italic/underline), reset after every colored run. Unhighlighted text (no color_my_ascii extmark on it) passes through unstyled in both formats. Available as a public Lua API for other consumers too, not just the two:Fenceflags above. Scheme Management~ *color_my_ascii-scheme-commands* :ColorMyAscii schemes list *:ColorMyAscii-schemes-list* List all available color schemes with their enabled features. Output shows scheme names and feature flags. :ColorMyAscii schemes switch {name} *:ColorMyAscii-schemes-switch* Switch to a different color scheme. Tab completion available for scheme names. Automatically re-highlights all active buffers.
Example:
:ColorMyAscii schemes switch matrix
:ColorMyAscii schemes pick *:ColorMyAscii-schemes-pick* Open Telescope picker for interactive scheme selection. Features live preview - scheme applies as you navigate. Press <CR> to confirm selection.
Requires:
• telescope.nvim installed
Keybindings in picker:
• j/k or ↓/↑ - Navigate schemes (live preview)
• <CR> - Apply selected scheme
• <Esc> - Cancel and keep current scheme
See |color_my_ascii-schemes| for the full list of available schemes.
9. KEYBINDINGS
All keymaps are opt-in and disabled by default. Enable and customize them via
the keymaps option in setup() - see |color_my_ascii-config-keymaps|.
Available actions:
require('color_my_ascii').setup({
keymaps = {
highlight = '<leader>ah', -- :ColorMyAscii
toggle = '<leader>at', -- :ColorMyAscii toggle
schemes = '<leader>as', -- :ColorMyAscii schemes pick
ensure_blank_lines = '<leader>af', -- :ColorMyAscii ensure-blank-lines
show_config = '<leader>ac', -- :ColorMyAscii show-config
debug = '<leader>ad', -- :ColorMyAscii debug
check_fences = '<leader>ax', -- :ColorMyAscii check-fences
fence_jump = '%', -- :ColorMyAscii fence-jump
hover = '<leader>ai', -- :ColorMyAscii hover
fence_yank = '<leader>fy', -- :Fence yank
fence_open = '<leader>fo', -- :Fence open
fence_run = '<leader>fr', -- :Fence run
fence_format = '<leader>fi', -- :Fence format
fence_select = '<leader>fv', -- :Fence select
fence_wrap = '<leader>fw', -- :Fence wrap
fence_unwrap = '<leader>fu', -- :Fence unwrap
fence_align = '<leader>fg', -- :Fence align
},
})
Thefence_*actions bind the argument-less |:Fence| sub-commands; likecheck_fences/fence_jumpthey only do anything in a markdown buffer, since |:Fence| itself is registered buffer-local there.fence_jumpis the odd one out: unlike the other actions it's meant to be bound to%itself. On a fence delimiter line (the ```lang opening line or its closing ```) it jumps to the matching delimiter, the same mental model%already applies to ()/{}/[] pairs - elsewhere on the line it falls back to Neovim's built-in%(bracket-pair matching), so binding it to%only adds behavior, it doesn't take anything away. Each mapping is set with adesc, so which-key.nvim picks them up automatically without extra configuration. lib.nvim (github.com/StefanBartl/lib.nvim) is a required dependency (the |:ColorMyAscii| command itself is built on it); lib.nvim.bindings.keymap specifically stays soft-guarded for keymap registration, falling back to vim.keymap.set. See the plugin's docs/BINDINGS.md for the full cheatsheet of user commands, keymap actions, and autocommands.
10. COLOR SCHEMES
The plugin includes 10 pre-configured color schemes.
Loading a color scheme:
require('color_my_ascii').setup(
require('color_my_ascii.schemes.matrix')
)
Available schemes:
*color_my_ascii-scheme-default* default Uses built-in Neovim highlights Maximum compatibility *color_my_ascii-scheme-matrix* matrix Dark with bright green (hacker style) Features: all enabled *color_my_ascii-scheme-nord* nord Cool blue/cyan colors Features: keywords, detection, functions Special: highlighted corners *color_my_ascii-scheme-gruvbox* gruvbox Warm, retro colors Features: keywords, detection, brackets, inline *color_my_ascii-scheme-dracula* dracula Vibrant purple and pink Features: all enabled *color_my_ascii-scheme-catppuccin* catppuccin Soft pastel colors (Catppuccin Mocha) Features: keywords, detection, functions, inline *color_my_ascii-scheme-onedark* onedark Atom-inspired dark theme, vibrant syntax colors Features: keywords, detection, functions, brackets, inline *color_my_ascii-scheme-solarized* solarized Precision colors for readability (Solarized Dark) Features: keywords, detection *color_my_ascii-scheme-tokyonight* tokyonight Deep blue night colors with vibrant accents Features: keywords, detection, functions, brackets, inline *color_my_ascii-scheme-monokai* monokai High contrast dark theme, vibrant neon colors Features: keywords, detection, functions, brackets, inline
Switching schemes:
Via command:
:ColorMyAscii schemes switch nord
Via Telescope (with live preview):
:ColorMyAscii schemes pick
Via API:
local scheme = require('color_my_ascii.schemes.nord')
require('color_my_ascii').setup(scheme)
Creating custom schemes:
require('color_my_ascii').setup({
groups = {
box_drawing = {
chars = "─│┌┐└┘",
hl = { fg = '#00ff00', bold = true },
},
},
overrides = {
['┌'] = { fg = '#ff0000' },
},
})
See docs/schemes.md for a detailed guide.
11. API
*color_my_ascii.setup()* Setup the plugin with configuration options.
Parameters:
{opts} (table|nil) Configuration options, see |color_my_ascii-config|
Usage:
require('color_my_ascii').setup({
enable_keywords = true,
})
*color_my_ascii.setup_buffer()* Setup highlighting for a specific buffer.
Parameters:
{bufnr} (integer) Buffer number
Usage:
require('color_my_ascii').setup_buffer(vim.api.nvim_get_current_buf())
*color_my_ascii.highlight_buffer()* Manually trigger highlighting for a buffer.
Parameters:
{bufnr} (integer|nil) Buffer number (default: current buffer)
Usage:
require('color_my_ascii').highlight_buffer()
*color_my_ascii.toggle()* Toggle the plugin on/off.
Returns:
boolean New state (true = enabled, false = disabled)
Usage:
local enabled = require('color_my_ascii').toggle()
print('Plugin enabled:', enabled)
*color_my_ascii.get_state()* Get the current plugin state.
Returns:
table State containing enabled status and active buffers *color_my_ascii.get_cache_stats()* Get cache statistics (hits, misses, size, etc.).
Returns:
table Cache statistics *color_my_ascii.get_cache_hit_rate()* Get the cache hit rate.
Returns:
number Hit rate as a percentage (0-100) *color_my_ascii.clear_caches()* Clear all cached parse results for all buffers.
Returns:
integer Number of cleared entries *color_my_ascii.get_debounce_config()* Get the current debounce configuration.
Returns:
table Debounce configuration *color_my_ascii.configure_cache()* Reconfigure cache behavior at runtime.
Parameters:
{opts} (table) Cache configuration ({ timeout, max_size, enable_stats })
Usage:
require('color_my_ascii').configure_cache({ timeout = 10000 })
*color_my_ascii.configure_debounce()* Reconfigure debounce behavior at runtime.
Parameters:
{opts} (table) Debounce configuration
Usage:
require('color_my_ascii').configure_debounce({ small_delay = 50 })
12. TROUBLESHOOTING
No highlighting visible
1. Check if plugin is loaded:
:ColorMyAscii debug
2. Verify buffer is markdown:
:set filetype?
3. Check for errors:
:messages
4. Run health check:
:checkhealth color_my_ascii
Incorrect language detection
Use explicit language marker:
```ascii-c
int x = 42;
```
<
Use a standard fence tag (if listed in fence_language_map):
```vim
nnoremap <leader>w :w<CR>
```
<
Or adjust detection threshold:
require('color_my_ascii').setup({
language_detection_threshold = 3, -- More strict
})
Or extend the fence_language_map:
require('color_my_ascii').setup({
fence_language_map = {
vim = 'vim',
sh = 'bash',
},
})
Characters shift or duplicate while editing
A line renders correctly, then garbles as soon as the cursor visits it: a character doubled, the next one swallowed, the cursor a cell beside the glyph it should be on, or a one-cell gap in the fence background at a line's end. E.g. "WARNING oil" rendering as "WWARNINGoil". This is not the highlighting. Neovim and the terminal disagree on how many cells a character occupies; the plugin only makes it visible, because a line with many highlight spans gets repainted in fragments and a fragment written at an absolute column lands one cell off. Check the suspect character:
:echo strdisplaywidth("\u26A0\uFE0F")
Compare that with the number of cells the terminal actually paints. If they differ, that is the cause - it reproduces innvim --cleanwith the plugin off. The usual culprits are East Asian "Ambiguous" codepoints, which this plugin's subject matter is full of: box drawing, arrows, and symbols carrying the emoji variation selector U+FE0F. Their width depends on which table each side uses -'ambiwidth'and'emoji'on the Neovim side, the terminal's own setting on the other (WezTerm:unicode_version, default 9, where U+FE0F widens nothing). Fix by bringing both onto the same table - exactly one of them, since changing both moves the mismatch to the other side:
vim.o.emoji = false -- Neovim follows the terminal
-- vim.o.ambiwidth = "double" -- if the terminal draws box drawing wide
or setunicode_version = 14in the terminal so it follows Neovim. Note that:Fence aligncounts UTF-8 codepoints rather than display cells, so a box holding double-width characters can still look crooked for the same reason.
Performance issues
Disable features you don't need:
require('color_my_ascii').setup({
enable_function_names = false,
enable_inline_code = false,
})
Or tune the cache/debounce settings, see |color_my_ascii-config-cache| and |color_my_ascii-config-debounce|.
Keywords not highlighting
Check if language is loaded:
:ColorMyAscii debug
Verify keywords are enabled:
require('color_my_ascii').setup({
enable_keywords = true,
})
Unmatched fences
Check for mismatched code blocks:
:ColorMyAscii check-fences
The command will report: • Opening fences without closing • Closing fences without opening • Line numbers for each issue
Colors not displaying
1. Enable true colors:
vim.opt.termguicolors = true
2. Check terminal support: Ensure your terminal supports 24-bit color
Wrong colors after colorscheme change
color_my_ascii's own dynamically created (fixed-hex) highlight groups are automatically re-applied on theColorSchemeautocommand event, so a plain:colorscheme <name>switch no longer requires a manual reload. If colors still look wrong afterwards, force a manual refresh:
:ColorMyAscii
Or use built-in highlight groups for automatic adaptation:
require('color_my_ascii').setup({
groups = {
box_drawing = { chars = "...", hl = 'Keyword' },
}
})
Telescope scheme picker not working
1. Check if Telescope is installed:
:echo luaeval('pcall(require, "telescope")')
2. Install Telescope:
{
'nvim-telescope/telescope.nvim',
dependencies = { 'nvim-lua/plenary.nvim' }
}
Scheme not applying
1. Verify scheme name:
:ColorMyAscii schemes list
2. Check for typos in command:
:ColorMyAscii schemes switch matrix " Correct
:ColorMyAscii schemes switch Matrix " Wrong (case-sensitive)
Blank lines command issues
1. Check buffer is modifiable:
:set modifiable?
2. Verify changes with undo:
:ColorMyAscii ensure-blank-lines
:undo " Revert if needed
Treesitter integration not working
1. Check parser availability:
:checkhealth color_my_ascii
2. Install the required parsers:
:TSInstall markdown
:TSInstall lua " or whichever language you need
Note: syntax_highlight is best-effort - it silently does nothing on content that doesn't parse as valid syntax (expected for most ASCII art).
13. DEVELOPMENT
The code is formatted with stylua and linted with luacheck. Both configs live at the repo root (.stylua.toml, .luacheckrc) and are enforced in CI (.github/workflows/lint.yml) on every push and pull request. Before opening a pull request, run both locally:
stylua lua/ plugin/ # format (use --check to only verify)
luacheck lua/ plugin/ # lint
Conventions:
• 2-space indent, single quotes, 120-column width.
• stylua owns line width, so luacheck's length check is disabled to avoid
conflicts between the two tools.
• .luacheckrc declares vim as a writable global, so plugin code may set
vim.g.*, vim.bo[b].*, etc. without warnings.
See docs/contributing.md for how to add a new language or character group.