feat: add Lua implementation
authorGreg Hurrell <greg@hurrell.net>
Mon, 12 Oct 2020 13:37:42 +0000 (15:37 +0200)
committerGreg Hurrell <greg@hurrell.net>
Mon, 12 Oct 2020 13:37:42 +0000 (15:37 +0200)
For use inside Neovim. You can replace calls to the autoloaded Vimscript
function with calls to the Lua implementation; they should be
equivalent.

eg. these two are equivalent:

    :call pinnacle#italicize('Comment')
    :lua require'wincent.pinnacle'.italicize('Comment')

lua/wincent/pinnacle.lua [new file with mode: 0644]

diff --git a/lua/wincent/pinnacle.lua b/lua/wincent/pinnacle.lua
new file mode 100644 (file)
index 0000000..ed99879
--- /dev/null
@@ -0,0 +1,164 @@
+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