aboutsummaryrefslogtreecommitdiff
path: root/lua/colorizer/matcher.lua
blob: ccd72a2bbe75bf11ef63206cd5bad7a798d35516 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
---Helper functions for colorizer to enable required parsers
--@module colorizer.matcher
local Trie = require "colorizer.trie"
local min, max = math.min, math.max

local color = require "colorizer.color"
local color_name_parser = color.name_parser
local rgba_hex_parser = color.rgba_hex_parser

local sass = require "colorizer.sass"
local sass_name_parser = sass.name_parser

local B_HASH, DOLLAR_HASH = ("#"):byte(), ("$"):byte()

local parser = {
  ["_0x"] = color.argb_hex_parser,
  ["_rgb"] = color.rgb_function_parser,
  ["_rgba"] = color.rgb_function_parser,
  ["_hsl"] = color.hsl_function_parser,
  ["_hsla"] = color.hsl_function_parser,
}

local matcher = {}

---Form a trie stuct with the given prefixes
---@param matchers table: List of prefixes, {"rgb", "hsl"}
---@param matchers_trie table: Table containing information regarding non-trie based parsers
---@return function: function which will just parse the line for enabled parsers
function matcher.compile(matchers, matchers_trie)
  local trie = Trie(matchers_trie)

  local function parse_fn(line, i, buf)
    -- prefix #
    if matchers.rgba_hex_parser then
      if line:byte(i) == B_HASH then
        return rgba_hex_parser(line, i, matchers.rgba_hex_parser)
      end
    end

    -- prefix $, SASS Colour names
    if matchers.sass_name_parser then
      if line:byte(i) == DOLLAR_HASH then
        return sass_name_parser(line, i, buf)
      end
    end

    -- Prefix 0x, rgba, rgb, hsla, hsl
    local prefix = trie:longest_prefix(line, i)
    if prefix then
      local fn = "_" .. prefix
      if parser[fn] then
        return parser[fn](line, i, matchers[prefix])
      end
    end

    -- Colour names
    if matchers.color_name_parser then
      return color_name_parser(line, i, matchers.color_name_parser)
    end
  end
  return parse_fn
end

local MATCHER_CACHE = {}
---Parse the given options and return a function with enabled parsers.
--if no parsers enabled then return false
--Do not try make the function again if it is present in the cache
---@param options table: options created in `colorizer.setup`
---@return function|boolean: function which will just parse the line for enabled parsers
function matcher.make(options)
  if not options then
    return false
  end

  local enable_names = options.names
  local enable_sass = options.sass and options.sass.enable
  local enable_tailwind = options.tailwind
  local enable_RGB = options.RGB
  local enable_RRGGBB = options.RRGGBB
  local enable_RRGGBBAA = options.RRGGBBAA
  local enable_AARRGGBB = options.AARRGGBB
  local enable_rgb = options.rgb_fn
  local enable_hsl = options.hsl_fn

  local matcher_key = 0
    + (enable_names and 1 or 0)
    + (enable_RGB and 1 or 1)
    + (enable_RRGGBB and 1 or 2)
    + (enable_RRGGBBAA and 1 or 3)
    + (enable_AARRGGBB and 1 or 4)
    + (enable_rgb and 1 or 5)
    + (enable_hsl and 1 or 6)
    + ((enable_tailwind == true or enable_tailwind == "normal") and 1 or 7)
    + (enable_tailwind == "lsp" and 1 or 8)
    + (enable_tailwind == "both" and 1 or 9)
    + (enable_sass and 1 or 10)

  if matcher_key == 0 then
    return false
  end

  local loop_parse_fn = MATCHER_CACHE[matcher_key]
  if loop_parse_fn then
    return loop_parse_fn
  end

  local matchers = {}
  local matchers_prefix = {}
  matchers.max_prefix_length = 0

  if enable_names then
    matchers.color_name_parser = { tailwind = options.tailwind }
  end

  if enable_sass then
    matchers.sass_name_parser = true
  end

  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
    matchers.rgba_hex_parser = {}
    matchers.rgba_hex_parser.valid_lengths = valid_lengths
    matchers.rgba_hex_parser.maxlen = maxlen
    matchers.rgba_hex_parser.minlen = minlen
  end

  if enable_AARRGGBB then
    table.insert(matchers_prefix, "0x")
  end

  if enable_rgb and enable_hsl then
    table.insert(matchers_prefix, "hsla")
    table.insert(matchers_prefix, "rgba")
    table.insert(matchers_prefix, "rgb")
    table.insert(matchers_prefix, "hsl")
  elseif enable_rgb then
    table.insert(matchers_prefix, "rgba")
    table.insert(matchers_prefix, "rgb")
  elseif enable_hsl then
    table.insert(matchers_prefix, "hsla")
    table.insert(matchers_prefix, "hsl")
  end

  for _, value in ipairs(matchers_prefix) do
    matchers[value] = { prefix = value }
  end

  loop_parse_fn = matcher.compile(matchers, matchers_prefix)
  MATCHER_CACHE[matcher_key] = loop_parse_fn

  return loop_parse_fn
end

return matcher