diff --git a/configuration.nix b/configuration.nix index 424d7173..b3828826 100644 --- a/configuration.nix +++ b/configuration.nix @@ -84,6 +84,7 @@ isMaximal: { ocaml.enable = false; elixir.enable = false; haskell.enable = false; + hcl.enable = false; ruby.enable = false; fsharp.enable = false; diff --git a/lib/languages.nix b/lib/languages.nix index c4074144..f4ab1cca 100644 --- a/lib/languages.nix +++ b/lib/languages.nix @@ -1,7 +1,7 @@ {lib}: let inherit (builtins) isString getAttr; inherit (lib.options) mkOption; - inherit (lib.types) listOf bool str submodule attrsOf anything either nullOr; + inherit (lib.types) listOf bool str submodule attrsOf anything either nullOr oneOf; inherit (lib.nvim.attrsets) mapListToAttrs; inherit (lib.nvim.types) luaInline; in { @@ -62,7 +62,7 @@ in { }; cmd = mkOption { - type = nullOr (listOf str); + type = nullOr (either luaInline (listOf str)); default = null; description = "Command used to start the LSP server"; }; diff --git a/modules/neovim/init/default.nix b/modules/neovim/init/default.nix index 30a481a1..3f195085 100644 --- a/modules/neovim/init/default.nix +++ b/modules/neovim/init/default.nix @@ -8,5 +8,6 @@ ./highlight.nix ./lsp.nix ./spellcheck.nix + ./util.nix ]; } diff --git a/modules/neovim/init/lsp.nix b/modules/neovim/init/lsp.nix index b89c3fde..6c18b98e 100644 --- a/modules/neovim/init/lsp.nix +++ b/modules/neovim/init/lsp.nix @@ -16,6 +16,7 @@ cfg = config.vim.lsp; + # TODO: lspConfigurations filter on enabledServers instead of cfg.servers? lspConfigurations = mapAttrsToList ( name: value: '' diff --git a/modules/neovim/init/util.nix b/modules/neovim/init/util.nix new file mode 100644 index 00000000..cc98d10a --- /dev/null +++ b/modules/neovim/init/util.nix @@ -0,0 +1,178 @@ +{ + config, + lib, + ... +}: let + inherit (builtins) filter; + inherit (lib.modules) mkIf mkMerge; + inherit (lib.nvim.dag) entryBefore; + + cfg = config.vim.lsp; +in { + config = mkMerge [ + (mkIf (cfg.servers != {}) { + vim.luaConfigRC.lsp-util = + entryBefore ["lsp-servers"] + /* + lua + */ + '' + -- Port of nvim-lspconfig util + local util = { path = {} } + + util.default_config = { + log_level = vim.lsp.protocol.MessageType.Warning, + message_level = vim.lsp.protocol.MessageType.Warning, + settings = vim.empty_dict(), + init_options = vim.empty_dict(), + handlers = {}, + autostart = true, + capabilities = vim.lsp.protocol.make_client_capabilities(), + } + + -- global on_setup hook + util.on_setup = nil + + do + local validate = vim.validate + local api = vim.api + local lsp = vim.lsp + local nvim_eleven = vim.fn.has 'nvim-0.11' == 1 + + local iswin = vim.uv.os_uname().version:match 'Windows' + + local function escape_wildcards(path) + return path:gsub('([%[%]%?%*])', '\\%1') + end + + local function is_fs_root(path) + if iswin then + return path:match '^%a:$' + else + return path == '/' + end + end + + local function traverse_parents(path, cb) + path = vim.uv.fs_realpath(path) + local dir = path + -- Just in case our algo is buggy, don't infinite loop. + for _ = 1, 100 do + dir = vim.fs.dirname(dir) + if not dir then + return + end + -- If we can't ascend further, then stop looking. + if cb(dir, path) then + return dir, path + end + if is_fs_root(dir) then + break + end + end + end + + util.root_pattern = function(...) + local patterns = util.tbl_flatten { ... } + return function(startpath) + startpath = util.strip_archive_subpath(startpath) + for _, pattern in ipairs(patterns) do + local match = util.search_ancestors(startpath, function(path) + for _, p in ipairs(vim.fn.glob(table.concat({ escape_wildcards(path), pattern }, '/'), true, true)) do + if vim.uv.fs_stat(p) then + return path + end + end + end) + + if match ~= nil then + return match + end + end + end + end + + util.root_markers_with_field = function(root_files, new_names, field, fname) + local path = vim.fn.fnamemodify(fname, ':h') + local found = vim.fs.find(new_names, { path = path, upward = true }) + + for _, f in ipairs(found or {}) do + -- Match the given `field`. + for line in io.lines(f) do + if line:find(field) then + root_files[#root_files + 1] = vim.fs.basename(f) + break + end + end + end + + return root_files + end + + util.insert_package_json = function(root_files, field, fname) + return util.root_markers_with_field(root_files, { 'package.json', 'package.json5' }, field, fname) + end + + util.strip_archive_subpath = function(path) + -- Matches regex from zip.vim / tar.vim + path = vim.fn.substitute(path, 'zipfile://\\(.\\{-}\\)::[^\\\\].*$', '\\1', ''') + path = vim.fn.substitute(path, 'tarfile:\\(.\\{-}\\)::.*$', '\\1', ''') + return path + end + + util.get_typescript_server_path = function(root_dir) + local project_roots = vim.fs.find('node_modules', { path = root_dir, upward = true, limit = math.huge }) + for _, project_root in ipairs(project_roots) do + local typescript_path = project_root .. '/typescript' + local stat = vim.loop.fs_stat(typescript_path) + if stat and stat.type == 'directory' then + return typescript_path .. '/lib' + end + end + return ''' + end + + util.search_ancestors = function(startpath, func) + if nvim_eleven then + validate('func', func, 'function') + end + if func(startpath) then + return startpath + end + local guard = 100 + for path in vim.fs.parents(startpath) do + -- Prevent infinite recursion if our algorithm breaks + guard = guard - 1 + if guard == 0 then + return + end + + if func(path) then + return path + end + end + end + + util.path.is_descendant = function(root, path) + if not path then + return false + end + + local function cb(dir, _) + return dir == root + end + + local dir, _ = traverse_parents(path, cb) + + return dir == root + end + + util.tbl_flatten = function(t) + --- @diagnostic disable-next-line:deprecated + return nvim_eleven and vim.iter(t):flatten(math.huge):totable() or vim.tbl_flatten(t) + end + end + ''; + }) + ]; +} diff --git a/modules/plugins/languages/asm.nix b/modules/plugins/languages/asm.nix index 9fe58a76..c0887d77 100644 --- a/modules/plugins/languages/asm.nix +++ b/modules/plugins/languages/asm.nix @@ -4,12 +4,24 @@ lib, ... }: let + inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.types) package; + inherit (lib.types) package enum listOf; inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.meta) getExe; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.assembly; + defaultServers = ["asm-lsp"]; + servers = { + asm-lsp = { + enable = true; + cmd = [(getExe pkgs.asm-lsp)]; + filetypes = ["asm" "vmasm"]; + root_markers = [".asm-lsp.toml" ".git"]; + }; + }; in { options.vim.languages.assembly = { enable = mkEnableOption "Assembly support"; @@ -20,12 +32,11 @@ in { }; lsp = { - enable = mkEnableOption "Assembly LSP support (asm-lsp)" // {default = config.vim.lsp.enable;}; - - package = mkOption { - type = package; - default = pkgs.asm-lsp; - description = "asm-lsp package"; + enable = mkEnableOption "Assembly LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "Assembly LSP server to use"; }; }; }; @@ -36,14 +47,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.asm-lsp = '' - lspconfig.asm_lsp.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = {"${cfg.lsp.package}/bin/asm-lsp"}, - } - ''; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/astro.nix b/modules/plugins/languages/astro.nix index 21b6e5a3..35de328b 100644 --- a/modules/plugins/languages/astro.nix +++ b/modules/plugins/languages/astro.nix @@ -11,25 +11,34 @@ inherit (lib.meta) getExe; inherit (lib.types) enum either listOf package str; inherit (lib.nvim.lua) expToLua; + inherit (lib.nvim.attrsets) mapListToAttrs; inherit (lib.nvim.types) mkGrammarOption diagnostics; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.astro; - defaultServer = "astro"; + defaultServers = ["astro"]; servers = { astro = { - package = pkgs.astro-language-server; - lspConfig = '' - lspconfig.astro.setup { - capabilities = capabilities, - on_attach = attach_keymaps, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/astro-ls", "--stdio"}'' - } - } - ''; + enable = true; + cmd = [(getExe pkgs.astro-language-server) "--stdio"]; + filetypes = ["astro"]; + root_markers = ["package.json" "tsconfig.json" "jsconfig.json" ".git"]; + init_options = { + typescript = {}; + }; + before_init = + mkLuaInline + /* + lua + */ + '' + function(_, config) + if config.init_options and config.init_options.typescript and not config.init_options.typescript.tsdk then + config.init_options.typescript.tsdk = util.get_typescript_server_path(config.root_dir) + end + end + ''; }; }; @@ -81,34 +90,26 @@ in { lsp = { enable = mkEnableOption "Astro LSP support" // {default = config.vim.lsp.enable;}; - - server = mkOption { - type = enum (attrNames servers); - default = defaultServer; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = "Astro LSP server to use"; }; - - package = mkOption { - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; - example = ''[lib.getExe pkgs.astro-language-server "--minify" "--stdio"]''; - description = "Astro LSP server package, or the command to run as a list of strings"; - }; }; format = { enable = mkEnableOption "Astro formatting" // {default = config.vim.languages.enableFormat;}; type = mkOption { - description = "Astro formatter to use"; type = enum (attrNames formats); default = defaultFormat; + description = "Astro formatter to use"; }; package = mkOption { - description = "Astro formatter package"; type = package; default = formats.${cfg.format.type}.package; + description = "Astro formatter package"; }; }; @@ -130,8 +131,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.astro-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { diff --git a/modules/plugins/languages/bash.nix b/modules/plugins/languages/bash.nix index 9a5e3e13..e34edccb 100644 --- a/modules/plugins/languages/bash.nix +++ b/modules/plugins/languages/bash.nix @@ -10,26 +10,25 @@ inherit (lib.modules) mkIf mkMerge; inherit (lib.lists) isList; inherit (lib.types) enum either package listOf str bool; + inherit (lib.generators) mkLuaInline; inherit (lib.nvim.types) diagnostics mkGrammarOption; inherit (lib.nvim.lua) expToLua; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.bash; - defaultServer = "bash-ls"; + defaultServers = ["bash-ls"]; servers = { bash-ls = { - package = pkgs.bash-language-server; - lspConfig = '' - lspconfig.bashls.setup{ - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/bash-language-server", "start"}'' + enable = true; + cmd = [(getExe pkgs.bash-language-server) "start"]; + filetypes = ["bash" "sh"]; + root_markers = [".git"]; + settings = { + basheIde = { + globPattern = mkLuaInline "vim.env.GLOB_PATTERN or '*@(.sh|.inc|.bash|.command)'"; }; - } - ''; + }; }; }; @@ -56,38 +55,30 @@ in { }; lsp = { - enable = mkEnableOption "Enable Bash LSP support" // {default = config.vim.lsp.enable;}; - - server = mkOption { + enable = mkEnableOption "Bash LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = "Bash LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "bash-language-server package, or the command to run as a list of strings"; - example = literalExpression ''[lib.getExe pkgs.bash-language-server "start"]''; - type = either package (listOf str); - default = pkgs.bash-language-server; }; }; format = { enable = mkOption { - description = "Enable Bash formatting"; type = bool; default = config.vim.languages.enableFormat; + description = "Enable Bash formatting"; }; type = mkOption { - description = "Bash formatter to use"; type = enum (attrNames formats); default = defaultFormat; + description = "Bash formatter to use"; }; package = mkOption { - description = "Bash formatter package"; type = package; default = formats.${cfg.format.type}.package; + description = "Bash formatter package"; }; }; @@ -108,8 +99,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.bash-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { diff --git a/modules/plugins/languages/clojure.nix b/modules/plugins/languages/clojure.nix index 0b932708..442df2b7 100644 --- a/modules/plugins/languages/clojure.nix +++ b/modules/plugins/languages/clojure.nix @@ -4,15 +4,27 @@ lib, ... }: let + inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; inherit (lib.meta) getExe; inherit (lib.lists) isList; - inherit (lib.types) either listOf package str; + inherit (lib.types) enum either listOf package str; inherit (lib.nvim.types) mkGrammarOption; inherit (lib.nvim.lua) expToLua; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.clojure; + + defaultServers = ["clojure-lsp"]; + servers = { + clojure-lsp = { + enable = true; + cmd = [(getExe pkgs.clojure-lsp)]; + filetypes = ["clojure" "edn"]; + root_markers = ["project.clj" "deps.edn" "build.boot" "shadow-cljs.edn" ".git" "bb.edn"]; + }; + }; in { options.vim.languages.clojure = { enable = mkEnableOption "Clojure language support"; @@ -24,28 +36,22 @@ in { lsp = { enable = mkEnableOption "Clojure LSP support" // {default = config.vim.lsp.enable;}; - package = mkOption { - type = either package (listOf str); - default = pkgs.clojure-lsp; - description = "Clojure LSP"; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "Clojure LSP server to use"; }; }; }; config = mkIf cfg.enable (mkMerge [ (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.clojure-lsp = '' - lspconfig.clojure_lsp.setup { - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${getExe cfg.lsp.package}"}'' - }; - } - ''; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.treesitter.enable { diff --git a/modules/plugins/languages/dart.nix b/modules/plugins/languages/dart.nix index c2a1810d..de267e7c 100644 --- a/modules/plugins/languages/dart.nix +++ b/modules/plugins/languages/dart.nix @@ -6,6 +6,7 @@ }: let inherit (builtins) attrNames; inherit (lib.modules) mkIf mkMerge; + inherit (lib.meta) getExe; inherit (lib.trivial) boolToString; inherit (lib.lists) isList; inherit (lib.options) mkEnableOption mkOption; @@ -14,26 +15,31 @@ inherit (lib.nvim.lua) expToLua; inherit (lib.nvim.types) mkGrammarOption; inherit (lib.nvim.dag) entryAfter; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.dart; ftcfg = cfg.flutter-tools; - defaultServer = "dart"; + defaultServers = ["dart"]; servers = { dart = { - package = pkgs.dart; - lspConfig = '' - lspconfig.dartls.setup{ - capabilities = capabilities; - on_attach=default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/dart", "language-server", "--protocol=lsp"}'' + enable = true; + cmd = [(getExe pkgs.dart) "language-server" "--protocol=lsp"]; + filetypes = ["dart"]; + root_markers = ["pubspec.yaml"]; + init_options = { + onlyAnalyzeProjectsWithOpenFiles = true; + suggestFromUnimportedLibraries = true; + closingLabels = true; + outline = true; + flutterOutline = true; + }; + settings = { + dart = { + completeFunctionCalls = true; + showTodos = true; }; - ${optionalString (cfg.lsp.opts != null) "init_options = ${cfg.lsp.dartOpts}"} - } - ''; + }; }; }; in { @@ -46,23 +52,11 @@ in { }; lsp = { - enable = mkEnableOption "Dart LSP support"; - server = mkOption { - description = "The Dart LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - package = mkOption { - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; - example = ''[lib.getExe pkgs.jdt-language-server "-data" "~/.cache/jdtls/workspace"]''; - description = "Dart LSP server package, or the command to run as a list of strings"; - }; - - opts = mkOption { - type = nullOr str; - default = null; - description = "Options to pass to Dart LSP server"; + enable = mkEnableOption "Dart LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "Dart LSP server to use"; }; }; @@ -131,19 +125,23 @@ in { }; }; - config.vim = mkIf cfg.enable (mkMerge [ + config = mkIf cfg.enable (mkMerge [ (mkIf cfg.treesitter.enable { - treesitter.enable = true; - treesitter.grammars = [cfg.treesitter.package]; + vim.treesitter.enable = true; + vim.treesitter.grammars = [cfg.treesitter.package]; }) (mkIf cfg.lsp.enable { - lsp.lspconfig.enable = true; - lsp.lspconfig.sources.dart-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf ftcfg.enable { - startPlugins = [ + vim.startPlugins = [ ( if ftcfg.enableNoResolvePatch then "flutter-tools-patched" @@ -152,7 +150,7 @@ in { "plenary-nvim" ]; - pluginRC.flutter-tools = entryAfter ["lsp-setup"] '' + vim.pluginRC.flutter-tools = entryAfter ["lsp-servers"] '' require('flutter-tools').setup { ${optionalString (ftcfg.flutterPackage != null) "flutter_path = \"${ftcfg.flutterPackage}/bin/flutter\","} lsp = { diff --git a/modules/plugins/languages/elixir.nix b/modules/plugins/languages/elixir.nix index 241efd8e..5204c96b 100644 --- a/modules/plugins/languages/elixir.nix +++ b/modules/plugins/languages/elixir.nix @@ -8,29 +8,37 @@ inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; inherit (lib.lists) isList; + inherit (lib.meta) getExe; inherit (lib.types) enum either listOf package str; + inherit (lib.generators) mkLuaInline; inherit (lib.nvim.types) mkGrammarOption; inherit (lib.nvim.lua) expToLua; inherit (lib.nvim.dag) entryAnywhere; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.elixir; - defaultServer = "elixirls"; + defaultServers = ["elixirls"]; servers = { elixirls = { - package = pkgs.elixir-ls; - lspConfig = '' - -- elixirls setup - lspconfig.elixirls.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/elixir-ls"}'' - } - } - ''; + enable = true; + cmd = [(getExe pkgs.elixir-ls)]; + filetypes = ["elixir" "eelixir" "heex" "surface"]; + root_dir = + mkLuaInline + /* + lua + */ + '' + function(bufnr, on_dir) + local fname = vim.api.nvim_buf_get_name(bufnr) + local matches = vim.fs.find({ 'mix.exs' }, { upward = true, limit = 2, path = fname }) + local child_or_root_path, maybe_umbrella_path = unpack(matches) + local root_dir = vim.fs.dirname(maybe_umbrella_path or child_or_root_path) + + on_dir(root_dir) + end + ''; }; }; @@ -54,18 +62,10 @@ in { lsp = { enable = mkEnableOption "Elixir LSP support" // {default = config.vim.lsp.enable;}; - - server = mkOption { + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = "Elixir LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "Elixir LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server " - data " " ~/.cache/jdtls/workspace "]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; @@ -73,15 +73,15 @@ in { enable = mkEnableOption "Elixir formatting" // {default = config.vim.languages.enableFormat;}; type = mkOption { - description = "Elixir formatter to use"; type = enum (attrNames formats); default = defaultFormat; + description = "Elixir formatter to use"; }; package = mkOption { - description = "Elixir formatter package"; type = package; default = formats.${cfg.format.type}.package; + description = "Elixir formatter package"; }; }; @@ -97,8 +97,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.elixir-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { diff --git a/modules/plugins/languages/gleam.nix b/modules/plugins/languages/gleam.nix index 272ef51e..26a001eb 100644 --- a/modules/plugins/languages/gleam.nix +++ b/modules/plugins/languages/gleam.nix @@ -8,27 +8,21 @@ inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; inherit (lib.lists) isList; + inherit (lib.meta) getExe; inherit (lib.types) enum either listOf package str; inherit (lib.nvim.lua) expToLua; inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.gleam; - defaultServer = "gleam"; + defaultServers = ["gleam"]; servers = { gleam = { - package = pkgs.gleam; - lspConfig = '' - lspconfig.gleam.setup{ - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/gleam", "lsp"}'' - } - } - ''; + enable = true; + cmd = [(getExe pkgs.gleam) "lsp"]; + filetypes = ["gleam"]; + root_markers = ["gleam.toml" ".git"]; }; }; in { @@ -42,18 +36,11 @@ in { lsp = { enable = mkEnableOption "Gleam LSP support" // {default = config.vim.lsp.enable;}; - - server = mkOption { - type = enum (attrNames servers); - default = defaultServer; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = "Gleam LSP server to use"; }; - - package = mkOption { - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; - description = "Gleam LSP server package, or the command to run as a list of strings"; - }; }; }; @@ -64,8 +51,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.gleam-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/haskell.nix b/modules/plugins/languages/haskell.nix index f50c9f09..476c2979 100644 --- a/modules/plugins/languages/haskell.nix +++ b/modules/plugins/languages/haskell.nix @@ -4,17 +4,67 @@ pkgs, ... }: let - inherit (builtins) isList; - inherit (lib.types) either package listOf str; + inherit (builtins) isList attrNames; + inherit (lib.types) either package enum listOf str; inherit (lib.options) mkEnableOption mkOption; inherit (lib.strings) optionalString; inherit (lib.modules) mkIf mkMerge; inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.dag) entryAfter; + inherit (lib.nvim.dag) entryAfter entryBefore; inherit (lib.nvim.lua) expToLua; + inherit (lib.meta) getExe'; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.attrsets) mapListToAttrs; inherit (pkgs) haskellPackages; + inherit (lib.nvim.lua) toLuaObject; cfg = config.vim.languages.haskell; + + defaultServers = ["hls"]; + servers = { + hls = { + enable = false; + cmd = [(getExe' pkgs.haskellPackages.haskell-language-server "haskell-language-server-wrapper") "--lsp"]; + filetypes = ["haskell" "lhaskell"]; + on_attach = + mkLuaInline + /* + lua + */ + '' + function(client, bufnr) + local ht = require("haskell-tools") + default_on_attach(client, bufnr, ht) + local opts = { noremap = true, silent = true, buffer = bufnr } + vim.keymap.set('n', 'cl', vim.lsp.codelens.run, opts) + vim.keymap.set('n', 'hs', ht.hoogle.hoogle_signature, opts) + vim.keymap.set('n', 'ea', ht.lsp.buf_eval_all, opts) + vim.keymap.set('n', 'rr', ht.repl.toggle, opts) + vim.keymap.set('n', 'rf', function() + ht.repl.toggle(vim.api.nvim_buf_get_name(0)) + end, opts) + vim.keymap.set('n', 'rq', ht.repl.quit, opts) + end + ''; + root_dir = + mkLuaInline + /* + lua + */ + '' + function(bufnr, on_dir) + local fname = vim.api.nvim_buf_get_name(bufnr) + on_dir(util.root_pattern('hie.yaml', 'stack.yaml', 'cabal.project', '*.cabal', 'package.yaml')(fname)) + end + ''; + settings = { + haskell = { + formattingProvider = "ormolu"; + cabalFormattingProvider = "cabalfmt"; + }; + }; + }; + }; in { options.vim.languages.haskell = { enable = mkEnableOption "Haskell support"; @@ -25,21 +75,20 @@ in { }; lsp = { - enable = mkEnableOption "LSP support for Haskell" // {default = config.vim.lsp.enable;}; - package = mkOption { - description = "Haskell LSP package or command to run the Haskell LSP"; - example = ''[ (lib.getExe pkgs.haskellPackages.haskell-language-server) "--debug" ]''; - default = haskellPackages.haskell-language-server; - type = either package (listOf str); + enable = mkEnableOption "Haskell LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "Haskell LSP server to use"; }; }; dap = { enable = mkEnableOption "DAP support for Haskell" // {default = config.vim.languages.enableDAP;}; package = mkOption { - description = "Haskell DAP package or command to run the Haskell DAP"; default = haskellPackages.haskell-debug-adapter; type = either package (listOf str); + description = "Haskell DAP package or command to run the Haskell DAP"; }; }; }; @@ -57,7 +106,7 @@ in { startPlugins = ["haskell-tools-nvim"]; luaConfigRC.haskell-tools-nvim = entryAfter - ["lsp-setup"] + ["lsp-servers"] '' vim.g.haskell_tools = { ${optionalString cfg.lsp.enable '' @@ -67,25 +116,7 @@ in { enable = true, }, }, - hls = { - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/haskell-language-server-wrapper", "--lsp"}'' - }, - on_attach = function(client, bufnr, ht) - default_on_attach(client, bufnr, ht) - local opts = { noremap = true, silent = true, buffer = bufnr } - vim.keymap.set('n', 'cl', vim.lsp.codelens.run, opts) - vim.keymap.set('n', 'hs', ht.hoogle.hoogle_signature, opts) - vim.keymap.set('n', 'ea', ht.lsp.buf_eval_all, opts) - vim.keymap.set('n', 'rr', ht.repl.toggle, opts) - vim.keymap.set('n', 'rf', function() - ht.repl.toggle(vim.api.nvim_buf_get_name(0)) - end, opts) - vim.keymap.set('n', 'rq', ht.repl.quit, opts) - end, - }, + hls = ${toLuaObject servers.hls} ''} ${optionalString cfg.dap.enable '' dap = { diff --git a/modules/plugins/languages/hcl.nix b/modules/plugins/languages/hcl.nix index e702170c..ca00f600 100644 --- a/modules/plugins/languages/hcl.nix +++ b/modules/plugins/languages/hcl.nix @@ -8,22 +8,19 @@ inherit (lib.options) mkEnableOption mkOption; inherit (lib.meta) getExe; inherit (lib.modules) mkIf mkMerge; - inherit (lib.types) package bool enum; + inherit (lib.types) package bool enum listOf; inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.hcl; - defaultServer = "terraform-ls"; + defaultServers = ["terraform-ls"]; servers = { terraform-ls = { - package = pkgs.terraform-ls; - lspConfig = '' - lspconfig.terraformls.setup { - capabilities = capabilities, - on_attach=default_on_attach, - cmd = {"${lib.getExe cfg.lsp.package}", "serve"}, - } - ''; + enable = true; + cmd = [(getExe pkgs.terraform-ls) "serve"]; + filetypes = ["terraform" "terraform-vars"]; + root_markers = [".terraform" ".git"]; }; }; @@ -43,12 +40,11 @@ in { }; lsp = { - enable = mkEnableOption "HCL LSP support (terraform-ls)" // {default = config.vim.lsp.enable;}; - # TODO: (maybe, is it better?) it would be cooler to use vscode-extensions.hashicorp.hcl probably, shouldn't be too hard - package = mkOption { - type = package; - default = servers.${defaultServer}.package; - description = "HCL language server package (terraform-ls)"; + enable = mkEnableOption "HCL LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "HCL LSP server to use"; }; }; @@ -96,10 +92,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources = lib.optionalAttrs (! config.vim.languages.terraform.lsp.enable) { - terraform-ls = servers.${cfg.lsp.server}.lspConfig; - }; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { diff --git a/modules/plugins/languages/helm.nix b/modules/plugins/languages/helm.nix index ffe115c9..f5fe7b64 100644 --- a/modules/plugins/languages/helm.nix +++ b/modules/plugins/languages/helm.nix @@ -4,44 +4,37 @@ lib, ... }: let - inherit (builtins) attrNames; + inherit (builtins) attrNames head; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; + inherit (lib.meta) getExe; + inherit (lib.types) enum listOf; inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.helm; yamlCfg = config.vim.languages.yaml; - helmCmd = - if isList cfg.lsp.package - then cfg.lsp.package - else ["${cfg.lsp.package}/bin/helm_ls" "serve"]; - yamlCmd = - if isList yamlCfg.lsp.package - then builtins.elemAt yamlCfg.lsp.package 0 - else "${yamlCfg.lsp.package}/bin/yaml-language-server"; - - defaultServer = "helm-ls"; + defaultServers = ["helm-ls"]; servers = { helm-ls = { - package = pkgs.helm-ls; - lspConfig = '' - lspconfig.helm_ls.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${expToLua helmCmd}, - settings = { - ['helm-ls'] = { - yamlls = { - path = "${yamlCmd}" - } - } - } - } - ''; + enable = true; + cmd = [(getExe pkgs.helm-ls) "serve"]; + filetypes = ["helm" "yaml.helm-values"]; + root_markers = ["Chart.yaml"]; + capabilities = { + didChangeWatchedFiles = { + dynamicRegistration = true; + }; + }; + settings = { + helm-ls = { + yamlls = { + # TODO: Determine if this is a good enough solution + path = (head yamlCfg.lsp.servers).cmd; + }; + }; + }; }; }; in { @@ -55,17 +48,10 @@ in { lsp = { enable = mkEnableOption "Helm LSP support" // {default = config.vim.lsp.enable;}; - - server = mkOption { + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = "Helm LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "Helm LSP server package"; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; }; @@ -77,8 +63,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.helm-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) { diff --git a/modules/plugins/languages/java.nix b/modules/plugins/languages/java.nix index 2e26feea..82fae184 100644 --- a/modules/plugins/languages/java.nix +++ b/modules/plugins/languages/java.nix @@ -7,12 +7,59 @@ inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; inherit (lib.meta) getExe; - inherit (lib.lists) isList; - inherit (lib.types) either listOf package str; + inherit (builtins) attrNames; + inherit (lib.types) listOf enum; inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.nvim.dag) entryBefore; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.java; + + defaultServers = ["jdtls"]; + servers = { + jdtls = { + enable = true; + cmd = + mkLuaInline + /* + lua + */ + '' + { + '${getExe pkgs.jdt-language-server}', + '-configuration', + get_jdtls_config_dir(), + '-data', + get_jdtls_workspace_dir(), + get_jdtls_jvm_args(), + } + ''; + filetypes = ["java"]; + root_markers = [ + # Multi-module projects + ".git" + "build.gradle" + "build.gradle.kts" + # Single-module projects + "build.xml" # Ant + "pom.xml" # Maven + "settings.gradle" # Gradle + "settings.gradle.kts" # Gradle + ]; + init_options = { + workspace = mkLuaInline "get_jdtls_workspace_dir()"; + jvm_args = {}; + os_config = mkLuaInline "nil"; + }; + handlers = { + "textDocument/codeAction" = mkLuaInline "jdtls_on_textdocument_codeaction"; + "textDocument/rename" = mkLuaInline "jdtls_on_textdocument_rename"; + "workspace/applyEdit" = mkLuaInline "jdtls_on_workspace_applyedit"; + "language/status" = mkLuaInline "vim.schedule_wrap(jdtls_on_language_status)"; + }; + }; + }; in { options.vim.languages.java = { enable = mkEnableOption "Java language support"; @@ -23,30 +70,107 @@ in { }; lsp = { - enable = mkEnableOption "Java LSP support (java-language-server)" // {default = config.vim.lsp.enable;}; - package = mkOption { - description = "java language server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server "-data" "~/.cache/jdtls/workspace"]''; - type = either package (listOf str); - default = pkgs.jdt-language-server; + enable = mkEnableOption "Java LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "Java LSP server to use"; }; }; }; config = mkIf cfg.enable (mkMerge [ (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.jdtls = '' - lspconfig.jdtls.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${getExe cfg.lsp.package}", "-data", vim.fn.stdpath("cache").."/jdtls/workspace"}'' - }, - } - ''; + vim.luaConfigRC.jdtls-util = + entryBefore ["lsp-servers"] + /* + lua + */ + '' + local jdtls_handlers = require 'vim.lsp.handlers' + + local jdtls_env = { + HOME = vim.uv.os_homedir(), + XDG_CACHE_HOME = os.getenv 'XDG_CACHE_HOME', + JDTLS_JVM_ARGS = os.getenv 'JDTLS_JVM_ARGS', + } + + local function get_cache_dir() + return jdtls_env.XDG_CACHE_HOME and jdtls_env.XDG_CACHE_HOME or jdtls_env.HOME .. '/.cache' + end + + local function get_jdtls_cache_dir() + return get_cache_dir() .. '/jdtls' + end + + local function get_jdtls_config_dir() + return get_jdtls_cache_dir() .. '/config' + end + + local function get_jdtls_workspace_dir() + return get_jdtls_cache_dir() .. '/workspace' + end + + local function get_jdtls_jvm_args() + local args = {} + for a in string.gmatch((jdtls_env.JDTLS_JVM_ARGS or '''), '%S+') do + local arg = string.format('--jvm-arg=%s', a) + table.insert(args, arg) + end + return unpack(args) + end + + -- TextDocument version is reported as 0, override with nil so that + -- the client doesn't think the document is newer and refuses to update + -- See: https://github.com/eclipse/eclipse.jdt.ls/issues/1695 + local function jdtls_fix_zero_version(workspace_edit) + if workspace_edit and workspace_edit.documentChanges then + for _, change in pairs(workspace_edit.documentChanges) do + local text_document = change.textDocument + if text_document and text_document.version and text_document.version == 0 then + text_document.version = nil + end + end + end + return workspace_edit + end + + local function jdtls_on_textdocument_codeaction(err, actions, ctx) + for _, action in ipairs(actions) do + -- TODO: (steelsojka) Handle more than one edit? + if action.command == 'java.apply.workspaceEdit' then -- 'action' is Command in java format + action.edit = jdtls_fix_zero_version(action.edit or action.arguments[1]) + elseif type(action.command) == 'table' and action.command.command == 'java.apply.workspaceEdit' then -- 'action' is CodeAction in java format + action.edit = jdtls_fix_zero_version(action.edit or action.command.arguments[1]) + end + end + + jdtls_handlers[ctx.method](err, actions, ctx) + end + + local function jdtls_on_textdocument_rename(err, workspace_edit, ctx) + jdtls_handlers[ctx.method](err, jdtls_fix_zero_version(workspace_edit), ctx) + end + + local function jdtls_on_workspace_applyedit(err, workspace_edit, ctx) + jdtls_handlers[ctx.method](err, jdtls_fix_zero_version(workspace_edit), ctx) + end + + -- Non-standard notification that can be used to display progress + local function jdtls_on_language_status(_, result) + local command = vim.api.nvim_command + command 'echohl ModeMsg' + command(string.format('echo "%s"', result.message)) + command 'echohl None' + end + ''; + + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.treesitter.enable { diff --git a/modules/plugins/languages/julia.nix b/modules/plugins/languages/julia.nix index 8c48b070..d5310613 100644 --- a/modules/plugins/languages/julia.nix +++ b/modules/plugins/languages/julia.nix @@ -4,63 +4,82 @@ config, ... }: let - inherit (builtins) attrNames isList; + inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; - inherit (lib.types) either listOf package str enum bool nullOr; + inherit (lib.types) listOf enum; inherit (lib.modules) mkIf mkMerge; - inherit (lib.strings) optionalString; + inherit (lib.meta) getExe; inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.nvim.dag) entryBefore; - defaultServer = "julials"; + defaultServers = ["jdtls"]; servers = { - julials = { - package = pkgs.julia.withPackages ["LanguageServer"]; - internalFormatter = true; - lspConfig = '' - lspconfig.julials.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else '' - { - "${optionalString (cfg.lsp.package != null) "${cfg.lsp.package}/bin/"}julia", - "--startup-file=no", - "--history-file=no", - "--eval", - [[ - using LanguageServer - - depot_path = get(ENV, "JULIA_DEPOT_PATH", "") - project_path = let - dirname(something( - ## 1. Finds an explicitly set project (JULIA_PROJECT) - Base.load_path_expand(( - p = get(ENV, "JULIA_PROJECT", nothing); - p === nothing ? nothing : isempty(p) ? nothing : p - )), - ## 2. Look for a Project.toml file in the current working directory, - ## or parent directories, with $HOME as an upper boundary - Base.current_project(), - ## 3. First entry in the load path - get(Base.load_path(), 1, nothing), - ## 4. Fallback to default global environment, - ## this is more or less unreachable - Base.load_path_expand("@v#.#"), - )) - end - @info "Running language server" VERSION pwd() project_path depot_path - server = LanguageServer.LanguageServerInstance(stdin, stdout, project_path, depot_path) - server.runlinter = true - run(server) - ]] - } - '' - } - } - ''; + jdtls = { + enable = true; + cmd = + mkLuaInline + /* + lua + */ + '' + { + '${getExe (pkgs.julia.withPackages ["LanguageServer"])}', + '--startup-file=no', + '--history-file=no', + '-e', + [[ + # Load LanguageServer.jl: attempt to load from ~/.julia/environments/nvim-lspconfig + # with the regular load path as a fallback + ls_install_path = joinpath( + get(DEPOT_PATH, 1, joinpath(homedir(), ".julia")), + "environments", "nvim-lspconfig" + ) + pushfirst!(LOAD_PATH, ls_install_path) + using LanguageServer + popfirst!(LOAD_PATH) + depot_path = get(ENV, "JULIA_DEPOT_PATH", "") + project_path = let + dirname(something( + ## 1. Finds an explicitly set project (JULIA_PROJECT) + Base.load_path_expand(( + p = get(ENV, "JULIA_PROJECT", nothing); + p === nothing ? nothing : isempty(p) ? nothing : p + )), + ## 2. Look for a Project.toml file in the current working directory, + ## or parent directories, with $HOME as an upper boundary + Base.current_project(), + ## 3. First entry in the load path + get(Base.load_path(), 1, nothing), + ## 4. Fallback to default global environment, + ## this is more or less unreachable + Base.load_path_expand("@v#.#"), + )) + end + @info "Running language server" VERSION pwd() project_path depot_path + server = LanguageServer.LanguageServerInstance(stdin, stdout, project_path, depot_path) + server.runlinter = true + run(server) + ]], + } + ''; + filetypes = ["julia"]; + root_markers = ["Project.toml" "JuliaProject.toml"]; + on_attach = + mkLuaInline + /* + lua + */ + '' + function(_, bufnr) + vim.api.nvim_buf_create_user_command(bufnr, 'LspJuliaActivateEnv', activate_julia_env, { + desc = 'Activate a Julia environment', + nargs = '?', + complete = 'file', + }) + end + ''; }; }; @@ -76,11 +95,12 @@ in { }; lsp = { - enable = mkOption { - type = bool; - default = config.vim.lsp.enable; + enable = mkEnableOption "Java LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = '' - Whether to enable Julia LSP support. + Julia LSP Server to Use ::: {.note} The entirety of Julia is bundled with nvf, if you enable this @@ -93,21 +113,6 @@ in { ::: ''; }; - - server = mkOption { - type = enum (attrNames servers); - default = defaultServer; - description = "Julia LSP server to use"; - }; - - package = mkOption { - description = '' - Julia LSP server package, `null` to use the Julia binary in {env}`PATH`, or - the command to run as a list of strings. - ''; - type = nullOr (either package (listOf str)); - default = servers.${cfg.lsp.server}.package; - }; }; }; }; @@ -119,8 +124,70 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.julia-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.luaConfigRC.julia-util = + entryBefore ["lsp-servers"] + /* + lua + */ + '' + local function activate_julia_env(path) + assert(vim.fn.has 'nvim-0.10' == 1, 'requires Nvim 0.10 or newer') + local bufnr = vim.api.nvim_get_current_buf() + local julials_clients = vim.lsp.get_clients { bufnr = bufnr, name = 'julials' } + assert( + #julials_clients > 0, + 'method julia/activateenvironment is not supported by any servers active on the current buffer' + ) + local function _activate_env(environment) + if environment then + for _, julials_client in ipairs(julials_clients) do + julials_client:notify('julia/activateenvironment', { envPath = environment }) + end + vim.notify('Julia environment activated: \n`' .. environment .. '`', vim.log.levels.INFO) + end + end + if path then + path = vim.fs.normalize(vim.fn.fnamemodify(vim.fn.expand(path), ':p')) + local found_env = false + for _, project_file in ipairs(root_files) do + local file = vim.uv.fs_stat(vim.fs.joinpath(path, project_file)) + if file and file.type then + found_env = true + break + end + end + if not found_env then + vim.notify('Path is not a julia environment: \n`' .. path .. '`', vim.log.levels.WARN) + return + end + _activate_env(path) + else + local depot_paths = vim.env.JULIA_DEPOT_PATH + and vim.split(vim.env.JULIA_DEPOT_PATH, vim.fn.has 'win32' == 1 and ';' or ':') + or { vim.fn.expand '~/.julia' } + local environments = {} + vim.list_extend(environments, vim.fs.find(root_files, { type = 'file', upward = true, limit = math.huge })) + for _, depot_path in ipairs(depot_paths) do + local depot_env = vim.fs.joinpath(vim.fs.normalize(depot_path), 'environments') + vim.list_extend( + environments, + vim.fs.find(function(name, env_path) + return vim.tbl_contains(root_files, name) and string.sub(env_path, #depot_env + 1):match '^/[^/]*$' + end, { path = depot_env, type = 'file', limit = math.huge }) + ) + end + environments = vim.tbl_map(vim.fs.dirname, environments) + vim.ui.select(environments, { prompt = 'Select a Julia environment' }, _activate_env) + end + end + ''; + + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/kotlin.nix b/modules/plugins/languages/kotlin.nix index 1118afdf..f316c4cb 100644 --- a/modules/plugins/languages/kotlin.nix +++ b/modules/plugins/languages/kotlin.nix @@ -4,16 +4,47 @@ lib, ... }: let - inherit (lib.options) mkEnableOption mkOption literalExpression; + inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.meta) getExe; - inherit (lib.types) either package listOf str; + inherit (lib.meta) getExe' getExe; + inherit (builtins) attrNames; + inherit (lib.types) enum listOf; inherit (lib.nvim.types) mkGrammarOption diagnostics; - inherit (lib.lists) isList; - inherit (lib.nvim.lua) expToLua; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.kotlin; + defaultServers = ["kotlin-language-server"]; + servers = { + kotlin-language-server = { + enable = true; + cmd = [(getExe' pkgs.kotlin-language-server "kotlin-language-server")]; + filetypes = ["kotlin"]; + root_markers = [ + "settings.gradle" # Gradle (multi-project) + "settings.gradle.kts" # Gradle (multi-project) + "build.xml" # Ant + "pom.xml" # Maven + "build.gradle" # Gradle + "build.gradle.kts" # gradle + ]; + init_options = { + storagePath = mkLuaInline " + vim.fs.root(vim.fn.expand '%:p:h', + { + 'settings.gradle', -- Gradle (multi-project) + 'settings.gradle.kts', -- Gradle (multi-project) + 'build.xml', -- Ant + 'pom.xml', -- Maven + 'build.gradle', -- Gradle + 'build.gradle.kts', -- Gradle + } + )"; + }; + }; + }; + defaultDiagnosticsProvider = ["ktlint"]; diagnosticsProviders = { ktlint = { @@ -31,22 +62,10 @@ in { lsp = { enable = mkEnableOption "Kotlin LSP support" // {default = config.vim.lsp.enable;}; - - package = mkOption { - description = "kotlin_language_server package with Kotlin runtime"; - type = either package (listOf str); - example = literalExpression '' - pkgs.symlinkJoin { - name = "kotlin-language-server-wrapped"; - paths = [pkgs.kotlin-language-server]; - nativeBuildInputs = [pkgs.makeBinaryWrapper]; - postBuild = ''' - wrapProgram $out/bin/kotlin-language-server \ - --prefix PATH : ''${pkgs.kotlin}/bin - '''; - }; - ''; - default = pkgs.kotlin-language-server; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "Kotlin LSP server to use"; }; }; @@ -78,23 +97,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.kotlin_language_server = '' - lspconfig.kotlin_language_server.setup { - capabilities = capabilities, - root_dir = lspconfig.util.root_pattern("main.kt", ".git"), - on_attach=default_on_attach, - init_options = { - -- speeds up the startup time for the LSP - storagePath = vim.fn.stdpath('state') .. '/kotlin', - }, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/kotlin-language-server"}'' - }, - } - ''; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/lua.nix b/modules/plugins/languages/lua.nix index 9f56cbea..e454eaa8 100644 --- a/modules/plugins/languages/lua.nix +++ b/modules/plugins/languages/lua.nix @@ -8,13 +8,32 @@ inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; inherit (lib.meta) getExe; - inherit (lib.lists) isList; - inherit (lib.types) bool either enum listOf package str; + inherit (lib.types) bool enum listOf package; inherit (lib.nvim.types) diagnostics mkGrammarOption; - inherit (lib.nvim.lua) expToLua; inherit (lib.nvim.dag) entryBefore; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.lua; + + defaultServers = ["lua-language-server"]; + servers = { + lua-language-server = { + enable = true; + cmd = [(getExe pkgs.lua-language-server)]; + filetypes = ["lua"]; + root_markers = [ + ".luarc.json" + ".luarc.jsonc" + ".luacheckrc" + ".stylua.toml" + "stylua.toml" + "selene.toml" + "selene.yml" + ".git" + ]; + }; + }; + defaultFormat = "stylua"; formats = { stylua = { @@ -43,12 +62,11 @@ in { }; lsp = { - enable = mkEnableOption "Lua LSP support via LuaLS" // {default = config.vim.lsp.enable;}; - - package = mkOption { - description = "LuaLS package, or the command to run as a list of strings"; - type = either package (listOf str); - default = pkgs.lua-language-server; + enable = mkEnableOption "Lua LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "Lua LSP server to use"; }; lazydev.enable = mkEnableOption "lazydev.nvim integration, useful for neovim plugin developers"; @@ -91,23 +109,17 @@ in { (mkIf cfg.enable (mkMerge [ (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.lua-lsp = '' - lspconfig.lua_ls.setup { - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${getExe cfg.lsp.package}"}'' - }; - } - ''; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.lsp.lazydev.enable { vim.startPlugins = ["lazydev-nvim"]; - vim.pluginRC.lazydev = entryBefore ["lua-lsp"] '' + vim.pluginRC.lazydev = entryBefore ["lsp-servers"] '' require("lazydev").setup({ enabled = function(root_dir) return not vim.uv.fs_stat(root_dir .. "/.luarc.json") diff --git a/modules/plugins/languages/markdown.nix b/modules/plugins/languages/markdown.nix index 23848835..78e9d67b 100644 --- a/modules/plugins/languages/markdown.nix +++ b/modules/plugins/languages/markdown.nix @@ -8,28 +8,20 @@ inherit (lib.meta) getExe; inherit (lib.modules) mkIf mkMerge; inherit (lib.options) mkEnableOption mkOption; - inherit (lib.lists) isList; - inherit (lib.types) bool enum either package listOf str nullOr; - inherit (lib.nvim.lua) expToLua toLuaObject; + inherit (lib.types) bool enum package listOf str nullOr; + inherit (lib.nvim.lua) toLuaObject; inherit (lib.nvim.types) diagnostics mkGrammarOption mkPluginSetupOption; inherit (lib.nvim.dag) entryAnywhere; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.markdown; - defaultServer = "marksman"; + defaultServers = ["marksman"]; servers = { marksman = { - package = pkgs.marksman; - lspConfig = '' - lspconfig.marksman.setup{ - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/marksman", "server"}'' - }, - } - ''; + enable = true; + cmd = [(getExe pkgs.marksman) "server"]; + filetypes = ["markdown" "markdown.mdx"]; + root_markers = [".marksman.toml" ".git"]; }; }; @@ -67,19 +59,12 @@ in { }; lsp = { - enable = mkEnableOption "Enable Markdown LSP support" // {default = config.vim.lsp.enable;}; + enable = mkEnableOption "Markdown LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { - type = enum (attrNames servers); - default = defaultServer; + servers = mkOption { description = "Markdown LSP server to use"; - }; - - package = mkOption { - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; - example = ''[lib.getExe pkgs.jdt-language-server " - data " " ~/.cache/jdtls/workspace "]''; - description = "Markdown LSP server package, or the command to run as a list of strings"; + type = listOf (enum (attrNames servers)); + default = defaultServers; }; }; @@ -161,8 +146,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.markdown-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { diff --git a/modules/plugins/languages/nim.nix b/modules/plugins/languages/nim.nix index 6e5885e2..289c9691 100644 --- a/modules/plugins/languages/nim.nix +++ b/modules/plugins/languages/nim.nix @@ -6,31 +6,34 @@ }: let inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; + inherit (lib.meta) getExe'; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; + inherit (lib.types) enum listOf package; inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.nim; - defaultServer = "nimlsp"; + defaultServers = ["nimlsp"]; servers = { nimlsp = { - package = pkgs.nimlsp; - lspConfig = '' - lspconfig.nimls.setup{ - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else '' - {"${cfg.lsp.package}/bin/nimlsp"} - '' - }; - } - ''; + enable = true; + cmd = [(getExe' pkgs.nimlsp "nimlsp")]; + filetypes = ["nim"]; + root_dir = + mkLuaInline + /* + lua + */ + '' + function(bufnr, on_dir) + local fname = vim.api.nvim_buf_get_name(bufnr) + on_dir( + util.root_pattern '*.nimble'(fname) or vim.fs.dirname(vim.fs.find('.git', { path = fname, upward = true })[1]) + ) + end + ''; }; }; @@ -54,32 +57,26 @@ in { lsp = { enable = mkEnableOption "Nim LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { - description = "Nim LSP server to use"; - type = str; - default = defaultServer; - }; - package = mkOption { - description = "Nim LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.nimlsp]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "Nim LSP server to use"; }; }; format = { enable = mkEnableOption "Nim formatting" // {default = config.vim.languages.enableFormat;}; type = mkOption { - description = "Nim formatter to use"; type = enum (attrNames formats); default = defaultFormat; + description = "Nim formatter to use"; }; package = mkOption { - description = "Nim formatter package"; type = package; default = formats.${cfg.format.type}.package; + description = "Nim formatter package"; }; }; }; @@ -100,8 +97,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.nim-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { diff --git a/modules/plugins/languages/nix.nix b/modules/plugins/languages/nix.nix index 83022adf..c460ca50 100644 --- a/modules/plugins/languages/nix.nix +++ b/modules/plugins/languages/nix.nix @@ -9,90 +9,43 @@ inherit (lib.meta) getExe; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.strings) optionalString; - inherit (lib.types) anything attrsOf enum either listOf nullOr package str; + inherit (lib.types) enum package listOf; inherit (lib.nvim.types) mkGrammarOption diagnostics; - inherit (lib.nvim.lua) expToLua toLuaObject; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.nix; - useFormat = "on_attach = default_on_attach"; - noFormat = "on_attach = attach_keymaps"; + formattingCmd = mkIf (cfg.format.enable && cfg.lsp.enable) { + formatting = mkMerge [ + (mkIf (cfg.format.type == "alejandra") { + command = ["${cfg.format.package}/bin/alejandra" "--quiet"]; + }) + (mkIf (cfg.format.type == "nixfmt") { + command = ["${cfg.format.package}/bin/nixfmt"]; + }) + ]; + }; - defaultServer = "nil"; - packageToCmd = package: defaultCmd: - if isList package - then expToLua package - else ''{"${package}/bin/${defaultCmd}"}''; + defaultServers = ["nil"]; servers = { nil = { - package = pkgs.nil; - internalFormatter = true; - lspConfig = '' - lspconfig.nil_ls.setup{ - capabilities = capabilities, - ${ - if cfg.format.enable - then useFormat - else noFormat - }, - cmd = ${packageToCmd cfg.lsp.package "nil"}, - ${optionalString cfg.format.enable '' - settings = { - ["nil"] = { - ${optionalString (cfg.format.type == "alejandra") - '' - formatting = { - command = {"${cfg.format.package}/bin/alejandra", "--quiet"}, - }, - ''} - ${optionalString (cfg.format.type == "nixfmt") - '' - formatting = { - command = {"${cfg.format.package}/bin/nixfmt"}, - }, - ''} - }, - }, - ''} - } - ''; + enable = true; + cmd = [(getExe pkgs.nil)]; + settings = { + nil = formattingCmd; + }; + filetypes = ["nix"]; + root_markers = [".git" "flake.nix"]; }; nixd = { - package = pkgs.nixd; - internalFormatter = true; - lspConfig = '' - lspconfig.nixd.setup{ - capabilities = capabilities, - ${ - if cfg.format.enable - then useFormat - else noFormat - }, - cmd = ${packageToCmd cfg.lsp.package "nixd"}, - ${optionalString cfg.format.enable '' - settings = { - nixd = { - ${optionalString (cfg.format.type == "alejandra") - '' - formatting = { - command = {"${cfg.format.package}/bin/alejandra", "--quiet"}, - }, - ''} - ${optionalString (cfg.format.type == "nixfmt") - '' - formatting = { - command = {"${cfg.format.package}/bin/nixfmt"}, - }, - ''} - options = ${toLuaObject cfg.lsp.options}, - }, - }, - ''} - } - ''; + enable = true; + cmd = [(getExe pkgs.nixd)]; + settings = { + nixd = formattingCmd; + }; + filetypes = ["nix"]; + root_markers = [".git" "flake.nix"]; }; }; @@ -144,23 +97,10 @@ in { lsp = { enable = mkEnableOption "Nix LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = "Nix LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "Nix LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server "-data" "~/.cache/jdtls/workspace"]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; - }; - - options = mkOption { - type = nullOr (attrsOf anything); - default = null; - description = "Options to pass to nixd LSP server"; }; }; @@ -201,13 +141,6 @@ in { ${concatStringsSep ", " (attrNames formats)} ''; } - { - assertion = cfg.lsp.server != "rnix"; - message = '' - rnix-lsp has been archived upstream. Please use one of the following available language servers: - ${concatStringsSep ", " (attrNames servers)} - ''; - } ]; } @@ -217,11 +150,15 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.nix-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) - (mkIf (cfg.format.enable && (!cfg.lsp.enable || !servers.${cfg.lsp.server}.internalFormatter)) { + (mkIf (cfg.format.enable && !cfg.lsp.enable) { vim.formatter.conform-nvim = { enable = true; setupOpts.formatters_by_ft.nix = [cfg.format.type]; diff --git a/modules/plugins/languages/nu.nix b/modules/plugins/languages/nu.nix index bd007fc8..55711104 100644 --- a/modules/plugins/languages/nu.nix +++ b/modules/plugins/languages/nu.nix @@ -5,27 +5,30 @@ ... }: let inherit (lib.options) mkEnableOption mkOption; - inherit (lib.types) str either package listOf; + inherit (lib.types) enum listOf; inherit (lib.modules) mkIf mkMerge; - inherit (lib.nvim.lua) expToLua; inherit (lib.nvim.types) mkGrammarOption; - inherit (builtins) isList; + inherit (lib.meta) getExe; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (builtins) attrNames; - defaultServer = "nushell"; + defaultServers = ["nushell"]; servers = { nushell = { - package = pkgs.nushell; - lspConfig = '' - lspconfig.nushell.setup{ - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/nu", "--no-config-file", "--lsp"}'' - } - } - ''; + enable = true; + cmd = [(getExe pkgs.nushell) "--no-config-file" "--lsp"]; + filetypes = ["nu"]; + root_dir = + mkLuaInline + /* + lua + */ + '' + function(bufnr, on_dir) + on_dir(vim.fs.root(bufnr, { '.git' }) or vim.fs.dirname(vim.api.nvim_buf_get_name(bufnr))) + end + ''; }; }; @@ -41,17 +44,11 @@ in { lsp = { enable = mkEnableOption "Nu LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { - type = str; - default = defaultServer; - description = "Nu LSP server to use"; - }; - package = mkOption { - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; - example = ''[(lib.getExe pkgs.nushell) "--lsp"]''; - description = "Nu LSP server package, or the command to run as a list of strings"; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "Nu LSP server to use"; }; }; }; @@ -63,8 +60,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.nu-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/ocaml.nix b/modules/plugins/languages/ocaml.nix index 995ca04d..a8f964e9 100644 --- a/modules/plugins/languages/ocaml.nix +++ b/modules/plugins/languages/ocaml.nix @@ -8,28 +8,50 @@ inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; inherit (lib.meta) getExe; - inherit (lib.lists) isList; - inherit (lib.types) either enum listOf package str; + inherit (lib.types) enum listOf package; inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.ocaml; - defaultServer = "ocaml-lsp"; + defaultServers = ["ocaml-lsp"]; servers = { ocaml-lsp = { - package = pkgs.ocamlPackages.ocaml-lsp; - lspConfig = '' - lspconfig.ocamllsp.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${getExe cfg.lsp.package}"}'' - }; - } - ''; + enable = true; + cmd = [(getExe pkgs.ocamlPackages.ocaml-lsp)]; + filetypes = ["ocaml" "menhir" "ocamlinterface" "ocamllex" "reason" "dune"]; + root_dir = + mkLuaInline + /* + lua + */ + '' + function(bufnr, on_dir) + local fname = vim.api.nvim_buf_get_name(bufnr) + on_dir(util.root_pattern('*.opam', 'esy.json', 'package.json', '.git', 'dune-project', 'dune-workspace')(fname)) + end + ''; + get_language_id = + mkLuaInline + /* + lua + */ + '' + function(_, ftype) + local language_id_of = { + menhir = 'ocaml.menhir', + ocaml = 'ocaml', + ocamlinterface = 'ocaml.interface', + ocamllex = 'ocaml.ocamllex', + reason = 'reason', + dune = 'dune', + } + + return language_id_of[ftype] + + end + ''; }; }; @@ -49,38 +71,38 @@ in { }; lsp = { - enable = mkEnableOption "OCaml LSP support (ocaml-lsp)" // {default = config.vim.lsp.enable;}; - server = mkOption { - description = "OCaml LSP server to user"; - type = enum (attrNames servers); - default = defaultServer; - }; - package = mkOption { - description = "OCaml language server package, or the command to run as a list of strings"; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; + enable = mkEnableOption "OCaml LSP support" // {default = config.vim.lsp.enable;}; + + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "OCaml LSP server to use"; }; }; format = { enable = mkEnableOption "OCaml formatting support (ocamlformat)" // {default = config.vim.languages.enableFormat;}; type = mkOption { - description = "OCaml formatter to use"; type = enum (attrNames formats); default = defaultFormat; + description = "OCaml formatter to use"; }; package = mkOption { - description = "OCaml formatter package"; type = package; default = formats.${cfg.format.type}.package; + description = "OCaml formatter package"; }; }; }; config = mkIf cfg.enable (mkMerge [ (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.ocaml-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.treesitter.enable { diff --git a/modules/plugins/languages/odin.nix b/modules/plugins/languages/odin.nix index 6d20351c..1b1bd751 100644 --- a/modules/plugins/languages/odin.nix +++ b/modules/plugins/languages/odin.nix @@ -7,26 +7,28 @@ inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) either listOf package str enum; - inherit (lib.nvim.lua) expToLua; + inherit (lib.types) listOf enum; + inherit (lib.meta) getExe; inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.attrsets) mapListToAttrs; - defaultServer = "ols"; + defaultServers = ["ols"]; servers = { ols = { - package = pkgs.ols; - lspConfig = '' - lspconfig.ols.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else "{'${cfg.lsp.package}/bin/ols'}" - } - } - ''; + enable = true; + cmd = [(getExe pkgs.ols)]; + filetypes = ["odin"]; + root_dir = + mkLuaInline + /* + lua + */ + '' + function(bufnr, on_dir) + local fname = vim.api.nvim_buf_get_name(bufnr) + on_dir(util.root_pattern('ols.json', '.git', '*.odin')(fname)) + end''; }; }; @@ -43,17 +45,11 @@ in { lsp = { enable = mkEnableOption "Odin LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { - type = enum (attrNames servers); - default = defaultServer; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = "Odin LSP server to use"; }; - - package = mkOption { - description = "Ols package, or the command to run as a list of strings"; - type = either package (listOf str); - default = pkgs.ols; - }; }; }; @@ -64,8 +60,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.odin-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/php.nix b/modules/plugins/languages/php.nix index 61ff0425..9eaae168 100644 --- a/modules/plugins/languages/php.nix +++ b/modules/plugins/languages/php.nix @@ -8,81 +8,60 @@ inherit (lib.options) mkEnableOption mkOption; inherit (lib.meta) getExe; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; + inherit (lib.types) enum listOf; inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.php; - defaultServer = "phpactor"; + defaultServers = ["phpactor"]; servers = { phpactor = { - package = pkgs.phpactor; - lspConfig = '' - lspconfig.phpactor.setup{ - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else '' - { - "${getExe cfg.lsp.package}", - "language-server" - }, - '' - } - } - ''; + enable = true; + cmd = [(getExe pkgs.phpactor) "language-server"]; + filetypes = ["php"]; + root_markers = [".git" "composer.json" ".phpactor.json" ".phpactor.yml"]; + workspace_required = true; }; phan = { - package = pkgs.php81Packages.phan; - lspConfig = '' - lspconfig.phan.setup{ - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else '' - { - "${getExe cfg.lsp.package}", - "-m", - "json", - "--no-color", - "--no-progress-bar", - "-x", - "-u", - "-S", - "--language-server-on-stdin", - "--allow-polyfill-parser" - }, - '' - } - } - ''; + enable = true; + cmd = [ + (getExe pkgs.php81Packages.phan) + "-m" + "json" + "--no-color" + "--no-progress-bar" + "-x" + "-u" + "-S" + "--language-server-on-stdin" + "--allow-polyfill-parser" + ]; + filetypes = ["php"]; + root_dir = + mkLuaInline + /* + lua + */ + '' + function(bufnr, on_dir) + local fname = vim.api.nvim_buf_get_name(bufnr) + local cwd = assert(vim.uv.cwd()) + local root = vim.fs.root(fname, { 'composer.json', '.git' }) + + -- prefer cwd if root is a descendant + on_dir(root and vim.fs.relpath(cwd, root) and cwd) + end + ''; }; intelephense = { - package = pkgs.intelephense; - lspConfig = '' - lspconfig.intelephense.setup{ - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else '' - { - "${getExe cfg.lsp.package}", - "--stdio" - }, - '' - } - } - ''; + enable = true; + cmd = [(getExe pkgs.intelephense) "--stdio"]; + filetypes = ["php"]; + root_markers = ["composer.json" ".git"]; }; }; in { @@ -97,17 +76,10 @@ in { lsp = { enable = mkEnableOption "PHP LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = "PHP LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "PHP LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server " - data " " ~/.cache/jdtls/workspace "]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; }; @@ -117,11 +89,14 @@ in { vim.treesitter.enable = true; vim.treesitter.grammars = [cfg.treesitter.package]; }) + (mkIf cfg.lsp.enable { - vim.lsp.lspconfig = { - enable = true; - sources.php-lsp = servers.${cfg.lsp.server}.lspConfig; - }; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/python.nix b/modules/plugins/languages/python.nix index bec7ec8b..aba4cd7e 100644 --- a/modules/plugins/languages/python.nix +++ b/modules/plugins/languages/python.nix @@ -8,57 +8,114 @@ inherit (lib.options) mkEnableOption mkOption literalExpression; inherit (lib.meta) getExe; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str bool; - inherit (lib.nvim.lua) expToLua; + inherit (lib.types) enum listOf package str bool; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.dag) entryBefore; cfg = config.vim.languages.python; - defaultServer = "basedpyright"; + defaultServers = ["basedpyright"]; servers = { pyright = { - package = pkgs.pyright; - lspConfig = '' - lspconfig.pyright.setup{ - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/pyright-langserver", "--stdio"}'' - } - } + enable = true; + cmd = [(getExe pkgs.pyright) "--stdio"]; + filetypes = ["python"]; + root_markers = [ + "pyproject.toml" + "setup.py" + "setup.cfg" + "requirements.txt" + "Pipfile" + "pyrightconfig.json" + ".git" + ]; + settings = { + python = { + analysis = { + autoSearchPaths = true; + useLibraryCodeForTypes = true; + diagnosticMode = "openFilesOnly"; + }; + }; + }; + on_attach = mkLuaInline '' + function(client, bufnr) + vim.api.nvim_buf_create_user_command(bufnr, 'LspPyrightOrganizeImports', function() + client:exec_cmd({ + command = 'pyright.organizeimports', + arguments = { vim.uri_from_bufnr(bufnr) }, + }) + end, { + desc = 'Organize Imports', + }) + vim.api.nvim_buf_create_user_command(bufnr, 'LspPyrightSetPythonPath', function(opts) + set_python_path('pyright', opts.args) + end, { + desc = 'Reconfigure pyright with the provided python path', + nargs = 1, + complete = 'file', + }) + end ''; }; basedpyright = { - package = pkgs.basedpyright; - lspConfig = '' - lspconfig.basedpyright.setup{ - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/basedpyright-langserver", "--stdio"}'' - } - } + enable = true; + cmd = [(getExe pkgs.basedpyright) "--stdio"]; + filetypes = ["python"]; + root_markers = [ + "pyproject.toml" + "setup.py" + "setup.cfg" + "requirements.txt" + "Pipfile" + "pyrightconfig.json" + ".git" + ]; + settings = { + basedpyright = { + analysis = { + autoSearchPaths = true; + useLibraryCodeForTypes = true; + diagnosticMode = "openFilesOnly"; + }; + }; + }; + on_attach = mkLuaInline '' + function(client, bufnr) + vim.api.nvim_buf_create_user_command(bufnr, 'LspPyrightOrganizeImports', function() + client:exec_cmd({ + command = 'basedpyright.organizeimports', + arguments = { vim.uri_from_bufnr(bufnr) }, + }) + end, { + desc = 'Organize Imports', + }) + + vim.api.nvim_buf_create_user_command(bufnr, 'LspPyrightSetPythonPath', function(opts) + set_python_path('basedpyright', opts.args) + end, { + desc = 'Reconfigure basedpyright with the provided python path', + nargs = 1, + complete = 'file', + }) + end ''; }; python-lsp-server = { - package = pkgs.python3Packages.python-lsp-server; - lspConfig = '' - lspconfig.pylsp.setup{ - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/pylsp"}'' - } - } - ''; + enable = true; + cmd = [(getExe pkgs.python3Packages.python-lsp-server)]; + filetypes = ["python"]; + root_markers = [ + "pyproject.toml" + "setup.py" + "setup.cfg" + "requirements.txt" + "Pipfile" + ".git" + ]; }; }; @@ -171,17 +228,10 @@ in { lsp = { enable = mkEnableOption "Python LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = "Python LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "python LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server "-data" "~/.cache/jdtls/workspace"]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; @@ -189,30 +239,30 @@ in { enable = mkEnableOption "Python formatting" // {default = config.vim.languages.enableFormat;}; type = mkOption { - description = "Python formatter to use"; type = enum (attrNames formats); default = defaultFormat; + description = "Python formatter to use"; }; package = mkOption { - description = "Python formatter package"; type = package; default = formats.${cfg.format.type}.package; + description = "Python formatter package"; }; }; # TODO this implementation is very bare bones, I don't know enough python to implement everything dap = { enable = mkOption { - description = "Enable Python Debug Adapter"; type = bool; default = config.vim.languages.enableDAP; + description = "Enable Python Debug Adapter"; }; debugger = mkOption { - description = "Python debugger to use"; type = enum (attrNames debuggers); default = defaultDebugger; + description = "Python debugger to use"; }; package = mkOption { @@ -234,8 +284,34 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.python-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.luaConfigRC.python-util = + entryBefore ["lsp-servers"] + /* + lua + */ + '' + local function set_python_path(server_name, path) + local clients = vim.lsp.get_clients { + bufnr = vim.api.nvim_get_current_buf(), + name = server_name, + } + for _, client in ipairs(clients) do + if client.settings then + client.settings.python = vim.tbl_deep_extend('force', client.settings.python or {}, { pythonPath = path }) + else + client.config.settings = vim.tbl_deep_extend('force', client.config.settings, { python = { pythonPath = path } }) + end + client.notify('workspace/didChangeConfiguration', { settings = nil }) + end + end + ''; + + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { diff --git a/modules/plugins/languages/r.nix b/modules/plugins/languages/r.nix index 894c63f6..9f28900a 100644 --- a/modules/plugins/languages/r.nix +++ b/modules/plugins/languages/r.nix @@ -7,10 +7,11 @@ inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption literalExpression; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; - inherit (lib.nvim.lua) expToLua; + inherit (lib.types) enum listOf package; + inherit (lib.meta) getExe; inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.r; @@ -50,22 +51,16 @@ }; }; - defaultServer = "r_language_server"; + defaultServers = ["r_language_server"]; servers = { r_language_server = { - package = pkgs.writeShellScriptBin "r_lsp" '' - ${r-with-languageserver}/bin/R --slave -e "languageserver::run()" - ''; - lspConfig = '' - lspconfig.r_language_server.setup{ - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${lib.getExe cfg.lsp.package}"}'' - } - } + enable = true; + cmd = [(getExe r-with-languageserver) "--no-echo" "-e" "languageserver::run()"]; + filetypes = ["r" "rmd" "quarto"]; + root_dir = mkLuaInline '' + function(bufnr, on_dir) + on_dir(vim.fs.root(bufnr, '.git') or vim.uv.os_homedir()) + end ''; }; }; @@ -81,17 +76,10 @@ in { lsp = { enable = mkEnableOption "R LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = "R LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "R LSP server package, or the command to run as a list of strings"; - example = literalExpression "[ (lib.getExe pkgs.jdt-language-server) \"-data\" \"~/.cache/jdtls/workspace\" ]"; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; @@ -127,8 +115,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.r-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/ruby.nix b/modules/plugins/languages/ruby.nix index 1f3ce82f..6b2e63da 100644 --- a/modules/plugins/languages/ruby.nix +++ b/modules/plugins/languages/ruby.nix @@ -9,44 +9,41 @@ inherit (lib.meta) getExe; inherit (lib.modules) mkIf mkMerge; inherit (lib.nvim.types) mkGrammarOption diagnostics; - inherit (lib.nvim.lua) expToLua; - inherit (lib.lists) isList; - inherit (lib.types) either listOf package str enum; + inherit (lib.types) listOf package enum; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.ruby; - defaultServer = "rubyserver"; + defaultServers = ["solargraph"]; servers = { - rubyserver = { - package = pkgs.rubyPackages.solargraph; - lspConfig = '' - lspconfig.solargraph.setup { - capabilities = capabilities, - on_attach = attach_keymaps, - flags = { - debounce_text_changes = 150, - }, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{ "${cfg.lsp.package}/bin/solargraph", "stdio" }'' - } - } - ''; + ruby_lsp = { + enable = true; + cmd = [(getExe pkgs.ruby-lsp)]; + filetypes = ["ruby" "eruby"]; + root_markers = ["Gemfile" ".git"]; + init_options = { + formatter = "auto"; + }; }; - rubylsp = { - package = pkgs.ruby-lsp; - lspConfig = '' - lspconfig.ruby_lsp.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{ "${cfg.lsp.package}/bin/ruby-lsp" }'' - } - } - ''; + + solargraph = { + enable = true; + cmd = [(getExe pkgs.rubyPackages.solargraph) "stdio"]; + filetypes = ["ruby"]; + root_markers = ["Gemfile" ".git"]; + settings = { + solargraph = { + diagnostics = true; + }; + }; + + flags = { + debounce_text_changes = 150; + }; + + init_options = { + formatting = true; + }; }; }; @@ -79,17 +76,11 @@ in { lsp = { enable = mkEnableOption "Ruby LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { - type = enum (attrNames servers); - default = defaultServer; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = "Ruby LSP server to use"; }; - - package = mkOption { - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; - description = "Ruby LSP server package, or the command to run as a list of strings"; - }; }; format = { @@ -128,8 +119,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.ruby-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { diff --git a/modules/plugins/languages/sql.nix b/modules/plugins/languages/sql.nix index add46c15..7321dcf5 100644 --- a/modules/plugins/languages/sql.nix +++ b/modules/plugins/languages/sql.nix @@ -8,31 +8,27 @@ inherit (lib.options) mkEnableOption mkOption; inherit (lib.meta) getExe; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; - inherit (lib.nvim.lua) expToLua; + inherit (lib.types) enum listOf package str; inherit (lib.nvim.types) diagnostics; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.sql; sqlfluffDefault = pkgs.sqlfluff; - defaultServer = "sqls"; + defaultServers = ["sqls"]; servers = { sqls = { - package = pkgs.sqls; - lspConfig = '' - lspconfig.sqls.setup { - on_attach = function(client) - client.server_capabilities.execute_command = true - on_attach_keymaps(client, bufnr) - require'sqls'.setup{} - end, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{ "${cfg.lsp.package}/bin/sqls", "-config", string.format("%s/config.yml", vim.fn.getcwd()) }'' - } - } + enable = true; + cmd = [(getExe pkgs.sqls)]; + filetypes = ["sql" "mysql"]; + root_markers = ["config.yml"]; + settings = {}; + on_attach = mkLuaInline '' + function(client, bufnr) + client.server_capabilities.execute_command = true + require'sqls'.setup{} + end ''; }; }; @@ -63,35 +59,28 @@ in { enable = mkEnableOption "SQL language support"; dialect = mkOption { - description = "SQL dialect for sqlfluff (if used)"; type = str; default = "ansi"; + description = "SQL dialect for sqlfluff (if used)"; }; treesitter = { enable = mkEnableOption "SQL treesitter" // {default = config.vim.languages.enableTreesitter;}; package = mkOption { - description = "SQL treesitter grammar to use"; type = package; default = pkgs.vimPlugins.nvim-treesitter.builtGrammars.sql; + description = "SQL treesitter grammar to use"; }; }; lsp = { enable = mkEnableOption "SQL LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = "SQL LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "SQL LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server "-data" "~/.cache/jdtls/workspace"]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; @@ -99,15 +88,15 @@ in { enable = mkEnableOption "SQL formatting" // {default = config.vim.languages.enableFormat;}; type = mkOption { - description = "SQL formatter to use"; type = enum (attrNames formats); default = defaultFormat; + description = "SQL formatter to use"; }; package = mkOption { - description = "SQL formatter package"; type = package; default = formats.${cfg.format.type}.package; + description = "SQL formatter package"; }; }; @@ -132,10 +121,12 @@ in { vim = { startPlugins = ["sqls-nvim"]; - lsp.lspconfig = { - enable = true; - sources.sql-lsp = servers.${cfg.lsp.server}.lspConfig; - }; + lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }; }) diff --git a/modules/plugins/languages/svelte.nix b/modules/plugins/languages/svelte.nix index 4cc9ffe9..a876f65d 100644 --- a/modules/plugins/languages/svelte.nix +++ b/modules/plugins/languages/svelte.nix @@ -7,28 +7,39 @@ inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; inherit (lib.meta) getExe; - inherit (lib.types) enum either listOf package str; - inherit (lib.nvim.lua) expToLua; + inherit (lib.types) enum listOf package; inherit (lib.nvim.types) mkGrammarOption diagnostics; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.svelte; - defaultServer = "svelte"; + defaultServers = ["svelte"]; servers = { svelte = { - package = pkgs.svelte-language-server; - lspConfig = '' - lspconfig.svelte.setup { - capabilities = capabilities; - on_attach = attach_keymaps, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/svelteserver", "--stdio"}'' - } - } + enable = true; + cmd = [(getExe pkgs.svelte-language-server) "--stdio"]; + filetypes = ["svelte"]; + root_dir = mkLuaInline '' + function(bufnr, on_dir) + local root_files = { 'package.json', '.git' } + local fname = vim.api.nvim_buf_get_name(bufnr) + -- Svelte LSP only supports file:// schema. https://github.com/sveltejs/language-tools/issues/2777 + if vim.uv.fs_stat(fname) ~= nil then + on_dir(vim.fs.dirname(vim.fs.find(root_files, { path = fname, upward = true })[1])) + end + end + ''; + on_attach = mkLuaInline '' + function(client, bufnr) + vim.api.nvim_buf_create_user_command(bufnr, 'LspMigrateToSvelte5', function() + client:exec_cmd({ + command = 'migrate_to_svelte_5', + arguments = { vim.uri_from_bufnr(bufnr) }, + }) + end, { desc = 'Migrate Component to Svelte 5 Syntax' }) + end ''; }; }; @@ -78,17 +89,10 @@ in { lsp = { enable = mkEnableOption "Svelte LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = "Svelte LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "Svelte LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server "-data" "~/.cache/jdtls/workspace"]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; @@ -96,15 +100,15 @@ in { enable = mkEnableOption "Svelte formatting" // {default = config.vim.languages.enableFormat;}; type = mkOption { - description = "Svelte formatter to use"; type = enum (attrNames formats); default = defaultFormat; + description = "Svelte formatter to use"; }; package = mkOption { - description = "Svelte formatter package"; type = package; default = formats.${cfg.format.type}.package; + description = "Svelte formatter package"; }; }; @@ -126,8 +130,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.svelte-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { diff --git a/modules/plugins/languages/tailwind.nix b/modules/plugins/languages/tailwind.nix index fbe707ba..601f0f88 100644 --- a/modules/plugins/languages/tailwind.nix +++ b/modules/plugins/languages/tailwind.nix @@ -7,26 +7,142 @@ inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; - inherit (lib.nvim.lua) expToLua; + inherit (lib.meta) getExe; + inherit (lib.types) enum listOf; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.tailwind; - defaultServer = "tailwindcss-language-server"; + defaultServers = ["tailwindcss"]; servers = { - tailwindcss-language-server = { - package = pkgs.tailwindcss-language-server; - lspConfig = '' - lspconfig.tailwindcss.setup { - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/tailwindcss-language-server", "--stdio"}'' - } - } + tailwindcss = { + enable = true; + cmd = [(getExe pkgs.tailwindcss-language-server) "--stdio"]; + filetypes = [ + # html + "aspnetcorerazor" + "astro" + "astro-markdown" + "blade" + "clojure" + "django-html" + "htmldjango" + "edge" + "eelixir" + "elixir" + "ejs" + "erb" + "eruby" + "gohtml" + "gohtmltmpl" + "haml" + "handlebars" + "hbs" + "html" + "htmlangular" + "html-eex" + "heex" + "jade" + "leaf" + "liquid" + "markdown" + "mdx" + "mustache" + "njk" + "nunjucks" + "php" + "razor" + "slim" + "twig" + # css + "css" + "less" + "postcss" + "sass" + "scss" + "stylus" + "sugarss" + # js + "javascript" + "javascriptreact" + "reason" + "rescript" + "typescript" + "typescriptreact" + # mixed + "vue" + "svelte" + "templ" + ]; + settings = { + tailwindCSS = { + validate = true; + lint = { + cssConflict = "warning"; + invalidApply = "error"; + invalidScreen = "error"; + invalidVariant = "error"; + invalidConfigPath = "error"; + invalidTailwindDirective = "error"; + recommendedVariantOrder = "warning"; + }; + classAttributes = [ + "class" + "className" + "class:list" + "classList" + "ngClass" + ]; + includeLanguages = { + eelixir = "html-eex"; + elixir = "phoenix-heex"; + eruby = "erb"; + heex = "phoenix-heex"; + htmlangular = "html"; + templ = "html"; + }; + }; + }; + before_init = mkLuaInline '' + function(_, config) + if not config.settings then + config.settings = {} + end + if not config.settings.editor then + config.settings.editor = {} + end + if not config.settings.editor.tabSize then + config.settings.editor.tabSize = vim.lsp.util.get_effective_tabstop() + end + end + ''; + workspace_required = true; + root_dir = mkLuaInline '' + function(bufnr, on_dir) + local util = require 'lspconfig.util' + local root_files = { + -- Generic + 'tailwind.config.js', + 'tailwind.config.cjs', + 'tailwind.config.mjs', + 'tailwind.config.ts', + 'postcss.config.js', + 'postcss.config.cjs', + 'postcss.config.mjs', + 'postcss.config.ts', + -- Django + 'theme/static_src/tailwind.config.js', + 'theme/static_src/tailwind.config.cjs', + 'theme/static_src/tailwind.config.mjs', + 'theme/static_src/tailwind.config.ts', + 'theme/static_src/postcss.config.js', + } + local fname = vim.api.nvim_buf_get_name(bufnr) + root_files = util.insert_package_json(root_files, 'tailwindcss', fname) + root_files = util.root_markers_with_field(root_files, { 'mix.lock', 'Gemfile.lock' }, 'tailwind', fname) + on_dir(vim.fs.dirname(vim.fs.find(root_files, { path = fname, upward = true })[1])) + end ''; }; }; @@ -37,25 +153,22 @@ in { lsp = { enable = mkEnableOption "Tailwindcss LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = "Tailwindcss LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "Tailwindcss LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server " - data " " ~/.cache/jdtls/workspace "]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; }; config = mkIf cfg.enable (mkMerge [ (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.tailwindcss-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/terraform.nix b/modules/plugins/languages/terraform.nix index 095da072..5f3952df 100644 --- a/modules/plugins/languages/terraform.nix +++ b/modules/plugins/languages/terraform.nix @@ -4,12 +4,25 @@ lib, ... }: let + inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.types) package; + inherit (lib.meta) getExe; + inherit (lib.types) enum listOf; inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.terraform; + + defaultServers = ["terraformls"]; + servers = { + terraformls = { + enable = true; + cmd = [(getExe pkgs.terraform-ls) "serve"]; + filetypes = ["terraform" "terraform-vars"]; + root_markers = [".terraform" ".git"]; + }; + }; in { options.vim.languages.terraform = { enable = mkEnableOption "Terraform/HCL support"; @@ -22,10 +35,10 @@ in { lsp = { enable = mkEnableOption "Terraform LSP support (terraform-ls)" // {default = config.vim.lsp.enable;}; - package = mkOption { - description = "terraform-ls package"; - type = package; - default = pkgs.terraform-ls; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "Terraform LSP server to use"; }; }; }; @@ -36,14 +49,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.terraform-ls = '' - lspconfig.terraformls.setup { - capabilities = capabilities, - on_attach=default_on_attach, - cmd = {"${cfg.lsp.package}/bin/terraform-ls", "serve"}, - } - ''; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/typst.nix b/modules/plugins/languages/typst.nix index 08a2252b..2bc4af42 100644 --- a/modules/plugins/languages/typst.nix +++ b/modules/plugins/languages/typst.nix @@ -6,52 +6,87 @@ }: let inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) nullOr enum either attrsOf listOf package str; + inherit (lib.types) nullOr enum attrsOf listOf package str; inherit (lib.attrsets) attrNames; inherit (lib.meta) getExe; - inherit (lib.nvim.lua) expToLua toLuaObject; inherit (lib.nvim.types) mkGrammarOption mkPluginSetupOption; inherit (lib.nvim.dag) entryAnywhere; + inherit (lib.nvim.lua) toLuaObject; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.typst; - defaultServer = "tinymist"; + defaultServers = ["tinymist"]; servers = { - typst-lsp = { - package = pkgs.typst-lsp; - lspConfig = '' - lspconfig.typst_lsp.setup { - capabilities = capabilities, - on_attach = function(client, bufnr) - -- Disable semantic tokens as a workaround for a semantic token error when using non-english characters - client.server_capabilities.semanticTokensProvider = nil - end, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/typst-lsp"}'' - }, - } + typst_lsp = { + enable = true; + cmd = [(getExe pkgs.typst-lsp)]; + filetypes = ["typst"]; + root_markers = [".git"]; + on_attach = mkLuaInline '' + function(client, bufnr) + -- Disable semantic tokens as a workaround for a semantic token error when using non-english characters + client.server_capabilities.semanticTokensProvider = nil + end ''; }; tinymist = { - package = pkgs.tinymist; - lspConfig = '' - lspconfig.tinymist.setup { - capabilities = capabilities, - single_file_support = true, - on_attach = function(client, bufnr) - -- Disable semantic tokens as a workaround for a semantic token error when using non-english characters - client.server_capabilities.semanticTokensProvider = nil - end, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/tinymist"}'' - }, - } + enable = true; + cmd = [(getExe pkgs.tinymist)]; + filetypes = ["typst"]; + root_markers = [".git"]; + on_attach = mkLuaInline '' + function(client, bufnr) + local function create_tinymist_command(command_name, client, bufnr) + local export_type = command_name:match 'tinymist%.export(%w+)' + local info_type = command_name:match 'tinymist%.(%w+)' + if info_type and info_type:match '^get' then + info_type = info_type:gsub('^get', 'Get') + end + local cmd_display = export_type or info_type + local function run_tinymist_command() + local arguments = { vim.api.nvim_buf_get_name(bufnr) } + local title_str = export_type and ('Export ' .. cmd_display) or cmd_display + local function handler(err, res) + if err then + return vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) + end + vim.notify(export_type and res or vim.inspect(res), vim.log.levels.INFO) + end + if vim.fn.has 'nvim-0.11' == 1 then + return client:exec_cmd({ + title = title_str, + command = command_name, + arguments = arguments, + }, { bufnr = bufnr }, handler) + else + return vim.notify('Tinymist commands require Neovim 0.11+', vim.log.levels.WARN) + end + end + local cmd_name = export_type and ('LspTinymistExport' .. cmd_display) or ('LspTinymist' .. cmd_display) + local cmd_desc = export_type and ('Export to ' .. cmd_display) or ('Get ' .. cmd_display) + return run_tinymist_command, cmd_name, cmd_desc + end + + for _, command in ipairs { + 'tinymist.exportSvg', + 'tinymist.exportPng', + 'tinymist.exportPdf', + 'tinymist.exportMarkdown', + 'tinymist.exportText', + 'tinymist.exportQuery', + 'tinymist.exportAnsiHighlight', + 'tinymist.getServerInfo', + 'tinymist.getDocumentTrace', + 'tinymist.getWorkspaceLabels', + 'tinymist.getDocumentMetrics', + } do + local cmd_func, cmd_name, cmd_desc = create_tinymist_command(command, client, bufnr) + vim.api.nvim_buf_create_user_command(bufnr, cmd_name, cmd_func, { nargs = 0, desc = cmd_desc }) + end + end ''; }; }; @@ -78,17 +113,10 @@ in { lsp = { enable = mkEnableOption "Typst LSP support (typst-lsp)" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = "Typst LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "typst-lsp package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server "-data" "~/.cache/jdtls/workspace"]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; @@ -96,15 +124,15 @@ in { enable = mkEnableOption "Typst document formatting" // {default = config.vim.languages.enableFormat;}; type = mkOption { - description = "Typst formatter to use"; type = enum (attrNames formats); default = defaultFormat; + description = "Typst formatter to use"; }; package = mkOption { - description = "Typst formatter package"; type = package; default = formats.${cfg.format.type}.package; + description = "Typst formatter package"; }; }; @@ -131,7 +159,7 @@ in { dependencies_bin = mkOption { type = attrsOf str; default = { - "tinymist" = getExe servers.tinymist.package; + "tinymist" = getExe pkgs.tinymist; "websocat" = getExe pkgs.websocat; }; @@ -169,8 +197,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.typst-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) # Extensions diff --git a/modules/plugins/languages/vala.nix b/modules/plugins/languages/vala.nix index 220926d0..85058e32 100644 --- a/modules/plugins/languages/vala.nix +++ b/modules/plugins/languages/vala.nix @@ -7,36 +7,57 @@ inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; + inherit (lib.meta) getExe; + inherit (lib.types) enum listOf; inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.vala; - defaultServer = "vala_ls"; + defaultServers = ["vala_ls"]; servers = { vala_ls = { - package = pkgs.symlinkJoin { - name = "vala-language-server-wrapper"; - paths = [pkgs.vala-language-server]; - buildInputs = [pkgs.makeBinaryWrapper]; - postBuild = '' - wrapProgram $out/bin/vala-language-server \ - --prefix PATH : ${pkgs.uncrustify}/bin - ''; - }; - internalFormatter = true; - lspConfig = '' - lspconfig.vala_ls.setup { - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/vala-language-server"}'' - }, - } + enable = true; + cmd = [ + (getExe (pkgs.symlinkJoin { + name = "vala-language-server-wrapper"; + paths = [pkgs.vala-language-server]; + buildInputs = [pkgs.makeBinaryWrapper]; + postBuild = '' + wrapProgram $out/bin/vala-language-server \ + --prefix PATH : ${pkgs.uncrustify}/bin + ''; + })) + ]; + filetypes = ["vala" "genie"]; + root_dir = mkLuaInline '' + function(bufnr, on_dir) + local meson_matcher = function(path) + local pattern = 'meson.build' + local f = vim.fn.glob(table.concat({ path, pattern }, '/')) + if f == ''' then + return nil + end + for line in io.lines(f) do + -- skip meson comments + if not line:match '^%s*#.*' then + local str = line:gsub('%s+', ''') + if str ~= ''' then + if str:match '^project%(' then + return path + else + break + end + end + end + end + end + + local fname = vim.api.nvim_buf_get_name(bufnr) + local root = vim.iter(vim.fs.parents(fname)):find(meson_matcher) + on_dir(root or vim.fs.dirname(vim.fs.find('.git', { path = fname, upward = true })[1])) + end ''; }; }; @@ -51,16 +72,10 @@ in { lsp = { enable = mkEnableOption "Vala LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = "Vala LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "Vala LSP server package, or the command to run as a list of strings"; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; }; @@ -72,8 +87,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.vala_ls = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/wgsl.nix b/modules/plugins/languages/wgsl.nix index f374543e..3b278a27 100644 --- a/modules/plugins/languages/wgsl.nix +++ b/modules/plugins/languages/wgsl.nix @@ -5,31 +5,23 @@ ... }: let inherit (builtins) attrNames; - inherit (lib.lists) isList; inherit (lib.modules) mkIf mkMerge; - inherit (lib.nvim.lua) expToLua; inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.options) literalExpression mkEnableOption mkOption; - inherit (lib.types) either enum listOf package str; + inherit (lib.options) mkEnableOption mkOption; + inherit (lib.types) enum listOf; + inherit (lib.meta) getExe; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.wgsl; - defaultServer = "wgsl-analyzer"; + defaultServers = ["wgsl_analyzer"]; servers = { - wgsl-analyzer = { - package = pkgs.wgsl-analyzer; - internalFormatter = true; - lspConfig = '' - lspconfig.wgsl_analyzer.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else "{'${cfg.lsp.package}/bin/wgsl_analyzer'}" - } - } - ''; + wgsl_analyzer = { + enable = true; + cmd = [(getExe pkgs.wgsl-analyzer)]; + filetypes = ["wgsl"]; + root_markers = [".git"]; + settings = {}; }; }; in { @@ -44,18 +36,11 @@ in { lsp = { enable = mkEnableOption "WGSL LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { - type = enum (attrNames servers); - default = defaultServer; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = "WGSL LSP server to use"; }; - - package = mkOption { - description = "wgsl-analyzer package, or the command to run as a list of strings"; - example = literalExpression "[(lib.getExe pkgs.wgsl-analyzer)]"; - type = either package (listOf str); - default = pkgs.wgsl-analyzer; - }; }; }; @@ -68,12 +53,12 @@ in { }) (mkIf cfg.lsp.enable { - vim = { - lsp.lspconfig = { - enable = true; - sources.wgsl_analyzer = servers.${cfg.lsp.server}.lspConfig; - }; - }; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/yaml.nix b/modules/plugins/languages/yaml.nix index a75a921f..de503e9c 100644 --- a/modules/plugins/languages/yaml.nix +++ b/modules/plugins/languages/yaml.nix @@ -7,10 +7,10 @@ inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; + inherit (lib.meta) getExe; + inherit (lib.types) enum listOf; inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.yaml; @@ -25,23 +25,22 @@ end'' else "on_attach = default_on_attach"; - defaultServer = "yaml-language-server"; + defaultServers = ["yaml-language-server"]; servers = { yaml-language-server = { - package = pkgs.yaml-language-server; - lspConfig = '' - - - lspconfig.yamlls.setup { - capabilities = capabilities, - ${onAttach}, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/yaml-language-server", "--stdio"}'' - }, - } - ''; + enable = true; + cmd = [(getExe pkgs.yaml-language-server) "--stdio"]; + filetypes = ["yaml" "yaml.docker-compose" "yaml.gitlab" "yaml.helm-values"]; + root_markers = [".git"]; + on_attach = onAttach; + # -- https://github.com/redhat-developer/vscode-redhat-telemetry#how-to-disable-telemetry-reporting + settings = { + redhat = { + telemetry = { + enabled = false; + }; + }; + }; }; }; in { @@ -55,18 +54,11 @@ in { }; lsp = { - enable = mkEnableOption "YAML LSP support" // {default = config.vim.lsp.enable;}; - - server = mkOption { - type = enum (attrNames servers); - default = defaultServer; - description = "YAML LSP server to use"; - }; - - package = mkOption { - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; - description = "YAML LSP server package"; + enable = mkEnableOption "Yaml LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "Yaml LSP server to use"; }; }; }; @@ -78,8 +70,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.yaml-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/zig.nix b/modules/plugins/languages/zig.nix index 89307eab..db38cc51 100644 --- a/modules/plugins/languages/zig.nix +++ b/modules/plugins/languages/zig.nix @@ -7,29 +7,21 @@ inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge mkDefault; - inherit (lib.lists) isList; - inherit (lib.types) bool either listOf package str enum; - inherit (lib.nvim.lua) expToLua; + inherit (lib.types) bool listOf package enum; inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.meta) getExe; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.zig; - defaultServer = "zls"; + defaultServers = ["zls"]; servers = { zls = { - package = pkgs.zls; - internalFormatter = true; - lspConfig = '' - lspconfig.zls.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else "{'${cfg.lsp.package}/bin/zls'}" - } - } - ''; + enable = true; + cmd = [(getExe pkgs.zls)]; + filetypes = ["zig" "zir"]; + root_markers = ["zls.json" "build.zig" ".git"]; + workspace_required = false; }; }; @@ -74,17 +66,11 @@ in { lsp = { enable = mkEnableOption "Zig LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { - type = enum (attrNames servers); - default = defaultServer; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; description = "Zig LSP server to use"; }; - - package = mkOption { - description = "ZLS package, or the command to run as a list of strings"; - type = either package (listOf str); - default = pkgs.zls; - }; }; dap = { @@ -118,10 +104,12 @@ in { (mkIf cfg.lsp.enable { vim = { - lsp.lspconfig = { - enable = true; - sources.zig-lsp = servers.${cfg.lsp.server}.lspConfig; - }; + lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; # nvf handles autosaving already globals.zig_fmt_autosave = mkDefault 0;