Initial Commit

This commit is contained in:
Henrik Hautakoski 2026-05-17 16:26:31 +02:00
commit 694515c168
16 changed files with 1325 additions and 0 deletions

126
lua/skift/colors.lua Normal file
View file

@ -0,0 +1,126 @@
local resolve = require('skift.utils.resolve_color_vars')
local hsl = require('skift.utils.hsl').hslToHex
--- @class Colors
local colors = {
-- Base colors
slate50 = hsl(213, 27, 94),
slate100 = hsl(233, 18, 54),
slate200 = hsl(228, 28, 32),
slate300 = hsl(228, 28, 27),
slate500 = hsl(228, 28, 14),
slate700 = hsl(228, 28, 8), --base
slate800 = hsl(228, 28, 7),
slate900 = hsl(228, 28, 6),
-- Color tokens
black = "slate900",
white = "slate50",
blue = hsl(217, 92, 76),
sky = hsl(227, 92, 40),
green = hsl(115, 54, 76),
red = hsl(343, 81, 75),
yellow = hsl(41, 86, 83),
orange = hsl(23, 92, 75),
purple = hsl(267, 84, 81),
rose = hsl(10, 56, 91),
lavender = hsl(232, 97, 85),
cyan = hsl(199, 76, 69),
teal = hsl(170, 57, 73),
-- Dimmed color tokens
red_dim = hsl(328, 22, 25),
orange_dim = hsl(15, 18, 27),
yellow_dim = hsl(37, 9, 29),
green_dim = hsl(140, 14, 26),
blue_dim = hsl(230, 19, 29),
cyan_dim = hsl(205, 32, 25),
-- Backgrounds
bg = "slate700",
bg_dim = "slate800",
bg_float = "slate900",
bg_elevated = "slate900",
bg_cursorline = "slate500",
bg_selection = "slate200",
bg_search = "accent",
-- Foregrounds
fg = "slate50",
fg_dim = "slate300",
fg_muted = "slate500",
fg_bright = "slate50", -- Remove?
fg_comment = "slate100",
fg_gutter = "slate200",
fg_gutter_active = "lavender",
-- Syntax
keyword = "yellow",
func = "fg",
string = "lavender",
number = "orange",
type = "blue",
constant = "orange",
variable = "green",
property = "white",
operator = "rose",
special = "purple",
tag = "cyan",
regex = "lavender",
-- UI
accent = "blue",
border = "slate500",
match_paren = "orange",
guide = "slate500",
guide_active = "#282e3c",
nontext = "fg_muted",
-- Diagnostics
error = "red",
warning = "yellow",
info = "blue",
hint = "lavender",
ok = "green",
-- Diff
diff_add = "green",
diff_change = "yellow",
diff_delete = "red",
diff_text = "fg_dim",
-- Diff backgrounds
diff_add_bg = "green_dim",
diff_change_bg = "orange_dim",
diff_delete_bg = "red_dim",
diff_text_bg = "fg_dim",
-- Git
git_add = "diff_add",
git_change = "diff_change",
git_delete = "diff_delete",
git_ignore = "diff_text",
-- Terminal
terminal_black = "black",
terminal_red = "red",
terminal_green = "green",
terminal_yellow = "yellow",
terminal_blue = "blue",
terminal_magenta = "purple",
terminal_cyan = "cyan",
terminal_white = "white",
terminal_bright_black = "black",
terminal_bright_red = "red",
terminal_bright_green = "green",
terminal_bright_yellow = "yellow",
terminal_bright_blue = "blue",
terminal_bright_magenta = "purple",
terminal_bright_cyan = "cyan",
terminal_bright_white = "white",
none = "NONE",
}
return resolve(colors)

22
lua/skift/config.lua Normal file
View file

@ -0,0 +1,22 @@
---@class Config
local defaults = {
transparent = false,
bold = true,
italic = true,
italic_comments = true,
on_colors = function(colors) end,
on_highlights = function(highlights, colors) end,
}
---@return Config
local config = {}
function config:merge(other)
for key, value in pairs(other or {}) do
self[key] = value
end
end
return setmetatable(config, {
__index = defaults,
})

View file

