mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00: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:
		 Lewis Russell
					Lewis Russell
				
			
				
					committed by
					
						 Lewis Russell
						Lewis Russell
					
				
			
			
				
	
			
			
			 Lewis Russell
						Lewis Russell
					
				
			
						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) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user