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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
|
--
-- 50-color.lua
--
-- * Highlight tweaks for the solarized colorscheme, plus popup-menu and tabpage-title styling.
-- * The lualine statusline (https://github.com/nvim-lualine/lualine.nvim), themed to match.
-- * Registers both into dotfiles.color, which re-applies them on colorscheme / 'background' changes
-- and once after startup.
-- * Sets the default colorscheme (solarized) and background (dark).
--
-- User commands:
-- `ColorsBgToggle`: toggle light/dark background
-- `TabRename`: rename current tab
-- `TabRenameFileName`: rename current tab to the active buffer's file name
-- `LualineConfig`: show the resolved lualine configuration
--
local color = require("dotfiles.color")
local lualine = require("lualine")
local solarized = { blue = "#268bd2" }
----------------------------------------------------------------------------------------------------
-- Highlights
----------------------------------------------------------------------------------------------------
-- Highlight applier: adjust the solarized groups to fit my personal tastes. No-ops unless solarized
-- is the active colorscheme.
local function apply_solarized_highlights()
if vim.g.colors_name ~= "solarized" then
return
end
-- Window bar style
vim.api.nvim_set_hl(0, "WinBarCwd", { fg = solarized.blue, italic = true })
vim.api.nvim_set_hl(0, "WinBarFilePath", { italic = true })
-- Matching delimiter
vim.api.nvim_set_hl(0, "MatchParen", { link = "CurSearch" })
-- Floating windows and popup menu windows follow the editor background, their borders too so
-- that the popup menu border draws a clean separation.
vim.opt.pumborder = "rounded"
vim.opt.pumwidth = 25 -- Minimum popup menu width
vim.api.nvim_set_hl(0, "NormalFloat", { link = "Normal" })
local normal_bg = vim.api.nvim_get_hl(0, { name = "Normal", link = false }).bg
vim.api.nvim_set_hl(0, "FloatBorder", { bg = normal_bg, update = true })
vim.api.nvim_set_hl(0, "Pmenu", { bg = normal_bg, update = true })
vim.api.nvim_set_hl(0, "PmenuSbar", { bg = normal_bg, update = true })
-- Currently selected tabpage
vim.api.nvim_set_hl(0, "TabLineSel", { bold = true, italic = true, update = true })
-- Todo hl group
vim.api.nvim_set_hl(0, "Todo", { bg = "Yellow", fg = "Black", bold = true })
-- Fold lines being the same color as the cursor line (default) make unfocused windows unreadable
-- Their background is set to blend in, the folds are visible enough with their dotted lines
vim.api.nvim_set_hl(0, "Folded", { bg = normal_bg, underline = true, update = true })
end
-- Highlight applier: create custom highlight groups for styling the tabpage title bars:
-- * TabLineWinCount: number of open windows in the tabpage
-- * TabLineSelWinCount: number of open windows in the currently active tabpage
local function apply_tabpage_title_highlights()
local normal = vim.api.nvim_get_hl(0, { name = "Normal", link = false })
local tabline = vim.api.nvim_get_hl(0, { name = "TabLine", link = false })
local tablinesel = vim.api.nvim_get_hl(0, { name = "TabLineSel", link = false })
vim.api.nvim_set_hl(0, "TabLineSel", { fg = normal.fg, update = true })
vim.api.nvim_set_hl(0, "TabLineWinCount", { fg = solarized.blue, bg = tabline.bg })
vim.api.nvim_set_hl(
0,
"TabLineSelWinCount",
{ fg = solarized.blue, bg = tablinesel.bg, bold = true }
)
end
----------------------------------------------------------------------------------------------------
-- Statusline
----------------------------------------------------------------------------------------------------
-- Spell flag: which spell languages are enabled, or nil when 'spelllang' matches none of these.
local function spell_status()
if vim.o.spelllang == "fr" then
return "+S(fr)"
elseif vim.o.spelllang == "en_us" then
return "+S(en)"
elseif vim.o.spelllang == "en_us,fr" then
return "+S(en,fr)"
end
end
-- Custom status flags: "+F" when formatting is on for the buffer, "+H" when LSP inlay hints are on,
-- followed by the spell flag.
local function statuses()
return (vim.b.format and "+F" or "")
.. (vim.lsp.inlay_hint.is_enabled({ bufnr = 0 }) and "+H" or "")
.. (vim.o.spell and spell_status() or "")
end
-- lualine builds its own section-b-backed highlights for the diff/diagnostics components
-- (lualine_b_diff_added_normal, lualine_b_diagnostics_error_normal, ...). Stamp bold onto them with
-- update=true so only the bold attribute changes — fg and section b's background are left untouched.
-- Must run after lualine (re)creates the groups: initial setup, theme switch, colorscheme.
local function bold_diff_diag()
for name in pairs(vim.api.nvim_get_hl(0, {})) do
if name:find("^lualine_b_diff") or name:find("^lualine_b_diagnostics") then
vim.api.nvim_set_hl(0, name, { bold = true, update = true })
end
end
end
local lualine_sections = {
lualine_c = { statuses },
lualine_x = { "encoding", "fileformat", "filetype", "lsp_status" },
}
local lualine_options = {
disabled_filetypes = { "netrw", "qf" },
component_separators = { left = "|", right = "|" },
section_separators = { left = "", right = "" },
}
-- Pick the lualine theme from 'background' and (re)apply the configuration.
local function configure_lualine()
lualine_options.theme = vim.o.background == "light" and "solarized_light" or "solarized_dark"
lualine.setup({ options = lualine_options, sections = lualine_sections })
end
-- Statusline applier: re-pick the lualine theme for the current 'background', then re-bold the
-- diff/diagnostics groups once lualine has rebuilt them.
local function apply_statusline_highlights()
configure_lualine()
vim.schedule(bold_diff_diag)
end
----------------------------------------------------------------------------------------------------
-- Tabline
----------------------------------------------------------------------------------------------------
-- Builds the window-count segment of a tabpage label, e.g. ` (2)[+] ` (no styling).
-- `is_modified` appends the `[+]` marker when any window in the tab has pending changes.
local function make_tab_info_label(win_count, is_modified)
local tab_win_count = "(" .. (win_count >= 10 and ">9" or tostring(win_count)) .. ")"
local tab_win_count_suffix = is_modified and "[+] " or " "
return " " .. tab_win_count .. tab_win_count_suffix
end
-- Builds the title segment of a tabpage label, padded or truncated to `width` (no styling).
-- Uses the tab's `tabname` if set, else the active buffer's file name, else `[No Name]`, prefixed
-- with `*` for the current tab and `-` for the last-accessed tab.
local function make_tab_label(id, is_current, width)
local is_last_accessed = vim.api.nvim_tabpage_get_number(id) == vim.fn.tabpagenr("#")
local tab_label_prefix = (is_current and "*" or "") .. (is_last_accessed and "-" or "")
local tab_label = vim.t[id].tabname
if tab_label == nil or tab_label == "" then
local win = vim.api.nvim_tabpage_get_win(id)
local bufname = vim.api.nvim_buf_get_name(vim.api.nvim_win_get_buf(win))
tab_label = vim.fn.fnamemodify(bufname, ":t")
end
if tab_label == "" then
tab_label = "[No Name]"
end
tab_label = tab_label_prefix .. tab_label
local display_width = vim.fn.strdisplaywidth(tab_label)
return (display_width > width) and (vim.fn.strcharpart(tab_label, 0, width - 1) .. "…")
or (tab_label .. string.rep(" ", width - display_width))
end
-- Returns a string compatible with the 'tabline' option, used to set the tabline content and style.
-- Declared as a global function to be available as `v:lua.GetTabLine` in the Vimscript engine for
-- the 'tabline' option. See `:help 'statusline'`.
function GetTabLine()
local max_tab_label_width = 30
local s = ""
for _, id in pairs(vim.api.nvim_list_tabpages()) do
local is_current = id == vim.api.nvim_get_current_tabpage()
local is_modified = false
local wins = vim.api.nvim_tabpage_list_wins(id)
for _, win in ipairs(wins) do
if vim.bo[vim.api.nvim_win_get_buf(win)].modified then
is_modified = true
break
end
end
-- Define start of tabpage label (marks where clickage region begins)
s = s .. "%" .. vim.api.nvim_tabpage_get_number(id) .. "T"
local tab_info_label = make_tab_info_label(#wins, is_modified)
local tab_label_width = max_tab_label_width - vim.fn.strdisplaywidth(tab_info_label)
local tab_label = make_tab_label(id, is_current, tab_label_width)
s = s .. (is_current and "%#TabLineSelWinCount#" or "%#TabLineWinCount#") .. tab_info_label
s = s .. (is_current and "%#TabLineSel#" or "%#TabLine#") .. tab_label
end
return s .. "%#TabLineFill#"
end
-- Name the current tab from the command argument.
local function rename_tab(opts)
vim.t.tabname = opts.args
vim.cmd.redrawtabline()
end
-- Name the current tab after the active buffer's file name (without extension).
local function rename_tab_to_cur_filename()
vim.t.tabname = vim.fn.expand("%:t:r")
vim.cmd.redrawtabline()
end
----------------------------------------------------------------------------------------------------
-- Commands & init
----------------------------------------------------------------------------------------------------
-- Flip between the light and dark backgrounds (the colorscheme reloads, OptionSet re-themes).
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
-- Show the resolved lualine configuration.
local function show_lualine_config()
vim.notify(vim.inspect(lualine.get_config()))
end
color.register(apply_solarized_highlights)
color.register(apply_tabpage_title_highlights)
color.register(apply_statusline_highlights)
vim.api.nvim_create_user_command(
"ColorsBgToggle",
toggle_theme,
{ desc = "Toggle light/dark theme" }
)
vim.api.nvim_create_user_command("TabRename", rename_tab, { nargs = 1 })
vim.api.nvim_create_user_command("TabRenameFileName", rename_tab_to_cur_filename, { nargs = 0 })
vim.api.nvim_create_user_command(
"LualineConfig",
show_lualine_config,
{ desc = "Show lualine configuration" }
)
-- See `:help stl-%!`
vim.o.tabline = "%!v:lua.GetTabLine()"
vim.cmd.colorscheme("solarized")
vim.opt.background = "dark"
|