clp

command line syntax highlighter
git clone git://jeskin.net/clp.git
README | Log | Files | Refs | LICENSE

commit b229df25a19f2ac04396b609d7ff132da8fc1764
parent 3ca2dbd0d184a84fb91f7e807513c1fcb1bab9de
Author: Jon Eskin <eskinjp@gmail.com>
Date:   Mon, 15 Aug 2022 12:29:17 -0400

add color themes

Diffstat:
MREADME.md | 29+++++++++++++++++++++++++++++
Alua/ansi_codes.lua | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlua/clp.lua | 24+++++++++++++-----------
Mlua/clprc.lua | 2+-
Mlua/style.lua | 118++++++++++---------------------------------------------------------------------
Alua/themes/ansi-16.lua | 10++++++++++
Alua/themes/codedark.lua | 24++++++++++++++++++++++++
Alua/themes/everforest.lua | 24++++++++++++++++++++++++
Alua/themes/github_dark.lua | 24++++++++++++++++++++++++
Alua/themes/github_light.lua | 24++++++++++++++++++++++++
Alua/themes/gruvbox.lua | 24++++++++++++++++++++++++
Alua/themes/material.lua | 24++++++++++++++++++++++++
Alua/themes/sonokai.lua | 24++++++++++++++++++++++++
Alua/themes/tokyonight.lua | 24++++++++++++++++++++++++
Mman/clp.1 | 12++++++++----
15 files changed, 355 insertions(+), 119 deletions(-)

