From a345a21966ed7c1ff26f247d4b20a4f307bedc79 Mon Sep 17 00:00:00 2001 From: Benjamin Rabier Date: Mon, 25 Sep 2023 01:49:41 -0700 Subject: [PATCH] feat: Add Helix extras (#409) * feat: Add Helix extras * docs: remove out of tree extras gen docs --------- Co-authored-by: Folke Lemaitre --- README.md | 4 +- lua/tokyonight/extra/helix.lua | 450 +++++++++++++++++++++++++++++++++ lua/tokyonight/extra/init.lua | 7 +- lua/tokyonight/theme.lua | 1 + 4 files changed, 459 insertions(+), 3 deletions(-) create mode 100644 lua/tokyonight/extra/helix.lua diff --git a/README.md b/README.md index 27dd75c..1ea36f0 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ TokyoNight](https://github.com/enkia/tokyo-night-vscode-theme) theme. Includes ### 🍭 Extras + - [Alacritty](https://github.com/alacritty/alacritty) ([alacritty](extras/alacritty)) - [Delta](https://github.com/dandavison/delta) ([delta](extras/delta)) - [Dunst](https://dunst-project.org/) ([dunst](extras/dunst)) @@ -44,6 +45,7 @@ TokyoNight](https://github.com/enkia/tokyo-night-vscode-theme) theme. Includes - [Fish Themes](https://fishshell.com/docs/current/interactive.html#syntax-highlighting) ([fish_themes](extras/fish_themes)) - [Foot](https://codeberg.org/dnkl/foot) ([foot](extras/foot)) - [GitUI](https://github.com/extrawurst/gitui) ([gitui](extras/gitui)) +- [Helix](https://helix-editor.com/) ([helix](extras/helix)) - [iTerm](https://iterm2.com/) ([iterm](extras/iterm)) - [Kitty](https://sw.kovidgoyal.net/kitty/conf.html) ([kitty](extras/kitty)) - [Lua Table for testing](https://www.lua.org) ([lua](extras/lua)) @@ -300,7 +302,7 @@ How to add a new extra template: 1. Create a file like `lua/tokyonight/extra/cool-app.lua`. 2. Add the name and output file extension to the `extras` table in `lua/tokyonight/extra/init.lua`. -3. Run the following command to generate new [extra](#-extras) themes: +3. Run the following command to generate new [extra](#-extras) themes from the tokyonight plugin directory: ```sh nvim --headless "+lua require('tokyonight.extra').setup()" +qa diff --git a/lua/tokyonight/extra/helix.lua b/lua/tokyonight/extra/helix.lua new file mode 100644 index 0000000..f9f06e0 --- /dev/null +++ b/lua/tokyonight/extra/helix.lua @@ -0,0 +1,450 @@ +local util = require("tokyonight.util") + +local M = {} + +--- @param colors ColorScheme +function M.generate(colors) + -- Ref: https://github.com/helix-editor/helix/blob/master/book/src/themes.md + -- nil is used when no equivalent was found. + local mapping = M.flatten({ + attribute = "@attribute", + type = { + "Type", + builtin = "@type.builtin", + enum = { + "@lsp.type.enum", + variant = "@lsp.type.enumMember" + } + }, + -- rust: pattern matching `let Some(_) = ...` + -- ^^^^ + constructor = "Type", + constant = { + "Constant", + builtin = { + "@constant.builtin", + boolean = "Boolean" + }, + character = { + "Character", + escape = "@string.escape" + }, + numeric = { + "Number", + float = "Float", + integer = "Number" + } + }, + string = { + "String", + regexp = "@string.regex", + special = { + "@string.special", + path = nil, + url = nil, + symbol = nil + } + }, + comment = { + "@comment", + line = nil, + block = { + nil, + -- not sure about that one + documentation = "@string.documentation" + } + }, + variable = { + "@variable", + builtin = "@variable.builtin", + parameter = "@parameter", + other = { + nil, + member = "@field" + } + }, + label = "@label", + keyword = { + "@keyword", + control = { + "Statement", + conditional = "Conditional", + ["repeat"] = "Repeat", + import = nil, + ["return"] = "@keyword.return", + exception = "Exception" + }, + operator = "Statement", + directive = "PreProc", + ["function"] = "@keyword.function", + storage = { + nil, -- rust: `let` + type = nil, -- rust: `struct` & `type` + modifier = nil -- rust: `mut` + } + }, + operator = "Operator", + ["function"] = { + "Function", + builtin = "@function.builtin", + method = "@method", + macro = "@function.macro", + -- Defined as "preprocessor in C", so using "PreProc", not sure though + special = "PreProc" + }, + tag = { + "@tag", + -- ??? + builtin = nil + }, + namespace = "@namespace", + special = "Special", + markup = { + nil, + heading = { + "@text.title", + marker = nil, + -- post-processed to remove the 'h' as we already use the first element (1) as the root value. + h1 = nil, + h2 = nil, + h3 = nil, + h4 = nil, + h5 = nil, + h6 = nil, + -- UI -- + completion = "Pmenu", + hover = "PmenuSel" + }, + list = { + "markdownHeadingDelimiter", + unnumbered = nil, + numbered = nil, + checked = nil, + unchecked = nil + }, + bold = "Bold", + italic = "Italic", + strikethrough = { + 'helix', + modifiers = { 'crossed_out' } + }, + link = { + "markdownLinkText", + url = "Underlined", + label = "markdownCode", + text = "markdownCode" + }, + quote = nil, + raw = { + "markdownCode", + inline = "@text.literal.markdown_inline", + block = "markdownCodeBlock", + -- UI -- + completion = nil, + hover = nil + }, + -- UI -- + normal = { + nil, + completion = "CmpItemMenu", + hover = "CmpItemKindDefault" + }, + }, + diff = { + nil, + plus = "diffAdded", + minus = "diffRemoved", + delta = { + "diffChanged", + moved = "diffFile" + } + }, + ui = { + background = { + { "helix", bg = "bg" }, + separator = nil + }, + cursor = { + "Cursor", + normal = nil, + insert = nil, + select = nil, + match = "MatchParen", + primary = { + nil, + normal = nil, + insert = nil, + select = nil, + }, + }, + debug = { + breakpoint = nil, + active = nil + }, + gutter = { + nil, + selected = nil + }, + highlight = { + nil, + frameline = nil + }, + linenr = { + "LineNr", + select = "CursorLineNr", + }, + statusline = { + "StatusLine", + inactive = "StatusLineNc", + -- Inspired from lualine + normal = { + 'helix', + bg = "blue", + fg = "black" + }, + insert = nil, + select = nil, + separator = nil + }, + popup = { + "TelescopeBorder", + info = nil + }, + window = "WinSeparator", + help = nil, + text = { + "Normal", + -- TelescopeSelection + focus = "Visual", + inactive = "Comment", + info = "TelescopeNormal", + }, + virtual = { + ruler = nil, + whitespace = nil, + ["indent-guide"] = nil, + ["inlay-hint"] = { + "DiagnosticVirtualTextHint", + parameter = nil, + type = nil, + }, + wrap = nil + }, + menu = { + "Pmenu", + selected = "PmenuSel", + scroll = { + 'helix', + fg = vim.api.nvim_get_hl(0, { name = "PmenuThumb" }).bg, + bg = vim.api.nvim_get_hl(0, { name = "PmenuSbar" }).bg, + } + }, + selection = { + { 'helix', bg = "bg_highlight" }, + primary = nil + }, + cursorline = { + primary = nil, + secondary = nil + }, + cursorcolumn = { + primary = nil, + secondary = nil + }, + }, + hint = "DiagnosticHint", + info = "DiagnosticInfo", + warning = "DiagnosticWarn", + error = "DiagnosticError", + diagnostic = { + nil, + hint = "DiagnosticUnderlineHint", + info = "DiagnosticUnderlineInfo", + warning = "DiagnosticUnderlineWarn", + error = "DiagnosticUnderlineError" + } + }) + + local config = {} + for hx_scope, group in M.pairsByKeys(mapping) do + -- print(hx_scope, util.dump(group)) + hx_scope = hx_scope:gsub("%.h(%d)", ".%1") + if hx_scope:match('%.') then + hx_scope = '"' .. hx_scope .. '"' + end + + if group == nil then + goto continue + end + + if type(group) == "table" and group[1] == 'helix' then + table.remove(group, 1); + table.insert(config, string.format("%s = %s", hx_scope, M.to_toml(group))) + goto continue + end + + local highlight = vim.api.nvim_get_hl(0, { name = group }) + while highlight and highlight.link do + highlight = vim.api.nvim_get_hl(0, { name = highlight.link }) + end + if highlight == nil then + print("Unknown highlight for " .. hx_scope) + goto continue + end + table.insert(config, string.format("%s = %s", hx_scope, M.to_helix_config(highlight))) + + ::continue:: + end + + table.insert(config, '\n[palette]'); + for name, color in M.pairsByKeys(M.flatten(colors)) do + if name:match('%.') then + name = '"' .. name .. '"' + end + if type(color) == 'string' and not string.starts(name, '_') and name ~= 'none' then + table.insert(config, string.format('%s = "%s"', name, color)) + end + end + return table.concat(config, '\n') +end + +function string.starts(String, Start) + return string.sub(String, 1, string.len(Start)) == Start +end + +function M.flatten(t) + local res = {} + for k, v in pairs(t) do + if type(v) == 'table' then + if v[1] ~= 'helix' then + for k2, v2 in pairs(M.flatten(v)) do + -- Special case for tables like: + -- { type = { "@type", enum = "@type.enum" } } + if k2 == 1 then + res[k] = v2 + else + res[k .. '.' .. k2] = v2 + end + end + else + res[k] = v + end + else + res[k] = v + end + end + return res +end + +-- https://www.lua.org/pil/19.3.html +function M.pairsByKeys(t, f) + local a = {} + for n in pairs(t) do table.insert(a, n) end + table.sort(a, f) + local i = 0 -- iterator variable + local iter = function() -- iterator function + i = i + 1 + if a[i] == nil then + return nil + else + return a[i], t[a[i]] + end + end + return iter +end + +function M.to_helix_config(highlight) + local style = {} + for hx_name, nvim_name in pairs({ fg = "fg", bg = "bg" }) do + style[hx_name] = M.to_rgb(highlight[nvim_name]) + end + local modifiers = {} + for _, mods in ipairs({ highlight, highlight.cterm }) do + if mods then + if mods.bold then + modifiers.bold = true + end + if mods.italic then + modifiers.italic = true + end + if mods.underline then + style.underline = { + style = "line" + } + end + if mods.undercurl and highlight.sp then + style.underline = { + color = M.to_rgb(mods.sp), + style = "curl" + } + end + end + end + if next(modifiers) ~= nil then + style.modifiers = M.key_set(modifiers) + end + return M.to_toml(style) +end + +function M.to_rgb(color) + if type(color) == "string" then + return color + elseif type(color) == "number" then + return string.format("#%06x", color) + end +end + +function M.key_set(t) + local keys = {} + for key, _ in pairs(t) do + table.insert(keys, key) + end + return keys +end + +function M.to_toml(style) + local buffer = {} + M.insert_as_toml(buffer, style) + return table.concat(buffer, '') +end + +function M.insert_as_toml(buffer, x) + if type(x) == "table" then + if next(x) == nil then + return + end + if M.is_array(x) then + table.insert(buffer, "[") + for _, v in pairs(x) do + M.insert_as_toml(buffer, v); + table.insert(buffer, ", ") + end + table.remove(buffer) + table.insert(buffer, "]") + else + table.insert(buffer, "{ ") + for k, v in M.pairsByKeys(x) do + table.insert(buffer, string.format("%s = ", k)); + M.insert_as_toml(buffer, v); + table.insert(buffer, ", ") + end + table.remove(buffer) + table.insert(buffer, " }") + end + elseif type(x) == "string" then + table.insert(buffer, '"' .. x .. '"') + elseif type(x) ~= nil then + table.insert(buffer, tostring(x)) + end +end + +function M.is_array(t) + local i = 0 + for _ in pairs(t) do + i = i + 1 + if t[i] == nil then return false end + end + return true +end + +return M diff --git a/lua/tokyonight/extra/init.lua b/lua/tokyonight/extra/init.lua index 51be720..32efd84 100644 --- a/lua/tokyonight/extra/init.lua +++ b/lua/tokyonight/extra/init.lua @@ -24,6 +24,7 @@ M.extras = { zathura = {ext = "zathurarc", url = "https://pwmt.org/projects/zathura/", label = "Zathura"}, dunst = {ext = "dunstrc", url = "https://dunst-project.org/", label = "Dunst"}, gitui = {ext = "ron", url = "https://github.com/extrawurst/gitui", label = "GitUI"}, + helix = { ext = "toml", url = "https://helix-editor.com/", label = "Helix"}, } local function write(str, fileName) @@ -66,7 +67,7 @@ end function M.setup() M.docs() - local config = require("tokyonight.config") + local tokyonight = require("tokyonight") vim.o.background = "dark" -- map of style to style name @@ -81,7 +82,9 @@ function M.setup() package.loaded["tokyonight.extra." .. extra] = nil local plugin = require("tokyonight.extra." .. extra) for style, style_name in pairs(styles) do - config.setup({ style = style }) + tokyonight.setup({ style = style }) + tokyonight.load({ style = style }) + vim.cmd.colorscheme('tokyonight-' .. style) local colors = require("tokyonight.colors").setup({ transform = true }) local fname = extra .. "/tokyonight_" .. style .. "." .. info.ext colors["_upstream_url"] = "https://github.com/folke/tokyonight.nvim/raw/main/extras/" .. fname diff --git a/lua/tokyonight/theme.lua b/lua/tokyonight/theme.lua index a93215a..d4eafd8 100644 --- a/lua/tokyonight/theme.lua +++ b/lua/tokyonight/theme.lua @@ -8,6 +8,7 @@ local M = {} ---@field bg string|nil ---@field sp string|nil ---@field style string|nil|Highlight +---@field link string|nil ---@alias Highlights table