diff options
| author | Ranjith Hegde <ranjithshegde@gmail.com> | 2022-08-23 16:03:20 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-08-23 07:03:20 -0700 |
| commit | fe7a6f41e59654db6bbc9eb2d084decc54b295e4 (patch) | |
| tree | c7967bb5b649ad7f8f00c697bedc2f60db9e507a | |
| parent | Revert "docs: vim.lsp.buf.formatting() is deprecated #2077 (diff) | |
| download | nvim-lspconfig-fe7a6f41e59654db6bbc9eb2d084decc54b295e4.tar nvim-lspconfig-fe7a6f41e59654db6bbc9eb2d084decc54b295e4.tar.gz nvim-lspconfig-fe7a6f41e59654db6bbc9eb2d084decc54b295e4.tar.bz2 nvim-lspconfig-fe7a6f41e59654db6bbc9eb2d084decc54b295e4.tar.lz nvim-lspconfig-fe7a6f41e59654db6bbc9eb2d084decc54b295e4.tar.xz nvim-lspconfig-fe7a6f41e59654db6bbc9eb2d084decc54b295e4.tar.zst nvim-lspconfig-fe7a6f41e59654db6bbc9eb2d084decc54b295e4.zip | |
feat!: 0.7 API update (#1984)
* switch to lua api for autocommands
* switch to nvim_create_user_command
* move to lua plugin initialization
NOTICE: Defining commands in server configurations will be deprecated in
future releases.
See `:help lspconfig.txt` to setup the same in an `on_attach` function.
Co-authored-by: Michael Lingelbach <m.j.lbach@gmail.com>
| -rw-r--r-- | doc/lspconfig.txt | 63 | ||||
| -rw-r--r-- | lua/lspconfig.lua | 76 | ||||
| -rw-r--r-- | lua/lspconfig/configs.lua | 61 | ||||
| -rw-r--r-- | lua/lspconfig/server_configurations/nickel_ls.lua | 2 | ||||
| -rw-r--r-- | lua/lspconfig/util.lua | 77 | ||||
| -rw-r--r-- | plugin/lspconfig.lua | 91 | ||||
| -rw-r--r-- | plugin/lspconfig.vim | 16 | ||||
| -rw-r--r-- | test/lspconfig_spec.lua | 42 | ||||
| -rw-r--r-- | test/minimal_init.lua | 14 |
9 files changed, 249 insertions, 193 deletions
diff --git a/doc/lspconfig.txt b/doc/lspconfig.txt index 70339a8b..b5f2c4aa 100644 --- a/doc/lspconfig.txt +++ b/doc/lspconfig.txt @@ -215,12 +215,12 @@ The global defaults for all servers can be overridden by extending the if params and params.type <= vim.lsp.protocol.MessageType.Log then vim.lsp.handlers["window/logMessage"](err, method, params, client_id) end - end; + end, ["window/showMessage"] = function(err, method, params, client_id) if params and params.type <= vim.lsp.protocol.MessageType.Warning.Error then vim.lsp.handlers["window/showMessage"](err, method, params, client_id) end - end; + end, } } ) @@ -264,35 +264,49 @@ The `configs` module is a singleton where configs are defined. The schema for validating using `vim.validate` is: > configs.SERVER_NAME = { - default_config = {'t'}; - on_new_config = {'f', true}; - on_attach = {'f', true}; - commands = {'t', true}; - docs = {'t', true}; + default_config = {'t'}, + on_new_config = {'f', true}, + on_attach = {'f', true}, + commands = {'t', true}, + docs = {'t', true}, } < where the structure of the docs table is as follows: > docs = { - description = {'s', true}; - default_config = {'t', true}; + description = {'s', true}, + default_config = {'t', true}, } < `commands` is a map of `name:definition` key:value pairs, where `definition` is a list whose first value is a function implementing the command, and the rest are either array values which will be formed into flags for the command, -or special keys like `description`. Example: +or special keys like `description`. + +Warning: Commands is deprecated and will be removed in future releases. +It is recommended to use `vim.api.nvim_create_user_command()` instead in an `on_attach` function. + +Example: > - commands = { - TexlabBuild = { - function() - buf_build(0) - end; - "-range"; - description = "Build the current buffer"; - }; - }; + local function organize_imports() + local params = { + command = 'pyright.organizeimports', + arguments = { vim.uri_from_bufnr(0) }, + } + vim.lsp.buf.execute_command(params) + end + + local on_attach = function(client, bufnr) + if client.name == "pyright" then + vim.api.nvim_create_user_command("PyrightOrganizeImports", organize_imports, {desc = 'Organize Imports'}) + end + end + + require("lspconfig")['pyright'].setup({ + on_attach = on_attach + }) < + The `configs.__newindex` metamethod consumes the config definition and returns an object with a `setup()` method, to be invoked by users: > @@ -305,6 +319,7 @@ Example: > configs.texlab.buf_build = buf_build < + ============================================================================== ADDING NEW SERVERS *lspconfig-new* @@ -320,13 +335,13 @@ The steps for adding and enabling a new server configuration are: if not configs.foo_lsp then configs.foo_lsp = { default_config = { - cmd = {'/home/neovim/lua-language-server/run.sh'}; - filetypes = {'lua'}; + cmd = {'/home/neovim/lua-language-server/run.sh'}, + filetypes = {'lua'}, root_dir = function(fname) return lspconfig.util.find_git_ancestor(fname) - end; - settings = {}; - }; + end, + settings = {}, + }, } end diff --git a/lua/lspconfig.lua b/lua/lspconfig.lua index b46ef65d..1178202e 100644 --- a/lua/lspconfig.lua +++ b/lua/lspconfig.lua @@ -4,82 +4,6 @@ local M = { util = require 'lspconfig.util', } -M._root = {} - -function M.available_servers() - local servers = {} - for server, config in pairs(configs) do - if config.manager ~= nil then - table.insert(servers, server) - end - end - return servers -end - --- Called from plugin/lspconfig.vim because it requires knowing that the last --- script in scriptnames to be executed is lspconfig. -function M._root._setup() - M._root.commands = { - LspInfo = { - function() - require 'lspconfig.ui.lspinfo'() - end, - '-nargs=0', - description = '`:LspInfo` Displays attached, active, and configured language servers', - }, - LspLog = { - function() - vim.cmd(string.format('tabnew %s', vim.lsp.get_log_path())) - end, - '-nargs=0', - description = '`:LspLog` Opens the Nvim LSP client log.', - }, - LspStart = { - function(server_name) - if server_name then - if configs[server_name] then - configs[server_name].launch() - end - else - local buffer_filetype = vim.bo.filetype - for _, config in pairs(configs) do - for _, filetype_match in ipairs(config.filetypes or {}) do - if buffer_filetype == filetype_match then - config.launch() - end - end - end - end - end, - '-nargs=? -complete=custom,v:lua.lsp_complete_configured_servers', - description = '`:LspStart` Manually launches a language server.', - }, - LspStop = { - function(cmd_args) - for _, client in ipairs(M.util.get_clients_from_cmd_args(cmd_args)) do - client.stop() - end - end, - '-nargs=? -complete=customlist,v:lua.lsp_get_active_client_ids', - description = '`:LspStop` Manually stops the given language client(s).', - }, - LspRestart = { - function(cmd_args) - for _, client in ipairs(M.util.get_clients_from_cmd_args(cmd_args)) do - client.stop() - vim.defer_fn(function() - configs[client.name].launch() - end, 500) - end - end, - '-nargs=? -complete=customlist,v:lua.lsp_get_active_client_ids', - description = '`:LspRestart` Manually restart the given language client(s).', - }, - } - - M.util.create_module_commands('_root', M._root.commands) -end - local mt = {} function mt:__index(k) if configs[k] == nil then diff --git a/lua/lspconfig/configs.lua b/lua/lspconfig/configs.lua index 07672a33..a0129776 100644 --- a/lua/lspconfig/configs.lua +++ b/lua/lspconfig/configs.lua @@ -31,6 +31,8 @@ function configs.__newindex(t, config_name, config_def) default_config.name = config_name function M.setup(config) + local lsp_group = vim.api.nvim_create_augroup('lspconfig', { clear = false }) + validate { cmd = { config.cmd, 't', true }, root_dir = { config.root_dir, 'f', true }, @@ -64,14 +66,17 @@ function configs.__newindex(t, config_name, config_def) event = 'BufReadPost' pattern = '*' end - api.nvim_command( - string.format( - "autocmd %s %s unsilent lua require'lspconfig'[%q].manager.try_add()", - event, - pattern, + vim.api.nvim_create_autocmd(event, { + pattern = pattern, + callback = function() + M.manager.try_add() + end, + group = lsp_group, + desc = string.format( + 'Checks whether server %s should start a new instance or attach to an existing one.', config.name - ) - ) + ), + }) end local get_root_dir = config.root_dir @@ -88,15 +93,18 @@ function configs.__newindex(t, config_name, config_def) end if root_dir then - -- Lazy-launching: attach when a buffer in this directory is opened. - api.nvim_command( - string.format( - "autocmd BufReadPost %s/* unsilent lua require'lspconfig'[%q].manager.try_add_wrapper()", - vim.fn.fnameescape(root_dir), - config.name - ) - ) - -- Attach for all existing buffers in this directory. + vim.api.nvim_create_autocmd('BufReadPost', { + pattern = vim.fn.fnameescape(root_dir) .. '/*', + callback = function() + M.manager.try_add_wrapper() + end, + group = lsp_group, + desc = string.format( + 'Checks whether server %s should attach to a newly opened buffer inside workspace %q.', + config.name, + root_dir + ), + }) for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do local bufname = api.nvim_buf_get_name(bufnr) if util.bufname_valid(bufname) then @@ -183,15 +191,15 @@ function configs.__newindex(t, config_name, config_def) M._setup_buffer(client.id, bufnr) else if vim.api.nvim_buf_is_valid(bufnr) then - api.nvim_command( - string.format( - "autocmd BufEnter <buffer=%d> ++once lua require'lspconfig'[%q]._setup_buffer(%d,%d)", - bufnr, - config_name, - client.id, - bufnr - ) - ) + vim.api.nvim_create_autocmd('BufEnter', { + callback = function() + M._setup_buffer(client.id, bufnr) + end, + group = lsp_group, + buffer = bufnr, + once = true, + desc = 'Reattaches the server with the updated configurations if changed.', + }) end end end) @@ -283,13 +291,10 @@ function configs.__newindex(t, config_name, config_def) M.commands = vim.tbl_deep_extend('force', M.commands, client.config.commands) end if not M.commands_created and not vim.tbl_isempty(M.commands) then - -- Create the module commands util.create_module_commands(config_name, M.commands) - M.commands_created = true end end - M.commands_created = false M.commands = config_def.commands M.name = config_name M.document_config = config_def diff --git a/lua/lspconfig/server_configurations/nickel_ls.lua b/lua/lspconfig/server_configurations/nickel_ls.lua index 3a9387e8..391abfee 100644 --- a/lua/lspconfig/server_configurations/nickel_ls.lua +++ b/lua/lspconfig/server_configurations/nickel_ls.lua @@ -30,7 +30,7 @@ cd nickel/lsp/nls cargo install --path . ``` -In order to have lspconfig detect Nickel filetypes (a prequisite for autostarting a server), +In order to have lspconfig detect Nickel filetypes (a prerequisite for autostarting a server), install the [Nickel vim plugin](https://github.com/nickel-lang/vim-nickel). ]], }, diff --git a/lua/lspconfig/util.lua b/lua/lspconfig/util.lua index 956be435..0729430f 100644 --- a/lua/lspconfig/util.lua +++ b/lua/lspconfig/util.lua @@ -3,7 +3,6 @@ local validate = vim.validate local api = vim.api local lsp = vim.lsp local uv = vim.loop -local fn = vim.fn local M = {} @@ -61,39 +60,39 @@ function M.add_hook_after(func, new_fn) end end -function M.create_module_commands(module_name, commands) - for command_name, def in pairs(commands) do - local parts = { 'command!' } - -- Insert attributes. - for k, v in pairs(def) do - if type(k) == 'string' and type(v) == 'boolean' and v then - table.insert(parts, '-' .. k) - elseif type(k) == 'number' and type(v) == 'string' and v:match '^%-' then - table.insert(parts, v) +-- Maps lspconfig-style command options to nvim_create_user_command (i.e. |command-attributes|) option names. +local opts_aliases = { + ['description'] = 'desc', +} + +---@param command_definition table<string | integer, any> +function M._parse_user_command_options(command_definition) + ---@type table<string, string | boolean | number> + local opts = {} + for k, v in pairs(command_definition) do + if type(k) == 'string' then + local attribute = k.gsub(k, '^%-+', '') + opts[opts_aliases[attribute] or attribute] = v + elseif type(k) == 'number' and type(v) == 'string' and v:match '^%-' then + -- Splits strings like "-nargs=* -complete=customlist,v:lua.something" into { "-nargs=*", "-complete=customlist,v:lua.something" } + for _, command_attribute in ipairs(vim.split(v, '%s')) do + -- Splits attribute into a key-value pair, like "-nargs=*" to { "-nargs", "*" } + local attribute, value = unpack(vim.split(command_attribute, '=', { plain = true })) + attribute = attribute.gsub(attribute, '^%-+', '') + opts[opts_aliases[attribute] or attribute] = value or true end end - table.insert(parts, command_name) - -- The command definition. - table.insert( - parts, - string.format("lua require'lspconfig'[%q].commands[%q][1](<f-args>)", module_name, command_name) - ) - api.nvim_command(table.concat(parts, ' ')) end + return opts end -function M.has_bins(...) - for i = 1, select('#', ...) do - if 0 == fn.executable((select(i, ...))) then - return false - end +function M.create_module_commands(module_name, commands) + for command_name, def in pairs(commands) do + local opts = M._parse_user_command_options(def) + api.nvim_create_user_command(command_name, function(info) + require('lspconfig')[module_name].commands[command_name][1](unpack(info.fargs)) + end, opts) end - return true -end - -M.script_path = function() - local str = debug.getinfo(2, 'S').source:sub(2) - return str:match '(.*[/\\])' end -- Some path utilities @@ -409,17 +408,6 @@ function M.get_other_matching_providers(filetype) return other_matching_configs end -function M.get_clients_from_cmd_args(arg) - local result = {} - for id in (arg or ''):gmatch '(%d+)' do - result[id] = vim.lsp.get_client_by_id(tonumber(id)) - end - if vim.tbl_isempty(result) then - return M.get_managed_clients() - end - return vim.tbl_values(result) -end - function M.get_active_client_by_name(bufnr, servername) for _, client in pairs(vim.lsp.buf_get_clients(bufnr)) do if client.name == servername then @@ -440,6 +428,17 @@ function M.get_managed_clients() return clients end +function M.available_servers() + local servers = {} + local configs = require 'lspconfig.configs' + for server, config in pairs(configs) do + if config.manager ~= nil then + table.insert(servers, server) + end + end + return servers +end + -- For zipfile: or tarfile: virtual paths, returns the path to the archive. -- Other paths are returned unaltered. function M.strip_archive_subpath(path) diff --git a/plugin/lspconfig.lua b/plugin/lspconfig.lua new file mode 100644 index 00000000..fd22a206 --- /dev/null +++ b/plugin/lspconfig.lua @@ -0,0 +1,91 @@ +if vim.g.lspconfig ~= nil then + return +end +vim.g.lspconfig = 1 + +local version_info = vim.version() +if vim.fn.has 'nvim-0.7' ~= 1 then + local warning_str = string.format( + '[lspconfig] requires neovim 0.7 or later. Detected neovim version: 0.%s.%s', + version_info.minor, + version_info.patch + ) + vim.notify_once(warning_str) + return +end + +local lsp_complete_configured_servers = function(arg) + return vim.tbl_filter(function(s) + return s:sub(1, #arg) == arg + end, vim.tbl_keys(require 'lspconfig.configs')) +end + +local lsp_get_active_client_ids = function(arg) + local clients = vim.tbl_map(function(client) + return ('%d (%s)'):format(client.id, client.name) + end, require('lspconfig.util').get_managed_clients()) + + return vim.tbl_filter(function(s) + return s:sub(1, #arg) == arg + end, clients) +end + +local get_clients_from_cmd_args = function(arg) + local result = {} + for id in (arg or ''):gmatch '(%d+)' do + result[id] = vim.lsp.get_client_by_id(tonumber(id)) + end + if vim.tbl_isempty(result) then + return require('lspconfig.util').get_managed_clients() + end + return vim.tbl_values(result) +end + +-- Called from plugin/lspconfig.vim because it requires knowing that the last +-- script in scriptnames to be executed is lspconfig. +vim.api.nvim_create_user_command('LspInfo', function() + require 'lspconfig.ui.lspinfo'() +end, { + desc = 'Displays attached, active, and configured language servers', +}) + +vim.api.nvim_create_user_command('LspStart', function(info) + local server_name = info.args[1] ~= '' and info.args + if server_name then + local config = require('lspconfig.configs')[server_name] + if config then + config.launch() + end + else + local other_matching_configs = require('lspconfig.util').get_other_matching_providers(vim.bo.filetype) + for _, config in ipairs(other_matching_configs) do + config.launch() + end + end +end, { + desc = 'Manually launches a language server', + nargs = '?', + complete = lsp_complete_configured_servers, +}) +vim.api.nvim_create_user_command('LspRestart', function(info) + for _, client in ipairs(get_clients_from_cmd_args(info.args)) do + client.stop() + vim.defer_fn(function() + require('lspconfig.configs')[client.name].launch() + end, 500) + end +end, { + desc = 'Manually restart the given language client(s)', + nargs = '?', + complete = lsp_get_active_client_ids, +}) + +vim.api.nvim_create_user_command('LspStop', function(info) + for _, client in ipairs(get_clients_from_cmd_args(info.args)) do + client.stop() + end +end, { + desc = 'Manually stops the given language client(s)', + nargs = '?', + complete = lsp_get_active_client_ids, +}) diff --git a/plugin/lspconfig.vim b/plugin/lspconfig.vim deleted file mode 100644 index 5c52d4e1..00000000 --- a/plugin/lspconfig.vim +++ /dev/null @@ -1,16 +0,0 @@ -if exists('g:lspconfig') - finish -endif -let g:lspconfig = 1 - -lua << EOF -lsp_complete_configured_servers = function() - return table.concat(require'lspconfig'.available_servers(), '\n') -end -lsp_get_active_client_ids = function() - return vim.tbl_map(function(client) - return ("%d (%s)"):format(client.id, client.name) - end, require'lspconfig.util'.get_managed_clients()) -end -require'lspconfig'._root._setup() -EOF diff --git a/test/lspconfig_spec.lua b/test/lspconfig_spec.lua index 89effd47..f98568c1 100644 --- a/test/lspconfig_spec.lua +++ b/test/lspconfig_spec.lua @@ -32,7 +32,7 @@ describe('lspconfig', function() ok(exec_lua [[ local lspconfig = require("lspconfig") - local not_exist_dir = vim.fn.getcwd().."/not/exsts" + local not_exist_dir = vim.fn.getcwd().."/not/exists" return lspconfig.util.path.exists(not_exist_dir) == false ]]) end) @@ -76,7 +76,7 @@ describe('lspconfig', function() ok(exec_lua [[ local lspconfig = require("lspconfig") - local not_exist_dir = vim.fn.getcwd().."/not/exsts" + local not_exist_dir = vim.fn.getcwd().."/not/exists" return not lspconfig.util.path.is_dir(not_exist_dir) ]]) end) @@ -213,7 +213,43 @@ describe('lspconfig', function() ]]) end) end) + + describe('user commands', function() + it('should translate command definition to nvim_create_user_command options', function() + eq( + { + nargs = '*', + complete = 'custom,v:lua.some_global', + }, + exec_lua [[ + local util = require("lspconfig.util") + return util._parse_user_command_options({ + function () end, + "-nargs=* -complete=custom,v:lua.some_global" + }) + ]] + ) + + eq( + { + desc = 'My awesome description.', + nargs = '*', + complete = 'custom,v:lua.another_global', + }, + exec_lua [[ + local util = require("lspconfig.util") + return util._parse_user_command_options({ + function () end, + ["-nargs"] = "*", + "-complete=custom,v:lua.another_global", + description = "My awesome description." + }) + ]] + ) + end) + end) end) + describe('config', function() it('normalizes user, server, and base default configs', function() eq( @@ -283,7 +319,7 @@ describe('lspconfig', function() local _ = lspconfig.sumneko_lua local _ = lspconfig.tsserver lspconfig.rust_analyzer.setup {} - return lspconfig.available_servers() + return require("lspconfig.util").available_servers() ]], { 'rust_analyzer' } ) diff --git a/test/minimal_init.lua b/test/minimal_init.lua index 8c7ad55a..ba2ae3fc 100644 --- a/test/minimal_init.lua +++ b/test/minimal_init.lua @@ -29,11 +29,9 @@ local function load_plugins() } end -_G.load_config = function() +local load_config = function() vim.lsp.set_log_level 'trace' - if vim.fn.has 'nvim-0.5.1' == 1 then - require('vim.lsp.log').set_format_func(vim.inspect) - end + require('vim.lsp.log').set_format_func(vim.inspect) local nvim_lsp = require 'lspconfig' local on_attach = function(_, bufnr) local function buf_set_option(...) @@ -86,9 +84,13 @@ if vim.fn.isdirectory(install_path) == 0 then vim.fn.system { 'git', 'clone', 'https://github.com/wbthomason/packer.nvim', install_path } load_plugins() require('packer').sync() - vim.cmd [[autocmd User PackerComplete ++once lua load_config()]] + local packer_group = vim.api.nvim_create_augroup('Packer', { clear = true }) + vim.api.nvim_create_autocmd( + 'User', + { pattern = 'PackerComplete', callback = load_config, group = packer_group, once = true } + ) else load_plugins() require('packer').sync() - _G.load_config() + load_config() end |
