-- lua/oasis/theme_generator.lua local Config = require("oasis.config") -- Groups to give transparency if enabled local TRANSPARENT_GROUPS = { "Normal", "NormalNC", "NormalFloat", "SignColumn", "FoldColumn", "StatusLine", "StatusLineNC", "TabLine", "TabLineFill", "Pmenu", "PmenuSbar", "CursorLine", "ColorColumn", "FloatBorder", } ---Create the full highlight table for a palette. ---@param c OasisPalette Color palette ---@param light_mode boolean Whether palette is light mode ---@param theme {primary: string, primary_light: string, primary_strong: string, secondary: string, title?: string} ---@return OasisHighlightGroupMap highlights local function create_highlights(c, light_mode, theme) local match_paren_bg = Config.get().match_paren_bg ~= false -- stylua: ignore start local highlights = { -- Main Theme Colors (Highlights for plugins) OasisTitle = { fg=theme.title, bg="NONE" }, OasisStrongPrimary = { fg=theme.primary_strong, bg="NONE" }, OasisPrimary = { fg=theme.primary, bg="NONE" }, OasisLightPrimary = { fg=theme.primary_light, bg="NONE" }, OasisFloatPrimary = { fg=theme.primary, bg=c.ui.float.border.bg }, OasisSecondary = { fg=theme.secondary, bg="NONE" }, OasisFloatSecondary = { fg=theme.secondary, bg=c.ui.float.border.bg }, OasisAccent = { fg=c.theme.accent, bg="NONE" }, -- The following are the Neovim (as of 0.8.0-dev+100-g371dfb174) highlight -- groups, mostly used for styling UI elements. -- See :h highlight-groups ColorColumn = { fg=c.fg.core, bg=c.bg.mantle }, -- Columns set with 'colorcolumn' Conceal = { fg=c.fg.muted }, -- Placeholder characters substituted for concealed text (see 'conceallevel') Cursor = { fg=c.bg.core, bg=(light_mode and c.syntax.statement or c.terminal.yellow) }, -- Character under the cursor CurSearch = { fg=c.ui.match.fg, bg=c.ui.match.bg, bold=true }, -- Highlighting a search pattern under the cursor (see 'hlsearch') lCursor = { fg=c.bg.core, bg=c.syntax.exception }, -- Character under the cursor when |language-mapping| is used (see 'guicursor') CursorIM = "Cursor", -- Like Cursor, but used when in IME mode |CursorIM| CursorColumn = { bg=c.ui.cursor_line }, -- Screen-column at the cursor, when 'cursorcolumn' is set. CursorLine = { bg=c.ui.cursor_line }, -- Screen-line at the cursor, when 'cursorline' is set. Low-priority if foreground (ctermc.fg OR guifg) is not set. Added = { fg=c.diff.add }, -- Added line Changed = { fg=c.diff.change }, -- Changed line Removed = { fg=c.diff.delete }, -- Deleted line DiffAdd = { bg=c.diff.add }, -- Diff mode: Added line |diff.txt| DiffChange = { bg=c.diff.change }, -- Diff mode: Changed line |diff.txt| DiffDelete = { bg=c.diff.delete }, -- Diff mode: Deleted line |diff.txt| DiffText = { bg=c.diff.text }, -- Diff mode: Changed text within a changed line |diff.txt| Directory = { fg=c.ui.dir }, -- Directory names (and other special names in listings) TermCursor = { reverse=true }, -- Cursor in a focused terminal TermCursorNC = { reverse=true }, -- Cursor in an unfocusd terminal ErrorMsg = { fg=c.ui.diag.error.fg, bg=c.ui.diag.error.bg }, -- Error messages on the command line Folded = { fg=c.syntax.statement, bg=c.bg.surface }, -- Line used for closed folds FoldColumn = { fg=c.fg.muted, bg=c.bg.core }, -- 'foldcolumn' SignColumn = { fg=c.fg.muted, bg="NONE" }, -- Column where |signs| are displayed IncSearch = { fg=c.ui.match.fg, bg=c.ui.match.bg, bold=true }, -- 'incsearch' highlighting; also used for the text replaced with ":s///c" Search = { fg=c.ui.search.fg, bg=c.ui.search.bg, bold=true }, -- Last search pattern highlighting (see 'hlsearch'). Also used for similar items that need to stand out. Substitute = "Search", -- |:substitute| replacement text highlighting LineNr = { fg=c.fg.muted, bg=(c.bg.gutter or "NONE") }, -- Line number for ":number" and ":#" commands, and when 'number' or 'relativenumber' option is set. LineNrAbove = "LineNr", -- Line number for when the 'relativenumber' option is set, above the cursor line LineNrBelow = "LineNr", -- Line number for when the 'relativenumber' option is set, below the cursor line CursorLineNr = { fg=c.ui.line_number, bg=(c.bg.gutter or "NONE"), bold=true }, -- Like LineNr when 'cursorline' or 'relativenumber' is set for the cursor line. CursorLineFold = { bg=c.bg.core }, -- Like FoldColumn when 'cursorline' is set for the cursor line CursorLineSign = { bg="NONE" }, -- Like SignColumn when 'cursorline' is set for the cursor line MatchParen = { fg=c.ui.match_parent.fg, bg=(match_paren_bg and c.ui.match_parent.bg or "NONE"), bold=true, underline=true, reverse=match_paren_bg }, -- Character under the cursor or just before it, if it is a paired bracket, and its match. |pi_paren.txt| ModeMsg = { fg=c.syntax.statement, bold=true }, -- 'showmode' message (e.g., "-- INSERT -- ") MsgArea = { fg=c.syntax.statement }, -- Area for messages and cmdline MoreMsg = { fg=c.syntax.type, bold=true }, -- |more-prompt| NonText = { fg=c.ui.nontext }, -- '@' at the end of the window, characters from 'showbreak' and other non-existant characters. See also |hl-EndOfBuffer|. EndOfBuffer = "NonText", -- Filler lines (~) after the end of the buffer. By default, this is highlighted like |hl-NonText|. Normal = { fg=c.fg.core, bg=c.bg.core }, -- Normal text NormalNC = "Normal", -- normal text in non-current windows NormalFloat = { fg=c.ui.float.fg, bg=c.ui.float.bg }, -- Normal text in floating windows. FloatBorder = { fg=c.ui.float.border.fg, bg=c.ui.float.border.bg }, -- Border of floating windows. FloatTitle = { fg=c.ui.float.title, bg=c.ui.float.border.bg, bold=true }, -- Title of floating windows. Pmenu = "NormalFloat", -- Popup menu: Normal item. PmenuSel = { bg=c.ui.visual.bg, bold=true }, -- Popup menu: Selected item. PmenuMatch = { fg=c.theme.accent }, PmenuKind = "Pmenu", -- Popup menu: Normal item "kind" PmenuKindSel = "PmenuSel", -- Popup menu: Selected item "kind" PmenuExtra = "Pmenu", -- Popup menu: Normal item "extra text" PmenuExtraSel = "PmenuSel", -- Popup menu: Selected item "extra text" PmenuBorder = "FloatBorder", -- Popup menu: border of popup menu. PmenuSbar = { bg=c.bg.core }, -- Popup menu: Scrollbar. PmenuThumb = { bg=c.fg.muted }, -- Popup menu: Thumb of the scrollbar. Question = { fg=c.ui.diag.ok.fg, bold=true }, -- |hit-enter| prompt and yes/no questions QuickFixLine = { fg=c.bg.core, bg=c.syntax.statement }, -- Current |quickfix| item in the quickfix window. Combined with |hl-CursorLine| when the cursor is there. SpecialKey = { fg=c.syntax.type }, -- Unprintable characters: text displayed differently from what it really is. But not 'listchars' whitespace. |hl-Whitespace| SpellBad = { bg=c.bg.shadow, undercurl=true, sp=c.ui.diag.error.fg }, -- Word that is not recognized by the spellchecker. |spell| Combined with the highlighting used otherwise. SpellCap = { bg=c.bg.shadow, undercurl=true, sp=c.ui.diag.info.fg }, -- Word that should start with a capital. |spell| Combined with the highlighting used otherwise. SpellLocal = { bg=c.bg.shadow, undercurl=true, sp=c.ui.diag.warn.fg }, -- Word that is recognized by the spellchecker as one that is used in another region. |spell| Combined with the highlighting used otherwise. SpellRare = { bg=c.bg.shadow, undercurl=true, sp=c.ui.diag.hint.fg }, -- Word that is recognized by the spellchecker as one that is hardly ever used. |spell| Combined with the highlighting used otherwise. StatusLine = { fg=theme.primary_light, bg=c.bg.mantle }, -- Status line of current window StatusLineNC = { fg=c.fg.muted, bg=c.bg.mantle }, -- Status lines of not-current windows. Note: If this is equal to "StatusLine" Vim will use "^^^" in the status line of the current window. StatusLineTerm = "StatusLine", -- Status lines of not-current windows. Note: If this is equal to "StatusLine" Vim will use "^^^" in the status line of the current window. TabLine = { fg=c.ui.border, bg=c.bg.surface }, -- Tab pages line, not active tab page label TabLineFill = { fg=c.ui.border, bg=c.bg.surface }, -- Tab pages line, where there are no labels TabLineSel = { fg=c.bg.core, bg=theme.secondary }, -- Tab pages line, active tab page label MsgSeparator = "StatusLine", -- Separator for scrolled messages, `msgsep` flag of 'display' Title = { fg=c.ui.title, bold=true }, -- Titles for output from ":set all", ":autocmd" etc. VertSplit = { fg=c.ui.border, bg=c.bg.crust }, -- Column separating vertically split windows Visual = { bg=c.ui.visual.bg, fg = (c.ui.visual.fg or "NONE") }, -- Visual mode selection VisualNOS = { bg=c.ui.visual.bg }, -- Visual mode selection when vim is "Not Owning the Selection". WarningMsg = { fg=c.ui.diag.warn.fg, bold=true }, -- Warning messages Whitespace = "NonText", -- "nbsp", "space", "tab" and "trail" in 'listchars' Winseparator = "VertSplit", -- Separator between window splits. Inherts from |hl-VertSplit| by default, which it will replace eventually. WildMenu = { fg=c.bg.core, bg=c.ui.diag.warn.fg }, -- Current match in 'wildmenu' completion WinBar = { bold=true }, -- Window bar of current window WinBarNC = "WinBar", -- Window bar of not-current windows -- Common vim syntax groups used for all kinds of code and markup. -- Commented-out groups should chain up to their preferred (*) group -- by default. -- See :h group-name Comment = { fg=c.syntax.comment, italic=true }, -- Any comment Constant = { fg=c.syntax.constant }, -- (*) Any constant String = { fg=c.syntax.string }, -- A string constant: "this is a string" Character = "String", -- A character constant: 'c', '\n' Number = "Constant", -- A number constant: 234, 0xff Float = "Number", -- A floating point constant: 2.3e10 Boolean = { fg=c.syntax.constant, bold=true }, -- A boolean constant: TRUE, FALSE Identifier = { fg=c.syntax.identifier }, -- (*) Any variable name Function = { fg=c.syntax.func }, -- Function name (also: methods for classes) Statement = { fg=c.syntax.statement, bold=true }, -- (*) Any statement Keyword = { fg=c.syntax.identifier, bold=true }, -- any other keyword Conditional = { fg=c.syntax.conditional, bold=true }, -- if, then, else, endif, switch, etc. Repeat = "Conditional", -- for, do, while, etc. Label = "Conditional", -- case, default, etc. Operator = { fg=c.syntax.operator }, -- "sizeof", "+", "*", etc. Exception = { fg=c.syntax.exception, bold=true }, -- try, catch, throw PreProc = { fg=c.syntax.preproc, bold=true, italic=true }, -- (*) Generic Preprocessor Include = "PreProc", -- Preprocessor #include Define = "PreProc", -- Preprocessor #define PreCondit = "PreProc", -- Preprocessor #if, #else, #endif, etc. Macro = { fg=c.syntax.macro, italic=true, bold=true }, -- Macros Type = { fg=c.syntax.type }, -- (*) int, long, char, etc. StorageClass = { fg=c.syntax.type, bold=true }, -- static, register, volatile, etc. Structure = "Type", -- struct, union, enum, etc. Typedef = { fg=c.syntax.typedef, italic=true }, -- A typedef Special = { fg=c.syntax.special }, -- (*) Any special symbol SpecialChar = "Exception", -- Special character in a constant Tag = { fg=c.syntax.special, underline=true }, -- You can use CTRL-] on this Delimiter = { fg=c.syntax.delimiter }, -- Character that needs attention SpecialComment = { fg=c.syntax.special, bold=true, italic=true }, -- Special things inside a comment (e.g. '\n') Debug = "Constant", -- Debugging statements Underlined = { underline=true }, -- Text that stands out, HTML links Ignore = { fg=c.fg.muted }, -- Left blank, hidden |hl-Ignore| Error = { fg=c.bg.core, bg=c.ui.diag.error.fg, reverse=true }, -- Any erroneous construct Todo = { fg=c.bg.core, bg=c.ui.diag.warn.fg, bold=true }, -- Anything that needs extra attention; mostly the keywords TODO FIXME and XXX -- These groups are for the native LSP client and diagnostic system. Some -- other LSP clients may use these groups, or use their own. Consult your -- LSP client's documentation. -- See :h lsp-highlight, some groups may not be listed, submit a PR fix to lush-template! LspReferenceText = { bg=c.bg.surface } , -- Used for highlighting "text" references LspReferenceRead = { bg=c.bg.surface } , -- Used for highlighting "read" references LspReferenceWrite = { bg=c.bg.surface } , -- Used for highlighting "write" references LspInlayHint = { fg=c.ui.nontext, bg=c.bg.shadow, italic=true } , -- Used to color the virtual text of the codelens. See |nvim_buf_set_extmark()|. LspCodeLens = { fg=c.fg.muted } , -- Used to color the virtual text of the codelens. See |nvim_buf_set_extmark()|. LspCodeLensSeparator = { fg=c.fg.muted } , -- Used to color the seperator between two or more code lens. LspSignatureActiveParameter= { fg=c.bg.core, bg=c.syntax.constant } , -- Used to highlight the active parameter in the signature help. See |vim.lsp.handlers.signature_help()|. -- See :h diagnostic-highlights, some groups may not be listed DiagnosticError = { fg=c.ui.diag.error.fg } , -- Used as the base highlight group. Other Diagnostic highlights link to this by default (except Underline) DiagnosticWarn = { fg=c.ui.diag.warn.fg } , -- Used as the base highlight group. Other Diagnostic highlights link to this by default (except Underline) DiagnosticInfo = { fg=c.ui.diag.info.fg } , -- Used as the base highlight group. Other Diagnostic highlights link to this by default (except Underline) DiagnosticHint = { fg=c.ui.diag.hint.fg } , -- Used as the base highlight group. Other Diagnostic highlights link to this by default (except Underline) DiagnosticOk = { fg=c.ui.diag.ok.fg } , -- Used as the base highlight group. Other Diagnostic highlights link to this by default (except Underline) DiagnosticVirtualTextError = { fg=c.ui.diag.error.fg, bg=c.ui.diag.error.bg } , -- Used for "Error" diagnostic virtual text. DiagnosticVirtualTextWarn = { fg=c.ui.diag.warn.fg, bg=c.ui.diag.warn.bg } , -- Used for "Warn" diagnostic virtual text. DiagnosticVirtualTextInfo = { fg=c.ui.diag.info.fg, bg=c.ui.diag.info.bg } , -- Used for "Info" diagnostic virtual text. DiagnosticVirtualTextHint = { fg=c.ui.diag.hint.fg, bg=c.ui.diag.hint.bg } , -- Used for "Hint" diagnostic virtual text. DiagnosticVirtualTextOk = { fg=c.ui.diag.ok.fg, bg=c.ui.diag.ok.bg } , -- Used for "Ok" diagnostic virtual text. DiagnosticUnderlineError = { undercurl=true, sp=c.ui.diag.error.fg } , -- Used to underline "Error" diagnostics. DiagnosticUnderlineWarn = { undercurl=true, sp=c.ui.diag.warn.fg } , -- Used to underline "Warn" diagnostics. DiagnosticUnderlineInfo = { undercurl=true, sp=c.ui.diag.info.fg } , -- Used to underline "Info" diagnostics. DiagnosticUnderlineHint = { undercurl=true, sp=c.ui.diag.hint.fg } , -- Used to underline "Hint" diagnostics. DiagnosticUnderlineOk = { undercurl=true, sp=c.ui.diag.ok.fg } , -- Used to underline "Ok" diagnostics. DiagnosticFloatingError = "DiagnosticError" , -- Used to color "Error" diagnostic messages in diagnostics float. See |vim.diagnostic.open_float()| DiagnosticFloatingWarn = "DiagnosticWarn" , -- Used to color "Warn" diagnostic messages in diagnostics float. DiagnosticFloatingInfo = "DiagnosticInfo" , -- Used to color "Info" diagnostic messages in diagnostics float. DiagnosticFloatingHint = "DiagnosticHint" , -- Used to color "Hint" diagnostic messages in diagnostics float. DiagnosticFloatingOk = "DiagnosticOk" , -- Used to color "Ok" diagnostic messages in diagnostics float. DiagnosticSignError = "DiagnosticError" , -- Used for "Error" signs in sign column. DiagnosticSignWarn = "DiagnosticWarn" , -- Used for "Warn" signs in sign column. DiagnosticSignInfo = "DiagnosticInfo" , -- Used for "Info" signs in sign column. DiagnosticSignHint = "DiagnosticHint" , -- Used for "Hint" signs in sign column. DiagnosticSignOk = "DiagnosticOk" , -- Used for "Ok" signs in sign column. TreesitterContext = { fg=c.ui.float.fg, bg=c.bg.surface }, -- "NormalFloat" -- Tree-Sitter syntax groups. -- See :h treesitter-highlight-groups, some groups may not be listed, ["@parameter"] = { fg=c.syntax.parameter }, -- Identifier ["@variable"] = { fg=c.fg.core }, -- Identifier ["@variable.builtin"] = { fg=c.syntax.builtin_var }, -- Identifier ["@variable.parameter"] = "@parameter", -- Identifier ["@variable.css"] = "@parameter", -- Identifier ["@variable.member"] = { fg=c.syntax.identifier }, -- Identifier ["@text.literal"] = "Comment", -- Comment ["@text.reference"] = "Identifier", -- Identifier ["@text.title"] = "Title", -- Title ["@text.uri"] = "Underlined", -- Underlined ["@text.underline"] = "Underlined", -- Underlined ["@text.todo"] = "Todo", -- Todo ["@comment"] = "Comment", -- Comment ["@comment.error"] = { fg=c.ui.diag.error.fg, bg=c.ui.diag.error.bg, bold=true }, -- Comment (e.g. `WARNING`, `FIX`, `HACK`) ["@comment.warning"] = { fg=c.ui.diag.warn.fg, bg=c.ui.diag.warn.bg, bold=true }, -- Comment (e.g. `WARNING`, `FIX`, `HACK`) -- ["@comment.todo"] = {} -- Comment todo-type comments (e.g. `TODO`, `WIP`) ["@comment.note"] = { fg=c.ui.diag.info.fg, bg=c.ui.diag.info.bg, bold=true }, -- Comment (e.g. `NOTE`, `INFO`, `XXX`) ["@punctuation"] = { fg=c.syntax.punctuation }, -- Delimiter ["@punctuation.delimiter"] = { fg=c.syntax.punctuation }, -- Delimiter (e.g. `;`, `.`, `,`) ["@punctuation.bracket"] = { fg=c.syntax.bracket }, -- Delimiter (e.g. `()`, `{}`, `[]`) ["@punctuation.special"] = { fg=c.syntax.operator }, -- Delimiter (e.g. `{}` in string interpolation) ["@constant"] = "Constant", -- Constant ["@constant.builtin"] = { fg=c.syntax.builtin_const, bold=true }, -- Special ["@constant.macro"] = "Macro", -- Define ["@define"] = "Define", -- Define ["@macro"] = "Macro", -- Macro ["@string"] = "String", -- String ["@string.regexp"] = { fg=c.syntax.regex, italic=true }, -- SpecialChar ["@string.escape"] = "Exception", -- SpecialChar ["@string.special"] = "SpecialChar", -- (e.g., dates) ["@string.special.symbol"]= { fg=c.syntax.identifier }, ["@string.special.url"] = { fg=c.ui.diag.hint.fg, underline=true }, ["@character"] = "Character", -- Character ["@character.special"] = "SpecialChar", -- SpecialChar ["@number"] = "Number", -- Number ["@boolean"] = "Boolean", -- Boolean ["@float"] = "Float", -- Float ["@function"] = "Function", -- Function ["@function.builtin"] = { fg=c.syntax.builtin_func }, -- Special ["@function.macro"] = "Macro", -- Macro ["@method"] = "Function", -- Function ["@field"] = "Identifier", -- Identifier ["@property"] = "Identifier", -- Identifier ["@module"] = "Special", -- Special ["@module.builtin"] = "@variable.builtin", -- Special ["@constructor"] = "Type", -- Special (e.g. 'Map', 'Set', 'Error') ["@constructor.lua"] = "@punctuation.bracket", -- Special (e.g. {}) ["@conditional"] = "Conditional", -- Conditional ["@repeat"] = "Repeat", -- Repeat ["@label"] = "Label", -- Label ["@operator"] = { fg=c.syntax.operator, bold=true }, -- Operator ["@keyword"] = "Statement", -- Keyword misc not fitting into specific categories ["@keyword.coroutine"] = { fg=c.syntax.conditional }, -- Keyword coroutines (e.g. `go` in Go, `async/await` in Python) ["@keyword.operator"] = { fg=c.syntax.operator, bold=true }, -- Keyword English words (e.g. `and`, `or`) ["@keyword.import"] = "PreProc", -- Keyword (e.g. `import`, `from` in Python) ["@keyword.export"] = { fg=c.syntax.preproc, bold=true }, -- Keyword (e.g. `export`) ["@keyword.directive"] = "PreProc", -- Keyword (e.g. #define, #if) ["@keyword.return"] = { fg=c.syntax.exception, bold=true }, -- Keyword -- `return` and `yield` ["@keyword.exception"] = "Exception", -- Keyword (e.g. `throw`, `catch`) ["@keyword.luap"] = "Exception", -- Keyword ["@keyword.modifier"] = "Exception", -- Keyword (e.g. `!important`) ["@keyword.repeat"] = "Repeat", -- Keyword ["@keyword.function"] = { fg=c.syntax.statement }, -- Keyword ["@keyword.conditional"] = "Conditional", -- Keyword ["@exception"] = "Exception", -- Exception ["@attribute"] = "Macro", -- Special (e.g. 'Map', 'Set', 'Error') ["@attribute.css"] = { fg=c.syntax.exception, italic=true }, -- (e.g. `:hover`, `::before`) ["@type"] = "Type", -- Type ["@type.builtin"] = "Typedef", ["@type.definition"] = "Typedef", -- Typedef ["@type.jsdoc"] = "Typedef", -- Type ["@storageclass"] = "StorageClass", -- StorageClass ["@structure"] = "Structure", -- Structure ["@namespace"] = "@variable.builtin", -- Identifier ["@namespace.builtin"] = "@variable.builtin", -- Identifier ["@include"] = "Include", -- Include ["@preproc"] = "PreProc", -- PreProc ["@debug"] = "Debug", -- Debug ["@tag"] = "Label", -- Tag ["@tag.attribute"] = "Keyword", -- Tag ["@tag.delimiter"] = { fg=c.syntax.punctuation }, -- Tag ["@tag.html"] = "Statement", -- Html tag ["@tag.css"] = "Statement", -- Html tag in CSS ["@tag.builtin"] = "Conditional", -- Html tag e.g. `
` ["@tag.tsx"] = "Type", -- Tsx tag ["@markup.heading"] = { fg = (c.theme.label and c.theme.label or theme.secondary), bold=true }, ["@markup.heading.1"] = { fg = c.terminal.red, bold = true }, ["@markup.heading.2"] = { fg = c.terminal.bright_yellow, bold = true }, ["@markup.heading.3"] = { fg = c.terminal.yellow, bold = true }, ["@markup.heading.4"] = { fg = c.terminal.green, bold = true }, ["@markup.heading.5"] = { fg = c.terminal.blue, bold = true }, ["@markup.heading.6"] = { fg = c.terminal.bright_magenta, bold = true }, ["@markup.link.label"] = { fg =(c.syntax.typedef) }, ["@markup.strong"] = { fg =(c.theme.label and c.theme.label or theme.secondary), bold = true }, ["@markup.italic"] = { fg =(c.theme.label and c.theme.label or theme.secondary), italic = true }, ["@markup.strikethrough"] = { strikethrough=true }, ["@markup.underline"] = { underline=true }, ["@markup.quote"] = "@variable.parameter", ["@markup.math"] = "Constant", ["@markup.environment"] = "Keyword", ["@markup.link.url"] = "@string.special.url", ["@markup.raw"] = "String", ["@diff.plus"] = "Added", -- added text (for diff files) ["@diff.delta"] = "Changed", -- changed text (for diff files) ["@diff.minus"] = "Removed", -- deleted text (for diff files) -- LSP ["@lsp.type.boolean"] = "@boolean", ["@lsp.type.builtinType"] = "@type.builtin", ["@lsp.type.comment"] = "@comment", ["@lsp.type.class"] = "@type", ["@lsp.type.decorator"] = "@attribute", ["@lsp.type.deriveHelper"] = "@attribute", ["@lsp.type.enum"] = "@type", ["@lsp.type.enumMember"] = "@constant", ["@lsp.type.escapeSequence"] = "@string.escape", ["@lsp.type.formatSpecifier"] = "@markup.list", ["@lsp.type.generic"] = "@variable", -- ["@lsp.type.interface"] = {}, ["@lsp.type.keyword"] = "@keyword", ["@lsp.type.lifetime"] = "@keyword.storage", ["@lsp.type.namespace"] = "@module", ["@lsp.type.namespace.python"] = "@variable", ["@lsp.type.number"] = "@number", ["@lsp.type.operator"] = "@operator", ["@lsp.type.parameter"] = "@variable.parameter", ["@lsp.type.property"] = "@property", ["@lsp.type.selfKeyword"] = "@variable.builtin", ["@lsp.type.selfTypeKeyword"] = "@variable.builtin", ["@lsp.type.string"] = "@string", ["@lsp.type.typeAlias"] = "@type.definition", ["@lsp.type.unresolvedReference"] = { undercurl = true, sp = c.ui.diag.error.fg }, ["@lsp.type.variable"] = {}, -- use treesitter styles for regular variables ["@lsp.typemod.class.defaultLibrary"] = "@type.builtin", ["@lsp.typemod.enum.defaultLibrary"] = "@type.builtin", ["@lsp.typemod.enumMember.defaultLibrary"] = "@constant.builtin", ["@lsp.typemod.function.defaultLibrary"] = "@function.builtin", ["@lsp.typemod.keyword.async"] = "@keyword.coroutine", ["@lsp.typemod.keyword.injected"] = "@keyword", ["@lsp.typemod.macro.defaultLibrary"] = { fg=c.syntax.macro, bold=true }, ["@lsp.typemod.method.defaultLibrary"] = "@function.builtin", ["@lsp.typemod.operator.injected"] = "@operator", ["@lsp.typemod.string.injected"] = "@string", ["@lsp.typemod.struct.defaultLibrary"] = "@type.builtin", ["@lsp.typemod.type.defaultLibrary"] = "@type.builtin", ["@lsp.typemod.typeAlias.defaultLibrary"] = "@type.builtin", ["@lsp.typemod.variable.callable"] = "@function", ["@lsp.typemod.variable.defaultLibrary"] = "@variable.builtin", ["@lsp.typemod.variable.global"] = "@variable.builtin", ["@lsp.typemod.variable.injected"] = "@variable", ["@lsp.typemod.variable.static"] = "@constant", } -- Apply light mode overrides if light_mode then -- Emphasize syntax highlights.MatchParen = { fg=c.ui.match_parent.fg, bg=c.ui.match_parent.bg, bold=true, reverse=match_paren_bg } highlights.Type = { fg=c.syntax.type, bold=true } highlights.Function = { fg=c.syntax.func, bold=true } highlights.String = { fg=c.syntax.string } highlights["@parameter"] = { fg=c.syntax.parameter, bold=true } highlights["@variable.parameter"] = { fg=c.syntax.parameter, bold=true } highlights["@keyword.return"] = { fg=c.syntax.exception, bold=true } highlights["@string.regexp"] = { fg=c.syntax.regex, bold=true } highlights["@variable.builtin"] = { fg=c.syntax.builtin_var, bold=true } -- Diff highlights.Added = { fg=c.diff.add, bold=true } highlights.Changed = { fg=c.diff.change, bold=true } highlights.Removed = { fg=c.diff.delete, bold=true } highlights.DiffAdd = { fg=c.diff.add, bg=c.ui.diag.ok.bg, bold=true } highlights.DiffChange = { fg=c.diff.text, bg=c.bg.surface, bold=true } highlights.DiffDelete = { fg=c.diff.delete, bg=c.ui.diag.error.bg, bold=true } highlights.DiffText = { fg=c.diff.text, bg=c.ui.diag.warn.bg, bold=true } -- Diagnostics highlights.DiagnosticUnderlineError = { undercurl=true, sp=c.ui.diag.error.fg, bold=true } highlights.DiagnosticUnderlineWarn = { undercurl=true, sp=c.ui.diag.warn.fg, bold=true } highlights.DiagnosticUnderlineInfo = { undercurl=true, sp=c.ui.diag.info.fg, bold=true } highlights.DiagnosticUnderlineHint = { undercurl=true, sp=c.ui.diag.hint.fg, bold=true } highlights.DiagnosticUnderlineOk = { undercurl=true, sp=c.ui.diag.ok.fg, bold=true } end -- stylua: ignore end return highlights end -- Table of plugin highlight group functions ---@alias PluginGroupFn fun(hl: OasisHighlightGroupMap, c: OasisPalette, light_mode: boolean|nil, is_desert: boolean|nil, theme: {primary: string, primary_light: string, primary_strong: string, secondary: string, title?: string}) ---@type table local PLUGIN_GROUPS = { -- FZF Lua fzf_lua = function(hl) hl.FzfLuaBorder = "FloatBorder" hl.FzfLuaTitle = "OasisFloatSecondary" end, -- Git Signs gitsigns = function(hl, c, light_mode) if light_mode then hl.GitSignsAdd = { fg = c.git.add, bg = c.ui.diag.ok.bg, bold = true } hl.GitSignsChange = { fg = c.git.change, bg = c.ui.diag.warn.bg, bold = true } hl.GitSignsDelete = { fg = c.git.delete, bg = c.ui.diag.error.bg, bold = true } else hl.GitSignsAdd = { fg = c.git.add } hl.GitSignsChange = { fg = c.git.change } hl.GitSignsDelete = { fg = c.git.delete } end end, -- Lazy lazy = function(hl, _, _, _, theme) hl.LazyH1 = { fg = theme.primary, bold = true } hl.LazyH2 = { fg = theme.primary_light, bold = true } hl.lazyActiveBorder = "Identifier" end, -- Mini mini = function(hl, c, _, _, theme) -- Mini Clue hl.MiniClueNextKey = "Statement" hl.MiniClueDescGroup = "OasisSecondary" hl.MiniClueDescSingle = "OasisLightPrimary" -- Mini Cmdline hl.MiniCmdlinePeekSep = { fg = c.fg.muted, bg = c.ui.float.bg } -- Mini Completion hl.MiniCompletionActiveParameter = { bg = c.bg.surface } -- Mini Diff hl.MiniDiffSignAdd = { fg = c.git.add } hl.MiniDiffSignChange = { fg = c.git.change } hl.MiniDiffSignDelete = { fg = c.git.delete } -- Mini Files hl.MiniFilesCursorLine = "PmenuSel" hl.MiniFilesBorderModified = { fg = c.ui.diag.warn.fg, bg = c.ui.float.bg } hl.MiniFilesTitleFocused = { fg = theme.secondary, bg = c.ui.float.bg, bold = true } -- Mini Icons hl.MiniIconsAzure = { fg = c.terminal.blue } hl.MiniIconsBlue = { fg = c.terminal.bright_blue } hl.MiniIconsCyan = { fg = c.terminal.cyan } hl.MiniIconsGreen = { fg = c.terminal.green } hl.MiniIconsGrey = { fg = c.terminal.white } hl.MiniIconsOrange = { fg = c.terminal.bright_yellow } hl.MiniIconsPurple = { fg = c.terminal.magenta } hl.MiniIconsRed = { fg = c.terminal.red } hl.MiniIconsYellow = { fg = c.terminal.yellow } -- Mini Jump hl.MiniJump = { undercurl = true, sp = c.theme.cursor } -- Mini Map hl.MiniMapNormal = { fg = c.fg.comment, bg = c.ui.float.bg } hl.MiniMapSymbolCount = { fg = c.fg.comment } -- Mini Pick hl.MiniPickBorderBusy = { fg = c.ui.diag.warn.fg, bg = c.ui.float.bg } hl.MiniPickMatchCurrent = "PmenuSel" hl.MiniPickMatchMarked = "Search" hl.MiniPickMatchRanges = "PmenuMatch" hl.MiniPickPreviewLine = { bg = c.ui.visual.bg } hl.MiniPickPrompt = { fg = c.ui.float.fg, bg = c.ui.float.bg, bold = true } hl.MiniPickPromptPrefix = { fg = c.ui.float.title, bg = c.ui.float.bg, bold = true } -- Mini Starter hl.MiniStarterFooter = "Comment" hl.MiniStarterInactive = { fg = c.fg.muted } hl.MiniStarterSection = "OasisSecondary" hl.MiniStarterItemPrefix = { fg = theme.primary_strong, bold = true } hl.MiniStarterQuery = { fg = c.theme.accent, bold = true } -- Mini Statusline hl.MiniStatuslineModeNormal = { bg = c.syntax.statement, fg = c.bg.core } hl.MiniStatuslineModeInsert = { bg = c.syntax.string, fg = c.bg.core } hl.MiniStatuslineModeCommand = { bg = c.syntax.parameter, fg = c.bg.core } hl.MiniStatuslineModeVisual = { bg = c.syntax.special, fg = c.bg.core } hl.MiniStatuslineModeReplace = { bg = c.syntax.exception, fg = c.bg.core } hl.MiniStatuslineModeOther = { bg = c.syntax.typedef, fg = c.bg.core } hl.MiniStatuslineDevInfo = { fg = c.syntax.statement, bg = c.bg.surface } hl.MiniStatuslineFileInfo = { fg = c.syntax.statement, bg = c.bg.surface } hl.MiniStatuslineFilename = { fg = theme.primary_light, bg = c.bg.mantle } hl.MiniStatuslineInactive = { fg = c.fg.comment, bg = c.bg.surface } -- Mini Tabline hl.MiniTablineCurrent = { fg = theme.secondary, bg = c.bg.surface, bold = true } hl.MiniTablineFill = "TabLineFill" hl.MiniTablineHidden = "TabLine" hl.MiniTablineModifiedCurrent = { fg = c.bg.core, bg = theme.secondary, bold = true } hl.MiniTablineModifiedHidden = { fg = c.bg.core, bg = c.ui.border } hl.MiniTablineModifiedVisible = { fg = c.bg.core, bg = c.ui.border, bold = true } hl.MiniTablineTabpagesection = { fg = c.bg.core, bg = c.theme.accent } hl.MiniTablineVisible = { fg = c.ui.border, bg = c.bg.surface, bold = true } -- Mini Trailspace hl.MiniTrailspace = { bg = c.syntax.exception } end, -- Render Markdown render_markdown = function(hl, c, _, _, theme) hl.RenderMarkdownBullet = "Special" hl.RenderMarkdownChecked = { fg = c.terminal.green } hl.RenderMarkdownTodo = { fg = c.ui.diag.info.fg } hl.RenderMarkdownUnchecked = { fg = c.terminal.red } hl.RenderMarkdownCode = { bg = c.bg.crust } hl.RenderMarkdownCodeBorder = { bg = c.bg.mantle } hl.RenderMarkdownCodeInline = { bg = c.bg.shadow } hl.RenderMarkdownLink = { fg = c.syntax.type } hl.RenderMarkdownTableHead = { fg = c.ui.border } hl.RenderMarkdownTableRow = { fg = c.ui.border } hl.RenderMarkdownHint = { fg = c.terminal.magenta } -- hl.RenderMarkdownWikiLink = {} end, -- Snacks snacks = function(hl, c, _, _, theme) -- Snacks Dashboard hl.SnacksDashboardHeader = "OasisTitle" hl.SnacksDashboardFile = { fg = theme.primary_light, bg = "NONE", bold = true } hl.SnacksDashboardSpecial = "OasisAccent" hl.SnacksDashboardIcon = "Number" hl.SnacksDashboardDesc = "OasisSecondary" -- Snacks Picker hl.SnacksPickerBoxTitle = c.ui.picker and { fg = c.ui.picker.title, bg = c.ui.picker.bg } or "OasisFloatSecondary" hl.SnacksPickerInputTitle = c.ui.picker and { fg = c.ui.picker.title, bg = c.ui.picker.bg } or "OasisFloatSecondary" hl.SnacksPickerInputBorder = c.ui.picker and { fg = c.ui.picker.border.fg, bg = c.ui.picker.border.bg } or "OasisFloatSecondary" hl.SnacksPickerPrompt = "Identifier" hl.SnacksPickerMatch = "Constant" end, -- Which Key which_key = function(hl) hl.WhichKey = "Statement" hl.WhichKeyDesc = "OasisLightPrimary" hl.WhichKeyGroup = "OasisSecondary" end, -- Yazi yazi = function(hl) hl.YaziFloat = "NormalFloat" end, } ---Create plugin highlights for enabled integrations. ---@param c OasisPalette Color palette ---@param highlights OasisHighlightGroupMap Highlight groups table to mutate ---@param light_mode boolean Whether palette is light mode ---@param is_desert boolean Whether palette is desert variant ---@param theme {primary: string, primary_light: string, primary_strong: string, secondary: string} local function create_plugin_highlights(c, highlights, light_mode, is_desert, theme) local integrations = (Config.get().integrations or {}) local default_enabled = integrations.default_enabled ~= false local user_plugins = Config.get_user_plugins() for plugin_name, apply_fn in pairs(PLUGIN_GROUPS) do local user_setting = user_plugins[plugin_name] local enabled = user_setting == nil and default_enabled or user_setting if enabled and type(apply_fn) == "function" then apply_fn(highlights, c, light_mode, is_desert, theme) end end end ---Filter highlight attributes based on style toggles. ---@param attrs OasisHighlightAttrs Highlight attributes ---@param styles OasisStyleConfig Style configuration table ---@param all_styles_enabled boolean Whether all styles are enabled ---@return OasisHighlightAttrs filtered_attrs local function apply_styles(attrs, styles, all_styles_enabled) if all_styles_enabled or type(attrs) ~= "table" then return attrs end local result = {} for k, v in pairs(attrs) do result[k] = v end if not styles.bold then result.bold = nil end if not styles.italic then result.italic = nil end if not styles.underline then result.underline = nil end if not styles.undercurl then result.undercurl = nil result.sp = nil end if not styles.strikethrough then result.strikethrough = nil end return result end ---Apply transparency to configured highlight groups. ---@param highlights OasisHighlightGroupMap Highlight groups table to mutate ---@param transparent boolean Whether transparency is enabled local function apply_transparency(highlights, transparent) if not transparent then return end for _, group in ipairs(TRANSPARENT_GROUPS) do if highlights[group] and type(highlights[group]) == "table" then highlights[group].bg = "NONE" end end end ---Apply user highlight overrides (takes precedence). ---@param c OasisPalette Color palette ---@param palette_name string Palette name (e.g., "oasis_lagoon") ---@param cfg OasisConfig Config table ---@param styles OasisStyleConfig Style configuration table ---@param all_styles_enabled boolean Whether all styles are enabled local function apply_user_overrides(c, palette_name, cfg, styles, all_styles_enabled) local user_overrides = cfg.highlight_overrides if user_overrides == nil or (type(user_overrides) == "table" and next(user_overrides) == nil) then return end local Overrides = require("oasis.lib.override_highlight") local overrides = Overrides.resolve(c, palette_name, cfg) local set_hl = vim.api.nvim_set_hl for name, attrs in pairs(overrides) do if type(attrs) == "table" then set_hl(0, name, apply_styles(attrs, styles, all_styles_enabled)) else ---@cast attrs string set_hl(0, name, { link = attrs }) end end end ---Apply highlight groups. ---@param highlights OasisHighlightGroupMap Highlight groups table ---@param styles OasisStyleConfig Style configuration table ---@param all_styles_enabled boolean Whether all styles are enabled local function set_highlight_groups(highlights, styles, all_styles_enabled) local set_hl = vim.api.nvim_set_hl for name, attrs in pairs(highlights) do if type(attrs) == "table" then set_hl(0, name, all_styles_enabled and attrs or apply_styles(attrs, styles, all_styles_enabled)) else ---@cast attrs string set_hl(0, name, { link = attrs }) end end end ---Apply terminal colors from palette. ---@param c OasisPalette Color palette ---@param terminal_colors boolean Whether terminal colors are enabled local function apply_terminal_colors(c, terminal_colors) vim.o.termguicolors = true if not (terminal_colors and c.terminal) then return end for i = 0, 15 do local key = ("color%d"):format(i) local val = c.terminal[key] if val and val ~= "NONE" then vim.g["terminal_color_" .. i] = val end end vim.g.terminal_color_background = c.bg.core vim.g.terminal_color_foreground = c.fg.core end ---Apply highlight groups, integrations, and terminal colors for a palette. ---@param c OasisPalette Color palette ---@param palette_name string Palette name (e.g., "oasis_lagoon") return function(c, palette_name) local light_mode = c.light_mode or false local cfg = Config.get() local is_desert = c.is_desert or palette_name:match("desert") local styles = cfg.styles or {} local all_styles_enabled = styles.all_enabled ~= false local theme = { primary = is_desert and c.theme.secondary or c.theme.primary, primary_light = is_desert and c.theme.secondary_light or c.theme.primary_light, primary_strong = is_desert and c.theme.secondary_strong or c.theme.primary_strong, secondary = is_desert and c.theme.primary or c.theme.secondary, title = is_desert and c.theme.title or is_desert and c.theme.secondary_strong or c.theme.primary_strong, } local highlights = create_highlights(c, light_mode, theme) create_plugin_highlights(c, highlights, light_mode, is_desert, theme) apply_transparency(highlights, cfg.transparent) set_highlight_groups(highlights, styles, all_styles_enabled) apply_user_overrides(c, palette_name, cfg, styles, all_styles_enabled) apply_terminal_colors(c, cfg.terminal_colors) end