From 32f2217feab778860288ab5076a59e911db1100b Mon Sep 17 00:00:00 2001 From: Andreas Louv Date: Wed, 12 Apr 2023 11:00:10 +0200 Subject: [PATCH] Make the code ready for adding support for "features" --- README.md | 1 + autoload/lsp/buffer.vim | 61 ++++++++++++++++++++++++++++++-------- autoload/lsp/lsp.vim | 7 ++++- autoload/lsp/lspserver.vim | 3 +- doc/lsp.txt | 46 ++++++++++++++++++++++++++++ 5 files changed, 104 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 2abe07e..54bdfee 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,7 @@ path|complete path to the LSP server executable (without any arguments). args|a list of command-line arguments passed to the LSP server. Each argument is a separate List item. initializationOptions|User provided initialization options. May be of any type. For example the *intelephense* PHP language server accept several options here with the License Key among others. customNotificationHandlers|A dictionary of notifications and functions that can be specified to add support for custom language server notifications. +features|A dictionary of booleans that can be specified to toggle what things a given LSP is providing (folding, goto definition, etc) This is useful when running multiple servers in one buffer. The LSP servers are added using the LspAddServer() function. This function accepts a list of LSP servers with the above information. diff --git a/autoload/lsp/buffer.vim b/autoload/lsp/buffer.vim index bdb69aa..fbe0ec3 100644 --- a/autoload/lsp/buffer.vim +++ b/autoload/lsp/buffer.vim @@ -30,9 +30,9 @@ export def BufLspServerRemove(bnr: number, lspserver: dict) endif enddef -# Returns the LSP server for the buffer 'bnr'. Returns an empty dict if the -# server is not found. -export def BufLspServerGet(bnr: number): dict +# Returns the LSP server for the buffer 'bnr' and optionally 'domain'. +# Returns an empty dict if the server is not found. +export def BufLspServerGet(bnr: number, domain: string = null_string): dict if !bufnrToServers->has_key(bnr) return {} endif @@ -41,8 +41,44 @@ export def BufLspServerGet(bnr: number): dict return {} endif - # TODO implement logic to compute which server to return - return bufnrToServers[bnr][0] + if domain == null_string + return bufnrToServers[bnr][0] + endif + + var SupportedCheckFns = { + } + + if !SupportedCheckFns->has_key(domain) + # If this happns it is a programming error, and should be fixed in the source code + :throw $'Error: ''{domain}'' is not a valid domain' + return {} + endif + + var SupportedCheckFn = SupportedCheckFns[domain] + + var possibleLSPs: list> = [] + + for lspserver in bufnrToServers[bnr] + if !SupportedCheckFn(lspserver) + continue + endif + + possibleLSPs->add(lspserver) + endfor + + if possibleLSPs->len() == 0 + return {} + endif + + # LSP server is configured to be a provider for 'domain' + for lspserver in possibleLSPs + if lspserver.features->has_key(domain) && lspserver.features[domain] + return lspserver + endif + endfor + + # Return the first LSP server that supports 'domain' + return possibleLSPs[0] enddef # Returns the LSP server for the buffer 'bnr' and with ID 'id'. Returns an empty @@ -71,10 +107,10 @@ export def BufLspServersGet(bnr: number): list> return bufnrToServers[bnr] enddef -# Returns the LSP server for the current buffer. Returns an empty dict if the -# server is not found. -export def CurbufGetServer(): dict - return BufLspServerGet(bufnr()) +# Returns the LSP server for the current buffer with the optionally 'domain'. +# Returns an empty dict if the server is not found. +export def CurbufGetServer(domain: string = null_string): dict + return BufLspServerGet(bufnr(), domain) enddef # Returns the LSP servers for the current buffer. Returns an empty list if the @@ -89,15 +125,16 @@ export def BufHasLspServer(bnr: number): bool return !lspserver->empty() enddef -# Returns the LSP server for the current buffer if it is running and is ready. +# Returns the LSP server for the current buffer with the optinally 'domain' if +# it is running and is ready. # Returns an empty dict if the server is not found or is not ready. -export def CurbufGetServerChecked(): dict +export def CurbufGetServerChecked(domain: string = null_string): dict var fname: string = @% if fname == '' return {} endif - var lspserver: dict = CurbufGetServer() + var lspserver: dict = CurbufGetServer(domain) if lspserver->empty() util.ErrMsg($'Error: Language server for "{&filetype}" file type is not found') return {} diff --git a/autoload/lsp/lsp.vim b/autoload/lsp/lsp.vim index 88e0b08..27674f9 100644 --- a/autoload/lsp/lsp.vim +++ b/autoload/lsp/lsp.vim @@ -598,6 +598,11 @@ export def AddServer(serverList: list>) customNotificationHandlers = server.customNotificationHandlers endif + var features: dict = {} + if server->has_key('features') + features = server.features + endif + if server.omnicompl->type() != v:t_bool util.ErrMsg($'Error: Setting of omnicompl {server.omnicompl} is not a Boolean') return @@ -632,7 +637,7 @@ export def AddServer(serverList: list>) server.workspaceConfig, server.rootSearch, customNotificationHandlers, - server.debug) + features, server.debug) var ftypes = server.filetype if ftypes->type() == v:t_string diff --git a/autoload/lsp/lspserver.vim b/autoload/lsp/lspserver.vim index 74e0914..bbc852f 100644 --- a/autoload/lsp/lspserver.vim +++ b/autoload/lsp/lspserver.vim @@ -1476,7 +1476,7 @@ export def NewLspServer(name_arg: string, path_arg: string, args: list, workspaceConfig: dict, rootSearchFiles: list, customNotificationHandlers: dict, - debug_arg: bool): dict + features: dict, debug_arg: bool): dict var lspserver: dict = { id: GetUniqueServerId(), name: name_arg, @@ -1485,6 +1485,7 @@ export def NewLspServer(name_arg: string, path_arg: string, args: list, syncInit: isSync, initializationOptions: initializationOptions, customNotificationHandlers: customNotificationHandlers, + features: features, running: false, ready: false, job: v:none, diff --git a/doc/lsp.txt b/doc/lsp.txt index 20f4e7e..73adf21 100644 --- a/doc/lsp.txt +++ b/doc/lsp.txt @@ -299,6 +299,11 @@ Aditionally the following configurations can be made: } }]) < + *lsp-cfg-features* + features + (Optional) toggle which features should be enabled for a + given langauge server. See |lsp-multiple-servers| for + more information. *lsp-cfg-omnicompl* omnicompl (Optional) a boolean value that enables (true) or disables (false) omni-completion for this file @@ -1320,4 +1325,45 @@ In the completion popup, will show something like this: > | createIfNotExists method() | | ... | < +============================================================================== +16. Multiple Language Servers for a buffer *lsp-multiple-servers* + +It's possible to run multiple language servers for a given buffer. + +By default the language server defined first will be used for as much as it +supports, then the next and so on. With the exception that diagnostics from all +running language servers will be joined together. This means that you can define +a language server that only supports a subset of features at first and then +define the general purpose language server after it: > + + vim9script + g:LspAddServer([ + # This language server reports that it only supports + # textDocument/documentFormatting, so it will be used + # for :LspFormat but nothing else. + { + filetype: ['html'], + path: 'html-pretty-lsp', + args: ['--stdio'] + }, + # This language server also supports + # textDocument/documentFormatting, but since it's been + # defined later, the one above will be used instead. + # However this server also supports + # textDocument/definition, textDocument/declaration, + # etc, so it will be used for :LspGotoDefinition, + # :LspGotoDeclaration, etc + { + filetype: ['html'], + path: 'html-language-server', + args: ['--stdio'] + } + ]) +< +By proving the configuration |lsp-cfg-features| it's possible specify which +servers should be used for a given method. The following flags are supported +> +#{ +} +< vim:tw=78:ts=8:noet:ft=help:norl: -- 2.48.1