A simple Neovim plugin that highlights text surrounded by ANSI color codes with the colors and styles, while hiding literal escape sequences from view with intelligent cursor-aware concealment.
- ๐จ Universal ANSI Support - Recognizes many different ANSI escape sequence formats
- ๐ญ Comprehensive Style Support - Bold, italic, underline, reverse, strikethrough
- ๐๏ธ Cursor-Aware Concealment - ANSI codes hidden except on cursorline
- โก Real-Time Updates - Highlights update as you type and move cursor
- ๐ง Flexible Control - Separate toggles for highlighting and concealment
- ๐ True Color Support - Works with modern 24-bit color terminals
- ๐ฏ Smart Detection - Handles mixed ANSI formats in the same file and line
Using lazy.nvim
{
"cohml/ansillary.nvim",
config = function()
require("ansillary").setup()
end,
}Using packer.nvim
use {
"cohml/ansillary.nvim",
config = function()
require("ansillary").setup()
end,
}Using vim-plug
Plug 'cohml/ansillary.nvim'Then add to your init.lua:
require("ansillary").setup()require("ansillary").setup({
conceal = true, -- Hide ANSI escape sequences
reveal_on_cursorline = true, -- Reveal ANSI codes when cursor is on the line
warn_on_unsupported = true, -- Show warning when unsupported attributes are encountered
text_highlights = { -- Text content highlighting configuration
enabled = true, -- Enable highlighting of text content based on ANSI codes
format = { -- Formatting options for text content
fg = {
color = "auto", -- Color for text (auto = inherit from ANSI codes)
style = "auto", -- Style for text (auto = inherit from ANSI codes, or comma-delimited styles)
-- Valid styles: bold, italic, underline, reverse, strikethrough
-- Examples: "bold", "italic,underline", "bold,reverse"
},
bg = {
color = "auto", -- Background color for text (auto = inherit from ANSI codes)
},
},
},
ansi_highlights = { -- ANSI escape sequence highlighting configuration
enabled = false, -- Enable highlighting of ANSI escape sequences themselves
format = { -- Formatting options for ANSI sequences
fg = {
color = "auto", -- Color for ANSI sequences (auto = inherit from ANSI codes)
style = "auto", -- Style for ANSI sequences (auto = inherit from ANSI codes, or comma-delimited styles)
-- Valid styles: bold, italic, underline, reverse, strikethrough
-- Examples: "bold", "italic,underline", "bold,reverse"
},
bg = {
color = "auto", -- Background color for ANSI sequences (auto = inherit from ANSI codes)
style = "auto", -- Background style for ANSI sequences (auto = inherit from ANSI codes, or comma-delimited styles)
-- Valid styles: bold, italic, underline, reverse, strikethrough
-- Examples: "bold", "italic,underline", "bold,reverse"
},
},
},
signcolumn = { -- Signcolumn indicator configuration
enabled = false, -- Enable signcolumn indicators for lines with ANSI codes
icon = "๐", -- Icon to display in signcolumn
format = { -- Formatting options for signcolumn icon
color = "#6d8086", -- Color for signcolumn icon (subtle gray-blue)
style = "", -- Style for signcolumn icon ("" = none, or comma-delimited styles)
-- Valid styles: bold, italic, underline, reverse, strikethrough
-- Examples: "bold", "italic,underline", "bold,reverse"
},
},
enabled_filetypes = { "*" }, -- Filetypes to apply highlighting to
disabled_filetypes = {}, -- Filetypes to exclude from highlighting
})| Option | Type | Default | Description |
|---|---|---|---|
conceal |
boolean |
true |
Enable concealment of ANSI codes |
reveal_on_cursorline |
boolean |
true |
Reveal ANSI codes when cursor is on the line (no effect when conceal = false) |
warn_on_unsupported |
boolean |
true |
Show warning when unsupported attributes (dim, blink) are encountered |
text_highlights.enabled |
boolean |
true |
Enable highlighting of text content based on ANSI codes |
text_highlights.format.fg.color |
string |
"auto" |
Foreground color for text content - use "auto" to inherit from ANSI codes, or specify custom colors (hex codes/highlight groups) |
text_highlights.format.fg.style |
string |
"auto" |
Foreground style for text content - use "auto" to inherit from ANSI codes, or specify custom styles ("bold,italic" etc.) |
text_highlights.format.bg.color |
string |
"auto" |
Background color for text content - use "auto" to inherit from ANSI codes, or specify custom colors (hex codes/highlight groups) |
ansi_highlights.enabled |
boolean |
false |
Enable highlighting of ANSI escape sequences themselves |
ansi_highlights.format.fg.color |
string |
"auto" |
Foreground color for ANSI sequences - use "auto" to inherit from ANSI codes, or specify custom colors (hex codes/highlight groups) |
ansi_highlights.format.fg.style |
string |
"auto" |
Foreground style for ANSI sequences - use "auto" to inherit from ANSI codes, or specify custom styles ("bold,italic" etc.) |
ansi_highlights.format.bg.color |
string |
"auto" |
Background color for ANSI sequences - use "auto" to inherit from ANSI codes, or specify custom colors (hex codes/highlight groups) |
ansi_highlights.format.bg.style |
string |
"auto" |
Background style for ANSI sequences - use "auto" to inherit from ANSI codes, or specify custom styles ("bold,italic" etc.) |
signcolumn.enabled |
boolean |
false |
Enable signcolumn indicators for lines with ANSI codes |
signcolumn.icon |
string |
"๐" |
Icon to display in signcolumn for lines with ANSI codes |
signcolumn.format.color |
string |
"#6d8086" |
Color for signcolumn icon (highlight group name or hex color) |
signcolumn.format.style |
string |
"" |
Style for signcolumn icon - specify custom styles ("bold,italic" etc.) |
enabled_filetypes |
table<string> |
{"*"} |
Filetypes to apply highlighting to (e.g., {"log", "txt", "*.log"}) |
disabled_filetypes |
table<string> |
{} |
Filetypes to exclude from highlighting (overrides enabled_filetypes, error if overlaps) |
-- Show ANSI codes always (no concealment)
require("ansillary").setup({
conceal = false,
})
-- Apply only to specific file types
require("ansillary").setup({
enabled_filetypes = { "log", "txt", "*.log", "gitcommit" },
})
-- Apply to all files except specific types
require("ansillary").setup({
enabled_filetypes = { "*" },
disabled_filetypes = { "markdown", "help" },
})
-- Disable unsupported attribute warnings
require("ansillary").setup({
warn_on_unsupported = false,
})
-- Always keep ANSI codes hidden (no cursor reveal)
require("ansillary").setup({
conceal = true,
reveal_on_cursorline = false,
})
-- Enable ANSI highlighting with auto-inheritance
require("ansillary").setup({
ansi_highlights = {
enabled = true,
format = {
fg = { color = "auto", style = "auto" }, -- ANSI codes inherit text formatting
bg = { color = "auto", style = "auto" },
},
},
})
-- Custom ANSI highlighting (gray with bold)
require("ansillary").setup({
ansi_highlights = {
enabled = true,
format = {
fg = { color = "#666666", style = "bold" },
bg = { color = "auto", style = "auto" },
},
},
})
-- Mixed auto and custom highlighting
require("ansillary").setup({
ansi_highlights = {
enabled = true,
format = {
fg = { color = "auto", style = "bold,italic" }, -- Auto color, custom style
bg = { color = "#1e1e1e", style = "auto" }, -- Custom background, auto style
},
},
})
-- Disable text highlighting (only show raw text, no ANSI styling)
require("ansillary").setup({
text_highlights = {
enabled = false,
},
})
-- Custom text highlighting (override ANSI codes with fixed styling)
require("ansillary").setup({
text_highlights = {
enabled = true,
format = {
fg = { color = "#00ff00", style = "bold" }, -- All text green and bold
bg = { color = "auto" }, -- Keep ANSI background colors
},
},
})
-- Mixed text highlighting (custom color, auto styling from ANSI)
require("ansillary").setup({
text_highlights = {
enabled = true,
format = {
fg = { color = "#ffaa00", style = "auto" }, -- Custom orange, ANSI syling
bg = { color = "auto" }, -- ANSI background colors
},
},
})
-- Enable signcolumn indicators with default settings
require("ansillary").setup({
signcolumn = {
enabled = true,
},
})
-- Enable signcolumn with custom icon
require("ansillary").setup({
signcolumn = {
enabled = true,
icon = "๐",
},
})
-- Enable signcolumn with red color and bold style
require("ansillary").setup({
signcolumn = {
enabled = true,
icon = "A",
format = {
color = "#f38ba8",
style = "bold",
},
},
})| Command | Description |
|---|---|
:AnsillaryToggle |
Toggle entire plugin on/off |
:AnsillaryToggleConceal |
Toggle ANSI code concealment only |
:AnsillaryToggleReveal |
Toggle revealing ANSI codes under cursor |
:AnsillaryToggleText |
Toggle highlighting of text content based on ANSI codes |
:AnsillaryToggleANSI |
Toggle highlighting of ANSI escape sequences |
local ansillary = require("ansillary")
-- Toggle plugin functionality completely
ansillary.toggle()
-- Toggle concealment only (keep highlighting)
ansillary.toggle_conceal()
-- Toggle revealing ANSI codes under cursor
ansillary.toggle_reveal()
-- Toggle highlighting text content
ansillary.toggle_text_highlights()
-- Toggle highlighting ANSI escape sequences
ansillary.toggle_ansi_highlights()ansillary.nvim supports many different ANSI escape sequence formats:
\033[1;34;46mLorem ipsum\033[0m # Octal escape
\e[1;34;46mLorem ipsum\e[0m # Short escape
\x1b[1;34;46mLorem ipsum\x1b[0m # Hex escape (lowercase)
\x1B[1;34;46mLorem ipsum\x1B[0m # Hex escape (uppercase)\u001b[1;34;46mLorem ipsum\u001b[0m # 4-digit Unicode
\U0000001b[1;34;46mLorem ipsum\U0000001b[0m # 8-digit Unicode$'\033[1;34;46mLorem ipsum\033[0m' # Quoted octal
$'\e[1;34;46mLorem ipsum\e[0m' # Quoted short
$'\x1b[1;34;46mLorem ipsum\x1b[0m' # Quoted hex (lowercase)
$'\x1B[1;34;46mLorem ipsum\x1B[0m' # Quoted hex (uppercase)
$'\u001b[1;34;46mLorem ipsum\u001b[0m' # Quoted Unicode (4-digit)
$'\U0000001b[1;34;46mLorem ipsum\U0000001b[0m' # Quoted Unicode (8-digit)^[[1;34;46mLorem ipsum^[[0m # Literal ESC character- Foreground:
30-37(standard),90-97(bright) - Background:
40-47(standard),100-107(bright)
0: Reset all formatting1: Bold2: Dim (not supported by Neovim - ignored with optional warning)3: Italic4: Underline5: Blink (not supported by Neovim - ignored with optional warning)7: Reverse video9: Strikethrough
Regular text with \033[32mgreen\033[0m and \033[31mred\033[0m words.
Fancy formats: \033[1;3;4;35mbold italic underlined magenta\033[0m
Background: \033[43;30myellow bg, black text\033[0m
grep --color=always "pattern" *.log | nvimecho -e "\e[1;32mโ SUCCESS:\e[0m Operation completed"
echo -e "\x1b[1;31mโ ERROR:\x1b[0m Operation failed"\033[31mRed\033[0m \u001b[32mGreen\u001b[0m ^[[34mBlue^[[0m text- Pattern Detection: Scans for ANSI sequences using regex
- Code Parsing: Extracts numeric codes and converts to color/style attributes
- Highlight Application: Creates Neovim highlight groups and applies them to text
- Smart Concealment: Uses extmarks to hide ANSI codes except on cursor line
- Real-Time Updates: Responds immediately to cursor movement and text edits
- Neovim โฅ 0.7.0
- Terminal with true color support (recommended)
This plugin includes comprehensive tests to ensure reliability and prevent regressions during development.
The test suite is organized as follows:
tests/
โโโ test_helper.lua # Test utilities and vim API mocking
โโโ fixtures/
โ โโโ ansi_samples.lua # Sample ANSI sequences for testing
โโโ unit/ # Unit tests for individual modules
โ โโโ regex_spec.lua # Tests for ANSI pattern matching
โ โโโ config_spec.lua # Tests for configuration handling
โ โโโ highlights_spec.lua # Tests for highlight creation
โ โโโ init_spec.lua # Tests for main plugin logic
โโโ integration/ # Integration tests
โโโ full_integration_spec.lua # End-to-end workflow tests
- Lua โฅ 5.1 (5.4 recommended)
- Busted testing framework (optional, basic runner included)
# Run all unit tests
make test
# Or run directly with lua
lua run_all_tests.lua# Run all unit tests (default)
make test
# Run all tests including integration
make test-all
# Run tests for specific modules
make test-regex # Test ANSI pattern matching
make test-config # Test configuration handling
make test-highlights # Test highlight creation
make test-init # Test main plugin logic
# Clean test artifacts
make clean
# Show help
make helpThe test suite covers:
Core Functionality:
- โ ANSI escape sequence detection and parsing
- โ All supported ANSI color codes (0-15)
- โ Text styling (bold, italic, underline, reverse, strikethrough)
- โ Highlight group creation and management
- โ Configuration validation and merging
ANSI Format Support:
- โ
Octal sequences (
\033[31m) - โ
Short sequences (
\e[31m) - โ
Hex sequences (
\x1b[31m,\x1B[31m) - โ
Unicode sequences (
\u001b[31m,\U0000001b[31m) - โ
Bash quoted formats (
$'\033[31m') - โ
Literal ESC characters (
^[[31m) - โ
CSI sequences (
\x9b31m)
Edge Cases:
- โ Empty ANSI codes (reset sequences)
- โ Malformed sequences
- โ Consecutive ANSI codes
- โ Very long attribute lists
- โ Mixed formats in single line
- โ Multiline content processing
Plugin Features:
- โ Concealment with cursor reveal
- โ ANSI sequence highlighting
- โ Filetype filtering
- โ Toggle operations
- โ Configuration options
- โ Real-world format compatibility
When contributing new features:
- Unit Tests: Add tests to appropriate
*_spec.luafile - Integration Tests: Add end-to-end scenarios to
full_integration_spec.lua - Fixtures: Add new ANSI samples to
ansi_samples.lua - Edge Cases: Ensure error conditions are tested
Example test:
describe("new feature", function()
it("should handle specific case", function()
helper.create_test_buffer({"test content"})
ansillary.setup({option = true})
assert.has_no.errors(function()
ansillary._trigger_highlighting_for_tests()
end)
end)
end)The test suite uses a custom vim API mock that simulates Neovim's behavior without requiring a full Neovim instance. This allows for:
- Fast execution - Tests run in milliseconds
- Reliable isolation - Each test starts with clean state
- Comprehensive mocking - All vim API functions used by the plugin
- Easy debugging - Pure Lua environment with standard debugging tools
Contributions are welcome! Feel free to:
- Report bugs or request features via [issues]
- Submit pull requests
- Improve documentation
- Add test cases
Before submitting pull requests:
- Run the test suite:
make test - Ensure all tests pass
- Add tests for new functionality
- Update documentation as needed
MIT License. See LICENSE file for details.
Made with โค๏ธ for the Neovim community