aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lua/mason-core/optional.lua8
-rw-r--r--lua/mason-lspconfig/api/command.lua141
-rw-r--r--lua/mason-lspconfig/init.lua2
-rw-r--r--lua/mason-lspconfig/mappings/server.lua2
-rw-r--r--lua/mason-schemas/lsp/deno.lua (renamed from lua/mason-schemas/lsp/deno-lsp.lua)0
-rw-r--r--lua/mason/api/command.lua44
-rw-r--r--lua/mason/mappings/language.lua103
-rw-r--r--lua/mason/ui/init.lua11
-rw-r--r--lua/mason/ui/instance.lua7
-rw-r--r--scripts/autogen_metadata.lua20
-rw-r--r--tests/mason-core/managers/go_spec.lua1
-rw-r--r--tests/mason-core/managers/luarocks_spec.lua1
-rw-r--r--tests/mason-core/optional_spec.lua14
13 files changed, 331 insertions, 23 deletions
diff --git a/lua/mason-core/optional.lua b/lua/mason-core/optional.lua
index 10af8ccb..5710ce0c 100644
--- a/lua/mason-core/optional.lua
+++ b/lua/mason-core/optional.lua
@@ -93,6 +93,14 @@ function Optional:if_present(fn)
return self
end
+---@param fn fun(value: any)
+function Optional:if_not_present(fn)
+ if not self:is_present() then
+ fn(self._value)
+ end
+ return self
+end
+
function Optional:is_present()
return self._value ~= nil
end
diff --git a/lua/mason-lspconfig/api/command.lua b/lua/mason-lspconfig/api/command.lua
new file mode 100644
index 00000000..22a96be5
--- /dev/null
+++ b/lua/mason-lspconfig/api/command.lua
@@ -0,0 +1,141 @@
+local a = require "mason-core.async"
+local Optional = require "mason-core.optional"
+local notify = require "mason-core.notify"
+local _ = require "mason-core.functional"
+
+local M = {}
+
+---@async
+---@param user_args string[] @The arguments, as provided by the user.
+local function parse_packages_from_user_args(user_args)
+ local Package = require "mason-core.package"
+ local server_mapping = require "mason-lspconfig.mappings.server"
+ local language_mapping = require "mason.mappings.language"
+
+ return _.filter_map(function(server_specifier)
+ local server_name, version = Package.Parse(server_specifier)
+ -- 1. first see if the provided arg is an actual lspconfig server name
+ return Optional
+ .of_nilable(server_mapping.lspconfig_to_package[server_name])
+ -- 2. if not, check if it's a language specifier (e.g., "typescript" or "java")
+ :or_(function()
+ return Optional.of_nilable(language_mapping[server_name]):map(function(package_names)
+ local lsp_package_names = _.filter(function(package_name)
+ return server_mapping.package_to_lspconfig[package_name] ~= nil
+ end, package_names)
+
+ if #lsp_package_names == 0 then
+ return nil
+ end
+
+ return a.promisify(vim.ui.select)(lsp_package_names, {
+ prompt = ("Please select which server you want to install for language %q:"):format(
+ server_name
+ ),
+ format_item = function(item)
+ return server_mapping.package_to_lspconfig[item]
+ end,
+ })
+ end)
+ end)
+ :map(function(package_name)
+ return { package = package_name, version = version }
+ end)
+ :if_not_present(function()
+ notify(("Could not find LSP server %q."):format(server_name), vim.log.levels.ERROR)
+ end)
+ end, user_args)
+end
+
+---@async
+local function parse_packages_from_heuristics()
+ local server_mapping = require "mason-lspconfig.mappings.server"
+
+ -- Prompt user which server they want to install (based on the current filetype)
+ local current_ft = vim.api.nvim_buf_get_option(vim.api.nvim_get_current_buf(), "filetype")
+ local filetype_mapping = require "mason-lspconfig.mappings.filetype"
+ return Optional.of_nilable(filetype_mapping[current_ft])
+ :map(function(server_names)
+ return a.promisify(vim.ui.select)(server_names, {
+ prompt = ("Please select which server you want to install for filetype %q:"):format(current_ft),
+ })
+ end)
+ :map(function(server_name)
+ local package_name = server_mapping.lspconfig_to_package[server_name]
+ return { package = package_name, version = nil }
+ end)
+ :or_else_get(function()
+ notify(("No LSP servers found for filetype %q."):format(current_ft), vim.log.levels.ERROR)
+ return {}
+ end)
+end
+
+local parse_packages_to_install = _.cond {
+ { _.compose(_.gt(0), _.length), parse_packages_from_user_args },
+ { _.compose(_.equals(0), _.length), parse_packages_from_heuristics },
+ { _.T, _.always {} },
+}
+
+vim.api.nvim_create_user_command(
+ "LspInstall",
+ a.scope(function(opts)
+ local packages_to_install = parse_packages_to_install(opts.fargs)
+ if #packages_to_install > 0 then
+ local registry = require "mason-registry"
+ _.each(function(target)
+ local pkg = registry.get_package(target.package)
+ pkg:install { version = target.version }
+ end, packages_to_install)
+ require("mason.ui").open()
+ require("mason.ui").set_view "LSP"
+ end
+ end),
+ {
+ desc = "Install one or more LSP servers.",
+ nargs = "*",
+ complete = "custom,v:lua.mason_lspconfig_completion.available_server_completion",
+ }
+)
+
+vim.api.nvim_create_user_command("LspUninstall", function(opts)
+ require("mason.ui").open()
+ require("mason.ui").set_view "LSP"
+ local registry = require "mason-registry"
+ local server_mapping = require "mason-lspconfig.mappings.server"
+ for _, server_specifier in ipairs(opts.fargs) do
+ local package_name = server_mapping.lspconfig_to_package[server_specifier]
+ local pkg = registry.get_package(package_name)
+ pkg:uninstall()
+ end
+end, {
+ desc = "Uninstall one or more LSP servers.",
+ nargs = "+",
+ complete = "custom,v:lua.mason_lspconfig_completion.installed_server_completion",
+})
+
+_G.mason_lspconfig_completion = {
+ available_server_completion = function()
+ local registry = require "mason-registry"
+ local server_mapping = require "mason-lspconfig.mappings.server"
+ local language_mapping = require "mason.mappings.language"
+
+ local package_names = _.filter_map(function(pkg_name)
+ return Optional.of_nilable(server_mapping.package_to_lspconfig[pkg_name])
+ end, registry.get_all_package_names())
+ local completion = _.concat(package_names, _.keys(language_mapping))
+ table.sort(completion)
+ return table.concat(completion, "\n")
+ end,
+ installed_server_completion = function()
+ local registry = require "mason-registry"
+ local server_mapping = require "mason-lspconfig.mappings.server"
+
+ local server_names = _.filter_map(function(pkg_name)
+ return Optional.of_nilable(server_mapping.package_to_lspconfig[pkg_name])
+ end, registry.get_installed_package_names())
+ table.sort(server_names)
+ return table.concat(server_names, "\n")
+ end,
+}
+
+return M
diff --git a/lua/mason-lspconfig/init.lua b/lua/mason-lspconfig/init.lua
index 67f4cd16..8682d2e8 100644
--- a/lua/mason-lspconfig/init.lua
+++ b/lua/mason-lspconfig/init.lua
@@ -103,6 +103,8 @@ function M.setup(config)
setup_lspconfig_hook()
ensure_installed()
+
+ require "mason-lspconfig.api.command"
end
---@param handlers table<string, fun(server_name: string)>
diff --git a/lua/mason-lspconfig/mappings/server.lua b/lua/mason-lspconfig/mappings/server.lua
index 9d784c9b..582963f4 100644
--- a/lua/mason-lspconfig/mappings/server.lua
+++ b/lua/mason-lspconfig/mappings/server.lua
@@ -27,7 +27,7 @@ M.lspconfig_to_package = {
["cssls"] = "css-lsp",
["cssmodules_ls"] = "cucumber-language-server",
["cucumber_language_server"] = "cucumber-language-server",
- ["denols"] = "deno-lsp",
+ ["denols"] = "deno",
["dhall_lsp_server"] = "dhall-lsp",
["diagnosticls"] = "diagnostic-languageserver",
["dockerls"] = "dockerfile-language-server",
diff --git a/lua/mason-schemas/lsp/deno-lsp.lua b/lua/mason-schemas/lsp/deno.lua
index 5ad46765..5ad46765 100644
--- a/lua/mason-schemas/lsp/deno-lsp.lua
+++ b/lua/mason-schemas/lsp/deno.lua
diff --git a/lua/mason/api/command.lua b/lua/mason/api/command.lua
index f3540fd9..064477f4 100644
--- a/lua/mason/api/command.lua
+++ b/lua/mason/api/command.lua
@@ -1,4 +1,5 @@
local notify = require "mason-core.notify"
+local _ = require "mason-core.functional"
local M = {}
@@ -9,18 +10,30 @@ end, {
nargs = 0,
})
+-- This is needed because neovim doesn't do any validation of command args when using custom completion (I think?)
+local filter_valid_packages = _.filter(function(pkg_specifier)
+ local Package = require "mason-core.package"
+ local registry = require "mason-registry"
+ local package_name = Package.Parse(pkg_specifier)
+ local ok = pcall(registry.get_package, package_name)
+ if ok then
+ return true
+ else
+ notify(("%q is not a valid package."):format(pkg_specifier), vim.log.levels.ERROR)
+ return false
+ end
+end)
+
vim.api.nvim_create_user_command("MasonInstall", function(opts)
local Package = require "mason-core.package"
local registry = require "mason-registry"
- for _, package_specifier in ipairs(opts.fargs) do
- ---@type string
- local package_name, version = Package.Parse(package_specifier)
- local ok, pkg = pcall(registry.get_package, package_name)
- if not ok then
- notify(("Cannot find package %q."):format(package_name), vim.log.levels.ERROR)
- return
- end
- local handle = pkg:install { version = version }
+ local valid_packages = filter_valid_packages(opts.fargs)
+ if #valid_packages > 0 then
+ _.each(function(pkg_specifier)
+ local package_name, version = Package.Parse(pkg_specifier)
+ local pkg = registry.get_package(package_name)
+ pkg:install { version = version }
+ end, valid_packages)
require("mason.ui").open()
end
end, {
@@ -31,13 +44,12 @@ end, {
vim.api.nvim_create_user_command("MasonUninstall", function(opts)
local registry = require "mason-registry"
- for _, package_name in ipairs(opts.fargs) do
- local ok, pkg = pcall(registry.get_package, package_name)
- if not ok then
- notify(("Cannot find package %q."):format(package_name), vim.log.levels.ERROR)
- return
- end
- pkg:uninstall()
+ local valid_packages = filter_valid_packages(opts.fargs)
+ if #valid_packages > 0 then
+ _.each(function(package_name)
+ local pkg = registry.get_package(package_name)
+ pkg:uninstall()
+ end, filter_valid_packages)
require("mason.ui").open()
end
end, {
diff --git a/lua/mason/mappings/language.lua b/lua/mason/mappings/language.lua
new file mode 100644
index 00000000..b005d2da
--- /dev/null
+++ b/lua/mason/mappings/language.lua
@@ -0,0 +1,103 @@
+-- THIS FILE IS GENERATED. DO NOT EDIT MANUALLY.
+-- stylua: ignore start
+return {
+ [".net"] = { "netcoredbg" },
+ ["1ะก:enterprise"] = { "bsl-language-server" },
+ angular = { "angular-language-server" },
+ ansible = { "ansible-language-server" },
+ apex = { "apex-language-server" },
+ arduino = { "arduino-language-server" },
+ assembly = { "asm-lsp" },
+ astro = { "astro-language-server" },
+ awk = { "awk-language-server" },
+ bash = { "bash-language-server", "shellcheck" },
+ beancount = { "beancount-language-server" },
+ bicep = { "bicep-lsp" },
+ c = { "ccls", "clangd", "codelldb", "cpptools" },
+ ["c#"] = { "csharp-language-server", "netcoredbg", "omnisharp-roslyn" },
+ ["c++"] = { "ccls", "clangd", "codelldb", "cpptools" },
+ clarity = { "clarity-lsp" },
+ clojure = { "clojure-lsp" },
+ clojurescript = { "clojure-lsp" },
+ cmake = { "cmake-language-server" },
+ codeql = { "codeql" },
+ crystal = { "crystalline" },
+ css = { "css-lsp", "cssmodules-language-server", "tailwindcss-language-server" },
+ cucumber = { "cucumber-language-server" },
+ d = { "serve-d" },
+ dhall = { "dhall-lsp" },
+ dockerfile = { "dockerfile-language-server" },
+ dot = { "dot-language-server" },
+ elixir = { "elixir-ls" },
+ elm = { "elm-format", "elm-language-server" },
+ ember = { "ember-language-server" },
+ emmet = { "emmet-ls" },
+ erlang = { "erlang-ls" },
+ ["f#"] = { "fsautocomplete" },
+ flux = { "flux-lsp" },
+ fortran = { "fortls" },
+ go = { "delve", "go-debug-adapter", "golangci-lint", "golangci-lint-langserver", "gomodifytags", "gopls", "gotests", "impl" },
+ graphql = { "graphql-language-service-cli" },
+ groovy = { "groovy-language-server" },
+ haskell = { "haskell-language-server" },
+ haxe = { "haxe-language-server" },
+ hoon = { "hoon-language-server" },
+ html = { "html-lsp" },
+ java = { "jdtls" },
+ javascript = { "chrome-debug-adapter", "deno", "eslint-lsp", "eslint_d", "firefox-debug-adapter", "node-debug2-adapter", "quick-lint-js", "rome", "typescript-language-server" },
+ json = { "json-lsp" },
+ jsonnet = { "jsonnet-language-server" },
+ julia = { "julia-lsp" },
+ kotlin = { "kotlin-language-server", "ktlint" },
+ latex = { "ltex-ls", "texlab" },
+ lelwel = { "lelwel" },
+ less = { "css-lsp" },
+ liquid = { "shopify-theme-check" },
+ lua = { "lemmy-help", "lua-language-server", "stylua" },
+ markdown = { "grammarly-languageserver", "ltex-ls", "marksman", "prosemd-lsp", "remark-language-server", "zk" },
+ ["metamath zero"] = { "metamath-zero-lsp" },
+ nickel = { "nickel-lang-lsp" },
+ nim = { "nimlsp" },
+ nix = { "rnix-lsp" },
+ ["obective-c"] = { "ccls" },
+ ocaml = { "ocaml-lsp" },
+ onescript = { "bsl-language-server" },
+ opencl = { "opencl-language-server" },
+ openfoam = { "foam-language-server" },
+ perl = { "perlnavigator" },
+ php = { "intelephense", "php-debug-adapter", "phpactor", "psalm" },
+ powershell = { "powershell-editor-services" },
+ prisma = { "prisma-language-server" },
+ puppet = { "puppet-editor-services" },
+ purescript = { "purescript-language-server" },
+ python = { "debugpy", "jedi-language-server", "pyright", "python-lsp-server", "sourcery" },
+ r = { "r-languageserver" },
+ reason = { "reason-language-server" },
+ rescript = { "rescript-lsp" },
+ ["robot framework"] = { "robotframework-lsp" },
+ ruby = { "solargraph", "sorbet" },
+ rust = { "codelldb", "cpptools", "rust-analyzer" },
+ salt = { "salt-lsp" },
+ scss = { "css-lsp" },
+ slint = { "slint-lsp" },
+ solidity = { "solang", "solidity" },
+ sphinx = { "esbonio" },
+ sql = { "sqlls", "sqls" },
+ stylelint = { "stylelint-lsp" },
+ svelte = { "svelte-language-server" },
+ systemverilog = { "svlangserver", "svls", "verible" },
+ teal = { "teal-language-server" },
+ terraform = { "terraform-ls", "tflint" },
+ text = { "grammarly-languageserver", "ltex-ls" },
+ toml = { "taplo" },
+ typescript = { "chrome-debug-adapter", "deno", "eslint-lsp", "eslint_d", "firefox-debug-adapter", "node-debug2-adapter", "rome", "typescript-language-server" },
+ v = { "vls" },
+ vala = { "vala-language-server" },
+ vimscript = { "vim-language-server" },
+ visualforce = { "visualforce-language-server" },
+ vue = { "vetur-vls", "vue-language-server" },
+ wgsl = { "wgsl-analyzer" },
+ xml = { "lemminx" },
+ yaml = { "yaml-language-server" },
+ zig = { "zls" }
+} \ No newline at end of file
diff --git a/lua/mason/ui/init.lua b/lua/mason/ui/init.lua
index 56b0b382..2181a555 100644
--- a/lua/mason/ui/init.lua
+++ b/lua/mason/ui/init.lua
@@ -1,11 +1,16 @@
local settings = require "mason.settings"
local M = {}
-M.open = function()
- local window = require "mason.ui.instance"
- window.open {
+function M.open()
+ local api = require "mason.ui.instance"
+ api.window.open {
border = settings.current.ui.border,
}
end
+function M.set_view(view)
+ local api = require "mason.ui.instance"
+ api.set_view(view)
+end
+
return M
diff --git a/lua/mason/ui/instance.lua b/lua/mason/ui/instance.lua
index 95ebe20f..beb73705 100644
--- a/lua/mason/ui/instance.lua
+++ b/lua/mason/ui/instance.lua
@@ -604,4 +604,9 @@ window.init {
highlight_groups = palette.highlight_groups,
}
-return window
+return {
+ window = window,
+ set_view = function(view)
+ set_view { payload = view }
+ end,
+}
diff --git a/scripts/autogen_metadata.lua b/scripts/autogen_metadata.lua
index 29339ce1..736b1bcb 100644
--- a/scripts/autogen_metadata.lua
+++ b/scripts/autogen_metadata.lua
@@ -3,6 +3,7 @@ local Path = require "mason-core.path"
local fetch = require "mason-core.fetch"
local _ = require "mason-core.functional"
local fs = require "mason-core.fs"
+local registry = require "mason-registry"
local lspconfig_server_mapping = require "mason-lspconfig.mappings.server"
---@async
@@ -48,6 +49,24 @@ local function create_lspconfig_filetype_map()
end
---@async
+local function create_language_map()
+ local language_map = {}
+ local sorted_packages = _.sort_by(_.prop "name", registry.get_all_packages())
+ _.each(function(pkg)
+ _.each(function(language)
+ local language_lc = language:lower()
+ language_map[language_lc] = _.append(pkg.name, language_map[language_lc] or {})
+ end, pkg.spec.languages)
+ end, sorted_packages)
+
+ write_file(
+ Path.concat { vim.loop.cwd(), "lua", "mason", "mappings", "language.lua" },
+ "return " .. vim.inspect(language_map),
+ "w"
+ )
+end
+
+---@async
local function create_lsp_setting_schema_files()
local lsp_schemas_path = Path.concat {
vim.loop.cwd(),
@@ -124,6 +143,7 @@ end
a.run_blocking(function()
a.wait_all(_.filter(_.identity, {
create_lspconfig_filetype_map,
+ create_language_map,
not vim.env.SKIP_SCHEMAS and create_lsp_setting_schema_files,
create_package_index,
}))
diff --git a/tests/mason-core/managers/go_spec.lua b/tests/mason-core/managers/go_spec.lua
index ebeb66ad..c1d2258d 100644
--- a/tests/mason-core/managers/go_spec.lua
+++ b/tests/mason-core/managers/go_spec.lua
@@ -105,7 +105,6 @@ gopls: go1.18
"gopls",
cwd = path.package_prefix "dummy",
}
- print(result:err_or_nil())
assert.is_true(result:is_success())
assert.equals("v0.8.1", result:get_or_nil())
diff --git a/tests/mason-core/managers/luarocks_spec.lua b/tests/mason-core/managers/luarocks_spec.lua
index 2139e4a6..749a7319 100644
--- a/tests/mason-core/managers/luarocks_spec.lua
+++ b/tests/mason-core/managers/luarocks_spec.lua
@@ -10,7 +10,6 @@ describe("luarocks manager", function()
local ctx = InstallContextGenerator(handle)
installer.run_installer(ctx, luarocks.package "lua-cjson")
assert.spy(ctx.spawn.luarocks).was_called(1)
- print(vim.inspect(ctx.spawn.luarocks))
assert.spy(ctx.spawn.luarocks).was_called_with {
"install",
"--tree",
diff --git a/tests/mason-core/optional_spec.lua b/tests/mason-core/optional_spec.lua
index e0068b10..4e6b1325 100644
--- a/tests/mason-core/optional_spec.lua
+++ b/tests/mason-core/optional_spec.lua
@@ -61,3 +61,17 @@ describe("Optional.if_present()", function()
assert.spy(present).was_called_with "value"
end)
end)
+
+describe("Optional.if_not_present()", function()
+ it("should not call .if_not_present() if value is not empty", function()
+ local present = spy.new()
+ Optional.of_nilable("value"):if_not_present(present)
+ assert.spy(present).was_not_called()
+ end)
+
+ it("should call .if_not_present() if value is empty", function()
+ local present = spy.new()
+ Optional.empty():if_not_present(present)
+ assert.spy(present).was_called(1)
+ end)
+end)