From 643f47142c3187079c3cd1083d5e0a427c9260e9 Mon Sep 17 00:00:00 2001 From: Daniel Viberg Date: Fri, 15 Mar 2024 09:57:20 +0100 Subject: [PATCH 1/6] Working mult server compl --- autoload/lsp/buffer.vim | 32 +++++++++++---- autoload/lsp/completion.vim | 78 ++++++++++++++++++------------------- autoload/lsp/lsp.vim | 20 +++++----- autoload/lsp/lspserver.vim | 2 + 4 files changed, 76 insertions(+), 56 deletions(-) diff --git a/autoload/lsp/buffer.vim b/autoload/lsp/buffer.vim index 5a076548..8f1fae57 100644 --- a/autoload/lsp/buffer.vim +++ b/autoload/lsp/buffer.vim @@ -15,7 +15,6 @@ export def BufLspServerSet(bnr: number, lspserver: dict) if !bufnrToServers->has_key(bnr) bufnrToServers[bnr] = [] endif - bufnrToServers[bnr]->add(lspserver) enddef @@ -135,12 +134,30 @@ enddef # Returns the LSP servers for the buffer "bnr". Returns an empty list if the # servers are not found. -export def BufLspServersGet(bnr: number): list> +export def BufLspServersGet(bnr: number, feature: string = null_string): list> if !bufnrToServers->has_key(bnr) return [] endif - return bufnrToServers[bnr] + if feature == null_string + return bufnrToServers[bnr] + else + var SupportedCheckFn = SupportedCheckFns[feature] + var possibleLSPs: list> = [] + for lspserver in bufnrToServers[bnr] + if !lspserver.ready || !SupportedCheckFn(lspserver) + continue + endif + possibleLSPs->add(lspserver) + endfor + + var servers: list> = [] + for lspserver in possibleLSPs + # var has_feature: bool = lspserver.features->get(feature, false) + servers->add(lspserver) + endfor + return servers + endif enddef # Returns the LSP server for the current buffer with the optionally "feature". @@ -151,13 +168,12 @@ enddef # Returns the LSP servers for the current buffer. Returns an empty list if the # servers are not found. -export def CurbufGetServers(): list> - return BufLspServersGet(bufnr()) +export def CurbufGetServers(feature: string = null_string): list> + return BufLspServersGet(bufnr(), feature) enddef -export def BufHasLspServer(bnr: number): bool - var lspserver = BufLspServerGet(bnr) - +export def BufHasLspServer(bnr: number, id: number): bool + var lspserver = BufLspServerGetById(bnr, id) return !lspserver->empty() enddef diff --git a/autoload/lsp/completion.vim b/autoload/lsp/completion.vim index 71984d82..6e4ff9c6 100644 --- a/autoload/lsp/completion.vim +++ b/autoload/lsp/completion.vim @@ -543,21 +543,23 @@ enddef # Insert mode completion handler. Used when 24x7 completion is enabled # (default). def LspComplete() - var lspserver: dict = buf.CurbufGetServer('completion') - if lspserver->empty() || !lspserver.running || !lspserver.ready - return - endif + var lspservers: list> = buf.CurbufGetServers('completion') + for lspserver in lspservers + if lspserver->empty() || !lspserver.running || !lspserver.ready + return + endif - var [triggerKind, triggerChar] = GetTriggerAttributes(lspserver) - if triggerKind < 0 - return - endif + var [triggerKind, triggerChar] = GetTriggerAttributes(lspserver) + if triggerKind < 0 + return + endif - # first send all the changes in the current buffer to the LSP server - listener_flush() + # first send all the changes in the current buffer to the LSP server + listener_flush() - # initiate a request to LSP server to get list of completions - lspserver.getCompletion(triggerKind, triggerChar) + # initiate a request to LSP server to get list of completions + lspserver.getCompletion(triggerKind, triggerChar) + endfor enddef # Lazy complete documentation handler @@ -602,36 +604,35 @@ enddef # complete done handler (LSP server-initiated actions after completion) def LspCompleteDone(bnr: number) var lspserver: dict = buf.BufLspServerGet(bnr, 'completion') - if lspserver->empty() - return - endif - - if v:completed_item->type() != v:t_dict - return - endif + if lspserver->empty() + return + endif - var completionData: any = v:completed_item->get('user_data', '') - if completionData->type() != v:t_dict - || !opt.lspOptions.completionTextEdit - return - endif + if v:completed_item->type() != v:t_dict + return + endif - if !completionData->has_key('additionalTextEdits') - # Some language servers (e.g. typescript) delay the computation of the - # additional text edits. So try to resolve the completion item now to get - # the text edits. - completionData = lspserver.resolveCompletion(completionData, true) - endif - if !completionData->get('additionalTextEdits', {})->empty() - textedit.ApplyTextEdits(bnr, completionData.additionalTextEdits) - endif + var completionData: any = v:completed_item->get('user_data', '') + if completionData->type() != v:t_dict + || !opt.lspOptions.completionTextEdit + return + endif - if completionData->has_key('command') - # Some language servers (e.g. haskell-language-server) want to apply - # additional commands after completion. - codeaction.DoCommand(lspserver, completionData.command) - endif + if !completionData->has_key('additionalTextEdits') + # Some language servers (e.g. typescript) delay the computation of the + # additional text edits. So try to resolve the completion item now to get + # the text edits. + completionData = lspserver.resolveCompletion(completionData, true) + endif + if !completionData->get('additionalTextEdits', {})->empty() + textedit.ApplyTextEdits(bnr, completionData.additionalTextEdits) + endif + if completionData->has_key('command') + # Some language servers (e.g. haskell-language-server) want to apply + # additional commands after completion. + codeaction.DoCommand(lspserver, completionData.command) + endif enddef # Initialize buffer-local completion options and autocmds @@ -692,7 +693,6 @@ export def BufferInit(lspserver: dict, bnr: number, ftype: string) event: 'CompleteDone', group: 'LSPBufferAutocmds', cmd: $'LspCompleteDone({bnr})'}) - autocmd_add(acmds) enddef diff --git a/autoload/lsp/lsp.vim b/autoload/lsp/lsp.vim index 3dfd7ab7..d8e8bec2 100644 --- a/autoload/lsp/lsp.vim +++ b/autoload/lsp/lsp.vim @@ -412,10 +412,12 @@ def BufferInit(lspserverId: number, bnr: number): void # It's only possible to initialize the features when the server # capabilities of all the registered language servers for this file type # are known. - var completionServer = buf.BufLspServerGet(bnr, 'completion') - if !completionServer->empty() && lspsrv.id == completionServer.id - completion.BufferInit(lspsrv, bnr, ftype) - endif + var completionServers = buf.BufLspServersGet(bnr, 'completion') + for compSrv in completionServers + if !compSrv->empty() && lspsrv.id == compSrv.id + completion.BufferInit(lspsrv, bnr, ftype) + endif + endfor var signatureServer = buf.BufLspServerGet(bnr, 'signatureHelp') if !signatureServer->empty() && lspsrv.id == signatureServer.id @@ -441,11 +443,6 @@ enddef # A new buffer is opened. If LSP is supported for this buffer, then add it export def AddFile(bnr: number): void - if buf.BufHasLspServer(bnr) - # LSP server for this buffer is already initialized and running - return - endif - # Skip remote files if util.LspUriRemote(bnr->bufname()->fnamemodify(':p')) return @@ -455,11 +452,16 @@ export def AddFile(bnr: number): void if ftype->empty() return endif + var lspservers: list> = LspGetServers(bnr, ftype) if lspservers->empty() return endif + for lspserver in lspservers + if buf.BufHasLspServer(bnr, lspserver.id) + continue + endif if !lspserver.running if !lspInitializedOnce LspInitOnce() diff --git a/autoload/lsp/lspserver.vim b/autoload/lsp/lspserver.vim index bfbdd77d..37428b7d 100644 --- a/autoload/lsp/lspserver.vim +++ b/autoload/lsp/lspserver.vim @@ -727,6 +727,7 @@ def GetCompletion(lspserver: dict, triggerKind_arg: number, triggerChar: st # interface CompletionContext params.context = {triggerKind: triggerKind_arg, triggerCharacter: triggerChar} + echomsg 'request' lspserver.rpc_a('textDocument/completion', params, completion.CompletionReply) enddef @@ -1856,6 +1857,7 @@ enddef export def NewLspServer(serverParams: dict): dict var lspserver: dict = { + now: '', id: GetUniqueServerId(), name: serverParams.name, path: serverParams.path, From 2dbf25bb6c1e7c74b3f93a0163a3d061b0d43431 Mon Sep 17 00:00:00 2001 From: Daniel Viberg Date: Fri, 15 Mar 2024 10:14:08 +0100 Subject: [PATCH 2/6] Removed time measure prop --- autoload/lsp/lspserver.vim | 2 -- 1 file changed, 2 deletions(-) diff --git a/autoload/lsp/lspserver.vim b/autoload/lsp/lspserver.vim index 37428b7d..bfbdd77d 100644 --- a/autoload/lsp/lspserver.vim +++ b/autoload/lsp/lspserver.vim @@ -727,7 +727,6 @@ def GetCompletion(lspserver: dict, triggerKind_arg: number, triggerChar: st # interface CompletionContext params.context = {triggerKind: triggerKind_arg, triggerCharacter: triggerChar} - echomsg 'request' lspserver.rpc_a('textDocument/completion', params, completion.CompletionReply) enddef @@ -1857,7 +1856,6 @@ enddef export def NewLspServer(serverParams: dict): dict var lspserver: dict = { - now: '', id: GetUniqueServerId(), name: serverParams.name, path: serverParams.path, From 00d6c2fa2982d5c8548eb36830c3d2659e519517 Mon Sep 17 00:00:00 2001 From: Daniel Viberg Date: Fri, 15 Mar 2024 12:35:21 +0100 Subject: [PATCH 3/6] Tweaked LspResolver to be server id dependent --- autoload/lsp/buffer.vim | 22 ++++++++++++++++------ autoload/lsp/completion.vim | 13 ++++++------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/autoload/lsp/buffer.vim b/autoload/lsp/buffer.vim index 8f1fae57..4f620a1e 100644 --- a/autoload/lsp/buffer.vim +++ b/autoload/lsp/buffer.vim @@ -177,6 +177,13 @@ export def BufHasLspServer(bnr: number, id: number): bool return !lspserver->empty() enddef +# Returns the LSP server for the current buffer, given the server id +# and with the optionally "feature" if it is running and is ready. +export def CurbufGetServerByIdChecked(id: number, feature: string = null_string): dict + var lspserver = BufLspServerGetById(bufnr(), id) + return CurbufGetServerChecks(lspserver, feature) +enddef + # Returns the LSP server for the current buffer with the optinally "feature" if # it is running and is ready. # Returns an empty dict if the server is not found or is not ready. @@ -185,9 +192,13 @@ export def CurbufGetServerChecked(feature: string = null_string): dict if fname->empty() || &filetype->empty() return {} endif - var lspserver: dict = CurbufGetServer(feature) - if lspserver->empty() + return CurbufGetServerChecks(lspserver, feature) +enddef + +# Runs checks of the LSP +export def CurbufGetServerChecks(lspServer: dict, feature: string = null_string): dict + if lspServer->empty() if feature == null_string util.ErrMsg($'Language server for "{&filetype}" file type is not found') else @@ -195,16 +206,15 @@ export def CurbufGetServerChecked(feature: string = null_string): dict endif return {} endif - if !lspserver.running + if !lspServer.running util.ErrMsg($'Language server for "{&filetype}" file type is not running') return {} endif - if !lspserver.ready + if !lspServer.ready util.ErrMsg($'Language server for "{&filetype}" file type is not ready') return {} endif - - return lspserver + return lspServer enddef # vim: tabstop=8 shiftwidth=2 softtabstop=2 diff --git a/autoload/lsp/completion.vim b/autoload/lsp/completion.vim index 6e4ff9c6..57b6b443 100644 --- a/autoload/lsp/completion.vim +++ b/autoload/lsp/completion.vim @@ -563,18 +563,17 @@ def LspComplete() enddef # Lazy complete documentation handler -def LspResolve() - var lspserver: dict = buf.CurbufGetServerChecked('completion') +def LspResolve(servId: number) + var lspserver: dict = buf.CurbufGetServerByIdChecked(servId, 'completion') if lspserver->empty() - return + return endif - var item = v:event.completed_item if item->has_key('user_data') && !item.user_data->empty() if item.user_data->type() == v:t_dict && !item.user_data->has_key('documentation') - lspserver.resolveCompletion(item.user_data) + lspserver.resolveCompletion(item.user_data) else - ShowCompletionDocumentation(item.user_data) + ShowCompletionDocumentation(item.user_data) endif endif enddef @@ -680,7 +679,7 @@ export def BufferInit(lspserver: dict, bnr: number, ftype: string) acmds->add({bufnr: bnr, event: 'CompleteChanged', group: 'LSPBufferAutocmds', - cmd: 'LspResolve()'}) + cmd: $'LspResolve({lspserver.id})'}) endif acmds->add({bufnr: bnr, From bf8b92609cf13e059bd70d90ddbb53df9b50d9e2 Mon Sep 17 00:00:00 2001 From: Daniel Viberg Date: Sat, 16 Mar 2024 16:33:03 +0100 Subject: [PATCH 4/6] Fixed text complete server dependent --- autoload/lsp/completion.vim | 11 +++++++---- test/run_tests.sh | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/autoload/lsp/completion.vim b/autoload/lsp/completion.vim index 57b6b443..08905f66 100644 --- a/autoload/lsp/completion.vim +++ b/autoload/lsp/completion.vim @@ -601,8 +601,9 @@ def LspSetPopupFileType() enddef # complete done handler (LSP server-initiated actions after completion) -def LspCompleteDone(bnr: number) - var lspserver: dict = buf.BufLspServerGet(bnr, 'completion') +def LspCompleteDone(servId: number, bnr: number) + var lspserver: dict = buf.BufLspServerGetById(bnr, servId) + echomsg lspserver.name if lspserver->empty() return endif @@ -641,7 +642,9 @@ export def BufferInit(lspserver: dict, bnr: number, ftype: string) return endif - if !opt.lspOptions.autoComplete && !LspOmniComplEnabled(ftype) + if !opt.lspOptions.autoComplete && + !LspOmniComplEnabled(ftype) && + !opt.lspOptions.omniComplete # LSP auto/omni completion support is not enabled for this buffer return endif @@ -691,7 +694,7 @@ export def BufferInit(lspserver: dict, bnr: number, ftype: string) acmds->add({bufnr: bnr, event: 'CompleteDone', group: 'LSPBufferAutocmds', - cmd: $'LspCompleteDone({bnr})'}) + cmd: $'LspCompleteDone({lspserver.id}, {bnr})'}) autocmd_add(acmds) enddef diff --git a/test/run_tests.sh b/test/run_tests.sh index 1a1e69bf..6e99a69a 100755 --- a/test/run_tests.sh +++ b/test/run_tests.sh @@ -10,7 +10,7 @@ fi VIM_CMD="$VIMPRG -u NONE -U NONE -i NONE --noplugin -N --not-a-term" -TESTS="clangd_tests.vim tsserver_tests.vim gopls_tests.vim not_lspserver_related_tests.vim markdown_tests.vim rust_tests.vim" +TESTS="volar_tests.vim" RunTestsInFile() { testfile=$1 @@ -42,7 +42,7 @@ for encoding in "utf-8" "utf-16" "utf-32" do export LSP_OFFSET_ENCODING=$encoding echo "LSP offset encoding: $LSP_OFFSET_ENCODING" - RunTestsInFile clangd_offsetencoding.vim + # RunTestsInFile clangd_offsetencoding.vim done echo "SUCCESS: All the tests passed." From f49b03e6d26173b96f306b799fea5f40905bf841 Mon Sep 17 00:00:00 2001 From: Daniel Viberg Date: Sat, 16 Mar 2024 16:56:57 +0100 Subject: [PATCH 5/6] Reset tests --- test/run_tests.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/run_tests.sh b/test/run_tests.sh index 6e99a69a..1a1e69bf 100755 --- a/test/run_tests.sh +++ b/test/run_tests.sh @@ -10,7 +10,7 @@ fi VIM_CMD="$VIMPRG -u NONE -U NONE -i NONE --noplugin -N --not-a-term" -TESTS="volar_tests.vim" +TESTS="clangd_tests.vim tsserver_tests.vim gopls_tests.vim not_lspserver_related_tests.vim markdown_tests.vim rust_tests.vim" RunTestsInFile() { testfile=$1 @@ -42,7 +42,7 @@ for encoding in "utf-8" "utf-16" "utf-32" do export LSP_OFFSET_ENCODING=$encoding echo "LSP offset encoding: $LSP_OFFSET_ENCODING" - # RunTestsInFile clangd_offsetencoding.vim + RunTestsInFile clangd_offsetencoding.vim done echo "SUCCESS: All the tests passed." From 9210df3af7247b016a569565d5a6339a2fb75e95 Mon Sep 17 00:00:00 2001 From: Daniel Viberg Date: Sat, 16 Mar 2024 17:21:39 +0100 Subject: [PATCH 6/6] completion.vim - removed echomsg --- autoload/lsp/completion.vim | 1 - 1 file changed, 1 deletion(-) diff --git a/autoload/lsp/completion.vim b/autoload/lsp/completion.vim index 7c4fc37e..e840f88f 100644 --- a/autoload/lsp/completion.vim +++ b/autoload/lsp/completion.vim @@ -603,7 +603,6 @@ enddef # complete done handler (LSP server-initiated actions after completion) def LspCompleteDone(servId: number, bnr: number) var lspserver: dict = buf.BufLspServerGetById(bnr, servId) - echomsg lspserver.name if lspserver->empty() return endif