diff --git a/README.md b/README.md @@ -10,6 +10,8 @@ Language support is implemented with LPEG, a tool developed by PUC which uses parsing expression grammars to improve upon traditional regex parsers (described in depth in [this article](http://www.inf.puc-rio.br/~roberto/docs/peg.pdf)). +More information is available [here](https://jeskin.net/blog/clp/), along with a blog post showing [how I use it with fzf](https://jeskin.net/blog/grep-fzf-clp/). + ## Installation ### Homebrew @@ -77,6 +79,33 @@ Parsers are upstreamed from the maintained, has great support even for niche languages, and easy to use relative to other syntax definition mechanisms. +## Configuration + +`clp` can be configured in `~/.config/clp/clprc.lua`: + +```lua +clprc = {} +clprc.theme = "ansi-16" +return clprc +``` + +### Setting your colorscheme + +To change your colorscheme, change the value of `clprc.theme` in `clprc.lua` to one of the following: + +### 3-bit ANSI theme +- ansi-16 + +### Truecolor themes +- material +- codedark +- github_dark +- github_light +- gruvbox +- tokyonight +- sonokai +- everforest + ## Contributing Contributions are welcome! Feel free to send a pull request on [Github](https://github.com/jpe90/clp) diff --git a/lua/ansi_codes.lua b/lua/ansi_codes.lua @@ -0,0 +1,87 @@ +local M = {} + +local rgb_to_ansi = {} +-- https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters +local sgr_params = { + reset = 0, + clear = 0, + default = 0, + bright = 1, + dim = 2, + italic = 3, + underscore = 4, + blink = 5, + reverse = 7, + hidden = 8, + + -- foreground + black = 30, + red = 31, + green = 32, + yellow = 33, + blue = 34, + magenta = 35, + cyan = 36, + white = 37, + + -- background + onblack = 40, + onred = 41, + ongreen = 42, + onyellow = 43, + onblue = 44, + onmagenta = 45, + oncyan = 46, + onwhite = 47, +} + +local function hex_to_rgb(hex) + hex = hex:sub(2) + local r = tonumber(hex:sub(1, 2), 16) + local g = tonumber(hex:sub(3, 4), 16) + local b = tonumber(hex:sub(5, 6), 16) + return r, g, b +end + +-- converts a SGR parameter to an ANSI escape string +-- https://en.wikipedia.org/wiki/ANSI_escape_code#Colors +local function ansi_string(sgr_number) + return string.char(27) .. '[' .. tostring(sgr_number) .. 'm' +end + +-- converts a SGR parameter to an ANSI escape string +-- https://en.wikipedia.org/wiki/ANSI_escape_code#Colors +-- not currently used +-- local function ansi_string_265(sgr_number) +-- return string.char(27) .."[38;5;" .. tostring(sgr_number) .. 'm' +-- end + +-- converts a SGR parameter to an ANSI escape string +-- https://en.wikipedia.org/wiki/ANSI_escape_code#Colors +local function ansi_string_4b(color) + local ansi_code = "" + for attr, value in pairs(color) do + if attr == "color" then + ansi_code = ansi_code .. ansi_string(sgr_params[value]) + else + print('error in 4 bit color value') + end + end + return ansi_code +end + +-- converts a SGR parameter to an ANSI escape string +-- https://en.wikipedia.org/wiki/ANSI_escape_code#Colors +local function ansi_string_24b(r,g,b) + return string.char(27) .."[38;2;" .. tostring(r) .. ';' .. tostring(g).. ';' .. tostring(b).. 'm' +end + +M.ansi_string = ansi_string +M.rgb_to_ansi = rgb_to_ansi +M.sgr_params = sgr_params +M.reset_sequence = ansi_string(sgr_params.reset) +M.hex_to_rgb = hex_to_rgb +M.ansi_string_24b = ansi_string_24b +M.ansi_string_4b = ansi_string_4b + +return M diff --git a/lua/clp.lua b/lua/clp.lua @@ -1,10 +1,11 @@ clp = {} -local colors = require('style') +local style = require('style') local ftdetect = require('ftdetect') local lexers = require('lexer') -local syntax_highlight_style = colors.syntax_highlight_style -local highlighted_line_style = colors.line_highlight_style +local ansi_codes = require('ansi_codes') +local syntax_highlight_theme = style.theme +local line_highlight_style = style.line_highlight_style require('util') function expand_theme(theme, lexer) @@ -32,7 +33,10 @@ function write(args) syntax = ftdetect.lookup_lexer(filename) end local lexer = lexers.load(syntax) - local lang_theme = expand_theme(syntax_highlight_style, lexer) + local lang_theme = expand_theme(syntax_highlight_theme, lexer) + if not lang_theme then + print(string.format('Failed to theme: `%s`', syntax)) + end if not lexer then print(string.format('Failed to load lexer: `%s`', syntax)) return 1 @@ -58,11 +62,9 @@ function write_nohl(text, lexer, theme) end function reset_colors() - io.write(tostring(colors.reset_sequence)) + io.write(tostring(ansi_codes.reset_sequence)) end --- I think modifying the lexer code to track highlighting location could be --- more efficient, but this is a quick and dirty approach for now function find_hl_bounds(s, n) local i = 0 local hl_start_pos @@ -86,7 +88,6 @@ function find_hl_bounds(s, n) else return hl_start_pos, i end - return nil end function write_hl(text, lexer, hl_line_start, hl_line_end, lang_theme) @@ -95,13 +96,13 @@ function write_hl(text, lexer, hl_line_start, hl_line_end, lang_theme) hl = hl:gsub("\n", "") local post_hl = text:sub(hl_line_end, nil) write_text(pre_hl, lexer, lang_theme) - if (hl ~= nil) then write_text(hl, lexer, highlighted_line_style) end + if (hl ~= nil) then write_text(hl, lexer, line_highlight_style) end reset_colors() if (post_hl ~= nil) then write_text(post_hl, lexer, lang_theme) end end -- https://github.com/martanne/vis/issues/601#issuecomment-327018674 -function write_text(text, lexer, style) +function write_text(text, lexer, local_style) local tokens = lexer:lex(text, 1) local token_start = 1 local last = '' @@ -110,7 +111,7 @@ function write_text(text, lexer, style) local token_end = tokens[i + 1] - 1 local name = tokens[i] - local current_style = style[name] + local current_style = local_style[name] if current_style ~= nil then -- Whereas the lexer reports all other syntaxes over -- the entire span of a token, it reports 'default' @@ -123,6 +124,7 @@ function write_text(text, lexer, style) last = name end io.write(text:sub(token_start, token_end)) + reset_colors() token_start = token_end + 1 end end diff --git a/lua/clprc.lua b/lua/clprc.lua @@ -1,3 +1,3 @@ clprc = {} -clprc.custom_theme = "dark" +clprc.theme = "ansi-16" return clprc diff --git a/lua/style.lua b/lua/style.lua @@ -1,106 +1,25 @@ +local ansi_codes = require('ansi_codes') local style = {} local ansi_colors = {} local clprc = require('clprc') +local selected_theme_name = clprc.theme +local selected_theme_path = 'themes/' .. selected_theme_name +local theme = require(selected_theme_path).theme +local theme_escape_codes = {} --- https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters -local sgr_params = { - reset = 0, - clear = 0, - default = 0, - bright = 1, - dim = 2, - underscore = 4, - blink = 5, - reverse = 7, - hidden = 8, - - -- TODO: get foreground values from external theme - -- e.g. black = theme.black_sgr_number - - -- foreground - black = 30, - red = 31, - green = 32, - yellow = 33, - blue = 34, - magenta = 35, - cyan = 36, - white = 37, - - -- background - onblack = 40, - onred = 41, - ongreen = 42, - onyellow = 43, - onblue = 44, - onmagenta = 45, - oncyan = 46, - onwhite = 47, -} - --- converts a SGR parameter to an ANSI escape string --- https://en.wikipedia.org/wiki/ANSI_escape_code#Colors -local function ansi_string(sgr_number) - if(sgr_number > 47) then - sgr_number = "38;5;" .. tostring(sgr_number) - end - return string.char(27) .. '[' .. tostring(sgr_number) .. 'm' +for sgr_name,sgr_number in pairs(ansi_codes.sgr_params) do + ansi_colors[sgr_name] = ansi_codes.ansi_string(sgr_number) end -for sgr_name,sgr_number in pairs(sgr_params) do - ansi_colors[sgr_name] = ansi_string(sgr_number) +for token, color in pairs(theme) do + if type(color) == 'table' then + theme_escape_codes[token] = ansi_codes.ansi_string_4b(color) + elseif color:sub(1,1) == '#' then + local r,g,b = ansi_codes.hex_to_rgb(color) + theme_escape_codes[token] = ansi_codes.ansi_string_24b(r,g,b) + end end --- TODO: get these values from external theme --- e.g. ['default'] = ansi_colors.default_color -local dark_mode_syntax_highlight_style = { - ['default'] = ansi_colors.white, - ['nothing'] = '', - ['class'] = ansi_colors.yellow .. ansi_colors.bright, - ['comment'] = ansi_colors.blue .. ansi_colors.bright, - ['constant'] = ansi_colors.cyan .. ansi_colors.bright, - ['definition'] = ansi_colors.blue .. ansi_colors.bright, - ['error'] = ansi_colors.red .. ansi_colors.underscore, - ['function'] = ansi_colors.blue .. ansi_colors.bright, - ['keyword'] = ansi_colors.yellow .. ansi_colors.bright, - ['label'] = ansi_colors.green .. ansi_colors.bright, - ['number'] = ansi_colors.red .. ansi_colors.bright, - ['operator'] = ansi_colors.cyan .. ansi_colors.bright, - ['regex'] = ansi_colors.green .. ansi_colors.bright, - ['string'] = ansi_colors.red .. ansi_colors.bright, - ['preprocessor'] = ansi_colors.magenta .. ansi_colors.bright, - ['tag'] = ansi_colors.red .. ansi_colors.bright, - ['type'] = ansi_colors.green .. ansi_colors.bright, - ['variable'] = ansi_colors.blue .. ansi_colors.bright, - ['whitespace'] = '', - ['embedded'] = ansi_colors.magenta .. ansi_colors.bright, - ['identifier'] = ansi_colors.white, -} - -local light_mode_syntax_highlight_style = { - ['default'] = ansi_colors.black, - ['nothing'] = '', - ['class'] = ansi_colors.magenta .. ansi_colors.bright, - ['comment'] = ansi_colors.blue .. ansi_colors.bright, - ['constant'] = ansi_colors.cyan .. ansi_colors.bright, - ['definition'] = ansi_colors.blue .. ansi_colors.bright, - ['error'] = ansi_colors.red .. ansi_colors.underscore, - ['function'] = ansi_colors.blue .. ansi_colors.bright, - ['keyword'] = ansi_colors.blue.. ansi_colors.bright, - ['label'] = ansi_colors.green .. ansi_colors.bright, - ['number'] = ansi_colors.red .. ansi_colors.bright, - ['operator'] = ansi_colors.black .. ansi_colors.bright, - ['regex'] = ansi_colors.green .. ansi_colors.bright, - ['string'] = ansi_colors.red .. ansi_colors.bright, - ['preprocessor'] = ansi_colors.magenta .. ansi_colors.bright, - ['tag'] = ansi_colors.red .. ansi_colors.bright, - ['type'] = ansi_colors.cyan .. ansi_colors.bright, - ['variable'] = ansi_colors.blue .. ansi_colors.bright, - ['whitespace'] = '', - ['embedded'] = ansi_colors.magenta .. ansi_colors.bright, - ['identifier'] = ansi_colors.black, -} - local line_highlight_style = { ['default'] = ansi_colors.black .. ansi_colors.onwhite, ['nothing'] = '' .. ansi_colors.onwhite, @@ -125,14 +44,7 @@ local line_highlight_style = { ['identifier'] = ansi_colors.black .. ansi_colors.onwhite, } -style.reset_sequence = ansi_string(sgr_params.reset) style.line_highlight_style = line_highlight_style - --- TODO: temporary hack, remove when external themes implemented -if clprc ~= nil and clprc.custom_theme == "light" then - style.syntax_highlight_style = light_mode_syntax_highlight_style -else - style.syntax_highlight_style = dark_mode_syntax_highlight_style -end +style.theme = theme_escape_codes return style diff --git a/lua/themes/ansi-16.lua b/lua/themes/ansi-16.lua @@ -0,0 +1,10 @@ +local M = {} +M.theme = { + ['comment'] = {color="cyan"}, + ['keyword'] = {color="blue"}, + ['number'] = {color="red"}, + ['string'] = {color="green"}, + ['preprocessor'] = {color="magenta"}, + ['type'] = {color="yellow"}, +} +return M diff --git a/lua/themes/codedark.lua b/lua/themes/codedark.lua @@ -0,0 +1,24 @@ +local M = {} +M.theme = { + ['default'] = "#d4d4d4", + ['nothing'] = '', + ['class'] = "#569cd6", + ['comment'] = "#6a9955", + ['constant'] = "#569cd6", + ['error'] = "#f44747", + ['function'] = "#dcdcaa", + ['keyword'] = "#c586c0", + ['label'] = "#c586c0", + ['number'] = "#b5cea8", + ['operator'] = "#d4d4d4", + ['regex'] = "#b5cea8", + ['string'] = "#ce9178", + ['preprocessor'] = "#c586c0", + ['tag'] = "#d4d4d4", + ['type'] = "#569cd6", + ['variable'] = "#9cdcfe", + ['whitespace'] = '', + ['embedded'] = "#ce9178", + ['identifier'] = "#9cdcfe", +} +return M diff --git a/lua/themes/everforest.lua b/lua/themes/everforest.lua @@ -0,0 +1,24 @@ +local M = {} +M.theme = { + ['default'] = "#d3c6aa", + ['nothing'] = '', + ['class'] = "#dbbc7f", + ['comment'] = "#859289", + ['constant'] = "#83c092", + ['error'] = "#e67e80", + ['function'] = "#a7c080", + ['keyword'] = "#e67e80", + ['label'] = "#e69875", + ['number'] = "#d699b6", + ['operator'] = "#e69875", + ['regex'] = "#d699b6", + ['string'] = "#a7c080", + ['preprocessor'] = "#d699b6", + ['tag'] = "#e69875", + ['type'] = "#dbbc7f", + ['variable'] = "#7fbbb3", + ['whitespace'] = '', + ['embedded'] = "#a7c080", + ['identifier'] = "#7fbbb3", +} +return M diff --git a/lua/themes/github_dark.lua b/lua/themes/github_dark.lua @@ -0,0 +1,24 @@ +local M = {} +M.theme = { + ['default'] = "#c9d1d9", + ['nothing'] = '', + ['class'] = "#f97583", + ['comment'] = "#6a737d", + ['constant'] = "#79b8ff", + ['error'] = "#f97583", + ['function'] = "#b392f0", + ['keyword'] = "#f97583", + ['label'] = "#f97583", + ['number'] = "#79b8ff", + ['operator'] = "#f97583", + ['regex'] = "#79b8ff", + ['string'] = "#9ecbff", + ['preprocessor'] = "#f97583", + ['tag'] = "#79b8ff", + ['type'] = "#f97583", + ['variable'] = "#79b8ff", + ['whitespace'] = '', + ['embedded'] = "#9ecbff", + ['identifier'] = "#79b8ff", +} +return M diff --git a/lua/themes/github_light.lua b/lua/themes/github_light.lua @@ -0,0 +1,24 @@ +local M = {} +M.theme = { + ['default'] = "#24292f", + ['nothing'] = '', + ['class'] = "#d73a49", + ['comment'] = "#6a737d", + ['constant'] = "#005cc5", + ['error'] = "#cb2431", + ['function'] = "#6f42c1", + ['keyword'] = "#d73a49", + ['label'] = "#d73a49", + ['number'] = "#005cc5", + ['operator'] = "#d73a49", + ['regex'] = "#005cc5", + ['string'] = "#032f62", + ['preprocessor'] = "#d73a49", + ['tag'] = "#005cc5", + ['type'] = "#d73a49", + ['variable'] = "#005cc5", + ['whitespace'] = '', + ['embedded'] = "#032f62", + ['identifier'] = "#005cc5", +} +return M diff --git a/lua/themes/gruvbox.lua b/lua/themes/gruvbox.lua @@ -0,0 +1,24 @@ +local M = {} +M.theme = { + ['default'] = "#ebdbb2", + ['nothing'] = '', + ['class'] = "#fabd2f", + ['comment'] = "#928374", + ['constant'] = "#d3869b", + ['error'] = "#282828", + ['function'] = "#b8bb26", + ['keyword'] = "#fb4934", + ['label'] = "#fb4934", + ['number'] = "#d3869b", + ['operator'] = "#fe8019", + ['regex'] = "#d3869b", + ['string'] = "#b8bb26", + ['preprocessor'] = "#8ec07c", + ['tag'] = "#fe8019", + ['type'] = "#fabd2f", + ['variable'] = "#83a598", + ['whitespace'] = '', + ['embedded'] = "#b8bb26", + ['identifier'] = "#83a598", +} +return M diff --git a/lua/themes/material.lua b/lua/themes/material.lua @@ -0,0 +1,24 @@ +local M = {} +M.theme = { + ['default'] = "#b0bec5", + ['nothing'] = '', + ['class'] = "#c792ea", + ['comment'] = "#546e7a", + ['constant'] = "#ffcb6b", + ['error'] = "#ff5370", + ['function'] = "#82aaff", + ['keyword'] = "#c792ea", + ['label'] = "#c792ea", + ['number'] = "#f78c6c", + ['operator'] = "#89ddff", + ['regex'] = "#f78c6c", + ['string'] = "#c3e88d", + ['preprocessor'] = "#c792ea", + ['tag'] = "#f07178", + ['type'] = "#c792ea", + ['variable'] = "#b0bec5", + ['whitespace'] = '', + ['embedded'] = "#c3e88d", + ['identifier'] = "#b0bec5", +} +return M diff --git a/lua/themes/sonokai.lua b/lua/themes/sonokai.lua @@ -0,0 +1,24 @@ +local M = {} +M.theme = { + ['default'] = "#e2e2e3", + ['nothing'] = '', + ['class'] = "#76cce0", + ['comment'] = "#7f8490", + ['constant'] = "#f39660", + ['error'] = "#fc5d7c", + ['function'] = "#9ed072", + ['keyword'] = "#fc5d7c", + ['label'] = "#b39df3", + ['number'] = "#b39df3", + ['operator'] = "#fc5d7c", + ['regex'] = "#b39df3", + ['string'] = "#e7c664", + ['preprocessor'] = "#fc5d7c", + ['tag'] = "#f39660", + ['type'] = "#76cce0", + ['variable'] = "#f39660", + ['whitespace'] = '', + ['embedded'] = "#e7c664", + ['identifier'] = "#f39660", +} +return M diff --git a/lua/themes/tokyonight.lua b/lua/themes/tokyonight.lua @@ -0,0 +1,24 @@ +local M = {} +M.theme = { + ['default'] = "#c0caf5", + ['nothing'] = '', + ['class'] = "#2ac3de", + ['comment'] = "#565f89", + ['constant'] = "#ff9e64", + ['error'] = "#db4b4b", + ['function'] = "#7aa2f7", + ['keyword'] = "#7dcfff", + ['label'] = "#bb9af7", + ['number'] = "#ff9e64", + ['operator'] = "#89ddff", + ['regex'] = "#ff9e64", + ['string'] = "#9ece6a", + ['preprocessor'] = "#7dcfff", + ['tag'] = "#2ac3de", + ['type'] = "#2ac3de", + ['variable'] = "#bb9af7", + ['whitespace'] = '', + ['embedded'] = "#9ece6a", + ['identifier'] = "#bb9af7", +} +return M diff --git a/man/clp.1 b/man/clp.1 @@ -21,10 +21,6 @@ Force a language's syntax for highlighting the file. To see available filetypes, \fB\-h\fR, \fB\-\-highlight\-line\fR {number} .IP Highlight a non-blank line -.SH CONFIGURATION -\fBclp\fR is configured by the \fBclprc.lua\fR file described in the \fBFILES\fR section. -.HP -If your terminal uses a light background, set clprc.custom_theme = "light" .SH FILES Upon startup .Nm @@ -38,3 +34,11 @@ The source directory where the project was built (when built from source) \fB$XDG_CONFIG_HOME/clp\fR or \fB$HOME/.config\fR if \fBXDG_CONFIG_HOME\fR is not set .HP When creating a new \fBclprc.lua\fR be sure to copy the structure from here. +.SH CONFIGURATION +\fBclp\fR is configured by the \fBclprc.lua\fR file described in the \fBFILES\fR section. +.HP +To change your colorscheme, set \fBclprc.theme = "{colorscheme name}"\fR in \fBclprc.lua\fR. A complete list of built-in colorschemes is available at \fBhttps://sr.ht/~eskin/clp/#setting-your-colorscheme\fR +.HP +Additional colorschemes can be installed to a "themes" folder in your clp configuration folder. For more information, see the README at \fBhttps://sr.ht/~eskin/clp/#configuration\fR + +