Inspired by the Log Analysis VSCode extension. I couldn't find any plugins for tracking and highlighting lines based on multiple regexes so I made this.
that's it.
You probably know how to use your preferred package manager. The GitHub repo
is KieranCanter/candela.nvim. No setup() required e.g. with lazy.nvim:
{ "KieranCanter/candela.nvim" }
Or with config changes:
{
"KieranCanter/candela.nvim",
opts = {...},
}
All options are optional. Candela works out of the box with sensible defaults. For lua_ls
autocomplete in your config, add ---@type Candela.Config above your opts table. See
"candela-configuration" in
doc/candela.txt for
more info on these options.
---@type Candela.Config
opts = {
window = {
width = 0.5, -- fraction of screen width
min_height = 5, -- starting/minimum height in buffer lines
max_height = 30, -- max height in buffer lines
margin = 16, -- margin between Candela UI and Neovim UI
},
engine = {
command = nil, -- auto-detected: rg > ag > ugrep > ack > grep
args = {}, -- args passed to regex command
},
matching = {
auto_refresh = false, -- refresh highlights on buffer switch
case = "system", -- "system" | "sensitive" | "ignore" | "smart"
hl_eol = false, -- highlight entire line vs matched text only
},
lightbox = {
default_view = "system-vsplit", -- default split for UI keymap
fold_style = "nvim", -- "nvim" | "fillchar" | "count" | "preview" | "detailed"
fillchar = "-", -- foldtext fillchar (see "fold" in `:h fillchars`)
custom_foldtext = nil, -- function used to define foldtext (`:h 'foldtext'`)
},
icons = {
nerd_font = false, -- set true for default nerd font icons
...,
},
palette = {
use = "replace", -- "replace" | "prepend" | "append"
cycle = "constant", -- "constant" | "random"
colors = { dark = { ... }, light = { ... } }, -- available colors (hex codes)
swatches = { dark = { ... }, light = { ... } }, -- named color shortcuts
},
syntax_highlighting = {
enabled = true, -- enable built-in log syntax highlighting
file_types = { ".log", "text" }, -- file types to enabled syntax highlighting for
},
}
icons = {
candela = "\u{f05e2}", --
regex = "\u{f069}", --
color = "\u{e22b}", --
highlight = {
header = "\u{ea61}", --
toggle_on = "\u{f1a25}", --
toggle_off = "\u{f1a26}", --
},
lightbox = {
header = "\u{e68f}", --
toggle_on = "\u{f1a25}", --
toggle_off = "\u{f1a26}", --
},
selection = {
toggle_on = "\u{ea71}", --
toggle_off = "\u{eabc}", --
},
}
icons = {
candela = "\u{1F56F}", -- 🕯
regex = "\u{2728}", -- ✨
color = "\u{1F3A8}", -- 🎨
highlight = {
header = "\u{1F4A1}", -- 💡
toggle_on = "\u{25C9}", -- ◉
toggle_off = "\u{25CB}", -- ○
},
lightbox = {
header = "\u{1F50D}", -- 🔍
toggle_on = "\u{25C9}", -- ◉
toggle_off = "\u{25CB}", -- ○
},
selection = {
toggle_on = "\u{25C9}", -- ◉
toggle_off = "\u{25CB}", -- ○
},
}
palette = {
use = "replace",
cycle = "constant",
colors = {
dark = {
"#9D4564", -- DARK MAUVE
"#A1464C", -- LIGHT MAROON
"#9E4D21", -- SIENNA
"#935800", -- MUD
"#7F6400", -- MUSTARD
"#6C6C00", -- MOSS
"#4C7522", -- LEAF GREEN
"#257A3F", -- JEWEL GREEN
"#007C6A", -- AQUAMARINE
"#007690", -- OCEAN
"#3368AB", -- MUTED BLUE
"#565FAC", -- DUSKY BLUE
"#7156A3", -- DARK LAVENDER
"#805098", -- EGGPLANT
"#94487C", -- ROUGE
},
light = {
"#F08FAE", -- PINK SHERBET
"#F49093", -- SEA PINK
"#F0986D", -- TANGERINE
"#E2A25D", -- DESERT
"#CBAE5E", -- GOLD
"#B6B75F", -- OLIVE
"#94C16F", -- PISTACHIO
"#75C787", -- MANTIS
"#65C5B1", -- NEPTUNE
"#64BFDB", -- BLUISH CYAN
"#7CB4FD", -- CRYSTAL BLUE
"#9DAAFE", -- PERIWINKLE
"#BBA0F3", -- LILAC
"#CD9AE7", -- BABY PURPLE
"#E592C8", -- LIGHT ORCHID
},
},
swatches = {
dark = {
GRAY = "#676767",
RED = "#A1454F",
BLUE = "#016DA6",
YELLOW = "#7B6600",
GREEN = "#2A793C",
ORANGE = "#9A510B",
PURPLE = "#7055A3",
},
light = {
GRAY = "#B1B1B1",
RED = "#F59282",
BLUE = "#3BC3E5",
YELLOW = "#C6B14D",
GREEN = "#82C57C",
ORANGE = "#EC9C60",
PURPLE = "#AAA5FB",
},
},
}
Candela only includes pre-set literal keymaps for UI interaction that are attached to the UI window
buffer so as to not clobber existing keymaps you may have set. These can be seen in the help menu
with the user command :Candela help or by pressing g? with the UI open.
| Key | Action |
|---|---|
<ESC>, <C-C> |
Close UI |
g? |
Toggle help menu |
<C-H> |
Toggle highlight on pattern |
<C-L> |
Toggle lightbox on pattern |
<M-l> |
Open lightbox window |
<M-c> |
Change pattern color (with autocomplete) |
<C-R> |
Refresh highlights |
<C-I> |
Import patterns from file |
<C-E> |
Export patterns to file |
<Tab> |
Select pattern and move down |
<S-Tab> |
Select pattern and move up |
<C-A> |
Select all / deselect all |
<C-N> |
Vim match (selected or current) |
<C-Q> |
Send to location list (selected or current) |
:w |
Write buffer changes to pattern list |
<Plug> keymapsYou are not left helpless though, as there are pre-set <Plug> keymaps provided that you may hook
into your own key combination.
<Plug> |
Action |
|---|---|
<Plug>CandelaUi |
Toggle UI window |
<Plug>CandelaRefresh |
Refresh patterns in current buffer |
<Plug>CandelaClear |
Clear all patterns |
<Plug>CandelaLightbox |
Toggle lightbox window |
<Plug>CandelaHelp |
Open help menu |
As an example, I set my keymaps as such:
vim.keymap.set("n", "<leader>cds", "<Plug>CandelaUi")
vim.keymap.set("n", "<leader>cdr", "<Plug>CandelaRefresh")
vim.keymap.set("n", "<leader>cdd", "<Plug>CandelaClear")
vim.keymap.set("n", "<leader>cdl", "<Plug>CandelaLightbox")
vim.keymap.set("n", "<leader>cdh", "<Plug>CandelaHelp")
See "candela-commands" in
doc/candela.txt for
more details on each command.
| Command | Description |
|---|---|
:Candela add [regex-or-index] [color] [highlight] [lightbox] |
Add a pattern |
:Candela edit <regex-or-index> <new_regex> |
Edit a pattern's regex |
:Candela copy <regex-or-index> <new_regex> |
Copy a pattern with a new regex |
:Candela delete <regex-or-index> [more...] |
Delete patterns |
:Candela clear |
Clear all patterns |
:Candela change_color <regex-or-index> <color> |
Change a pattern's color |
:Candela toggle_highlight <regex-or-index> |
Toggle highlight on a pattern |
:Candela toggle_lightbox <regex-or-index> |
Toggle lightbox on a pattern |
:Candela vimmatch [regex-or-index] [more...] |
Set vim search register and jump |
:Candela loclist [regex-or-index] [more...] |
Populate location list |
:Candela lightbox [view] |
Toggle lightbox (view: split-right, tab, etc.) |
:Candela import <path> |
Import patterns from a .lua file |
:Candela export [path] |
Export patterns to a .lua file |
:Candela help |
Open help menu |
:Candela health |
Run health check |
See "candela-api" in
doc/candela.txt for
more details on each API function.
local patterns = require("candela.patterns")
patterns.add({regex}, {color?}, {highlight?}, {lightbox?})
patterns.edit({old_regex}, {new_regex})
patterns.delete({regex})
patterns.clear()
patterns.get({regex})
patterns.count()
patterns.resolve({index_or_regex})
patterns.change_color({regex}, {new_color})
patterns.toggle_highlight({regex})
patterns.toggle_lightbox({regex})
patterns.regen_colors()
patterns.add_to_selected({regex})
patterns.remove_from_selected({regex})
patterns.add_all_to_selected()
patterns.clear_selected()
patterns.get_selected()
local ui = require("candela.ui")
ui.open()
ui.close()
ui.toggle()
ui.resize({width?}, {height?})
ui.render({entries})
ui.get_lines()
ui.render_selection({selected_set})
ui.toggle_selection({row}, {selected})
ui.clear_selection()
ui.help()
local highlighter = require("candela.highlighter")
highlighter.highlight({regex})
highlighter.remove({regex})
highlighter.remove_all()
highlighter.toggle_highlights({regex}, {toggle})
highlighter.update_color({regex})
highlighter.refresh()
highlighter.refresh_ui()
local lightbox = require("candela.lightbox")
lightbox.open({view})
lightbox.close()
lightbox.toggle({view?})
lightbox.toggle_pattern({regex})
lightbox.refresh()
lightbox.update_folds()
local locator = require("candela.locator")
locator.vimmatch({regexes})
locator.loclist({regexes})
local cdio = require("candela.io")
cdio.import({path})
cdio.export({path?})
cdio.clear()
:w to apply<Tab> for aggregate searchesThe "Lightbox" is an integral feature of Candela that allows you to dissect the lines you care about into their own window, with none of the noise cluttering your view.
Open it with <M-l> from the UI or :Candela lightbox [view].
View options: split-left, split-right, split-above, split-below, system-split,
system-vsplit, tab. If view is empty, your config's default_view is used (defaults to
system-vsplit)
When you open the lightbox window, all lines containing patterns that have the lightbox toggle set to "on" will appear in a new window split or tab, but only those lines. All unmatched lines will be folded, leaving you with a stripped down view containing only what you're looking for. Even the foldtext can be a bit noisy, so you can also configure the foldtext used to reduce clutter and further isolate the logs you care about.
The highlight toggle is still completely functional within the lightbox window. Setting a pattern's highlight toggle to "off" will remove the highlight from the matched line, but it won't remove it from the lightbox window. This can be useful if you want the matched line to still be displayed as contextual information but don't want it to have the same emphasis or importance that highlighted lines have.
Keep in mind, the lightbox window is only that, a window. It reuses the same base buffer that is currently being searched/highlighted, so any changes made to the original will result in the lightbox as well.
Candela provides a convenient way to import and export patterns should you want to reuse a list of
commonly used patterns or don't want to have to re-enter a long list when exiting and opening
Neovim. These patterns exist in the form of a .lua file that returns a list of
Candela.Pattern
data structures.
---@class Candela.Pattern
---@field regex string
---@field color string
---@field highlight boolean
---@field lightbox boolean
---@field count integer
Use <C-I> and <C-E> from the UI for quick access with file path autocomplete, or use the user
commands :Candela import and :Candela export directly.
-- my-saved-patterns.lua
return {
{
"my.*regex",
"#FFFF00",
true,
true,
},
{
"search for (this|that)?",
"#676767",
false,
true,
},
}
Candela includes default syntax highlighting for common log formats. The specifics can be viewed in
the lua/candela/syntax.lua file and will effectively match many different tokens including dates
and times of many different formats, system info (host, facility, process), log levels/severity,
primitive types (integers, booleans, floats, etc.), strings, and entities like URLs, internet
addresses, file paths, hashes, etc.
Syntax highlighting can be disabled in your user config and you may specify file types/extensions to activate syntax highlighting on.
These projects were extremely useful resources that helped me during the development of Candela: