Skip to content

Commit

Permalink
refactor: specify commands independently, not via lspconfig (chipsall…
Browse files Browse the repository at this point in the history
…iance#737)

This is not a desirable solution, but it's better than as-is.

There's a couple of reasons for this:
1) lspconfig only enables these commands upon successful attachment of
   servers, which doesn't really fit their use case.
2) We gain direct access to new Lua APIs for defining user commands,
   allowing us to leverage Lua callbacks and functions instead of
   intermediary global variables.
  • Loading branch information
williamboman authored May 29, 2022
1 parent 1c85bf2 commit 9cc15aa
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 76 deletions.
21 changes: 21 additions & 0 deletions lua/nvim-lsp-installer/middleware.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
local util = require "lspconfig.util"
local _ = require "nvim-lsp-installer.core.functional"
local notify = require "nvim-lsp-installer.notify"
local servers = require "nvim-lsp-installer.servers"
local settings = require "nvim-lsp-installer.settings"
Expand Down Expand Up @@ -38,6 +39,17 @@ local function should_auto_install(server_name)
return false
end

local registered_server_hooks = {}

---@param server_name string
---@param fn fun(config: table)
function M.register_server_hook(server_name, fn)
if not registered_server_hooks[server_name] then
registered_server_hooks[server_name] = {}
end
table.insert(registered_server_hooks[server_name], fn)
end

function M.register_lspconfig_hook()
util.on_setup = util.add_hook_before(util.on_setup, function(config)
local ok, server = servers.get_server(config.name)
Expand All @@ -49,6 +61,15 @@ function M.register_lspconfig_hook()
server:install()
end
end

if registered_server_hooks[config.name] then
_.each(function(fn)
local ok, err = pcall(fn, config)
if not ok then
notify(err, vim.log.levels.ERROR)
end
end, registered_server_hooks[config.name])
end
end)
end

