summaryrefslogtreecommitdiffstats
path: root/.config/nvim/plugin/40-colors.lua
blob: 32ae452e179e77e4acc7a80542cb79cec2a0f5bf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
--
-- 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" })