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