mirror of
https://github.com/neovim/neovim.git
synced 2024-12-19 10:45:16 -07:00
feat(lsp): add vim.lsp.config and vim.lsp.enable
Design goals/requirements: - Default configuration of a server can be distributed across multiple sources. - And via RTP discovery. - Default configuration can be specified for all servers. - Configuration _can_ be project specific. Solution: - Two new API's: - `vim.lsp.config(name, cfg)`: - Used to define default configurations for servers of name. - Can be used like a table or called as a function. - Use `vim.lsp.confg('*', cfg)` to specify default config for all servers. - `vim.lsp.enable(name)` - Used to enable servers of name. Uses configuration defined via `vim.lsp.config()`.
This commit is contained in:
parent
ca760e645b
commit
3f1d09bc94
@ -28,31 +28,114 @@ Follow these steps to get LSP features:
|
|||||||
upstream installation instructions. You can find language servers here:
|
upstream installation instructions. You can find language servers here:
|
||||||
https://microsoft.github.io/language-server-protocol/implementors/servers/
|
https://microsoft.github.io/language-server-protocol/implementors/servers/
|
||||||
|
|
||||||
2. Use |vim.lsp.start()| to start the LSP server (or attach to an existing
|
2. Use |vim.lsp.config()| to define a configuration for an LSP client.
|
||||||
one) when a file is opened. Example: >lua
|
Example: >lua
|
||||||
-- Create an event handler for the FileType autocommand
|
vim.lsp.config['luals'] = {
|
||||||
vim.api.nvim_create_autocmd('FileType', {
|
-- Command and arguments to start the server.
|
||||||
-- This handler will fire when the buffer's 'filetype' is "python"
|
cmd = { 'lua-language-server' }
|
||||||
pattern = 'python',
|
|
||||||
callback = function(args)
|
|
||||||
vim.lsp.start({
|
|
||||||
name = 'my-server-name',
|
|
||||||
cmd = {'name-of-language-server-executable', '--option', 'arg1', 'arg2'},
|
|
||||||
|
|
||||||
-- Set the "root directory" to the parent directory of the file in the
|
-- Filetypes to automatically attach to.
|
||||||
-- current buffer (`args.buf`) that contains either a "setup.py" or a
|
filetypes = { 'lua' },
|
||||||
-- "pyproject.toml" file. Files that share a root directory will reuse
|
|
||||||
-- the connection to the same LSP server.
|
-- Sets the "root directory" to the parent directory of the file in the
|
||||||
root_dir = vim.fs.root(args.buf, {'setup.py', 'pyproject.toml'}),
|
-- current buffer that contains either a ".luarc.json" or a
|
||||||
})
|
-- ".luarc.jsonc" file. Files that share a root directory will reuse
|
||||||
end,
|
-- the connection to the same LSP server.
|
||||||
})
|
root_markers = { '.luarc.json', '.luarc.jsonc' },
|
||||||
|
|
||||||
|
-- Specific settings to send to the server. The schema for this is
|
||||||
|
-- defined by the server. For example the schema for lua-language-server
|
||||||
|
-- can be found here https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json
|
||||||
|
settings = {
|
||||||
|
Lua = {
|
||||||
|
runtime = {
|
||||||
|
version = 'LuaJIT',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
<
|
<
|
||||||
3. Check that the buffer is attached to the server: >vim
|
3. Use |vim.lsp.enable()| to enable a configuration.
|
||||||
:checkhealth lsp
|
Example: >lua
|
||||||
|
vim.lsp.enable('luals')
|
||||||
|
<
|
||||||
|
4. Check that the buffer is attached to the server: >vim
|
||||||
|
:checkhealth vim.lsp
|
||||||
|
<
|
||||||
|
5. (Optional) Configure keymaps and autocommands to use LSP features.
|
||||||
|
|lsp-attach|
|
||||||
|
|
||||||
4. (Optional) Configure keymaps and autocommands to use LSP features. |lsp-config|
|
*lsp-config*
|
||||||
|
|
||||||
|
Configurations for LSP clients is done via |vim.lsp.config()|.
|
||||||
|
|
||||||
|
When an LSP client starts, it resolves a configuration by merging
|
||||||
|
configurations, in increasing priority, from the following:
|
||||||
|
|
||||||
|
1. Configuration defined for the `'*'` name.
|
||||||
|
|
||||||
|
2. Configuration from the result of sourcing all `lsp/<name>.lua` files
|
||||||
|
in 'runtimepath' for a server of name `name`.
|
||||||
|
|
||||||
|
Note: because of this, calls to |vim.lsp.config()| in `lsp/*.lua` are
|
||||||
|
treated independently to other calls. This ensures configurations
|
||||||
|
defined in `lsp/*.lua` have a lower priority.
|
||||||
|
|
||||||
|
3. Configurations defined anywhere else.
|
||||||
|
|
||||||
|
Note: The merge semantics of configurations follow the behaviour of
|
||||||
|
|vim.tbl_deep_extend()|.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
Given: >lua
|
||||||
|
-- Defined in init.lua
|
||||||
|
vim.lsp.config('*', {
|
||||||
|
capabilities = {
|
||||||
|
textDocument = {
|
||||||
|
semanticTokens = {
|
||||||
|
multilineTokenSupport = true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root_markers = { '.git' },
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Defined in ../lsp/clangd.lua
|
||||||
|
vim.lsp.config('clangd', {
|
||||||
|
cmd = { 'clangd' },
|
||||||
|
root_markers = { '.clangd', 'compile_commands.json' },
|
||||||
|
filetypes = { 'c', 'cpp' },
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Defined in init.lua
|
||||||
|
vim.lsp.config('clangd', {
|
||||||
|
filetypes = { 'c' },
|
||||||
|
})
|
||||||
|
<
|
||||||
|
Results in the configuration: >lua
|
||||||
|
{
|
||||||
|
-- From the clangd configuration in <rtp>/lsp/clangd.lua
|
||||||
|
cmd = { 'clangd' },
|
||||||
|
|
||||||
|
-- From the clangd configuration in <rtp>/lsp/clangd.lua
|
||||||
|
-- Overrides the * configuration in init.lua
|
||||||
|
root_markers = { '.clangd', 'compile_commands.json' },
|
||||||
|
|
||||||
|
-- From the clangd configuration in init.lua
|
||||||
|
-- Overrides the * configuration in init.lua
|
||||||
|
filetypes = { 'c' },
|
||||||
|
|
||||||
|
-- From the * configuration in init.lua
|
||||||
|
capabilities = {
|
||||||
|
textDocument = {
|
||||||
|
semanticTokens = {
|
||||||
|
multilineTokenSupport = true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<
|
||||||
*lsp-defaults*
|
*lsp-defaults*
|
||||||
When the Nvim LSP client starts it enables diagnostics |vim.diagnostic| (see
|
When the Nvim LSP client starts it enables diagnostics |vim.diagnostic| (see
|
||||||
|vim.diagnostic.config()| to customize). It also sets various default options,
|
|vim.diagnostic.config()| to customize). It also sets various default options,
|
||||||
@ -98,7 +181,7 @@ To override or delete any of the above defaults, set or unset the options on
|
|||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
<
|
<
|
||||||
*lsp-config*
|
*lsp-attach*
|
||||||
To use other LSP features, set keymaps and other buffer options on
|
To use other LSP features, set keymaps and other buffer options on
|
||||||
|LspAttach|. Not all language servers provide the same capabilities. Use
|
|LspAttach|. Not all language servers provide the same capabilities. Use
|
||||||
capability checks to ensure you only use features supported by the language
|
capability checks to ensure you only use features supported by the language
|
||||||
@ -107,16 +190,16 @@ server. Example: >lua
|
|||||||
vim.api.nvim_create_autocmd('LspAttach', {
|
vim.api.nvim_create_autocmd('LspAttach', {
|
||||||
callback = function(args)
|
callback = function(args)
|
||||||
local client = vim.lsp.get_client_by_id(args.data.client_id)
|
local client = vim.lsp.get_client_by_id(args.data.client_id)
|
||||||
if client.supports_method('textDocument/implementation') then
|
if client:supports_method('textDocument/implementation') then
|
||||||
-- Create a keymap for vim.lsp.buf.implementation
|
-- Create a keymap for vim.lsp.buf.implementation
|
||||||
end
|
end
|
||||||
|
|
||||||
if client.supports_method('textDocument/completion') then
|
if client:supports_method('textDocument/completion') then
|
||||||
-- Enable auto-completion
|
-- Enable auto-completion
|
||||||
vim.lsp.completion.enable(true, client.id, args.buf, {autotrigger = true})
|
vim.lsp.completion.enable(true, client.id, args.buf, {autotrigger = true})
|
||||||
end
|
end
|
||||||
|
|
||||||
if client.supports_method('textDocument/formatting') then
|
if client:supports_method('textDocument/formatting') then
|
||||||
-- Format the current buffer on save
|
-- Format the current buffer on save
|
||||||
vim.api.nvim_create_autocmd('BufWritePre', {
|
vim.api.nvim_create_autocmd('BufWritePre', {
|
||||||
buffer = args.buf,
|
buffer = args.buf,
|
||||||
@ -465,7 +548,7 @@ EVENTS *lsp-events*
|
|||||||
LspAttach *LspAttach*
|
LspAttach *LspAttach*
|
||||||
After an LSP client attaches to a buffer. The |autocmd-pattern| is the
|
After an LSP client attaches to a buffer. The |autocmd-pattern| is the
|
||||||
name of the buffer. When used from Lua, the client ID is passed to the
|
name of the buffer. When used from Lua, the client ID is passed to the
|
||||||
callback in the "data" table. See |lsp-config| for an example.
|
callback in the "data" table. See |lsp-attach| for an example.
|
||||||
|
|
||||||
LspDetach *LspDetach*
|
LspDetach *LspDetach*
|
||||||
Just before an LSP client detaches from a buffer. The |autocmd-pattern|
|
Just before an LSP client detaches from a buffer. The |autocmd-pattern|
|
||||||
@ -478,7 +561,7 @@ LspDetach *LspDetach*
|
|||||||
local client = vim.lsp.get_client_by_id(args.data.client_id)
|
local client = vim.lsp.get_client_by_id(args.data.client_id)
|
||||||
|
|
||||||
-- Remove the autocommand to format the buffer on save, if it exists
|
-- Remove the autocommand to format the buffer on save, if it exists
|
||||||
if client.supports_method('textDocument/formatting') then
|
if client:supports_method('textDocument/formatting') then
|
||||||
vim.api.nvim_clear_autocmds({
|
vim.api.nvim_clear_autocmds({
|
||||||
event = 'BufWritePre',
|
event = 'BufWritePre',
|
||||||
buffer = args.buf,
|
buffer = args.buf,
|
||||||
@ -590,6 +673,27 @@ LspTokenUpdate *LspTokenUpdate*
|
|||||||
==============================================================================
|
==============================================================================
|
||||||
Lua module: vim.lsp *lsp-core*
|
Lua module: vim.lsp *lsp-core*
|
||||||
|
|
||||||
|
*vim.lsp.Config*
|
||||||
|
Extends: |vim.lsp.ClientConfig|
|
||||||
|
|
||||||
|
|
||||||
|
Fields: ~
|
||||||
|
• {cmd}? (`string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient`)
|
||||||
|
See `cmd` in |vim.lsp.ClientConfig|.
|
||||||
|
• {filetypes}? (`string[]`) Filetypes the client will attach to, if
|
||||||
|
activated by `vim.lsp.enable()`. If not provided,
|
||||||
|
then the client will attach to all filetypes.
|
||||||
|
• {root_markers}? (`string[]`) Directory markers (.e.g. '.git/') where
|
||||||
|
the LSP server will base its workspaceFolders,
|
||||||
|
rootUri, and rootPath on initialization. Unused if
|
||||||
|
`root_dir` is provided.
|
||||||
|
• {reuse_client}? (`fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean`)
|
||||||
|
Predicate used to decide if a client should be
|
||||||
|
re-used. Used on all running clients. The default
|
||||||
|
implementation re-uses a client if name and root_dir
|
||||||
|
matches.
|
||||||
|
|
||||||
|
|
||||||
buf_attach_client({bufnr}, {client_id}) *vim.lsp.buf_attach_client()*
|
buf_attach_client({bufnr}, {client_id}) *vim.lsp.buf_attach_client()*
|
||||||
Implements the `textDocument/did…` notifications required to track a
|
Implements the `textDocument/did…` notifications required to track a
|
||||||
buffer for any language server.
|
buffer for any language server.
|
||||||
@ -689,7 +793,7 @@ commands *vim.lsp.commands*
|
|||||||
value is a function which is called if any LSP action (code action, code
|
value is a function which is called if any LSP action (code action, code
|
||||||
lenses, ...) triggers the command.
|
lenses, ...) triggers the command.
|
||||||
|
|
||||||
If a LSP response contains a command for which no matching entry is
|
If an LSP response contains a command for which no matching entry is
|
||||||
available in this registry, the command will be executed via the LSP
|
available in this registry, the command will be executed via the LSP
|
||||||
server using `workspace/executeCommand`.
|
server using `workspace/executeCommand`.
|
||||||
|
|
||||||
@ -698,6 +802,65 @@ commands *vim.lsp.commands*
|
|||||||
|
|
||||||
The second argument is the `ctx` of |lsp-handler|
|
The second argument is the `ctx` of |lsp-handler|
|
||||||
|
|
||||||
|
config({name}, {cfg}) *vim.lsp.config()*
|
||||||
|
Update the configuration for an LSP client.
|
||||||
|
|
||||||
|
Use name '*' to set default configuration for all clients.
|
||||||
|
|
||||||
|
Can also be table-assigned to redefine the configuration for a client.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
• Add a root marker for all clients: >lua
|
||||||
|
vim.lsp.config('*', {
|
||||||
|
root_markers = { '.git' },
|
||||||
|
})
|
||||||
|
<
|
||||||
|
• Add additional capabilities to all clients: >lua
|
||||||
|
vim.lsp.config('*', {
|
||||||
|
capabilities = {
|
||||||
|
textDocument = {
|
||||||
|
semanticTokens = {
|
||||||
|
multilineTokenSupport = true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
<
|
||||||
|
• (Re-)define the configuration for clangd: >lua
|
||||||
|
vim.lsp.config.clangd = {
|
||||||
|
cmd = {
|
||||||
|
'clangd',
|
||||||
|
'--clang-tidy',
|
||||||
|
'--background-index',
|
||||||
|
'--offset-encoding=utf-8',
|
||||||
|
},
|
||||||
|
root_markers = { '.clangd', 'compile_commands.json' },
|
||||||
|
filetypes = { 'c', 'cpp' },
|
||||||
|
}
|
||||||
|
<
|
||||||
|
• Get configuration for luals: >lua
|
||||||
|
local cfg = vim.lsp.config.luals
|
||||||
|
<
|
||||||
|
|
||||||
|
Parameters: ~
|
||||||
|
• {name} (`string`)
|
||||||
|
• {cfg} (`vim.lsp.Config`) See |vim.lsp.Config|.
|
||||||
|
|
||||||
|
enable({name}, {enable}) *vim.lsp.enable()*
|
||||||
|
Enable an LSP server to automatically start when opening a buffer.
|
||||||
|
|
||||||
|
Uses configuration defined with `vim.lsp.config`.
|
||||||
|
|
||||||
|
Examples: >lua
|
||||||
|
vim.lsp.enable('clangd')
|
||||||
|
|
||||||
|
vim.lsp.enable({'luals', 'pyright'})
|
||||||
|
<
|
||||||
|
|
||||||
|
Parameters: ~
|
||||||
|
• {name} (`string|string[]`) Name(s) of client(s) to enable.
|
||||||
|
• {enable} (`boolean?`) `true|nil` to enable, `false` to disable.
|
||||||
|
|
||||||
foldclose({kind}, {winid}) *vim.lsp.foldclose()*
|
foldclose({kind}, {winid}) *vim.lsp.foldclose()*
|
||||||
Close all {kind} of folds in the the window with {winid}.
|
Close all {kind} of folds in the the window with {winid}.
|
||||||
|
|
||||||
|
@ -237,6 +237,9 @@ LSP
|
|||||||
• Functions in |vim.lsp.Client| can now be called as methods.
|
• Functions in |vim.lsp.Client| can now be called as methods.
|
||||||
• Implemented LSP folding: |vim.lsp.foldexpr()|
|
• Implemented LSP folding: |vim.lsp.foldexpr()|
|
||||||
https://microsoft.github.io/language-server-protocol/specification/#textDocument_foldingRange
|
https://microsoft.github.io/language-server-protocol/specification/#textDocument_foldingRange
|
||||||
|
• |vim.lsp.config()| has been added to define default configurations for
|
||||||
|
servers. In addition, configurations can be specified in `lsp/<name>.lua`.
|
||||||
|
• |vim.lsp.enable()| has been added to enable servers.
|
||||||
|
|
||||||
LUA
|
LUA
|
||||||
|
|
||||||
|
@ -4810,6 +4810,7 @@ A jump table for the options with a short description can be found at |Q_op|.
|
|||||||
indent/ indent scripts |indent-expression|
|
indent/ indent scripts |indent-expression|
|
||||||
keymap/ key mapping files |mbyte-keymap|
|
keymap/ key mapping files |mbyte-keymap|
|
||||||
lang/ menu translations |:menutrans|
|
lang/ menu translations |:menutrans|
|
||||||
|
lsp/ LSP client configurations |lsp-config|
|
||||||
lua/ |Lua| plugins
|
lua/ |Lua| plugins
|
||||||
menu.vim GUI menus |menu.vim|
|
menu.vim GUI menus |menu.vim|
|
||||||
pack/ packages |:packadd|
|
pack/ packages |:packadd|
|
||||||
|
1
runtime/lua/vim/_meta/options.lua
generated
1
runtime/lua/vim/_meta/options.lua
generated
@ -5010,6 +5010,7 @@ vim.go.ruf = vim.go.rulerformat
|
|||||||
--- indent/ indent scripts `indent-expression`
|
--- indent/ indent scripts `indent-expression`
|
||||||
--- keymap/ key mapping files `mbyte-keymap`
|
--- keymap/ key mapping files `mbyte-keymap`
|
||||||
--- lang/ menu translations `:menutrans`
|
--- lang/ menu translations `:menutrans`
|
||||||
|
--- lsp/ LSP client configurations `lsp-config`
|
||||||
--- lua/ `Lua` plugins
|
--- lua/ `Lua` plugins
|
||||||
--- menu.vim GUI menus `menu.vim`
|
--- menu.vim GUI menus `menu.vim`
|
||||||
--- pack/ packages `:packadd`
|
--- pack/ packages `:packadd`
|
||||||
|
@ -316,6 +316,240 @@ local function create_and_initialize_client(config)
|
|||||||
return client.id, nil
|
return client.id, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @class vim.lsp.Config : vim.lsp.ClientConfig
|
||||||
|
---
|
||||||
|
--- See `cmd` in [vim.lsp.ClientConfig].
|
||||||
|
--- @field cmd? string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient
|
||||||
|
---
|
||||||
|
--- Filetypes the client will attach to, if activated by `vim.lsp.enable()`.
|
||||||
|
--- If not provided, then the client will attach to all filetypes.
|
||||||
|
--- @field filetypes? string[]
|
||||||
|
---
|
||||||
|
--- Directory markers (.e.g. '.git/') where the LSP server will base its workspaceFolders,
|
||||||
|
--- rootUri, and rootPath on initialization. Unused if `root_dir` is provided.
|
||||||
|
--- @field root_markers? string[]
|
||||||
|
---
|
||||||
|
--- Predicate used to decide if a client should be re-used. Used on all
|
||||||
|
--- running clients. The default implementation re-uses a client if name and
|
||||||
|
--- root_dir matches.
|
||||||
|
--- @field reuse_client? fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean
|
||||||
|
|
||||||
|
--- Update the configuration for an LSP client.
|
||||||
|
---
|
||||||
|
--- Use name '*' to set default configuration for all clients.
|
||||||
|
---
|
||||||
|
--- Can also be table-assigned to redefine the configuration for a client.
|
||||||
|
---
|
||||||
|
--- Examples:
|
||||||
|
---
|
||||||
|
--- - Add a root marker for all clients:
|
||||||
|
--- ```lua
|
||||||
|
--- vim.lsp.config('*', {
|
||||||
|
--- root_markers = { '.git' },
|
||||||
|
--- })
|
||||||
|
--- ```
|
||||||
|
--- - Add additional capabilities to all clients:
|
||||||
|
--- ```lua
|
||||||
|
--- vim.lsp.config('*', {
|
||||||
|
--- capabilities = {
|
||||||
|
--- textDocument = {
|
||||||
|
--- semanticTokens = {
|
||||||
|
--- multilineTokenSupport = true,
|
||||||
|
--- }
|
||||||
|
--- }
|
||||||
|
--- }
|
||||||
|
--- })
|
||||||
|
--- ```
|
||||||
|
--- - (Re-)define the configuration for clangd:
|
||||||
|
--- ```lua
|
||||||
|
--- vim.lsp.config.clangd = {
|
||||||
|
--- cmd = {
|
||||||
|
--- 'clangd',
|
||||||
|
--- '--clang-tidy',
|
||||||
|
--- '--background-index',
|
||||||
|
--- '--offset-encoding=utf-8',
|
||||||
|
--- },
|
||||||
|
--- root_markers = { '.clangd', 'compile_commands.json' },
|
||||||
|
--- filetypes = { 'c', 'cpp' },
|
||||||
|
--- }
|
||||||
|
--- ```
|
||||||
|
--- - Get configuration for luals:
|
||||||
|
--- ```lua
|
||||||
|
--- local cfg = vim.lsp.config.luals
|
||||||
|
--- ```
|
||||||
|
---
|
||||||
|
--- @param name string
|
||||||
|
--- @param cfg vim.lsp.Config
|
||||||
|
--- @diagnostic disable-next-line:assign-type-mismatch
|
||||||
|
function lsp.config(name, cfg)
|
||||||
|
local _, _ = name, cfg -- ignore unused
|
||||||
|
-- dummy proto for docs
|
||||||
|
end
|
||||||
|
|
||||||
|
lsp._enabled_configs = {} --- @type table<string,{resolved_config:vim.lsp.Config?}>
|
||||||
|
|
||||||
|
--- If a config in vim.lsp.config() is accessed then the resolved config becomes invalid.
|
||||||
|
--- @param name string
|
||||||
|
local function invalidate_enabled_config(name)
|
||||||
|
if name == '*' then
|
||||||
|
for _, v in pairs(lsp._enabled_configs) do
|
||||||
|
v.resolved_config = nil
|
||||||
|
end
|
||||||
|
elseif lsp._enabled_configs[name] then
|
||||||
|
lsp._enabled_configs[name].resolved_config = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @nodoc
|
||||||
|
--- @class vim.lsp.config
|
||||||
|
--- @field [string] vim.lsp.Config
|
||||||
|
--- @field package _configs table<string,vim.lsp.Config>
|
||||||
|
lsp.config = setmetatable({ _configs = {} }, {
|
||||||
|
--- @param self vim.lsp.config
|
||||||
|
--- @param name string
|
||||||
|
--- @return vim.lsp.Config
|
||||||
|
__index = function(self, name)
|
||||||
|
validate('name', name, 'string')
|
||||||
|
invalidate_enabled_config(name)
|
||||||
|
self._configs[name] = self._configs[name] or {}
|
||||||
|
return self._configs[name]
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- @param self vim.lsp.config
|
||||||
|
--- @param name string
|
||||||
|
--- @param cfg vim.lsp.Config
|
||||||
|
__newindex = function(self, name, cfg)
|
||||||
|
validate('name', name, 'string')
|
||||||
|
validate('cfg', cfg, 'table')
|
||||||
|
invalidate_enabled_config(name)
|
||||||
|
self._configs[name] = cfg
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- @param self vim.lsp.config
|
||||||
|
--- @param name string
|
||||||
|
--- @param cfg vim.lsp.Config
|
||||||
|
__call = function(self, name, cfg)
|
||||||
|
validate('name', name, 'string')
|
||||||
|
validate('cfg', cfg, 'table')
|
||||||
|
invalidate_enabled_config(name)
|
||||||
|
self[name] = vim.tbl_deep_extend('force', self._configs[name] or {}, cfg)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
--- @private
|
||||||
|
--- @param name string
|
||||||
|
--- @return vim.lsp.Config
|
||||||
|
function lsp._resolve_config(name)
|
||||||
|
local econfig = lsp._enabled_configs[name] or {}
|
||||||
|
|
||||||
|
if not econfig.resolved_config then
|
||||||
|
-- Resolve configs from lsp/*.lua
|
||||||
|
-- Calls to vim.lsp.config in lsp/* have a lower precedence than calls from other sites.
|
||||||
|
local orig_configs = lsp.config._configs
|
||||||
|
lsp.config._configs = {}
|
||||||
|
pcall(vim.cmd.runtime, { ('lsp/%s.lua'):format(name), bang = true })
|
||||||
|
local rtp_configs = lsp.config._configs
|
||||||
|
lsp.config._configs = orig_configs
|
||||||
|
|
||||||
|
local config = vim.tbl_deep_extend(
|
||||||
|
'force',
|
||||||
|
lsp.config._configs['*'] or {},
|
||||||
|
rtp_configs[name] or {},
|
||||||
|
lsp.config._configs[name] or {}
|
||||||
|
)
|
||||||
|
|
||||||
|
config.name = name
|
||||||
|
|
||||||
|
validate('cmd', config.cmd, { 'function', 'table' })
|
||||||
|
validate('cmd', config.reuse_client, 'function', true)
|
||||||
|
-- All other fields are validated in client.create
|
||||||
|
|
||||||
|
econfig.resolved_config = config
|
||||||
|
end
|
||||||
|
|
||||||
|
return assert(econfig.resolved_config)
|
||||||
|
end
|
||||||
|
|
||||||
|
local lsp_enable_autocmd_id --- @type integer?
|
||||||
|
|
||||||
|
--- @param bufnr integer
|
||||||
|
local function lsp_enable_callback(bufnr)
|
||||||
|
-- Only ever attach to buffers that represent an actual file.
|
||||||
|
if vim.bo[bufnr].buftype ~= '' then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @param config vim.lsp.Config
|
||||||
|
local function can_start(config)
|
||||||
|
if config.filetypes and not vim.tbl_contains(config.filetypes, vim.bo[bufnr].filetype) then
|
||||||
|
return false
|
||||||
|
elseif type(config.cmd) == 'table' and vim.fn.executable(config.cmd[1]) == 0 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
for name in vim.spairs(lsp._enabled_configs) do
|
||||||
|
local config = lsp._resolve_config(name)
|
||||||
|
|
||||||
|
if can_start(config) then
|
||||||
|
-- Deepcopy config so changes done in the client
|
||||||
|
-- do not propagate back to the enabled configs.
|
||||||
|
config = vim.deepcopy(config)
|
||||||
|
|
||||||
|
vim.lsp.start(config, {
|
||||||
|
bufnr = bufnr,
|
||||||
|
reuse_client = config.reuse_client,
|
||||||
|
_root_markers = config.root_markers,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Enable an LSP server to automatically start when opening a buffer.
|
||||||
|
---
|
||||||
|
--- Uses configuration defined with `vim.lsp.config`.
|
||||||
|
---
|
||||||
|
--- Examples:
|
||||||
|
---
|
||||||
|
--- ```lua
|
||||||
|
--- vim.lsp.enable('clangd')
|
||||||
|
---
|
||||||
|
--- vim.lsp.enable({'luals', 'pyright'})
|
||||||
|
--- ```
|
||||||
|
---
|
||||||
|
--- @param name string|string[] Name(s) of client(s) to enable.
|
||||||
|
--- @param enable? boolean `true|nil` to enable, `false` to disable.
|
||||||
|
function lsp.enable(name, enable)
|
||||||
|
validate('name', name, { 'string', 'table' })
|
||||||
|
|
||||||
|
local names = vim._ensure_list(name) --[[@as string[] ]]
|
||||||
|
for _, nm in ipairs(names) do
|
||||||
|
if nm == '*' then
|
||||||
|
error('Invalid name')
|
||||||
|
end
|
||||||
|
lsp._enabled_configs[nm] = enable == false and nil or {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if not next(lsp._enabled_configs) then
|
||||||
|
if lsp_enable_autocmd_id then
|
||||||
|
api.nvim_del_autocmd(lsp_enable_autocmd_id)
|
||||||
|
lsp_enable_autocmd_id = nil
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Only ever create autocmd once to reuse computation of config merging.
|
||||||
|
lsp_enable_autocmd_id = lsp_enable_autocmd_id
|
||||||
|
or api.nvim_create_autocmd('FileType', {
|
||||||
|
group = api.nvim_create_augroup('nvim.lsp.enable', {}),
|
||||||
|
callback = function(args)
|
||||||
|
lsp_enable_callback(args.buf)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
--- @class vim.lsp.start.Opts
|
--- @class vim.lsp.start.Opts
|
||||||
--- @inlinedoc
|
--- @inlinedoc
|
||||||
---
|
---
|
||||||
@ -334,6 +568,8 @@ end
|
|||||||
---
|
---
|
||||||
--- Suppress error reporting if the LSP server fails to start (default false).
|
--- Suppress error reporting if the LSP server fails to start (default false).
|
||||||
--- @field silent? boolean
|
--- @field silent? boolean
|
||||||
|
---
|
||||||
|
--- @field package _root_markers? string[]
|
||||||
|
|
||||||
--- Create a new LSP client and start a language server or reuses an already
|
--- Create a new LSP client and start a language server or reuses an already
|
||||||
--- running client if one is found matching `name` and `root_dir`.
|
--- running client if one is found matching `name` and `root_dir`.
|
||||||
@ -379,6 +615,11 @@ function lsp.start(config, opts)
|
|||||||
local reuse_client = opts.reuse_client or reuse_client_default
|
local reuse_client = opts.reuse_client or reuse_client_default
|
||||||
local bufnr = vim._resolve_bufnr(opts.bufnr)
|
local bufnr = vim._resolve_bufnr(opts.bufnr)
|
||||||
|
|
||||||
|
if not config.root_dir and opts._root_markers then
|
||||||
|
config = vim.deepcopy(config)
|
||||||
|
config.root_dir = vim.fs.root(bufnr, opts._root_markers)
|
||||||
|
end
|
||||||
|
|
||||||
for _, client in pairs(all_clients) do
|
for _, client in pairs(all_clients) do
|
||||||
if reuse_client(client, config) then
|
if reuse_client(client, config) then
|
||||||
if opts.attach == false then
|
if opts.attach == false then
|
||||||
@ -387,9 +628,8 @@ function lsp.start(config, opts)
|
|||||||
|
|
||||||
if lsp.buf_attach_client(bufnr, client.id) then
|
if lsp.buf_attach_client(bufnr, client.id) then
|
||||||
return client.id
|
return client.id
|
||||||
else
|
|
||||||
return nil
|
|
||||||
end
|
end
|
||||||
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -398,7 +638,7 @@ function lsp.start(config, opts)
|
|||||||
if not opts.silent then
|
if not opts.silent then
|
||||||
vim.notify(err, vim.log.levels.WARN)
|
vim.notify(err, vim.log.levels.WARN)
|
||||||
end
|
end
|
||||||
return nil
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if opts.attach == false then
|
if opts.attach == false then
|
||||||
@ -408,8 +648,6 @@ function lsp.start(config, opts)
|
|||||||
if client_id and lsp.buf_attach_client(bufnr, client_id) then
|
if client_id and lsp.buf_attach_client(bufnr, client_id) then
|
||||||
return client_id
|
return client_id
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Consumes the latest progress messages from all clients and formats them as a string.
|
--- Consumes the latest progress messages from all clients and formats them as a string.
|
||||||
@ -1275,7 +1513,7 @@ end
|
|||||||
--- and the value is a function which is called if any LSP action
|
--- and the value is a function which is called if any LSP action
|
||||||
--- (code action, code lenses, ...) triggers the command.
|
--- (code action, code lenses, ...) triggers the command.
|
||||||
---
|
---
|
||||||
--- If a LSP response contains a command for which no matching entry is
|
--- If an LSP response contains a command for which no matching entry is
|
||||||
--- available in this registry, the command will be executed via the LSP server
|
--- available in this registry, the command will be executed via the LSP server
|
||||||
--- using `workspace/executeCommand`.
|
--- using `workspace/executeCommand`.
|
||||||
---
|
---
|
||||||
|
@ -359,16 +359,6 @@ local function get_name(id, config)
|
|||||||
return tostring(id)
|
return tostring(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @generic T
|
|
||||||
--- @param x elem_or_list<T>?
|
|
||||||
--- @return T[]
|
|
||||||
local function ensure_list(x)
|
|
||||||
if type(x) == 'table' then
|
|
||||||
return x
|
|
||||||
end
|
|
||||||
return { x }
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @nodoc
|
--- @nodoc
|
||||||
--- @param config vim.lsp.ClientConfig
|
--- @param config vim.lsp.ClientConfig
|
||||||
--- @return vim.lsp.Client?
|
--- @return vim.lsp.Client?
|
||||||
@ -395,13 +385,13 @@ function Client.create(config)
|
|||||||
settings = config.settings or {},
|
settings = config.settings or {},
|
||||||
flags = config.flags or {},
|
flags = config.flags or {},
|
||||||
get_language_id = config.get_language_id or default_get_language_id,
|
get_language_id = config.get_language_id or default_get_language_id,
|
||||||
capabilities = config.capabilities or lsp.protocol.make_client_capabilities(),
|
capabilities = config.capabilities,
|
||||||
workspace_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir),
|
workspace_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir),
|
||||||
root_dir = config.root_dir,
|
root_dir = config.root_dir,
|
||||||
_before_init_cb = config.before_init,
|
_before_init_cb = config.before_init,
|
||||||
_on_init_cbs = ensure_list(config.on_init),
|
_on_init_cbs = vim._ensure_list(config.on_init),
|
||||||
_on_exit_cbs = ensure_list(config.on_exit),
|
_on_exit_cbs = vim._ensure_list(config.on_exit),
|
||||||
_on_attach_cbs = ensure_list(config.on_attach),
|
_on_attach_cbs = vim._ensure_list(config.on_attach),
|
||||||
_on_error_cb = config.on_error,
|
_on_error_cb = config.on_error,
|
||||||
_trace = get_trace(config.trace),
|
_trace = get_trace(config.trace),
|
||||||
|
|
||||||
@ -417,6 +407,9 @@ function Client.create(config)
|
|||||||
messages = { name = name, messages = {}, progress = {}, status = {} },
|
messages = { name = name, messages = {}, progress = {}, status = {} },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.capabilities =
|
||||||
|
vim.tbl_deep_extend('force', lsp.protocol.make_client_capabilities(), self.capabilities or {})
|
||||||
|
|
||||||
--- @class lsp.DynamicCapabilities
|
--- @class lsp.DynamicCapabilities
|
||||||
--- @nodoc
|
--- @nodoc
|
||||||
self.dynamic_capabilities = {
|
self.dynamic_capabilities = {
|
||||||
|
@ -28,42 +28,45 @@ local function check_log()
|
|||||||
report_fn(string.format('Log size: %d KB', log_size / 1000))
|
report_fn(string.format('Log size: %d KB', log_size / 1000))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @param f function
|
||||||
|
--- @return string
|
||||||
|
local function func_tostring(f)
|
||||||
|
local info = debug.getinfo(f, 'S')
|
||||||
|
return ('<function %s:%s>'):format(info.source, info.linedefined)
|
||||||
|
end
|
||||||
|
|
||||||
local function check_active_clients()
|
local function check_active_clients()
|
||||||
vim.health.start('vim.lsp: Active Clients')
|
vim.health.start('vim.lsp: Active Clients')
|
||||||
local clients = vim.lsp.get_clients()
|
local clients = vim.lsp.get_clients()
|
||||||
if next(clients) then
|
if next(clients) then
|
||||||
for _, client in pairs(clients) do
|
for _, client in pairs(clients) do
|
||||||
local cmd ---@type string
|
local cmd ---@type string
|
||||||
if type(client.config.cmd) == 'table' then
|
local ccmd = client.config.cmd
|
||||||
cmd = table.concat(client.config.cmd --[[@as table]], ' ')
|
if type(ccmd) == 'table' then
|
||||||
elseif type(client.config.cmd) == 'function' then
|
cmd = vim.inspect(ccmd)
|
||||||
cmd = tostring(client.config.cmd)
|
elseif type(ccmd) == 'function' then
|
||||||
|
cmd = func_tostring(ccmd)
|
||||||
end
|
end
|
||||||
local dirs_info ---@type string
|
local dirs_info ---@type string
|
||||||
if client.workspace_folders and #client.workspace_folders > 1 then
|
if client.workspace_folders and #client.workspace_folders > 1 then
|
||||||
dirs_info = string.format(
|
local wfolders = {} --- @type string[]
|
||||||
' Workspace folders:\n %s',
|
for _, dir in ipairs(client.workspace_folders) do
|
||||||
vim
|
wfolders[#wfolders + 1] = dir.name
|
||||||
.iter(client.workspace_folders)
|
end
|
||||||
---@param folder lsp.WorkspaceFolder
|
dirs_info = ('- Workspace folders:\n %s'):format(table.concat(wfolders, '\n '))
|
||||||
:map(function(folder)
|
|
||||||
return folder.name
|
|
||||||
end)
|
|
||||||
:join('\n ')
|
|
||||||
)
|
|
||||||
else
|
else
|
||||||
dirs_info = string.format(
|
dirs_info = string.format(
|
||||||
' Root directory: %s',
|
'- Root directory: %s',
|
||||||
client.root_dir and vim.fn.fnamemodify(client.root_dir, ':~')
|
client.root_dir and vim.fn.fnamemodify(client.root_dir, ':~')
|
||||||
) or nil
|
) or nil
|
||||||
end
|
end
|
||||||
report_info(table.concat({
|
report_info(table.concat({
|
||||||
string.format('%s (id: %d)', client.name, client.id),
|
string.format('%s (id: %d)', client.name, client.id),
|
||||||
dirs_info,
|
dirs_info,
|
||||||
string.format(' Command: %s', cmd),
|
string.format('- Command: %s', cmd),
|
||||||
string.format(' Settings: %s', vim.inspect(client.settings, { newline = '\n ' })),
|
string.format('- Settings: %s', vim.inspect(client.settings, { newline = '\n ' })),
|
||||||
string.format(
|
string.format(
|
||||||
' Attached buffers: %s',
|
'- Attached buffers: %s',
|
||||||
vim.iter(pairs(client.attached_buffers)):map(tostring):join(', ')
|
vim.iter(pairs(client.attached_buffers)):map(tostring):join(', ')
|
||||||
),
|
),
|
||||||
}, '\n'))
|
}, '\n'))
|
||||||
@ -174,10 +177,45 @@ local function check_position_encodings()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function check_enabled_configs()
|
||||||
|
vim.health.start('vim.lsp: Enabled Configurations')
|
||||||
|
|
||||||
|
for name in vim.spairs(vim.lsp._enabled_configs) do
|
||||||
|
local config = vim.lsp._resolve_config(name)
|
||||||
|
local text = {} --- @type string[]
|
||||||
|
text[#text + 1] = ('%s:'):format(name)
|
||||||
|
for k, v in
|
||||||
|
vim.spairs(config --[[@as table<string,any>]])
|
||||||
|
do
|
||||||
|
local v_str --- @type string?
|
||||||
|
if k == 'name' then
|
||||||
|
v_str = nil
|
||||||
|
elseif k == 'filetypes' or k == 'root_markers' then
|
||||||
|
v_str = table.concat(v, ', ')
|
||||||
|
elseif type(v) == 'function' then
|
||||||
|
v_str = func_tostring(v)
|
||||||
|
else
|
||||||
|
v_str = vim.inspect(v, { newline = '\n ' })
|
||||||
|
end
|
||||||
|
|
||||||
|
if k == 'cmd' and type(v) == 'table' and vim.fn.executable(v[1]) == 0 then
|
||||||
|
report_warn(("'%s' is not executable. Configuration will not be used."):format(v[1]))
|
||||||
|
end
|
||||||
|
|
||||||
|
if v_str then
|
||||||
|
text[#text + 1] = ('- %s: %s'):format(k, v_str)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
text[#text + 1] = ''
|
||||||
|
report_info(table.concat(text, '\n'))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--- Performs a healthcheck for LSP
|
--- Performs a healthcheck for LSP
|
||||||
function M.check()
|
function M.check()
|
||||||
check_log()
|
check_log()
|
||||||
check_active_clients()
|
check_active_clients()
|
||||||
|
check_enabled_configs()
|
||||||
check_watcher()
|
check_watcher()
|
||||||
check_position_encodings()
|
check_position_encodings()
|
||||||
end
|
end
|
||||||
|
@ -1409,4 +1409,14 @@ function vim._resolve_bufnr(bufnr)
|
|||||||
return bufnr
|
return bufnr
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @generic T
|
||||||
|
--- @param x elem_or_list<T>?
|
||||||
|
--- @return T[]
|
||||||
|
function vim._ensure_list(x)
|
||||||
|
if type(x) == 'table' then
|
||||||
|
return x
|
||||||
|
end
|
||||||
|
return { x }
|
||||||
|
end
|
||||||
|
|
||||||
return vim
|
return vim
|
||||||
|
@ -515,6 +515,8 @@ local function inline_type(obj, classes)
|
|||||||
elseif desc == '' then
|
elseif desc == '' then
|
||||||
if ty_islist then
|
if ty_islist then
|
||||||
desc = desc .. 'A list of objects with the following fields:'
|
desc = desc .. 'A list of objects with the following fields:'
|
||||||
|
elseif cls.parent then
|
||||||
|
desc = desc .. fmt('Extends |%s| with the additional fields:', cls.parent)
|
||||||
else
|
else
|
||||||
desc = desc .. 'A table with the following fields:'
|
desc = desc .. 'A table with the following fields:'
|
||||||
end
|
end
|
||||||
|
@ -6755,6 +6755,7 @@ return {
|
|||||||
indent/ indent scripts |indent-expression|
|
indent/ indent scripts |indent-expression|
|
||||||
keymap/ key mapping files |mbyte-keymap|
|
keymap/ key mapping files |mbyte-keymap|
|
||||||
lang/ menu translations |:menutrans|
|
lang/ menu translations |:menutrans|
|
||||||
|
lsp/ LSP client configurations |lsp-config|
|
||||||
lua/ |Lua| plugins
|
lua/ |Lua| plugins
|
||||||
menu.vim GUI menus |menu.vim|
|
menu.vim GUI menus |menu.vim|
|
||||||
pack/ packages |:packadd|
|
pack/ packages |:packadd|
|
||||||
|
@ -6098,15 +6098,6 @@ describe('LSP', function()
|
|||||||
end
|
end
|
||||||
|
|
||||||
eq(is_os('mac') or is_os('win'), check_registered(nil)) -- start{_client}() defaults to make_client_capabilities().
|
eq(is_os('mac') or is_os('win'), check_registered(nil)) -- start{_client}() defaults to make_client_capabilities().
|
||||||
eq(false, check_registered(vim.empty_dict()))
|
|
||||||
eq(
|
|
||||||
false,
|
|
||||||
check_registered({
|
|
||||||
workspace = {
|
|
||||||
ignoreMe = true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
eq(
|
eq(
|
||||||
false,
|
false,
|
||||||
check_registered({
|
check_registered({
|
||||||
@ -6129,4 +6120,88 @@ describe('LSP', function()
|
|||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe('vim.lsp.config() and vim.lsp.enable()', function()
|
||||||
|
it('can merge settings from "*"', function()
|
||||||
|
eq(
|
||||||
|
{
|
||||||
|
name = 'foo',
|
||||||
|
cmd = { 'foo' },
|
||||||
|
root_markers = { '.git' },
|
||||||
|
},
|
||||||
|
exec_lua(function()
|
||||||
|
vim.lsp.config('*', { root_markers = { '.git' } })
|
||||||
|
vim.lsp.config('foo', { cmd = { 'foo' } })
|
||||||
|
|
||||||
|
return vim.lsp._resolve_config('foo')
|
||||||
|
end)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('sets up an autocmd', function()
|
||||||
|
eq(
|
||||||
|
1,
|
||||||
|
exec_lua(function()
|
||||||
|
vim.lsp.config('foo', {
|
||||||
|
cmd = { 'foo' },
|
||||||
|
root_markers = { '.foorc' },
|
||||||
|
})
|
||||||
|
vim.lsp.enable('foo')
|
||||||
|
return #vim.api.nvim_get_autocmds({
|
||||||
|
group = 'nvim.lsp.enable',
|
||||||
|
event = 'FileType',
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('attaches to buffers', function()
|
||||||
|
exec_lua(create_server_definition)
|
||||||
|
|
||||||
|
local tmp1 = t.tmpname(true)
|
||||||
|
local tmp2 = t.tmpname(true)
|
||||||
|
|
||||||
|
exec_lua(function()
|
||||||
|
local server = _G._create_server({
|
||||||
|
handlers = {
|
||||||
|
initialize = function(_, _, callback)
|
||||||
|
callback(nil, { capabilities = {} })
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.lsp.config('foo', {
|
||||||
|
cmd = server.cmd,
|
||||||
|
filetypes = { 'foo' },
|
||||||
|
root_markers = { '.foorc' },
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.lsp.config('bar', {
|
||||||
|
cmd = server.cmd,
|
||||||
|
filetypes = { 'bar' },
|
||||||
|
root_markers = { '.foorc' },
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.lsp.enable('foo')
|
||||||
|
vim.lsp.enable('bar')
|
||||||
|
|
||||||
|
vim.cmd.edit(tmp1)
|
||||||
|
vim.bo.filetype = 'foo'
|
||||||
|
_G.foo_buf = vim.api.nvim_get_current_buf()
|
||||||
|
|
||||||
|
vim.cmd.edit(tmp2)
|
||||||
|
vim.bo.filetype = 'bar'
|
||||||
|
_G.bar_buf = vim.api.nvim_get_current_buf()
|
||||||
|
end)
|
||||||
|
|
||||||
|
eq(
|
||||||
|
{ 1, 'foo', 1, 'bar' },
|
||||||
|
exec_lua(function()
|
||||||
|
local foos = vim.lsp.get_clients({ bufnr = assert(_G.foo_buf) })
|
||||||
|
local bars = vim.lsp.get_clients({ bufnr = assert(_G.bar_buf) })
|
||||||
|
return { #foos, foos[1].name, #bars, bars[1].name }
|
||||||
|
end)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
Loading…
Reference in New Issue
Block a user