inputs: { lib, pkgs, ... }: { programs.nixvim = { enable = true; nixpkgs.useGlobalPackages = true; luaLoader.enable = true; globals = { mapleader = ";"; maplocalleader = ","; polyglot_disabled = [ # ftdetect messes up `vim.filetype.add` "ftdetect" "autoindent" "sensible" ]; }; opts = { backup = true; # Use `//` at the end to store the absolute file name backupdir.__raw = "vim.fn.stdpath('data') .. '/backup//'"; completeopt = [ "menu" "menuone" "noinsert" "noselect" ]; cursorline = true; diffopt = [ "internal" "filler" "closeoff" "linematch:60" ]; grepprg = "${lib.getExe pkgs.ripgrep} --vimgrep --smart-case"; ignorecase = true; inccommand = "split"; list = true; listchars = { extends = ">"; nbsp = "+"; precedes = "<"; tab = " "; trail = "-"; }; scrolloff = 3; shiftwidth = 4; shortmess = "ncAxoTOtfFli"; showmode = false; sidescrolloff = 5; smartcase = true; smartindent = true; tabstop = 4; title = true; undofile = true; updatetime = 1000; wildmode = [ "longest:full" "full" ]; }; colorschemes.melange.enable = true; diagnostics.virtual_lines = true; keymaps = let toggle = key: option: { mode = "n"; key = "yo${key}"; action = "setlocal ${option}! ${option}?"; options.desc = "Toggle ${option}"; }; in [ { key = "-"; action.__raw = "require('oil').open"; options.desc = "Open parent directory"; } # Git { key = "gg"; action = "Neogit"; options.desc = "Open Neogit"; } { key = "gd"; action = "DiffviewOpen"; options.desc = "Open Diffview"; } # GitSigns # TODO: noremap? buffer local? silent? { key = "gs"; action.__raw = "require('gitsigns').stage_hunk"; options.desc = "Stage hunk"; } { key = "gr"; action.__raw = "require('gitsigns').reset_hunk"; options.desc = "Reset hunk"; } # TODO: visual stage/reset { key = "gS"; action.__raw = "require('gitsigns').stage_buffer"; options.desc = "Stage buffer"; } { key = "gR"; action.__raw = "require('gitsigns').reset_buffer"; options.desc = "Stage buffer"; } { key = "gu"; action.__raw = "require('gitsigns').undo_stage_hunk"; options.desc = "Undo stage hunk"; } { key = "gp"; action.__raw = "require('gitsigns').preview_hunk_inline"; options.desc = "Preview hunk"; } { key = "gb"; action.__raw = "function() require('gitsigns').blame_line { full = true } end"; options.desc = "Blame line"; } { key = "]g"; action.__raw = '' function() if vim.wo.diff then return ']c' end vim.schedule(function() package.loaded.gitsigns.next_hunk() end) return '' end ''; options = { expr = true; desc = "Next hunk"; }; } { key = "[g"; action.__raw = '' function() if vim.wo.diff then return '[c' end vim.schedule(function() package.loaded.gitsigns.prev_hunk() end) return '' end ''; options = { expr = true; desc = "Next hunk"; }; } # Leap { key = ""; mode = [ "n" "x" "o" ]; action = "(leap-forward)"; options.desc = "Leap forward to"; } { key = ""; mode = [ "n" "x" "o" ]; action = "(leap-backward)"; options.desc = "Leap backward to"; } { key = "g"; mode = [ "n" "x" "o" ]; action = "(leap-from-window)"; options.desc = "Leap from window"; } { key = "gnn"; mode = [ "n" "x" "o" ]; action.__raw = '' function() require('leap.treesitter').select() end ''; options.desc = "Start incremental selection"; } { key = "gnN"; mode = [ "n" "x" "o" ]; action = "Vlua require('leap.treesitter').select()"; options.desc = "Start linewise incremental selection"; } # Toggle options (toggle "c" "cursorline") (toggle "C" "cursorcolumn") (toggle "h" "hlsearch") (toggle "i" "ignorecase") (toggle "l" "list") (toggle "n" "number") (toggle "r" "relativenumber") (toggle "s" "spell") (toggle "w" "wrap") { mode = "n"; key = "yod"; action.__raw = '' function() if vim.wo.diff then vim.cmd.diffoff() print('diffoff') else vim.cmd.diffthis() print('diffthis') end end ''; options.desc = "Toggle diff"; } # Trouble { key = "xx"; mode = "n"; action = "Trouble diagnostics toggle"; options.desc = "Diagnostics (Trouble)"; } { key = "xX"; mode = "n"; action = "Trouble diagnostics toggle filter.buf=0"; options.desc = "Buffer Diagnostics (Trouble)"; } { key = "cs"; mode = "n"; action = "Trouble symbols toggle focus=false"; options.desc = "Symbols (Trouble)"; } { key = "cl"; mode = "n"; action = "Trouble lsp toggle focus=false win.position=right"; options.desc = "LSP Definitions / references / ... (Trouble)"; } { key = "xL"; mode = "n"; action = "Trouble loclist toggle"; options.desc = "Location List (Trouble)"; } { key = "xQ"; mode = "n"; action = "Trouble qflist toggle"; options.desc = "Quickfix List (Trouble)"; } { key = "sd"; action = '' function() local new_config = not vim.diagnostic.config().virtual_lines vim.diagnostic.config({ virtual_lines = new_config }) end ''; mode = "n"; lua = true; options.desc = "LSP toggle inline diagnostics"; } { key = "sT"; action = '' function() vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled()) end ''; mode = "n"; lua = true; options.desc = "LSP toggle inline text"; } ]; # TODO: extraPlugins = with pkgs.unstable.vimPlugins; [ highlight-undo-nvim vim-abolish # TODO: make that modular playground vim-rhubarb fugitive-gitlab-vim hunk-nvim nui-nvim vim-polyglot nvim-surround ]; plugins = { blink-cmp = { enable = true; settings = { keymap.preset = "enter"; completion.documentation.auto_show = true; }; }; diffview.enable = true; dressing.enable = true; fugitive.enable = true; gitignore.enable = true; gitsigns.enable = true; grug-far = { enable = true; settings.engines.astgrep.path = "ast-grep"; lazyLoad.settings = { cmd = "GrugFar"; keys = [ { __unkeyed-1 = "fG"; __unkeyed-2 = "GrugFar"; desc = "Grug find and replace"; } ]; }; }; lastplace.enable = true; leap = { enable = true; addDefaultMappings = false; }; lualine = { enable = true; settings.options = { component_separators.left = ""; component_separators.right = ""; section_separators.left = ""; section_separators.right = ""; icons_enabled = false; }; }; luasnip.enable = true; lz-n.enable = true; mini = { enable = true; modules = { ai.custom_textobjects = { search_method = "cover_or_nearest"; f.__raw = "require('mini.ai').gen_spec.treesitter({ a = '@function.outer', i = '@function.inner' })"; F.__raw = "require('mini.ai').gen_spec.treesitter({ a = '@call.outer', i = '@call.inner' })"; c.__raw = "require('mini.ai').gen_spec.treesitter({ a = '@comment.outer', i = '@comment.inner' })"; b.__raw = "require('mini.ai').gen_spec.treesitter({ a = '@block.outer', i = '@block.inner' })"; a.__raw = "require('mini.ai').gen_spec.treesitter({ a = '@parameter.outer', i = '@parameter.inner' })"; o.__raw = '' require('mini.ai').gen_spec.treesitter({ a = { '@conditional.outer', '@loop.outer' }, i = { '@conditional.inner', '@loop.inner' }, }) ''; }; align = { }; bracketed = { }; clue = { triggers = [ { mode = "n"; keys = "yo"; } { mode = "n"; keys = "["; } { mode = "n"; keys = "]"; } { mode = "n"; keys = ""; } { mode = "n"; keys = "gr"; } ]; }; trailspace = { }; }; }; neogit = { enable = true; settings = { graph_style = "unicode"; integrations = { diffview = true; telescope = true; }; }; }; none-ls = { enable = true; sources = { code_actions = { gitrebase.enable = true; gitsigns.enable = true; # TODO: maybe? #refactoring.enable = true; statix.enable = true; }; diagnostics = { deadnix.enable = true; statix = { enable = true; settings.extra_args = let config = (pkgs.formats.toml { }).generate "statix.toml" { disabled = [ "repeated_keys" ]; }; in [ "-c" "${config}" ]; }; vale = { enable = true; settings.extra_filetypes = [ "pandoc" "rst" ]; }; }; formatting = { shfmt.enable = true; typstyle.enable = true; }; }; }; oil.enable = true; # TODO: maybe #refactoring.enable = true; smartcolumn = { enable = true; settings = { colorcolumn = "80"; custom_colorcolumn = { cpp = [ "100" "140" ]; java = [ "100" "140" ]; nix = [ "100" "120" ]; rust = [ "80" "100" ]; }; disabled_filetypes = [ "checkhealth" "help" "lspinfo" "Trouble" ]; }; }; snacks = { enable = true; settings = { bigfile.enabled = true; indent = { enabled = true; animate.enabled = false; }; input.enabled = true; notifier.enabled = true; quickfile.enabled = true; statuscolumn.enabled = true; words.enabled = true; }; }; spider = { enable = true; keymaps.motions = { b = "b"; e = "e"; ge = "ge"; w = "w"; }; }; telescope = { enable = true; settings.defaults.mappings = { i = { "".__raw = "require('trouble.sources.telescope').open"; "".__raw = "require('trouble.sources.telescope').add"; }; n = { "".__raw = "require('trouble.sources.telescope').open"; "".__raw = "require('trouble.sources.telescope').add"; }; }; extensions.undo.enable = true; keymaps = { "fb" = { action = "buffers"; options.desc = "Telescope buffers"; }; "ff" = { action = "find_files"; options.desc = "Telescope find files"; }; "fg" = { action = "live_grep"; options.desc = "Telescope live grep"; }; "fh" = { action = "help_tags"; options.desc = "Telescope help tags"; }; "fo" = { action = "oldfiles"; options.desc = "Telescope old files"; }; "fs" = { action = "spell_suggest"; options.desc = "Telescope spell suggest"; }; "ft" = { action = "treesitter"; options.desc = "Telescope treesitter"; }; "fu" = { action = "undo"; options.desc = "Telescope undo"; }; "fw" = { action = "git_status"; options.desc = "Telescope git status"; }; }; }; treesitter = { enable = true; nixvimInjections = true; settings = { highlight.enable = true; indent.enable = true; }; }; treesitter-context = { enable = true; settings = { max_lines = 5; min_window_height = 20; }; }; treesitter-textobjects = { enable = true; lspInterop = { enable = true; peekDefinitionCode = { "df" = { query = "@function.outer"; desc = "Peek outer function"; }; "dF" = { query = "@class.outer"; desc = "Peek outer class"; }; }; }; }; trouble.enable = true; vim-matchup = { enable = true; treesitter.enable = true; }; web-devicons.enable = true; }; extraConfigLua = '' vim.api.nvim_create_autocmd("TextYankPost", { desc = "Highlight yanked text", callback = function() vim.highlight.on_yank() end, }) require('highlight-undo').setup({ keymaps = { -- Right now messes up registers paste = { disabled = true, }, Paste = { disabled = true, }, }, }) -- For fugitive's :GBrowse vim.api.nvim_create_user_command("Browse", function(opts) vim.ui.open(opts.args) end, { nargs = 1 }) require("nvim-surround").setup() require("hunk").setup({ ui = { layout = "horizontal", }, }) ''; extraConfigLuaPost = '' -- Load local config local function isModuleAvailable(name) if package.loaded[name] then return true else for _, searcher in ipairs(package.searchers or package.loaders) do local loader = searcher(name) if type(loader) == "function" then package.preload[name] = loader return true end end return false end end vim.opt.runtimepath:append(vim.fn.stdpath("config")) -- ~/.config/nvim if isModuleAvailable "local_config" then require "local_config" end ''; }; nixpkgs.overlays = [ (final: _prev: { inherit (final.unstable) neovim neovim-unwrapped vimPlugins; }) ]; environment.variables = { EDITOR = "nvim"; MANPAGER = "nvim +Man!"; }; home-manager.users.minijackson.xdg.dataFile."nvim/backup/.keep".text = ""; home-manager.users.root.xdg.dataFile."nvim/backup/.keep".text = ""; }