From 94197db4be1c82ddfb67dafa576e9bc7e77e657e Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Thu, 17 Oct 2019 16:26:20 -0700 Subject: Add readme, docs, rgb(), #RGB, and options - Add support for `rgb(...)` functions behind an option gate. - Highlight #RGB codes. - Add options, like `rgb_fn` and `no_names` - Add luadoc. --- README.md | 81 ++++++++++++ doc/index.html | 65 ++++++++++ doc/ldoc.css | 303 +++++++++++++++++++++++++++++++++++++++++++++ doc/modules/colorizer.html | 237 +++++++++++++++++++++++++++++++++++ doc/modules/trie.html | 66 ++++++++++ lua/colorizer.lua | 79 ++++++++++-- lua/trie.lua | 2 +- 7 files changed, 823 insertions(+), 10 deletions(-) create mode 100644 README.md create mode 100644 doc/index.html create mode 100644 doc/ldoc.css create mode 100644 doc/modules/colorizer.html create mode 100644 doc/modules/trie.html diff --git a/README.md b/README.md new file mode 100644 index 0000000..29e274e --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +# colorizer.lua + +[![luadoc](https://img.shields.io/badge/luadoc-0.1-blue)](https://norcalli.github.io/luadoc/nvim-colorizer.lua/doc/modules/colorizer.html) + +A high-performance color highlighter for Neovim which has **no external dependencies**! Written in performant Luajit. + +## Installation and Usage + +Use your plugin manager or clone directly into your `runtimepath`. + +```vim +Plug 'norcalli/nvim-colorizer.lua' +``` + +As long as you have `malloc()` and `free()` on your system, this will work. Which includes Linux, OSX, and Windows. + +One line setup. This will create an `autocmd` for `FileType *` to highlight every filetype. + +```vim +lua require'colorizer'.setup() +``` + +### Why another highlighter? + +This has no external dependencies, which means you install it and it works. Other colorizers typically were synchronous and slow, as well. Being written with performance in mind and leveraging the excellent LuaJIT and a handwritten parser, updates can be done in real time. There are plugins such as [hexokinase](https://github.com/RRethy/vim-hexokinase) which have good performance, but it has some difficulty with becoming out of sync. The downside is that *this only works for Neovim*, and that will never change. + +Additionally, having a Lua API that's available means users can use this as a library to do custom highlighting themselves. + +### Customization + +The available highlight modes are `foreground`, `background`. The default is +`background`. + +Full options list: +- `no_names`: Disable parsing names like "Blue" +- `rgb_fn`: Enable parsing `rgb(...)` functions. +- `mode`: Highlight mode. Valid options: `foreground`,`background` + +For basic setup, you can use a command like the following. + +```lua +-- Attaches to every FileType mode +require 'colorizer'.setup() + +-- Attach to certain Filetypes, add special configuration for `html` +-- Use `background` for everything else. +require 'colorizer'.setup { + 'css'; + 'javascript'; + html = { + mode = 'foreground'; + } +} + +-- Use the `default_options` as the second parameter, which uses +-- `foreground` for every mode. This is the inverse of the previous +-- setup configuration. +require 'colorizer'.setup({ + 'css'; + 'javascript'; + html = { mode = 'background' }; +}, { mode = 'foreground' }) + +-- Use the `default_options` as the second parameter, which uses +-- `foreground` for every mode. This is the inverse of the previous +-- setup configuration. +require 'colorizer'.setup { + '*'; -- Highlight all files, but customize some others. + css = { rgb_fn = true; }; -- Enable parsing rgb(...) functions in css. + html = { no_names = true; } -- Disable parsing "names" like Blue or Gray +} +``` + + +For lower level interface, see the [LuaDocs for API details](https://norcalli.github.io/luadoc/nvim-colorizer.lua/doc/modules/colorizer.html) or use `:h colorizer.lua` once installed. + +## TODO + +- [ ] Add more display modes? +- [ ] Use a more space efficient trie implementation. +- [ ] Create a COMMON_SETUP which does obvious things like enable `rgb_fn` for css diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..371e5da --- /dev/null +++ b/doc/index.html @@ -0,0 +1,65 @@ + + + + + Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ + + +

Modules

