diff --git a/config/nvim/README.md b/config/nvim/README.md index 3140602..8d3da31 100644 --- a/config/nvim/README.md +++ b/config/nvim/README.md @@ -31,6 +31,8 @@ - 共通 grammar runtime は [modules/neovim.nix](/home/rito528/dotfiles/modules/neovim.nix:1) で `NVIM_TREESITTER_RUNTIME_GLOBAL` として配る - project 固有 grammar runtime は `templates/*/flake.nix` の `shellHook` で `NVIM_TREESITTER_RUNTIME_PROJECT` として配る - Neovim 側は [init.lua](/home/rito528/dotfiles/config/nvim/init.lua:1) で両方を `runtimepath` に追加する +- 言語固有の LSP 実行環境は、必要に応じて対応する `templates/*/flake.nix` の dev shell が供給する +- Scala は `templates/seichi-assist/flake.nix` の dev shell で `metals`, `coursier`, `bloop`, `sbt` を揃え、Neovim 側は `nvim-metals` でそれらを利用する - parser を追加・削除するときは、この判断軸と実際の template の責務が一致しているかを確認する ## 変更時の確認 @@ -38,4 +40,6 @@ - `.nix` を変えたら `nixfmt` を実行する - Home Manager 側の変更は `home-manager build --flake .#testuser` で確認する - template 側の変更は対応する devShell が評価できることを確認する +- Scala の IDE 機能は `seichi-assist` dev shell 内で Neovim を起動した場合だけを保証する +- Metals 初回起動時は build import や build server 接続に少し時間がかかることがある - Treesitter の配布先を変えた場合は、この README と関連ドキュメントも更新する diff --git a/config/nvim/lua/config/lsp.lua b/config/nvim/lua/config/lsp.lua new file mode 100644 index 0000000..f191f7a --- /dev/null +++ b/config/nvim/lua/config/lsp.lua @@ -0,0 +1,35 @@ +local M = {} + +function M.capabilities() + local base = vim.lsp.protocol.make_client_capabilities() + local ok, blink = pcall(require, "blink.cmp") + + if not ok then + return base + end + + return blink.get_lsp_capabilities(base) +end + +function M.on_attach(_, bufnr) + local map = function(mode, lhs, rhs, desc) + vim.keymap.set(mode, lhs, rhs, { + buffer = bufnr, + silent = true, + desc = desc, + }) + end + + map("n", "K", vim.lsp.buf.hover, "LSP Hover") + map("n", "gd", vim.lsp.buf.definition, "LSP Definition") + map("n", "gD", vim.lsp.buf.declaration, "LSP Declaration") + map("n", "gi", vim.lsp.buf.implementation, "LSP Implementation") + map("n", "gr", vim.lsp.buf.references, "LSP References") + map("n", "rn", vim.lsp.buf.rename, "LSP Rename") + map({ "n", "v" }, "ca", vim.lsp.buf.code_action, "LSP Code Action") + map("n", "lf", vim.diagnostic.open_float, "Line Diagnostics") + map("n", "[d", vim.diagnostic.goto_prev, "Previous Diagnostic") + map("n", "]d", vim.diagnostic.goto_next, "Next Diagnostic") +end + +return M diff --git a/config/nvim/lua/plugins/completion.lua b/config/nvim/lua/plugins/completion.lua index 8ed4a4f..75533b1 100644 --- a/config/nvim/lua/plugins/completion.lua +++ b/config/nvim/lua/plugins/completion.lua @@ -13,6 +13,9 @@ return { appearance = { nerd_font_variant = "mono", }, + sources = { + default = { "lsp", "path", "snippets", "buffer" }, + }, completion = { documentation = { auto_show = false, diff --git a/config/nvim/lua/plugins/lsp.lua b/config/nvim/lua/plugins/lsp.lua index f24381c..b225da6 100644 --- a/config/nvim/lua/plugins/lsp.lua +++ b/config/nvim/lua/plugins/lsp.lua @@ -3,10 +3,18 @@ return { "neovim/nvim-lspconfig", event = { "BufReadPre", "BufNewFile" }, config = function() - vim.lsp.config("ts_ls", {}) + local lsp = require("config.lsp") + local base_config = { + capabilities = lsp.capabilities(), + on_attach = lsp.on_attach, + } + + vim.lsp.config("ts_ls", base_config) vim.lsp.enable("ts_ls") vim.lsp.config("eslint", { + capabilities = base_config.capabilities, + on_attach = base_config.on_attach, settings = { eslint = { autoFixOnSave = true, @@ -15,13 +23,13 @@ return { }) vim.lsp.enable("eslint") - vim.lsp.config("jsonls", {}) + vim.lsp.config("jsonls", base_config) vim.lsp.enable("jsonls") - vim.lsp.config("rust_analyzer", {}) + vim.lsp.config("rust_analyzer", base_config) vim.lsp.enable("rust_analyzer") - vim.lsp.config("taplo", {}) + vim.lsp.config("taplo", base_config) vim.lsp.enable("taplo") end, }, diff --git a/config/nvim/lua/plugins/scala.lua b/config/nvim/lua/plugins/scala.lua new file mode 100644 index 0000000..2cef3e5 --- /dev/null +++ b/config/nvim/lua/plugins/scala.lua @@ -0,0 +1,56 @@ +return { + { + "scalameta/nvim-metals", + ft = { "scala", "sbt", "java" }, + dependencies = { + "nvim-lua/plenary.nvim", + "saghen/blink.cmp", + }, + config = function() + if vim.fn.executable("cs") ~= 1 and vim.fn.executable("coursier") ~= 1 then + return + end + + if vim.fn.executable("bloop") ~= 1 then + return + end + + local lsp = require("config.lsp") + local metals = require("metals") + local metals_config = metals.bare_config() + + metals_config.capabilities = lsp.capabilities() + metals_config.init_options = vim.tbl_deep_extend("force", metals_config.init_options or {}, { + statusBarProvider = "on", + }) + metals_config.settings = { + showImplicitArguments = true, + showInferredType = true, + } + metals_config.on_attach = function(client, bufnr) + local map = function(mode, lhs, rhs, desc) + vim.keymap.set(mode, lhs, rhs, { + buffer = bufnr, + silent = true, + desc = desc, + }) + end + + lsp.on_attach(client, bufnr) + map("n", "mi", "MetalsImportBuild", "Metals Import Build") + map("n", "md", "MetalsRunDoctor", "Metals Doctor") + map("n", "mr", "MetalsRestartBuild", "Metals Restart Build Server") + end + + local group = vim.api.nvim_create_augroup("nvim-metals", { clear = true }) + + vim.api.nvim_create_autocmd("FileType", { + group = group, + pattern = { "scala", "sbt", "java" }, + callback = function() + metals.initialize_or_attach(metals_config) + end, + }) + end, + }, +} diff --git a/templates/seichi-assist/flake.nix b/templates/seichi-assist/flake.nix index bf5dae8..d0695ae 100644 --- a/templates/seichi-assist/flake.nix +++ b/templates/seichi-assist/flake.nix @@ -17,6 +17,8 @@ { devShells.${system}.default = pkgs.mkShell { buildInputs = [ + pkgs.bloop + pkgs.coursier pkgs.jdk17 pkgs.sbt pkgs.metals @@ -24,6 +26,7 @@ pkgs.stdenv.cc.cc.lib ]; shellHook = '' + export JAVA_HOME="${pkgs.jdk17}" export NVIM_TREESITTER_RUNTIME_PROJECT="${treesitterRuntime}" export LD_LIBRARY_PATH="${pkgs.stdenv.cc.cc.lib}/lib''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" '';