Before: call ale#test#SetDirectory('/testplugin/test') call ale#test#SetFilename('dummy.txt') let g:old_filename = expand('%:p') let g:Callback = '' 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 runtime autoload/ale/lsp.vim runtime autoload/ale/util.vim function! ale#lsp_linter#StartLSP(buffer, linter) abort 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': g:conn_id, \ 'project_root': '/foo/bar', \ 'language_id': 'python', \} endfunction function! ale#lsp#WaitForCapability(conn_id, capability, callback) abort let g:capability_checked = a:capability let g:WaitCallback = a:callback endfunction function! ale#lsp#RegisterCallback(conn_id, callback) abort let g:Callback = a:callback endfunction function! ale#lsp#Send(conn_id, message) abort call add(g:message_list, a:message) return 42 endfunction function! ale#util#Execute(expr) abort call add(g:expr_list, a:expr) endfunction After: 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() 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 unlet! b:ale_linters runtime autoload/ale/lsp_linter.vim runtime autoload/ale/lsp.vim runtime autoload/ale/util.vim Execute(Other messages for the tsserver handler should be ignored): call ale#definition#HandleTSServerResponse(1, {'command': 'foo'}) Execute(Failed definition responses should be handled correctly): call ale#definition#SetMap({3: {'open_in': 'current-buffer'}}) call ale#definition#HandleTSServerResponse( \ 1, \ {'command': 'definition', 'request_seq': 3} \) AssertEqual {}, ale#definition#GetMap() Execute(Failed definition responses with no files should be handled correctly): call ale#definition#SetMap({3: {'open_in': 'current-buffer'}}) call ale#definition#HandleTSServerResponse( \ 1, \ { \ 'command': 'definition', \ 'request_seq': 3, \ 'success': v:true, \ 'body': [], \ } \) AssertEqual {}, ale#definition#GetMap() Given typescript(Some typescript file): foo somelongerline bazxyzxyzxyz Execute(Other files should be jumped to for definition responses): call ale#definition#SetMap({3: {'open_in': 'current-buffer'}}) call ale#definition#HandleTSServerResponse( \ 1, \ { \ 'command': 'definition', \ 'request_seq': 3, \ 'success': v:true, \ 'body': [ \ { \ 'file': ale#path#Simplify(g:dir . '/completion_dummy_file'), \ 'start': {'line': 3, 'offset': 7}, \ }, \ ], \ } \) AssertEqual \ [ \ 'edit +3 ' . fnameescape(ale#path#Simplify(g:dir . '/completion_dummy_file')), \ ], \ g:expr_list AssertEqual [3, 7], getpos('.')[1:2] AssertEqual {}, ale#definition#GetMap() Execute(Other files should be jumped to for definition responses in tabs too): call ale#definition#SetMap({3: {'open_in': 'tab'}}) call ale#definition#HandleTSServerResponse( \ 1, \ { \ 'command': 'definition', \ 'request_seq': 3, \ 'success': v:true, \ 'body': [ \ { \ 'file': ale#path#Simplify(g:dir . '/completion_dummy_file'), \ 'start': {'line': 3, 'offset': 7}, \ }, \ ], \ } \) AssertEqual \ [ \ 'tabedit +3 ' . fnameescape(ale#path#Simplify(g:dir . '/completion_dummy_file')), \ ], \ g:expr_list AssertEqual [3, 7], getpos('.')[1:2] AssertEqual {}, ale#definition#GetMap() Execute(Other files should be jumped to for definition responses in splits too): call ale#definition#SetMap({3: {'open_in': 'horizontal-split'}}) call ale#definition#HandleTSServerResponse( \ 1, \ { \ 'command': 'definition', \ 'request_seq': 3, \ 'success': v:true, \ 'body': [ \ { \ 'file': ale#path#Simplify(g:dir . '/completion_dummy_file'), \ 'start': {'line': 3, 'offset': 7}, \ }, \ ], \ } \) AssertEqual \ [ \ 'split +3 ' . fnameescape(ale#path#Simplify(g:dir . '/completion_dummy_file')), \ ], \ g:expr_list AssertEqual [3, 7], getpos('.')[1:2] AssertEqual {}, ale#definition#GetMap() Execute(Other files should be jumped to for definition responses in vsplits too): call ale#definition#SetMap({3: {'open_in': 'vertical-split'}}) call ale#definition#HandleTSServerResponse( \ 1, \ { \ 'command': 'definition', \ 'request_seq': 3, \ 'success': v:true, \ 'body': [ \ { \ 'file': ale#path#Simplify(g:dir . '/completion_dummy_file'), \ 'start': {'line': 3, 'offset': 7}, \ }, \ ], \ } \) AssertEqual \ [ \ 'vsplit +3 ' . fnameescape(ale#path#Simplify(g:dir . '/completion_dummy_file')), \ ], \ g:expr_list AssertEqual [3, 7], getpos('.')[1:2] AssertEqual {}, ale#definition#GetMap() Execute(tsserver completion requests should be sent): runtime ale_linters/typescript/tsserver.vim call setpos('.', [bufnr(''), 2, 5, 0]) ALEGoToDefinition " We shouldn't register the callback yet. AssertEqual '''''', string(g:Callback) AssertEqual type(function('type')), type(g:WaitCallback) AssertEqual 'definition', g:capability_checked call call(g:WaitCallback, [g:conn_id, '/foo/bar']) AssertEqual \ 'function(''ale#definition#HandleTSServerResponse'')', \ string(g:Callback) AssertEqual \ [[0, 'ts@definition', {'file': expand('%:p'), 'line': 2, 'offset': 5}]], \ g:message_list AssertEqual {'42': {'open_in': 'current-buffer'}}, ale#definition#GetMap() Execute(tsserver tab completion requests should be sent): runtime ale_linters/typescript/tsserver.vim call setpos('.', [bufnr(''), 2, 5, 0]) ALEGoToDefinitionInTab " We shouldn't register the callback yet. AssertEqual '''''', string(g:Callback) AssertEqual type(function('type')), type(g:WaitCallback) AssertEqual 'definition', g:capability_checked call call(g:WaitCallback, [g:conn_id, '/foo/bar']) AssertEqual \ 'function(''ale#definition#HandleTSServerResponse'')', \ string(g:Callback) AssertEqual \ [[0, 'ts@definition', {'file': expand('%:p'), 'line': 2, 'offset': 5}]], \ g:message_list AssertEqual {'42': {'open_in': 'tab'}}, ale#definition#GetMap() Given python(Some Python file): foo somelongerline bazxyzxyzxyz Execute(Other files should be jumped to for LSP definition responses): call ale#definition#SetMap({3: {'open_in': 'current-buffer'}}) call ale#definition#HandleLSPResponse( \ 1, \ { \ 'id': 3, \ 'result': { \ 'uri': ale#path#ToURI(ale#path#Simplify(g:dir . '/completion_dummy_file')), \ 'range': { \ 'start': {'line': 2, 'character': 7}, \ }, \ }, \ } \) AssertEqual \ [ \ 'edit +3 ' . fnameescape(ale#path#Simplify(g:dir . '/completion_dummy_file')), \ ], \ g:expr_list AssertEqual [3, 8], getpos('.')[1:2] AssertEqual {}, ale#definition#GetMap() Execute(Locations inside the same file should be jumped to without using :edit): call ale#definition#SetMap({3: {'open_in': 'current-buffer'}}) call ale#definition#HandleLSPResponse( \ 1, \ { \ 'id': 3, \ 'result': { \ 'uri': ale#path#ToURI(ale#path#Simplify(expand('%:p'))), \ 'range': { \ 'start': {'line': 2, 'character': 7}, \ }, \ }, \ } \) AssertEqual \ [ \ ], \ g:expr_list AssertEqual [3, 8], getpos('.')[1:2] AssertEqual {}, ale#definition#GetMap() Execute(Other files should be jumped to in tabs for LSP definition responses): call ale#definition#SetMap({3: {'open_in': 'tab'}}) call ale#definition#HandleLSPResponse( \ 1, \ { \ 'id': 3, \ 'result': { \ 'uri': ale#path#ToURI(ale#path#Simplify(g:dir . '/completion_dummy_file')), \ 'range': { \ 'start': {'line': 2, 'character': 7}, \ }, \ }, \ } \) AssertEqual \ [ \ 'tabedit +3 ' . fnameescape(ale#path#Simplify(g:dir . '/completion_dummy_file')), \ ], \ g:expr_list AssertEqual [3, 8], getpos('.')[1:2] AssertEqual {}, ale#definition#GetMap() Execute(Definition responses with lists should be handled): call ale#definition#SetMap({3: {'open_in': 'current-buffer'}}) call ale#definition#HandleLSPResponse( \ 1, \ { \ 'id': 3, \ 'result': [ \ { \ 'uri': ale#path#ToURI(ale#path#Simplify(g:dir . '/completion_dummy_file')), \ 'range': { \ 'start': {'line': 2, 'character': 7}, \ }, \ }, \ { \ 'uri': ale#path#ToURI(ale#path#Simplify(g:dir . '/other_file')), \ 'range': { \ 'start': {'line': 20, 'character': 3}, \ }, \ }, \ ], \ } \) AssertEqual \ [ \ 'edit +3 ' . fnameescape(ale#path#Simplify(g:dir . '/completion_dummy_file')), \ ], \ g:expr_list AssertEqual [3, 8], getpos('.')[1:2] AssertEqual {}, ale#definition#GetMap() Execute(Definition responses with null response should be handled): call ale#definition#SetMap({3: {'open_in': 'current-buffer'}}) call ale#definition#HandleLSPResponse(1, {'id': 3, 'result': v:null}) AssertEqual [], g:expr_list Execute(LSP completion requests should be sent): runtime ale_linters/python/pyls.vim let b:ale_linters = ['pyls'] call setpos('.', [bufnr(''), 1, 5, 0]) ALEGoToDefinition " We shouldn't register the callback yet. AssertEqual '''''', string(g:Callback) AssertEqual type(function('type')), type(g:WaitCallback) AssertEqual 'definition', g:capability_checked call call(g:WaitCallback, [g:conn_id, '/foo/bar']) AssertEqual \ 'function(''ale#definition#HandleLSPResponse'')', \ string(g:Callback) 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/definition', { \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))}, \ 'position': {'line': 0, 'character': 2}, \ }], \ ], \ g:message_list AssertEqual {'42': {'open_in': 'current-buffer'}}, ale#definition#GetMap() Execute(LSP tab completion requests should be sent): runtime ale_linters/python/pyls.vim let b:ale_linters = ['pyls'] call setpos('.', [bufnr(''), 1, 5, 0]) ALEGoToDefinitionInTab " We shouldn't register the callback yet. AssertEqual '''''', string(g:Callback) AssertEqual type(function('type')), type(g:WaitCallback) AssertEqual 'definition', g:capability_checked call call(g:WaitCallback, [g:conn_id, '/foo/bar']) AssertEqual \ 'function(''ale#definition#HandleLSPResponse'')', \ string(g:Callback) 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/definition', { \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))}, \ 'position': {'line': 0, 'character': 2}, \ }], \ ], \ g:message_list AssertEqual {'42': {'open_in': 'tab'}}, ale#definition#GetMap()