diff options
Diffstat (limited to 'autoload/ale/completion.vim')
-rw-r--r-- | autoload/ale/completion.vim | 141 |
1 files changed, 80 insertions, 61 deletions
diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index 7440f8cd..9dd913f5 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -39,10 +39,14 @@ let s:LSP_COMPLETION_COLOR_KIND = 16 let s:LSP_COMPLETION_FILE_KIND = 17 let s:LSP_COMPLETION_REFERENCE_KIND = 18 +let s:lisp_regex = '\v[a-zA-Z_\-][a-zA-Z_\-0-9]*$' + " Regular expressions for checking the characters in the line before where " the insert cursor is. If one of these matches, we'll check for completions. let s:should_complete_map = { \ '<default>': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$|\.$', +\ 'clojure': s:lisp_regex, +\ 'lisp': s:lisp_regex, \ 'typescript': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$|\.$|''$|"$', \ 'rust': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$|\.$|::$', \} @@ -75,6 +79,7 @@ endfunction " Check if we should look for completions for a language. function! ale#completion#GetPrefix(filetype, line, column) abort let l:regex = s:GetFiletypeValue(s:should_complete_map, a:filetype) + " The column we're using completions for is where we are inserting text, " like so: " abc @@ -93,14 +98,15 @@ function! ale#completion#GetTriggerCharacter(filetype, prefix) abort return '' endfunction -function! ale#completion#Filter(buffer, suggestions, prefix) abort +function! ale#completion#Filter(buffer, filetype, suggestions, prefix) abort let l:excluded_words = ale#Var(a:buffer, 'completion_excluded_words') + let l:triggers = s:GetFiletypeValue(s:trigger_character_map, a:filetype) " For completing... " foo. " ^ " We need to include all of the given suggestions. - if a:prefix is# '.' + if index(l:triggers, a:prefix) >= 0 let l:filtered_suggestions = a:suggestions else let l:filtered_suggestions = [] @@ -113,7 +119,7 @@ function! ale#completion#Filter(buffer, suggestions, prefix) abort for l:item in a:suggestions " A List of String values or a List of completion item Dictionaries " is accepted here. - let l:word = type(l:item) == type('') ? l:item : l:item.word + let l:word = type(l:item) is v:t_string ? l:item : l:item.word " Add suggestions if the suggestion starts with a case-insensitive " match for the prefix. @@ -133,7 +139,7 @@ function! ale#completion#Filter(buffer, suggestions, prefix) abort " Remove suggestions with words in the exclusion List. call filter( \ l:filtered_suggestions, - \ 'index(l:excluded_words, type(v:val) is type('''') ? v:val : v:val.word) < 0', + \ 'index(l:excluded_words, type(v:val) is v:t_string ? v:val : v:val.word) < 0', \) endif @@ -214,8 +220,10 @@ function! ale#completion#Show(response, completion_parser) abort " function, and then start omni-completion. let b:ale_completion_response = a:response let b:ale_completion_parser = a:completion_parser + " Replace completion options shortly before opening the menu. call s:ReplaceCompletionOptions() - call ale#util#FeedKeys("\<Plug>(ale_show_completion_menu)") + + call timer_start(0, {-> ale#util#FeedKeys("\<Plug>(ale_show_completion_menu)")}) endfunction function! s:CompletionStillValid(request_id) abort @@ -257,7 +265,7 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort call add(l:documentationParts, l:part.text) endfor - if l:suggestion.kind is# 'clasName' + if l:suggestion.kind is# 'className' let l:kind = 'f' elseif l:suggestion.kind is# 'parameterName' let l:kind = 'f' @@ -315,10 +323,10 @@ function! ale#completion#ParseLSPCompletions(response) abort let l:item_list = [] - if type(get(a:response, 'result')) is type([]) + if type(get(a:response, 'result')) is v:t_list let l:item_list = a:response.result - elseif type(get(a:response, 'result')) is type({}) - \&& type(get(a:response.result, 'items')) is type([]) + elseif type(get(a:response, 'result')) is v:t_dict + \&& type(get(a:response.result, 'items')) is v:t_list let l:item_list = a:response.result.items endif @@ -352,17 +360,23 @@ function! ale#completion#ParseLSPCompletions(response) abort let l:kind = 'v' endif + let l:doc = get(l:item, 'documentation', '') + + if type(l:doc) is v:t_dict && has_key(l:doc, 'value') + let l:doc = l:doc.value + endif + call add(l:results, { \ 'word': l:word, \ 'kind': l:kind, \ 'icase': 1, \ 'menu': get(l:item, 'detail', ''), - \ 'info': get(l:item, 'documentation', ''), + \ 'info': (type(l:doc) is v:t_string ? l:doc : ''), \}) endfor if has_key(l:info, 'prefix') - return ale#completion#Filter(l:buffer, l:results, l:info.prefix) + return ale#completion#Filter(l:buffer, &filetype, l:results, l:info.prefix) endif return l:results @@ -383,6 +397,7 @@ function! ale#completion#HandleTSServerResponse(conn_id, response) abort if l:command is# 'completions' let l:names = ale#completion#Filter( \ l:buffer, + \ &filetype, \ ale#completion#ParseTSServerCompletions(a:response), \ b:ale_completion_info.prefix, \)[: g:ale_completion_max_suggestions - 1] @@ -422,6 +437,58 @@ function! ale#completion#HandleLSPResponse(conn_id, response) abort \) endfunction +function! s:OnReady(linter, lsp_details, ...) abort + let l:buffer = a:lsp_details.buffer + let l:id = a:lsp_details.connection_id + + " 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') + call ale#lsp#RegisterCallback(l:id, l:Callback) + + if a:linter.lsp is# 'tsserver' + let l:message = ale#lsp#tsserver_message#Completions( + \ l:buffer, + \ b:ale_completion_info.line, + \ b:ale_completion_info.column, + \ b:ale_completion_info.prefix, + \) + 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: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 + " this correctly. + let l:message = ale#lsp#message#Completion( + \ l:buffer, + \ b:ale_completion_info.line, + \ min([ + \ b:ale_completion_info.line_length, + \ b:ale_completion_info.column, + \ ]), + \ ale#completion#GetTriggerCharacter(&filetype, b:ale_completion_info.prefix), + \) + endif + + let l:request_id = ale#lsp#Send(l:id, l:message) + + if l:request_id + let b:ale_completion_info.conn_id = l:id + let b:ale_completion_info.request_id = l:request_id + + if has_key(a:linter, 'completion_filter') + let b:ale_completion_info.completion_filter = a:linter.completion_filter + endif + endif +endfunction + function! s:GetLSPCompletions(linter) abort let l:buffer = bufnr('') let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter) @@ -431,58 +498,10 @@ function! s:GetLSPCompletions(linter) abort endif let l:id = l:lsp_details.connection_id - 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') - call ale#lsp#RegisterCallback(l:id, l:Callback) - - if a:linter.lsp is# 'tsserver' - let l:message = ale#lsp#tsserver_message#Completions( - \ l:buffer, - \ b:ale_completion_info.line, - \ b:ale_completion_info.column, - \ b:ale_completion_info.prefix, - \) - 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) - - " For LSP completions, we need to clamp the column to the length of - " the line. python-language-server and perhaps others do not implement - " this correctly. - let l:message = ale#lsp#message#Completion( - \ l:buffer, - \ b:ale_completion_info.line, - \ min([ - \ b:ale_completion_info.line_length, - \ b:ale_completion_info.column, - \ ]), - \ ale#completion#GetTriggerCharacter(&filetype, b:ale_completion_info.prefix), - \) - endif - let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root) - - if l:request_id - let b:ale_completion_info.conn_id = l:id - let b:ale_completion_info.request_id = l:request_id - - if has_key(a:linter, 'completion_filter') - let b:ale_completion_info.completion_filter = a:linter.completion_filter - endif - endif - endfunction + let l:OnReady = function('s:OnReady', [a:linter, l:lsp_details]) - call ale#lsp#WaitForCapability(l:id, l:root, 'completion', function('OnReady')) + call ale#lsp#WaitForCapability(l:id, 'completion', l:OnReady) endfunction function! ale#completion#GetCompletions() abort |