@ -0,0 +1,79 @@
local M = {}
---@param c Colors
function M.get(c)
return {
Normal = { fg = c.fg, bg = c.bg },
NormalNC = { fg = c.fg, bg = c.bg },
NormalFloat = { fg = c.fg, bg = c.bg_float },
FloatBorder = { fg = c.border, bg = c.bg_float },
FloatTitle = { fg = c.accent, bg = c.bg_float, bold = true },
WinBar = { fg = c.fg_dim, bg = c.none },
WinBarNC = { fg = c.fg_gutter, bg = c.none },
Cursor = { fg = c.bg, bg = c.fg },
lCursor = { link = "Cursor" },
CursorIM = { link = "Cursor" },
TermCursor = { link = "Cursor" },
TermCursorNC = { fg = c.bg, bg = c.fg_dim },
CursorLine = { bg = c.bg_cursorline },
CursorColumn = { link = "CursorLine" },
LineNr = { fg = c.fg_gutter },
CursorLineNr = { fg = c.fg_gutter_active, bold = true },
SignColumn = { fg = c.fg_gutter, bg = c.none },
FoldColumn = { fg = c.fg_gutter, bg = c.none },
Search = { fg = c.fg_bright, bg = c.bg_search },
IncSearch = { fg = c.bg, bg = c.bg_search },
CurSearch = { link = "IncSearch" },
Substitute = { fg = c.bg, bg = c.operator },
Visual = { bg = c.bg_selection },
VisualNOS = { link = "Visual" },
StatusLine = { fg = c.fg, bg = c.bg_float },
StatusLineNC = { fg = c.fg_dim, bg = c.bg_float },
WinSeparator = { fg = c.border },
VertSplit = { link = "WinSeparator" },
TabLine = { fg = c.fg_dim, bg = c.bg_float },
TabLineFill = { bg = c.bg_float },
TabLineSel = { fg = c.fg_bright, bg = c.bg, bold = true },
Pmenu = { fg = c.fg, bg = c.bg_float },
PmenuSel = { fg = c.fg_bright, bg = c.bg_selection },
PmenuSbar = { bg = c.bg_elevated },
PmenuThumb = { bg = c.fg_gutter },
PmenuKind = { fg = c.keyword, bg = c.bg_float },
PmenuKindSel = { fg = c.keyword, bg = c.bg_selection },
PmenuExtra = { fg = c.fg_dim, bg = c.bg_float },
PmenuExtraSel = { fg = c.fg_dim, bg = c.bg_selection },
Folded = { fg = c.fg_comment, bg = c.none },
NonText = { fg = c.nontext },
SpecialKey = { link = "NonText" },
Whitespace = { link = "NonText" },
EndOfBuffer = { link = "NonText" },
MatchParen = { fg = c.match_paren, bold = true },
ModeMsg = { fg = c.fg_bright, bold = true },
MsgArea = { fg = c.fg },
MoreMsg = { fg = c.accent },
Question = { fg = c.accent },
Directory = { fg = c.accent },
Title = { fg = c.accent, bold = true },
ErrorMsg = { fg = c.error },
WarningMsg = { fg = c.warning },
Conceal = { fg = c.fg_dim },
SpellBad = { sp = c.error, undercurl = true },
SpellCap = { sp = c.warning, undercurl = true },
SpellLocal = { sp = c.info, undercurl = true },
SpellRare = { sp = c.hint, undercurl = true },
ColorColumn = { bg = c.bg_cursorline },
QuickFixLine = { bg = c.bg_selection, bold = true },
WildMenu = { link = "PmenuSel" },
}
end
return M

View file

