diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index c3139da..a338ce7 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -30,6 +30,7 @@ let s:LSP_COMPLETION_REFERENCE_KIND = 18 " the insert cursor is. If one of these matches, we'll check for completions. let s:should_complete_map = { \ '': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$|\.$', +\ 'typescript': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$|\.$|''$|"$', \ 'rust': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$|\.$|::$', \} @@ -41,6 +42,7 @@ let s:omni_start_map = { " A map of exact characters for triggering LSP completions. let s:trigger_character_map = { \ '': ['.'], +\ 'typescript': ['.', '''', '"'], \ 'rust': ['.', '::'], \} @@ -198,7 +200,9 @@ function! ale#completion#ParseTSServerCompletions(response) abort endfunction function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort + let l:buffer = bufnr('') let l:results = [] + let l:names_with_details = [] for l:suggestion in a:response.body let l:displayParts = [] @@ -232,6 +236,26 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort \}) endfor + let l:names = getbufvar(l:buffer, 'ale_tsserver_completion_names', []) + + if !empty(l:names) && len(l:names) != len(l:results) + let l:names_with_details = map(copy(l:results), 'v:val.word') + let l:missing_names = filter( + \ copy(l:names), + \ 'index(l:names_with_details, v:val) < 0', + \) + + for l:name in l:missing_names + call add(l:results, { + \ 'word': l:name, + \ 'kind': 'v', + \ 'icase': 1, + \ 'menu': '', + \ 'info': '', + \}) + endfor + endif + return l:results endfunction @@ -322,6 +346,10 @@ function! ale#completion#HandleTSServerResponse(conn_id, response) abort \ b:ale_completion_info.prefix, \)[: g:ale_completion_max_suggestions - 1] + " We need to remember some names for tsserver, as it doesn't send + " details back for everything we send. + call setbufvar(l:buffer, 'ale_tsserver_completion_names', l:names) + if !empty(l:names) let b:ale_completion_info.request_id = ale#lsp#Send( \ b:ale_completion_info.conn_id, diff --git a/test/completion/test_completion_prefixes.vader b/test/completion/test_completion_prefixes.vader index a88e978..0b2cfea 100644 --- a/test/completion/test_completion_prefixes.vader +++ b/test/completion/test_completion_prefixes.vader @@ -2,6 +2,8 @@ Given typescript(): let abc = y. let foo = ab let foo = (ab) + let string1 = ' + let string2 = " Execute(Completion should be done after dots in TypeScript): AssertEqual '.', ale#completion#GetPrefix(&filetype, 1, 13) @@ -15,6 +17,13 @@ Execute(Completion should be done after words in parens in TypeScript): Execute(Completion should not be done after parens in TypeScript): AssertEqual '', ale#completion#GetPrefix(&filetype, 3, 15) +Execute(Completion should be done after strings in TypeScript): + AssertEqual '''', ale#completion#GetPrefix(&filetype, 4, 16) + AssertEqual '"', ale#completion#GetPrefix(&filetype, 5, 16) + +Execute(Completion prefixes should work for other filetypes): + AssertEqual 'ab', ale#completion#GetPrefix('xxxyyyzzz', 3, 14) + Execute(Completion prefixes should work for other filetypes): AssertEqual 'ab', ale#completion#GetPrefix('xxxyyyzzz', 3, 14) diff --git a/test/completion/test_lsp_completion_messages.vader b/test/completion/test_lsp_completion_messages.vader index af3aa8c..734b330 100644 --- a/test/completion/test_lsp_completion_messages.vader +++ b/test/completion/test_lsp_completion_messages.vader @@ -41,6 +41,7 @@ After: unlet! b:ale_completion_parser unlet! b:ale_complete_done_time unlet! b:ale_linters + unlet! b:ale_tsserver_completion_names call ale#test#RestoreDirectory() call ale#linter#Reset() @@ -112,6 +113,12 @@ Execute(The right message sent to the tsserver LSP when the first completion mes \ ], \}) + " We should save the names we got in the buffer, as TSServer doesn't return + " details for every name. + AssertEqual + \ ['Foo', 'FooBar', 'frazzle'], + \ get(b:, 'ale_tsserver_completion_names', []) + " The entry details messages should have been sent. AssertEqual \ [[ diff --git a/test/completion/test_tsserver_completion_parsing.vader b/test/completion/test_tsserver_completion_parsing.vader index b663ef4..c8e2c99 100644 --- a/test/completion/test_tsserver_completion_parsing.vader +++ b/test/completion/test_tsserver_completion_parsing.vader @@ -1,3 +1,6 @@ +After: + unlet! b:ale_tsserver_completion_names + Execute(TypeScript completions responses should be parsed correctly): AssertEqual [], \ ale#completion#ParseTSServerCompletions({ @@ -73,3 +76,74 @@ Execute(TypeScript completion details responses should be parsed correctly): \ }, \ ], \}) + +Execute(Entries without details should be included in the responses): + let b:ale_tsserver_completion_names = ['xyz'] + + AssertEqual + \ [ + \ { + \ 'word': 'abc', + \ 'menu': '(property) Foo.abc: number', + \ 'info': '', + \ 'kind': 'f', + \ 'icase': 1, + \ }, + \ { + \ 'word': 'def', + \ 'menu': '(property) Foo.def: number', + \ 'info': 'foo bar baz', + \ 'kind': 'f', + \ 'icase': 1, + \ }, + \ { + \ 'word': 'xyz', + \ 'menu': '', + \ 'info': '', + \ 'kind': 'v', + \ 'icase': 1, + \ }, + \ ], + \ ale#completion#ParseTSServerCompletionEntryDetails({ + \ 'body': [ + \ { + \ 'name': 'abc', + \ 'kind': 'parameterName', + \ 'displayParts': [ + \ {'text': '('}, + \ {'text': 'property'}, + \ {'text': ')'}, + \ {'text': ' '}, + \ {'text': 'Foo'}, + \ {'text': '.'}, + \ {'text': 'abc'}, + \ {'text': ':'}, + \ {'text': ' '}, + \ {'text': 'number'}, + \ ], + \ }, + \ { + \ 'name': 'def', + \ 'kind': 'parameterName', + \ 'displayParts': [ + \ {'text': '('}, + \ {'text': 'property'}, + \ {'text': ')'}, + \ {'text': ' '}, + \ {'text': 'Foo'}, + \ {'text': '.'}, + \ {'text': 'def'}, + \ {'text': ':'}, + \ {'text': ' '}, + \ {'text': 'number'}, + \ ], + \ 'documentation': [ + \ {'text': 'foo'}, + \ {'text': ' '}, + \ {'text': 'bar'}, + \ {'text': ' '}, + \ {'text': 'baz'}, + \ ], + \ }, + \ ], + \})