diff --git a/configuration.nix b/configuration.nix index 797edba..8f03986 100644 --- a/configuration.nix +++ b/configuration.nix @@ -68,6 +68,7 @@ isMaximal: { lsp.server = "clangd"; }; + scala.enable = isMaximal; rust = { enable = isMaximal; crates.enable = isMaximal; diff --git a/flake.lock b/flake.lock index ad9efef..843acb2 100644 --- a/flake.lock +++ b/flake.lock @@ -1197,6 +1197,22 @@ "type": "github" } }, + "plugin-nvim-metals": { + "flake": false, + "locked": { + "lastModified": 1728295172, + "narHash": "sha256-ja/+MNxZ3H9io9jDwm5rhE6iKNi86a22eCOY75g19O8=", + "owner": "scalameta", + "repo": "nvim-metals", + "rev": "f861db9fda55939797ac1b05238c49b0dcdc3bdb", + "type": "github" + }, + "original": { + "owner": "scalameta", + "repo": "nvim-metals", + "type": "github" + } + }, "plugin-nvim-navbuddy": { "flake": false, "locked": { @@ -1898,6 +1914,7 @@ "plugin-nvim-docs-view": "plugin-nvim-docs-view", "plugin-nvim-lightbulb": "plugin-nvim-lightbulb", "plugin-nvim-lspconfig": "plugin-nvim-lspconfig", + "plugin-nvim-metals": "plugin-nvim-metals", "plugin-nvim-navbuddy": "plugin-nvim-navbuddy", "plugin-nvim-navic": "plugin-nvim-navic", "plugin-nvim-neoclip": "plugin-nvim-neoclip", diff --git a/flake.nix b/flake.nix index 53e9e93..8a9f93c 100644 --- a/flake.nix +++ b/flake.nix @@ -206,6 +206,11 @@ flake = false; }; + plugin-nvim-metals = { + url = "github:scalameta/nvim-metals"; + flake = false; + }; + # Copying/Registers plugin-registers = { url = "github:tversteeg/registers.nvim"; diff --git a/modules/plugins/languages/default.nix b/modules/plugins/languages/default.nix index 28c1fd8..1c2e2ba 100644 --- a/modules/plugins/languages/default.nix +++ b/modules/plugins/languages/default.nix @@ -19,6 +19,7 @@ in { ./python.nix ./r.nix ./rust.nix + ./scala.nix ./sql.nix ./svelte.nix ./tailwind.nix diff --git a/modules/plugins/languages/scala.nix b/modules/plugins/languages/scala.nix new file mode 100644 index 0000000..0175fc3 --- /dev/null +++ b/modules/plugins/languages/scala.nix @@ -0,0 +1,150 @@ +{ + config, + pkgs, + lib, + ... +}: let + inherit (lib.generators) mkLuaInline; + inherit (lib.modules) mkIf mkMerge; + inherit (lib.nvim.binds) mkMappingOption; + inherit (lib.nvim.dag) entryAfter; + inherit (lib.nvim.lua) toLuaObject; + inherit (lib.nvim.lib) luaInline; + inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.options) mkOption mkEnableOption mkPackageOption; + inherit (lib.strings) optionalString; + inherit (lib.types) attrsOf anything bool; + + listCommandsAction = + if config.vim.telescope.enable + then ''require("telescope").extensions.metals.commands()'' + else ''require("metals").commands()''; + + cfg = config.vim.languages.scala; + + usingDap = config.vim.debugger.nvim-dap.enable && cfg.dap.enable; + usingLualine = config.vim.statusline.lualine.enable; +in { + options.vim.languages.scala = { + enable = mkEnableOption "Scala language support"; + + treesitter = { + enable = mkEnableOption "Scala treesitter" // {default = config.vim.languages.enableTreesitter;}; + package = mkGrammarOption pkgs "scala"; + }; + + lsp = { + enable = mkEnableOption "Scala LSP support (metals)" // {default = config.vim.languages.enableLSP;}; + package = mkPackageOption pkgs "metals" { + default = ["metals"]; + }; + + extraMappings = { + listCommands = mkMappingOption "List Metals commands" "lc"; + }; + + extraSettings = mkOption { + type = attrsOf anything; + description = "Extra settings passed to the metals config. Check nvim-metals docs for available options"; + default = { + showImplicitArguments = true; + showImplicitConversionsAndClasses = true; + showInferredType = true; + excludedPackages = [ + "akka.actor.typed.javadsl" + "com.github.swagger.akka.javadsl" + ]; + }; + }; + }; + + dap = { + enable = mkEnableOption "Scala Debug Adapter support (metals)" // {default = config.vim.languages.enableDAP;}; + config = mkOption { + description = "Lua configuration for dap"; + type = luaInline; + default = mkLuaInline '' + dap.configurations.scala = { + { + type = "scala", + request = "launch", + name = "RunOrTest", + metals = { + runType = "runOrTestFile", + --args = { "firstArg", "secondArg", "thirdArg" }, -- here just as an example + }, + }, + { + type = "scala", + request = "launch", + name = "Test Target", + metals = { + runType = "testTarget", + }, + }, + } + ''; + }; + }; + + fixShortmess = mkOption { + type = bool; + description = "Remove the 'F' flag from shortmess to allow messages to be shown. Without doing this, autocommands that deal with filetypes prohibit messages from being shown"; + default = true; + }; + }; + + config = mkIf cfg.enable ( + mkMerge [ + (mkIf cfg.treesitter.enable { + vim.treesitter.enable = true; + vim.treesitter.grammars = [cfg.treesitter.package]; + }) + (mkIf (cfg.lsp.enable || cfg.dap.enable) { + vim = { + startPlugins = ["nvim-metals"]; + pluginRC.nvim-metals = entryAfter ["lsp-setup"] '' + local metals_caps = capabilities -- from lsp-setup + + local attach_metals_keymaps = function(client, bufnr) + attach_keymaps(client, bufnr) -- from lsp-setup + vim.api.nvim_buf_set_keymap(bufnr, 'n', '${cfg.lsp.extraMappings.listCommands}', 'lua ${listCommandsAction}', {noremap=true, silent=true, desc='Show all Metals commands'}) + end + + metals_config = require('metals').bare_config() + ${optionalString usingLualine "metals_config.init_options.statusBarProvider = 'on'"} + + metals_config.capabilities = metals_caps + metals_config.on_attach = function(client, bufnr) + ${optionalString usingDap "require('metals').setup_dap()"} + attach_metals_keymaps(client, bufnr) + end + + metals_config.settings = ${toLuaObject cfg.lsp.extraSettings} + metals_config.settings.metalsBinaryPath = "${cfg.lsp.package}/bin/metals" + + metals_config.handlers["textDocument/publishDiagnostics"] = vim.lsp.with( + vim.lsp.diagnostic.on_publish_diagnostics, { + virtual_text = { + prefix = '⚠', + } + } + ) + + ${optionalString cfg.fixShortmess ''vim.opt_global.shortmess:remove("F")''} + + local lsp_group = vim.api.nvim_create_augroup('lsp', { clear = true }) + + vim.api.nvim_create_autocmd('FileType', { + group = lsp_group, + pattern = {'java', 'scala', 'sbt'}, + callback = function() + require('metals').initialize_or_attach(metals_config) + end, + }) + ''; + }; + }) + ] + ); +}