@ -0,0 +1,241 @@
local M = {}
---@param c Colors
function M.get(c)
return {
-- Git signs
GitSignsAdd = { fg = c.git_add },
GitSignsChange = { fg = c.git_change },
GitSignsDelete = { fg = c.git_delete },
-- Diff
DiffAdd = { bg = c.diff_add_bg },
DiffChange = { bg = c.diff_change_bg },
DiffDelete = { bg = c.diff_delete_bg },
DiffText = { bg = c.diff_text_bg },
Added = { fg = c.git_add },
Changed = { fg = c.git_change },
Removed = { fg = c.git_delete },
-- Telescope
TelescopeBorder = { fg = c.border, bg = c.bg_float },
TelescopeNormal = { fg = c.fg, bg = c.bg_float },
TelescopePromptBorder = { fg = c.border, bg = c.bg_float },
TelescopePromptNormal = { fg = c.fg, bg = c.bg_float },
TelescopePromptPrefix = { fg = c.accent },
TelescopePromptTitle = { fg = c.accent, bold = true },
TelescopePreviewTitle = { fg = c.string, bold = true },
TelescopeResultsTitle = { fg = c.keyword, bold = true },
TelescopeSelection = { bg = c.bg_selection },
TelescopeMatching = { fg = c.accent, bold = true },
-- nvim-cmp
CmpItemAbbr = { fg = c.fg },
CmpItemAbbrDeprecated = { fg = c.fg_gutter, strikethrough = true },
CmpItemAbbrMatch = { fg = c.accent, bold = true },
CmpItemAbbrMatchFuzzy = { fg = c.accent, bold = true },
CmpItemMenu = { fg = c.fg_comment },
CmpItemKindDefault = { fg = c.fg_dim },
CmpItemKindKeyword = { fg = c.keyword },
CmpItemKindVariable = { fg = c.variable },
CmpItemKindConstant = { fg = c.constant },
CmpItemKindReference = { fg = c.variable },
CmpItemKindValue = { fg = c.constant },
CmpItemKindFunction = { fg = c.func },
CmpItemKindMethod = { fg = c.func },
CmpItemKindConstructor = { fg = c.type },
CmpItemKindClass = { fg = c.type },
CmpItemKindInterface = { fg = c.type },
CmpItemKindStruct = { fg = c.type },
CmpItemKindEvent = { fg = c.keyword },
CmpItemKindEnum = { fg = c.type },
CmpItemKindUnit = { fg = c.constant },
CmpItemKindModule = { fg = c.fg_dim },
CmpItemKindProperty = { fg = c.operator },
CmpItemKindField = { fg = c.operator },
CmpItemKindTypeParameter = { fg = c.type },
CmpItemKindEnumMember = { fg = c.constant },
CmpItemKindOperator = { fg = c.operator },
CmpItemKindSnippet = { fg = c.special },
CmpItemKindText = { fg = c.fg },
CmpItemKindFile = { fg = c.fg },
CmpItemKindFolder = { fg = c.accent },
CmpItemKindColor = { fg = c.special },
-- blink.cmp
BlinkCmpMenu = { fg = c.fg, bg = c.bg_float },
BlinkCmpMenuBorder = { fg = c.border, bg = c.bg_float },
BlinkCmpMenuSelection = { bg = c.bg_selection },
BlinkCmpLabel = { fg = c.fg },
BlinkCmpLabelMatch = { fg = c.accent, bold = true },
BlinkCmpLabelDeprecated = { fg = c.fg_gutter, strikethrough = true },
BlinkCmpKind = { fg = c.fg_dim },
BlinkCmpKindFunction = { fg = c.func },
BlinkCmpKindMethod = { fg = c.func },
BlinkCmpKindConstructor = { fg = c.type },
BlinkCmpKindClass = { fg = c.type },
BlinkCmpKindStruct = { fg = c.type },
BlinkCmpKindInterface = { fg = c.type },
BlinkCmpKindModule = { fg = c.fg_dim },
BlinkCmpKindProperty = { fg = c.operator },
BlinkCmpKindField = { fg = c.operator },
BlinkCmpKindVariable = { fg = c.variable },
BlinkCmpKindKeyword = { fg = c.keyword },
BlinkCmpKindSnippet = { fg = c.special },
BlinkCmpKindText = { fg = c.fg },
BlinkCmpKindValue = { fg = c.constant },
BlinkCmpKindEnum = { fg = c.type },
BlinkCmpKindColor = { fg = c.special },
BlinkCmpKindFile = { fg = c.fg },
BlinkCmpKindFolder = { fg = c.accent },
BlinkCmpKindConstant = { fg = c.constant },
BlinkCmpKindTypeParameter = { fg = c.type },
BlinkCmpKindOperator = { fg = c.operator },
BlinkCmpDoc = { fg = c.fg, bg = c.bg_float },
BlinkCmpDocBorder = { fg = c.border, bg = c.bg_float },
-- Indent blankline
IblIndent = { fg = c.guide },
IblScope = { fg = c.guide_active },
IblWhitespace = { fg = c.guide },
-- Which-key
WhichKey = { fg = c.accent },
WhichKeyGroup = { fg = c.keyword },
WhichKeyDesc = { fg = c.fg },
WhichKeySeparator = { fg = c.fg_comment },
WhichKeyValue = { fg = c.fg_dim },
WhichKeyBorder = { fg = c.border },
-- Trouble
TroubleText = { fg = c.fg },
TroubleCount = { fg = c.accent, bold = true },
TroubleNormal = { fg = c.fg, bg = c.bg_float },
-- Nvim-tree
NvimTreeNormal = { fg = c.fg, bg = c.bg_dim },
NvimTreeRootFolder = { fg = c.accent, bold = true },
NvimTreeFolderName = { fg = c.accent },
NvimTreeFolderIcon = { link = "NvimTreeFolderName" },
NvimTreeOpenedFolderName = { link = "NvimTreeFolderName" },
NvimTreeOpenedHL = { fg = c.accent },
NvimTreeGitDirty = { fg = c.git_change },
NvimTreeGitNew = { fg = c.fg_dim },
NvimTreeGitDeleted = { fg = c.git_delete },
NvimTreeGitIgnored = { fg = c.git_ignore },
NvimTreeGitStaged = { fg = c.git_add },
NvimTreeSpecialFile = { fg = c.special },
NvimTreeIndentMarker = { fg = c.guide },
NvimTreeWinSeparator = { fg = c.border, bg = c.bg_dim },
-- Neo-tree
NeoTreeNormal = { fg = c.fg, bg = c.bg_float },
NeoTreeNormalNC = { fg = c.fg, bg = c.bg_float },
NeoTreeDimText = { fg = c.fg_dim },
NeoTreeRootName = { fg = c.accent, bold = true },
NeoTreeDirectoryName = { fg = c.accent },
NeoTreeDirectoryIcon = { fg = c.accent },
NeoTreeGitAdded = { fg = c.git_add },
NeoTreeGitModified = { fg = c.git_change },
NeoTreeGitDeleted = { fg = c.git_delete },
NeoTreeGitIgnored = { fg = c.git_ignore },
NeoTreeIndentMarker = { fg = c.guide },
NeoTreeWinSeparator = { fg = c.border },
-- Lazy
LazyButton = { fg = c.fg, bg = c.bg_elevated },
LazyButtonActive = { fg = c.fg_bright, bg = c.bg_selection, bold = true },
LazyH1 = { fg = c.bg, bg = c.accent, bold = true },
LazyProgressDone = { fg = c.accent },
LazyProgressTodo = { fg = c.fg_gutter },
LazySpecial = { fg = c.accent },
-- Mason
MasonHeader = { fg = c.bg, bg = c.accent, bold = true },
MasonHighlight = { fg = c.accent },
MasonHighlightBlock = { fg = c.bg, bg = c.accent },
MasonMutedBlock = { fg = c.fg, bg = c.bg_elevated },
-- Noice
NoiceCmdlinePopup = { fg = c.fg, bg = c.bg_float },
NoiceCmdlinePopupBorder = { fg = c.border },
NoiceCmdlineIcon = { fg = c.accent },
NoiceConfirm = { fg = c.fg, bg = c.bg_float },
NoiceConfirmBorder = { fg = c.border },
-- Notify
NotifyERRORBorder = { fg = c.error },
NotifyERRORIcon = { fg = c.error },
NotifyERRORTitle = { fg = c.error },
NotifyWARNBorder = { fg = c.warning },
NotifyWARNIcon = { fg = c.warning },
NotifyWARNTitle = { fg = c.warning },
NotifyINFOBorder = { fg = c.info },
NotifyINFOIcon = { fg = c.info },
NotifyINFOTitle = { fg = c.info },
NotifyDEBUGBorder = { fg = c.fg_comment },
NotifyDEBUGIcon = { fg = c.fg_comment },
NotifyDEBUGTitle = { fg = c.fg_comment },
NotifyTRACEBorder = { fg = c.keyword },
NotifyTRACEIcon = { fg = c.keyword },
NotifyTRACETitle = { fg = c.keyword },
-- Mini
MiniStatuslineModeNormal = { fg = c.bg, bg = c.accent, bold = true },
MiniStatuslineModeInsert = { fg = c.bg, bg = c.string, bold = true },
MiniStatuslineModeVisual = { fg = c.bg, bg = c.keyword, bold = true },
MiniStatuslineModeReplace = { fg = c.bg, bg = c.error, bold = true },
MiniStatuslineModeCommand = { fg = c.bg, bg = c.warning, bold = true },
MiniStatuslineModeOther = { fg = c.bg, bg = c.special, bold = true },
MiniStatuslineFilename = { fg = c.fg, bg = c.bg_elevated },
MiniStatuslineFileinfo = { fg = c.fg_dim, bg = c.bg_elevated },
MiniStatuslineDevinfo = { fg = c.fg_dim, bg = c.bg_elevated },
MiniStatuslineInactive = { fg = c.fg_gutter, bg = c.bg_elevated },
-- Snacks
SnacksIndent = { fg = c.guide },
SnacksIndentScope = { fg = c.guide_active },
-- Flash
FlashLabel = { fg = c.bg, bg = c.accent, bold = true },
FlashMatch = { fg = c.fg_dim, bg = c.bg_selection },
FlashCurrent = { fg = c.fg_bright, bg = c.bg_search },
-- Treesitter context
TreesitterContext = { bg = c.bg_elevated },
TreesitterContextLineNumber = { fg = c.fg_gutter_active, bg = c.bg_elevated },
-- Dashboard / Alpha
DashboardHeader = { fg = c.accent },
DashboardFooter = { fg = c.fg_comment, italic = true },
DashboardDesc = { fg = c.fg },
DashboardKey = { fg = c.constant },
DashboardIcon = { fg = c.accent },
DashboardShortCut = { fg = c.keyword },
-- Render-markdown
RenderMarkdownH1Bg = { fg = c.red, bg = c.red_dim },
RenderMarkdownH2Bg = { fg = c.orange, bg = c.orange_dim },
RenderMarkdownH3Bg = { fg = c.yellow, bg = c.yellow_dim },
RenderMarkdownH4Bg = { fg = c.green, bg = c.green_dim },
RenderMarkdownH5Bg = { fg = c.cyan, bg = c.cyan_dim },
RenderMarkdownH6Bg = { fg = c.blue, bg = c.blue_dim },
RenderMarkdownCode = { bg = c.bg_elevated },
-- Oil
OilDir = { fg = c.accent },
OilDirIcon = { fg = c.accent },
OilFile = { fg = c.fg },
OilCreate = { fg = c.git_add },
OilDelete = { fg = c.git_delete },
OilMove = { fg = c.git_change },
OilCopy = { fg = c.info },
-- Fzf-lua
FzfLuaBorder = { fg = c.border },
FzfLuaTitle = { fg = c.accent, bold = true },
FzfLuaCursorLine = { bg = c.bg_selection },
}
end
return M

