mirror of
https://github.com/dense-analysis/ale
synced 2024-12-17 20:05:31 +00:00
Close #3268 - Implement :ALEImport
A new command, `:ALEImport`, has been added, which lets you import words at your cursor if a completion provider can provide a completion for that word which includes some additional text changes.
This commit is contained in:
parent
5bc49d2047
commit
c36053d4cc
@ -188,7 +188,13 @@ function! ale#completion#GetTriggerCharacter(filetype, prefix) abort
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
function! ale#completion#Filter(buffer, filetype, suggestions, prefix) abort
|
||||
function! ale#completion#Filter(
|
||||
\ buffer,
|
||||
\ filetype,
|
||||
\ suggestions,
|
||||
\ prefix,
|
||||
\ exact_prefix_match,
|
||||
\) abort
|
||||
let l:excluded_words = ale#Var(a:buffer, 'completion_excluded_words')
|
||||
|
||||
if empty(a:prefix)
|
||||
@ -215,10 +221,17 @@ function! ale#completion#Filter(buffer, filetype, suggestions, prefix) abort
|
||||
" Dictionaries is accepted here.
|
||||
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.
|
||||
if l:word[: len(a:prefix) - 1] is? a:prefix
|
||||
call add(l:filtered_suggestions, l:item)
|
||||
if a:exact_prefix_match
|
||||
" Add suggestions if the word is an exact match.
|
||||
if l:word is# a:prefix
|
||||
call add(l:filtered_suggestions, l:item)
|
||||
endif
|
||||
else
|
||||
" Add suggestions if the suggestion starts with a
|
||||
" case-insensitive match for the prefix.
|
||||
if l:word[: len(a:prefix) - 1] is? a:prefix
|
||||
call add(l:filtered_suggestions, l:item)
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
@ -241,21 +254,17 @@ function! ale#completion#Filter(buffer, filetype, suggestions, prefix) abort
|
||||
return l:filtered_suggestions
|
||||
endfunction
|
||||
|
||||
function! s:ReplaceCompletionOptions() abort
|
||||
let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '')
|
||||
|
||||
if l:source is# 'ale-automatic' || l:source is# 'ale-manual'
|
||||
" Remember the old omnifunc value, if there is one.
|
||||
" If we don't store an old one, we'll just never reset the option.
|
||||
" This will stop some random exceptions from appearing.
|
||||
if !exists('b:ale_old_omnifunc') && !empty(&l:omnifunc)
|
||||
let b:ale_old_omnifunc = &l:omnifunc
|
||||
endif
|
||||
|
||||
let &l:omnifunc = 'ale#completion#AutomaticOmniFunc'
|
||||
function! s:ReplaceCompletionOptions(source) abort
|
||||
" Remember the old omnifunc value, if there is one.
|
||||
" If we don't store an old one, we'll just never reset the option.
|
||||
" This will stop some random exceptions from appearing.
|
||||
if !exists('b:ale_old_omnifunc') && !empty(&l:omnifunc)
|
||||
let b:ale_old_omnifunc = &l:omnifunc
|
||||
endif
|
||||
|
||||
if l:source is# 'ale-automatic'
|
||||
let &l:omnifunc = 'ale#completion#AutomaticOmniFunc'
|
||||
|
||||
if a:source is# 'ale-automatic'
|
||||
if !exists('b:ale_old_completeopt')
|
||||
let b:ale_old_completeopt = &l:completeopt
|
||||
endif
|
||||
@ -318,7 +327,11 @@ function! ale#completion#AutomaticOmniFunc(findstart, base) abort
|
||||
else
|
||||
let l:result = ale#completion#GetCompletionResult()
|
||||
|
||||
call s:ReplaceCompletionOptions()
|
||||
let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '')
|
||||
|
||||
if l:source is# 'ale-automatic' || l:source is# 'ale-manual'
|
||||
call s:ReplaceCompletionOptions(l:source)
|
||||
endif
|
||||
|
||||
return l:result isnot v:null ? l:result : []
|
||||
endif
|
||||
@ -331,31 +344,53 @@ function! s:OpenCompletionMenu(...) abort
|
||||
endfunction
|
||||
|
||||
function! ale#completion#Show(result) abort
|
||||
if ale#util#Mode() isnot# 'i'
|
||||
let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '')
|
||||
|
||||
if ale#util#Mode() isnot# 'i' && l:source isnot# 'ale-import'
|
||||
return
|
||||
endif
|
||||
|
||||
" Set the list in the buffer, temporarily replace omnifunc with our
|
||||
" function, and then start omni-completion.
|
||||
" Set the list in the buffer.
|
||||
let b:ale_completion_result = a:result
|
||||
|
||||
" Don't try to open the completion menu if there's nothing to show.
|
||||
if empty(b:ale_completion_result)
|
||||
if l:source is# 'ale-import'
|
||||
" If we ran completion from :ALEImport,
|
||||
" tell the user that nothing is going to happen.
|
||||
call s:message('No possible imports found.')
|
||||
endif
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
" Replace completion options shortly before opening the menu.
|
||||
call s:ReplaceCompletionOptions()
|
||||
|
||||
let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '')
|
||||
|
||||
if l:source is# 'ale-automatic' || l:source is# 'ale-manual'
|
||||
call s:ReplaceCompletionOptions(l:source)
|
||||
|
||||
call timer_start(0, function('s:OpenCompletionMenu'))
|
||||
endif
|
||||
|
||||
if l:source is# 'ale-callback'
|
||||
call b:CompleteCallback(b:ale_completion_result)
|
||||
endif
|
||||
|
||||
if l:source is# 'ale-import'
|
||||
call ale#completion#HandleUserData(b:ale_completion_result[0])
|
||||
|
||||
let l:text_changed = '' . g:ale_lint_on_text_changed
|
||||
|
||||
" Check the buffer again right away, if linting is enabled.
|
||||
if g:ale_enabled
|
||||
\&& (
|
||||
\ l:text_changed is# '1'
|
||||
\ || l:text_changed is# 'always'
|
||||
\ || l:text_changed is# 'normal'
|
||||
\ || l:text_changed is# 'insert'
|
||||
\)
|
||||
call ale#Queue(0, '')
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#completion#GetAllTriggers() abort
|
||||
@ -386,7 +421,10 @@ endfunction
|
||||
function! s:CompletionStillValid(request_id) abort
|
||||
let [l:line, l:column] = getpos('.')[1:2]
|
||||
|
||||
return ale#util#Mode() is# 'i'
|
||||
return (
|
||||
\ ale#util#Mode() is# 'i'
|
||||
\ || b:ale_completion_info.source is# 'ale-import'
|
||||
\)
|
||||
\&& has_key(b:, 'ale_completion_info')
|
||||
\&& b:ale_completion_info.request_id == a:request_id
|
||||
\&& b:ale_completion_info.line == l:line
|
||||
@ -394,6 +432,7 @@ function! s:CompletionStillValid(request_id) abort
|
||||
\ b:ale_completion_info.column == l:column
|
||||
\ || b:ale_completion_info.source is# 'ale-omnifunc'
|
||||
\ || b:ale_completion_info.source is# 'ale-callback'
|
||||
\ || b:ale_completion_info.source is# 'ale-import'
|
||||
\)
|
||||
endfunction
|
||||
|
||||
@ -418,6 +457,7 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
|
||||
let l:buffer = bufnr('')
|
||||
let l:results = []
|
||||
let l:names_with_details = []
|
||||
let l:info = get(b:, 'ale_completion_info', {})
|
||||
|
||||
for l:suggestion in a:response.body
|
||||
let l:displayParts = []
|
||||
@ -459,7 +499,8 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
|
||||
\ 'kind': ale#completion#GetCompletionSymbols(l:suggestion.kind),
|
||||
\ 'icase': 1,
|
||||
\ 'menu': join(l:displayParts, ''),
|
||||
\ 'dup': g:ale_completion_autoimport,
|
||||
\ 'dup': get(l:info, 'additional_edits_only', 0)
|
||||
\ || g:ale_completion_autoimport,
|
||||
\ 'info': join(l:documentationParts, ''),
|
||||
\}
|
||||
|
||||
@ -469,7 +510,12 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
|
||||
\ })
|
||||
endif
|
||||
|
||||
call add(l:results, l:result)
|
||||
" Include this item if we'll accept any items,
|
||||
" or if we only want items with additional edits, and this has them.
|
||||
if !get(l:info, 'additional_edits_only', 0)
|
||||
\|| has_key(l:result, 'user_data')
|
||||
call add(l:results, l:result)
|
||||
endif
|
||||
endfor
|
||||
|
||||
let l:names = getbufvar(l:buffer, 'ale_tsserver_completion_names', [])
|
||||
@ -544,7 +590,10 @@ function! ale#completion#ParseLSPCompletions(response) abort
|
||||
" Don't use LSP items with additional text edits when autoimport for
|
||||
" completions is turned off.
|
||||
if !empty(get(l:item, 'additionalTextEdits'))
|
||||
\&& !g:ale_completion_autoimport
|
||||
\&& !(
|
||||
\ get(l:info, 'additional_edits_only', 0)
|
||||
\ || g:ale_completion_autoimport
|
||||
\)
|
||||
continue
|
||||
endif
|
||||
|
||||
@ -594,11 +643,22 @@ function! ale#completion#ParseLSPCompletions(response) abort
|
||||
endif
|
||||
endif
|
||||
|
||||
call add(l:results, l:result)
|
||||
" Include this item if we'll accept any items,
|
||||
" or if we only want items with additional edits, and this has them.
|
||||
if !get(l:info, 'additional_edits_only', 0)
|
||||
\|| has_key(l:result, 'user_data')
|
||||
call add(l:results, l:result)
|
||||
endif
|
||||
endfor
|
||||
|
||||
if has_key(l:info, 'prefix')
|
||||
let l:results = ale#completion#Filter(l:buffer, &filetype, l:results, l:info.prefix)
|
||||
let l:results = ale#completion#Filter(
|
||||
\ l:buffer,
|
||||
\ &filetype,
|
||||
\ l:results,
|
||||
\ l:info.prefix,
|
||||
\ get(l:info, 'additional_edits_only', 0),
|
||||
\)
|
||||
endif
|
||||
|
||||
return l:results[: g:ale_completion_max_suggestions - 1]
|
||||
@ -622,13 +682,18 @@ function! ale#completion#HandleTSServerResponse(conn_id, response) abort
|
||||
\ &filetype,
|
||||
\ ale#completion#ParseTSServerCompletions(a:response),
|
||||
\ b:ale_completion_info.prefix,
|
||||
\ get(b:ale_completion_info, 'additional_edits_only', 0),
|
||||
\)[: 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)
|
||||
if empty(l:names)
|
||||
" Response with no results now and skip making a redundant request
|
||||
" for nothing.
|
||||
call ale#completion#Show([])
|
||||
else
|
||||
let l:identifiers = []
|
||||
|
||||
for l:name in l:names
|
||||
@ -702,7 +767,8 @@ function! s:OnReady(linter, lsp_details) abort
|
||||
\ b:ale_completion_info.line,
|
||||
\ b:ale_completion_info.column,
|
||||
\ b:ale_completion_info.prefix,
|
||||
\ g:ale_completion_autoimport,
|
||||
\ get(b:ale_completion_info, 'additional_edits_only', 0)
|
||||
\ || g:ale_completion_autoimport,
|
||||
\)
|
||||
else
|
||||
" Send a message saying the buffer has changed first, otherwise
|
||||
@ -761,9 +827,19 @@ function! ale#completion#GetCompletions(...) abort
|
||||
let b:CompleteCallback = l:CompleteCallback
|
||||
endif
|
||||
|
||||
let [l:line, l:column] = getpos('.')[1:2]
|
||||
if has_key(l:options, 'line') && has_key(l:options, 'column')
|
||||
" Use a provided line and column, if given.
|
||||
let l:line = l:options.line
|
||||
let l:column = l:options.column
|
||||
else
|
||||
let [l:line, l:column] = getpos('.')[1:2]
|
||||
endif
|
||||
|
||||
let l:prefix = ale#completion#GetPrefix(&filetype, l:line, l:column)
|
||||
if has_key(l:options, 'prefix')
|
||||
let l:prefix = l:options.prefix
|
||||
else
|
||||
let l:prefix = ale#completion#GetPrefix(&filetype, l:line, l:column)
|
||||
endif
|
||||
|
||||
if l:source is# 'ale-automatic' && empty(l:prefix)
|
||||
return 0
|
||||
@ -782,6 +858,11 @@ function! ale#completion#GetCompletions(...) abort
|
||||
\}
|
||||
unlet! b:ale_completion_result
|
||||
|
||||
if has_key(l:options, 'additional_edits_only')
|
||||
let b:ale_completion_info.additional_edits_only =
|
||||
\ l:options.additional_edits_only
|
||||
endif
|
||||
|
||||
let l:buffer = bufnr('')
|
||||
let l:Callback = function('s:OnReady')
|
||||
|
||||
@ -798,6 +879,37 @@ function! ale#completion#GetCompletions(...) abort
|
||||
return l:started
|
||||
endfunction
|
||||
|
||||
function! s:message(message) abort
|
||||
call ale#util#Execute('echom ' . string(a:message))
|
||||
endfunction
|
||||
|
||||
" This function implements the :ALEImport command.
|
||||
function! ale#completion#Import() abort
|
||||
let l:word = expand('<cword>')
|
||||
|
||||
if empty(l:word)
|
||||
call s:message('Nothing to complete at cursor!')
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
let [l:line, l:column] = getpos('.')[1:2]
|
||||
let l:column = searchpos('\V' . escape(l:word, '/\'), 'bn', l:line)[1]
|
||||
|
||||
if l:column isnot 0
|
||||
let l:started = ale#completion#GetCompletions('ale-import', {
|
||||
\ 'line': l:line,
|
||||
\ 'column': l:column,
|
||||
\ 'prefix': l:word,
|
||||
\ 'additional_edits_only': 1,
|
||||
\})
|
||||
|
||||
if !l:started
|
||||
call s:message('No completion providers are available.')
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#completion#OmniFunc(findstart, base) abort
|
||||
if a:findstart
|
||||
let l:started = ale#completion#GetCompletions('ale-omnifunc')
|
||||
@ -876,6 +988,7 @@ function! ale#completion#HandleUserData(completed_item) abort
|
||||
if l:source isnot# 'ale-automatic'
|
||||
\&& l:source isnot# 'ale-manual'
|
||||
\&& l:source isnot# 'ale-callback'
|
||||
\&& l:source isnot# 'ale-import'
|
||||
return
|
||||
endif
|
||||
|
||||
|
28
doc/ale.txt
28
doc/ale.txt
@ -526,6 +526,12 @@ completion information with Deoplete, consult Deoplete's documentation.
|
||||
ALE by can support automatic imports from external modules. This behavior can
|
||||
be enabled by setting the |g:ale_completion_autoimport| variable to `1`.
|
||||
|
||||
You can manually request imports for symbols at the cursor with the
|
||||
|ALEImport| command. The word at the cursor must be an exact match for some
|
||||
potential completion result which includes additional text to insert into the
|
||||
current buffer, which ALE will assume is code for an import line. This command
|
||||
can be useful when your code already contains something you need to import.
|
||||
|
||||
When working with TypeScript files, ALE can remove warnings from your
|
||||
completions by setting the |g:ale_completion_tsserver_remove_warnings|
|
||||
variable to 1.
|
||||
@ -3052,6 +3058,23 @@ ALEHover *ALEHover*
|
||||
A plug mapping `<Plug>(ale_hover)` is defined for this command.
|
||||
|
||||
|
||||
ALEImport *ALEImport*
|
||||
|
||||
Try to import a symbol using `tsserver` or a Language Server.
|
||||
|
||||
ALE will look for completions for the word at the cursor which contain
|
||||
additional text edits that possible insert lines to import the symbol. The
|
||||
first match with additional text edits will be used, and may add other code
|
||||
to the current buffer other than import lines.
|
||||
|
||||
If linting is enabled, and |g:ale_lint_on_text_changed| is set to ever check
|
||||
buffers when text is changed, the buffer will be checked again after changes
|
||||
are made.
|
||||
|
||||
A Plug mapping `<Plug>(ale_import)` is defined for this command. This
|
||||
mapping should only be bound for normal mode.
|
||||
|
||||
|
||||
ALEOrganizeImports *ALEOrganizeImports*
|
||||
|
||||
Organize imports using tsserver. Currently not implemented for LSPs.
|
||||
@ -3059,9 +3082,10 @@ ALEOrganizeImports *ALEOrganizeImports*
|
||||
|
||||
ALERename *ALERename*
|
||||
|
||||
Rename a symbol using TypeScript server or Language Server.
|
||||
Rename a symbol using `tsserver` or a Language Server.
|
||||
|
||||
The user will be prompted for a new name.
|
||||
The symbol where the cursor is resting will be the symbol renamed, and a
|
||||
prompt will open to request a new name.
|
||||
|
||||
|
||||
ALERepeatSelection *ALERepeatSelection*
|
||||
|
@ -229,8 +229,12 @@ command! -bar ALEDocumentation :call ale#hover#ShowDocumentationAtCursor()
|
||||
" Search for appearances of a symbol, such as a type name or function name.
|
||||
command! -nargs=1 ALESymbolSearch :call ale#symbol#Search(<q-args>)
|
||||
|
||||
" Complete text with tsserver and LSP
|
||||
command! -bar ALEComplete :call ale#completion#GetCompletions('ale-manual')
|
||||
|
||||
" Try to find completions for the current symbol that add additional text.
|
||||
command! -bar ALEImport :call ale#completion#Import()
|
||||
|
||||
" Rename symbols using tsserver and LSP
|
||||
command! -bar ALERename :call ale#rename#Execute()
|
||||
|
||||
@ -275,6 +279,7 @@ nnoremap <silent> <Plug>(ale_find_references) :ALEFindReferences<Return>
|
||||
nnoremap <silent> <Plug>(ale_hover) :ALEHover<Return>
|
||||
nnoremap <silent> <Plug>(ale_documentation) :ALEDocumentation<Return>
|
||||
inoremap <silent> <Plug>(ale_complete) <C-\><C-O>:ALEComplete<Return>
|
||||
nnoremap <silent> <Plug>(ale_import) :ALEImport<Return>
|
||||
nnoremap <silent> <Plug>(ale_rename) :ALERename<Return>
|
||||
nnoremap <silent> <Plug>(ale_repeat_selection) :ALERepeatSelection<Return>
|
||||
|
||||
|
562
test/completion/test_ale_import_command.vader
Normal file
562
test/completion/test_ale_import_command.vader
Normal file
@ -0,0 +1,562 @@
|
||||
Before:
|
||||
Save g:ale_enabled
|
||||
Save b:ale_enabled
|
||||
Save g:ale_lint_on_text_changed
|
||||
Save g:ale_completion_enabled
|
||||
Save g:ale_completion_autoimport
|
||||
Save g:ale_completion_max_suggestions
|
||||
Save g:ale_linters
|
||||
Save b:ale_linters
|
||||
|
||||
let g:ale_enabled = 0
|
||||
let b:ale_enabled = 0
|
||||
let g:ale_lint_on_text_changed = 'always'
|
||||
let g:ale_completion_enabled = 0
|
||||
let g:ale_completion_autoimport = 0
|
||||
let g:ale_completion_max_suggestions = 50
|
||||
let g:ale_linters = {'typescript': ['tsserver'], 'python': ['pyre']}
|
||||
unlet! b:ale_linters
|
||||
|
||||
let g:server_started_value = 1
|
||||
let g:request_id = 0
|
||||
let g:LastCallback = v:null
|
||||
let g:LastHandleCallback = v:null
|
||||
let g:sent_message_list = []
|
||||
let g:code_action_list = []
|
||||
let g:execute_list = []
|
||||
let g:ale_queue_call_list = []
|
||||
|
||||
runtime autoload/ale.vim
|
||||
runtime autoload/ale/util.vim
|
||||
runtime autoload/ale/code_action.vim
|
||||
runtime autoload/ale/lsp.vim
|
||||
runtime autoload/ale/lsp_linter.vim
|
||||
|
||||
function! ale#util#Execute(expr) abort
|
||||
call add(g:execute_list, a:expr)
|
||||
endfunction
|
||||
|
||||
function! ale#Queue(...) abort
|
||||
call add(g:ale_queue_call_list, a:000)
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#RegisterCallback(id, Callback) abort
|
||||
let g:LastHandleCallback = a:Callback
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#NotifyForChanges(id, buffer) abort
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#HasCapability(id, capability) abort
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#Send(id, message) abort
|
||||
let g:request_id += 1
|
||||
|
||||
call add(g:sent_message_list, a:message)
|
||||
|
||||
return g:request_id
|
||||
endfunction
|
||||
|
||||
function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort
|
||||
let g:LastCallback = a:Callback
|
||||
|
||||
return g:server_started_value
|
||||
endfunction
|
||||
|
||||
function! ale#code_action#HandleCodeAction(code_action, should_save) abort
|
||||
Assert !a:should_save
|
||||
|
||||
call add(g:code_action_list, a:code_action)
|
||||
endfunction
|
||||
|
||||
function GetLastMessage()
|
||||
return get(g:execute_list, -1, '')
|
||||
endfunction
|
||||
|
||||
function CheckLintStates(conn_id, message)
|
||||
" Check that we request more linter results after adding completions.
|
||||
AssertEqual [[0, '']], g:ale_queue_call_list
|
||||
|
||||
let g:ale_enabled = 0
|
||||
|
||||
let g:ale_queue_call_list = []
|
||||
call g:LastHandleCallback(a:conn_id, a:message)
|
||||
AssertEqual [], g:ale_queue_call_list
|
||||
|
||||
let g:ale_enabled = 1
|
||||
let g:ale_lint_on_text_changed = 1
|
||||
|
||||
let g:ale_queue_call_list = []
|
||||
call g:LastHandleCallback(a:conn_id, a:message)
|
||||
AssertEqual [[0, '']], g:ale_queue_call_list
|
||||
|
||||
let g:ale_lint_on_text_changed = 'normal'
|
||||
|
||||
let g:ale_queue_call_list = []
|
||||
call g:LastHandleCallback(a:conn_id, a:message)
|
||||
AssertEqual [[0, '']], g:ale_queue_call_list
|
||||
|
||||
let g:ale_lint_on_text_changed = 'insert'
|
||||
|
||||
let g:ale_queue_call_list = []
|
||||
call g:LastHandleCallback(a:conn_id, a:message)
|
||||
AssertEqual [[0, '']], g:ale_queue_call_list
|
||||
|
||||
let g:ale_queue_call_list = []
|
||||
let g:ale_lint_on_text_changed = 'never'
|
||||
|
||||
call g:LastHandleCallback(a:conn_id, a:message)
|
||||
AssertEqual [], g:ale_queue_call_list
|
||||
|
||||
let g:ale_lint_on_text_changed = '0'
|
||||
|
||||
call g:LastHandleCallback(a:conn_id, a:message)
|
||||
AssertEqual [], g:ale_queue_call_list
|
||||
|
||||
let g:ale_lint_on_text_changed = 0
|
||||
|
||||
call g:LastHandleCallback(a:conn_id, a:message)
|
||||
AssertEqual [], g:ale_queue_call_list
|
||||
|
||||
let g:ale_lint_on_text_changed = 'xxx'
|
||||
|
||||
call g:LastHandleCallback(a:conn_id, a:message)
|
||||
AssertEqual [], g:ale_queue_call_list
|
||||
endfunction
|
||||
|
||||
After:
|
||||
call ale#linter#Reset()
|
||||
|
||||
Restore
|
||||
|
||||
delfunction GetLastMessage
|
||||
delfunction CheckLintStates
|
||||
|
||||
unlet! g:LastCallback
|
||||
unlet! g:LastHandleCallback
|
||||
unlet! g:request_id
|
||||
unlet! g:server_started_value
|
||||
unlet! g:sent_message_list
|
||||
unlet! g:code_action_list
|
||||
unlet! g:ale_queue_call_list
|
||||
unlet! g:execute_list
|
||||
unlet! g:received_message
|
||||
unlet! b:ale_old_omnifunc
|
||||
unlet! b:ale_old_completeopt
|
||||
unlet! b:ale_completion_info
|
||||
unlet! b:ale_completion_result
|
||||
unlet! b:ale_complete_done_time
|
||||
|
||||
runtime autoload/ale.vim
|
||||
runtime autoload/ale/util.vim
|
||||
runtime autoload/ale/code_action.vim
|
||||
runtime autoload/ale/lsp.vim
|
||||
runtime autoload/ale/lsp_linter.vim
|
||||
|
||||
Given typescript(Some example TypeScript code):
|
||||
let xyz = 123
|
||||
let foo = missingword
|
||||
|
||||
let abc = 456
|
||||
|
||||
Execute(ALEImport should complain when there's no word at the cursor):
|
||||
call setpos('.', [bufnr(''), 3, 1, 0])
|
||||
ALEImport
|
||||
|
||||
AssertEqual 'echom ''Nothing to complete at cursor!''', GetLastMessage()
|
||||
|
||||
Execute(ALEImport should tell the user if no LSP is available):
|
||||
let g:server_started_value = 0
|
||||
|
||||
call setpos('.', [bufnr(''), 2, 16, 0])
|
||||
ALEImport
|
||||
|
||||
AssertEqual
|
||||
\ 'echom ''No completion providers are available.''',
|
||||
\ GetLastMessage()
|
||||
|
||||
Execute(ALEImport should request imports correctly for tsserver):
|
||||
call setpos('.', [bufnr(''), 2, 16, 0])
|
||||
|
||||
ALEImport
|
||||
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'conn_id': 0,
|
||||
\ 'request_id': 0,
|
||||
\ 'source': 'ale-import',
|
||||
\ 'column': 11,
|
||||
\ 'line': 2,
|
||||
\ 'line_length': 21,
|
||||
\ 'prefix': 'missingword',
|
||||
\ 'additional_edits_only': 1,
|
||||
\ },
|
||||
\ b:ale_completion_info
|
||||
Assert g:LastCallback isnot v:null
|
||||
|
||||
call g:LastCallback(ale#linter#Get(&filetype)[0], {
|
||||
\ 'connection_id': 347,
|
||||
\ 'buffer': bufnr(''),
|
||||
\})
|
||||
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'conn_id': 347,
|
||||
\ 'request_id': 1,
|
||||
\ 'source': 'ale-import',
|
||||
\ 'column': 11,
|
||||
\ 'line': 2,
|
||||
\ 'line_length': 21,
|
||||
\ 'prefix': 'missingword',
|
||||
\ 'additional_edits_only': 1,
|
||||
\ },
|
||||
\ b:ale_completion_info
|
||||
Assert g:LastHandleCallback isnot v:null
|
||||
|
||||
call g:LastHandleCallback(347, {
|
||||
\ 'request_seq': 1,
|
||||
\ 'command': 'completions',
|
||||
\ 'body': [
|
||||
\ {'name': 'missingwordIgnoreMe'},
|
||||
\ {'name': 'missingword'},
|
||||
\ ],
|
||||
\})
|
||||
|
||||
AssertEqual
|
||||
\ [
|
||||
\ [0, 'ts@completions', {
|
||||
\ 'file': expand('%:p'),
|
||||
\ 'includeExternalModuleExports': 1,
|
||||
\ 'offset': 11,
|
||||
\ 'line': 2,
|
||||
\ 'prefix': 'missingword',
|
||||
\ }],
|
||||
\ [0, 'ts@completionEntryDetails', {
|
||||
\ 'file': expand('%:p'),
|
||||
\ 'entryNames': [{'name': 'missingword'}],
|
||||
\ 'offset': 11,
|
||||
\ 'line': 2,
|
||||
\ }]
|
||||
\ ],
|
||||
\ g:sent_message_list
|
||||
AssertEqual 2, b:ale_completion_info.request_id
|
||||
|
||||
let g:ale_enabled = 1
|
||||
let g:received_message = {
|
||||
\ 'request_seq': 2,
|
||||
\ 'command': 'completionEntryDetails',
|
||||
\ 'body': [
|
||||
\ {
|
||||
\ 'name': 'missingword',
|
||||
\ 'kind': 'className',
|
||||
\ 'displayParts': [],
|
||||
\ 'codeActions': [{
|
||||
\ 'description': 'import { missingword } from "./Something";',
|
||||
\ 'changes': [],
|
||||
\ }],
|
||||
\ },
|
||||
\ ],
|
||||
\}
|
||||
call g:LastHandleCallback(347, g:received_message)
|
||||
|
||||
AssertEqual
|
||||
\ [
|
||||
\ {
|
||||
\ 'description': 'import { missingword } from "./Something";',
|
||||
\ 'changes': [],
|
||||
\ },
|
||||
\ ],
|
||||
\ g:code_action_list
|
||||
|
||||
call CheckLintStates(347, g:received_message)
|
||||
|
||||
Execute(ALEImport should tell the user when no completions were found from tsserver):
|
||||
call setpos('.', [bufnr(''), 2, 16, 0])
|
||||
|
||||
ALEImport
|
||||
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'conn_id': 0,
|
||||
\ 'request_id': 0,
|
||||
\ 'source': 'ale-import',
|
||||
\ 'column': 11,
|
||||
\ 'line': 2,
|
||||
\ 'line_length': 21,
|
||||
\ 'prefix': 'missingword',
|
||||
\ 'additional_edits_only': 1,
|
||||
\ },
|
||||
\ b:ale_completion_info
|
||||
Assert g:LastCallback isnot v:null
|
||||
|
||||
call g:LastCallback(ale#linter#Get(&filetype)[0], {
|
||||
\ 'connection_id': 347,
|
||||
\ 'buffer': bufnr(''),
|
||||
\})
|
||||
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'conn_id': 347,
|
||||
\ 'request_id': 1,
|
||||
\ 'source': 'ale-import',
|
||||
\ 'column': 11,
|
||||
\ 'line': 2,
|
||||
\ 'line_length': 21,
|
||||
\ 'prefix': 'missingword',
|
||||
\ 'additional_edits_only': 1,
|
||||
\ },
|
||||
\ b:ale_completion_info
|
||||
Assert g:LastHandleCallback isnot v:null
|
||||
|
||||
call g:LastHandleCallback(347, {
|
||||
\ 'request_seq': 1,
|
||||
\ 'command': 'completions',
|
||||
\ 'body': [
|
||||
\ {'name': 'missingwordIgnoreMe'},
|
||||
\ ],
|
||||
\})
|
||||
|
||||
AssertEqual 'echom ''No possible imports found.''', GetLastMessage()
|
||||
|
||||
Given python(Some example Python code):
|
||||
xyz = 123
|
||||
foo = missingword
|
||||
|
||||
abc = 456
|
||||
|
||||
Execute(ALEImport should request imports correctly for language servers):
|
||||
call setpos('.', [bufnr(''), 2, 12, 0])
|
||||
|
||||
ALEImport
|
||||
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'conn_id': 0,
|
||||
\ 'request_id': 0,
|
||||
\ 'source': 'ale-import',
|
||||
\ 'column': 7,
|
||||
\ 'line': 2,
|
||||
\ 'line_length': 17,
|
||||
\ 'prefix': 'missingword',
|
||||
\ 'additional_edits_only': 1,
|
||||
\ },
|
||||
\ b:ale_completion_info
|
||||
Assert g:LastCallback isnot v:null
|
||||
|
||||
call g:LastCallback(ale#linter#Get(&filetype)[0], {
|
||||
\ 'connection_id': 347,
|
||||
\ 'buffer': bufnr(''),
|
||||
\})
|
||||
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'conn_id': 347,
|
||||
\ 'request_id': 1,
|
||||
\ 'source': 'ale-import',
|
||||
\ 'column': 7,
|
||||
\ 'line': 2,
|
||||
\ 'line_length': 17,
|
||||
\ 'prefix': 'missingword',
|
||||
\ 'additional_edits_only': 1,
|
||||
\ 'completion_filter': 'ale#completion#python#CompletionItemFilter',
|
||||
\ },
|
||||
\ b:ale_completion_info
|
||||
Assert g:LastHandleCallback isnot v:null
|
||||
|
||||
AssertEqual
|
||||
\ [
|
||||
\ [0, 'textDocument/completion', {
|
||||
\ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))},
|
||||
\ 'position': {'character': 6, 'line': 1}
|
||||
\ }],
|
||||
\ ],
|
||||
\ g:sent_message_list
|
||||
AssertEqual 1, b:ale_completion_info.request_id
|
||||
|
||||
let g:ale_enabled = 1
|
||||
let g:received_message = {
|
||||
\ 'id': 1,
|
||||
\ 'jsonrpc': '2.0',
|
||||
\ 'result': {
|
||||
\ 'isIncomplete': v:false,
|
||||
\ 'items': [
|
||||
\ {
|
||||
\ 'detail': 'Some other word we should ignore',
|
||||
\ 'filterText': 'missingwordIgnoreMe',
|
||||
\ 'insertText': 'missingwordIgnoreMe',
|
||||
\ 'insertTextFormat': 1,
|
||||
\ 'kind': 6,
|
||||
\ 'label': ' missingwordIgnoreMe',
|
||||
\ 'sortText': '3ee19999missingword',
|
||||
\ 'additionalTextEdits': [
|
||||
\ {
|
||||
\ 'range': {
|
||||
\ 'start': {'line': 1, 'character': 1},
|
||||
\ 'end': {'line': 2, 'character': 1},
|
||||
\ },
|
||||
\ 'newText': 'from something import missingwordIgnoreMe',
|
||||
\ },
|
||||
\ ],
|
||||
\ },
|
||||
\ {
|
||||
\ 'detail': 'Some word without text edits',
|
||||
\ 'filterText': 'missingword',
|
||||
\ 'insertText': 'missingword',
|
||||
\ 'insertTextFormat': 1,
|
||||
\ 'kind': 6,
|
||||
\ 'label': ' missingword',
|
||||
\ 'sortText': '3ee19999missingword',
|
||||
\ },
|
||||
\ {
|
||||
\ 'detail': 'The word we should use',
|
||||
\ 'filterText': 'missingword',
|
||||
\ 'insertText': 'missingword',
|
||||
\ 'insertTextFormat': 1,
|
||||
\ 'kind': 6,
|
||||
\ 'label': ' missingword',
|
||||
\ 'sortText': '3ee19999missingword',
|
||||
\ 'additionalTextEdits': [
|
||||
\ {
|
||||
\ 'range': {
|
||||
\ 'start': {'line': 1, 'character': 1},
|
||||
\ 'end': {'line': 2, 'character': 1},
|
||||
\ },
|
||||
\ 'newText': 'from something import missingword',
|
||||
\ },
|
||||
\ ],
|
||||
\ },
|
||||
\ {
|
||||
\ 'detail': 'The other word we should not use',
|
||||
\ 'filterText': 'missingword',
|
||||
\ 'insertText': 'missingword',
|
||||
\ 'insertTextFormat': 1,
|
||||
\ 'kind': 6,
|
||||
\ 'label': ' missingword',
|
||||
\ 'sortText': '3ee19999missingword',
|
||||
\ 'additionalTextEdits': [
|
||||
\ {
|
||||
\ 'range': {
|
||||
\ 'start': {'line': 1, 'character': 1},
|
||||
\ 'end': {'line': 2, 'character': 1},
|
||||
\ },
|
||||
\ 'newText': 'from something_else import missingword',
|
||||
\ },
|
||||
\ ],
|
||||
\ },
|
||||
\ ],
|
||||
\ },
|
||||
\}
|
||||
call g:LastHandleCallback(347, g:received_message)
|
||||
|
||||
AssertEqual
|
||||
\ [
|
||||
\ {
|
||||
\ 'description': 'completion',
|
||||
\ 'changes': [
|
||||
\ {
|
||||
\ 'fileName': expand('%:p'),
|
||||
\ 'textChanges': [
|
||||
\ {
|
||||
\ 'start': {'line': 2, 'offset': 2},
|
||||
\ 'end': {'line': 3, 'offset': 2},
|
||||
\ 'newText': 'from something import missingword',
|
||||
\ },
|
||||
\ ],
|
||||
\ },
|
||||
\ ],
|
||||
\ },
|
||||
\ ],
|
||||
\ g:code_action_list
|
||||
|
||||
call CheckLintStates(347, g:received_message)
|
||||
|
||||
Execute(ALEImport should tell the user when no completions were found from a language server):
|
||||
call setpos('.', [bufnr(''), 2, 12, 0])
|
||||
|
||||
ALEImport
|
||||
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'conn_id': 0,
|
||||
\ 'request_id': 0,
|
||||
\ 'source': 'ale-import',
|
||||
\ 'column': 7,
|
||||
\ 'line': 2,
|
||||
\ 'line_length': 17,
|
||||
\ 'prefix': 'missingword',
|
||||
\ 'additional_edits_only': 1,
|
||||
\ },
|
||||
\ b:ale_completion_info
|
||||
Assert g:LastCallback isnot v:null
|
||||
|
||||
call g:LastCallback(ale#linter#Get(&filetype)[0], {
|
||||
\ 'connection_id': 347,
|
||||
\ 'buffer': bufnr(''),
|
||||
\})
|
||||
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'conn_id': 347,
|
||||
\ 'request_id': 1,
|
||||
\ 'source': 'ale-import',
|
||||
\ 'column': 7,
|
||||
\ 'line': 2,
|
||||
\ 'line_length': 17,
|
||||
\ 'prefix': 'missingword',
|
||||
\ 'additional_edits_only': 1,
|
||||
\ 'completion_filter': 'ale#completion#python#CompletionItemFilter',
|
||||
\ },
|
||||
\ b:ale_completion_info
|
||||
Assert g:LastHandleCallback isnot v:null
|
||||
|
||||
AssertEqual
|
||||
\ [
|
||||
\ [0, 'textDocument/completion', {
|
||||
\ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))},
|
||||
\ 'position': {'character': 6, 'line': 1}
|
||||
\ }],
|
||||
\ ],
|
||||
\ g:sent_message_list
|
||||
AssertEqual 1, b:ale_completion_info.request_id
|
||||
|
||||
let g:received_message = {
|
||||
\ 'id': 1,
|
||||
\ 'jsonrpc': '2.0',
|
||||
\ 'result': {
|
||||
\ 'isIncomplete': v:false,
|
||||
\ 'items': [
|
||||
\ {
|
||||
\ 'detail': 'Some other word we should ignore',
|
||||
\ 'filterText': 'missingwordIgnoreMe',
|
||||
\ 'insertText': 'missingwordIgnoreMe',
|
||||
\ 'insertTextFormat': 1,
|
||||
\ 'kind': 6,
|
||||
\ 'label': ' missingwordIgnoreMe',
|
||||
\ 'sortText': '3ee19999missingword',
|
||||
\ 'additionalTextEdits': [
|
||||
\ {
|
||||
\ 'range': {
|
||||
\ 'start': {'line': 1, 'character': 1},
|
||||
\ 'end': {'line': 2, 'character': 1},
|
||||
\ },
|
||||
\ 'newText': 'from something import missingwordIgnoreMe',
|
||||
\ },
|
||||
\ ],
|
||||
\ },
|
||||
\ {
|
||||
\ 'detail': 'Some word without text edits',
|
||||
\ 'filterText': 'missingword',
|
||||
\ 'insertText': 'missingword',
|
||||
\ 'insertTextFormat': 1,
|
||||
\ 'kind': 6,
|
||||
\ 'label': ' missingword',
|
||||
\ 'sortText': '3ee19999missingword',
|
||||
\ },
|
||||
\ ],
|
||||
\ },
|
||||
\}
|
||||
call g:LastHandleCallback(347, g:received_message)
|
||||
|
||||
AssertEqual 'echom ''No possible imports found.''', GetLastMessage()
|
@ -12,13 +12,24 @@ After:
|
||||
Execute(Prefix filtering should work for Lists of strings):
|
||||
AssertEqual
|
||||
\ ['FooBar', 'foo'],
|
||||
\ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], 'foo')
|
||||
\ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], 'foo', 0)
|
||||
AssertEqual
|
||||
\ ['FooBar', 'FongBar', 'baz', 'foo'],
|
||||
\ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], '.')
|
||||
\ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], '.', 0)
|
||||
AssertEqual
|
||||
\ ['FooBar', 'FongBar', 'baz', 'foo'],
|
||||
\ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], '')
|
||||
\ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], '', 0)
|
||||
|
||||
Execute(Exact filtering should work):
|
||||
AssertEqual
|
||||
\ ['foo'],
|
||||
\ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], 'foo', 1)
|
||||
AssertEqual
|
||||
\ ['FooBar', 'FongBar', 'baz', 'foo'],
|
||||
\ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], '.', 1)
|
||||
AssertEqual
|
||||
\ ['Foo'],
|
||||
\ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'Foo', 'foo'], 'Foo', 1)
|
||||
|
||||
Execute(Prefix filtering should work for completion items):
|
||||
AssertEqual
|
||||
@ -32,7 +43,8 @@ Execute(Prefix filtering should work for completion items):
|
||||
\ {'word': 'baz'},
|
||||
\ {'word': 'foo'},
|
||||
\ ],
|
||||
\ 'foo'
|
||||
\ 'foo',
|
||||
\ 0,
|
||||
\ )
|
||||
|
||||
AssertEqual
|
||||
@ -51,7 +63,8 @@ Execute(Prefix filtering should work for completion items):
|
||||
\ {'word': 'baz'},
|
||||
\ {'word': 'foo'},
|
||||
\ ],
|
||||
\ '.'
|
||||
\ '.',
|
||||
\ 0,
|
||||
\ )
|
||||
|
||||
Execute(Excluding words from completion results should work):
|
||||
@ -66,7 +79,8 @@ Execute(Excluding words from completion results should work):
|
||||
\ {'word': 'Italian'},
|
||||
\ {'word': 'it'},
|
||||
\ ],
|
||||
\ 'it'
|
||||
\ 'it',
|
||||
\ 0,
|
||||
\ )
|
||||
|
||||
AssertEqual
|
||||
@ -78,7 +92,8 @@ Execute(Excluding words from completion results should work):
|
||||
\ {'word': 'describe'},
|
||||
\ {'word': 'Deutsch'},
|
||||
\ ],
|
||||
\ 'de'
|
||||
\ 'de',
|
||||
\ 0,
|
||||
\ )
|
||||
|
||||
AssertEqual
|
||||
@ -90,7 +105,8 @@ Execute(Excluding words from completion results should work):
|
||||
\ {'word': 'describe'},
|
||||
\ {'word': 'Deutsch'},
|
||||
\ ],
|
||||
\ '.'
|
||||
\ '.',
|
||||
\ 0,
|
||||
\ )
|
||||
|
||||
Execute(Excluding words from completion results should work with lists of Strings):
|
||||
@ -98,29 +114,29 @@ Execute(Excluding words from completion results should work with lists of String
|
||||
|
||||
AssertEqual
|
||||
\ ['Italian'],
|
||||
\ ale#completion#Filter(bufnr(''), '', ['Italian', 'it'], 'it')
|
||||
\ ale#completion#Filter(bufnr(''), '', ['Italian', 'it'], 'it', 0)
|
||||
AssertEqual
|
||||
\ ['Deutsch'],
|
||||
\ ale#completion#Filter(bufnr(''), '', ['describe', 'Deutsch'], 'de')
|
||||
\ ale#completion#Filter(bufnr(''), '', ['describe', 'Deutsch'], 'de', 0)
|
||||
AssertEqual
|
||||
\ ['Deutsch'],
|
||||
\ ale#completion#Filter(bufnr(''), '', ['describe', 'Deutsch'], '.')
|
||||
\ ale#completion#Filter(bufnr(''), '', ['describe', 'Deutsch'], '.', 0)
|
||||
AssertEqual
|
||||
\ ['Deutsch'],
|
||||
\ ale#completion#Filter(bufnr(''), '', ['Deutsch'], '')
|
||||
\ ale#completion#Filter(bufnr(''), '', ['Deutsch'], '', 0)
|
||||
|
||||
Execute(Filtering shouldn't modify the original list):
|
||||
let b:ale_completion_excluded_words = ['it', 'describe']
|
||||
let b:suggestions = [{'word': 'describe'}]
|
||||
|
||||
AssertEqual [], ale#completion#Filter(bufnr(''), '', b:suggestions, '.')
|
||||
AssertEqual [], ale#completion#Filter(bufnr(''), '', b:suggestions, '.', 0)
|
||||
AssertEqual b:suggestions, [{'word': 'describe'}]
|
||||
AssertEqual [], ale#completion#Filter(bufnr(''), '', b:suggestions, 'de')
|
||||
AssertEqual [], ale#completion#Filter(bufnr(''), '', b:suggestions, 'de', 0)
|
||||
AssertEqual b:suggestions, [{'word': 'describe'}]
|
||||
|
||||
Execute(Filtering should respect filetype triggers):
|
||||
let b:suggestions = [{'word': 'describe'}]
|
||||
|
||||
AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), '', b:suggestions, '.')
|
||||
AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), 'rust', b:suggestions, '.')
|
||||
AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), 'rust', b:suggestions, '::')
|
||||
AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), '', b:suggestions, '.', 0)
|
||||
AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), 'rust', b:suggestions, '.', 0)
|
||||
AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), 'rust', b:suggestions, '::', 0)
|
||||
|
Loading…
Reference in New Issue
Block a user