From 6afd86c4ef058fc60c3fe609ec54be8f7d4c7458 Mon Sep 17 00:00:00 2001 From: Paul-Henri Froidmont Date: Sun, 14 Jul 2024 02:51:36 +0200 Subject: [PATCH] Neovim: configure using lazy.nvim --- modules/editor/vim.nix | 789 +++++++++++++++++++++++++++++------------ 1 file changed, 571 insertions(+), 218 deletions(-) diff --git a/modules/editor/vim.nix b/modules/editor/vim.nix index ab5a801..bd82b71 100644 --- a/modules/editor/vim.nix +++ b/modules/editor/vim.nix @@ -152,6 +152,11 @@ in { }; keymaps = [ + # Search + { + key = ""; + action = "Telescope fd layout_strategy=vertical"; + } { key = "sh"; action = "Telescope help_tags"; @@ -169,28 +174,20 @@ in { } { key = "/"; - action = "Telescope live_grep"; - options.desc = "[S]earch by [G]rep"; + action = "Telescope live_grep layout_strategy=vertical"; + options.desc = "Search with rg"; } { key = "sr"; action = "Telescope resume"; options.desc = "[S]earch [R]esume"; } - { - key = "."; - action.__raw = /*lua*/'' - function() - require("yazi").yazi() - end - ''; - options.desc = "Open file manager"; - } { key = "cx"; - action = "Telescope diagnostics"; + action = "Telescope diagnostics layout_strategy=vertical"; options.desc = "Search diagnostics"; } + # File { key = "fs"; action.__raw = /*lua*/'' @@ -201,22 +198,27 @@ in { ''; options.desc = "Format and save buffer"; } + { + mode = "n"; + key = "fn"; + action = "enew"; + options.desc = "New File"; + } + # Project { key = "ps"; action = "wa"; options.desc = "Save all buffers"; } + # Git { key = "gg"; action = "Neogit"; } - { - key = ""; - action = "Telescope fd"; - } + # Buffers { key = "bb"; - action = "Telescope buffers"; + action = "Telescope buffers layout_strategy=vertical"; } { key = "bn"; @@ -235,217 +237,557 @@ in { } { key = "bk"; - action = "bdel"; - options.desc = "Delete buffer"; + action = "bd"; + options.desc = "Delete buffer and Window"; } { key = "bd"; - action = "bdel"; - options.desc = "Delete buffer"; + action = "bd"; + options.desc = "Delete buffer and Window"; + } + # Windows + { + mode = "n"; + key = "ww"; + action = "p"; + options = { desc = "Other Window"; remap = true; }; + } + { + mode = "n"; + key = "wd"; + action = "c"; + options = { desc = "Delete Window"; remap = true; }; + } + { + mode = "n"; + key = "ws"; + action = "s"; + options = { desc = "Split Window Below"; remap = true; }; + } + { + mode = "n"; + key = "wv"; + action = "v"; + options = { desc = "Split Window Right"; remap = true; }; + } + { + mode = "n"; + key = ""; + action = "j"; + options = { desc = "Go to Lower Winddow"; remap = true; }; + } + { + mode = "n"; + key = "wj"; + action = "j"; + options = { desc = "Go to Lower Winddow"; remap = true; }; + } + { + mode = "n"; + key = ""; + action = "k"; + options = { desc = "Go to Upper Winddow"; remap = true; }; + } + { + mode = "n"; + key = "wk"; + action = "k"; + options = { desc = "Go to Upper Winddow"; remap = true; }; + } + { + mode = "n"; + key = ""; + action = "l"; + options = { desc = "Go to Right Winddow"; remap = true; }; + } + # Move lines + { + mode = "n"; + key = ""; + action = "m .+1=="; + options.desc = "Move Down"; + } + { + mode = "n"; + key = ""; + action = "m .-2=="; + options.desc = "Move Up"; + } + { + mode = "i"; + key = ""; + action = "m .+1==gi"; + options.desc = "Move Down"; + } + { + mode = "i"; + key = ""; + action = "m .-2==gi"; + options.desc = "Move Up"; + } + { + mode = "v"; + key = ""; + action = ":m '>+1gv=gv"; + options.desc = "Move Down"; + } + { + mode = "v"; + key = ""; + action = ":m '<-2gv=gv"; + options.desc = "Move Up"; + } + # Better indenting + { + mode = "v"; + key = "<"; + action = "", true, true, true) + end + if opts.skip_next and next ~= "" and next:match(opts.skip_next) then + return o + end + if opts.skip_ts and #opts.skip_ts > 0 then + local ok, captures = pcall(vim.treesitter.get_captures_at_pos, 0, cursor[1] - 1, math.max(cursor[2] - 1, 0)) + for _, capture in ipairs(ok and captures or {}) do + if vim.tbl_contains(opts.skip_ts, capture.capture) then + return o + end + end + end + if opts.skip_unbalanced and next == c and c ~= o then + local _, count_open = line:gsub(vim.pesc(pair:sub(1, 1)), "") + local _, count_close = line:gsub(vim.pesc(pair:sub(2, 2)), "") + if count_close > count_open then + return o + end + end + return open(pair, neigh_pattern) + end + end + ''; + } + { + pkg = pkgs.vimPlugins.lualine-nvim; + event = "VeryLazy"; + init = /*lua*/ '' + function() + vim.g.lualine_laststatus = vim.o.laststatus + if vim.fn.argc(-1) > 0 then + -- set an empty statusline till lualine loads + vim.o.statusline = " " + else + -- hide the statusline on the starter page + vim.o.laststatus = 0 + end + end + ''; + opts.__raw = /*lua*/ '' + { + options = { + icons_enabled = true, + } + } + ''; + } + { + pkg = pkgs.unstable.vimPlugins.neogit; + dependencies = [ pkgs.vimPlugins.plenary-nvim pkgs.vimPlugins.diffview-nvim pkgs.vimPlugins.telescope-nvim ]; + event = "VeryLazy"; + config = true; + } + { + pkg = pkgs.vimPlugins.gitsigns-nvim; + opts.__raw = /*lua*/ '' + { + on_attach = function(buffer) + local gs = package.loaded.gitsigns + + local function map(mode, l ,r, desc) + vim.keymap.set(mode, l, r, { buffer = buffer, desc = desc}) + end + + map("n", "h;", function() + if vim.wo.diff then + vim.cmd.normal({ "]c", bang = true }) + else + gs.nav_hunk("next") + end + end, "Next Hunk") + map("n", "h,", function() + if vim.wo.diff then + vim.cmd.normal({ "[c", bang = true }) + else + gs.nav_hunk("prev") + end + end, "Prev Hunk") + map({ "n", "v" }, "gs", gs.stage_hunk, "Stage Hunk") + map({ "n", "v" }, "gr", gs.reset_hunk, "Reset Hunk") + map("n", "gS", gs.stage_buffer, "Stage Buffer") + map("n", "gu", gs.undo_stage_hunk, "Undo Stage Hunk") + map("n", "gR", gs.reset_buffer, "Reset Buffer") + map("n", "gp", gs.preview_hunk_inline, "Preview Hunk Inline") + map("n", "gb", function() gs.blame_line({ full = true }) end, "Blame Line") + map("n", "gd", gs.diffthis, "Diff This") + map("n", "gD", function() gs.diffthis("~") end, "Diff This ~") + map('n', 'tb', gs.toggle_current_line_blame, "Toggle current line blame") + map({ "o", "x" }, "ih", ":Gitsigns select_hunk", "GitSigns Select Hunk") + end, + } + ''; + } + { + pkg = + (pkgs.vimUtils.buildVimPlugin { + name = "yazi.nvim"; + src = inputs.vim-yazi; + }); + event = "VeryLazy"; + keys.__raw = /*lua*/ '' + { + { ".", function() require("yazi").yazi() end, desc = "Open file manager" }, + { "", desc = "Decrement Selection", mode = "x" }, + } + ''; + opts.__raw = /*lua*/ '' + { + open_for_directories = true, + } + ''; + } + { + pkg = pkgs.unstable.vimPlugins.fidget-nvim; + opts.__raw = /*lua*/ '' + { + logger = { level = vim.log.levels.WARN }, + notification = { filter = vim.log.levels.INFO }, + } + ''; + } + { + pkg = pkgs.unstable.vimPlugins.indent-blankline-nvim; + } + { + pkg = pkgs.unstable.vimPlugins.vim-sleuth; + } + { + pkg = pkgs.unstable.vimPlugins.comment-nvim; + } + { + pkg = pkgs.vimPlugins.leap-nvim; + config = /*lua*/ '' + function (_, opts) + local leap = require("leap") + for k, v in pairs(opts) do + leap.opts[k] = v + end + leap.add_default_mappings(true) + end + ''; + } + { + pkg = pkgs.vimPlugins.telescope-nvim; + dependencies = [ + pkgs.vimPlugins.plenary-nvim + pkgs.vimPlugins.nvim-web-devicons + pkgs.vimPlugins.telescope-ui-select-nvim + pkgs.vimPlugins.telescope-fzf-native-nvim ]; - mapping = { - "" = "cmp.mapping.confirm({ select = true })"; - "" = "cmp.mapping.complete()"; - "" = "cmp.mapping.scroll_docs(-4)"; - "" = "cmp.mapping.close()"; - "" = "cmp.mapping.scroll_docs(4)"; - "" = "cmp.mapping(cmp.mapping.select_prev_item(), {'i', 's'})"; - "" = "cmp.mapping(cmp.mapping.select_next_item(), {'i', 's'})"; - }; - }; - }; - gitgutter.enable = true; - leap.enable = true; - fidget.enable = true; - neogit = { - enable = true; - package = pkgs.unstable.vimPlugins.neogit; - }; - sleuth.enable = true; - comment.enable = true; - direnv.enable = true; - nix.enable = true; - treesitter = { - enable = true; - indent = true; - incrementalSelection.enable = true; - ignoreInstall = [ "org" ]; - }; - telescope = { - enable = true; - extensions.fzf-native.enable = true; - extensions.ui-select.enable = true; - }; - which-key = { - enable = true; - registrations = { - "b".name = "[B]uffer"; - "c".name = "[C]ode"; - "d".name = "[D]ocument"; - "f".name = "[F]ile"; - "g".name = "[G]it"; - "r".name = "[R]ename"; - "s".name = "[S]earch"; - "w".name = "[W]orkspace"; - "t".name = "[T]oggle"; - "h".name = "Git [H]unk"; - }; - }; - lsp = { - enable = true; - servers = { - ansiblels.enable = true; - bashls.enable = true; - cssls.enable = true; - docker-compose-language-service.enable = true; - dockerls.enable = true; - eslint.enable = true; - nixd.enable = true; - html.enable = true; - lua-ls.enable = true; - marksman.enable = true; - sqls.enable = true; - terraformls.enable = true; - tsserver.enable = true; - jsonls.enable = true; - yamlls.enable = true; - typos-lsp = { - enable = true; - extraOptions.init_options.diagnosticSeverity = "Hint"; - }; - }; - keymaps = { - silent = true; - lspBuf = { - gd = { - action = "definition"; - desc = "Goto Definition"; - }; - gr = { - action = "references"; - desc = "Goto References"; - }; - gD = { - action = "declaration"; - desc = "Goto Declaration"; - }; - gI = { - action = "implementation"; - desc = "Goto Implementation"; - }; - gT = { - action = "type_definition"; - desc = "Type Definition"; - }; - K = { - action = "hover"; - desc = "Hover"; - }; - "cw" = { - action = "workspace_symbol"; - desc = "Workspace Symbol"; - }; - "cr" = { - action = "rename"; - desc = "Rename"; - }; - }; - diagnostic = { - "cd" = { - action = "open_float"; - desc = "Line Diagnostics"; - }; - "[d" = { - action = "goto_next"; - desc = "Next Diagnostic"; - }; - "]d" = { - action = "goto_prev"; - desc = "Previous Diagnostic"; - }; - }; - }; + opts.__raw = /*lua*/ '' + { + defaults = { + layout_config = { + vertical = { width = 0.9 } + }, + }, + } + ''; + } + { + pkg = pkgs.vimPlugins.nvim-treesitter.withAllGrammars; + lazy.__raw = "vim.fn.argc(-1) == 0"; + init = /*lua*/ '' + function(plugin) + -- PERF: add nvim-treesitter queries to the rtp and it's custom query predicates early + -- This is needed because a bunch of plugins no longer `require("nvim-treesitter")`, which + -- no longer trigger the **nvim-treesitter** module to be loaded in time. + -- Luckily, the only things that those plugins need are the custom queries, which we make available + -- during startup. + require("lazy.core.loader").add_to_rtp(plugin) + require("nvim-treesitter.query_predicates") + end + ''; + cmd = [ "TSUpdateSync" "TSUpdate" "TSInstall" ]; + keys.__raw = /*lua*/ '' + { + { "", desc = "Increment Selection" }, + { "", desc = "Decrement Selection", mode = "x" }, + } + ''; + opts.__raw = /*lua*/ '' + { + highlight = { enable = true }, + indent = { enable = true }, + ensure_installed = "all", + parser_install_dir = vim.fs.joinpath(vim.fn.stdpath('data'), 'site'), + incremental_selection = { + enable = true, + keymaps = { + init_selection = "", + node_incremental = "", + scope_incremental = false, + node_decremental = "", + }, + }, + ignore_install = { "org" }, + } + ''; + config = /*lua*/ '' + function (_, opts) + require("nvim-treesitter.configs").setup(opts) + vim.opt.runtimepath:prepend(vim.fs.joinpath(vim.fn.stdpath('data'), 'site')) + end + ''; + } + { + pkg = pkgs.unstable.vimPlugins.which-key-nvim; + event = "VimEnter"; + config = /*lua*/ '' + function () + require('which-key').setup() + require('which-key').register { + ['b'] = { name = 'Buffer', _ = 'which_key_ignore' }, + ['c'] = { name = 'Code', _ = 'which_key_ignore' }, + ['d'] = { name = 'Document', _ = 'which_key_ignore' }, + ['f'] = { name = 'File', _ = 'which_key_ignore' }, + ['g'] = { name = 'Git', _ = 'which_key_ignore' }, + ['o'] = { name = 'Org', _ = 'which_key_ignore' }, + ['n'] = { name = 'Org-roam', _ = 'which_key_ignore' }, + ['p'] = { name = 'Project', _ = 'which_key_ignore' }, + ['r'] = { name = 'Rename', _ = 'which_key_ignore' }, + ['s'] = { name = 'Search', _ = 'which_key_ignore' }, + ['w'] = { name = 'Window', _ = 'which_key_ignore' }, + ['t'] = { name = 'Toggle', _ = 'which_key_ignore' }, + } + end + ''; + } + { + pkg = pkgs.vimPlugins.nvim-lspconfig; + config = /*lua*/ '' + function () + vim.api.nvim_create_autocmd('LspAttach', { + group = vim.api.nvim_create_augroup('lsp-attach', { clear = true }), + callback = function(event) + local map = function(keys, func, desc) + vim.keymap.set('n', keys, func, { buffer = event.buf, desc = 'LSP: ' .. desc }) + end + + map('gd', require('telescope.builtin').lsp_definitions, 'Go to definition') + map('gr', require('telescope.builtin').lsp_references, 'Go to references') + map('gI', require('telescope.builtin').lsp_implementations, 'Goto implementation') + map('gD', vim.lsp.buf.declaration, 'Go to declaration') + map('gT', require('telescope.builtin').lsp_type_definitions, 'Type definition') + map('cD', require('telescope.builtin').lsp_document_symbols, 'Document symbols') + map('cw', require('telescope.builtin').lsp_dynamic_workspace_symbols, 'Workspace symbols ') + map('cd', vim.diagnostic.open_float, 'Line diagnostics') + map('c,', vim.diagnostic.goto_prev, 'Previous diagnostics') + map('c;', vim.diagnostic.goto_next, 'Next diagnostics') + map('cr', vim.lsp.buf.rename, 'Rename') + map('ca', vim.lsp.buf.code_action, 'Code action') + map('K', vim.lsp.buf.hover, 'Hover Documentation') + end + }) + + local has_cmp, cmp_nvim_lsp = pcall(require, "cmp_nvim_lsp") + local capabilities = vim.tbl_deep_extend( + "force", + {}, + vim.lsp.protocol.make_client_capabilities(), + has_cmp and cmp_nvim_lsp.default_capabilities() or {} + ) + + local function setup(server, server_opts) + local server_opts_with_caps = vim.tbl_deep_extend("force", { + capabilities = vim.deepcopy(capabilities), + }, server_opts) + + require("lspconfig")[server].setup(server_opts_with_caps) + end + + setup("yamlls", {}) + setup("typos_lsp", { + init_options = { diagnosticSeverity = "Hint" } + }) + setup("tsserver", {}) + setup("terraformls", {}) + setup("sqls", {}) + setup("nixd", {}) + setup("marksman", {}) + setup("lua_ls", {}) + setup("jsonls", { cmd = { "${pkgs.vscode-langservers-extracted}/bin/vscode-json-language-server", "--stdio" } }) + setup("html", { cmd = { "${pkgs.vscode-langservers-extracted}/bin/vscode-html-language-server", "--stdio" } }) + setup("eslint", { cmd = { "${pkgs.vscode-langservers-extracted}/bin/vscode-eslint-language-server", "--stdio" } }) + setup("dockerls", { cmd = { "${pkgs.dockerfile-language-server-nodejs}/bin/docker-langserver", "--stdio" } }) + setup("docker_compose_language_service", {}) + setup("cssls", { cmd = { "${pkgs.vscode-langservers-extracted}/bin/vscode-css-language-server", "--stdio" } }) + setup("bashls", {}) + setup("ansiblels", { cmd = { "${pkgs.ansible-language-server}/bin/ansible-language-server", "--stdio" } }) + end + ''; + } + { + pkg = pkgs.unstable.vimPlugins.nvim-metals; + dependencies = [ pkgs.vimPlugins.plenary-nvim ]; + ft = [ "scala" "sbt" ]; + opts.__raw = /*lua*/ '' + function() + local metals_config = require("metals").bare_config() + metals_config.on_attach = function(client, bufnr) + vim.keymap.set( + "n", + "me", + function() require("telescope").extensions.metals.commands() end, + { noremap=true, silent=true, buffer = bufn, desc = "Metals commands"} + ) + vim.keymap.set( + "n", + "co", + "MetalsOrganizeImports", + { noremap=true, silent=true, buffer = bufn, desc = "Organize imports"} + ) + end + metals_config.init_options.statusBarProvider = "off" + metals_config.settings = { + showImplicitArguments = true, + } + return metals_config + end + ''; + config = /*lua*/ '' + function (self, metals_config) + local nvim_metals_group = vim.api.nvim_create_augroup("nvim-metals", { clear = true }) + vim.api.nvim_create_autocmd("FileType", { + pattern = self.ft, + callback = function() + require("metals").initialize_or_attach(metals_config) + end, + group = nvim_metals_group, + }) + end + ''; + } + { + pkg = pkgs.unstable.vimPlugins.nvim-cmp; + event = "InsertEnter"; + dependencies = [ + pkgs.unstable.vimPlugins.cmp-nvim-lsp + pkgs.unstable.vimPlugins.cmp-path + pkgs.unstable.vimPlugins.cmp-buffer + ]; + opts.__raw = /*lua*/ '' + function() + local cmp = require("cmp") + return { + mapping = { + [""] = cmp.mapping.complete(), + [""] = cmp.mapping.scroll_docs(-4), + [""] = cmp.mapping.close(), + [""] = cmp.mapping.scroll_docs(4), + [""] = cmp.mapping.confirm({ select = true }), + [""] = cmp.mapping.confirm({ select = true }), + [""] = cmp.mapping(cmp.mapping.select_prev_item(), { "i", "s" }), + [""] = cmp.mapping(cmp.mapping.select_next_item(), { "i", "s" }), + }, + sources = { + { name = "nvim_lsp" }, + { name = "path" }, + { name = "buffer" }, + { name = "orgmode" }, + }, + } + end + ''; + } + + # Disabled for now as it tries to write org grammar to its own directory in the nix store + # https://github.com/nvim-orgmode/orgmode/blob/95fb795a422f0455e03d13a3f83525f1d00793ad/lua/orgmode/utils/treesitter/install.lua#L9 + # { + # pkg = pkgs.unstable.vimPlugins.orgmode; + # event = "VeryLazy"; + # ft = [ "org" ]; + # config = /*lua*/ '' + # function () + # require('orgmode').setup({ + # org_agend_files = '~/Nextcloud/Org/**/*', + # org_default_notes_file = '~/Nextcloud/Org/refile.org', + # }) + # end + # ''; + # } + # { + # pkg = (pkgs.vimUtils.buildVimPlugin { + # name = "org-roam.nvim"; + # src = inputs.vim-org-roam; + # }); + # dependencies = [ pkgs.unstable.vimPlugins.orgmode ]; + # event = "VeryLazy"; + # ft = [ "org" ]; + # config = /*lua*/ '' + # function () + # require('org-roam').setup({ + # directory = '~/Nextcloud/OrgRoam', + # }) + # end + # ''; + # } + ]; }; }; - extraPlugins = [ - pkgs.unstable.vimPlugins.vim-airline-themes - pkgs.unstable.vimPlugins.nvim-metals - (pkgs.vimUtils.buildVimPlugin { - name = "yazi.nvim"; - src = inputs.vim-yazi; - }) - pkgs.unstable.vimPlugins.orgmode - (pkgs.vimUtils.buildVimPlugin { - name = "org-roam.nvim"; - src = inputs.vim-org-roam; - }) - ]; - extraConfigLua = /*lua*/ - '' - require('yazi').setup({ - opts = { - open_for_directories = true, - }, - }) - - - require('orgmode').setup({ - org_agend_files = '~/Nextcloud/Org/**/*', - org_default_notes_file = '~/Nextcloud/Org/refile.org', - }) - require('org-roam').setup({ - directory = '~/Nextcloud/OrgRoam', - }) - - - local metals_config = require("metals").bare_config() - - metals_config.init_options.statusBarProvider = "off" - - metals_config.settings = { - showImplicitArguments = true, - } - - metals_config.on_attach = function(client, bufnr) - vim.keymap.set( - "n", - "me", - function() require("telescope").extensions.metals.commands() end, - { noremap=true, silent=true, buffer = bufn, desc = "Metals commands"} - ) - vim.keymap.set( - "n", - "co", - "MetalsOrganizeImports", - { noremap=true, silent=true, buffer = bufn, desc = "Organize imports"} - ) - end - - local nvim_metals_group = vim.api.nvim_create_augroup("nvim-metals", { clear = true }) - vim.api.nvim_create_autocmd("FileType", { - pattern = { "scala", "sbt", "java" }, - callback = function() - require("metals").initialize_or_attach(metals_config) - end, - group = nvim_metals_group, - }) - ''; colorschemes.gruvbox = { enable = true; package = pkgs.unstable.vimPlugins.gruvbox-nvim; @@ -481,7 +823,18 @@ in { nixfmt-rfc-style nixpkgs-fmt coursier - nil + + # LSP + yaml-language-server + typos-lsp + nodePackages.typescript-language-server + terraform-ls + sqls + nixd + marksman + lua-language-server + docker-compose-language-service + bash-language-server ]; }; };