98
lua/skift/groups/lsp.lua Normal file
View file

@ -0,0 +1,98 @@
local M = {}
---@param c Colors
function M.get(c)
return {
LspReferenceText = { bg = c.bg_selection },
LspReferenceRead = { bg = c.bg_selection },
LspReferenceWrite = { bg = c.bg_selection, bold = true },
LspCodeLens = { fg = c.fg_comment },
LspCodeLensSeparator = { fg = c.fg_gutter },
LspSignatureActiveParameter = { fg = c.accent, bold = true },
LspInlayHint = { fg = c.fg_gutter, italic = true },
DiagnosticError = { fg = c.error },
DiagnosticWarn = { fg = c.warning },
DiagnosticInfo = { fg = c.info },
DiagnosticHint = { fg = c.hint },
DiagnosticOk = { fg = c.ok },
DiagnosticSignError = { fg = c.error },
DiagnosticSignWarn = { fg = c.warning },
DiagnosticSignInfo = { fg = c.info },
DiagnosticSignHint = { fg = c.hint },
DiagnosticSignOk = { fg = c.ok },
DiagnosticVirtualTextError = { fg = c.error, bg = c.bg_elevated },
DiagnosticVirtualTextWarn = { fg = c.warning, bg = c.bg_elevated },
DiagnosticVirtualTextInfo = { fg = c.info, bg = c.bg_elevated },
DiagnosticVirtualTextHint = { fg = c.hint, bg = c.bg_elevated },
DiagnosticVirtualTextOk = { fg = c.ok, bg = c.bg_elevated },
DiagnosticUnderlineError = { sp = c.error, undercurl = true },
DiagnosticUnderlineWarn = { sp = c.warning, undercurl = true },
DiagnosticUnderlineInfo = { sp = c.info, undercurl = true },
DiagnosticUnderlineHint = { sp = c.hint, undercurl = true },
DiagnosticUnderlineOk = { sp = c.ok, undercurl = true },
DiagnosticFloatingError = { fg = c.error },
DiagnosticFloatingWarn = { fg = c.warning },
DiagnosticFloatingInfo = { fg = c.info },
DiagnosticFloatingHint = { fg = c.hint },
DiagnosticFloatingOk = { fg = c.ok },
["@lsp.type.boolean"] = { link = "@boolean" },
["@lsp.type.builtinType"] = { link = "@type.builtin" },
["@lsp.type.comment"] = { link = "@comment" },
["@lsp.type.decorator"] = { link = "@attribute" },
["@lsp.type.deriveHelper"] = { link = "@attribute" },
["@lsp.type.enum"] = { link = "@type" },
["@lsp.type.enumMember"] = { link = "@constant" },
["@lsp.type.escapeSequence"] = { link = "@string.escape" },
["@lsp.type.formatSpecifier"] = { link = "@punctuation.special" },
["@lsp.type.function"] = { link = "@function" },
["@lsp.type.generic"] = { link = "@variable" },
["@lsp.type.interface"] = { link = "@type" },
["@lsp.type.keyword"] = { link = "@keyword" },
["@lsp.type.lifetime"] = { link = "@keyword.modifier" },
["@lsp.type.macro"] = { link = "@function.macro" },
["@lsp.type.method"] = { link = "@function.method" },
["@lsp.type.namespace"] = { link = "@module" },
["@lsp.type.number"] = { link = "@number" },
["@lsp.type.operator"] = { link = "@operator" },
["@lsp.type.parameter"] = { link = "@variable.parameter" },
["@lsp.type.property"] = { link = "@property" },
["@lsp.type.selfKeyword"] = { link = "@variable.builtin" },
["@lsp.type.selfTypeKeyword"] = { link = "@variable.builtin" },
["@lsp.type.string"] = { link = "@string" },
["@lsp.type.struct"] = { link = "@type" },
["@lsp.type.type"] = { link = "@type" },
["@lsp.type.typeAlias"] = { link = "@type.definition" },
["@lsp.type.typeParameter"] = { link = "@type" },
["@lsp.type.unresolvedReference"] = { sp = c.error, undercurl = true },
["@lsp.type.variable"] = {},
["@lsp.typemod.class.defaultLibrary"] = { link = "@type.builtin" },
["@lsp.typemod.enum.defaultLibrary"] = { link = "@type.builtin" },
["@lsp.typemod.enumMember.defaultLibrary"] = { link = "@constant.builtin" },
["@lsp.typemod.function.defaultLibrary"] = { link = "@function.builtin" },
["@lsp.typemod.keyword.async"] = { link = "@keyword.coroutine" },
["@lsp.typemod.keyword.injected"] = { link = "@keyword" },
["@lsp.typemod.macro.defaultLibrary"] = { link = "@function.builtin" },
["@lsp.typemod.method.defaultLibrary"] = { link = "@function.builtin" },
["@lsp.typemod.operator.injected"] = { link = "@operator" },
["@lsp.typemod.string.injected"] = { link = "@string" },
["@lsp.typemod.struct.defaultLibrary"] = { link = "@type.builtin" },
["@lsp.typemod.type.defaultLibrary"] = { link = "@type.builtin" },
["@lsp.typemod.typeAlias.defaultLibrary"] = { link = "@type.builtin" },
["@lsp.typemod.variable.callable"] = { link = "@function" },
["@lsp.typemod.variable.defaultLibrary"] = { link = "@variable.builtin" },
["@lsp.typemod.variable.injected"] = { link = "@variable" },
["@lsp.typemod.variable.static"] = { link = "@constant" },
["@lsp.typemod.property"] = { link = "@property" },
["@lsp.typemod.property.declaration"] = { link = "@variable" }, -- a property declaration is a variable
}
end
return M

