--- /dev/null
+local pinnacle = {}
+
+local prefix = 'cterm'
+
+if vim.fn.has('gui') then
+ prefix = 'gui'
+elseif vim.fn.has('termguicolors') and vim.api.nvim_get_option('termguicolors') then
+ prefix = 'gui'
+end
+
+-- Gets the current value of a highlight group.
+pinnacle.capture_highlight = function(group)
+ return pinnacle.capture_line('0verbose silent highlight ' .. group)
+end
+
+-- Runs a command and returns the captured output as a single line.
+--
+-- Useful when we don't want to let long lines on narrow windows produce
+-- unwanted embedded newlines.
+pinnacle.capture_line = function(command)
+ local capture = vim.fn.execute(command)
+
+ return pinnacle.sub_newlines(capture)
+end
+
+-- Returns a copy of `group` decorated with `style` (eg. "bold",
+-- "italic" etc) suitable for passing to `:highlight`.
+--
+-- To decorate with multiple styles, `style` should be a comma-separated
+-- list.
+pinnacle.decorate = function(style, group)
+ local original = pinnacle.extract_highlight(group)
+
+ for _, lhs in ipairs({'gui', 'term', 'cterm'}) do
+ local before, setting, after = original:match(''
+ .. '^(.*)'
+ .. '%f[%a](' .. lhs .. '=%S+)'
+ .. '(.*)$'
+ )
+
+ if setting == nil then
+ -- No setting: add one with just style in it.
+ original = original .. ' ' .. lhs .. '=' .. style
+ else
+ for s in vim.gsplit(style, ',') do
+ local trimmed = vim.trim(s)
+ if not setting:match('%f[%a]' .. trimmed .. '%f[%A]') then
+ setting = setting .. ',' .. trimmed
+ end
+ end
+ original = before .. setting .. after
+ end
+
+ return pinnacle.sub_newlines(original)
+ end
+end
+
+-- Returns a dictionary representation of the specified highlight group.
+pinnacle.dump = function(group)
+ local result = {}
+
+ for _, component in ipairs({'bg', 'fg'}) do
+ local value = pinnacle.extract_component(group, component)
+ if value ~= '' then
+ result[component] = value
+ end
+ end
+
+ local active = {}
+
+ for _, component in ipairs({'bold', 'inverse', 'italic', 'reverse', 'standout', 'undercurl', 'underline'}) do
+ if pinnacle.extract_component(group, component) == '1' then
+ table.insert(active, component)
+ end
+ end
+
+ if #active > 0 then
+ result[prefix] = table.concat(active, ',')
+ end
+
+ return result
+end
+
+-- Returns an bold copy of `group` suitable for passing to `:highlight`.
+pinnacle.embolden = function(group)
+ return pinnacle.decorate('bold', group)
+end
+
+-- Extracts just the "bg" portion of the specified highlight group.
+pinnacle.extract_bg = function(group)
+ return pinnacle.extract_component(group, 'bg')
+end
+
+-- Extracts a single component (eg. "bg", "fg", "italic" etc) from the
+-- specified highlight group.
+pinnacle.extract_component = function(group, component)
+ return vim.fn.synIDattr(
+ vim.fn.synIDtrans(vim.fn.hlID(group)),
+ component
+ )
+end
+
+-- Extracts just the "fg" portion of the specified highlight group.
+pinnacle.extract_fg = function(group)
+ return pinnacle.extract_component(group, 'fg')
+end
+
+-- Extracts a highlight string from a group, recursively traversing
+-- linked groups, and returns a string suitable for passing to
+-- `:highlight`.
+pinnacle.extract_highlight = function(group)
+ group = pinnacle.capture_highlight(group)
+
+ -- Traverse links back to authoritative group.
+ local links = ' links to '
+
+ while group:match(links) ~= nil do
+ local start, finish = string.find(group, links)
+ local linked = string.sub(group, finish + 1)
+ group = pinnacle.capture_highlight(linked)
+ end
+
+ -- Extract the highlighting details (the bit after "xxx").
+ return group:match('%sxxx%s+(.*)')
+end
+
+-- Returns a string representation of a table containing bg, fg, term,
+-- cterm and guiterm entries.
+pinnacle.highlight = function(highlight)
+ local result = {}
+
+ for _, key in ipairs({'bg', 'fg'}) do
+ if highlight[key] ~= nil then
+ table.insert(result, prefix .. key .. '=' .. highlight[key])
+ end
+ end
+
+ for _, key in ipairs({'term', 'cterm', 'guiterm'}) do
+ if highlight[key] ~= nil then
+ table.insert(result, prefix .. '=' .. highlight[key])
+ end
+ end
+
+ return table.concat(result, ' ')
+end
+
+-- Returns an italicized copy of `group` suitable for passing to
+-- `:highlight`.
+pinnacle.italicize = function(group)
+ return pinnacle.decorate('italic', group)
+end
+
+-- Replaces newlines with spaces.
+pinnacle.sub_newlines = function(string)
+ return ({string:gsub('[\r\n]', ' ')})[1]
+end
+
+-- Returns an underlined copy of `group` suitable for passing to
+-- `:highlight`.
+pinnacle.underline = function(group)
+ return pinnacle.decorate('underline', group)
+end
+
+return pinnacle