Expand Down
3 changes: 1 addition & 2 deletions lua/nvim-lsp-installer/servers/pylsp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ In order for these plugins to work with the `pylsp` server managed by this plugi
:PylspInstall pyls-flake8 pylsp-mypy pyls-isort
```

The `:PylspInstall` command will only be available once the `pylsp` server is started (this happens once you've opened a
Python file, for example).
The `:PylspInstall` command will only be available once the `pylsp` server has been set up.

**Note that these extra pylsp plugins will not be reinstalled if you update/reinstall the `pylsp` server, you will have to manage
them manually.**
86 changes: 46 additions & 40 deletions lua/nvim-lsp-installer/servers/pylsp/init.lua
Original file line number Diff line number Diff line change
@@ -1,21 +1,55 @@
local a = require "nvim-lsp-installer.core.async"
local _ = require "nvim-lsp-installer.core.functional"
local server = require "nvim-lsp-installer.server"
local pip3 = require "nvim-lsp-installer.core.managers.pip3"
local process = require "nvim-lsp-installer.core.process"
local notify = require "nvim-lsp-installer.notify"

function _G.lsp_installer_pylsp_install_completion()
return table.concat({
"pyls-flake8",
"pylsp-mypy",
"pyls-spyder",
"pyls-isort",
"python-lsp-black",
"pyls-memestra",
"pylsp-rope",
}, "\n")
end
local middleware = require "nvim-lsp-installer.middleware"
local spawn = require "nvim-lsp-installer.core.spawn"

return function(name, root_dir)
middleware.register_server_hook(name, function()
vim.api.nvim_create_user_command(
"PylspInstall",
a.scope(function(opts)
local plugins = opts.fargs
local plugins_str = table.concat(plugins, ", ")
notify(("Installing %s..."):format(plugins_str))
local result = spawn.pip {
"install",
"-U",
"--disable-pip-version-check",
plugins,
stdio_sink = process.simple_sink(),
with_paths = { pip3.venv_path(root_dir) },
}
if vim.in_fast_event() then
a.scheduler()
end
result
:on_success(function()
notify(("Successfully installed pylsp plugins %s"):format(plugins_str))
end)
:on_failure(function()
notify("Failed to install requested pylsp plugins.", vim.log.levels.ERROR)
end)
end),
{
desc = "[nvim-lsp-installer] Installs the provided packages in the same venv as pylsp.",
nargs = "+",
complete = _.always {
"pyls-flake8",
"pylsp-mypy",
"pyls-spyder",
"pyls-isort",
"python-lsp-black",
"pyls-memestra",
"pylsp-rope",
},
}
)
end)

return server.Server:new {
name = name,
root_dir = root_dir,
Expand All @@ -24,34 +58,6 @@ return function(name, root_dir)
installer = pip3.packages { "python-lsp-server[all]" },
default_options = {
cmd_env = pip3.env(root_dir),
commands = {
PylspInstall = {
function(...)
-- `nargs+` requires at least one argument -> no empty table
local plugins = { ... }
local plugins_str = table.concat(plugins, ", ")
notify(("Installing %q..."):format(plugins_str))
process.spawn(
"pip",
{
args = vim.list_extend({ "install", "-U", "--disable-pip-version-check" }, plugins),
stdio_sink = process.simple_sink(),
env = process.graft_env(pip3.env(root_dir)),
},
vim.schedule_wrap(function(success)
if success then
notify(("Successfully installed %q"):format(plugins_str))
else
notify("Failed to install requested plugins.", vim.log.levels.ERROR)
end
end)
)
end,
description = "Installs the provided packages in the same venv as pylsp.",
["nargs=+"] = true,
["complete=custom,v:lua.lsp_installer_pylsp_install_completion"] = true,
},
},
},
}
end
13 changes: 9 additions & 4 deletions lua/nvim-lsp-installer/servers/tflint/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@

## Installing TFLint plugins

To install TFLint plugins, using the same installation of TFLint as nvim-lsp-installer, you may run the `:TFLintInit`
command. This command assumes that:
TFLint has [third party plugins](https://github.com/terraform-linters/tflint/blob/master/docs/user-guide/plugins.md) which are not installed by default.

1. there exists a `.tflint.hcl` file in neovim's current working directory.
1. you've called `server:setup(opts)` for `tflint`
To install TFLint plugins, there's a convenient `:TFLintInit` command that does this for you. It will use Neovim's
current working directory to locate the plugins to install (according to `tflint --init`):

```
:TFLintInit
```

The `:TFLintInit` command will only be available once the `tflint` server has been set up.
63 changes: 33 additions & 30 deletions lua/nvim-lsp-installer/servers/tflint/init.lua
Original file line number Diff line number Diff line change
@@ -1,14 +1,44 @@
local a = require "nvim-lsp-installer.core.async"
local notify = require "nvim-lsp-installer.notify"
local spawn = require "nvim-lsp-installer.core.spawn"
local process = require "nvim-lsp-installer.core.process"
local server = require "nvim-lsp-installer.server"
local functional = require "nvim-lsp-installer.core.functional"
local a = require "nvim-lsp-installer.core.async"
local platform = require "nvim-lsp-installer.core.platform"
local github = require "nvim-lsp-installer.core.managers.github"
local spawn = require "nvim-lsp-installer.core.spawn"
local process = require "nvim-lsp-installer.core.process"
local middleware = require "nvim-lsp-installer.middleware"

local coalesce, when = functional.coalesce, functional.when

return function(name, root_dir)
middleware.register_server_hook(name, function()
vim.api.nvim_create_user_command(
"TFLintInit",
a.scope(function()
notify "Installing TFLint plugins…"
local result = spawn.tflint {
"--init",
cwd = vim.loop.cwd(),
stdio_sink = process.simple_sink(),
with_paths = { root_dir },
}
if vim.in_fast_event() then
a.scheduler()
end
result
:on_success(function()
notify "Successfully installed TFLint plugins."
end)
:on_failure(function()
notify("Failed to install TFLint plugins.", vim.log.levels.ERROR)
end)
end),
{
desc = "[nvim-lsp-installer] Runs tflint --init in the current directory.",
}
)
end)

return server.Server:new {
name = name,
root_dir = root_dir,
Expand All @@ -31,33 +61,6 @@ return function(name, root_dir)
cmd_env = {
PATH = process.extend_path { root_dir },
},
commands = {
TFLintInit = {
a.scope(function()
local notify = require "nvim-lsp-installer.notify"

notify "Installing TFLint plugins…"
spawn.tflint({
"--init",
cwd = vim.loop.getcwd(),
with_paths = { root_dir },
})
:on_success(function()
if vim.in_fast_event() then
a.scheduler()
end
notify "Successfully installed TFLint plugins."
end)
:on_failure(function()
if vim.in_fast_event() then
a.scheduler()
end
notify "Failed to install TFLint plugins."
end)
end),
description = "Runs tflint --init in the current working directory.",
},
},
},
}
end

0 comments on commit 9cc15aa

Please sign in to comment.