View file

@ -0,0 +1,46 @@
local M = {}
---@param c Colors
function M.get(c)
return {
Comment = { fg = c.fg_comment, italic = true },
String = { fg = c.string },
Character = { fg = c.string },
Number = { fg = c.number },
Float = { link = "Number" },
Boolean = { link = "Constant" },
Identifier = { link = "Constant" },
Function = { link = "Normal" },
Statement = { link = "Keyword" },
Conditional = { link = "Keyword" },
Repeat = { link = "Keyword" },
Label = { fg = c.tag },
Operator = { fg = c.operator },
Keyword = { fg = c.keyword },
Exception = { fg = c.error },
PreProc = { fg = c.special },
Include = { link = "PreProc" },
Define = { link = "PreProc" },
Macro = { link = "PreProc" },
PreCondit = { link = "PreProc" },
Type = { fg = c.type },
StorageClass = { fg = c.keyword, italic = true },
Structure = { fg = c.type, bold = true },
Typedef = { link = "PreProc" },
Constant = { fg = c.constant },
Special = { fg = c.special },
SpecialChar = { link = "Special" },
Tag = { fg = c.tag },
Delimiter = { fg = c.fg_dim },
SpecialComment = { fg = c.constant, italic = true },
Debug = { fg = c.hint },
Underlined = { fg = c.accent, underline = true },
Bold = { bold = true },
Italic = { italic = true },
Ignore = { fg = c.nontext },
Error = { fg = c.error },
Todo = { fg = c.bg, bg = c.info, bold = true },
}
end
return M

