diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index 5c054dd7..7b3a0e82 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -432,6 +432,11 @@ function! s:GetLSPCompletions(linter) abort let l:root = l:lsp_details.project_root function! OnReady(...) abort closure + " If we have sent a completion request already, don't send another. + if b:ale_completion_info.request_id + return + endif + let l:Callback = a:linter.lsp is# 'tsserver' \ ? function('ale#completion#HandleTSServerResponse') \ : function('ale#completion#HandleLSPResponse') diff --git a/test/completion/test_lsp_completion_messages.vader b/test/completion/test_lsp_completion_messages.vader index 30a9c8e1..ed0f358b 100644 --- a/test/completion/test_lsp_completion_messages.vader +++ b/test/completion/test_lsp_completion_messages.vader @@ -15,7 +15,7 @@ Before: let g:message_list = [] let g:capability_checked = '' let g:Callback = '' - let g:WaitCallback = v:null + let g:wait_callback_list = [] function! ale#lsp_linter#StartLSP(buffer, linter) abort let l:conn = ale#lsp#NewConnection({}) @@ -37,7 +37,7 @@ Before: function! ale#lsp#WaitForCapability(conn_id, project_root, capability, callback) abort let g:capability_checked = a:capability - let g:WaitCallback = a:callback + call add(g:wait_callback_list, a:callback) endfunction function! ale#lsp#RegisterCallback(conn_id, callback) abort @@ -47,6 +47,8 @@ Before: " Replace the Send function for LSP, so we can monitor calls to it. function! ale#lsp#Send(conn_id, message, ...) abort call add(g:message_list, a:message) + + return 1 endfunction After: @@ -54,7 +56,7 @@ After: unlet! g:message_list unlet! g:capability_checked - unlet! g:WaitCallback + unlet! g:wait_callback_list unlet! g:Callback unlet! b:ale_old_omnifunc unlet! b:ale_old_completopt @@ -98,9 +100,9 @@ Execute(The right message should be sent for the initial tsserver request): " We shouldn't register the callback yet. AssertEqual '''''', string(g:Callback) - AssertEqual type(function('type')), type(g:WaitCallback) + AssertEqual 1, len(g:wait_callback_list) AssertEqual 'completion', g:capability_checked - call call(g:WaitCallback, [347, '/foo/bar']) + call map(g:wait_callback_list, 'v:val([347, ''/foo/bar''])') " We should send the right callback. AssertEqual @@ -114,9 +116,9 @@ Execute(The right message should be sent for the initial tsserver request): AssertEqual \ { \ 'line_length': 3, - \ 'conn_id': 0, + \ 'conn_id': 347, \ 'column': 3, - \ 'request_id': 0, + \ 'request_id': 1, \ 'line': 1, \ 'prefix': 'fo', \ }, @@ -185,9 +187,9 @@ Execute(The right message should be sent for the initial LSP request): " We shouldn't register the callback yet. AssertEqual '''''', string(g:Callback) - AssertEqual type(function('type')), type(g:WaitCallback) + AssertEqual 1, len(g:wait_callback_list) AssertEqual 'completion', g:capability_checked - call call(g:WaitCallback, [347, '/foo/bar']) + call map(g:wait_callback_list, 'v:val([347, ''/foo/bar''])') " We should send the right callback. AssertEqual @@ -217,10 +219,58 @@ Execute(The right message should be sent for the initial LSP request): AssertEqual \ { \ 'line_length': 3, - \ 'conn_id': 0, + \ 'conn_id': 347, \ 'column': 3, - \ 'request_id': 0, + \ 'request_id': 1, \ 'line': 1, \ 'prefix': 'fo', + \ 'completion_filter': 'ale#completion#python#CompletionItemFilter', \ }, \ get(b:, 'ale_completion_info', {}) + +Execute(Two completion requests shouldn't be sent in a row): + call ale#linter#PreventLoading('python') + call ale#linter#Define('python', { + \ 'name': 'foo', + \ 'lsp': 'stdio', + \ 'executable': 'foo', + \ 'command': 'foo', + \ 'project_root_callback': {-> '/foo/bar'}, + \}) + call ale#linter#Define('python', { + \ 'name': 'bar', + \ 'lsp': 'stdio', + \ 'executable': 'foo', + \ 'command': 'foo', + \ 'project_root_callback': {-> '/foo/bar'}, + \}) + let b:ale_linters = ['foo', 'bar'] + + " The cursor position needs to match what was saved before. + call setpos('.', [bufnr(''), 1, 5, 0]) + + call ale#completion#GetCompletions() + + " We shouldn't register the callback yet. + AssertEqual '''''', string(g:Callback) + + AssertEqual 2, len(g:wait_callback_list) + AssertEqual 'completion', g:capability_checked + call map(g:wait_callback_list, 'v:val([347, ''/foo/bar''])') + + " We should only send one completion message for two LSP servers. + AssertEqual + \ [ + \ [1, 'textDocument/didChange', { + \ 'textDocument': { + \ 'uri': ale#path#ToURI(expand('%:p')), + \ 'version': g:ale_lsp_next_version_id - 1, + \ }, + \ 'contentChanges': [{'text': join(getline(1, '$'), "\n") . "\n"}] + \ }], + \ [0, 'textDocument/completion', { + \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))}, + \ 'position': {'line': 0, 'character': 3}, + \ }], + \ ], + \ g:message_list