nix/config/nvim/lua/raytheme.lua

536 lines
20 KiB
Lua

-- raytheme.lua - Personal colorscheme framework
-- A theme engine that applies palettes defined in colors/
local M = {}
---@class RaythemePalette
---@field bg string
---@field bg_dark string
---@field bg_darker string
---@field bg_float string
---@field bg_highlight string
---@field bg_visual string
---@field diff_add string
---@field diff_delete string
---@field diff_change string
---@field fg string
---@field fg_dark string
---@field fg_gutter string
---@field comment string
---@field keyword string
---@field tag string
---@field variable string
---@field string string
---@field string_escape string
---@field number string
---@field func string
---@field type string
---@field constant string
---@field support string
---@field misc string
---@field error string
---@field warning string
---@field info string
---@field hint string
---@field border string
---@field cursor string
---@field search string
---@field match string
---@class RaythemeConfig
---@field transparent boolean
---@field italic_comments boolean
---@field bold_keywords boolean
---@field overrides? table<string, vim.api.keyset.highlight>
---@type RaythemeConfig
M.defaults = {
transparent = false,
italic_comments = true,
bold_keywords = false,
overrides = {},
}
---@type RaythemeConfig?
M.config = nil
---@type RaythemePalette?
M.palette = nil
local function hi(group, opts) vim.api.nvim_set_hl(0, group, opts) end
---@param c RaythemePalette
---@param opts RaythemeConfig
local function apply_highlights(c, opts)
local bg = opts.transparent and 'NONE' or c.bg
local bg_dark = opts.transparent and 'NONE' or c.bg_dark
local bg_darker = opts.transparent and 'NONE' or c.bg_darker
local comment_style = opts.italic_comments and { fg = c.comment, italic = true } or { fg = c.comment }
local keyword_style = opts.bold_keywords and { fg = c.keyword, bold = true } or { fg = c.keyword }
-- Editor UI
hi('Normal', { fg = c.fg, bg = bg })
hi('NormalFloat', { fg = c.fg, bg = c.bg_float })
hi('FloatBorder', { fg = c.border, bg = c.bg_float })
hi('Cursor', { fg = c.bg, bg = c.cursor })
hi('CursorLine', { bg = c.bg_highlight })
hi('CursorColumn', { bg = c.bg_float })
hi('ColorColumn', { bg = bg_dark })
hi('LineNr', { fg = c.fg_gutter })
hi('CursorLineNr', { fg = c.misc, bold = true })
hi('SignColumn', { fg = c.fg_gutter, bg = bg })
hi('VertSplit', { fg = c.border })
hi('WinSeparator', { fg = c.border })
hi('Folded', { fg = c.comment, bg = bg_dark })
hi('FoldColumn', { fg = c.fg_gutter })
hi('NonText', { fg = c.fg_gutter })
hi('SpecialKey', { fg = c.fg_gutter })
hi('Whitespace', { fg = c.fg_gutter })
hi('EndOfBuffer', { fg = c.bg })
hi('Directory', { fg = c.misc })
hi('Conceal', { fg = c.fg_gutter })
hi('MatchParen', { fg = c.match, bold = true })
-- Selection & Search
hi('Visual', { bg = c.bg_highlight })
hi('VisualNOS', { bg = c.bg_highlight })
hi('Search', { fg = c.bg, bg = c.search })
hi('IncSearch', { fg = c.bg, bg = c.match })
hi('CurSearch', { fg = c.bg, bg = c.match })
hi('Substitute', { fg = c.bg, bg = c.error })
-- Pmenu (completion menu)
hi('Pmenu', { fg = c.fg, bg = bg_dark })
hi('PmenuSel', { fg = c.fg, bg = c.bg_highlight })
hi('PmenuSbar', { bg = c.bg_float })
hi('PmenuThumb', { bg = c.fg_gutter })
-- Statusline & Tabline
hi('StatusLine', { fg = c.fg, bg = c.bg_float })
hi('StatusLineNC', { fg = c.fg_dark, bg = c.bg_float })
hi('TabLine', { fg = c.fg_dark, bg = bg_darker })
hi('TabLineFill', { bg = bg_darker })
hi('TabLineSel', { fg = c.fg, bg = bg })
hi('WinBar', { fg = c.fg, bg = c.bg_float })
hi('WinBarNC', { fg = c.fg_dark, bg = c.bg_float })
-- Messages
hi('ModeMsg', { fg = c.fg, bold = true })
hi('MsgArea', { fg = c.fg })
hi('MoreMsg', { fg = c.string })
hi('Question', { fg = c.string })
hi('WarningMsg', { fg = c.warning })
hi('ErrorMsg', { fg = c.error })
hi('Title', { fg = c.tag, bold = true })
-- Diff
hi('DiffAdd', { bg = c.diff_add })
hi('DiffChange', { bg = c.diff_change })
hi('DiffDelete', { bg = c.diff_delete })
hi('DiffText', { bg = c.bg_highlight })
hi('diffAdded', { fg = c.string })
hi('diffRemoved', { fg = c.error })
hi('diffChanged', { fg = c.misc })
hi('diffFile', { fg = c.tag })
hi('diffIndexLine', { fg = c.comment })
-- Spelling
hi('SpellBad', { undercurl = true, sp = c.error })
hi('SpellCap', { undercurl = true, sp = c.warning })
hi('SpellLocal', { undercurl = true, sp = c.info })
hi('SpellRare', { undercurl = true, sp = c.hint })
-- Diagnostics
hi('DiagnosticError', { fg = c.error })
hi('DiagnosticWarn', { fg = c.warning })
hi('DiagnosticInfo', { fg = c.info })
hi('DiagnosticHint', { fg = c.hint })
hi('DiagnosticOk', { fg = c.string })
hi('DiagnosticUnderlineError', { undercurl = true, sp = c.error })
hi('DiagnosticUnderlineWarn', { undercurl = true, sp = c.warning })
hi('DiagnosticUnderlineInfo', { undercurl = true, sp = c.info })
hi('DiagnosticUnderlineHint', { undercurl = true, sp = c.hint })
hi('DiagnosticUnderlineOk', { undercurl = true, sp = c.string })
hi('DiagnosticVirtualTextError', { fg = c.error, bg = c.diff_delete })
hi('DiagnosticVirtualTextWarn', { fg = c.warning, bg = c.diff_change })
hi('DiagnosticVirtualTextInfo', { fg = c.info, bg = c.bg_float })
hi('DiagnosticVirtualTextHint', { fg = c.hint, bg = c.bg_float })
-- Syntax highlighting
hi('Comment', comment_style)
hi('Constant', { fg = c.constant })
hi('String', { fg = c.string })
hi('Character', { fg = c.string })
hi('Number', { fg = c.number })
hi('Boolean', { fg = c.number })
hi('Float', { fg = c.number })
hi('Identifier', { fg = c.variable })
hi('Function', { fg = c.func })
hi('Statement', keyword_style)
hi('Conditional', keyword_style)
hi('Repeat', keyword_style)
hi('Label', keyword_style)
hi('Operator', { fg = c.tag })
hi('Keyword', keyword_style)
hi('Exception', keyword_style)
hi('PreProc', { fg = c.support })
hi('Include', keyword_style)
hi('Define', { fg = c.support })
hi('Macro', { fg = c.support })
hi('PreCondit', { fg = c.support })
hi('Type', { fg = c.type })
hi('StorageClass', keyword_style)
hi('Structure', { fg = c.type })
hi('Typedef', { fg = c.type })
hi('Special', { fg = c.misc })
hi('SpecialChar', { fg = c.string_escape })
hi('Tag', { fg = c.tag })
hi('Delimiter', { fg = c.fg })
hi('SpecialComment', { fg = c.comment, bold = true })
hi('Debug', { fg = c.warning })
hi('Underlined', { underline = true })
hi('Error', { fg = c.error })
hi('Todo', { fg = c.bg, bg = c.misc, bold = true })
-- Treesitter
hi('@variable', { fg = c.variable })
hi('@variable.builtin', { fg = c.tag })
hi('@variable.parameter', { fg = c.variable })
hi('@variable.member', { fg = c.variable })
hi('@constant', { fg = c.constant })
hi('@constant.builtin', { fg = c.constant })
hi('@module', { fg = c.support })
hi('@label', { fg = c.misc })
hi('@string', { fg = c.string })
hi('@string.escape', { fg = c.string_escape })
hi('@string.regexp', { fg = c.string_escape })
hi('@string.special', { fg = c.string_escape })
hi('@string.special.url', { fg = c.misc, underline = true })
hi('@character', { fg = c.string })
hi('@number', { fg = c.number })
hi('@boolean', { fg = c.number })
hi('@float', { fg = c.number })
hi('@function', { fg = c.func })
hi('@function.builtin', { fg = c.func })
hi('@function.call', { fg = c.func })
hi('@function.method', { fg = c.func })
hi('@function.method.call', { fg = c.func })
hi('@constructor', { fg = c.support })
hi('@keyword', keyword_style)
hi('@keyword.function', keyword_style)
hi('@keyword.operator', keyword_style)
hi('@keyword.import', keyword_style)
hi('@keyword.return', keyword_style)
hi('@keyword.conditional', keyword_style)
hi('@keyword.repeat', keyword_style)
hi('@keyword.exception', keyword_style)
hi('@keyword.modifier', keyword_style)
hi('@operator', { fg = c.tag })
hi('@punctuation', { fg = c.fg })
hi('@punctuation.bracket', { fg = c.fg })
hi('@punctuation.delimiter', { fg = c.fg })
hi('@punctuation.special', { fg = c.misc })
hi('@comment', comment_style)
hi('@comment.todo', { fg = c.bg, bg = c.misc, bold = true })
hi('@comment.note', { fg = c.bg, bg = c.info, bold = true })
hi('@comment.warning', { fg = c.bg, bg = c.warning, bold = true })
hi('@comment.error', { fg = c.bg, bg = c.error, bold = true })
hi('@type', { fg = c.type })
hi('@type.builtin', { fg = c.type })
hi('@type.definition', { fg = c.type })
hi('@attribute', { fg = c.type })
hi('@property', { fg = c.variable })
hi('@tag', { fg = c.tag })
hi('@tag.attribute', { fg = c.variable })
hi('@tag.delimiter', { fg = c.fg })
hi('@diff.plus', { fg = c.string })
hi('@diff.minus', { fg = c.error })
hi('@diff.delta', { fg = c.misc })
-- LSP semantic tokens
hi('@lsp.type.class', { fg = c.type })
hi('@lsp.type.decorator', { fg = c.func })
hi('@lsp.type.enum', { fg = c.type })
hi('@lsp.type.enumMember', { fg = c.constant })
hi('@lsp.type.function', { fg = c.func })
hi('@lsp.type.interface', { fg = c.type })
hi('@lsp.type.macro', { fg = c.support })
hi('@lsp.type.method', { fg = c.func })
hi('@lsp.type.namespace', { fg = c.support })
hi('@lsp.type.parameter', { fg = c.variable })
hi('@lsp.type.property', { fg = c.variable })
hi('@lsp.type.struct', { fg = c.type })
hi('@lsp.type.type', { fg = c.type })
hi('@lsp.type.typeParameter', { fg = c.type })
hi('@lsp.type.variable', { fg = c.variable })
hi('@lsp.mod.deprecated', { strikethrough = true })
hi('@lsp.mod.readonly', { italic = true })
-- LSP references
hi('LspReferenceText', { bg = c.bg_highlight })
hi('LspReferenceRead', { bg = c.bg_highlight })
hi('LspReferenceWrite', { bg = c.bg_highlight })
hi('LspSignatureActiveParameter', { fg = c.match, bold = true })
hi('LspCodeLens', { fg = c.comment })
hi('LspInlayHint', { fg = c.fg_gutter, bg = c.bg_dark, italic = true })
-- Git signs
hi('GitSignsAdd', { fg = c.string })
hi('GitSignsChange', { fg = c.misc })
hi('GitSignsDelete', { fg = c.error })
hi('GitSignsCurrentLineBlame', { fg = c.fg_gutter })
-- Neogit / Diffview
hi('NeogitDiffAdd', { fg = c.string, bg = c.diff_add })
hi('NeogitDiffDelete', { fg = c.error, bg = c.diff_delete })
hi('NeogitDiffContext', { bg = c.bg_dark })
hi('NeogitHunkHeader', { fg = c.misc, bg = c.bg_float })
hi('NeogitBranch', { fg = c.keyword })
hi('NeogitRemote', { fg = c.support })
hi('DiffviewFilePanelTitle', { fg = c.tag, bold = true })
hi('DiffviewFilePanelCounter', { fg = c.misc })
-- Which-key
hi('WhichKey', { fg = c.func })
hi('WhichKeyGroup', { fg = c.keyword })
hi('WhichKeyDesc', { fg = c.fg })
hi('WhichKeySeparator', { fg = c.comment })
hi('WhichKeyFloat', { bg = c.bg_float })
hi('WhichKeyBorder', { fg = c.border })
hi('WhichKeyValue', { fg = c.fg_dark })
-- Telescope / Snacks picker
hi('TelescopeBorder', { fg = c.border })
hi('TelescopePromptBorder', { fg = c.border })
hi('TelescopeResultsBorder', { fg = c.border })
hi('TelescopePreviewBorder', { fg = c.border })
hi('TelescopeMatching', { fg = c.match, bold = true })
hi('TelescopeSelection', { bg = c.bg_highlight })
hi('TelescopePromptPrefix', { fg = c.misc })
hi('TelescopeSelectionCaret', { fg = c.misc })
hi('TelescopeTitle', { fg = c.tag, bold = true })
-- Blink.cmp
hi('BlinkCmpMenu', { fg = c.fg, bg = bg_dark })
hi('BlinkCmpMenuBorder', { fg = c.border })
hi('BlinkCmpMenuSelection', { bg = c.bg_highlight })
hi('BlinkCmpLabel', { fg = c.fg })
hi('BlinkCmpLabelMatch', { fg = c.match, bold = true })
hi('BlinkCmpKind', { fg = c.support })
hi('BlinkCmpDoc', { fg = c.fg, bg = c.bg_float })
hi('BlinkCmpDocBorder', { fg = c.border })
-- Oil.nvim
hi('OilDir', { fg = c.misc })
hi('OilDirIcon', { fg = c.misc })
hi('OilFile', { fg = c.fg })
hi('OilCreate', { fg = c.string })
hi('OilDelete', { fg = c.error })
hi('OilMove', { fg = c.warning })
hi('OilCopy', { fg = c.info })
hi('OilChange', { fg = c.misc })
hi('OilTitle', { fg = c.tag, bg = c.bg_float, bold = true })
hi('OilWinbar', { fg = c.fg, bg = c.bg_float })
-- Grapple
hi('GrappleTitle', { fg = c.tag, bold = true })
hi('GrappleBorder', { fg = c.border })
hi('GrappleCurrent', { fg = c.misc, bold = true })
hi('GrappleFooter', { fg = c.comment })
-- Mini.statusline
hi('MiniStatuslineModeNormal', { fg = c.bg, bg = c.misc, bold = true })
hi('MiniStatuslineModeInsert', { fg = c.bg, bg = c.string, bold = true })
hi('MiniStatuslineModeVisual', { fg = c.bg, bg = c.keyword, bold = true })
hi('MiniStatuslineModeReplace', { fg = c.bg, bg = c.error, bold = true })
hi('MiniStatuslineModeCommand', { fg = c.bg, bg = c.warning, bold = true })
hi('MiniStatuslineModeOther', { fg = c.bg, bg = c.support, bold = true })
hi('MiniStatuslineDevinfo', { fg = c.fg, bg = c.bg_highlight })
hi('MiniStatuslineFilename', { fg = c.fg, bg = c.bg_float })
hi('MiniStatuslineFileinfo', { fg = c.fg, bg = c.bg_highlight })
hi('MiniStatuslineInactive', { fg = c.fg_dark, bg = c.bg_float })
hi('MiniStatuslineMail', { fg = c.bg, bg = c.info, bold = true })
-- Mini.icons
hi('MiniIconsAzure', { fg = c.info })
hi('MiniIconsBlue', { fg = c.misc })
hi('MiniIconsCyan', { fg = c.support })
hi('MiniIconsGreen', { fg = c.string })
hi('MiniIconsGrey', { fg = c.fg_dark })
hi('MiniIconsOrange', { fg = c.tag })
hi('MiniIconsPurple', { fg = c.number })
hi('MiniIconsRed', { fg = c.error })
hi('MiniIconsYellow', { fg = c.warning })
-- Find-file (custom)
hi('FindFilePrompt', { fg = c.tag, bold = true })
hi('FindFileSep', { fg = c.comment })
hi('FindFileMatch', { fg = c.fg, bg = c.bg_float })
hi('FindFileMatchSelected', { fg = c.fg, bg = c.bg_highlight })
hi('FindFileMatchChar', { fg = c.match, bold = true })
-- Markdown
hi('@markup.heading', { fg = c.tag, bold = true })
hi('@markup.heading.1', { fg = c.tag, bold = true })
hi('@markup.heading.2', { fg = c.keyword, bold = true })
hi('@markup.heading.3', { fg = c.func, bold = true })
hi('@markup.heading.4', { fg = c.type, bold = true })
hi('@markup.heading.5', { fg = c.constant, bold = true })
hi('@markup.heading.6', { fg = c.misc, bold = true })
hi('@markup.link', { fg = c.misc, underline = true })
hi('@markup.link.url', { fg = c.comment, underline = true })
hi('@markup.link.label', { fg = c.misc })
hi('@markup.raw', { fg = c.string })
hi('@markup.raw.block', { fg = c.string })
hi('@markup.list', { fg = c.tag })
hi('@markup.list.checked', { fg = c.string })
hi('@markup.list.unchecked', { fg = c.fg_gutter })
hi('@markup.italic', { italic = true })
hi('@markup.strong', { bold = true })
hi('@markup.strikethrough', { strikethrough = true })
hi('@markup.quote', { fg = c.comment, italic = true })
-- Markview.nvim
-- Palette colors (used as base for many elements)
hi('MarkviewPalette0', { fg = c.tag, bg = c.bg_dark })
hi('MarkviewPalette0Fg', { fg = c.tag })
hi('MarkviewPalette0Bg', { bg = c.bg_dark })
hi('MarkviewPalette0Sign', { fg = c.tag })
hi('MarkviewPalette1', { fg = c.keyword, bg = c.bg_dark })
hi('MarkviewPalette1Fg', { fg = c.keyword })
hi('MarkviewPalette1Bg', { bg = c.bg_dark })
hi('MarkviewPalette1Sign', { fg = c.keyword })
hi('MarkviewPalette2', { fg = c.func, bg = c.bg_dark })
hi('MarkviewPalette2Fg', { fg = c.func })
hi('MarkviewPalette2Bg', { bg = c.bg_dark })
hi('MarkviewPalette2Sign', { fg = c.func })
hi('MarkviewPalette3', { fg = c.type, bg = c.bg_dark })
hi('MarkviewPalette3Fg', { fg = c.type })
hi('MarkviewPalette3Bg', { bg = c.bg_dark })
hi('MarkviewPalette3Sign', { fg = c.type })
hi('MarkviewPalette4', { fg = c.constant, bg = c.bg_dark })
hi('MarkviewPalette4Fg', { fg = c.constant })
hi('MarkviewPalette4Bg', { bg = c.bg_dark })
hi('MarkviewPalette4Sign', { fg = c.constant })
hi('MarkviewPalette5', { fg = c.misc, bg = c.bg_dark })
hi('MarkviewPalette5Fg', { fg = c.misc })
hi('MarkviewPalette5Bg', { bg = c.bg_dark })
hi('MarkviewPalette5Sign', { fg = c.misc })
hi('MarkviewPalette6', { fg = c.string, bg = c.bg_dark })
hi('MarkviewPalette6Fg', { fg = c.string })
hi('MarkviewPalette6Bg', { bg = c.bg_dark })
hi('MarkviewPalette6Sign', { fg = c.string })
-- Headings
hi('MarkviewHeading1', { fg = c.tag, bold = true })
hi('MarkviewHeading2', { fg = c.keyword, bold = true })
hi('MarkviewHeading3', { fg = c.func, bold = true })
hi('MarkviewHeading4', { fg = c.type, bold = true })
hi('MarkviewHeading5', { fg = c.constant, bold = true })
hi('MarkviewHeading6', { fg = c.misc, bold = true })
hi('MarkviewHeading1Sign', { fg = c.tag })
hi('MarkviewHeading2Sign', { fg = c.keyword })
hi('MarkviewHeading3Sign', { fg = c.func })
hi('MarkviewHeading4Sign', { fg = c.type })
hi('MarkviewHeading5Sign', { fg = c.constant })
hi('MarkviewHeading6Sign', { fg = c.misc })
-- Code
hi('MarkviewCode', { bg = c.bg_dark })
hi('MarkviewCodeInfo', { fg = c.comment })
hi('MarkviewCodeFg', { fg = c.fg })
hi('MarkviewInlineCode', { fg = c.string, bg = c.bg_dark })
-- Lists
hi('MarkviewListItemStar', { fg = c.tag })
hi('MarkviewListItemPlus', { fg = c.string })
hi('MarkviewListItemMinus', { fg = c.keyword })
-- Checkboxes
hi('MarkviewCheckboxChecked', { fg = c.string })
hi('MarkviewCheckboxUnchecked', { fg = c.fg })
hi('MarkviewCheckboxPending', { fg = c.warning })
hi('MarkviewCheckboxProgress', { fg = c.info })
hi('MarkviewCheckboxCancelled', { fg = c.error, strikethrough = true })
hi('MarkviewCheckboxStriked', { fg = c.fg_gutter, strikethrough = true })
-- Block quotes
hi('MarkviewBlockQuoteDefault', { fg = c.comment })
hi('MarkviewBlockQuoteWarn', { fg = c.warning })
hi('MarkviewBlockQuoteError', { fg = c.error })
hi('MarkviewBlockQuoteNote', { fg = c.info })
hi('MarkviewBlockQuoteOk', { fg = c.string })
hi('MarkviewBlockQuoteSpecial', { fg = c.misc })
-- Tables
hi('MarkviewTableHeader', { fg = c.tag, bold = true })
hi('MarkviewTableBorder', { fg = c.border })
hi('MarkviewTableAlignLeft', { fg = c.border })
hi('MarkviewTableAlignCenter', { fg = c.border })
hi('MarkviewTableAlignRight', { fg = c.border })
-- Links
hi('MarkviewHyperlink', { fg = c.misc, underline = true })
hi('MarkviewEmail', { fg = c.misc, underline = true })
hi('MarkviewImage', { fg = c.support, underline = true })
-- Text styles
hi('MarkviewSubscript', { fg = c.fg_dark })
hi('MarkviewSuperscript', { fg = c.fg_dark })
-- Icons (match palette colors)
hi('MarkviewIcon0', { fg = c.tag })
hi('MarkviewIcon1', { fg = c.keyword })
hi('MarkviewIcon2', { fg = c.func })
hi('MarkviewIcon3', { fg = c.type })
hi('MarkviewIcon4', { fg = c.constant })
hi('MarkviewIcon5', { fg = c.misc })
hi('MarkviewIcon6', { fg = c.string })
-- Gradients (subtle transition from dark to light)
hi('MarkviewGradient0', { fg = c.fg_gutter })
hi('MarkviewGradient1', { fg = c.fg_gutter })
hi('MarkviewGradient2', { fg = c.comment })
hi('MarkviewGradient3', { fg = c.comment })
hi('MarkviewGradient4', { fg = c.fg_dark })
hi('MarkviewGradient5', { fg = c.fg_dark })
hi('MarkviewGradient6', { fg = c.fg_dark })
hi('MarkviewGradient7', { fg = c.fg })
hi('MarkviewGradient8', { fg = c.fg })
hi('MarkviewGradient9', { fg = c.fg })
-- Apply user overrides
if opts.overrides then
for group, hl in pairs(opts.overrides) do
hi(group, hl)
end
end
end
---@param opts? RaythemeConfig
function M.setup(opts) M.config = vim.tbl_deep_extend('force', M.defaults, opts or {}) end
---@param palette RaythemePalette
---@param name string
function M.load(palette, name)
if not palette then
vim.notify('raytheme: palette is required', vim.log.levels.ERROR)
return
end
local config = M.config or M.defaults
if vim.g.colors_name then vim.cmd 'highlight clear' end
vim.opt.background = 'dark'
vim.g.colors_name = name or 'raytheme'
M.palette = palette
apply_highlights(palette, config)
end
--- Get current palette colors
---@return RaythemePalette?
function M.colors() return M.palette end
return M