View file

@ -0,0 +1,121 @@
local M = {}
---@param c Colors
function M.get(c)
return {
["@variable"] = { fg = c.variable },
["@variable.builtin"] = { fg = c.variable, italic = true },
["@variable.parameter"] = { link = "@variable" },
["@variable.parameter.builtin"] = { link = "@variable.builtin" },
["@variable.member"] = { link = "@variable" },
["@property"] = { fg = c.property },
["@constant"] = { fg = c.constant },
["@constant.builtin"] = { fg = c.constant, bold = true },
["@constant.macro"] = { fg = c.special, bold = true },
["@preproc"] = { link = "PreProc" },
["@module"] = { link = "@preproc" },
["@module.builtin"] = { link = "@module" },
["@label"] = { fg = c.tag },
["@string"] = { link = "String" },
["@string.documentation"] = { link = "@string" },
["@string.escape"] = { fg = c.special },
["@string.regexp"] = { fg = c.special },
["@string.special"] = { fg = c.special },
["@string.special.symbol"] = { link = "@string.special" },
["@string.special.url"] = { fg = c.special, underline = true },
["@string.special.path"] = { link = "@string.special" },
["@character"] = { link = "String" },
["@character.special"] = { link = "@string.special" },
["@number"] = { link = "Number" },
["@number.float"] = { link = "@number" },
["@boolean"] = { link = "Boolean" },
["@type"] = { link = "Type" },
["@type.builtin"] = { link = "@type" },
["@type.definition"] = { link = "@type" },
["@type.qualifier"] = { link = "@type" },
["@attribute"] = { fg = c.type },
["@attribute.builtin"] = { fg = c.type, italic = true },
["@function"] = { link = "Function" },
["@function.builtin"] = { link = "@function" },
["@function.call"] = { link = "@function" },
["@function.macro"] = { link = "@function" },
["@function.method"] = { link = "@function" },
["@function.method.call"] = { link = "@function" },
["@constructor"] = { link = "Normal" },
["@class"] = { link = "Normal" },
["@operator"] = { link = "Operator" },
["@keyword"] = { link = "Keyword" },
["@keyword.coroutine"] = { fg = c.special },
["@keyword.function"] = { link = "@keyword" },
["@keyword.operator"] = { link = "@operator" },
["@keyword.import"] = { link = "@keyword" },
["@keyword.type"] = { link = "@keyword" },
["@keyword.modifier"] = { link = "@keyword" },
["@keyword.repeat"] = { link = "@keyword" },
["@keyword.return"] = { link = "@keyword" },
["@keyword.debug"] = { fg = c.error },
["@keyword.exception"] = { fg = c.error },
["@keyword.conditional"] = { link = "Conditional" },
["@keyword.conditional.ternary"] = { link = "Conditional" },
["@keyword.directive"] = { link = "PreProc" },
["@keyword.directive.define"] = { link = "@keyword.directive" },
["@punctuation.bracket"] = { link = "Operator" },
["@punctuation.delimiter"] = { link = "Operator" },
["@punctuation.special"] = { link = "Operator" },
["@comment"] = { link = "Comment" },
["@comment.documentation"] = { link = "@comment" },
["@comment.error"] = { fg = c.error },
["@comment.warning"] = { fg = c.warning },
["@comment.todo"] = { fg = c.info },
["@comment.note"] = { fg = c.hint },
["@markup.strong"] = { fg = c.fg_bright, bold = true },
["@markup.italic"] = { fg = c.fg_bright, italic = true },
["@markup.strikethrough"] = { fg = c.fg_dim, strikethrough = true },
["@markup.underline"] = { underline = true },
["@markup.heading"] = { fg = c.red, bold = true },
["@markup.heading.1"] = { fg = c.red, bold = true },
["@markup.heading.2"] = { fg = c.orange, bold = true },
["@markup.heading.3"] = { fg = c.yellow, bold = true },
["@markup.heading.4"] = { fg = c.green, bold = true },
["@markup.heading.5"] = { fg = c.cyan, bold = true },
["@markup.heading.6"] = { fg = c.blue, bold = true },
["@markup.quote"] = { fg = c.fg_dim, italic = true },
["@markup.math"] = { fg = c.constant },
["@markup.environment"] = { fg = c.keyword },
["@markup.link"] = { fg = c.string, underline = true },
["@markup.link.label"] = { fg = c.string },
["@markup.link.url"] = { fg = c.string, underline = true },
["@markup.raw"] = { fg = c.rose },
["@markup.raw.block"] = { fg = c.fg },
["@markup.list"] = { fg = c.operator },
["@markup.list.checked"] = { fg = c.ok },
["@markup.list.unchecked"] = { fg = c.fg_dim },
["@tag"] = { fg = c.keyword },
["@tag.builtin"] = { fg = c.keyword, italic = true },
["@tag.attribute"] = { link = "@tag" },
["@tag.delimiter"] = { fg = c.operator },
["@diff.plus"] = { fg = c.git_add },
["@diff.minus"] = { fg = c.git_delete },
["@diff.delta"] = { fg = c.git_change },
}
end
return M

