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 *color_my_ascii-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-intro*

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 *color_my_ascii-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 *color_my_ascii-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 *color_my_ascii-quickstart*

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 *color_my_ascii-config*

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 with
                                            hl when 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 (missing words or hl) is
                            skipped with a warning; the rest still load.

                            Calling setup() again (e.g. after editing a
                            languages entry) 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 by amount
                            percent - so the block interior reads as a
                            related-but-distinguishable tint of its own
                            delimiter lines, without hand-tuning a second
                            palette per theme. hl skips 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 *color_my_ascii-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 *color_my_ascii-treesitter*

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 *color_my_ascii-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 the fence_jump keymap
    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 actual hl_group color_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's ui.kit note popup 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, u undoes 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.lua re-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 :Fence flags 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 *color_my_ascii-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
      },
    })
The fence_* actions bind the argument-less |:Fence| sub-commands; like
check_fences/fence_jump they only do anything in a markdown buffer, since
|:Fence| itself is registered buffer-local there.

fence_jump is 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 a desc, 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 *color_my_ascii-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-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 *color_my_ascii-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 in nvim --clean with 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 set unicode_version = 14 in the terminal so it follows Neovim.

Note that :Fence align counts 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 the ColorScheme autocommand 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 *color_my_ascii-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.