From fbe4a8d12ec1238ab1552361f64c852debd9a33f Mon Sep 17 00:00:00 2001 From: siduck Date: Fri, 22 Jul 2022 16:04:09 +0000 Subject: format files --- .stylua.toml | 2 +- lua/colorizer.lua | 1193 ++++++++++++++++++++++++------------------------ lua/colorizer/nvim.lua | 398 +++++++++------- lua/colorizer/trie.lua | 362 ++++++++------- test/print-trie.lua | 96 ++-- 5 files changed, 1057 insertions(+), 994 deletions(-) diff --git a/.stylua.toml b/.stylua.toml index e9bf648..df96b7b 100644 --- a/.stylua.toml +++ b/.stylua.toml @@ -1,6 +1,6 @@ column_width = 120 line_endings = "Unix" indent_type = "Spaces" -indent_width = 3 +indent_width = 2 quote_style = "AutoPreferDouble" no_call_parentheses = true diff --git a/lua/colorizer.lua b/lua/colorizer.lua index 9183f9e..f24c792 100644 --- a/lua/colorizer.lua +++ b/lua/colorizer.lua @@ -18,56 +18,56 @@ local COLOR_MAP local COLOR_TRIE local COLOR_NAME_MINLEN, COLOR_NAME_MAXLEN local COLOR_NAME_SETTINGS = { - lowercase = true, - strip_digits = false, + lowercase = true, + strip_digits = false, } --- Setup the COLOR_MAP and COLOR_TRIE local function initialize_trie() - if not COLOR_TRIE then - COLOR_MAP = {} - COLOR_TRIE = Trie() - for k, v in pairs(nvim.get_color_map()) do - if not (COLOR_NAME_SETTINGS.strip_digits and k:match "%d+$") then - COLOR_NAME_MINLEN = COLOR_NAME_MINLEN and min(#k, COLOR_NAME_MINLEN) or #k - COLOR_NAME_MAXLEN = COLOR_NAME_MAXLEN and max(#k, COLOR_NAME_MAXLEN) or #k - local rgb_hex = tohex(v, 6) - COLOR_MAP[k] = rgb_hex - COLOR_TRIE:insert(k) - if COLOR_NAME_SETTINGS.lowercase then - local lowercase = k:lower() - COLOR_MAP[lowercase] = rgb_hex - COLOR_TRIE:insert(lowercase) - end - end - end - end + if not COLOR_TRIE then + COLOR_MAP = {} + COLOR_TRIE = Trie() + for k, v in pairs(nvim.get_color_map()) do + if not (COLOR_NAME_SETTINGS.strip_digits and k:match "%d+$") then + COLOR_NAME_MINLEN = COLOR_NAME_MINLEN and min(#k, COLOR_NAME_MINLEN) or #k + COLOR_NAME_MAXLEN = COLOR_NAME_MAXLEN and max(#k, COLOR_NAME_MAXLEN) or #k + local rgb_hex = tohex(v, 6) + COLOR_MAP[k] = rgb_hex + COLOR_TRIE:insert(k) + if COLOR_NAME_SETTINGS.lowercase then + local lowercase = k:lower() + COLOR_MAP[lowercase] = rgb_hex + COLOR_TRIE:insert(lowercase) + end + end + end + end end local function merge(...) - local res = {} - for i = 1, select("#", ...) do - local o = select(i, ...) - for k, v in pairs(o) do - res[k] = v - end - end - return res + local res = {} + for i = 1, select("#", ...) do + local o = select(i, ...) + for k, v in pairs(o) do + res[k] = v + end + end + return res end local DEFAULT_OPTIONS = { - RGB = true, -- #RGB hex codes - RRGGBB = true, -- #RRGGBB hex codes - names = true, -- "Name" codes like Blue or blue - RRGGBBAA = false, -- #RRGGBBAA hex codes - AARRGGBB = false, -- 0xAARRGGBB hex codes - rgb_fn = false, -- CSS rgb() and rgba() functions - hsl_fn = false, -- CSS hsl() and hsla() functions - css = false, -- Enable all CSS features: rgb_fn, hsl_fn, names, RGB, RRGGBB - css_fn = false, -- Enable all CSS *functions*: rgb_fn, hsl_fn - -- Available modes: foreground, background, sign, virtualtext - mode = "background", -- Set the display mode. - virtualtext = "■", + RGB = true, -- #RGB hex codes + RRGGBB = true, -- #RRGGBB hex codes + names = true, -- "Name" codes like Blue or blue + RRGGBBAA = false, -- #RRGGBBAA hex codes + AARRGGBB = false, -- 0xAARRGGBB hex codes + rgb_fn = false, -- CSS rgb() and rgba() functions + hsl_fn = false, -- CSS hsl() and hsla() functions + css = false, -- Enable all CSS features: rgb_fn, hsl_fn, names, RGB, RRGGBB + css_fn = false, -- Enable all CSS *functions*: rgb_fn, hsl_fn + -- Available modes: foreground, background, sign, virtualtext + mode = "background", -- Set the display mode. + virtualtext = "■", } -- -- TODO use rgb as the return value from the matcher functions @@ -87,207 +87,207 @@ local CATEGORY_ALPHA = lshift(1, 1) local CATEGORY_HEX = lshift(1, 2) local CATEGORY_ALPHANUM = bor(CATEGORY_ALPHA, CATEGORY_DIGIT) do - local b = string.byte - for i = 0, 255 do - local v = 0 - -- Digit is bit 1 - if i >= b "0" and i <= b "9" then - v = bor(v, lshift(1, 0)) - v = bor(v, lshift(1, 2)) - v = bor(v, lshift(i - b "0", 4)) - end - local lowercase = bor(i, 0x20) - -- Alpha is bit 2 - if lowercase >= b "a" and lowercase <= b "z" then - v = bor(v, lshift(1, 1)) - if lowercase <= b "f" then - v = bor(v, lshift(1, 2)) - v = bor(v, lshift(lowercase - b "a" + 10, 4)) - end - end - BYTE_CATEGORY[i] = v - end + local b = string.byte + for i = 0, 255 do + local v = 0 + -- Digit is bit 1 + if i >= b "0" and i <= b "9" then + v = bor(v, lshift(1, 0)) + v = bor(v, lshift(1, 2)) + v = bor(v, lshift(i - b "0", 4)) + end + local lowercase = bor(i, 0x20) + -- Alpha is bit 2 + if lowercase >= b "a" and lowercase <= b "z" then + v = bor(v, lshift(1, 1)) + if lowercase <= b "f" then + v = bor(v, lshift(1, 2)) + v = bor(v, lshift(lowercase - b "a" + 10, 4)) + end + end + BYTE_CATEGORY[i] = v + end end local function byte_is_hex(byte) - return band(BYTE_CATEGORY[byte], CATEGORY_HEX) ~= 0 + return band(BYTE_CATEGORY[byte], CATEGORY_HEX) ~= 0 end local function byte_is_alphanumeric(byte) - local category = BYTE_CATEGORY[byte] - return band(category, CATEGORY_ALPHANUM) ~= 0 + local category = BYTE_CATEGORY[byte] + return band(category, CATEGORY_ALPHANUM) ~= 0 end local function parse_hex(b) - return rshift(BYTE_CATEGORY[b], 4) + return rshift(BYTE_CATEGORY[b], 4) end local function percent_or_hex(v) - if v:sub(-1, -1) == "%" then - return tonumber(v:sub(1, -2)) / 100 * 255 - end - local x = tonumber(v) - if x > 255 then - return - end - return x + if v:sub(-1, -1) == "%" then + return tonumber(v:sub(1, -2)) / 100 * 255 + end + local x = tonumber(v) + if x > 255 then + return + end + return x end --- Determine whether to use black or white text -- Ref: https://stackoverflow.com/a/1855903/837964 -- https://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color local function color_is_bright(r, g, b) - -- Counting the perceptive luminance - human eye favors green color - local luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255 - if luminance > 0.5 then - return true -- Bright colors, black font - else - return false -- Dark colors, white font - end + -- Counting the perceptive luminance - human eye favors green color + local luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255 + if luminance > 0.5 then + return true -- Bright colors, black font + else + return false -- Dark colors, white font + end end -- https://gist.github.com/mjackson/5311256 local function hue_to_rgb(p, q, t) - if t < 0 then - t = t + 1 - end - if t > 1 then - t = t - 1 - end - if t < 1 / 6 then - return p + (q - p) * 6 * t - end - if t < 1 / 2 then - return q - end - if t < 2 / 3 then - return p + (q - p) * (2 / 3 - t) * 6 - end - return p + if t < 0 then + t = t + 1 + end + if t > 1 then + t = t - 1 + end + if t < 1 / 6 then + return p + (q - p) * 6 * t + end + if t < 1 / 2 then + return q + end + if t < 2 / 3 then + return p + (q - p) * (2 / 3 - t) * 6 + end + return p end local function hsl_to_rgb(h, s, l) - if h > 1 or s > 1 or l > 1 then - return - end - if s == 0 then - local r = l * 255 - return r, r, r - end - local q - if l < 0.5 then - q = l * (1 + s) - else - q = l + s - l * s - end - local p = 2 * l - q - return 255 * hue_to_rgb(p, q, h + 1 / 3), 255 * hue_to_rgb(p, q, h), 255 * hue_to_rgb(p, q, h - 1 / 3) + if h > 1 or s > 1 or l > 1 then + return + end + if s == 0 then + local r = l * 255 + return r, r, r + end + local q + if l < 0.5 then + q = l * (1 + s) + else + q = l + s - l * s + end + local p = 2 * l - q + return 255 * hue_to_rgb(p, q, h + 1 / 3), 255 * hue_to_rgb(p, q, h), 255 * hue_to_rgb(p, q, h - 1 / 3) end local function color_name_parser(line, i) - if i > 1 and byte_is_alphanumeric(line:byte(i - 1)) then + if i > 1 and byte_is_alphanumeric(line:byte(i - 1)) then + return + end + if #line < i + COLOR_NAME_MINLEN - 1 then + return + end + local prefix = COLOR_TRIE:longest_prefix(line, i) + if prefix then + -- Check if there is a letter here so as to disallow matching here. + -- Take the Blue out of Blueberry + -- Line end or non-letter. + local next_byte_index = i + #prefix + if #line >= next_byte_index and byte_is_alphanumeric(line:byte(next_byte_index)) then return - end - if #line < i + COLOR_NAME_MINLEN - 1 then - return - end - local prefix = COLOR_TRIE:longest_prefix(line, i) - if prefix then - -- Check if there is a letter here so as to disallow matching here. - -- Take the Blue out of Blueberry - -- Line end or non-letter. - local next_byte_index = i + #prefix - if #line >= next_byte_index and byte_is_alphanumeric(line:byte(next_byte_index)) then - return - end - return #prefix, COLOR_MAP[prefix] - end + end + return #prefix, COLOR_MAP[prefix] + end end local b_hash = ("#"):byte() local function rgb_hex_parser(line, i, minlen, maxlen) - if i > 1 and byte_is_alphanumeric(line:byte(i - 1)) then - return - end - if line:byte(i) ~= b_hash then - return - end - local j = i + 1 - if #line < j + minlen - 1 then - return - end - local n = j + maxlen - local alpha - local v = 0 - while j <= min(n, #line) do - local b = line:byte(j) - if not byte_is_hex(b) then - break - end - if j - i >= 7 then - alpha = parse_hex(b) + lshift(alpha or 0, 4) - else - v = parse_hex(b) + lshift(v, 4) - end - j = j + 1 - end - if #line >= j and byte_is_alphanumeric(line:byte(j)) then - return - end - local length = j - i - if length ~= 4 and length ~= 7 and length ~= 9 then - return - end - if alpha then - alpha = tonumber(alpha) / 255 - local r = floor(band(rshift(v, 16), 0xFF) * alpha) - local g = floor(band(rshift(v, 8), 0xFF) * alpha) - local b = floor(band(v, 0xFF) * alpha) - v = bor(lshift(r, 16), lshift(g, 8), b) - return 9, tohex(v, 6) - end - return length, line:sub(i + 1, i + length - 1) + if i > 1 and byte_is_alphanumeric(line:byte(i - 1)) then + return + end + if line:byte(i) ~= b_hash then + return + end + local j = i + 1 + if #line < j + minlen - 1 then + return + end + local n = j + maxlen + local alpha + local v = 0 + while j <= min(n, #line) do + local b = line:byte(j) + if not byte_is_hex(b) then + break + end + if j - i >= 7 then + alpha = parse_hex(b) + lshift(alpha or 0, 4) + else + v = parse_hex(b) + lshift(v, 4) + end + j = j + 1 + end + if #line >= j and byte_is_alphanumeric(line:byte(j)) then + return + end + local length = j - i + if length ~= 4 and length ~= 7 and length ~= 9 then + return + end + if alpha then + alpha = tonumber(alpha) / 255 + local r = floor(band(rshift(v, 16), 0xFF) * alpha) + local g = floor(band(rshift(v, 8), 0xFF) * alpha) + local b = floor(band(v, 0xFF) * alpha) + v = bor(lshift(r, 16), lshift(g, 8), b) + return 9, tohex(v, 6) + end + return length, line:sub(i + 1, i + length - 1) end local RGB_FUNCTION_TRIE = Trie { "0x" } local function rgb_0x_parser(line, i) - local prefix = RGB_FUNCTION_TRIE:longest_prefix(line:sub(i)) - if not prefix then - return - end - - local j = i + 2 - if #line < 10 then - return - end - local n = j + 8 - local alpha - local v = 0 - while j <= min(n, #line) do - local b = line:byte(j) - if not byte_is_hex(b) then - break - end - if j - i <= 3 then - alpha = parse_hex(b) + lshift(alpha or 0, 4) - else - v = parse_hex(b) + lshift(v, 4) - end - j = j + 1 - end - if #line >= j and byte_is_alphanumeric(line:byte(j)) then - return - end - local length = j - i - if length ~= 10 then - return - end - alpha = tonumber(alpha) / 255 - local r = floor(band(rshift(v, 16), 0xFF) * alpha) - local g = floor(band(rshift(v, 8), 0xFF) * alpha) - local b = floor(band(v, 0xFF) * alpha) - v = bor(lshift(r, 16), lshift(g, 8), b) - return length, tohex(v, 6) + local prefix = RGB_FUNCTION_TRIE:longest_prefix(line:sub(i)) + if not prefix then + return + end + + local j = i + 2 + if #line < 10 then + return + end + local n = j + 8 + local alpha + local v = 0 + while j <= min(n, #line) do + local b = line:byte(j) + if not byte_is_hex(b) then + break + end + if j - i <= 3 then + alpha = parse_hex(b) + lshift(alpha or 0, 4) + else + v = parse_hex(b) + lshift(v, 4) + end + j = j + 1 + end + if #line >= j and byte_is_alphanumeric(line:byte(j)) then + return + end + local length = j - i + if length ~= 10 then + return + end + alpha = tonumber(alpha) / 255 + local r = floor(band(rshift(v, 16), 0xFF) * alpha) + local g = floor(band(rshift(v, 8), 0xFF) * alpha) + local b = floor(band(v, 0xFF) * alpha) + v = bor(lshift(r, 16), lshift(g, 8), b) + return length, tohex(v, 6) end -- TODO consider removing the regexes here @@ -295,162 +295,159 @@ end -- Things like pumblend might be useful here. local css_fn = {} do - local CSS_RGB_FN_MINIMUM_LENGTH = #"rgb(0,0,0)" - 1 - local CSS_RGBA_FN_MINIMUM_LENGTH = #"rgba(0,0,0,0)" - 1 - local CSS_HSL_FN_MINIMUM_LENGTH = #"hsl(0,0%,0%)" - 1 - local CSS_HSLA_FN_MINIMUM_LENGTH = #"hsla(0,0%,0%,0)" - 1 - function css_fn.rgb(line, i) - if #line < i + CSS_RGB_FN_MINIMUM_LENGTH then - return - end - local r, g, b, match_end = line:sub(i):match "^rgb%(%s*(%d+%%?)%s*,%s*(%d+%%?)%s*,%s*(%d+%%?)%s*%)()" - if not match_end then - return - end - r = percent_or_hex(r) - if not r then - return - end - g = percent_or_hex(g) - if not g then - return - end - b = percent_or_hex(b) - if not b then - return - end - local rgb_hex = tohex(bor(lshift(r, 16), lshift(g, 8), b), 6) - return match_end - 1, rgb_hex - end - function css_fn.hsl(line, i) - if #line < i + CSS_HSL_FN_MINIMUM_LENGTH then - return - end - local h, s, l, match_end = line:sub(i):match "^hsl%(%s*(%d+)%s*,%s*(%d+)%%%s*,%s*(%d+)%%%s*%)()" - if not match_end then - return - end - h = tonumber(h) - if h > 360 then - return - end - s = tonumber(s) - if s > 100 then - return - end - l = tonumber(l) - if l > 100 then - return - end - local r, g, b = hsl_to_rgb(h / 360, s / 100, l / 100) - if r == nil or g == nil or b == nil then - return - end - local rgb_hex = tohex(bor(lshift(floor(r), 16), lshift(floor(g), 8), floor(b)), 6) - return match_end - 1, rgb_hex - end - function css_fn.rgba(line, i) - if #line < i + CSS_RGBA_FN_MINIMUM_LENGTH then - return - end - local r, g, b, a, match_end = line - :sub(i) - :match "^rgba%(%s*(%d+%%?)%s*,%s*(%d+%%?)%s*,%s*(%d+%%?)%s*,%s*([.%d]+)%s*%)()" - if not match_end then - return - end - a = tonumber(a) - if not a or a > 1 then - return - end - r = percent_or_hex(r) - if not r then - return - end - g = percent_or_hex(g) - if not g then - return - end - b = percent_or_hex(b) - if not b then - return - end - local rgb_hex = tohex(bor(lshift(floor(r * a), 16), lshift(floor(g * a), 8), floor(b * a)), 6) - return match_end - 1, rgb_hex - end - function css_fn.hsla(line, i) - if #line < i + CSS_HSLA_FN_MINIMUM_LENGTH then - return - end - local h, s, l, a, match_end = line - :sub(i) - :match "^hsla%(%s*(%d+)%s*,%s*(%d+)%%%s*,%s*(%d+)%%%s*,%s*([.%d]+)%s*%)()" - if not match_end then - return - end - a = tonumber(a) - if not a or a > 1 then - return - end - h = tonumber(h) - if h > 360 then - return - end - s = tonumber(s) - if s > 100 then - return - end - l = tonumber(l) - if l > 100 then - return - end - local r, g, b = hsl_to_rgb(h / 360, s / 100, l / 100) - if r == nil or g == nil or b == nil then - return - end - local rgb_hex = tohex(bor(lshift(floor(r * a), 16), lshift(floor(g * a), 8), floor(b * a)), 6) - return match_end - 1, rgb_hex - end + local CSS_RGB_FN_MINIMUM_LENGTH = #"rgb(0,0,0)" - 1 + local CSS_RGBA_FN_MINIMUM_LENGTH = #"rgba(0,0,0,0)" - 1 + local CSS_HSL_FN_MINIMUM_LENGTH = #"hsl(0,0%,0%)" - 1 + local CSS_HSLA_FN_MINIMUM_LENGTH = #"hsla(0,0%,0%,0)" - 1 + function css_fn.rgb(line, i) + if #line < i + CSS_RGB_FN_MINIMUM_LENGTH then + return + end + local r, g, b, match_end = line:sub(i):match "^rgb%(%s*(%d+%%?)%s*,%s*(%d+%%?)%s*,%s*(%d+%%?)%s*%)()" + if not match_end then + return + end + r = percent_or_hex(r) + if not r then + return + end + g = percent_or_hex(g) + if not g then + return + end + b = percent_or_hex(b) + if not b then + return + end + local rgb_hex = tohex(bor(lshift(r, 16), lshift(g, 8), b), 6) + return match_end - 1, rgb_hex + end + function css_fn.hsl(line, i) + if #line < i + CSS_HSL_FN_MINIMUM_LENGTH then + return + end + local h, s, l, match_end = line:sub(i):match "^hsl%(%s*(%d+)%s*,%s*(%d+)%%%s*,%s*(%d+)%%%s*%)()" + if not match_end then + return + end + h = tonumber(h) + if h > 360 then + return + end + s = tonumber(s) + if s > 100 then + return + end + l = tonumber(l) + if l > 100 then + return + end + local r, g, b = hsl_to_rgb(h / 360, s / 100, l / 100) + if r == nil or g == nil or b == nil then + return + end + local rgb_hex = tohex(bor(lshift(floor(r), 16), lshift(floor(g), 8), floor(b)), 6) + return match_end - 1, rgb_hex + end + function css_fn.rgba(line, i) + if #line < i + CSS_RGBA_FN_MINIMUM_LENGTH then + return + end + local r, g, b, a, match_end = + line:sub(i):match "^rgba%(%s*(%d+%%?)%s*,%s*(%d+%%?)%s*,%s*(%d+%%?)%s*,%s*([.%d]+)%s*%)()" + if not match_end then + return + end + a = tonumber(a) + if not a or a > 1 then + return + end + r = percent_or_hex(r) + if not r then + return + end + g = percent_or_hex(g) + if not g then + return + end + b = percent_or_hex(b) + if not b then + return + end + local rgb_hex = tohex(bor(lshift(floor(r * a), 16), lshift(floor(g * a), 8), floor(b * a)), 6) + return match_end - 1, rgb_hex + end + function css_fn.hsla(line, i) + if #line < i + CSS_HSLA_FN_MINIMUM_LENGTH then + return + end + local h, s, l, a, match_end = line:sub(i):match "^hsla%(%s*(%d+)%s*,%s*(%d+)%%%s*,%s*(%d+)%%%s*,%s*([.%d]+)%s*%)()" + if not match_end then + return + end + a = tonumber(a) + if not a or a > 1 then + return + end + h = tonumber(h) + if h > 360 then + return + end + s = tonumber(s) + if s > 100 then + return + end + l = tonumber(l) + if l > 100 then + return + end + local r, g, b = hsl_to_rgb(h / 360, s / 100, l / 100) + if r == nil or g == nil or b == nil then + return + end + local rgb_hex = tohex(bor(lshift(floor(r * a), 16), lshift(floor(g * a), 8), floor(b * a)), 6) + return match_end - 1, rgb_hex + end end local css_function_parser, rgb_function_parser, hsl_function_parser do - local CSS_FUNCTION_TRIE = Trie { "rgb", "rgba", "hsl", "hsla" } - local RGB_FUNCTION_TRIE = Trie { "rgb", "rgba" } - local HSL_FUNCTION_TRIE = Trie { "hsl", "hsla" } - css_function_parser = function(line, i) - local prefix = CSS_FUNCTION_TRIE:longest_prefix(line:sub(i)) - if prefix then - return css_fn[prefix](line, i) - end - end - rgb_function_parser = function(line, i) - local prefix = RGB_FUNCTION_TRIE:longest_prefix(line:sub(i)) - if prefix then - return css_fn[prefix](line, i) - end - end - hsl_function_parser = function(line, i) - local prefix = HSL_FUNCTION_TRIE:longest_prefix(line:sub(i)) - if prefix then - return css_fn[prefix](line, i) - end - end + local CSS_FUNCTION_TRIE = Trie { "rgb", "rgba", "hsl", "hsla" } + local RGB_FUNCTION_TRIE = Trie { "rgb", "rgba" } + local HSL_FUNCTION_TRIE = Trie { "hsl", "hsla" } + css_function_parser = function(line, i) + local prefix = CSS_FUNCTION_TRIE:longest_prefix(line:sub(i)) + if prefix then + return css_fn[prefix](line, i) + end + end + rgb_function_parser = function(line, i) + local prefix = RGB_FUNCTION_TRIE:longest_prefix(line:sub(i)) + if prefix then + return css_fn[prefix](line, i) + end + end + hsl_function_parser = function(line, i) + local prefix = HSL_FUNCTION_TRIE:longest_prefix(line:sub(i)) + if prefix then + return css_fn[prefix](line, i) + end + end end local function compile_matcher(matchers) - local parse_fn = matchers[1] - for j = 2, #matchers do - local old_parse_fn = parse_fn - local new_parse_fn = matchers[j] - parse_fn = function(line, i) - local length, rgb_hex = new_parse_fn(line, i) - if length then - return length, rgb_hex - end - return old_parse_fn(line, i) - end - end - return parse_fn + local parse_fn = matchers[1] + for j = 2, #matchers do + local old_parse_fn = parse_fn + local new_parse_fn = matchers[j] + parse_fn = function(line, i) + local length, rgb_hex = new_parse_fn(line, i) + if length then + return length, rgb_hex + end + return old_parse_fn(line, i) + end + end + return parse_fn end --- Default namespace used in `highlight_buffer` and `attach_to_buffer`. @@ -460,131 +457,131 @@ end local DEFAULT_NAMESPACE = nvim.create_namespace "colorizer" local HIGHLIGHT_NAME_PREFIX = "colorizer" local HIGHLIGHT_MODE_NAMES = { - background = "mb", - foreground = "mf", - virtualtext = "mv", + background = "mb", + foreground = "mf", + virtualtext = "mv", } local HIGHLIGHT_CACHE = {} --- Make a deterministic name for a highlight given these attributes local function make_highlight_name(rgb, mode) - return table.concat({ HIGHLIGHT_NAME_PREFIX, HIGHLIGHT_MODE_NAMES[mode], rgb }, "_") + return table.concat({ HIGHLIGHT_NAME_PREFIX, HIGHLIGHT_MODE_NAMES[mode], rgb }, "_") end local function create_highlight(rgb_hex, options) - local mode = options.mode or "background" - -- TODO validate rgb format? - rgb_hex = rgb_hex:lower() - local cache_key = table.concat({ HIGHLIGHT_MODE_NAMES[mode], rgb_hex }, "_") - local highlight_name = HIGHLIGHT_CACHE[cache_key] - -- 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, mode) - if mode == "foreground" then - nvim.ex.highlight(highlight_name, "guifg=#" .. rgb_hex) + local mode = options.mode or "background" + -- TODO validate rgb format? + rgb_hex = rgb_hex:lower() + local cache_key = table.concat({ HIGHLIGHT_MODE_NAMES[mode], rgb_hex }, "_") + local highlight_name = HIGHLIGHT_CACHE[cache_key] + -- 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, mode) + if mode == "foreground" then + nvim.ex.highlight(highlight_name, "guifg=#" .. rgb_hex) + else + local r, g, b = rgb_hex:sub(1, 2), rgb_hex:sub(3, 4), rgb_hex:sub(5, 6) + r, g, b = tonumber(r, 16), tonumber(g, 16), tonumber(b, 16) + local fg_color + if color_is_bright(r, g, b) then + fg_color = "Black" else - local r, g, b = rgb_hex:sub(1, 2), rgb_hex:sub(3, 4), rgb_hex:sub(5, 6) - r, g, b = tonumber(r, 16), tonumber(g, 16), tonumber(b, 16) - local fg_color - if color_is_bright(r, g, b) then - fg_color = "Black" - else - fg_color = "White" - end - nvim.ex.highlight(highlight_name, "guifg=" .. fg_color, "guibg=#" .. rgb_hex) + fg_color = "White" end - HIGHLIGHT_CACHE[cache_key] = highlight_name - end - return highlight_name + nvim.ex.highlight(highlight_name, "guifg=" .. fg_color, "guibg=#" .. rgb_hex) + end + HIGHLIGHT_CACHE[cache_key] = highlight_name + end + return highlight_name end local MATCHER_CACHE = {} local function make_matcher(options) - local enable_names = options.css or options.names - local enable_RGB = options.css or options.RGB - local enable_RRGGBB = options.css or options.RRGGBB - local enable_RRGGBBAA = options.css or options.RRGGBBAA - local enable_AARRGGBB = options.AARRGGBB - local enable_rgb = options.css or options.css_fns or options.rgb_fn - local enable_hsl = options.css or options.css_fns or options.hsl_fn - - local matcher_key = bor( - lshift(enable_names and 1 or 0, 0), - lshift(enable_RGB and 1 or 0, 1), - lshift(enable_RRGGBB and 1 or 0, 2), - lshift(enable_RRGGBBAA and 1 or 0, 3), - lshift(enable_rgb and 1 or 0, 4), - lshift(enable_hsl and 1 or 0, 5) - ) - - if matcher_key == 0 then - return - end - - local loop_parse_fn = MATCHER_CACHE[matcher_key] - if loop_parse_fn then - return loop_parse_fn - end - - local loop_matchers = {} - if enable_names then - table.insert(loop_matchers, color_name_parser) - end - if enable_AARRGGBB then - table.insert(loop_matchers, rgb_0x_parser) - end - do - local valid_lengths = { [3] = enable_RGB, [6] = enable_RRGGBB, [8] = enable_RRGGBBAA } - local minlen, maxlen - for k, v in pairs(valid_lengths) do - if v then - minlen = minlen and min(k, minlen) or k - maxlen = maxlen and max(k, maxlen) or k - end - end - if minlen then - table.insert(loop_matchers, function(line, i) - local length, rgb_hex = rgb_hex_parser(line, i, minlen, maxlen) - if length and valid_lengths[length - 1] then - return length, rgb_hex - end - end) - end - end - if enable_rgb and enable_hsl then - table.insert(loop_matchers, css_function_parser) - elseif enable_rgb then - table.insert(loop_matchers, rgb_function_parser) - elseif enable_hsl then - table.insert(loop_matchers, hsl_function_parser) - end - loop_parse_fn = compile_matcher(loop_matchers) - MATCHER_CACHE[matcher_key] = loop_parse_fn - return loop_parse_fn + local enable_names = options.css or options.names + local enable_RGB = options.css or options.RGB + local enable_RRGGBB = options.css or options.RRGGBB + local enable_RRGGBBAA = options.css or options.RRGGBBAA + local enable_AARRGGBB = options.AARRGGBB + local enable_rgb = options.css or options.css_fns or options.rgb_fn + local enable_hsl = options.css or options.css_fns or options.hsl_fn + + local matcher_key = bor( + lshift(enable_names and 1 or 0, 0), + lshift(enable_RGB and 1 or 0, 1), + lshift(enable_RRGGBB and 1 or 0, 2), + lshift(enable_RRGGBBAA and 1 or 0, 3), + lshift(enable_rgb and 1 or 0, 4), + lshift(enable_hsl and 1 or 0, 5) + ) + + if matcher_key == 0 then + return + end + + local loop_parse_fn = MATCHER_CACHE[matcher_key] + if loop_parse_fn then + return loop_parse_fn + end + + local loop_matchers = {} + if enable_names then + table.insert(loop_matchers, color_name_parser) + end + if enable_AARRGGBB then + table.insert(loop_matchers, rgb_0x_parser) + end + do + local valid_lengths = { [3] = enable_RGB, [6] = enable_RRGGBB, [8] = enable_RRGGBBAA } + local minlen, maxlen + for k, v in pairs(valid_lengths) do + if v then + minlen = minlen and min(k, minlen) or k + maxlen = maxlen and max(k, maxlen) or k + end + end + if minlen then + table.insert(loop_matchers, function(line, i) + local length, rgb_hex = rgb_hex_parser(line, i, minlen, maxlen) + if length and valid_lengths[length - 1] then + return length, rgb_hex + end + end) + end + end + if enable_rgb and enable_hsl then + table.insert(loop_matchers, css_function_parser) + elseif enable_rgb then + table.insert(loop_matchers, rgb_function_parser) + elseif enable_hsl then + table.insert(loop_matchers, hsl_function_parser) + end + loop_parse_fn = compile_matcher(loop_matchers) + MATCHER_CACHE[matcher_key] = loop_parse_fn + return loop_parse_fn end local function add_highlight(options, buf, ns, data) - for linenr, hls in pairs(data) do - if vim.tbl_contains({ "foreground", "background" }, options.mode) then - for _, hl in ipairs(hls) do - nvim_buf_add_highlight(buf, ns, hl.name, linenr, hl.range[1], hl.range[2]) - end - elseif options.mode == "virtualtext" then - local chunks = {} - for _, hl in ipairs(hls) do - table.insert(chunks, { options.virtualtext, hl.name }) - end - nvim_buf_set_virtual_text(buf, ns, linenr, chunks, {}) - end - end + for linenr, hls in pairs(data) do + if vim.tbl_contains({ "foreground", "background" }, options.mode) then + for _, hl in ipairs(hls) do + nvim_buf_add_highlight(buf, ns, hl.name, linenr, hl.range[1], hl.range[2]) + end + elseif options.mode == "virtualtext" then + local chunks = {} + for _, hl in ipairs(hls) do + table.insert(chunks, { options.virtualtext, hl.name }) + end + nvim_buf_set_virtual_text(buf, ns, linenr, chunks, {}) + end + end end --[[-- Highlight the buffer region. @@ -599,30 +596,30 @@ buffer `buf` and attach it to the namespace `ns`. @see setup ]] local function highlight_buffer(buf, ns, lines, line_start, options) - -- TODO do I have to put this here? - initialize_trie() - ns = ns or DEFAULT_NAMESPACE - local loop_parse_fn = make_matcher(options) - local data = {} - local mode = options.mode == "background" and { mode = "background" } or { mode = "foreground" } - for current_linenum, line in ipairs(lines) do - current_linenum = current_linenum - 1 + line_start - -- Upvalues are options and current_linenum - local i = 1 - while i < #line do - local length, rgb_hex = loop_parse_fn(line, i) - if length then - local name = create_highlight(rgb_hex, mode) - local d = data[current_linenum] or {} - table.insert(d, { name = name, range = { i - 1, i + length - 1 } }) - data[current_linenum] = d - i = i + length - else - i = i + 1 - end + -- TODO do I have to put this here? + initialize_trie() + ns = ns or DEFAULT_NAMESPACE + local loop_parse_fn = make_matcher(options) + local data = {} + local mode = options.mode == "background" and { mode = "background" } or { mode = "foreground" } + for current_linenum, line in ipairs(lines) do + current_linenum = current_linenum - 1 + line_start + -- Upvalues are options and current_linenum + local i = 1 + while i < #line do + local length, rgb_hex = loop_parse_fn(line, i) + if length then + local name = create_highlight(rgb_hex, mode) + local d = data[current_linenum] or {} + table.insert(d, { name = name, range = { i - 1, i + length - 1 } }) + data[current_linenum] = d + i = i + length + else + i = i + 1 end - end - add_highlight(options, buf, ns, data) + end + end + add_highlight(options, buf, ns, data) end --- @@ -630,36 +627,36 @@ end --- local SETUP_SETTINGS = { - exclusions = {}, - default_options = DEFAULT_OPTIONS, + exclusions = {}, + default_options = DEFAULT_OPTIONS, } local BUFFER_OPTIONS = {} local FILETYPE_OPTIONS = {} local function rehighlight_buffer(buf, options) - local ns = DEFAULT_NAMESPACE - if buf == 0 or buf == nil then - buf = nvim_get_current_buf() - end - assert(options) - nvim_buf_clear_namespace(buf, ns, 0, -1) - local lines = nvim_buf_get_lines(buf, 0, -1, true) - highlight_buffer(buf, ns, lines, 0, options) + local ns = DEFAULT_NAMESPACE + if buf == 0 or buf == nil then + buf = nvim_get_current_buf() + end + assert(options) + nvim_buf_clear_namespace(buf, ns, 0, -1) + local lines = nvim_buf_get_lines(buf, 0, -1, true) + highlight_buffer(buf, ns, lines, 0, options) end local function new_buffer_options(buf) - local filetype = nvim.buf_get_option(buf, "filetype") - return FILETYPE_OPTIONS[filetype] or SETUP_SETTINGS.default_options + local filetype = nvim.buf_get_option(buf, "filetype") + return FILETYPE_OPTIONS[filetype] or SETUP_SETTINGS.default_options end --- Check if attached to a buffer. -- @tparam[opt=0|nil] integer buf A value of 0 implies the current buffer. -- @return true if attached to the buffer, false otherwise. local function is_buffer_attached(buf) - if buf == 0 or buf == nil then - buf = nvim_get_current_buf() - end - return BUFFER_OPTIONS[buf] ~= nil + if buf == 0 or buf == nil then + buf = nvim_get_current_buf() + end + return BUFFER_OPTIONS[buf] ~= nil end --- Attach to a buffer and continuously highlight changes. @@ -667,45 +664,45 @@ end -- @param[opt] options Configuration options as described in `setup` -- @see setup local function attach_to_buffer(buf, options) - if buf == 0 or buf == nil then - buf = nvim_get_current_buf() - end - local already_attached = BUFFER_OPTIONS[buf] ~= nil - local ns = DEFAULT_NAMESPACE - if not options then - options = new_buffer_options(buf) - end - BUFFER_OPTIONS[buf] = options - rehighlight_buffer(buf, options) - if already_attached then - return - end - -- send_buffer: true doesn't actually do anything in Lua (yet) - nvim.buf_attach(buf, false, { - on_lines = function(event_type, buf, changed_tick, firstline, lastline, new_lastline) - -- This is used to signal stopping the handler highlights - if not BUFFER_OPTIONS[buf] then - return true - end - nvim_buf_clear_namespace(buf, ns, firstline, new_lastline) - local lines = nvim_buf_get_lines(buf, firstline, new_lastline, false) - highlight_buffer(buf, ns, lines, firstline, BUFFER_OPTIONS[buf]) - end, - on_detach = function() - BUFFER_OPTIONS[buf] = nil - end, - }) + if buf == 0 or buf == nil then + buf = nvim_get_current_buf() + end + local already_attached = BUFFER_OPTIONS[buf] ~= nil + local ns = DEFAULT_NAMESPACE + if not options then + options = new_buffer_options(buf) + end + BUFFER_OPTIONS[buf] = options + rehighlight_buffer(buf, options) + if already_attached then + return + end + -- send_buffer: true doesn't actually do anything in Lua (yet) + nvim.buf_attach(buf, false, { + on_lines = function(event_type, buf, changed_tick, firstline, lastline, new_lastline) + -- This is used to signal stopping the handler highlights + if not BUFFER_OPTIONS[buf] then + return true + end + nvim_buf_clear_namespace(buf, ns, firstline, new_lastline) + local lines = nvim_buf_get_lines(buf, firstline, new_lastline, false) + highlight_buffer(buf, ns, lines, firstline, BUFFER_OPTIONS[buf]) + end, + on_detach = function() + BUFFER_OPTIONS[buf] = nil + end, + }) end --- Stop highlighting the current buffer. -- @tparam[opt=0|nil] integer buf A value of 0 or nil implies the current buffer. -- @tparam[opt=DEFAULT_NAMESPACE] integer ns the namespace id. local function detach_from_buffer(buf, ns) - if buf == 0 or buf == nil then - buf = nvim_get_current_buf() - end - nvim_buf_clear_namespace(buf, ns or DEFAULT_NAMESPACE, 0, -1) - BUFFER_OPTIONS[buf] = nil + if buf == 0 or buf == nil then + buf = nvim_get_current_buf() + end + nvim_buf_clear_namespace(buf, ns or DEFAULT_NAMESPACE, 0, -1) + BUFFER_OPTIONS[buf] = nil end --- Easy to use function if you want the full setup without fine grained control. @@ -728,84 +725,84 @@ end -- @tparam[opt] {[string]=string} default_options Default options to apply for the filetypes enable. -- @usage require'colorizer'.setup() local function setup(filetypes, user_default_options) - if not nvim.o.termguicolors then - nvim.err_writeln "&termguicolors must be set" + if not nvim.o.termguicolors then + nvim.err_writeln "&termguicolors must be set" + return + end + FILETYPE_OPTIONS = {} + SETUP_SETTINGS = { + exclusions = {}, + default_options = merge(DEFAULT_OPTIONS, user_default_options or {}), + } + -- Initialize this AFTER setting COLOR_NAME_SETTINGS + initialize_trie() + function COLORIZER_SETUP_HOOK() + local filetype = nvim.bo.filetype + if SETUP_SETTINGS.exclusions[filetype] then return - end - FILETYPE_OPTIONS = {} - SETUP_SETTINGS = { - exclusions = {}, - default_options = merge(DEFAULT_OPTIONS, user_default_options or {}), - } - -- Initialize this AFTER setting COLOR_NAME_SETTINGS - initialize_trie() - function COLORIZER_SETUP_HOOK() - local filetype = nvim.bo.filetype - if SETUP_SETTINGS.exclusions[filetype] then - return + end + local options = FILETYPE_OPTIONS[filetype] or SETUP_SETTINGS.default_options + attach_to_buffer(nvim_get_current_buf(), options) + end + nvim.ex.augroup "ColorizerSetup" + nvim.ex.autocmd_() + if not filetypes then + nvim.ex.autocmd "FileType * lua COLORIZER_SETUP_HOOK()" + else + for k, v in pairs(filetypes) do + local filetype + local options = SETUP_SETTINGS.default_options + if type(k) == "string" then + filetype = k + if type(v) ~= "table" then + nvim.err_writeln("colorizer: Invalid option type for filetype " .. filetype) + else + options = merge(SETUP_SETTINGS.default_options, v) + assert( + HIGHLIGHT_MODE_NAMES[options.mode or "background"], + "colorizer: Invalid mode: " .. tostring(options.mode) + ) + end + else + filetype = v end - local options = FILETYPE_OPTIONS[filetype] or SETUP_SETTINGS.default_options - attach_to_buffer(nvim_get_current_buf(), options) - end - nvim.ex.augroup "ColorizerSetup" - nvim.ex.autocmd_() - if not filetypes then - nvim.ex.autocmd "FileType * lua COLORIZER_SETUP_HOOK()" - else - for k, v in pairs(filetypes) do - local filetype - local options = SETUP_SETTINGS.default_options - if type(k) == "string" then - filetype = k - if type(v) ~= "table" then - nvim.err_writeln("colorizer: Invalid option type for filetype " .. filetype) - else - options = merge(SETUP_SETTINGS.default_options, v) - assert( - HIGHLIGHT_MODE_NAMES[options.mode or "background"], - "colorizer: Invalid mode: " .. tostring(options.mode) - ) - end - else - filetype = v - end - -- Exclude - if filetype:sub(1, 1) == "!" then - SETUP_SETTINGS.exclusions[filetype:sub(2)] = true - else - FILETYPE_OPTIONS[filetype] = options - -- TODO What's the right mode for this? BufEnter? - nvim.ex.autocmd("FileType", filetype, "lua COLORIZER_SETUP_HOOK()") - end + -- Exclude + if filetype:sub(1, 1) == "!" then + SETUP_SETTINGS.exclusions[filetype:sub(2)] = true + else + FILETYPE_OPTIONS[filetype] = options + -- TODO What's the right mode for this? BufEnter? + nvim.ex.autocmd("FileType", filetype, "lua COLORIZER_SETUP_HOOK()") end - end - nvim.ex.augroup "END" + end + end + nvim.ex.augroup "END" end --- Reload all of the currently active highlighted buffers. local function reload_all_buffers() - for buf, buffer_options in pairs(BUFFER_OPTIONS) do - attach_to_buffer(buf) - end + for buf, buffer_options in pairs(BUFFER_OPTIONS) do + attach_to_buffer(buf) + end end --- Return the currently active buffer options. -- @tparam[opt=0|nil] integer buf A value of 0 or nil implies the current buffer. local function get_buffer_options(buf) - if buf == 0 or buf == nil then - buf = nvim_get_current_buf() - end - return merge({}, BUFFER_OPTIONS[buf]) + if buf == 0 or buf == nil then + buf = nvim_get_current_buf() + end + return merge({}, BUFFER_OPTIONS[buf]) end --- @export return { - DEFAULT_NAMESPACE = DEFAULT_NAMESPACE, - setup = setup, - is_buffer_attached = is_buffer_attached, - attach_to_buffer = attach_to_buffer, - detach_from_buffer = detach_from_buffer, - highlight_buffer = highlight_buffer, - reload_all_buffers = reload_all_buffers, - get_buffer_options = get_buffer_options, + DEFAULT_NAMESPACE = DEFAULT_NAMESPACE, + setup = setup, + is_buffer_attached = is_buffer_attached, + attach_to_buffer = attach_to_buffer, + detach_from_buffer = detach_from_buffer, + highlight_buffer = highlight_buffer, + reload_all_buffers = reload_all_buffers, + get_buffer_options = get_buffer_options, } diff --git a/lua/colorizer/nvim.lua b/lua/colorizer/nvim.lua index c51d2ca..3d83bd0 100644 --- a/lua/colorizer/nvim.lua +++ b/lua/colorizer/nvim.lua @@ -3,45 +3,102 @@ -- Equivalent to `echo vim.inspect(...)` local function nvim_print(...) - if select("#", ...) == 1 then - vim.api.nvim_out_write(vim.inspect((...))) - else - vim.api.nvim_out_write(vim.inspect {...}) - end - vim.api.nvim_out_write("\n") + if select("#", ...) == 1 then + vim.api.nvim_out_write(vim.inspect((...))) + else + vim.api.nvim_out_write(vim.inspect { ... }) + end + vim.api.nvim_out_write "\n" end --- Equivalent to `echo` EX command local function nvim_echo(...) - for i = 1, select("#", ...) do - local part = select(i, ...) - vim.api.nvim_out_write(tostring(part)) - -- vim.api.nvim_out_write("\n") - vim.api.nvim_out_write(" ") - end - vim.api.nvim_out_write("\n") + for i = 1, select("#", ...) do + local part = select(i, ...) + vim.api.nvim_out_write(tostring(part)) + -- vim.api.nvim_out_write("\n") + vim.api.nvim_out_write " " + end + vim.api.nvim_out_write "\n" end local window_options = { - arab = true; arabic = true; breakindent = true; breakindentopt = true; - bri = true; briopt = true; cc = true; cocu = true; - cole = true; colorcolumn = true; concealcursor = true; conceallevel = true; - crb = true; cuc = true; cul = true; cursorbind = true; - cursorcolumn = true; cursorline = true; diff = true; fcs = true; - fdc = true; fde = true; fdi = true; fdl = true; - fdm = true; fdn = true; fdt = true; fen = true; - fillchars = true; fml = true; fmr = true; foldcolumn = true; - foldenable = true; foldexpr = true; foldignore = true; foldlevel = true; - foldmarker = true; foldmethod = true; foldminlines = true; foldnestmax = true; - foldtext = true; lbr = true; lcs = true; linebreak = true; - list = true; listchars = true; nu = true; number = true; - numberwidth = true; nuw = true; previewwindow = true; pvw = true; - relativenumber = true; rightleft = true; rightleftcmd = true; rl = true; - rlc = true; rnu = true; scb = true; scl = true; - scr = true; scroll = true; scrollbind = true; signcolumn = true; - spell = true; statusline = true; stl = true; wfh = true; - wfw = true; winbl = true; winblend = true; winfixheight = true; - winfixwidth = true; winhighlight = true; winhl = true; wrap = true; + arab = true, + arabic = true, + breakindent = true, + breakindentopt = true, + bri = true, + briopt = true, + cc = true, + cocu = true, + cole = true, + colorcolumn = true, + concealcursor = true, + conceallevel = true, + crb = true, + cuc = true, + cul = true, + cursorbind = true, + cursorcolumn = true, + cursorline = true, + diff = true, + fcs = true, + fdc = true, + fde = true, + fdi = true, + fdl = true, + fdm = true, + fdn = true, + fdt = true, + fen = true, + fillchars = true, + fml = true, + fmr = true, + foldcolumn = true, + foldenable = true, + foldexpr = true, + foldignore = true, + foldlevel = true, + foldmarker = true, + foldmethod = true, + foldminlines = true, + foldnestmax = true, + foldtext = true, + lbr = true, + lcs = true, + linebreak = true, + list = true, + listchars = true, + nu = true, + number = true, + numberwidth = true, + nuw = true, + previewwindow = true, + pvw = true, + relativenumber = true, + rightleft = true, + rightleftcmd = true, + rl = true, + rlc = true, + rnu = true, + scb = true, + scl = true, + scr = true, + scroll = true, + scrollbind = true, + signcolumn = true, + spell = true, + statusline = true, + stl = true, + wfh = true, + wfw = true, + winbl = true, + winblend = true, + winfixheight = true, + winfixwidth = true, + winhighlight = true, + winhl = true, + wrap = true, } -- `nvim.$method(...)` redirects to `nvim.api.nvim_$method(...)` @@ -51,143 +108,144 @@ local window_options = { -- `nvim.echo(...)` is approximately `echo table.concat({...}, '\n')` -- Both methods cache the inital lookup in the metatable, but there is a small overhead regardless. return setmetatable({ - print = nvim_print; - echo = nvim_echo; - fn = setmetatable({}, { - __index = function(self, k) - local mt = getmetatable(self) - local x = mt[k] - if x ~= nil then - return x - end - local f = function(...) return vim.api.nvim_call_function(k, {...}) end - mt[k] = f - return f - end - }); - buf = setmetatable({ - }, { - __index = function(self, k) - local mt = getmetatable(self) - local x = mt[k] - if x ~= nil then return x end - local f - if k == 'line' then - f = function() - local pos = vim.api.nvim_win_get_cursor(0) - return vim.api.nvim_buf_get_lines(0, pos[1]-1, pos[1], 'line')[1] - end - elseif k == 'nr' then - f = vim.api.nvim_get_current_buf - end - mt[k] = f - return f - end - }); - ex = setmetatable({}, { - __index = function(self, k) - local mt = getmetatable(self) - local x = mt[k] - if x ~= nil then - return x - end - local command = k:gsub("_$", "!") - local f = function(...) - return vim.api.nvim_command(table.concat(vim.tbl_flatten {command, ...}, " ")) - end - mt[k] = f - return f - end - }); - g = setmetatable({}, { - __index = function(_, k) - return vim.api.nvim_get_var(k) - end; - __newindex = function(_, k, v) - if v == nil then - return vim.api.nvim_del_var(k) - else - return vim.api.nvim_set_var(k, v) - end - end; - }); - v = setmetatable({}, { - __index = function(_, k) - return vim.api.nvim_get_vvar(k) - end; - __newindex = function(_, k, v) - return vim.api.nvim_set_vvar(k, v) - end - }); - b = setmetatable({}, { - __index = function(_, k) - return vim.api.nvim_buf_get_var(0, k) - end; - __newindex = function(_, k, v) - if v == nil then - return vim.api.nvim_buf_del_var(0, k) - else - return vim.api.nvim_buf_set_var(0, k, v) - end - end - }); - w = setmetatable({}, { - __index = function(_, k) - return vim.api.nvim_win_get_var(0, k) - end; - __newindex = function(_, k, v) - if v == nil then - return vim.api.nvim_win_del_var(0, k) - else - return vim.api.nvim_win_set_var(0, k, v) - end - end - }); - o = setmetatable({}, { - __index = function(_, k) - return vim.api.nvim_get_option(k) - end; - __newindex = function(_, k, v) - return vim.api.nvim_set_option(k, v) - end - }); - -- TODO add warning if you try to use a window option here? - bo = setmetatable({}, { - __index = function(_, k) - return vim.api.nvim_buf_get_option(0, k) - end; - __newindex = function(_, k, v) - return vim.api.nvim_buf_set_option(0, k, v) - end - }); - wo = setmetatable({}, { - __index = function(_, k) - return vim.api.nvim_win_get_option(0, k) - end; - __newindex = function(_, k, v) - -- passing v == nil will clear the value, just like above. - return vim.api.nvim_win_set_option(0, k, v) - end - }); - env = setmetatable({}, { - __index = function(_, k) - return vim.api.nvim_call_function('getenv', {k}) - end; - __newindex = function(_, k, v) - return vim.api.nvim_call_function('setenv', {k, v}) - end - }); + print = nvim_print, + echo = nvim_echo, + fn = setmetatable({}, { + __index = function(self, k) + local mt = getmetatable(self) + local x = mt[k] + if x ~= nil then + return x + end + local f = function(...) + return vim.api.nvim_call_function(k, { ... }) + end + mt[k] = f + return f + end, + }), + buf = setmetatable({}, { + __index = function(self, k) + local mt = getmetatable(self) + local x = mt[k] + if x ~= nil then + return x + end + local f + if k == "line" then + f = function() + local pos = vim.api.nvim_win_get_cursor(0) + return vim.api.nvim_buf_get_lines(0, pos[1] - 1, pos[1], "line")[1] + end + elseif k == "nr" then + f = vim.api.nvim_get_current_buf + end + mt[k] = f + return f + end, + }), + ex = setmetatable({}, { + __index = function(self, k) + local mt = getmetatable(self) + local x = mt[k] + if x ~= nil then + return x + end + local command = k:gsub("_$", "!") + local f = function(...) + return vim.api.nvim_command(table.concat(vim.tbl_flatten { command, ... }, " ")) + end + mt[k] = f + return f + end, + }), + g = setmetatable({}, { + __index = function(_, k) + return vim.api.nvim_get_var(k) + end, + __newindex = function(_, k, v) + if v == nil then + return vim.api.nvim_del_var(k) + else + return vim.api.nvim_set_var(k, v) + end + end, + }), + v = setmetatable({}, { + __index = function(_, k) + return vim.api.nvim_get_vvar(k) + end, + __newindex = function(_, k, v) + return vim.api.nvim_set_vvar(k, v) + end, + }), + b = setmetatable({}, { + __index = function(_, k) + return vim.api.nvim_buf_get_var(0, k) + end, + __newindex = function(_, k, v) + if v == nil then + return vim.api.nvim_buf_del_var(0, k) + else + return vim.api.nvim_buf_set_var(0, k, v) + end + end, + }), + w = setmetatable({}, { + __index = function(_, k) + return vim.api.nvim_win_get_var(0, k) + end, + __newindex = function(_, k, v) + if v == nil then + return vim.api.nvim_win_del_var(0, k) + else + return vim.api.nvim_win_set_var(0, k, v) + end + end, + }), + o = setmetatable({}, { + __index = function(_, k) + return vim.api.nvim_get_option(k) + end, + __newindex = function(_, k, v) + return vim.api.nvim_set_option(k, v) + end, + }), + -- TODO add warning if you try to use a window option here? + bo = setmetatable({}, { + __index = function(_, k) + return vim.api.nvim_buf_get_option(0, k) + end, + __newindex = function(_, k, v) + return vim.api.nvim_buf_set_option(0, k, v) + end, + }), + wo = setmetatable({}, { + __index = function(_, k) + return vim.api.nvim_win_get_option(0, k) + end, + __newindex = function(_, k, v) + -- passing v == nil will clear the value, just like above. + return vim.api.nvim_win_set_option(0, k, v) + end, + }), + env = setmetatable({}, { + __index = function(_, k) + return vim.api.nvim_call_function("getenv", { k }) + end, + __newindex = function(_, k, v) + return vim.api.nvim_call_function("setenv", { k, v }) + end, + }), }, { - __index = function(self, k) - local mt = getmetatable(self) - local x = mt[k] - if x ~= nil then - return x - end - local f = vim.api['nvim_'..k] - mt[k] = f - return f - end + __index = function(self, k) + local mt = getmetatable(self) + local x = mt[k] + if x ~= nil then + return x + end + local f = vim.api["nvim_" .. k] + mt[k] = f + return f + end, }) - - diff --git a/lua/colorizer/trie.lua b/lua/colorizer/trie.lua index 21794ef..21ea543 100644 --- a/lua/colorizer/trie.lua +++ b/lua/colorizer/trie.lua @@ -13,7 +13,7 @@ -- You should have received a copy of the GNU General Public License -- along with this program. If not, see . -local ffi = require 'ffi' +local ffi = require "ffi" ffi.cdef [[ struct Trie { @@ -24,219 +24,227 @@ void *malloc(size_t size); void free(void *ptr); ]] -local Trie_t = ffi.typeof('struct Trie') -local Trie_ptr_t = ffi.typeof('$ *', Trie_t) +local Trie_t = ffi.typeof "struct Trie" +local Trie_ptr_t = ffi.typeof("$ *", Trie_t) local Trie_size = ffi.sizeof(Trie_t) local function trie_create() - local ptr = ffi.C.malloc(Trie_size) - ffi.fill(ptr, Trie_size) - return ffi.cast(Trie_ptr_t, ptr) + local ptr = ffi.C.malloc(Trie_size) + ffi.fill(ptr, Trie_size) + return ffi.cast(Trie_ptr_t, ptr) end local function trie_destroy(trie) - if trie == nil then - return - end - for i = 0, 61 do - local child = trie.character[i] - if child ~= nil then - trie_destroy(child) - end - end - ffi.C.free(trie) + if trie == nil then + return + end + for i = 0, 61 do + local child = trie.character[i] + if child ~= nil then + trie_destroy(child) + end + end + ffi.C.free(trie) end -local INDEX_LOOKUP_TABLE = ffi.new 'uint8_t[256]' -local CHAR_LOOKUP_TABLE = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' +local INDEX_LOOKUP_TABLE = ffi.new "uint8_t[256]" +local CHAR_LOOKUP_TABLE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" do - local b = string.byte - for i = 0, 255 do - if i >= b'0' and i <= b'9' then - INDEX_LOOKUP_TABLE[i] = i - b'0' - elseif i >= b'A' and i <= b'Z' then - INDEX_LOOKUP_TABLE[i] = i - b'A' + 10 - elseif i >= b'a' and i <= b'z' then - INDEX_LOOKUP_TABLE[i] = i - b'a' + 10 + 26 - else - INDEX_LOOKUP_TABLE[i] = 255 - end - end + local b = string.byte + for i = 0, 255 do + if i >= b "0" and i <= b "9" then + INDEX_LOOKUP_TABLE[i] = i - b "0" + elseif i >= b "A" and i <= b "Z" then + INDEX_LOOKUP_TABLE[i] = i - b "A" + 10 + elseif i >= b "a" and i <= b "z" then + INDEX_LOOKUP_TABLE[i] = i - b "a" + 10 + 26 + else + INDEX_LOOKUP_TABLE[i] = 255 + end + end end local function trie_insert(trie, value) - if trie == nil then return false end - local node = trie - for i = 1, #value do - local index = INDEX_LOOKUP_TABLE[value:byte(i)] - if index == 255 then - return false - end - if node.character[index] == nil then - node.character[index] = trie_create() - end - node = node.character[index] - end - node.is_leaf = true - return node, trie + if trie == nil then + return false + end + local node = trie + for i = 1, #value do + local index = INDEX_LOOKUP_TABLE[value:byte(i)] + if index == 255 then + return false + end + if node.character[index] == nil then + node.character[index] = trie_create() + end + node = node.character[index] + end + node.is_leaf = true + return node, trie end local function trie_search(trie, value, start) - if trie == nil then return false end - local node = trie - for i = (start or 1), #value do - local index = INDEX_LOOKUP_TABLE[value:byte(i)] - if index == 255 then - return - end - local child = node.character[index] - if child == nil then - return false - end - node = child - end - return node.is_leaf + if trie == nil then + return false + end + local node = trie + for i = (start or 1), #value do + local index = INDEX_LOOKUP_TABLE[value:byte(i)] + if index == 255 then + return + end + local child = node.character[index] + if child == nil then + return false + end + node = child + end + return node.is_leaf end local function trie_longest_prefix(trie, value, start) - if trie == nil then return false end - -- insensitive = insensitive and 0x20 or 0 - start = start or 1 - local node = trie - local last_i = nil - for i = start, #value do - local index = INDEX_LOOKUP_TABLE[value:byte(i)] --- local index = INDEX_LOOKUP_TABLE[bor(insensitive, value:byte(i))] - if index == 255 then - break - end - local child = node.character[index] - if child == nil then - break - end - if child.is_leaf then - last_i = i - end - node = child - end - if last_i then - -- Avoid a copy if the whole string is a match. - if start == 1 and last_i == #value then - return value - else - return value:sub(start, last_i) - end - end + if trie == nil then + return false + end + -- insensitive = insensitive and 0x20 or 0 + start = start or 1 + local node = trie + local last_i = nil + for i = start, #value do + local index = INDEX_LOOKUP_TABLE[value:byte(i)] + -- local index = INDEX_LOOKUP_TABLE[bor(insensitive, value:byte(i))] + if index == 255 then + break + end + local child = node.character[index] + if child == nil then + break + end + if child.is_leaf then + last_i = i + end + node = child + end + if last_i then + -- Avoid a copy if the whole string is a match. + if start == 1 and last_i == #value then + return value + else + return value:sub(start, last_i) + end + end end local function trie_extend(trie, t) - assert(type(t) == 'table') - for _, v in ipairs(t) do - trie_insert(trie, v) - end + assert(type(t) == "table") + for _, v in ipairs(t) do + trie_insert(trie, v) + end end --- Printing utilities local function index_to_char(index) - if index < 0 or index > 61 then return end - return CHAR_LOOKUP_TABLE:sub(index+1, index+1) + if index < 0 or index > 61 then + return + end + return CHAR_LOOKUP_TABLE:sub(index + 1, index + 1) end local function trie_as_table(trie) - if trie == nil then - return nil - end - local children = {} - for i = 0, 61 do - local child = trie.character[i] - if child ~= nil then - local child_table = trie_as_table(child) - child_table.c = index_to_char(i) - table.insert(children, child_table) - end - end - return { - is_leaf = trie.is_leaf; - children = children; - } + if trie == nil then + return nil + end + local children = {} + for i = 0, 61 do + local child = trie.character[i] + if child ~= nil then + local child_table = trie_as_table(child) + child_table.c = index_to_char(i) + table.insert(children, child_table) + end + end + return { + is_leaf = trie.is_leaf, + children = children, + } end local function print_trie_table(s) - local mark - if not s then - return {'nil'} - end - if s.c then - if s.is_leaf then - mark = s.c.."*" - else - mark = s.c.."─" - end - else - mark = "├─" - end - if #s.children == 0 then - return {mark} - end - local lines = {} - for _, child in ipairs(s.children) do - local child_lines = print_trie_table(child, thicc) - for _, child_line in ipairs(child_lines) do - table.insert(lines, child_line) - end - end - local child_count = 0 - for i, line in ipairs(lines) do - local line_parts = {} - if line:match("^%w") then - child_count = child_count + 1 - if i == 1 then - line_parts = {mark} - elseif i == #lines or child_count == #s.children then - line_parts = {"└─"} - else - line_parts = {"├─"} - end - else - if i == 1 then - line_parts = {mark} - elseif #s.children > 1 and child_count ~= #s.children then - line_parts = {"│ "} - else - line_parts = {" "} - end - end - table.insert(line_parts, line) - lines[i] = table.concat(line_parts) - end - return lines + local mark + if not s then + return { "nil" } + end + if s.c then + if s.is_leaf then + mark = s.c .. "*" + else + mark = s.c .. "─" + end + else + mark = "├─" + end + if #s.children == 0 then + return { mark } + end + local lines = {} + for _, child in ipairs(s.children) do + local child_lines = print_trie_table(child, thicc) + for _, child_line in ipairs(child_lines) do + table.insert(lines, child_line) + end + end + local child_count = 0 + for i, line in ipairs(lines) do + local line_parts = {} + if line:match "^%w" then + child_count = child_count + 1 + if i == 1 then + line_parts = { mark } + elseif i == #lines or child_count == #s.children then + line_parts = { "└─" } + else + line_parts = { "├─" } + end + else + if i == 1 then + line_parts = { mark } + elseif #s.children > 1 and child_count ~= #s.children then + line_parts = { "│ " } + else + line_parts = { " " } + end + end + table.insert(line_parts, line) + lines[i] = table.concat(line_parts) + end + return lines end local function trie_to_string(trie) - if trie == nil then - return 'nil' - end - local as_table = trie_as_table(trie) - return table.concat(print_trie_table(as_table), '\n') + if trie == nil then + return "nil" + end + local as_table = trie_as_table(trie) + return table.concat(print_trie_table(as_table), "\n") end local Trie_mt = { - __new = function(_, init) - local trie = trie_create() - if type(init) == 'table' then - trie_extend(trie, init) - end - return trie - end; - __index = { - insert = trie_insert; - search = trie_search; - longest_prefix = trie_longest_prefix; - extend = trie_extend; - }; - __tostring = trie_to_string; - __gc = trie_destroy; + __new = function(_, init) + local trie = trie_create() + if type(init) == "table" then + trie_extend(trie, init) + end + return trie + end, + __index = { + insert = trie_insert, + search = trie_search, + longest_prefix = trie_longest_prefix, + extend = trie_extend, + }, + __tostring = trie_to_string, + __gc = trie_destroy, } -return ffi.metatype('struct Trie', Trie_mt) +return ffi.metatype("struct Trie", Trie_mt) diff --git a/test/print-trie.lua b/test/print-trie.lua index 2ec1322..7575b5c 100644 --- a/test/print-trie.lua +++ b/test/print-trie.lua @@ -1,62 +1,62 @@ -- TODO this is kinda shitty -local function dirname(str,sep) - sep=sep or'/' - return str:match("(.*"..sep..")") +local function dirname(str, sep) + sep = sep or "/" + return str:match("(.*" .. sep .. ")") end local script_dir = dirname(arg[0]) -package.path = script_dir.."/../lua/?.lua;"..package.path +package.path = script_dir .. "/../lua/?.lua;" .. package.path -local Trie = require 'trie' -local nvim = require 'nvim' +local Trie = require "trie" +local nvim = require "nvim" local function print_color_trie() - local tohex = bit.tohex - local min, max = math.min, math.max + local tohex = bit.tohex + local min, max = math.min, math.max - local COLOR_NAME_SETTINGS = { - lowercase = false; - strip_digits = true; - } - local COLOR_MAP = {} - local COLOR_TRIE = Trie() - for k, v in pairs(nvim.get_color_map()) do - if not (COLOR_NAME_SETTINGS.strip_digits and k:match("%d+$")) then - COLOR_NAME_MINLEN = COLOR_NAME_MINLEN and min(#k, COLOR_NAME_MINLEN) or #k - COLOR_NAME_MAXLEN = COLOR_NAME_MAXLEN and max(#k, COLOR_NAME_MAXLEN) or #k - COLOR_MAP[k] = tohex(v, 6) - COLOR_TRIE:insert(k) - if COLOR_NAME_SETTINGS.lowercase then - local lowercase = k:lower() - COLOR_MAP[lowercase] = tohex(v, 6) - COLOR_TRIE:insert(lowercase) - end - end - end - print(COLOR_TRIE) + local COLOR_NAME_SETTINGS = { + lowercase = false, + strip_digits = true, + } + local COLOR_MAP = {} + local COLOR_TRIE = Trie() + for k, v in pairs(nvim.get_color_map()) do + if not (COLOR_NAME_SETTINGS.strip_digits and k:match "%d+$") then + COLOR_NAME_MINLEN = COLOR_NAME_MINLEN and min(#k, COLOR_NAME_MINLEN) or #k + COLOR_NAME_MAXLEN = COLOR_NAME_MAXLEN and max(#k, COLOR_NAME_MAXLEN) or #k + COLOR_MAP[k] = tohex(v, 6) + COLOR_TRIE:insert(k) + if COLOR_NAME_SETTINGS.lowercase then + local lowercase = k:lower() + COLOR_MAP[lowercase] = tohex(v, 6) + COLOR_TRIE:insert(lowercase) + end + end + end + print(COLOR_TRIE) end local trie = Trie { - "cat"; - "car"; - "celtic"; - "carb"; - "carb0"; - "CART0"; - "CaRT0"; - "Cart0"; - "931"; - "191"; - "121"; - "cardio"; - "call"; - "calcium"; - "calciur"; - "carry"; - "dog"; - "catdog"; + "cat", + "car", + "celtic", + "carb", + "carb0", + "CART0", + "CaRT0", + "Cart0", + "931", + "191", + "121", + "cardio", + "call", + "calcium", + "calciur", + "carry", + "dog", + "catdog", } print(trie) -print("catdo", trie:longest_prefix("catdo")) -print("catastrophic", trie:longest_prefix("catastrophic")) +print("catdo", trie:longest_prefix "catdo") +print("catastrophic", trie:longest_prefix "catastrophic") -- cgit v1.2.3-70-g09d2