138
lua/skift/init.lua Normal file
View file

@ -0,0 +1,138 @@
local config = require('skift.config')
local cache = require("skift.utils.cache")
local M = {}
local function apply_highlights(highlights)
for name, val in pairs(highlights) do
if type(val) == 'string' then
val = { link = val }
end
vim.api.nvim_set_hl(0, name, val)
end
end
local function apply_terminal_colors(c)
vim.g.terminal_color_0 = c.terminal_black
vim.g.terminal_color_1 = c.terminal_red
vim.g.terminal_color_2 = c.terminal_green
vim.g.terminal_color_3 = c.terminal_yellow
vim.g.terminal_color_4 = c.terminal_blue
vim.g.terminal_color_5 = c.terminal_magenta
vim.g.terminal_color_6 = c.terminal_cyan
vim.g.terminal_color_7 = c.terminal_white
vim.g.terminal_color_8 = c.terminal_bright_black
vim.g.terminal_color_9 = c.terminal_bright_red
vim.g.terminal_color_10 = c.terminal_bright_green
vim.g.terminal_color_11 = c.terminal_bright_yellow
vim.g.terminal_color_12 = c.terminal_bright_blue
vim.g.terminal_color_13 = c.terminal_bright_magenta
vim.g.terminal_color_14 = c.terminal_bright_cyan
vim.g.terminal_color_15 = c.terminal_bright_white
end
function M.setup(opts)
config:merge(opts)
end
function M.load()
if vim.g.colors_name then
vim.cmd("hi clear")
end
vim.g.colors_name = "skift"
vim.o.termguicolors = true
vim.o.background = "dark"
local cache_key = cache.key(config)
local cached = cache.read(cache_key)
if cached and cached.highlights and cached.colors then
apply_highlights(cached.highlights)
apply_terminal_colors(cached.colors)
return
end
local c = vim.deepcopy(require("skift.colors"))
config.on_colors(c)
local highlights = {}
local groups = {
require("skift.groups.editor").get(c),
require("skift.groups.syntax").get(c),
require("skift.groups.treesitter").get(c),
require("skift.groups.lsp").get(c),
require("skift.groups.integrations").get(c),
}
for _, group in ipairs(groups) do
highlights = vim.tbl_extend("force", highlights, group)
end
if config.transparent then
local transparent_groups = {
"Normal", "NormalNC", "NormalFloat", "SignColumn",
"FoldColumn", "TabLineFill", "StatusLine", "StatusLineNC",
"NvimTreeNormal", "NeoTreeNormal", "NeoTreeNormalNC",
"TreesitterContext",
}
for _, name in ipairs(transparent_groups) do
if highlights[name] then
highlights[name].bg = "NONE"
end
end
highlights.WinSeparator = { fg = "NONE" }
highlights.VertSplit = { fg = "NONE" }
highlights.FloatBorder = { fg = "NONE", bg = "NONE" }
end
if not config.bold then
for _, hl in pairs(highlights) do
hl.bold = false
end
end
if not config.italic then
for _, hl in pairs(highlights) do
hl.italic = false
end
elseif not config.italic_comments then
for _, name in ipairs({ "Comment", "@comment", "@comment.documentation" }) do
if highlights[name] then
highlights[name].italic = false
end
end
end
config.on_highlights(highlights, c)
apply_highlights(highlights)
apply_terminal_colors(c)
cache.write(cache_key, {
highlights = highlights,
colors = {
terminal_black = c.terminal_black,
terminal_red = c.terminal_red,
terminal_green = c.terminal_green,
terminal_yellow = c.terminal_yellow,
terminal_blue = c.terminal_blue,
terminal_magenta = c.terminal_magenta,
terminal_cyan = c.terminal_cyan,
terminal_white = c.terminal_white,
terminal_bright_black = c.terminal_bright_black,
terminal_bright_red = c.terminal_bright_red,
terminal_bright_green = c.terminal_bright_green,
terminal_bright_yellow = c.terminal_bright_yellow,
terminal_bright_blue = c.terminal_bright_blue,
terminal_bright_magenta = c.terminal_bright_magenta,
terminal_bright_cyan = c.terminal_bright_cyan,
terminal_bright_white = c.terminal_bright_white,
},
})
end
function M.clear_cache()
cache.clear()
end
return M

107
lua/skift/utils/cache.lua Normal file
View file

