diff --git a/Makefile b/Makefile
index 4a7fc81c6..cd43a116d 100644
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,7 @@ NVIM_HEADLESS:=nvim --headless --noplugin -u tests/minimal_init.vim
dependencies:
git clone --depth 1 https://github.com/nvim-lua/plenary.nvim dependencies/pack/vendor/start/plenary.nvim
+ git clone --depth 1 https://github.com/neovim/nvim-lspconfig dependencies/pack/vendor/start/nvim-lspconfig
.PHONY: clean_dependencies
clean_dependencies:
diff --git a/README.md b/README.md
index 82454362f..e8d54d453 100644
--- a/README.md
+++ b/README.md
@@ -5,33 +5,34 @@
- [About](#about)
- [Screenshots](#screenshots)
- [Installation](#installation)
- - [Packer](#packer)
- - [vim-plug](#vim-plug)
- [Usage](#usage)
- - [Commands](#commands)
- [Setup](#setup)
+ - [Commands](#commands)
- [Configuration](#configuration)
- [Available LSPs](#available-lsps)
- [Custom servers](#custom-servers)
- [Logo](#logo)
-- [Roadmap](#roadmap)
- [Default configuration](#default-configuration)
## About
-Neovim plugin that allows you to seamlessly install LSP servers locally (inside `:echo stdpath("data")`).
+Neovim plugin that allows you to manage LSP servers (servers are installed inside `:echo stdpath("data")` by default).
+It works in tandem with [`lspconfig`](https://github.com/neovim/nvim-lspconfig)1 by registering a hook that
+enhances the `PATH` environment variable, allowing neovim's LSP client to locate the installed server executable.2
On top of just providing commands for installing & uninstalling LSP servers, it:
- provides a graphical UI
-- is optimized for blazing fast startup times
-- provides the ability to check for new server versions
+- provides the ability to check for, and upgrade to, new server versions through a single interface
- supports installing custom versions of LSP servers (for example `:LspInstall rust_analyzer@nightly`)
- relaxes the minimum requirements by attempting multiple different utilities (for example, only one of `wget`, `curl`, or `Invoke-WebRequest` is required for HTTP requests)
-- allows you to install and setup servers without having to restart neovim
- hosts [a suite of system tests](https://github.com/williamboman/nvim-lspconfig-test) for all supported servers
- has full support for Windows
+1 - while lspconfig is the main target, this plugin may also be used for other use cases
+
+2 - some servers don't provide an executable, in which case the full command to spawn the server is provided instead
+
## Screenshots
| | | |
@@ -83,6 +84,17 @@ Plug 'williamboman/nvim-lsp-installer'
## Usage
+### Setup
+
+In order for nvim-lsp-installer to register the necessary hooks at the right moment, **make sure you call the `.setup()`
+function before you set up any servers with `lspconfig`**!
+
+```lua
+require("nvim-lsp-installer").setup {}
+```
+
+Refer to the [Configuration](#configuration) section for information about which settings are available.
+
### Commands
- `:LspInstallInfo` - opens a graphical overview of your language servers
@@ -92,54 +104,16 @@ Plug 'williamboman/nvim-lsp-installer'
- `:LspInstallLog` - opens the log file in a new tab window
- `:LspPrintInstalled` - prints all installed language servers
-### Setup
-
-The recommended way of setting up your installed servers is to do it through nvim-lsp-installer.
-By doing so, nvim-lsp-installer will make sure to inject any necessary properties before calling lspconfig's setup
-function for you. You may find a minimal example below. To see how you can override the default settings for a server,
-refer to the [Wiki][overriding-default-settings].
-
-Make sure you don't also set up your servers directly via lspconfig (e.g. `require("lspconfig").clangd.setup {}`), as
-this will cause servers to be set up twice!
-
-[overriding-default-settings]: https://github.com/williamboman/nvim-lsp-installer/wiki/Advanced-Configuration#overriding-the-default-lsp-server-options
-
-```lua
-local lsp_installer = require("nvim-lsp-installer")
-
--- Register a handler that will be called for each installed server when it's ready (i.e. when installation is finished
--- or if the server is already installed).
-lsp_installer.on_server_ready(function(server)
- local opts = {}
-
- -- (optional) Customize the options passed to the server
- -- if server.name == "tsserver" then
- -- opts.root_dir = function() ... end
- -- end
-
- -- This setup() function will take the provided server configuration and decorate it with the necessary properties
- -- before passing it onwards to lspconfig.
- -- Refer to https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md
- server:setup(opts)
-end)
-```
-
-For more advanced use cases you may also interact with more APIs nvim-lsp-installer has to offer, refer to `:help nvim-lsp-installer` for more docs.
-
### Configuration
-You can configure certain behavior of nvim-lsp-installer by calling the `.settings()` function.
-
-_Make sure to provide your settings before any other interactions with nvim-lsp-installer!_
+You may optionally configure certain behavior of nvim-lsp-installer when calling the `.setup()` function.
Refer to the [default configuration](#default-configuration) for all available settings.
Example:
```lua
-local lsp_installer = require("nvim-lsp-installer")
-
-lsp_installer.settings({
+require("nvim-lsp-installer").setup({
ui = {
icons = {
server_installed = "✓",
@@ -289,10 +263,6 @@ You can create your own installers by using the same APIs nvim-lsp-installer its
Illustrations in the logo are derived from [@Kaligule](https://schauderbasis.de/)'s "Robots" collection.
-## Roadmap
-
-- Command (and corresponding Lua API) to update outdated servers (e.g., `:LspUpdateAll`)
-
## Default configuration
```lua
@@ -309,12 +279,16 @@ local DEFAULT_SETTINGS = {
keymaps = {
-- Keymap to expand a server in the UI
toggle_server_expand = "",
- -- Keymap to install a server
+ -- Keymap to install the server under the current cursor position
install_server = "i",
- -- Keymap to reinstall/update a server
+ -- Keymap to reinstall/update the server under the current cursor position
update_server = "u",
+ -- Keymap to check for new version for the server under the current cursor position
+ check_server_version = "c",
-- Keymap to update all installed servers
update_all_servers = "U",
+ -- Keymap to check which installed servers are outdated
+ check_outdated_servers = "C",
-- Keymap to uninstall a server
uninstall_server = "X",
},
diff --git a/doc/nvim-lsp-installer.txt b/doc/nvim-lsp-installer.txt
index 863ad2c93..9f755a761 100644
--- a/doc/nvim-lsp-installer.txt
+++ b/doc/nvim-lsp-installer.txt
@@ -20,7 +20,6 @@ it:
- relaxes the minimum requirements by attempting multiple different
utilities (for example, only one of `wget`, `curl`, or `Invoke-WebRequest`
is required for HTTP requests)
-- allows you to install and setup servers without having to restart neovim
- hosts a suite of system tests for all supported servers
- has full support for Windows
@@ -49,6 +48,25 @@ https://github.com/williamboman/nvim-lsp-installer/blob/main/CUSTOM_SERVERS.md.
==============================================================================
QUICK START *nvim-lsp-installer-quickstart*
+The only thing needed to get started with nvim-lsp-installer is to make sure
+to call the `setup()` function _before_ you set up any servers: >
+
+ require("nvim-lsp-installer").setup {}
+<
+
+Next, in your initialization files |init.lua|, setup the servers you want to use.
+Refer to |lspconfig| for more information! For example: >
+
+ local lspconfig = require("lspconfig")
+
+ local function on_attach(client, bufnr)
+ -- set up buffer keymaps, etc.
+ end
+
+ lspconfig.sumneko_lua.setup { on_attach = on_attach }
+ lspconfig.tsserver.setup { on_attach = on_attach }
+<
+
To view the UI for nvim-lsp-installer, run: >
:LspInstallInfo
@@ -76,32 +94,7 @@ buffer, simply just run: >
:LspInstall
<
-Please refer to each server's own release page to find which versions are
-available.
-
-Then, somewhere in your initialization script (see `:h init.lua`): >
-
- -- Register a handler that will be called for each installed server when it's ready (i.e. when installation is finished
- -- or if the server is already installed).
- lsp_installer.on_server_ready(function(server)
- local opts = {}
-
- -- (optional) Customize the options passed to the server
- -- if server.name == "tsserver" then
- -- opts.root_dir = function() ... end
- -- end
-
- -- This setup() function will take the provided server configuration and decorate it with the necessary properties
- -- before passing it onwards to lspconfig.
- -- Refer to https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md
- server:setup(opts)
- end)
-<
-
-Make sure you don't also set up your servers directly via lspconfig (e.g.
-`require("lspconfig").clangd.setup {}`), as this will cause servers to be set
-up twice!
-
+Please refer to each server's own release page to find which versions are available.
==============================================================================
COMMANDS *nvim-lsp-installer-commands*
@@ -156,20 +149,14 @@ Prints all installed language servers.
==============================================================================
SETTINGS *nvim-lsp-installer-settings*
-You can configure certain behavior of nvim-lsp-installer by calling the
-`.settings()` function.
-
-Make sure to provide your settings before any other interactions with
-nvim-lsp-installer!
+You can configure certain behavior of nvim-lsp-installer when calling the
+`.setup()` function.
Refer to the |nvim-lsp-installer-default-settings| for all available settings.
Example: >
- local lsp_installer = require("nvim-lsp-installer")
-
- -- Provide settings first!
- lsp_installer.settings({
+ require("nvim-lsp-installer").setup({
ui = {
icons = {
server_installed = "✓",
@@ -178,10 +165,6 @@ Example: >
}
}
})
-
- lsp_installer.on_server_ready(function (server)
- server:setup {}
- end)
<
*nvim-lsp-installer-default-settings*
@@ -200,12 +183,16 @@ The following settings are applied by default. >
keymaps = {
-- Keymap to expand a server in the UI
toggle_server_expand = "",
- -- Keymap to install a server
+ -- Keymap to install the server under the current cursor position
install_server = "i",
- -- Keymap to reinstall/update a server
+ -- Keymap to reinstall/update the server under the current cursor position
update_server = "u",
+ -- Keymap to check for new version for the server under the current cursor position
+ check_server_version = "c",
-- Keymap to update all installed servers
update_all_servers = "U",
+ -- Keymap to check which installed servers are outdated
+ check_outdated_servers = "C",
-- Keymap to uninstall a server
uninstall_server = "X",
},
@@ -238,7 +225,9 @@ DEBUGGING *nvim-lsp-installer-debugging*
To help with debugging issues with installing/uninstall servers, please make
sure to set nvim-lsp-installer's log level to DEBUG or TRACE, like so: >
- :lua require("nvim-lsp-installer").settings({ log_level = vim.log.levels.DEBUG })
+ require("nvim-lsp-installer").setup {
+ log_level = vim.log.levels.DEBUG
+ }
<
You may find the logs by entering the command `:LspInstallLog`. Providing the
@@ -247,6 +236,11 @@ contents of this file when reporting an issue will help tremendously.
==============================================================================
Lua module: nvim-lsp-installer
+ *nvim-lsp-installer.setup()*
+setup({config})
+ Sets up nvim-lsp-installer with the provided {config} (see
+ |nvim-lsp-installer-settings|).
+
*nvim-lsp-installer.install()*
install({server_name})
Installs the provided {server_name}. If {server_name} is already
@@ -345,7 +339,7 @@ class: Server
servers.
Methods: ~
- - setup({opts})
+ - setup({opts}) *DEPRECATED - setup servers directly via lspconfig instead*
Sets up the language server and attaches all open buffers.
See:
@@ -356,7 +350,7 @@ class: Server
{opts} (table) The lspconfig server configuration.
See https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md
- - setup_lsp({opts})
+ - setup_lsp({opts}) *DEPRECATED - setup servers directly via lspconfig instead*
Sets up the language server via lspconfig.
This function has the same signature as the setup function
in nvim-lspconfig.
@@ -365,13 +359,16 @@ class: Server
{opts} (table) The lspconfig server configuration.
See https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md
- - attach_buffers()
+ - attach_buffers() *DEPRECATED - setup servers directly via lspconfig instead*
Attaches this server to all current open buffers with a
'filetype' that matches the server's configured filetypes.
- get_default_options()
Returns a deep copy of the default options provided to
lspconfig in the setup({opts}) method.
+ Note: These only include the options provided by
+ nvim-lsp-installer, and not the default options provided
+ by lspconfig.
- on_ready({handler})
Registers the provided {handler} to be called when the
diff --git a/lua/nvim-lsp-installer.lua b/lua/nvim-lsp-installer.lua
index 62b20a1e2..a910440e8 100644
--- a/lua/nvim-lsp-installer.lua
+++ b/lua/nvim-lsp-installer.lua
@@ -14,6 +14,15 @@ local M = {}
M.settings = settings.set
+---@param config table
+function M.setup(config)
+ if config then
+ settings.set(config)
+ end
+ settings.uses_new_setup = true
+ require("nvim-lsp-installer.middleware").register_lspconfig_hook()
+end
+
M.info_window = {
---Opens the status window.
open = function()
diff --git a/lua/nvim-lsp-installer/middleware.lua b/lua/nvim-lsp-installer/middleware.lua
new file mode 100644
index 000000000..3e01b976a
--- /dev/null
+++ b/lua/nvim-lsp-installer/middleware.lua
@@ -0,0 +1,32 @@
+local util = require "lspconfig.util"
+local servers = require "nvim-lsp-installer.servers"
+
+local M = {}
+
+---@param t1 table
+---@param t2 table
+local function merge_in_place(t1, t2)
+ for k, v in pairs(t2) do
+ if type(v) == "table" then
+ if type(t1[k]) == "table" and not vim.tbl_islist(t1[k]) then
+ merge_in_place(t1[k], v)
+ else
+ t1[k] = v
+ end
+ else
+ t1[k] = v
+ end
+ end
+ return t1
+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)
+ if ok then
+ merge_in_place(config, server._default_options)
+ end
+ end)
+end
+
+return M
diff --git a/lua/nvim-lsp-installer/server.lua b/lua/nvim-lsp-installer/server.lua
index de6481a4b..66d9655f1 100644
--- a/lua/nvim-lsp-installer/server.lua
+++ b/lua/nvim-lsp-installer/server.lua
@@ -71,6 +71,10 @@ end
---Sets up the language server and attaches all open buffers.
---@param opts table @The lspconfig server configuration.
function M.Server:setup(opts)
+ assert(
+ not settings.uses_new_setup,
+ "Please set up servers directly via lspconfig instead of going through nvim-lsp-installer (this method is now deprecated)! Refer to :h nvim-lsp-installer-quickstart for more information."
+ )
self:setup_lsp(opts)
if not (opts.autostart == false) then
self:attach_buffers()
diff --git a/lua/nvim-lsp-installer/servers/init.lua b/lua/nvim-lsp-installer/servers/init.lua
index efca1dd52..9af4c1bb8 100644
--- a/lua/nvim-lsp-installer/servers/init.lua
+++ b/lua/nvim-lsp-installer/servers/init.lua
@@ -2,6 +2,7 @@ local Data = require "nvim-lsp-installer.data"
local path = require "nvim-lsp-installer.path"
local fs = require "nvim-lsp-installer.fs"
local settings = require "nvim-lsp-installer.settings"
+local log = require "nvim-lsp-installer.log"
local M = {}
@@ -180,15 +181,22 @@ end
---@param server_name string
---@return string
local function get_server_install_dir(server_name)
- return INSTALL_DIRS[server_name] or server_name
+ log.fmt_trace("Getting server installation dirname. uses_new_setup=%s", settings.uses_new_setup)
+ if settings.uses_new_setup then
+ return server_name
+ else
+ return INSTALL_DIRS[server_name] or server_name
+ end
end
function M.get_server_install_path(dirname)
+ log.trace("Getting server installation path", settings.current.install_root_dir, dirname)
return path.concat { settings.current.install_root_dir, dirname }
end
---@param server_name string
function M.is_server_installed(server_name)
+ log.trace("Checking if server is installed", server_name)
local scanned_server_dirs = scan_server_roots()
local dirname = get_server_install_dir(server_name)
return scanned_server_dirs[dirname] or false
@@ -213,6 +221,7 @@ function M.get_server(server_name)
local ok, server_factory = pcall(require, ("nvim-lsp-installer.servers.%s"):format(server_name))
if ok then
+ log.trace("Initializing core server", server_name)
INITIALIZED_SERVERS[server_name] = server_factory(
server_name,
M.get_server_install_path(get_server_install_dir(server_name))
diff --git a/lua/nvim-lsp-installer/settings.lua b/lua/nvim-lsp-installer/settings.lua
index ad875b728..87499354a 100644
--- a/lua/nvim-lsp-installer/settings.lua
+++ b/lua/nvim-lsp-installer/settings.lua
@@ -59,4 +59,8 @@ function M.set(opts)
M.current = vim.tbl_deep_extend("force", M.current, opts)
end
+-- Whether the new .setup() function has been called.
+-- This will temporarily be used as a flag to toggle certain behavior.
+M.uses_new_setup = false
+
return M
diff --git a/tests/middleware_spec.lua b/tests/middleware_spec.lua
new file mode 100644
index 000000000..5c629ee6d
--- /dev/null
+++ b/tests/middleware_spec.lua
@@ -0,0 +1,39 @@
+local util = require "lspconfig.util"
+local servers = require "nvim-lsp-installer.servers"
+local middleware = require "nvim-lsp-installer.middleware"
+
+describe("middleware", function()
+ it("should register on_setup hook with lspconfig", function()
+ -- 1. setup dummy server
+ local default_options = {
+ cmd = { "dummy-lsp" },
+ cmd_env = { PATH = "/keep/my/path/out/your/f/mouth" },
+ }
+ local server = ServerGenerator {
+ name = "dummy",
+ default_options = default_options,
+ }
+ servers.register(server)
+
+ -- 2. register hook
+ middleware.register_lspconfig_hook()
+
+ -- 3. call lspconfig hook
+ local config = {
+ name = "dummy",
+ cmd = { "should", "be", "overwritten" },
+ custom = "setting",
+ cmd_env = { SOME_DEFAULT_ENV = "important" },
+ }
+ util.on_setup(config)
+ assert.are.same({
+ cmd = { "dummy-lsp" },
+ name = "dummy",
+ custom = "setting",
+ cmd_env = {
+ PATH = "/keep/my/path/out/your/f/mouth",
+ SOME_DEFAULT_ENV = "important",
+ },
+ }, config)
+ end)
+end)
diff --git a/tests/minimal_debug_init.lua b/tests/minimal_debug_init.lua
index cd42ed246..f090b3535 100644
--- a/tests/minimal_debug_init.lua
+++ b/tests/minimal_debug_init.lua
@@ -32,25 +32,21 @@ local function load_plugins()
end
function _G.load_config()
- -- ==================================================
- -- ======= MODIFY YOUR CONFIG HERE, IF NEEDED =======
- -- ==================================================
- local lsp_installer = require "nvim-lsp-installer"
+ local lspconfig = require "lspconfig"
local function on_attach(client, bufnr)
vim.api.nvim_buf_set_option(bufnr, "omnifunc", "v:lua.vim.lsp.omnifunc")
end
- require("nvim-lsp-installer").settings {
+ require("nvim-lsp-installer").setup {
log = vim.log.levels.DEBUG,
}
- lsp_installer.on_server_ready(function(server)
- server:setup {
- on_attach = on_attach,
- }
- end)
-- ==================================================
+ -- ========= SETUP RELEVANT SERVER(S) HERE! =========
+ -- ==================================================
+ --
+ -- lspconfig.sumneko_lua.setup { on_attach = on_attach }
end
if vim.fn.isdirectory(install_path) == 0 then