+ + + + + + + + + +
colorizerHighlights terminal CSI ANSI color codes.
trieTrie implementation in luajit + Copyright © 2019 Ashkan Kiani
+ +
+
+
+generated by LDoc 1.4.6 +Last updated 2019-10-17 16:25:49 +
+
+ + diff --git a/doc/ldoc.css b/doc/ldoc.css new file mode 100644 index 0000000..52c4ad2 --- /dev/null +++ b/doc/ldoc.css @@ -0,0 +1,303 @@ +/* BEGIN RESET + +Copyright (c) 2010, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.com/yui/license.html +version: 2.8.2r1 +*/ +html { + color: #000; + background: #FFF; +} +body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td { + margin: 0; + padding: 0; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +fieldset,img { + border: 0; +} +address,caption,cite,code,dfn,em,strong,th,var,optgroup { + font-style: inherit; + font-weight: inherit; +} +del,ins { + text-decoration: none; +} +li { + margin-left: 20px; +} +caption,th { + text-align: left; +} +h1,h2,h3,h4,h5,h6 { + font-size: 100%; + font-weight: bold; +} +q:before,q:after { + content: ''; +} +abbr,acronym { + border: 0; + font-variant: normal; +} +sup { + vertical-align: baseline; +} +sub { + vertical-align: baseline; +} +legend { + color: #000; +} +input,button,textarea,select,optgroup,option { + font-family: inherit; + font-size: inherit; + font-style: inherit; + font-weight: inherit; +} +input,button,textarea,select {*font-size:100%; +} +/* END RESET */ + +body { + margin-left: 1em; + margin-right: 1em; + font-family: arial, helvetica, geneva, sans-serif; + background-color: #ffffff; margin: 0px; +} + +code, tt { font-family: monospace; font-size: 1.1em; } +span.parameter { font-family:monospace; } +span.parameter:after { content:":"; } +span.types:before { content:"("; } +span.types:after { content:")"; } +.type { font-weight: bold; font-style:italic } + +body, p, td, th { font-size: .95em; line-height: 1.2em;} + +p, ul { margin: 10px 0 0 0px;} + +strong { font-weight: bold;} + +em { font-style: italic;} + +h1 { + font-size: 1.5em; + margin: 20px 0 20px 0; +} +h2, h3, h4 { margin: 15px 0 10px 0; } +h2 { font-size: 1.25em; } +h3 { font-size: 1.15em; } +h4 { font-size: 1.06em; } + +a:link { font-weight: bold; color: #004080; text-decoration: none; } +a:visited { font-weight: bold; color: #006699; text-decoration: none; } +a:link:hover { text-decoration: underline; } + +hr { + color:#cccccc; + background: #00007f; + height: 1px; +} + +blockquote { margin-left: 3em; } + +ul { list-style-type: disc; } + +p.name { + font-family: "Andale Mono", monospace; + padding-top: 1em; +} + +pre { + background-color: rgb(245, 245, 245); + border: 1px solid #C0C0C0; /* silver */ + padding: 10px; + margin: 10px 0 10px 0; + overflow: auto; + font-family: "Andale Mono", monospace; +} + +pre.example { + font-size: .85em; +} + +table.index { border: 1px #00007f; } +table.index td { text-align: left; vertical-align: top; } + +#container { + margin-left: 1em; + margin-right: 1em; + background-color: #f0f0f0; +} + +#product { + text-align: center; + border-bottom: 1px solid #cccccc; + background-color: #ffffff; +} + +#product big { + font-size: 2em; +} + +#main { + background-color: #f0f0f0; + border-left: 2px solid #cccccc; +} + +#navigation { + float: left; + width: 14em; + vertical-align: top; + background-color: #f0f0f0; + overflow: visible; +} + +#navigation h2 { + background-color:#e7e7e7; + font-size:1.1em; + color:#000000; + text-align: left; + padding:0.2em; + border-top:1px solid #dddddd; + border-bottom:1px solid #dddddd; +} + +#navigation ul +{ + font-size:1em; + list-style-type: none; + margin: 1px 1px 10px 1px; +} + +#navigation li { + text-indent: -1em; + display: block; + margin: 3px 0px 0px 22px; +} + +#navigation li li a { + margin: 0px 3px 0px -1em; +} + +#content { + margin-left: 14em; + padding: 1em; + width: 700px; + border-left: 2px solid #cccccc; + border-right: 2px solid #cccccc; + background-color: #ffffff; +} + +#about { + clear: both; + padding: 5px; + border-top: 2px solid #cccccc; + background-color: #ffffff; +} + +@media print { + body { + font: 12pt "Times New Roman", "TimeNR", Times, serif; + } + a { font-weight: bold; color: #004080; text-decoration: underline; } + + #main { + background-color: #ffffff; + border-left: 0px; + } + + #container { + margin-left: 2%; + margin-right: 2%; + background-color: #ffffff; + } + + #content { + padding: 1em; + background-color: #ffffff; + } + + #navigation { + display: none; + } + pre.example { + font-family: "Andale Mono", monospace; + font-size: 10pt; + page-break-inside: avoid; + } +} + +table.module_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.module_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.module_list td.name { background-color: #f0f0f0; min-width: 200px; } +table.module_list td.summary { width: 100%; } + + +table.function_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.function_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.function_list td.name { background-color: #f0f0f0; min-width: 200px; } +table.function_list td.summary { width: 100%; } + +ul.nowrap { + overflow:auto; + white-space:nowrap; +} + +dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} +dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;} +dl.table h3, dl.function h3 {font-size: .95em;} + +/* stop sublists from having initial vertical space */ +ul ul { margin-top: 0px; } +ol ul { margin-top: 0px; } +ol ol { margin-top: 0px; } +ul ol { margin-top: 0px; } + +/* make the target distinct; helps when we're navigating to a function */ +a:target + * { + background-color: #FF9; +} + + +/* styles for prettification of source */ +pre .comment { color: #558817; } +pre .constant { color: #a8660d; } +pre .escape { color: #844631; } +pre .keyword { color: #aa5050; font-weight: bold; } +pre .library { color: #0e7c6b; } +pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; } +pre .string { color: #8080ff; } +pre .number { color: #f8660d; } +pre .operator { color: #2239a8; font-weight: bold; } +pre .preprocessor, pre .prepro { color: #a33243; } +pre .global { color: #800080; } +pre .user-keyword { color: #800080; } +pre .prompt { color: #558817; } +pre .url { color: #272fc2; text-decoration: underline; } + diff --git a/doc/modules/colorizer.html b/doc/modules/colorizer.html new file mode 100644 index 0000000..adce131 --- /dev/null +++ b/doc/modules/colorizer.html @@ -0,0 +1,237 @@ + + + + + Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module colorizer

+

Highlights terminal CSI ANSI color codes.

+

+ + +

Functions

+ + + + + + + + + + + + + +
highlight_buffer (buf[, ns=DEFAULT_NAMESPACE], lines, line_start[, options])Highlight the buffer region.
attach_to_buffer ([buf=0[, options]])Attach to a buffer and continuously highlight changes.
setup ([filetypes={'*'}[, default_options]])Easy to use function if you want the full setup without fine grained control.
+

Fields

+ + + + + +
DEFAULT_NAMESPACEDefault namespace used in `highlight_buffer` and `attach_to_buffer`.
+ +
+
+ + +

Functions

+ +
+
+ + highlight_buffer (buf[, ns=DEFAULT_NAMESPACE], lines, line_start[, options]) +
+
+ Highlight the buffer region. +Highlight starting from `line_start` (0-indexed) for each line described by `lines` in the +buffer `buf` and attach it to the namespace `ns`. + + + +

Parameters:

+
    +
  • buf + integer + buffer id. +
  • +
  • ns + integer + the namespace id. Create it with `vim.api.create_namespace` + (default DEFAULT_NAMESPACE) +
  • +
  • lines + {string,...} + the lines to highlight from the buffer. +
  • +
  • line_start + integer + should be 0-indexed +
  • +
  • options + Configuration options as described in `setup` + (optional) +
  • +
+ + + +

See also:

+ + + +
+
+ + attach_to_buffer ([buf=0[, options]]) +
+
+ Attach to a buffer and continuously highlight changes. + + +

Parameters:

+
    +
  • buf + integer + A value of 0 implies the current buffer. + (default 0) +
  • +
  • options + Configuration options as described in `setup` + (optional) +
  • +
+ + + +

See also:

+ + + +
+
+ + setup ([filetypes={'*'}[, default_options]]) +
+
+ Easy to use function if you want the full setup without fine grained control. + Setup an autocmd which enables colorizing for the filetypes and options specified. +

By default highlights all FileTypes. +

Example config: + ``` + { 'scss', 'html', css = { rgb_fn = true; }, javascript = { no_names = true } } + ``` +

You can combine an array and more specific options. + Possible options: + - `no_names`: Don't highlight names like Blue + - `rgb_fn`: Highlight `rgb(...)` functions. + - `mode`: Highlight mode. Valid options: `foreground`,`background` + + + +

Parameters:

+
    +
  • filetypes + A table/array of filetypes to selectively enable and/or customize. By default, enables all filetypes. + (default {'*'}) +
  • +
  • default_options + {[string]=string} + Default options to apply for the filetypes enable. + (optional) +
  • +
+ + + + +

Usage:

+
    +
    require'colorizer'.setup()
    +
+ +
+
+

Fields

+ +
+
+ + DEFAULT_NAMESPACE +
+
+ Default namespace used in `highlight_buffer` and `attach_to_buffer`. + The name is "terminal_highlight" + + + + + +

See also:

+ + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2019-10-17 16:25:49 +
+
+ + diff --git a/doc/modules/trie.html b/doc/modules/trie.html new file mode 100644 index 0000000..898b18e --- /dev/null +++ b/doc/modules/trie.html @@ -0,0 +1,66 @@ + + + + + Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module trie

+

Trie implementation in luajit + Copyright © 2019 Ashkan Kiani

+

+ + + +
+
+ + + + +
+
+
+generated by LDoc 1.4.6 +Last updated 2019-10-17 16:25:49 +
+
+ + diff --git a/lua/colorizer.lua b/lua/colorizer.lua index 03d2bbc..c53057d 100644 --- a/lua/colorizer.lua +++ b/lua/colorizer.lua @@ -1,5 +1,5 @@ --- Highlights terminal CSI ANSI color codes. --- @module terminal +-- @module colorizer local nvim = require 'nvim' local Trie = require 'trie' @@ -12,6 +12,7 @@ local DEFAULT_NAMESPACE = nvim.create_namespace 'colorizer' local COLOR_MAP local COLOR_TRIE +--- Setup the COLOR_MAP and COLOR_TRIE local function initialize_trie() if not COLOR_TRIE then COLOR_MAP = nvim.get_color_map() @@ -69,9 +70,17 @@ end local function create_highlight(rgb_hex, options) -- TODO validate rgb format? + rgb_hex = rgb_hex:lower() local highlight_name = highlight_cache[rgb_hex] -- Look up in our cache. if not highlight_name then + if #rgb_hex == 3 then + rgb_hex = table.concat { + rgb_hex:sub(1,1):rep(2); + rgb_hex:sub(2,2):rep(2); + rgb_hex:sub(3,3):rep(2); + } + end -- Create the highlight highlight_name = make_highlight_name(rgb_hex) if options.mode == 'foreground' then @@ -116,6 +125,8 @@ buffer `buf` and attach it to the namespace `ns`. @tparam[opt=DEFAULT_NAMESPACE] integer ns the namespace id. Create it with `vim.api.create_namespace` @tparam {string,...} lines the lines to highlight from the buffer. @tparam integer line_start should be 0-indexed +@param[opt] options Configuration options as described in `setup` +@see setup ]] local function highlight_buffer(buf, ns, lines, line_start, options) options = options or {} @@ -127,7 +138,23 @@ local function highlight_buffer(buf, ns, lines, line_start, options) current_linenum = current_linenum - 1 + line_start local i = 1 while i < #line do + if options.rgb_fn then + -- TODO this can have improved performance by either reusing my trie or + -- doing a byte comp. + line:gsub("()rgb%(%s*(%d+%%?)%s*,%s*(%d+%%?)%s*,%s*(%d+%%?)%s*%)()", function(match_start, r,g,b, match_end) + if r:sub(-1,-1) == "%" then r = math.floor(r:sub(1,-2)/100*255) end + if g:sub(-1,-1) == "%" then g = math.floor(g:sub(1,-2)/100*255) end + if b:sub(-1,-1) == "%" then b = math.floor(b:sub(1,-2)/100*255) end + local rgb_hex = ("%02x%02x%02x"):format(r,g,b) + if #rgb_hex ~= 6 then + return + end + local highlight_name = create_highlight(rgb_hex, options) + nvim.buf_add_highlight(buf, ns, highlight_name, current_linenum, match_start-1, match_end-1) + end) + end local byte = line:byte(i) + -- # indicates an #RGB or #RRGGBB code if byte == b_hash then i = i + 1 if #line >= i + 5 then @@ -141,14 +168,29 @@ local function highlight_buffer(buf, ns, lines, line_start, options) end if not invalid then local rgb_hex = line:sub(i, i+5) - -- TODO figure out proper background/foreground local highlight_name = create_highlight(rgb_hex, options) -- Subtract one because 0-indexed, subtract another 1 for the '#' nvim.buf_add_highlight(buf, ns, highlight_name, current_linenum, i-1-1, i+6-1) i = i + 5 end + elseif #line >= i + 2 then + local invalid = false + for n = i, i+2 do + byte = line:byte(n) + if not byte_is_hex(byte) then + invalid = true + break + end + end + if not invalid then + local rgb_hex = line:sub(i, i+2) + local highlight_name = create_highlight(rgb_hex, options) + -- Subtract one because 0-indexed, subtract another 1 for the '#' + nvim.buf_add_highlight(buf, ns, highlight_name, current_linenum, i-1-1, i+3-1) + i = i + 2 + end end - else + elseif not options.no_names then -- TODO skip if the remaining length is less than the shortest length -- of an entry in our trie. local prefix = COLOR_TRIE:longest_prefix(line:sub(i)) @@ -162,6 +204,8 @@ local function highlight_buffer(buf, ns, lines, line_start, options) else i = i + 1 end + else + i = i + 1 end end end @@ -169,7 +213,8 @@ end --- Attach to a buffer and continuously highlight changes. -- @tparam[opt=0] integer buf A value of 0 implies the current buffer. --- @see highlight_buffer +-- @param[opt] options Configuration options as described in `setup` +-- @see setup local function attach_to_buffer(buf, options) local ns = DEFAULT_NAMESPACE if buf == 0 or buf == nil then @@ -196,9 +241,25 @@ local function attach_to_buffer(buf, options) end --- Easy to use function if you want the full setup without fine grained control. --- Establishes an autocmd for `FileType terminal` +-- Setup an autocmd which enables colorizing for the filetypes and options specified. +-- +-- By default highlights all FileTypes. +-- +-- Example config: +-- ``` +-- { 'scss', 'html', css = { rgb_fn = true; }, javascript = { no_names = true } } +-- ``` +-- +-- You can combine an array and more specific options. +-- Possible options: +-- - `no_names`: Don't highlight names like Blue +-- - `rgb_fn`: Highlight `rgb(...)` functions. +-- - `mode`: Highlight mode. Valid options: `foreground`,`background` +-- +-- @param[opt={'*'}] filetypes A table/array of filetypes to selectively enable and/or customize. By default, enables all filetypes. +-- @tparam[opt] {[string]=string} default_options Default options to apply for the filetypes enable. -- @usage require'colorizer'.setup() -local function auto_setup(filetypes, default_options) +local function setup(filetypes, default_options) if not nvim.o.termguicolors then nvim.err_writeln("&termguicolors must be set") return @@ -230,7 +291,7 @@ local function auto_setup(filetypes, default_options) end filetype_options[filetype] = options -- TODO What's the right mode for this? BufEnter? - nvim.ex.autocmd("FileType", filetype, "lua COLORIZER_SETUP_HOOK(%s)") + nvim.ex.autocmd("FileType", filetype, "lua COLORIZER_SETUP_HOOK()") end end nvim.ex.augroup("END") @@ -239,9 +300,9 @@ end --- @export return { DEFAULT_NAMESPACE = DEFAULT_NAMESPACE; - setup = auto_setup; + setup = setup; attach_to_buffer = attach_to_buffer; highlight_buffer = highlight_buffer; - initialize = initialize_trie; + -- initialize = initialize_trie; } diff --git a/lua/trie.lua b/lua/trie.lua index 60750f9..b0751aa 100644 --- a/lua/trie.lua +++ b/lua/trie.lua @@ -1,4 +1,4 @@ --- Trie implementation in luajit +--- Trie implementation in luajit -- Copyright © 2019 Ashkan Kiani -- This program is free software: you can redistribute it and/or modify -- cgit v1.2.3-70-g09d2