Flake for my NixOS devices
1{inputs, ...}: {
2 pkgs,
3 config,
4 lib,
5 ...
6}: {
7 imports = [inputs.nixvim.homeModules.nixvim];
8
9 options.cow.neovim.enable = lib.mkEnableOption "Neovim + Nixvim + Customizations";
10
11 config = lib.mkIf config.cow.neovim.enable {
12 cow.imperm.keep = [".local/share/nvim"];
13
14 home.packages = with pkgs; [
15 ripgrep
16 fd
17 ];
18
19 home.sessionVariables.EDITOR = "nvim";
20
21 programs.neovide = lib.mkIf config.cow.gdi.enable {
22 enable = true;
23 settings = {
24 fork = true;
25 font = {
26 normal = [{family = "monospace";}];
27 size = 18.0;
28 };
29 title-hidden = false;
30 };
31 };
32
33 programs.nixvim = {
34 enable = true;
35 enableMan = false;
36 defaultEditor = true;
37 viAlias = true;
38 vimAlias = true;
39
40 nixpkgs.pkgs = pkgs;
41 clipboard.providers.wl-copy.enable = config.cow.gdi.enable;
42
43 globals.mapleader = " ";
44
45 colorschemes.catppuccin = lib.mkIf config.cow.cat.enable {
46 enable = true;
47 settings = {
48 inherit (config.catppuccin) flavor;
49 no_underline = false;
50 no_bold = false;
51 no_italics = false;
52 term_colors = true;
53 # transparent_background = true;
54 integrations = {
55 alpha = true;
56 dropbar.enabled = true;
57 fidget = true;
58 markdown = true;
59 dap = true;
60 ufo = true;
61 rainbow_delimiters = true;
62 lsp_trouble = true;
63 which_key = true;
64 telescope.enabled = true;
65 treesitter = true;
66 lsp_saga = true;
67 illuminate = {
68 enabled = true;
69 lsp = true;
70 };
71 neotree = true;
72 native_lsp = {
73 enabled = true;
74 inlay_hints = {
75 background = true;
76 };
77 virtual_text = {
78 errors = ["italic"];
79 hints = ["italic"];
80 information = ["italic"];
81 warnings = ["italic"];
82 ok = ["italic"];
83 };
84 underlines = {
85 errors = ["underline"];
86 hints = ["underline"];
87 information = ["underline"];
88 warnings = ["underline"];
89 };
90 };
91 };
92 };
93 };
94
95 extraConfigLua = ''
96 vim.diagnostic.config({
97 signs = {
98 text = {
99 [vim.diagnostic.severity.ERROR] = "",
100 [vim.diagnostic.severity.WARN] = "",
101 },
102 },
103 })
104 vim.g.neovide_cursor_vfx_mode = "pixiedust"
105
106 -- require("satellite").setup({})
107 '';
108
109 autoGroups = {
110 restore_cursor = {};
111 open_neotree = {};
112 };
113
114 filetype.extension = {
115 mdx = "mdx";
116 };
117
118 opts = {
119 number = true;
120 relativenumber = true;
121 smartindent = true;
122 cursorline = true;
123 showtabline = 2;
124 tabstop = 2;
125 shiftwidth = 2;
126 breakindent = true;
127 fillchars.__raw = ''[[eob: ,fold: ,foldopen:,foldsep: ,foldclose:]]'';
128 foldcolumn = "1";
129 foldlevel = 10;
130 foldlevelstart = 10;
131 foldenable = true;
132 mouse = "";
133 };
134
135 autoCmd = [
136 {
137 group = "restore_cursor";
138 event = ["BufReadPost"];
139 pattern = "*";
140 callback.__raw = ''
141 function()
142 if
143 vim.fn.line "'\"" > 1
144 and vim.fn.line "'\"" <= vim.fn.line "$"
145 and vim.bo.filetype ~= "commit"
146 and vim.fn.index({ "xxd", "gitrebase" }, vim.bo.filetype) == -1
147 then
148 vim.cmd "normal! g`\""
149 end
150 end
151 '';
152 }
153 {
154 group = "open_neotree";
155 event = ["BufRead"];
156 pattern = "*";
157 once = true;
158 callback.__raw = ''
159 function()
160 if
161 vim.bo.filetype ~= "alpha"
162 and (not vim.g.neotree_opened)
163 then
164 vim.cmd "Neotree show"
165 vim.g.neotree_opened = true
166 end
167 end
168 '';
169 }
170 ];
171
172 performance = {
173 byteCompileLua = {
174 enable = true;
175 nvimRuntime = true;
176 plugins = true;
177 };
178 combinePlugins = {
179 enable = true;
180 };
181 };
182
183 keymaps = let
184 prefixMap = pre: maps:
185 builtins.map (k: {
186 action = "<cmd>${k.action}<cr>";
187 key = "${pre}${k.key}";
188 options = k.options;
189 })
190 maps;
191 in
192 lib.lists.flatten (
193 builtins.map (g:
194 if builtins.hasAttr "group" g
195 then prefixMap g.prefix g.keys
196 else g) [
197 {
198 action = ''"+p'';
199 key = "<C-S-V>";
200 options.desc = "Paste from system clipboard";
201 }
202 {
203 action = ''"+y'';
204 key = "<C-S-C>";
205 options.desc = "Copy to system clipboard";
206 }
207 {
208 action = ''"+x'';
209 key = "<C-S-X>";
210 options.desc = "Cut to system clipboard";
211 }
212 {
213 action = ''<cmd>Format<cr>'';
214 key = "<C-S-I>";
215 options.desc = "Format Buffer";
216 }
217 {
218 group = "Tab Navigation";
219 prefix = "<Tab>";
220 keys = [
221 {
222 action = "BufferLineCycleNext";
223 key = "e";
224 options.desc = "Next Tab";
225 }
226 {
227 action = "BufferLineCyclePrev";
228 key = "q";
229 options.desc = "Previous Tab";
230 }
231 {
232 action = "Neotree toggle";
233 key = "t";
234 options.desc = "Toggle Neotree";
235 }
236 ];
237 }
238 {
239 group = "Tab Closing";
240 prefix = "<Tab><Tab>";
241 keys = [
242 {
243 action = "BufferLineCloseLeft";
244 key = "q";
245 options.desc = "Close Tab Left";
246 }
247 {
248 action = "BufferLineCloseRight";
249 key = "e";
250 options.desc = "Close Tab Right";
251 }
252 {
253 action = "BufferLinePickClose";
254 key = "<Tab>";
255 options.desc = "Pick Tab and Close";
256 }
257 {
258 action = "BufferLineCloseOthers";
259 key = "w";
260 options.desc = "Close Other Tabs";
261 }
262 ];
263 }
264 {
265 action = "<cmd>Bdelete<cr>";
266 key = "<C-q>";
267 options.desc = "Close Current Buffer";
268 }
269 {
270 group = "LSP Actions";
271 prefix = "<C-.>";
272 keys = [
273 {
274 action = "Lspsaga code_action code_action";
275 key = "a";
276 options.desc = "Code Actions";
277 }
278 {
279 action = "Lspsaga rename";
280 key = "r";
281 options.desc = "LSP Rename";
282 }
283 {
284 action = "Lspsaga diagnostic_jump_next";
285 key = "e";
286 options.desc = "Next Diagnostic";
287 }
288 {
289 action = "Lspsaga diagnostic_jump_previous";
290 key = "E";
291 options.desc = "Previous Diagnostic";
292 }
293 {
294 action = "Lspsaga goto_definition";
295 key = "d";
296 options.desc = "Jump to Definition";
297 }
298 {
299 action = "Lspsaga peek_definition";
300 key = "D";
301 options.desc = "Peek Definition";
302 }
303 {
304 action = "Lspsaga finder ref";
305 key = "fr";
306 options.desc = "Find References";
307 }
308 {
309 action = "Lspsaga finder imp";
310 key = "fi";
311 options.desc = "Find Implementations";
312 }
313 {
314 action = "Lspsaga finder def";
315 key = "fd";
316 options.desc = "Find Definitions";
317 }
318 {
319 action = "Lspsaga finder";
320 key = "ff";
321 options.desc = "Finder";
322 }
323 {
324 action = "Lspsaga hover_doc";
325 key = "h";
326 options.desc = "Hover Doc";
327 }
328 ];
329 }
330 {
331 action = "<cmd>Telescope<cr>";
332 key = "<leader><leader>";
333 options.desc = "Telescope Launch";
334 }
335 {
336 action.__raw = "[[<C-\\><C-n><C-w>]]";
337 mode = ["t"];
338 key = "<C-w>";
339 }
340 {
341 action.__raw = "[[<C-\\><C-n>]]";
342 mode = ["t"];
343 key = "<esc>";
344 }
345 {
346 action = "<cmd>WhichKey<cr>";
347 key = "<C-/>";
348 }
349 ]
350 );
351
352 extraPlugins = with pkgs.vimPlugins;
353 (lib.optional config.cow.dev.web {plugin = pkgs.nvim-mdx;})
354 ++ [
355 {plugin = satellite-nvim;}
356 {plugin = flatten-nvim;}
357 {plugin = tiny-devicons-auto-colors-nvim;}
358 ];
359
360 plugins = {
361 telescope = {
362 enable = true;
363 settings.defaults = {
364 layout_config.prompt_position = "top";
365 };
366 extensions = {
367 file-browser.enable = true;
368 ui-select.enable = true;
369 undo.enable = true;
370 };
371 keymaps = lib.fix (self: {
372 "<leader>u" = {
373 action = "undo";
374 options.desc = "Undo Tree";
375 };
376 "<leader>c" = {
377 action = "commands";
378 options.desc = "Browse Commands";
379 };
380 "<leader>w" = {
381 action = "spell_suggest";
382 options.desc = "Spell Suggest";
383 };
384 "<leader>s" = {
385 action = "lsp_document_symbols";
386 options.desc = "LSP Symbols";
387 };
388 "<leader>t" = {
389 action = "treesitter";
390 options.desc = "Treesitter Symbols";
391 };
392 "<leader>f" = {
393 action = "find_files";
394 options.desc = "Files";
395 };
396 "<leader>gs" = {
397 action = "git_status";
398 options.desc = "Git Status";
399 };
400 "<leader>gb" = {
401 action = "git_branches";
402 options.desc = "Git Branches";
403 };
404 "<leader>gc" = {
405 action = "git_commits";
406 options.desc = "Git Commits";
407 };
408 "<leader>r" = {
409 action = "oldfiles";
410 options.desc = "Recent Files";
411 };
412 "<leader>l" = self."<C-S-F>";
413 "<C-S-F>" = {
414 action = "live_grep";
415 options.desc = "Live Grep";
416 };
417 });
418 };
419
420 alpha = {
421 enable = true;
422 settings.opts = {
423 position = "center";
424 };
425 settings.layout = let
426 o = {
427 position = "center";
428 };
429 txt = s: {
430 type = "text";
431 val = s;
432 opts =
433 {
434 hl = "Keyword";
435 }
436 // o;
437 };
438 grp = g: {
439 type = "group";
440 val = g;
441 opts.spacing = 1;
442 };
443 btn = {
444 val,
445 onClick,
446 ...
447 }: {
448 type = "button";
449 inherit val;
450 opts = o;
451 on_press.__raw = "function() vim.cmd[[${onClick}]] end";
452 };
453 cmd = {
454 command,
455 width,
456 height,
457 }: {
458 type = "terminal";
459 inherit command width height;
460 opts = o;
461 };
462 pad = {
463 type = "padding";
464 val = 2;
465 };
466 in
467 [
468 pad
469 pad
470 ]
471 ++ (lib.intersperse pad [
472 (cmd {
473 command = ''
474 ${pkgs.toilet}/bin/toilet " NIXVIM " -f mono12 -F border | ${pkgs.lolcat}/bin/lolcat -f
475 '';
476 # Hardcoding to prevent IFD
477 width = 83; # (builtins.stringLength (lib.trim (builtins.elemAt (lib.splitString "\n" bannerText) 1))) - 3;
478 height = 12; # (builtins.length (lib.splitString "\n" bannerText)) - 1;
479 })
480 (grp [
481 (btn {
482 val = " Terminal";
483 onClick = "ToggleTerm";
484 })
485 (btn {
486 val = " Quit";
487 onClick = "q";
488 })
489 ])
490 (grp [
491 (txt " Neovim Version ${pkgs.neovim.version}")
492 (txt " NixVim Rev ${builtins.substring 0 5 inputs.nixvim.rev}")
493 ])
494 ])
495 ++ [pad];
496 };
497
498 trouble = {
499 enable = true;
500 };
501
502 toggleterm = {
503 enable = true;
504 luaConfig.post = ''
505 local flatten = require('flatten')
506
507 ---@type Terminal?
508 local saved_terminal
509
510 flatten.setup({
511 hooks = {
512 should_block = function(argv)
513 return vim.tbl_contains(argv, "-b")
514 end,
515 pre_open = function()
516 local term = require("toggleterm.terminal")
517 local termid = term.get_focused_id()
518 saved_terminal = term.get(termid)
519 end,
520 post_open = function(opts)
521 if saved_terminal then
522 saved_terminal:close()
523 else
524 vim.api.nvim_set_current_win(opts.winnr)
525 end
526
527 if opts.filetype == "gitcommit" or opts.filetype == "gitrebase" then
528 vim.api.nvim_create_autocmd("BufWritePost", {
529 buffer = opts.bufnr,
530 once = true,
531 callback = vim.schedule_wrap(function()
532 require('bufdelete').bufdelete(opts.bufnr, true)
533 end),
534 })
535 end
536 end,
537 block_end = function()
538 vim.schedule(function()
539 if saved_terminal then
540 saved_terminal:open()
541 saved_terminal = nil
542 end
543 end)
544 end,
545 },
546 window = {
547 open = "alternate",
548 },
549 })
550 '';
551 settings = {
552 size = 20;
553 open_mapping = "[[<C-x>]]";
554 direction = "horizontal";
555 start_in_insert = true;
556 insert_mappings = true;
557 terminal_mappings = true;
558 };
559 };
560
561 treesitter = {
562 enable = true;
563 luaConfig.post = lib.mkIf config.cow.dev.web ''
564 require('mdx').setup()
565 '';
566 settings = {
567 highlight.enable = true;
568 };
569 };
570
571 illuminate.enable = true;
572 cursorline.enable = true;
573
574 # neocord = {
575 # enable = true;
576 # settings.logo = "https://raw.githubusercontent.com/IogaMaster/neovim/main/.github/assets/nixvim-dark.webp";
577 # };
578
579 bufdelete.enable = true;
580
581 bufferline = {
582 enable = true;
583 settings.highlights.__raw = ''
584 require("catppuccin.special.bufferline").get_theme()
585 '';
586 settings.options = {
587 indicator.style = "none";
588 show_buffer_close_icons = false;
589 show_close_icon = false;
590 offsets = [
591 {
592 filetype = "neo-tree";
593 highlight = "String";
594 text = " Neovim";
595 text_align = "center";
596 # separator = true;
597 }
598 ];
599 separator_style = "slant";
600 close_command.__raw = ''require('bufdelete').bufdelete'';
601 hover = {
602 enabled = true;
603 delay = 150;
604 reveal = ["close"];
605 };
606 sort_by = "insert_at_end";
607 diagnostics = "nvim_lsp";
608 diagnostics_indicator.__raw = ''
609 function(count, level, diagnostics_dict, context)
610 local icon = level:match("error") and " " or " "
611 return " " .. icon .. count
612 end
613 '';
614 };
615 };
616
617 statuscol = {
618 enable = true;
619 settings.segments = let
620 dispCond = {
621 __raw = ''
622 function(ln)
623 return vim.bo.filetype ~= "neo-tree"
624 end
625 '';
626 };
627 in [
628 {
629 click = "v:lua.ScSa";
630 condition = [
631 dispCond
632 ];
633 text = [
634 "%s"
635 ];
636 }
637 {
638 click = "v:lua.ScLa";
639 condition = [dispCond];
640 text = [
641 {
642 __raw = "require('statuscol.builtin').lnumfunc";
643 }
644 ];
645 }
646 {
647 click = "v:lua.ScFa";
648 condition = [
649 dispCond
650 {
651 __raw = "require('statuscol.builtin').not_empty";
652 }
653 ];
654 text = [
655 {
656 __raw = "require('statuscol.builtin').foldfunc";
657 }
658 " "
659 ];
660 }
661 ];
662 };
663
664 dropbar = {
665 enable = true;
666 settings = {
667 bar.padding.right = 5;
668 bar.padding.left = 1;
669 };
670 };
671
672 nvim-ufo = {
673 enable = true;
674 };
675
676 # gitgutter = {
677 # enable = true;
678 # settings = {
679 #
680 # };
681 # };
682
683 # TODO: Broken in nixpkgs rn, hash mismatch, #464773
684 lualine = {
685 enable = true;
686 settings = {
687 extensions = [
688 "trouble"
689 "toggleterm"
690 ];
691
692 options = {
693 theme = "catppuccin";
694 disabled_filetypes = ["neo-tree"];
695 ignore_focus = ["neo-tree"];
696 };
697 };
698 };
699
700 nix-develop = {
701 enable = true;
702 package = pkgs.vimPlugins.nix-develop-nvim.overrideAttrs (
703 prev: next: {
704 src = pkgs.fetchFromGitHub {
705 owner = "Bwc9876";
706 repo = "nix-develop.nvim";
707 rev = "089cd52191ccbb3726594e21cd96567af6088dd5";
708 sha256 = "sha256-EIEJk8/IAuG+UICUJ2F8QakgRpFrQ1ezDSJ79NAVuD8=";
709 };
710 }
711 );
712 };
713
714 neo-tree = {
715 enable = true;
716 settings = {
717 hide_root_node = false;
718 add_blank_line_at_top = true;
719 default_component_configs = {
720 container.right_padding = 2;
721 name.trailing_slash = true;
722 indent = {
723 indent_size = 2;
724 with_expanders = true;
725 };
726 };
727 window.width = 40;
728 auto_clean_after_session_restore = true;
729 close_if_last_window = true;
730 filesystem.components.name.__raw = ''
731 function(config, node, state)
732 local components = require('neo-tree.sources.common.components')
733 local name = components.name(config, node, state)
734 if node:get_depth() == 1 then
735 name.text = vim.fs.basename(vim.loop.cwd() or "") .. "/"
736 end
737 return name
738 end
739 '';
740 };
741 };
742
743 image = {
744 luaConfig = {
745 pre = "if not vim.g.neovide then";
746 post = "end";
747 };
748 enable = true;
749 };
750 web-devicons.enable = true;
751
752 indent-o-matic.enable = true;
753 intellitab.enable = true;
754
755 which-key = {
756 enable = true;
757 settings = {
758 show_help = true;
759 preset = "modern";
760 win.wo.winblend = 8;
761 };
762 };
763
764 fidget = {
765 enable = true;
766 settings.notification = {
767 override_vim_notify = true;
768 window = {
769 y_padding = 2;
770 x_padding = 2;
771 zindex = 50;
772 align = "top";
773 winblend = 0;
774 };
775 };
776 };
777
778 # none-ls = {
779 # enable = true;
780 # sources.formatting = {
781 # prettier = {
782 # enable = true;
783 # disableTsServerFormatter = true;
784 # };
785 # yamlfmt.enable = true;
786 # typstyle.enable = true;
787 # markdownlint.enable = true;
788 # };
789 # sources.diagnostics = {
790 # markdownlint.enable = true;
791 # };
792 # };
793
794 cmp = {
795 enable = true;
796 settings = {
797 sources = map (name: {inherit name;}) [
798 "nvim_lsp"
799 "nvim_lsp_signature_help"
800 "path"
801 "buffer"
802 ];
803 mapping = {
804 "<Esc>" = "cmp.mapping.abort()";
805 "<Tab>" = "cmp.mapping.confirm({ select = true })";
806 "<Up>" = "cmp.mapping(cmp.mapping.select_prev_item(), {'i', 's'})";
807 "<Down>" = "cmp.mapping(cmp.mapping.select_next_item(), {'i', 's'})";
808 };
809 };
810 };
811
812 cmp-nvim-lsp.enable = true;
813 cmp-nvim-lsp-document-symbol.enable = true;
814 cmp-spell.enable = true;
815
816 rainbow-delimiters.enable = true;
817 preview.enable = true;
818
819 # jupytext.enable = true;
820
821 # Broken
822 # hex = {
823 # enable = true;
824 # settings = {
825 # assemble_cmd = "xxd -r";
826 # dump_cmd = "xxd -g 1 -u";
827 # };
828 # };
829
830 conform-nvim = {
831 enable = true;
832 settings.default_format_opts = {
833 lsp_format = "prefer";
834 };
835 # Taken from https://github.com/stevearc/conform.nvim/blob/master/doc/recipes.md#format-command
836 luaConfig.post = ''
837 vim.api.nvim_create_user_command("Format", function(args)
838 local range = nil
839 if args.count ~= -1 then
840 local end_line = vim.api.nvim_buf_get_lines(0, args.line2 - 1, args.line2, true)[1]
841 range = {
842 start = { args.line1, 0 },
843 ["end"] = { args.line2, end_line:len() },
844 }
845 end
846 require("conform").format({ async = true, lsp_format = "fallback", range = range })
847 end, { range = true })
848 '';
849 };
850
851 lspconfig.enable = true;
852
853 lspsaga = {
854 enable = true;
855 settings = {
856 symbol_in_winbar.enable = false;
857 implement.enable = false;
858 lightbulb.enable = false;
859 ui = {
860 code_action = "";
861 actionfix = "";
862 };
863 hover = {
864 openCmd = "!xdg-open";
865 openLink = "<leader>o";
866 maxWidth = 0.5;
867 maxHeight = 0.4;
868 };
869 rename.autoSave = true;
870 finder = {
871 keys.close = "<ESC>";
872 };
873 codeAction.keys.quit = "<ESC>";
874 };
875 };
876
877 crates.enable = true;
878
879 numbertoggle.enable = true;
880
881 # rustaceanvim.enable = true;
882 vim-css-color.enable = true;
883 };
884
885 lsp = {
886 inlayHints.enable = true;
887
888 servers = let
889 inherit
890 (config.cow.dev)
891 dotnet
892 python
893 haskell
894 rust
895 web
896 c
897 ;
898 in {
899 clangd.enable = c;
900 astro.enable = web;
901 hls = lib.mkIf haskell {
902 enable = true;
903 # ghcPackage = pkgs.haskell.compiler.ghc912;
904 package = pkgs.haskell.packages.ghc912.haskell-language-server;
905 };
906 sqls.enable = web;
907 mdx_analyzer = lib.mkIf web {
908 enable = true;
909 package = pkgs.mdx-language-server;
910 };
911 # denols.enable = true;
912 ts_ls.enable = web;
913 html.enable = web;
914 marksman.enable = web;
915 cssls.enable = web;
916 tailwindcss.enable = web;
917 jsonls.enable = web;
918 yamlls.enable = web;
919 ruff.enable = python;
920 csharp_ls.enable = dotnet;
921 svelte.enable = web;
922 nil_ls.enable = true;
923 bashls.enable = true;
924 nushell.enable = config.cow.nushell.enable;
925 taplo.enable = rust;
926 typos_lsp.enable = true;
927 rust_analyzer = lib.mkIf rust {
928 enable = true;
929 package = pkgs.rust-analyzer-nightly;
930 packageFallback = true;
931 };
932 lemminx.enable = web;
933 eslint.enable = web;
934 just.enable = true;
935 };
936 };
937 };
938 };
939}