@ -0,0 +1,107 @@
local M = {}
local CACHE_VERSION = 1
local CACHE_DIR = vim.fn.stdpath("cache") .. "/skift"
local SOURCE_FILES = {
"lua/skift/palette.lua",
"lua/skift/init.lua",
"lua/skift/groups/editor.lua",
"lua/skift/groups/syntax.lua",
"lua/skift/groups/treesitter.lua",
"lua/skift/groups/lsp.lua",
"lua/skift/groups/integrations.lua",
"lua/skift/utils/palette.lua",
}
local function function_fingerprint(fn)
if type(fn) ~= "function" then
return tostring(fn)
end
local ok, dumped = pcall(string.dump, fn)
if ok and dumped then
return vim.fn.sha256(dumped)
end
local info = debug.getinfo(fn, "S")
if info and info.source then
return string.format("%s:%d", info.source, info.linedefined or 0)
end
return tostring(fn)
end
local function source_fingerprint()
local stamp = {}
for _, pattern in ipairs(SOURCE_FILES) do
local path = vim.api.nvim_get_runtime_file(pattern, false)[1]
if path then
local stat = vim.loop.fs_stat(path)
stamp[#stamp + 1] = {
file = pattern,
mtime = stat and stat.mtime and stat.mtime.sec or 0,
size = stat and stat.size or 0,
}
else
stamp[#stamp + 1] = { file = pattern, mtime = 0, size = 0 }
end
end
return stamp
end
local function cache_file(cache_key)
return CACHE_DIR .. "/highlights-" .. cache_key .. ".json"
end
---@param config Config
function M.key(config)
local payload = {
version = CACHE_VERSION,
transparent = config.transparent,
bold = config.bold,
italic = config.italic,
italic_comments = config.italic_comments,
on_colors = function_fingerprint(config.on_colors),
on_highlights = function_fingerprint(config.on_highlights),
sources = source_fingerprint(),
}
return vim.fn.sha256(vim.json.encode(payload))
end
function M.read(cache_key)
local path = cache_file(cache_key)
if vim.fn.filereadable(path) == 0 then
return nil
end
local ok_read, lines = pcall(vim.fn.readfile, path)
if not ok_read or not lines then
return nil
end
local raw = table.concat(lines, "\n")
local ok_decode, decoded = pcall(vim.json.decode, raw)
if not ok_decode then
return nil
end
return decoded
end
function M.write(cache_key, payload)
vim.fn.mkdir(CACHE_DIR, "p")
local path = cache_file(cache_key)
local encoded = vim.json.encode(payload)
local ok_write = pcall(vim.fn.writefile, { encoded }, path)
return ok_write
end
function M.clear()
vim.fn.delete(CACHE_DIR, "rf")
end
return M

71
lua/skift/utils/hsl.lua Normal file
View file

@ -0,0 +1,71 @@
-- https://github.com/EmmanuelOga/columns/blob/master/utils/color.lua
local M = {}
--- Converts an HSL color value to RGB. Conversion formula
--- adapted from http://en.wikipedia.org/wiki/HSL_color_space.
--- Assumes h, s, and l are contained in the set [0, 1] and
--- returns r, g, and b in the set [0, 255].
---
--- @param h number The hue
--- @param s number The saturation
--- @param l number The lightness
--- @return number, number, number # The RGB representation
function M.hslToRgb(h, s, l)
--- @type number, number, number
local r, g, b
if s == 0 then
r, g, b = l, l, l -- achromatic
else
--- @param p number
--- @param q number
--- @param t number
local function hue2rgb(p, q, t)
if t < 0 then
t = t + 1
end
if t > 1 then
t = t - 1
end
if t < 1 / 6 then
return p + (q - p) * 6 * t
end
if t < 1 / 2 then
return q
end
if t < 2 / 3 then
return p + (q - p) * (2 / 3 - t) * 6
end
return p
end
--- @type number
local q
if l < 0.5 then
q = l * (1 + s)
else
q = l + s - l * s
end
local p = 2 * l - q
r = hue2rgb(p, q, h + 1 / 3)
g = hue2rgb(p, q, h)
b = hue2rgb(p, q, h - 1 / 3)
end
return r * 255, g * 255, b * 255
end
--- Converts an HSL color value to RGB in Hex representation.
--- @param h number The hue
--- @param s number The saturation
--- @param l number The lightness
--- @return string # The hex representation
function M.hslToHex(h, s, l)
local r, g, b = M.hslToRgb(h / 360, s / 100, l / 100)
return string.format("#%02x%02x%02x", r, g, b)
end
return M

View file

@ -0,0 +1,53 @@
local cache = setmetatable({}, { __mode = 'k' })
---@param definitions table<string, string>
---@return table<string, string>
return function(definitions)
local cached = cache[definitions]
if cached ~= nil then
return cached
end
local resolved = {}
local resolving = {}
local function resolve(key)
if resolved[key] ~= nil then
return resolved[key]
end
local value = definitions[key]
if value == nil then
return nil
end
if type(value) ~= 'string' then
resolved[key] = value
return value
end
if resolving[key] then
error('sora.palette: cyclic color alias detected for key "' .. key .. '"')
end
if definitions[value] == nil then
resolved[key] = value
return value
end
resolving[key] = true
local alias_value = resolve(value)
resolving[key] = nil
resolved[key] = alias_value
return alias_value
end
for key in pairs(definitions) do
resolve(key)
end
cache[definitions] = resolved
return resolved
end