From c4eca7c417945a684949342da8d0ab30e6c82b3a Mon Sep 17 00:00:00 2001 From: w0rp Date: Fri, 24 Aug 2018 13:16:58 +0100 Subject: [PATCH] Use one LSP connection per project --- autoload/ale/completion.vim | 8 +- autoload/ale/definition.vim | 12 +- autoload/ale/hover.vim | 12 +- autoload/ale/lsp.vim | 413 ++++++++---------- autoload/ale/lsp_linter.vim | 27 +- autoload/ale/references.vim | 12 +- .../test_lsp_completion_messages.vader | 26 +- test/lsp/test_did_save_event.vader | 15 +- test/lsp/test_lsp_command_formatting.vader | 6 +- test/lsp/test_lsp_connections.vader | 54 --- ...st_other_initialize_message_handling.vader | 120 ++--- test/test_find_references.vader | 22 +- test/test_go_to_definition.vader | 26 +- 13 files changed, 286 insertions(+), 467 deletions(-) diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index 33d9f658..abe0f56e 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -427,7 +427,6 @@ endfunction function! s:OnReady(linter, lsp_details, ...) abort let l:buffer = a:lsp_details.buffer let l:id = a:lsp_details.connection_id - let l:root = a:lsp_details.project_root " If we have sent a completion request already, don't send another. if b:ale_completion_info.request_id @@ -449,7 +448,7 @@ function! s:OnReady(linter, lsp_details, ...) abort else " Send a message saying the buffer has changed first, otherwise " completions won't know what text is nearby. - call ale#lsp#NotifyForChanges(l:id, l:root, l:buffer) + call ale#lsp#NotifyForChanges(l:id, l:buffer) " For LSP completions, we need to clamp the column to the length of " the line. python-language-server and perhaps others do not implement @@ -465,7 +464,7 @@ function! s:OnReady(linter, lsp_details, ...) abort \) endif - let l:request_id = ale#lsp#Send(l:id, l:message, l:root) + let l:request_id = ale#lsp#Send(l:id, l:message) if l:request_id let b:ale_completion_info.conn_id = l:id @@ -486,11 +485,10 @@ function! s:GetLSPCompletions(linter) abort endif let l:id = l:lsp_details.connection_id - let l:root = l:lsp_details.project_root let l:OnReady = function('s:OnReady', [a:linter, l:lsp_details]) - call ale#lsp#WaitForCapability(l:id, l:root, 'completion', l:OnReady) + call ale#lsp#WaitForCapability(l:id, 'completion', l:OnReady) endfunction function! ale#completion#GetCompletions() abort diff --git a/autoload/ale/definition.vim b/autoload/ale/definition.vim index 18bec988..e68d279d 100644 --- a/autoload/ale/definition.vim +++ b/autoload/ale/definition.vim @@ -60,7 +60,6 @@ endfunction function! s:OnReady(linter, lsp_details, line, column, options, ...) abort let l:buffer = a:lsp_details.buffer let l:id = a:lsp_details.connection_id - let l:root = a:lsp_details.project_root let l:Callback = a:linter.lsp is# 'tsserver' \ ? function('ale#definition#HandleTSServerResponse') @@ -76,7 +75,7 @@ function! s:OnReady(linter, lsp_details, line, column, options, ...) abort else " Send a message saying the buffer has changed first, or the " definition position probably won't make sense. - call ale#lsp#NotifyForChanges(l:id, l:root, l:buffer) + call ale#lsp#NotifyForChanges(l:id, l:buffer) " For LSP completions, we need to clamp the column to the length of " the line. python-language-server and perhaps others do not implement @@ -84,7 +83,7 @@ function! s:OnReady(linter, lsp_details, line, column, options, ...) abort let l:message = ale#lsp#message#Definition(l:buffer, a:line, a:column) endif - let l:request_id = ale#lsp#Send(l:id, l:message, l:root) + let l:request_id = ale#lsp#Send(l:id, l:message) let s:go_to_definition_map[l:request_id] = { \ 'open_in_tab': get(a:options, 'open_in_tab', 0), @@ -105,13 +104,10 @@ function! s:GoToLSPDefinition(linter, options) abort endif let l:id = l:lsp_details.connection_id - let l:root = l:lsp_details.project_root - let l:OnReady = function('s:OnReady', [ + call ale#lsp#WaitForCapability(l:id, 'definition', function('s:OnReady', [ \ a:linter, l:lsp_details, l:line, l:column, a:options - \]) - - call ale#lsp#WaitForCapability(l:id, l:root, 'definition', l:OnReady) + \])) endfunction function! ale#definition#GoTo(options) abort diff --git a/autoload/ale/hover.vim b/autoload/ale/hover.vim index 7c7386f5..69db276e 100644 --- a/autoload/ale/hover.vim +++ b/autoload/ale/hover.vim @@ -95,7 +95,6 @@ endfunction function! s:OnReady(linter, lsp_details, line, column, opt, ...) abort let l:buffer = a:lsp_details.buffer let l:id = a:lsp_details.connection_id - let l:root = a:lsp_details.project_root let l:Callback = a:linter.lsp is# 'tsserver' \ ? function('ale#hover#HandleTSServerResponse') @@ -113,14 +112,14 @@ function! s:OnReady(linter, lsp_details, line, column, opt, ...) abort else " Send a message saying the buffer has changed first, or the " hover position probably won't make sense. - call ale#lsp#NotifyForChanges(l:id, l:root, l:buffer) + call ale#lsp#NotifyForChanges(l:id, l:buffer) let l:column = min([a:column, len(getbufline(l:buffer, a:line)[0])]) let l:message = ale#lsp#message#Hover(l:buffer, a:line, l:column) endif - let l:request_id = ale#lsp#Send(l:id, l:message, l:root) + let l:request_id = ale#lsp#Send(l:id, l:message) let s:hover_map[l:request_id] = { \ 'buffer': l:buffer, @@ -138,13 +137,10 @@ function! s:ShowDetails(linter, buffer, line, column, opt, ...) abort endif let l:id = l:lsp_details.connection_id - let l:root = l:lsp_details.project_root - let l:OnReady = function('s:OnReady', [ + call ale#lsp#WaitForCapability(l:id, 'hover', function('s:OnReady', [ \ a:linter, l:lsp_details, a:line, a:column, a:opt - \]) - - call ale#lsp#WaitForCapability(l:id, l:root, 'hover', l:OnReady) + \])) endfunction " Obtain Hover information for the specified position diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim index 98160995..7f99422a 100644 --- a/autoload/ale/lsp.vim +++ b/autoload/ale/lsp.vim @@ -1,62 +1,66 @@ " Author: w0rp " Description: Language Server Protocol client code -" A List of connections, used for tracking servers which have been connected -" to, and programs which are run. -let s:connections = get(s:, 'connections', []) +" A Dictionary for tracking connections. +let s:connections = get(s:, 'connections', {}) let g:ale_lsp_next_message_id = 1 -" Exposed only so tests can get at it. -" Do not call this function basically anywhere. -function! ale#lsp#NewConnection(initialization_options) abort - " id: The job ID as a Number, or the server address as a string. - " data: The message data received so far. - " executable: An executable only set for program connections. - " open_documents: A Dictionary mapping buffers to b:changedtick, keeping - " track of when documents were opened, and when we last changed them. - " callback_list: A list of callbacks for handling LSP responses. - " initialization_options: Options to send to the server. - " capabilities: Features the server supports. - let l:conn = { - \ 'is_tsserver': 0, - \ 'id': '', - \ 'data': '', - \ 'projects': {}, - \ 'open_documents': {}, - \ 'callback_list': [], - \ 'initialization_options': a:initialization_options, - \ 'capabilities': { - \ 'hover': 0, - \ 'references': 0, - \ 'completion': 0, - \ 'completion_trigger_characters': [], - \ 'definition': 0, - \ }, - \} +" Given an id, which can be an executable or address, and a project path, +" create a new connection if needed. Return a unique ID for the connection. +function! ale#lsp#Register(executable_or_address, project, init_options) abort + let l:conn_id = a:executable_or_address . ':' . a:project - call add(s:connections, l:conn) + if !has_key(s:connections, l:conn_id) + " is_tsserver: 1 if the connection is for tsserver. + " data: The message data received so far. + " root: The project root. + " open_documents: A Dictionary mapping buffers to b:changedtick, keeping + " track of when documents were opened, and when we last changed them. + " initialized: 0 if the connection is ready, 1 otherwise. + " init_request_id: The ID for the init request. + " init_options: Options to send to the server. + " callback_list: A list of callbacks for handling LSP responses. + " message_queue: Messages queued for sending to callbacks. + " capabilities_queue: The list of callbacks to call with capabilities. + " capabilities: Features the server supports. + let s:connections[l:conn_id] = { + \ 'is_tsserver': 0, + \ 'data': '', + \ 'root': a:project, + \ 'open_documents': {}, + \ 'initialized': 0, + \ 'init_request_id': 0, + \ 'init_options': a:init_options, + \ 'callback_list': [], + \ 'message_queue': [], + \ 'capabilities_queue': [], + \ 'capabilities': { + \ 'hover': 0, + \ 'references': 0, + \ 'completion': 0, + \ 'completion_trigger_characters': [], + \ 'definition': 0, + \ }, + \} + endif - return l:conn + return l:conn_id endfunction " Remove an LSP connection with a given ID. This is only for tests. function! ale#lsp#RemoveConnectionWithID(id) abort - call filter(s:connections, 'v:val.id isnot a:id') + if has_key(s:connections, a:id) + call remove(s:connections, a:id) + endif endfunction -function! s:FindConnection(key, value) abort - for l:conn in s:connections - if has_key(l:conn, a:key) && get(l:conn, a:key) is# a:value - return l:conn - endif - endfor +" This is only needed for tests +function! ale#lsp#MarkDocumentAsOpen(id, buffer) abort + let l:conn = get(s:connections, a:id, {}) - return {} -endfunction - -" Get the capabilities for a connection, or an empty Dictionary. -function! ale#lsp#GetConnectionCapabilities(id) abort - return get(s:FindConnection('id', a:id), 'capabilities', {}) + if !empty(l:conn) + let l:conn.open_documents[a:buffer] = -1 + endif endfunction function! ale#lsp#GetNextMessageID() abort @@ -100,7 +104,7 @@ endfunction " Given a List of one or two items, [method_name] or [method_name, params], " return a List containing [message_id, message_data] function! ale#lsp#CreateMessageData(message) abort - if a:message[1] =~# '^ts@' + if a:message[1][:2] is# 'ts@' return s:CreateTSServerMessageData(a:message) endif @@ -167,49 +171,6 @@ function! ale#lsp#ReadMessageData(data) abort return [l:remainder, l:response_list] endfunction -function! s:FindProjectWithInitRequestID(conn, init_request_id) abort - for l:project_root in keys(a:conn.projects) - let l:project = a:conn.projects[l:project_root] - - if l:project.init_request_id == a:init_request_id - return l:project - endif - endfor - - return {} -endfunction - -function! s:MarkProjectAsInitialized(conn, project) abort - let a:project.initialized = 1 - - " After the server starts, send messages we had queued previously. - for l:message_data in a:project.message_queue - call s:SendMessageData(a:conn, l:message_data) - endfor - - " Remove the messages now. - let a:conn.message_queue = [] - - " Call capabilities callbacks queued for the project. - for [l:capability, l:Callback] in a:project.capabilities_queue - if a:conn.is_tsserver || a:conn.capabilities[l:capability] - call call(l:Callback, [a:conn.id, a:project.root]) - endif - endfor - - " Clear the queued callbacks now. - let a:project.capabilities_queue = [] -endfunction - -function! s:HandleInitializeResponse(conn, response) abort - let l:request_id = a:response.request_id - let l:project = s:FindProjectWithInitRequestID(a:conn, l:request_id) - - if !empty(l:project) - call s:MarkProjectAsInitialized(a:conn, l:project) - endif -endfunction - " Update capabilities from the server, so we know which features the server " supports. function! s:UpdateCapabilities(conn, capabilities) abort @@ -242,166 +203,138 @@ function! s:UpdateCapabilities(conn, capabilities) abort endif endfunction -function! ale#lsp#HandleOtherInitializeResponses(conn, response) abort - let l:uninitialized_projects = [] +function! ale#lsp#HandleInitResponse(conn, response) abort + if get(a:response, 'method', '') is# 'initialize' + let a:conn.initialized = 1 + elseif type(get(a:response, 'result')) is v:t_dict + \&& has_key(a:response.result, 'capabilities') + call s:UpdateCapabilities(a:conn, a:response.result.capabilities) - for [l:key, l:value] in items(a:conn.projects) - if l:value.initialized == 0 - call add(l:uninitialized_projects, [l:key, l:value]) - endif - endfor + let a:conn.initialized = 1 + endif - if empty(l:uninitialized_projects) + if !a:conn.initialized return endif - if get(a:response, 'method', '') is# '' - if type(get(a:response, 'result')) is v:t_dict - \&& has_key(a:response.result, 'capabilities') - call s:UpdateCapabilities(a:conn, a:response.result.capabilities) + " After the server starts, send messages we had queued previously. + for l:message_data in a:conn.message_queue + call s:SendMessageData(a:conn, l:message_data) + endfor - for [l:dir, l:project] in l:uninitialized_projects - call s:MarkProjectAsInitialized(a:conn, l:project) - endfor + " Remove the messages now. + let a:conn.message_queue = [] + + " Call capabilities callbacks queued for the project. + for [l:capability, l:Callback] in a:conn.capabilities_queue + if a:conn.capabilities[l:capability] + call call(l:Callback, [a:conn.id]) endif - elseif get(a:response, 'method', '') is# 'textDocument/publishDiagnostics' - let l:filename = ale#path#FromURI(a:response.params.uri) + endfor - for [l:dir, l:project] in l:uninitialized_projects - if l:filename[:len(l:dir) - 1] is# l:dir - call s:MarkProjectAsInitialized(a:conn, l:project) - endif - endfor - endif + let a:conn.capabilities_queue = [] endfunction -function! ale#lsp#HandleMessage(conn, message) abort +function! ale#lsp#HandleMessage(conn_id, message) abort + let l:conn = get(s:connections, a:conn_id, {}) + + if empty(l:conn) + return + endif + if type(a:message) isnot v:t_string " Ignore messages that aren't strings. return endif - let a:conn.data .= a:message + let l:conn.data .= a:message " Parse the objects now if we can, and keep the remaining text. - let [a:conn.data, l:response_list] = ale#lsp#ReadMessageData(a:conn.data) + let [l:conn.data, l:response_list] = ale#lsp#ReadMessageData(l:conn.data) - " Call our callbacks. - for l:response in l:response_list - if get(l:response, 'method', '') is# 'initialize' - call s:HandleInitializeResponse(a:conn, l:response) - else - call ale#lsp#HandleOtherInitializeResponses(a:conn, l:response) + " Look for initialize responses first. + if !l:conn.initialized + for l:response in l:response_list + call ale#lsp#HandleInitResponse(l:conn, l:response) + endfor + endif + " If the connection is marked as initialized, call the callbacks with the + " responses. + if l:conn.initialized + for l:response in l:response_list " Call all of the registered handlers with the response. - for l:Callback in a:conn.callback_list - call ale#util#GetFunction(l:Callback)(a:conn.id, l:response) + for l:Callback in l:conn.callback_list + call ale#util#GetFunction(l:Callback)(a:conn_id, l:response) endfor - endif - endfor + endfor + endif endfunction " Given a connection ID, mark it as a tsserver connection, so it will be " handled that way. function! ale#lsp#MarkConnectionAsTsserver(conn_id) abort - let l:conn = s:FindConnection('id', a:conn_id) - - if !empty(l:conn) - let l:conn.is_tsserver = 1 - endif + let l:conn = s:connections[a:conn_id] + let l:conn.is_tsserver = 1 + let l:conn.initialized = 1 + " Set capabilities which are supported by tsserver. + let l:conn.capabilities.hover = 1 + let l:conn.capabilities.references = 1 + let l:conn.capabilities.completion = 1 + let l:conn.capabilities.completion_trigger_characters = ['.'] + let l:conn.capabilities.definition = 1 endfunction -" Register a project for an LSP connection. +" Start a program for LSP servers. " -" This function will throw if the connection doesn't exist. -function! ale#lsp#RegisterProject(conn_id, project_root) abort - let l:conn = s:FindConnection('id', a:conn_id) +" 1 will be returned if the program is running, or 0 if the program could +" not be started. +function! ale#lsp#StartProgram(conn_id, executable, command) abort + let l:conn = s:connections[a:conn_id] - " Empty strings can't be used for Dictionary keys in NeoVim, due to E713. - " This appears to be a nonsensical bug in NeoVim. - let l:key = empty(a:project_root) ? '<>' : a:project_root - - if !has_key(l:conn.projects, l:key) - " Tools without project roots are ready right away, like tsserver. - let l:conn.projects[l:key] = { - \ 'root': a:project_root, - \ 'initialized': empty(a:project_root), - \ 'init_request_id': 0, - \ 'message_queue': [], - \ 'capabilities_queue': [], - \} - endif -endfunction - -function! ale#lsp#GetProject(conn, project_root) abort - if empty(a:conn) - return {} - endif - - let l:key = empty(a:project_root) ? '<>' : a:project_root - - return get(a:conn.projects, l:key, {}) -endfunction - -" Start a program for LSP servers which run with executables. -" -" The job ID will be returned for for the program if it ran, otherwise -" 0 will be returned. -function! ale#lsp#StartProgram(executable, command, init_options) abort - if !executable(a:executable) - return 0 - endif - - let l:conn = s:FindConnection('executable', a:executable) - - " Get the current connection or a new one. - let l:conn = !empty(l:conn) ? l:conn : ale#lsp#NewConnection(a:init_options) - let l:conn.executable = a:executable - - if !has_key(l:conn, 'id') || !ale#job#IsRunning(l:conn.id) + if !has_key(l:conn, 'job_id') || !ale#job#IsRunning(l:conn.job_id) let l:options = { \ 'mode': 'raw', - \ 'out_cb': {_, message -> ale#lsp#HandleMessage(l:conn, message)}, + \ 'out_cb': {_, message -> ale#lsp#HandleMessage(a:conn_id, message)}, \} let l:job_id = ale#job#Start(a:command, l:options) else - let l:job_id = l:conn.id + let l:job_id = l:conn.job_id endif - if l:job_id <= 0 - return 0 + if l:job_id > 0 + let l:conn.job_id = l:job_id endif - let l:conn.id = l:job_id - - return l:job_id + return l:job_id > 0 endfunction -" Connect to an address and set up a callback for handling responses. -function! ale#lsp#ConnectToAddress(address, init_options) abort - let l:conn = s:FindConnection('id', a:address) - " Get the current connection or a new one. - let l:conn = !empty(l:conn) ? l:conn : ale#lsp#NewConnection(a:init_options) +" Connect to an LSP server via TCP. +" +" 1 will be returned if the connection is running, or 0 if the connection could +" not be opened. +function! ale#lsp#ConnectToAddress(conn_id, address) abort + let l:conn = s:connections[a:conn_id] if !has_key(l:conn, 'channel_id') || !ale#socket#IsOpen(l:conn.channel_id) - let l:conn.channel_id = ale#socket#Open(a:address, { - \ 'callback': {_, message -> ale#lsp#HandleMessage(l:conn, message)}, + let l:channel_id = ale#socket#Open(a:address, { + \ 'callback': {_, mess -> ale#lsp#HandleMessage(a:conn_id, mess)}, \}) + else + let l:channel_id = l:conn.channel_id endif - if l:conn.channel_id < 0 - return '' + if l:channel_id >= 0 + let l:conn.channel_id = l:channel_id endif - let l:conn.id = a:address - - return a:address + return l:channel_id >= 0 endfunction " Given a connection ID and a callback, register that callback for handling " messages if the connection exists. function! ale#lsp#RegisterCallback(conn_id, callback) abort - let l:conn = s:FindConnection('id', a:conn_id) + let l:conn = get(s:connections, a:conn_id, {}) if !empty(l:conn) " Add the callback to the List if it's not there already. @@ -409,23 +342,33 @@ function! ale#lsp#RegisterCallback(conn_id, callback) abort endif endfunction +" Stop a single LSP connection. +function! ale#lsp#Stop(conn_id) abort + if has_key(s:connections, a:conn_id) + let l:conn = remove(s:connections, a:conn_id) + + if has_key(l:conn, 'channel_id') + call ale#socket#Close(l:conn.channel_id) + elseif has_key(l:conn, 'job_id') + call ale#job#Stop(l:conn.job_id) + endif + endif +endfunction + +function! ale#lsp#CloseDocument(conn_id) abort +endfunction + " Stop all LSP connections, closing all jobs and channels, and removing any " queued messages. function! ale#lsp#StopAll() abort - for l:conn in s:connections - if has_key(l:conn, 'channel_id') - call ale#socket#Close(l:conn.channel_id) - else - call ale#job#Stop(l:conn.id) - endif + for l:conn_id in keys(s:connections) + call ale#lsp#Stop(l:conn_id) endfor - - let s:connections = [] endfunction function! s:SendMessageData(conn, data) abort - if has_key(a:conn, 'executable') - call ale#job#SendRaw(a:conn.id, a:data) + if has_key(a:conn, 'job_id') + call ale#job#SendRaw(a:conn.job_id, a:data) elseif has_key(a:conn, 'channel_id') && ale#socket#IsOpen(a:conn.channel_id) " Send the message to the server call ale#socket#Send(a:conn.channel_id, a:data) @@ -442,38 +385,32 @@ endfunction " Returns -1 when a message is sent, but no response is expected " 0 when the message is not sent and " >= 1 with the message ID when a response is expected. -function! ale#lsp#Send(conn_id, message, ...) abort - let l:project_root = get(a:000, 0, '') +function! ale#lsp#Send(conn_id, message) abort + let l:conn = get(s:connections, a:conn_id, {}) - let l:conn = s:FindConnection('id', a:conn_id) - let l:project = ale#lsp#GetProject(l:conn, l:project_root) - - if empty(l:project) + if empty(l:conn) return 0 endif " If we haven't initialized the server yet, then send the message for it. - if !l:project.initialized - " Only send the init message once. - if !l:project.init_request_id - let [l:init_id, l:init_data] = ale#lsp#CreateMessageData( - \ ale#lsp#message#Initialize(l:project_root, l:conn.initialization_options), - \) + if !l:conn.initialized && !l:conn.init_request_id + let [l:init_id, l:init_data] = ale#lsp#CreateMessageData( + \ ale#lsp#message#Initialize(l:conn.root, l:conn.init_options), + \) - let l:project.init_request_id = l:init_id + let l:conn.init_request_id = l:init_id - call s:SendMessageData(l:conn, l:init_data) - endif + call s:SendMessageData(l:conn, l:init_data) endif let [l:id, l:data] = ale#lsp#CreateMessageData(a:message) - if l:project.initialized + if l:conn.initialized " Send the message now. call s:SendMessageData(l:conn, l:data) else " Add the message we wanted to send to a List to send later. - call add(l:project.message_queue, l:data) + call add(l:conn.message_queue, l:data) endif return l:id == 0 ? -1 : l:id @@ -481,11 +418,10 @@ endfunction " Notify LSP servers or tsserver if a document is opened, if needed. " If a document is opened, 1 will be returned, otherwise 0 will be returned. -function! ale#lsp#OpenDocument(conn_id, project_root, buffer, language_id) abort - let l:conn = s:FindConnection('id', a:conn_id) +function! ale#lsp#OpenDocument(conn_id, buffer, language_id) abort + let l:conn = get(s:connections, a:conn_id, {}) let l:opened = 0 - " FIXME: Return 1 if the document is already open? if !empty(l:conn) && !has_key(l:conn.open_documents, a:buffer) if l:conn.is_tsserver let l:message = ale#lsp#tsserver_message#Open(a:buffer) @@ -493,7 +429,7 @@ function! ale#lsp#OpenDocument(conn_id, project_root, buffer, language_id) abort let l:message = ale#lsp#message#DidOpen(a:buffer, a:language_id) endif - call ale#lsp#Send(a:conn_id, l:message, a:project_root) + call ale#lsp#Send(a:conn_id, l:message) let l:conn.open_documents[a:buffer] = getbufvar(a:buffer, 'changedtick') let l:opened = 1 endif @@ -503,8 +439,8 @@ endfunction " Notify LSP servers or tsserver that a document has changed, if needed. " If a notification is sent, 1 will be returned, otherwise 0 will be returned. -function! ale#lsp#NotifyForChanges(conn_id, project_root, buffer) abort - let l:conn = s:FindConnection('id', a:conn_id) +function! ale#lsp#NotifyForChanges(conn_id, buffer) abort + let l:conn = get(s:connections, a:conn_id, {}) let l:notified = 0 if !empty(l:conn) && has_key(l:conn.open_documents, a:buffer) @@ -517,7 +453,7 @@ function! ale#lsp#NotifyForChanges(conn_id, project_root, buffer) abort let l:message = ale#lsp#message#DidChange(a:buffer) endif - call ale#lsp#Send(a:conn_id, l:message, a:project_root) + call ale#lsp#Send(a:conn_id, l:message) let l:conn.open_documents[a:buffer] = l:new_tick let l:notified = 1 endif @@ -528,25 +464,24 @@ endfunction " Given some LSP details that must contain at least `connection_id` and " `project_root` keys, -function! ale#lsp#WaitForCapability(conn_id, project_root, capability, callback) abort - let l:conn = s:FindConnection('id', a:conn_id) - let l:project = ale#lsp#GetProject(l:conn, a:project_root) +function! ale#lsp#WaitForCapability(conn_id, capability, callback) abort + let l:conn = get(s:connections, a:conn_id, {}) - if empty(l:project) - return 0 + if empty(l:conn) + return endif if type(get(l:conn.capabilities, a:capability, v:null)) isnot v:t_number throw 'Invalid capability ' . a:capability endif - if l:project.initialized - if l:conn.is_tsserver || l:conn.capabilities[a:capability] + if l:conn.initialized + if l:conn.capabilities[a:capability] " The project has been initialized, so call the callback now. - call call(a:callback, [a:conn_id, a:project_root]) + call call(a:callback, [a:conn_id]) endif else " Call the callback later, once we have the information we need. - call add(l:project.capabilities_queue, [a:capability, a:callback]) + call add(l:conn.capabilities_queue, [a:capability, a:callback]) endif endfunction diff --git a/autoload/ale/lsp_linter.vim b/autoload/ale/lsp_linter.vim index 87aee759..8fbad12f 100644 --- a/autoload/ale/lsp_linter.vim +++ b/autoload/ale/lsp_linter.vim @@ -143,7 +143,8 @@ function! ale#lsp_linter#StartLSP(buffer, linter) abort if a:linter.lsp is# 'socket' let l:address = ale#linter#GetAddress(a:buffer, a:linter) - let l:conn_id = ale#lsp#ConnectToAddress(l:address, l:init_options) + let l:conn_id = ale#lsp#Register(l:address, l:root, l:init_options) + let l:ready = ale#lsp#ConnectToAddress(l:conn_id, l:address) else let l:executable = ale#linter#GetExecutable(a:buffer, a:linter) @@ -151,18 +152,16 @@ function! ale#lsp_linter#StartLSP(buffer, linter) abort return {} endif + let l:conn_id = ale#lsp#Register(l:executable, l:root, l:init_options) + let l:command = ale#linter#GetCommand(a:buffer, a:linter) " Format the command, so %e can be formatted into it. let l:command = ale#command#FormatCommand(a:buffer, l:executable, l:command, 0)[1] let l:command = ale#job#PrepareCommand(a:buffer, l:command) - let l:conn_id = ale#lsp#StartProgram( - \ l:executable, - \ l:command, - \ l:init_options, - \) + let l:ready = ale#lsp#StartProgram(l:conn_id, l:executable, l:command) endif - if empty(l:conn_id) + if !l:ready if g:ale_history_enabled && !empty(l:command) call ale#history#Add(a:buffer, 'failed', l:conn_id, l:command) endif @@ -175,9 +174,6 @@ function! ale#lsp_linter#StartLSP(buffer, linter) abort call ale#lsp#MarkConnectionAsTsserver(l:conn_id) endif - " Register the project now the connection is ready. - call ale#lsp#RegisterProject(l:conn_id, l:root) - let l:language_id = ale#util#GetFunction(a:linter.language_callback)(a:buffer) let l:details = { @@ -188,7 +184,7 @@ function! ale#lsp_linter#StartLSP(buffer, linter) abort \ 'language_id': l:language_id, \} - if ale#lsp#OpenDocument(l:conn_id, l:root, a:buffer, l:language_id) + if ale#lsp#OpenDocument(l:conn_id, a:buffer, l:language_id) if g:ale_history_enabled && !empty(l:command) call ale#history#Add(a:buffer, 'started', l:conn_id, l:command) endif @@ -196,7 +192,7 @@ function! ale#lsp_linter#StartLSP(buffer, linter) abort " The change message needs to be sent for tsserver before doing anything. if a:linter.lsp is# 'tsserver' - call ale#lsp#NotifyForChanges(l:conn_id, l:root, a:buffer) + call ale#lsp#NotifyForChanges(l:conn_id, a:buffer) endif return l:details @@ -211,7 +207,6 @@ function! ale#lsp_linter#CheckWithLSP(buffer, linter) abort endif let l:id = l:lsp_details.connection_id - let l:root = l:lsp_details.project_root " Register a callback now for handling errors now. let l:Callback = function('ale#lsp_linter#HandleLSPResponse') @@ -222,16 +217,16 @@ function! ale#lsp_linter#CheckWithLSP(buffer, linter) abort if a:linter.lsp is# 'tsserver' let l:message = ale#lsp#tsserver_message#Geterr(a:buffer) - let l:notified = ale#lsp#Send(l:id, l:message, l:root) != 0 + let l:notified = ale#lsp#Send(l:id, l:message) != 0 else - let l:notified = ale#lsp#NotifyForChanges(l:id, l:root, a:buffer) + let l:notified = ale#lsp#NotifyForChanges(l:id, a:buffer) endif " If this was a file save event, also notify the server of that. if a:linter.lsp isnot# 'tsserver' \&& getbufvar(a:buffer, 'ale_save_event_fired', 0) let l:save_message = ale#lsp#message#DidSave(a:buffer) - let l:notified = ale#lsp#Send(l:id, l:save_message, l:root) != 0 + let l:notified = ale#lsp#Send(l:id, l:save_message) != 0 endif if l:notified diff --git a/autoload/ale/references.vim b/autoload/ale/references.vim index bcf486b4..d00a1fa9 100644 --- a/autoload/ale/references.vim +++ b/autoload/ale/references.vim @@ -67,7 +67,6 @@ endfunction function! s:OnReady(linter, lsp_details, line, column, ...) abort let l:buffer = a:lsp_details.buffer let l:id = a:lsp_details.connection_id - let l:root = a:lsp_details.project_root let l:Callback = a:linter.lsp is# 'tsserver' \ ? function('ale#references#HandleTSServerResponse') @@ -84,12 +83,12 @@ function! s:OnReady(linter, lsp_details, line, column, ...) abort else " Send a message saying the buffer has changed first, or the " references position probably won't make sense. - call ale#lsp#NotifyForChanges(l:id, l:root, l:buffer) + call ale#lsp#NotifyForChanges(l:id, l:buffer) let l:message = ale#lsp#message#References(l:buffer, a:line, a:column) endif - let l:request_id = ale#lsp#Send(l:id, l:message, a:lsp_details.project_root) + let l:request_id = ale#lsp#Send(l:id, l:message) let s:references_map[l:request_id] = {} endfunction @@ -109,13 +108,10 @@ function! s:FindReferences(linter) abort endif let l:id = l:lsp_details.connection_id - let l:root = l:lsp_details.project_root - let l:OnReady = function('s:OnReady', [ + call ale#lsp#WaitForCapability(l:id, 'references', function('s:OnReady', [ \ a:linter, l:lsp_details, l:line, l:column - \]) - - call ale#lsp#WaitForCapability(l:id, l:root, 'references', l:OnReady) + \])) endfunction function! ale#references#Find() abort diff --git a/test/completion/test_lsp_completion_messages.vader b/test/completion/test_lsp_completion_messages.vader index ed0f358b..130f31b9 100644 --- a/test/completion/test_lsp_completion_messages.vader +++ b/test/completion/test_lsp_completion_messages.vader @@ -14,17 +14,17 @@ Before: let g:message_list = [] let g:capability_checked = '' + let g:conn_id = v:null let g:Callback = '' let g:wait_callback_list = [] function! ale#lsp_linter#StartLSP(buffer, linter) abort - let l:conn = ale#lsp#NewConnection({}) - let l:conn.id = 347 - let l:conn.open_documents = {a:buffer : -1} + let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {}) + call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer) return { \ 'buffer': a:buffer, - \ 'connection_id': 347, + \ 'connection_id': g:conn_id, \ 'project_root': '/foo/bar', \ 'language_id': 'python', \} @@ -35,7 +35,7 @@ Before: return 'i' endfunction - function! ale#lsp#WaitForCapability(conn_id, project_root, capability, callback) abort + function! ale#lsp#WaitForCapability(conn_id, capability, callback) abort let g:capability_checked = a:capability call add(g:wait_callback_list, a:callback) endfunction @@ -45,7 +45,7 @@ Before: endfunction " Replace the Send function for LSP, so we can monitor calls to it. - function! ale#lsp#Send(conn_id, message, ...) abort + function! ale#lsp#Send(conn_id, message) abort call add(g:message_list, a:message) return 1 @@ -54,9 +54,14 @@ Before: After: Restore + if g:conn_id isnot v:null + call ale#lsp#RemoveConnectionWithID(g:conn_id) + endif + unlet! g:message_list unlet! g:capability_checked unlet! g:wait_callback_list + unlet! g:conn_id unlet! g:Callback unlet! b:ale_old_omnifunc unlet! b:ale_old_completopt @@ -72,7 +77,6 @@ After: return call('mode', a:000) endfunction - call ale#lsp#RemoveConnectionWithID(347) call ale#test#RestoreDirectory() call ale#linter#Reset() @@ -102,7 +106,7 @@ Execute(The right message should be sent for the initial tsserver request): AssertEqual 1, len(g:wait_callback_list) AssertEqual 'completion', g:capability_checked - call map(g:wait_callback_list, 'v:val([347, ''/foo/bar''])') + call map(g:wait_callback_list, 'v:val([g:conn_id, ''/foo/bar''])') " We should send the right callback. AssertEqual @@ -116,7 +120,7 @@ Execute(The right message should be sent for the initial tsserver request): AssertEqual \ { \ 'line_length': 3, - \ 'conn_id': 347, + \ 'conn_id': g:conn_id, \ 'column': 3, \ 'request_id': 1, \ 'line': 1, @@ -189,7 +193,7 @@ Execute(The right message should be sent for the initial LSP request): AssertEqual 1, len(g:wait_callback_list) AssertEqual 'completion', g:capability_checked - call map(g:wait_callback_list, 'v:val([347, ''/foo/bar''])') + call map(g:wait_callback_list, 'v:val([g:conn_id, ''/foo/bar''])') " We should send the right callback. AssertEqual @@ -219,7 +223,7 @@ Execute(The right message should be sent for the initial LSP request): AssertEqual \ { \ 'line_length': 3, - \ 'conn_id': 347, + \ 'conn_id': g:conn_id, \ 'column': 3, \ 'request_id': 1, \ 'line': 1, diff --git a/test/lsp/test_did_save_event.vader b/test/lsp/test_did_save_event.vader index 428135fb..f8ff8f70 100644 --- a/test/lsp/test_did_save_event.vader +++ b/test/lsp/test_did_save_event.vader @@ -13,6 +13,7 @@ Before: let b:ale_enabled = 1 let g:ale_lsp_next_message_id = 1 let g:ale_run_synchronously = 1 + let g:conn_id = v:null let g:message_list = [] function! LanguageCallback() abort @@ -34,26 +35,29 @@ Before: let g:ale_linters = {'foobar': ['dummy_linter']} function! ale#lsp_linter#StartLSP(buffer, linter) abort - let l:conn = ale#lsp#NewConnection({}) - let l:conn.id = 347 - let l:conn.open_documents = {a:buffer : -1} + let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {}) + call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer) return { \ 'buffer': a:buffer, - \ 'connection_id': 347, + \ 'connection_id': g:conn_id, \ 'project_root': '/foo/bar', \ 'language_id': 'foobar', \} endfunction " Replace the Send function for LSP, so we can monitor calls to it. - function! ale#lsp#Send(conn_id, message, ...) abort + function! ale#lsp#Send(conn_id, message) abort call add(g:message_list, a:message) endfunction After: Restore + if g:conn_id isnot v:null + call ale#lsp#RemoveConnectionWithID(g:conn_id) + endif + unlet! b:ale_enabled unlet! b:ale_linters unlet! g:message_list @@ -61,7 +65,6 @@ After: delfunction LanguageCallback delfunction ProjectRootCallback - call ale#lsp#RemoveConnectionWithID(347) call ale#test#RestoreDirectory() call ale#linter#Reset() diff --git a/test/lsp/test_lsp_command_formatting.vader b/test/lsp/test_lsp_command_formatting.vader index 9d2c84ee..9721f37f 100644 --- a/test/lsp/test_lsp_command_formatting.vader +++ b/test/lsp/test_lsp_command_formatting.vader @@ -5,7 +5,7 @@ Before: " Mock the StartProgram function so we can just capture the arguments. function! ale#lsp#StartProgram(...) abort - let g:args = a:000 + let g:args = a:000[1:] endfunction After: @@ -27,10 +27,10 @@ Execute(Command formatting should be applied correctly for LSP linters): if has('win32') AssertEqual - \ ['cmd', 'cmd /s/c "cmd --foo"', {}], + \ ['cmd', 'cmd /s/c "cmd --foo"'], \ g:args else AssertEqual - \ ['true', [&shell, '-c', '''true'' --foo'], {}], + \ ['true', [&shell, '-c', '''true'' --foo']], \ g:args endif diff --git a/test/lsp/test_lsp_connections.vader b/test/lsp/test_lsp_connections.vader index ae64eadb..1c2fceab 100644 --- a/test/lsp/test_lsp_connections.vader +++ b/test/lsp/test_lsp_connections.vader @@ -225,57 +225,3 @@ Execute(ale#lsp#ReadMessageData() should handle a message with part of a second \ . '{"id":2,"jsonrpc":"2.0","result":{"foo":"barÜ"}}' \ . b:data \ ) - -Execute(Projects with regular project roots should be registered correctly): - let b:conn = ale#lsp#NewConnection({}) - call ale#lsp#RegisterProject(b:conn.id, '/foo/bar') - - AssertEqual - \ { - \ '/foo/bar': { - \ 'root': '/foo/bar', - \ 'initialized': 0, - \ 'message_queue': [], - \ 'capabilities_queue': [], - \ 'init_request_id': 0, - \ }, - \ }, - \ b:conn.projects - -Execute(Projects with regular project roots should be fetched correctly): - let b:conn = { - \ 'projects': { - \ '/foo/bar': {'initialized': 0, 'message_queue': [], 'init_request_id': 0}, - \ }, - \} - - AssertEqual - \ {'initialized': 0, 'message_queue': [], 'init_request_id': 0}, - \ ale#lsp#GetProject(b:conn, '/foo/bar') - -Execute(Projects with empty project roots should be registered correctly): - let b:conn = ale#lsp#NewConnection({}) - call ale#lsp#RegisterProject(b:conn.id, '') - - AssertEqual - \ { - \ '<>': { - \ 'root': '', - \ 'initialized': 1, - \ 'message_queue': [], - \ 'capabilities_queue': [], - \ 'init_request_id': 0, - \ }, - \ }, - \ b:conn.projects - -Execute(Projects with empty project roots should be fetched correctly): - let b:conn = { - \ 'projects': { - \ '<>': {'initialized': 1, 'message_queue': [], 'init_request_id': 0}, - \ }, - \} - - AssertEqual - \ {'initialized': 1, 'message_queue': [], 'init_request_id': 0}, - \ ale#lsp#GetProject(b:conn, '') diff --git a/test/lsp/test_other_initialize_message_handling.vader b/test/lsp/test_other_initialize_message_handling.vader index f5e0f1da..e29f3358 100644 --- a/test/lsp/test_other_initialize_message_handling.vader +++ b/test/lsp/test_other_initialize_message_handling.vader @@ -1,15 +1,15 @@ Before: - let b:project = { + let b:conn = { + \ 'is_tsserver': 0, + \ 'data': '', + \ 'root': '/foo/bar', + \ 'open_documents': {}, \ 'initialized': 0, - \ 'init_request_id': 3, + \ 'init_request_id': 0, + \ 'init_options': {}, + \ 'callback_list': [], \ 'message_queue': [], \ 'capabilities_queue': [], - \} - - let b:conn = { - \ 'projects': { - \ '/foo/bar': b:project, - \ }, \ 'capabilities': { \ 'hover': 0, \ 'references': 0, @@ -20,64 +20,26 @@ Before: \} After: - unlet! b:project unlet! b:conn -Execute(publishDiagnostics messages with files inside project directories should initialize projects): - " This is for some other file, ignore this one. - call ale#lsp#HandleOtherInitializeResponses(b:conn, { - \ 'method': 'textDocument/publishDiagnostics', - \ 'params': {'uri': 'file:///xyz/bar/baz.txt'}, - \}) - - AssertEqual - \ { - \ 'initialized': 0, - \ 'init_request_id': 3, - \ 'message_queue': [], - \ 'capabilities_queue': [], - \ }, - \ b:project - - call ale#lsp#HandleOtherInitializeResponses(b:conn, { - \ 'method': 'textDocument/publishDiagnostics', - \ 'params': {'uri': 'file:///foo/bar/baz.txt'}, - \}) - - AssertEqual - \ { - \ 'initialized': 1, - \ 'init_request_id': 3, - \ 'message_queue': [], - \ 'capabilities_queue': [], - \ }, - \ b:project - Execute(Messages with no method and capabilities should initialize projects): - call ale#lsp#HandleOtherInitializeResponses(b:conn, { + call ale#lsp#HandleInitResponse(b:conn, { \ 'result': {'capabilities': {}}, \}) - AssertEqual - \ { - \ 'initialized': 1, - \ 'init_request_id': 3, - \ 'message_queue': [], - \ 'capabilities_queue': [], - \ }, - \ b:project + AssertEqual 1, b:conn.initialized Execute(Other messages should not initialize projects): - call ale#lsp#HandleOtherInitializeResponses(b:conn, {'method': 'lolwat'}) + call ale#lsp#HandleInitResponse(b:conn, {'method': 'lolwat'}) - AssertEqual 0, b:project.initialized + AssertEqual 0, b:conn.initialized - call ale#lsp#HandleOtherInitializeResponses(b:conn, {'result': {'x': {}}}) + call ale#lsp#HandleInitResponse(b:conn, {'result': {'x': {}}}) - AssertEqual 0, b:project.initialized + AssertEqual 0, b:conn.initialized Execute(Capabilities should bet set up correctly): - call ale#lsp#HandleOtherInitializeResponses(b:conn, { + call ale#lsp#HandleInitResponse(b:conn, { \ 'jsonrpc': '2.0', \ 'id': 1, \ 'result': { @@ -110,29 +72,19 @@ Execute(Capabilities should bet set up correctly): \ }, \}) + AssertEqual 1, b:conn.initialized AssertEqual \ { - \ 'capabilities': { - \ 'completion_trigger_characters': ['.'], - \ 'completion': 1, - \ 'references': 1, - \ 'hover': 1, - \ 'definition': 1, - \ }, - \ 'message_queue': [], - \ 'projects': { - \ '/foo/bar': { - \ 'initialized': 1, - \ 'message_queue': [], - \ 'capabilities_queue': [], - \ 'init_request_id': 3, - \ }, - \ }, + \ 'completion_trigger_characters': ['.'], + \ 'completion': 1, + \ 'references': 1, + \ 'hover': 1, + \ 'definition': 1, \ }, - \ b:conn + \ b:conn.capabilities Execute(Disabled capabilities should be recognised correctly): - call ale#lsp#HandleOtherInitializeResponses(b:conn, { + call ale#lsp#HandleInitResponse(b:conn, { \ 'jsonrpc': '2.0', \ 'id': 1, \ 'result': { @@ -161,29 +113,19 @@ Execute(Disabled capabilities should be recognised correctly): \ }, \}) + AssertEqual 1, b:conn.initialized AssertEqual \ { - \ 'capabilities': { - \ 'completion_trigger_characters': [], - \ 'completion': 0, - \ 'references': 0, - \ 'hover': 0, - \ 'definition': 0, - \ }, - \ 'message_queue': [], - \ 'projects': { - \ '/foo/bar': { - \ 'initialized': 1, - \ 'message_queue': [], - \ 'capabilities_queue': [], - \ 'init_request_id': 3, - \ }, - \ }, + \ 'completion_trigger_characters': [], + \ 'completion': 0, + \ 'references': 0, + \ 'hover': 0, + \ 'definition': 0, \ }, - \ b:conn + \ b:conn.capabilities Execute(Results that are not dictionaries should be handled correctly): - call ale#lsp#HandleOtherInitializeResponses(b:conn, { + call ale#lsp#HandleInitResponse(b:conn, { \ 'jsonrpc': '2.0', \ 'id': 1, \ 'result': v:null, diff --git a/test/test_find_references.vader b/test/test_find_references.vader index ecced068..88b2d762 100644 --- a/test/test_find_references.vader +++ b/test/test_find_references.vader @@ -9,6 +9,7 @@ Before: let g:preview_called = 0 let g:item_list = [] let g:capability_checked = '' + let g:conn_id = v:null let g:WaitCallback = v:null runtime autoload/ale/linter.vim @@ -17,19 +18,18 @@ Before: runtime autoload/ale/preview.vim function! ale#lsp_linter#StartLSP(buffer, linter) abort - let l:conn = ale#lsp#NewConnection({}) - let l:conn.id = 347 - let l:conn.open_documents = {a:buffer : -1} + let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {}) + call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer) return { \ 'buffer': a:buffer, - \ 'connection_id': 347, + \ 'connection_id': g:conn_id, \ 'project_root': '/foo/bar', \ 'language_id': 'python', \} endfunction - function! ale#lsp#WaitForCapability(conn_id, project_root, capability, callback) abort + function! ale#lsp#WaitForCapability(conn_id, capability, callback) abort let g:capability_checked = a:capability let g:WaitCallback = a:callback endfunction @@ -38,7 +38,7 @@ Before: let g:Callback = a:callback endfunction - function! ale#lsp#Send(conn_id, message, root) abort + function! ale#lsp#Send(conn_id, message) abort call add(g:message_list, a:message) return 42 @@ -54,7 +54,10 @@ Before: endfunction After: - call ale#lsp#RemoveConnectionWithID(347) + if g:conn_id isnot v:null + call ale#lsp#RemoveConnectionWithID(g:conn_id) + endif + call ale#references#SetMap({}) call ale#test#RestoreDirectory() call ale#linter#Reset() @@ -62,6 +65,7 @@ After: unlet! g:capability_checked unlet! g:WaitCallback unlet! g:old_filename + unlet! g:conn_id unlet! g:Callback unlet! g:message_list unlet! g:expr_list @@ -168,7 +172,7 @@ Execute(tsserver reference requests should be sent): AssertEqual type(function('type')), type(g:WaitCallback) AssertEqual 'references', g:capability_checked - call call(g:WaitCallback, [347, '/foo/bar']) + call call(g:WaitCallback, [g:conn_id, '/foo/bar']) AssertEqual \ 'function(''ale#references#HandleTSServerResponse'')', @@ -249,7 +253,7 @@ Execute(LSP reference requests should be sent): AssertEqual type(function('type')), type(g:WaitCallback) AssertEqual 'references', g:capability_checked - call call(g:WaitCallback, [347, '/foo/bar']) + call call(g:WaitCallback, [g:conn_id, '/foo/bar']) AssertEqual \ 'function(''ale#references#HandleLSPResponse'')', diff --git a/test/test_go_to_definition.vader b/test/test_go_to_definition.vader index 53f566e9..c64db514 100644 --- a/test/test_go_to_definition.vader +++ b/test/test_go_to_definition.vader @@ -7,6 +7,7 @@ Before: let g:message_list = [] let g:expr_list = [] let g:capability_checked = '' + let g:conn_id = v:null let g:WaitCallback = v:null runtime autoload/ale/linter.vim @@ -14,19 +15,18 @@ Before: runtime autoload/ale/util.vim function! ale#lsp_linter#StartLSP(buffer, linter) abort - let l:conn = ale#lsp#NewConnection({}) - let l:conn.id = 347 - let l:conn.open_documents = {a:buffer : -1} + let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {}) + call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer) return { \ 'buffer': a:buffer, - \ 'connection_id': 347, + \ 'connection_id': g:conn_id, \ 'project_root': '/foo/bar', \ 'language_id': 'python', \} endfunction - function! ale#lsp#WaitForCapability(conn_id, project_root, capability, callback) abort + function! ale#lsp#WaitForCapability(conn_id, capability, callback) abort let g:capability_checked = a:capability let g:WaitCallback = a:callback endfunction @@ -35,7 +35,7 @@ Before: let g:Callback = a:callback endfunction - function! ale#lsp#Send(conn_id, message, root) abort + function! ale#lsp#Send(conn_id, message) abort call add(g:message_list, a:message) return 42 @@ -46,7 +46,10 @@ Before: endfunction After: - call ale#lsp#RemoveConnectionWithID(347) + if g:conn_id isnot v:null + call ale#lsp#RemoveConnectionWithID(g:conn_id) + endif + call ale#definition#SetMap({}) call ale#test#RestoreDirectory() call ale#linter#Reset() @@ -54,6 +57,7 @@ After: unlet! g:capability_checked unlet! g:WaitCallback unlet! g:old_filename + unlet! g:conn_id unlet! g:Callback unlet! g:message_list unlet! g:expr_list @@ -153,7 +157,7 @@ Execute(tsserver completion requests should be sent): AssertEqual type(function('type')), type(g:WaitCallback) AssertEqual 'definition', g:capability_checked - call call(g:WaitCallback, [347, '/foo/bar']) + call call(g:WaitCallback, [g:conn_id, '/foo/bar']) AssertEqual \ 'function(''ale#definition#HandleTSServerResponse'')', @@ -174,7 +178,7 @@ Execute(tsserver tab completion requests should be sent): AssertEqual type(function('type')), type(g:WaitCallback) AssertEqual 'definition', g:capability_checked - call call(g:WaitCallback, [347, '/foo/bar']) + call call(g:WaitCallback, [g:conn_id, '/foo/bar']) AssertEqual \ 'function(''ale#definition#HandleTSServerResponse'')', @@ -306,7 +310,7 @@ Execute(LSP completion requests should be sent): AssertEqual type(function('type')), type(g:WaitCallback) AssertEqual 'definition', g:capability_checked - call call(g:WaitCallback, [347, '/foo/bar']) + call call(g:WaitCallback, [g:conn_id, '/foo/bar']) AssertEqual \ 'function(''ale#definition#HandleLSPResponse'')', @@ -342,7 +346,7 @@ Execute(LSP tab completion requests should be sent): AssertEqual type(function('type')), type(g:WaitCallback) AssertEqual 'definition', g:capability_checked - call call(g:WaitCallback, [347, '/foo/bar']) + call call(g:WaitCallback, [g:conn_id, '/foo/bar']) AssertEqual \ 'function(''ale#definition#HandleLSPResponse'')',