]> git.wincent.com - pinnacle.git/blob - lua/wincent/pinnacle.lua
fix: capture/extract highlight without relying on nvim_exec
[pinnacle.git] / lua / wincent / pinnacle.lua
1 local pinnacle = {}
2
3 local prefix = 'cterm'
4
5 if vim.fn.has('gui') then
6   prefix = 'gui'
7 elseif vim.fn.has('termguicolors') and vim.api.nvim_get_option('termguicolors') then
8   prefix = 'gui'
9 end
10
11 -- Gets the current value of a highlight group.
12 pinnacle.capture_highlight = function(group)
13   return group .. ' xxx ' .. pinnacle.extract_highlight(group)
14 end
15
16 -- Returns a copy of `group` decorated with `style` (eg. "bold",
17 -- "italic" etc) suitable for passing to `:highlight`.
18 --
19 -- To decorate with multiple styles, `style` should be a comma-separated
20 -- list.
21 pinnacle.decorate = function(style, group)
22   local original = pinnacle.extract_highlight(group)
23
24   for _, lhs in ipairs({'gui', 'term', 'cterm'}) do
25     local before, setting, after = original:match(''
26       .. '^(.*)'
27       .. '%f[%a](' .. lhs .. '=%S+)'
28       .. '(.*)$'
29     )
30
31     if setting == nil then
32       -- No setting: add one with just style in it.
33       original = original .. ' ' .. lhs .. '=' .. style
34     else
35       for s in vim.gsplit(style, ',') do
36         local trimmed = vim.trim(s)
37         if not setting:match('%f[%a]' .. trimmed .. '%f[%A]') then
38           setting = setting .. ',' .. trimmed
39         end
40       end
41       original = before .. setting .. after
42     end
43
44     return original
45   end
46 end
47
48 -- Returns a dictionary representation of the specified highlight group.
49 pinnacle.dump = function(group)
50   local result = {}
51
52   for _, component in ipairs({'bg', 'fg'}) do
53     local value = pinnacle.extract_component(group, component)
54     if value ~= '' then
55       result[component] = value
56     end
57   end
58
59   local active = {}
60
61   for _, component in ipairs({'bold', 'inverse', 'italic', 'reverse', 'standout', 'undercurl', 'underline'}) do
62     if pinnacle.extract_component(group, component) == '1' then
63       table.insert(active, component)
64     end
65   end
66
67   if #active > 0 then
68     result[prefix] = table.concat(active, ',')
69   end
70
71   return result
72 end
73
74 -- Returns an bold copy of `group` suitable for passing to `:highlight`.
75 pinnacle.embolden = function(group)
76   return pinnacle.decorate('bold', group)
77 end
78
79 -- Extracts just the "bg" portion of the specified highlight group.
80 pinnacle.extract_bg = function(group)
81   return pinnacle.extract_component(group, 'bg')
82 end
83
84 -- Extracts a single component (eg. "bg", "fg", "italic" etc) from the
85 -- specified highlight group.
86 pinnacle.extract_component = function(group, component)
87   return vim.fn.synIDattr(
88     vim.fn.synIDtrans(vim.fn.hlID(group)),
89     component
90   )
91 end
92
93 -- Extracts just the "fg" portion of the specified highlight group.
94 pinnacle.extract_fg = function(group)
95   return pinnacle.extract_component(group, 'fg')
96 end
97
98 -- Extracts a highlight string from a group, recursively traversing
99 -- linked groups, and returns a string suitable for passing to
100 -- `:highlight` (effectivenly extracts the bit after "xxx").
101 pinnacle.extract_highlight = function(group)
102   -- We originally relied on:
103   --
104   --    vim.api.nvim_exec('0verbose highlight ' .. group, true)
105   --
106   -- But for some reason it sometimes returns an empty string, so we do this
107   -- instead:
108   return pinnacle.highlight(pinnacle.dump(group))
109 end
110
111 -- Returns a string representation of a table containing bg, fg, term,
112 -- cterm and guiterm entries.
113 pinnacle.highlight = function(highlight)
114   local result = {}
115
116   for _, key in ipairs({'bg', 'fg'}) do
117     if highlight[key] ~= nil then
118       table.insert(result, prefix .. key .. '=' .. highlight[key])
119     end
120   end
121
122   for _, key in ipairs({'term', 'cterm', 'guiterm'}) do
123     if highlight[key] ~= nil then
124       table.insert(result, prefix .. '=' .. highlight[key])
125     end
126   end
127
128   return table.concat(result, ' ')
129 end
130
131 -- Returns an italicized copy of `group` suitable for passing to
132 -- `:highlight`.
133 pinnacle.italicize = function(group)
134   return pinnacle.decorate('italic', group)
135 end
136
137 -- Returns an underlined copy of `group` suitable for passing to
138 -- `:highlight`.
139 pinnacle.underline = function(group)
140   return pinnacle.decorate('underline', group)
141 end
142
143 return pinnacle