aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Boman <william@redwill.se>2022-05-12 18:02:47 +0200
committerGitHub <noreply@github.com>2022-05-12 18:02:47 +0200
commitf637ef2e6dbfb03cc896143d73749f541ca18e3e (patch)
tree2c15a25a2661d5eb424eace9267642977c58d2e9
parentrun autogen_metadata.lua (diff)
downloadmason-f637ef2e6dbfb03cc896143d73749f541ca18e3e.tar
mason-f637ef2e6dbfb03cc896143d73749f541ca18e3e.tar.gz
mason-f637ef2e6dbfb03cc896143d73749f541ca18e3e.tar.bz2
mason-f637ef2e6dbfb03cc896143d73749f541ca18e3e.tar.lz
mason-f637ef2e6dbfb03cc896143d73749f541ca18e3e.tar.xz
mason-f637ef2e6dbfb03cc896143d73749f541ca18e3e.tar.zst
mason-f637ef2e6dbfb03cc896143d73749f541ca18e3e.zip
feat: patch :LspInfo's "cmd is executable" diagnostic message (#691)
-rw-r--r--ftplugin/lspinfo.lua117
-rw-r--r--lua/nvim-lsp-installer/core/functional.lua25
-rw-r--r--tests/core/functional_spec.lua39
3 files changed, 181 insertions, 0 deletions
diff --git a/ftplugin/lspinfo.lua b/ftplugin/lspinfo.lua
new file mode 100644
index 00000000..59303347
--- /dev/null
+++ b/ftplugin/lspinfo.lua
@@ -0,0 +1,117 @@
+local servers = require "nvim-lsp-installer.servers"
+local functional = require "nvim-lsp-installer.core.functional"
+local log = require "nvim-lsp-installer.log"
+
+local filter, compose = functional.filter, functional.compose
+
+---@class LspInfoClient
+---@field name string
+---@field cmd string[]
+---@field cmd_diagnostics {lineno: integer}|nil
+
+---@param client LspInfoClient
+local function is_client_managed(client)
+ local ok, server = servers.get_server(client.name)
+ return ok and server:is_installed()
+end
+
+---@param client LspInfoClient
+local function client_has_cmd_diagnostics(client)
+ return client.cmd_diagnostics ~= nil
+end
+
+---@param client LspInfoClient
+local function client_has_cmd(client)
+ return client.cmd ~= nil
+end
+
+---@param client LspInfoClient[]
+local function is_cmd_executable(client)
+ local cmd = client.cmd[1]
+ local ok, server = servers.get_server(client.name)
+ if not ok then
+ -- should not really happen
+ return false
+ end
+ local options = server:get_default_options()
+ local path = options.cmd_env and options.cmd_env.PATH
+ if path then
+ local old_path = vim.env.PATH
+ local is_executable = pcall(function()
+ vim.env.PATH = path
+ assert(vim.fn.executable(cmd) == 1)
+ end)
+ vim.env.PATH = old_path
+ return is_executable
+ else
+ return vim.fn.executable(cmd) == 1
+ end
+end
+
+local ok, err = pcall(function()
+ ---@type LspInfoClient[]
+ local clients = {}
+
+ local lines = vim.api.nvim_buf_get_lines(0, 1, -1, false)
+
+ local function parse_line(line)
+ local client_name = line:match "^%s+Client:%s+(.+)%s+%(.*$"
+ if client_name then
+ return "client_header", client_name
+ end
+
+ local config_name = line:match "^%s+Config:%s+(.+)$"
+ if config_name then
+ return "client_header", config_name
+ end
+
+ local cmd_diagnostics = line:match "^%s+cmd is executable:.*$"
+ if cmd_diagnostics then
+ return "cmd_diagnostics"
+ end
+
+ local cmd = line:match "^%s+cmd:%s+(.+)$"
+ if cmd then
+ return "cmd", vim.split(cmd, "%s")
+ end
+ end
+
+ local current_client
+ for lineno, line in ipairs(lines) do
+ local type, value = parse_line(line)
+ if type == "client_header" then
+ current_client = { name = value }
+ table.insert(clients, current_client)
+ elseif type == "cmd_diagnostics" then
+ current_client.cmd_diagnostics = { lineno = lineno }
+ elseif type == "cmd" then
+ current_client.cmd = value
+ end
+ end
+
+ ---@type LspInfoClient[]
+ local executable_clients = compose(
+ filter(is_cmd_executable),
+ filter(client_has_cmd),
+ filter(client_has_cmd_diagnostics),
+ filter(is_client_managed)
+ )(clients)
+
+ local override_cmd_diagnostics = functional.partial(functional.each, function(client)
+ vim.api.nvim_buf_set_lines(
+ 0,
+ client.cmd_diagnostics.lineno,
+ client.cmd_diagnostics.lineno + 1,
+ false,
+ { " cmd is executable: true (checked by nvim-lsp-installer)" }
+ )
+ end)
+
+ vim.api.nvim_buf_set_option(0, "modifiable", true)
+ override_cmd_diagnostics(executable_clients)
+ vim.api.nvim_buf_set_option(0, "modifiable", false)
+end)
+
+if not ok then
+ log.error("Failed to patch :LspInfo window", err)
+end
diff --git a/lua/nvim-lsp-installer/core/functional.lua b/lua/nvim-lsp-installer/core/functional.lua
index 244c3d96..8b549fb6 100644
--- a/lua/nvim-lsp-installer/core/functional.lua
+++ b/lua/nvim-lsp-installer/core/functional.lua
@@ -172,4 +172,29 @@ function functional.partial(fn, ...)
end
end
+function functional.compose(...)
+ local functions = functional.table_pack(...)
+ assert(functions.n > 0, "compose requires at least one function")
+ return function(...)
+ local result = functional.table_pack(...)
+ for i = functions.n, 1, -1 do
+ result = functional.table_pack(functions[i](unpack(result, 1, result.n)))
+ end
+ return unpack(result, 1, result.n)
+ end
+end
+
+---@generic T
+---@param filter_fn fun(item: T): boolean
+---@return fun(list: T[]): T[]
+function functional.filter(filter_fn)
+ return functional.partial(vim.tbl_filter, filter_fn)
+end
+
+function functional.each(fn, list)
+ for k, v in pairs(list) do
+ fn(v, k)
+ end
+end
+
return functional
diff --git a/tests/core/functional_spec.lua b/tests/core/functional_spec.lua
index 8301d47e..734217a2 100644
--- a/tests/core/functional_spec.lua
+++ b/tests/core/functional_spec.lua
@@ -163,4 +163,43 @@ describe("functional", function()
partially_funcy("d", nil, "f")
assert.spy(funcy).was_called_with("a", nil, "c", "d", nil, "f")
end)
+
+ it("should compose functions", function()
+ local function add(x)
+ return function(y)
+ return y + x
+ end
+ end
+ local function subtract(x)
+ return function(y)
+ return y - x
+ end
+ end
+ local function multiply(x)
+ return function(y)
+ return y * x
+ end
+ end
+
+ local big_maths = functional.compose(add(1), subtract(3), multiply(5))
+
+ assert.equals(23, big_maths(5))
+ end)
+
+ it("should not allow composing no functions", function()
+ local e = assert.error(function()
+ functional.compose()
+ end)
+ assert.equals("compose requires at least one function", e)
+ end)
+
+ it("should iterate list in .each", function()
+ local list = { "BLUE", "YELLOW", "RED" }
+ local iterate_fn = spy.new()
+ functional.each(iterate_fn, list)
+ assert.spy(iterate_fn).was_called(3)
+ assert.spy(iterate_fn).was_called_with("BLUE", 1)
+ assert.spy(iterate_fn).was_called_with("YELLOW", 2)
+ assert.spy(iterate_fn).was_called_with("RED", 3)
+ end)
end)