--
-- Color scheme, custom highlights & per-window highlight namespaces
--
local solarized_colors = {
base03 = "#002b36",
base02 = "#073642",
base01 = "#586e75",
base00 = "#657b83",
base0 = "#839496",
base1 = "#93a1a1",
base2 = "#eee8d5",
base3 = "#fdf6e3",
yellow = "#b58900",
orange = "#cb4b16",
red = "#dc322f",
magenta = "#d33682",
violet = "#6c71c4",
blue = "#268bd2",
cyan = "#2aa198",
green = "#859900",
-- solarized.nvim "mix" tints: subtle highlight backgrounds, theme-specific
mix_base1 = "#2c4e56", -- dark (active cursor line)
mix_blue_light = "#e7ebe1", -- light (active cursor line)
mix_green = "#274c25", -- dark (inactive cursor line: muted green)
mix_magenta_light = "#f8e2d9", -- light (inactive cursor line: muted pink)
}
-- Per-window highlight namespaces: one for the focused window, one for the
-- unfocused windows. A window's active namespace decides its CursorLine color,
-- which is how the focused window is made obvious. Both namespaces must define
-- `dotfiles.Todo`: an extmark's hl_group is resolved against the window's
-- active namespace and does NOT fall back to the global namespace, so the TODO
-- highlight (placed in 50-extmarks.lua) only renders if the group exists in
-- whichever namespace the window currently uses.
local ns_active = vim.api.nvim_create_namespace("extmarks")
local ns_inactive = vim.api.nvim_create_namespace("extmarks.inactive")
local todo_hl = { bg = "Yellow", fg = "Black", bold = true }
vim.api.nvim_set_hl(ns_active, "dotfiles.Todo", todo_hl)
vim.api.nvim_set_hl(ns_inactive, "dotfiles.Todo", todo_hl)
-- Publish the extmark storage namespace and TODO group name for 50-extmarks.lua.
-- This file is named 40- (not 50-) so it is sourced before 50-extmarks.lua and
-- the contract below exists when that file reads it. Preserve the existing
-- `dotfiles` table (augroup).
local dotfiles = vim.g.dotfiles
dotfiles.todo_ns = ns_active
dotfiles.todo_hl = "dotfiles.Todo"
vim.g.dotfiles = dotfiles
-- Custom highlights in the global namespace, independent of the per-window ones
local function adjust_highlight()
if vim.g.colors_name == "solarized" then
vim.api.nvim_set_hl(0, "EndOfBuffer", { fg = solarized_colors.base0, update = true })
vim.api.nvim_set_hl(0, "MatchParen", { link = "CurSearch" })
vim.api.nvim_set_hl(0, "WinBarCwd", { fg = solarized_colors.blue, italic = true })
vim.api.nvim_set_hl(0, "WinBarFile", { italic = true })
vim.api.nvim_set_hl(0, "TabLine", { fg = solarized_colors.yellow, bold = true })
if vim.o.background == "light" then
vim.api.nvim_set_hl(0, "TabLineSel", { bg = "#edffd5", fg = solarized_colors.yellow, bold = true })
elseif vim.o.background == "dark" then
vim.api.nvim_set_hl(0, "TabLineSel", { bg = "#043624" })
end
end
end
-- Active and inactive cursorline highlight
local function set_cursorline_hl()
local accent = vim.o.background == "light" and solarized_colors.mix_blue_light or solarized_colors.mix_base1
vim.api.nvim_set_hl(ns_active, "CursorLine", { bg = accent })
vim.api.nvim_set_hl(ns_active, "CursorLineNr", { bg = accent })
vim.api.nvim_set_hl(ns_inactive, "CursorLine", { bg = accent, underline = true })
vim.api.nvim_set_hl(ns_inactive, "CursorLineNr", { bg = accent, underline = true })
end
local function apply_highlights()
adjust_highlight()
set_cursorline_hl()
end
-- Single owner of every window's highlight namespace: the focused window gets
-- `ns_active`, all others `ns_inactive`. Reapplied to every window on each
-- focus change because WinLeave alone misses windows that existed at startup
-- or were restored from a session.
local function is_normal_win(win)
return vim.api.nvim_win_is_valid(win) and vim.api.nvim_win_get_config(win).relative == ""
end
local function update_focus_ns()
local cur = vim.api.nvim_get_current_win()
-- Focus in a floating window (completion, hover, ...) must not recolor the
-- split underneath; leave every window as it is.
if not is_normal_win(cur) then
return
end
for _, win in ipairs(vim.api.nvim_list_wins()) do
if is_normal_win(win) then
vim.api.nvim_win_set_hl_ns(win, (win == cur) and ns_active or ns_inactive)
end
end
end
-- Note: a closed fold's screen line is always drawn with `Folded` (see
-- `:help fold.txt` / hl-Folded); Neovim has no per-line override, so the
-- unfocused cursor line is not visible when it sits on a closed fold. This is
-- standard behavior (cursorline never renders on closed folds in any window)
-- and is accepted as-is.
local function next_colorscheme()
local colorschemes = vim.fn.getcompletion("", "color")
vim.g.colors_name = vim.g.colors_name or "default"
for i = 1, #colorschemes do
if colorschemes[i] == vim.g.colors_name then
vim.cmd.colorscheme(colorschemes[(i % #colorschemes) + 1])
vim.notify("Color scheme set to " .. vim.g.colors_name)
return
end
end
end
local function random_colorscheme()
local colorschemes = vim.fn.getcompletion("", "color")
vim.cmd.colorscheme(colorschemes[math.random(#colorschemes)])
vim.notify("Color scheme set to " .. vim.g.colors_name)
end
local function toggle_theme()
vim.opt.background = vim.o.background == "light" and "dark" or "light"
vim.notify("Color theme set to " .. vim.o.background)
end
-- Reapply custom highlights after the colorscheme loads and on theme change
vim.api.nvim_create_autocmd("ColorScheme", {
desc = "Reapply custom highlights",
group = vim.g.dotfiles.augroup,
callback = apply_highlights,
})
vim.api.nvim_create_autocmd("OptionSet", {
desc = "Reapply custom highlights on theme change",
pattern = "background",
group = vim.g.dotfiles.augroup,
callback = apply_highlights,
})
-- Keep the focused/unfocused cursor line correct as windows and focus change
vim.api.nvim_create_autocmd(
{ "VimEnter", "WinEnter", "WinNew", "WinClosed", "TabEnter", "SessionLoadPost", "FileType" },
{
desc = "Track the focused window for the cursor line",
group = vim.g.dotfiles.augroup,
callback = function()
-- Defer so the window list and current window have settled.
vim.schedule(update_focus_ns)
end,
}
)
vim.cmd.colorscheme("solarized") -- Default colorscheme
vim.opt.background = "dark" -- Default theme
apply_highlights()
vim.api.nvim_create_user_command("ColorsNext", next_colorscheme, { desc = "Load next color scheme" })
vim.api.nvim_create_user_command("ColorsRandom", random_colorscheme, { desc = "Load random color scheme" })
vim.api.nvim_create_user_command("ColorsThemeToggle", toggle_theme, { desc = "Toggle light/dark theme" })