summaryrefslogtreecommitdiffstats
path: root/.config/nvim
diff options
context:
space:
mode:
authorThomas Vanbesien <tvanbesi@proton.me>2026-06-12 16:58:39 +0200
committerThomas Vanbesien <tvanbesi@proton.me>2026-06-12 22:57:30 +0200
commit2e42bf1ef8c27173ed3a540135eada4c24abbaaf (patch)
treea7e04f70f47b82e44a0e4721505a315867aac854 /.config/nvim
parent9236e1f700b028da61302be8371401fe0fd86f0c (diff)
downloaddotfiles-2e42bf1ef8c27173ed3a540135eada4c24abbaaf.tar.gz
dotfiles-2e42bf1ef8c27173ed3a540135eada4c24abbaaf.zip
refactor(nvim): rewrite the highlight and extended marks plugins
Diffstat (limited to '.config/nvim')
-rw-r--r--.config/nvim/init.lua7
-rw-r--r--.config/nvim/plugin/10-treesitter.lua17
-rw-r--r--.config/nvim/plugin/40-colors.lua163
-rw-r--r--.config/nvim/plugin/50-extmarks.lua225
-rw-r--r--.config/nvim/plugin/50-highlight.lua70
5 files changed, 210 insertions, 272 deletions
diff --git a/.config/nvim/init.lua b/.config/nvim/init.lua
index 5bdaaf5..a99e2c6 100644
--- a/.config/nvim/init.lua
+++ b/.config/nvim/init.lua
@@ -9,6 +9,12 @@ vim.g.dotfiles = {
textwidth = { sh = 80, lua = 120, markdown = 120, gitcommit = 72, python = 88 },
}
+-- Sets the window bar content and style (custom hl-groups WinBarCwd and WinBarFilePath)
+-- Note that this will usually be cleared when loading a colorscheme (which do the equivalent of :hi clear)
+vim.api.nvim_set_hl(0, "WinBarCwd", { bold = true })
+vim.api.nvim_set_hl(0, "WinBarFilePath", { link = "Normal" })
+vim.opt.winbar = "%#WinBarCwd#%{fnamemodify(getcwd(),':~')}%* | %#WinBarFilePath#%f%*"
+
------------------------------------------------------------------------------------------------------------------------
-- Local functions
------------------------------------------------------------------------------------------------------------------------
@@ -204,7 +210,6 @@ vim.opt.ignorecase = true -- Search ignores case by default
vim.opt.smartcase = true -- Search is case-sensitive if searching for uppercase characters
vim.opt.conceallevel = 2 -- Hide text with the "conceal" syntax attribute
vim.opt.showtabline = 2 -- Always show tabline
-vim.opt.winbar = "%#WinBarCwd#%{fnamemodify(getcwd(),':~')}%* | %#WinBarFile#%f%*"
vim.opt.winborder = "rounded" -- Rounded outline for floating windows
vim.opt.updatetime = 1000 -- Swap file save frequency; also how often GitGutter signs update in ms
vim.opt.list = true -- Display <Tab> and other non-printables
diff --git a/.config/nvim/plugin/10-treesitter.lua b/.config/nvim/plugin/10-treesitter.lua
index c0879b6..941614c 100644
--- a/.config/nvim/plugin/10-treesitter.lua
+++ b/.config/nvim/plugin/10-treesitter.lua
@@ -2,9 +2,7 @@
-- 10-treesitter.lua
--
-- * Installs treesitter parsers.
--- * Sets up an autocommand to parse the tree synchronously on 'FileType'.
--- This file should sort early (alphabetically) in plugin/ so that this autcommand triggers before other autcommands
--- that use the tree.
+-- * Creates an autocommand to start treesitter automatically.
--
require("nvim-treesitter").install({
@@ -21,19 +19,14 @@ require("nvim-treesitter").install({
"vimdoc",
})
--- Parse the tree synchronously as early as possible (which is as soon as we know the filetype)
--- This can be useful for example so that the tree is ready before any other code calls vim.treesitter.get_node(), which
--- returns nil when the tree is not parsed.
--- TODO actually test it
vim.api.nvim_create_autocmd("FileType", {
desc = "Start treesitter",
group = vim.g.dotfiles.augroup,
- callback = function()
- local parser = vim.treesitter.get_parser(0)
- if parser == nil then
+ callback = function(ev)
+ local parser = vim.treesitter.get_parser(ev.buf)
+ if not parser then
return
end
- parser:parse()
- vim.treesitter.start()
+ vim.treesitter.start(ev.buf)
end,
})
diff --git a/.config/nvim/plugin/40-colors.lua b/.config/nvim/plugin/40-colors.lua
deleted file mode 100644
index df2e48c..0000000
--- a/.config/nvim/plugin/40-colors.lua
+++ /dev/null
@@ -1,163 +0,0 @@
---
--- 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 })
- -- Floating windows (LSP hover, etc.) follow the editor background;
- -- keep the border's line color but match its bg to the float interior.
- vim.api.nvim_set_hl(0, "NormalFloat", { link = "Normal" })
- local normal_bg = vim.api.nvim_get_hl(0, { name = "Normal", link = false }).bg
- local border_fg = vim.api.nvim_get_hl(0, { name = "FloatBorder", link = false }).fg
- vim.api.nvim_set_hl(0, "FloatBorder", { fg = border_fg, bg = normal_bg })
- -- Popup menu (completion, etc.): match the editor background like floats do.
- vim.api.nvim_set_hl(0, "Pmenu", { fg = solarized_colors.base0, bg = normal_bg })
- vim.api.nvim_set_hl(0, "PmenuSbar", { bg = normal_bg })
- 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
-
-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
-
-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("ColorsToggle", toggle_theme, { desc = "Toggle light/dark theme" })
diff --git a/.config/nvim/plugin/50-extmarks.lua b/.config/nvim/plugin/50-extmarks.lua
index 1324da0..50ba81d 100644
--- a/.config/nvim/plugin/50-extmarks.lua
+++ b/.config/nvim/plugin/50-extmarks.lua
@@ -1,129 +1,162 @@
--
--- TODO highlighting (treesitter-aware extmarks)
+-- 50-extmarks.lua
+--
+-- * Creates a global highlight namespace for extended marks.
+-- * Keeps track of the current global highlight namespace in `vim.g.hl_ns`.
+-- * Creates an autocommand that initializes and updates extended marks.
+-- For now, only "TODO" strings are marked but the system can be easily extended by adding items
+-- in `marks`.
+-- Doesn't support multi-line patterns.
+--
+-- Global variables:
+-- `vim.g.hl_ns`: global highlight namespace
+--
+-- User commands:
+-- `ExtmarkToggle`: toggle extended marks highlight namespace
--
--- The `dotfiles.Todo` highlight group and the storage namespace are owned by
--- 40-colors.lua, because the same per-window namespaces also drive the
--- focused/unfocused cursor line. That file is numbered 40- so it is sourced
--- before this one and `vim.g.dotfiles.todo_ns` / `.todo_hl` exist below. Here
--- we only place the extmarks.
--- Returns an iterator over the ascendants of `node`, starting at the root node
-local function node_ascendants(node)
- local root = node:tree():root()
- local current = root
- return function()
- if current:equal(node) then
- return nil
- end
- current = assert(current:child_with_descendant(node))
- return current
+vim.g.hl_ns = 0
+
+local extmarks_hl_ns = vim.api.nvim_create_namespace("dotfiles_extmarks")
+
+local hl_group_todo = "dotfiles.Todo"
+vim.api.nvim_set_hl(extmarks_hl_ns, hl_group_todo, { link = "Todo" })
+
+-- Wrapper around `nvim_set_hl_ns()`; sets the global highlight namespace to `hl_ns` and save it to
+-- `vim.g.hl_ns`. Useful because the global highlight namespace is not saved anywhere by default.
+local function set_global_hl_ns(hl_ns)
+ vim.g.hl_ns = hl_ns
+ vim.api.nvim_set_hl_ns(hl_ns)
+end
+
+-- Toggles extended marks highlight namespace
+local function toggle_marks()
+ local new_hl_ns = vim.g.hl_ns == 0 and extmarks_hl_ns or 0
+ set_global_hl_ns(new_hl_ns)
+end
+
+-- Parses the treesitter tree synchronously, including injections, in the 0-based, end-exclusive
+-- line range `first`:`last` of buffer `buf`.
+--- @return boolean parser_exists
+local function parse_tree(buf, first, last)
+ local parser = vim.treesitter.get_parser(buf)
+ if parser then
+ parser:parse({ first, last })
end
+ return parser ~= nil
end
--- Returns true if `node` or any of its parent has the type `type`
-local function node_within_type(node, type)
- local next_ascendant = node_ascendants(node)
- local next = next_ascendant()
- while next do
- if next:type() == type then
+-- Returns a predicate telling whether a "TODO" string should be marked at a given position.
+-- In markdown buffers, positions inside code are invalid, otherwise positions outside comments are
+-- invalid. All positions are valid if there is no parser for the buffer filetype.
+-- Only the line range `first`:`last` is parsed; the predicate must not be called outside it.
+--- @param buf number
+--- @return fun(row: number, col: number): boolean predicate taking a 0-based position
+local function todo_filter(buf, first, last)
+ local has_parser = parse_tree(buf, first, last)
+ return function(row, col)
+ assert(first <= row and row < last, "Position outside the parsed line range")
+ if not has_parser then
return true
end
- next = next_ascendant()
+ local block = vim.treesitter.get_node({
+ bufnr = buf,
+ pos = { row, col },
+ })
+ assert(block, "Expected node to be truthy, is the tree parsed?")
+ if vim.bo[buf].filetype == "markdown" then
+ if string.match(block:type(), "code") then
+ return false
+ end
+ local inline = vim.treesitter.get_node({
+ bufnr = buf,
+ ignore_injections = false,
+ pos = { row, col },
+ })
+ assert(inline, "Expected node to be truthy, is the tree parsed?")
+ return inline:type() ~= "code_span"
+ else
+ return string.match(block:type(), "comment") ~= nil
+ end
end
- return false
end
--- Returns an iterator over the positions (`{ col1, col2, row }`) of matches of pattern in lines
-local function pmatches(lines, pattern)
- local col1, col2, row = nil, nil, 1
+-- Returns an iterator over the positions of valid "TODO" strings in buffer `buf` in the line range
+-- `first`:`last`. Each item is a table with 0-based fields: row `y`, start column `x1` (inclusive)
+-- and end column `x2` (exclusive). See `todo_filter()` for what makes a position valid.
+local function find_todo(buf, first, last)
+ local lines = vim.api.nvim_buf_get_lines(buf, first, last, false)
+ local should_mark = todo_filter(buf, first, first + #lines)
+ local x1, x2, y = nil, nil, 1
return function()
- while row <= #lines do
- col1, col2 = string.find(lines[row], pattern, (col2 or 0) + 1)
- if col1 then
- return { col1 = col1, col2 = col2, row = row }
+ while y <= #lines do
+ x1, x2 = string.find(lines[y], "TODO", (x2 or 0) + 1)
+ if x1 and x2 then
+ local row = y + first - 1
+ if should_mark(row, x1 - 1) then
+ return { y = row, x1 = x1 - 1, x2 = x2 }
+ end
else
- col1, col2, row = 0, 0, row + 1
+ x1, x2, y = 0, 0, y + 1
end
end
return nil
end
end
--- Set extmarks are the positions (`{ col1, col2, row }`) given by `next_pos()`
--- `opts`: {
--- ns: highlight namespace
--- predicate: is passed row and col, if true then set the mark
--- hl_group: highlight group
--- }
-local function set_extmarks(buf, next_pos, opts)
- for pos in next_pos do
- local marks = vim.api.nvim_buf_get_extmarks(
- buf,
- opts.ns,
- { pos.row - 1, pos.col1 - 1 },
- { pos.row - 1, pos.col2 - 1 },
- {}
- )
- if #marks == 0 and opts.predicate(buf, pos.row - 1, pos.col1 - 1) then
+-- List of extended marks to apply with `mark_all()`
+--
+-- Each item is a table with two fields: `hl_group`, the name of the highlight group to use
+-- for the marks, and `find`, which must return an iterator over the positions to mark (see
+-- `find_todo()` for the format).
+local marks = {
+ { hl_group = hl_group_todo, find = find_todo },
+}
+
+-- Updates extended marks in buffer `buf` in range `first`:`last`.
+-- For each item in `marks`, sets extended marks on positions returned by `item.find` to the
+-- highlight group `item.hl_group`.
+local function mark_all(buf, first, last)
+ vim.api.nvim_buf_clear_namespace(buf, extmarks_hl_ns, first, last)
+ for _, marks_opts in ipairs(marks) do
+ for p in marks_opts.find(buf, first, last) do
vim.api.nvim_buf_set_extmark(
buf,
- opts.ns,
- pos.row - 1,
- pos.col1 - 1,
- { end_col = pos.col2, hl_group = opts.hl_group }
+ extmarks_hl_ns,
+ p.y,
+ p.x1,
+ { end_col = p.x2, hl_group = marks_opts.hl_group }
)
end
end
end
--- TODO a better way to do this:
--- For code, only check in "comment" node ranges
--- For non-code, do it per type. For Markdown, only check "paragraph" nodes
-local function hl_todo_predicate(buf, row, col)
- if vim.bo[buf].filetype == "markdown" or vim.treesitter.get_parser(buf) == nil then
- return true
+-- Create extended marks in buffer `ev.buf` and updates them when the buffer changes.
+local function watch_extmarks(ev)
+ mark_all(ev.buf, 0, -1)
+ if vim.b[ev.buf].dotfiles_todo_attached then
+ return
end
- local node = assert(vim.treesitter.get_node({ bufnr = buf, pos = { row, col } }))
- return node_within_type(node, "comment")
+ vim.b[ev.buf].dotfiles_todo_attached = true
+ vim.api.nvim_buf_attach(ev.buf, false, {
+ on_lines = function(_, b, _, first, _, last_new)
+ vim.schedule(function()
+ if vim.api.nvim_buf_is_valid(b) then
+ mark_all(b, first, last_new)
+ end
+ end)
+ end,
+ })
end
-local patterns = { todo = "TODO" }
-local extmarks_todo_opts = {
- ns = vim.g.dotfiles.todo_ns,
- hl_group = vim.g.dotfiles.todo_hl,
- predicate = hl_todo_predicate,
-}
-
-local function scan(buf, first, last)
- local lines = vim.api.nvim_buf_get_lines(buf, first, last, false)
- local iter = pmatches(lines, patterns.todo)
- set_extmarks(buf, function()
- local pos = iter()
- if pos then
- pos.row = pos.row + first
- end
- return pos
- end, extmarks_todo_opts)
-end
+----------------------------------------------------------------------------------------------------
vim.api.nvim_create_autocmd("FileType", {
- desc = "Initialize and watch TODO extmarks",
+ desc = "Set extended marks watcher",
group = vim.g.dotfiles.augroup,
- callback = function(args)
- local buf = args.buf
- scan(buf, 0, -1)
- if vim.b[buf].dotfiles_todo_attached then
- return
- end
- vim.b[buf].dotfiles_todo_attached = true
- vim.api.nvim_buf_attach(buf, false, {
- on_lines = function(_, b, _, first, _, last_new)
- vim.schedule(function()
- if vim.api.nvim_buf_is_valid(b) then
- scan(b, first, last_new)
- end
- end)
- end,
- })
- end,
+ callback = watch_extmarks,
})
+
+vim.api.nvim_create_user_command("ExtmarkToggle", toggle_marks, { desc = "Toggle extended marks" })
+
+set_global_hl_ns(extmarks_hl_ns)
diff --git a/.config/nvim/plugin/50-highlight.lua b/.config/nvim/plugin/50-highlight.lua
new file mode 100644
index 0000000..1daba82
--- /dev/null
+++ b/.config/nvim/plugin/50-highlight.lua
@@ -0,0 +1,70 @@
+--
+-- 50-highlight.lua
+--
+-- * Creates a function to adjust highlight groups for the solarized colorscheme. It also sets popup
+-- menu width and border style.
+-- * Creates an autocommand to trigger this function when the background or color scheme changes.
+-- * Sets the default colorscheme (solarized) and background (dark).
+--
+-- User commands:
+-- `ColorsBgToggle`: toggle light/dark background
+--
+
+-- Adjust highlights (for the solarized theme) to fit my personal tastes.
+local function adjust_solarized_highlights()
+ local colors = { blue = "#268bd2", yellow = "#b58900" }
+ -- Window bar style
+ vim.api.nvim_set_hl(0, "WinBarCwd", { fg = colors.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", { fg = colors.yellow, bold = true, update = true })
+ -- Todo hl group
+ vim.api.nvim_set_hl(0, "Todo", { bg = "Yellow", fg = "Black", bold = true })
+end
+
+-- Adjust highlights to fit my personal tastes.
+local function adjust_highlights()
+ if vim.g.colors_name == "solarized" then
+ adjust_solarized_highlights()
+ end
+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
+
+----------------------------------------------------------------------------------------------------
+
+vim.api.nvim_create_autocmd("ColorScheme", {
+ desc = "Adjust custom highlights",
+ group = vim.g.dotfiles.augroup,
+ callback = adjust_highlights,
+})
+
+vim.api.nvim_create_autocmd("OptionSet", {
+ desc = "Adjust custom highlights on theme change",
+ pattern = "background",
+ group = vim.g.dotfiles.augroup,
+ callback = adjust_highlights,
+})
+
+vim.api.nvim_create_user_command(
+ "ColorsBgToggle",
+ toggle_theme,
+ { desc = "Toggle light/dark theme" }
+)
+
+vim.cmd.colorscheme("solarized")
+vim.opt.background = "dark"