summaryrefslogtreecommitdiff
path: root/autoload/ale/completion.vim
diff options
context:
space:
mode:
Diffstat (limited to 'autoload/ale/completion.vim')
-rw-r--r--autoload/ale/completion.vim141
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