This repository has no description
12 kB
381 lines
1{ inputs, ... }:
2{
3 perSystem =
4 { system, pkgs, ... }:
5 {
6 packages.nvim = inputs.nixvim.legacyPackages.${system}.makeNixvim {
7 # The options-reference manpage builds via nixvim's docs/pkgs.nix, which
8 # patches nixos-render-docs with nixpkgs PR#538502 (admonition support).
9 # That PR is already merged in our nixpkgs, so the patch fails to apply
10 # and breaks the build. We follow a single nixpkgs, so we can't feed
11 # nixvim an older one — just drop the manpage (irrelevant to the editor).
12 enableMan = false;
13 vimAlias = true;
14 colorschemes = {
15 ayu = {
16 enable = true;
17 settings = {
18 mirage = false;
19 dark = true;
20 };
21 settings.overrides = {
22 EoLSpace = {
23 bg = "#883333";
24 };
25 };
26 };
27 everforest = {
28 enable = true;
29 settings.overrides = {
30 EoLSpace = {
31 bg = "#FF2222"; # doesn't work
32 };
33 };
34 };
35 };
36 extraConfigLua = ''
37 -- Cross-platform background detection and colorscheme switching
38 local last_theme = nil
39
40 local function detect_and_set_theme()
41 local is_dark = false
42
43 if vim.fn.has("mac") == 1 then
44 -- macOS: check system appearance
45 local result = vim.fn.system("defaults read -g AppleInterfaceStyle 2>/dev/null")
46 is_dark = result:match("Dark") ~= nil
47 elseif vim.fn.has("unix") == 1 then
48 -- Linux: check if running under a dark theme
49 -- Check NixOS theme system first (always check this fresh)
50 local f = io.open("/var/theme/current_theme", "r")
51 if f then
52 local content = f:read("*all"):gsub("^%s+", ""):gsub("%s+$", "")
53 f:close()
54 is_dark = (content == "dark")
55 else
56 -- Fallback to other methods if theme file doesn't exist
57 local methods = {
58 -- GNOME/GTK
59 "gsettings get org.gnome.desktop.interface gtk-theme 2>/dev/null",
60 "gsettings get org.gnome.desktop.interface color-scheme 2>/dev/null",
61 -- KDE
62 "kreadconfig5 --file kdeglobals --group General --key ColorScheme 2>/dev/null",
63 -- Check if COLORFGBG suggests dark background
64 function()
65 local colorfgbg = vim.env.COLORFGBG
66 if colorfgbg then
67 local fg, bg = colorfgbg:match("(%d+);(%d+)")
68 if fg and bg then
69 -- If foreground is lighter than background, likely dark theme
70 return tonumber(fg) > tonumber(bg)
71 end
72 end
73 return false
74 end
75 }
76
77 for _, method in ipairs(methods) do
78 if type(method) == "function" then
79 is_dark = method()
80 if is_dark then break end
81 else
82 local result = vim.fn.system(method)
83 if result:match("[Dd]ark") or result:match("prefer%-dark") then
84 is_dark = true
85 break
86 end
87 end
88 end
89 end
90 end
91
92 local new_theme = is_dark and "dark" or "light"
93
94 if new_theme ~= last_theme then
95 last_theme = new_theme
96 vim.o.background = new_theme
97
98 if is_dark then
99 vim.cmd("colorscheme ayu")
100 else
101 vim.cmd("colorscheme everforest")
102 end
103 end
104 end
105
106 -- Set initial theme
107 vim.defer_fn(detect_and_set_theme, 100)
108
109 -- Check for theme changes every 3 seconds (less frequent to be less resource intensive)
110 local timer = vim.loop.new_timer()
111 timer:start(3000, 3000, vim.schedule_wrap(detect_and_set_theme))
112
113 -- Create a command to manually refresh theme
114 vim.api.nvim_create_user_command("RefreshTheme", detect_and_set_theme, {})
115
116 -- Configure lsp_lines to always show and disable default virtual_text
117 vim.diagnostic.config({
118 virtual_text = false,
119 virtual_lines = true,
120 })
121 '';
122 globals = {
123 mapleader = " ";
124 };
125 opts = {
126 mouse = "a";
127 number = true;
128 shiftwidth = 2;
129 clipboard = "unnamedplus";
130 # undo stuff
131 undofile = true;
132 undolevels = 10000;
133 undoreload = 10000;
134 listchars = "tab:⇥ ,extends:❯,precedes:❮,nbsp:␣,trail:·,eol:↲";
135 };
136 extraConfigVim = ''
137 set et
138
139 " show special characters
140 set list
141
142 " match trailing whitespace
143 match EoLSpace /\s\+$/
144
145 " need to use lua to expand $HOME
146 lua vim.o.undodir = vim.fs.normalize('$HOME/.vim/undodir')
147 '';
148 extraPlugins = [
149 pkgs.vimPlugins.vim-fetch
150 ];
151 keymaps = [
152 {
153 mode = "i";
154 key = "<C-p>";
155 action = "<Nop>";
156 }
157 {
158 mode = "i";
159 key = "<C-n>";
160 action = "<Nop>";
161 }
162 {
163 mode = "n";
164 key = "<C-c>";
165 action = ":q<cr>";
166 }
167 {
168 mode = "n";
169 key = "<Space>";
170 action = "<Nop>";
171 }
172 {
173 mode = [
174 "n"
175 "v"
176 ];
177 key = "<leader>cc";
178 action = ":CopilotChatOpen<cr>";
179 }
180 {
181 mode = [
182 "n"
183 "v"
184 ];
185 key = "<leader>ce";
186 action = ":CopilotChatExplain<cr>";
187 }
188 {
189 mode = [
190 "n"
191 "v"
192 ];
193 key = "<leader>cr";
194 action = ":CopilotChatReview<cr>";
195 }
196 {
197 mode = [
198 "n"
199 "v"
200 ];
201 key = "<leader>cf";
202 action = ":CopilotChatFix<cr>";
203 }
204 {
205 mode = [
206 "n"
207 "v"
208 ];
209 key = "<leader>cd";
210 action = ":CopilotChatDocs<cr>";
211 }
212 {
213 mode = [
214 "n"
215 "x"
216 ];
217 key = "p";
218 action = "<Plug>(YankyPutAfter)";
219 }
220 {
221 mode = [
222 "n"
223 "x"
224 ];
225 key = "P";
226 action = "<Plug>(YankyPutBefore)";
227 }
228 {
229 mode = [
230 "c"
231 "i"
232 "n"
233 "x"
234 ];
235 key = "<M-CR>";
236 action = ":lua vim.lsp.buf.code_action()<CR>";
237 }
238 {
239 mode = [
240 "n"
241 ];
242 key = "gd";
243 action = ":lua vim.lsp.buf.definition()<CR>";
244 }
245 {
246 mode = [
247 "n"
248 ];
249 key = "gD";
250 action = ":lua vim.lsp.buf.declaration()<CR>";
251 }
252 {
253 mode = "n";
254 key = "<leader>ll";
255 action = ":lua require('lsp_lines').toggle()<CR>";
256 options.desc = "Toggle lsp_lines";
257 }
258 ];
259 # autoCmd = [
260 # {
261 # event = "BufWritePost";
262 # desc = "run treefmt on save";
263 # # https://stackoverflow.com/questions/77466697/how-to-automatically-format-on-save
264 # callback.__raw = ''
265 # function()
266 # vim.cmd("silent !treefmt %")
267 # vim.cmd("edit")
268 # end
269 # '';
270 # }
271 # ];
272 plugins.none-ls = {
273 enable = true;
274 # sources.formatting.treefmt = {
275 # enable = true;
276 # package = pkgs.emptyDirectory;
277 # };
278 # package = pkgs.vimPlugins.none-ls-nvim.overrideAttrs (_old: {
279 # src = pkgs.fetchFromGitHub {
280 # owner = "lassulus";
281 # repo = "none-ls.nvim";
282 # rev = "292c2b5eadea0a097b9a01804aea70f5c8125200";
283 # sha256 = "sha256-nbzqXsxmYfxngdJH3NXMLmGgygh6reSEnruoc6l8t/s=";
284 # };
285 # });
286 sources.formatting.nix_flake_fmt.enable = true;
287 };
288 plugins.web-devicons.enable = true;
289 plugins.telescope.extensions.undo.enable = true;
290 plugins.telescope = {
291 enable = true;
292 keymaps = {
293 "<leader>b" = "buffers";
294 "<leader>u" = "undo";
295 "<leader>ff" = "find_files";
296 "<leader>fg" = "live_grep";
297 "<leader>fh" = "help_tags";
298 "<leader>fp" = "yank_history";
299 };
300 };
301 plugins.lsp-format.enable = true;
302 plugins.lsp-lines.enable = true;
303 plugins.lsp = {
304 enable = true;
305 servers = {
306 bashls.enable = true; # bash
307 nixd = {
308 enable = true; # nix
309 extraOptions.offset_encoding = "utf-8"; # workaround https://github.com/nix-community/nixvim/issues/2390#issuecomment-2408101568
310 };
311 ruff.enable = true; # python
312 pyright.enable = true; # python
313 jsonls.enable = true; # json
314 };
315 };
316 plugins.treesitter.enable = true;
317 plugins.copilot-cmp.enable = true;
318 plugins.copilot-lua.settings.panel.enabled = false;
319 plugins.copilot-lua.settings.suggestion.enabled = false;
320 plugins.cmp_yanky.enable = true;
321 plugins.cmp = {
322 enable = true;
323 autoEnableSources = true;
324 settings = {
325 experimental = {
326 ghost_text = true;
327 };
328 sources = [
329 { name = "nvim_lsp"; }
330 {
331 name = "buffer"; # text within current buffer
332 option.get_bufnrs.__raw = "vim.api.nvim_list_bufs";
333 keywordLength = 2;
334 }
335 {
336 name = "copilot"; # enable copilot
337 }
338 {
339 name = "path"; # file system paths
340 keywordLength = 3;
341 }
342 { name = "cmp_yanky"; }
343 ];
344
345 mapping = {
346 "<Tab>" = "cmp.mapping.select_next_item()";
347 "<S-Tab>" = "cmp.mapping.select_prev_item()";
348 "<C-n>" = "cmp.mapping.select_next_item()";
349 "<C-p>" = "cmp.mapping.select_prev_item()";
350 "<C-e>" = "cmp.mapping.abort()";
351 "<C-b>" = "cmp.mapping.scroll_docs(-4)";
352 "<C-f>" = "cmp.mapping.scroll_docs(4)";
353 "<CR>" = ''
354 cmp.mapping({
355 i = function(fallback)
356 if cmp.visible() and cmp.get_active_entry() then
357 cmp.confirm({ behavior = cmp.ConfirmBehavior.Replace, select = false })
358 else
359 fallback()
360 end
361 end,
362 s = cmp.mapping.confirm({ select = true }),
363 c = cmp.mapping.confirm({ behavior = cmp.ConfirmBehavior.Replace, select = true }),
364 })
365 '';
366 };
367 };
368 };
369 plugins.bufferline.enable = true;
370 plugins.which-key.enable = true;
371 plugins.yanky = {
372 enable = true;
373 enableTelescope = true;
374 };
375 plugins.comment.enable = true;
376 plugins.copilot-chat.enable = true;
377 plugins.undotree.enable = true; # seems to be needed for undo history in telescope
378 plugins.gitsigns.enable = true;
379 };
380 };
381}