Close #3333 - Add an ALECompletePost event

Add an `ALECompletePost` event along with everything needed to make it
useful for its primary purpose: fixing code after inserting completions.

* `ALEFix` can now be called with a bang (`!`) to suppress errors.
* A new `ALELintStop` command lets you stop linting, and start it later.
This commit is contained in:
w0rp 2020-09-08 21:40:10 +01:00
parent b4b75126f9
commit 7d90ff56d9
No known key found for this signature in database
GPG key ID: 0FC1ECAA8C81CD83
12 changed files with 273 additions and 92 deletions

View file

@ -503,17 +503,19 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
\ || g:ale_completion_autoimport, \ || g:ale_completion_autoimport,
\ 'info': join(l:documentationParts, ''), \ 'info': join(l:documentationParts, ''),
\} \}
" This flag is used to tell if this completion came from ALE or not.
let l:user_data = {'_ale_completion_item': 1}
if has_key(l:suggestion, 'codeActions') if has_key(l:suggestion, 'codeActions')
let l:result.user_data = json_encode({ let l:user_data.code_actions = l:suggestion.codeActions
\ 'codeActions': l:suggestion.codeActions,
\ })
endif endif
let l:result.user_data = json_encode(l:user_data)
" Include this item if we'll accept any items, " Include this item if we'll accept any items,
" or if we only want items with additional edits, and this has them. " or if we only want items with additional edits, and this has them.
if !get(l:info, 'additional_edits_only', 0) if !get(l:info, 'additional_edits_only', 0)
\|| has_key(l:result, 'user_data') \|| has_key(l:user_data, 'code_actions')
call add(l:results, l:result) call add(l:results, l:result)
endif endif
endfor endfor
@ -534,6 +536,7 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
\ 'icase': 1, \ 'icase': 1,
\ 'menu': '', \ 'menu': '',
\ 'info': '', \ 'info': '',
\ 'user_data': json_encode({'_ale_completion_item': 1}),
\}) \})
endfor endfor
endif endif
@ -610,6 +613,8 @@ function! ale#completion#ParseLSPCompletions(response) abort
\ 'menu': get(l:item, 'detail', ''), \ 'menu': get(l:item, 'detail', ''),
\ 'info': (type(l:doc) is v:t_string ? l:doc : ''), \ 'info': (type(l:doc) is v:t_string ? l:doc : ''),
\} \}
" This flag is used to tell if this completion came from ALE or not.
let l:user_data = {'_ale_completion_item': 1}
if has_key(l:item, 'additionalTextEdits') if has_key(l:item, 'additionalTextEdits')
let l:text_changes = [] let l:text_changes = []
@ -629,24 +634,24 @@ function! ale#completion#ParseLSPCompletions(response) abort
endfor endfor
if !empty(l:text_changes) if !empty(l:text_changes)
let l:result.user_data = json_encode({ let l:user_data.code_actions = [{
\ 'codeActions': [{ \ 'description': 'completion',
\ 'description': 'completion', \ 'changes': [
\ 'changes': [ \ {
\ { \ 'fileName': expand('#' . l:buffer . ':p'),
\ 'fileName': expand('#' . l:buffer . ':p'), \ 'textChanges': l:text_changes,
\ 'textChanges': l:text_changes, \ },
\ } \ ],
\ ], \}]
\ }],
\})
endif endif
endif endif
let l:result.user_data = json_encode(l:user_data)
" Include this item if we'll accept any items, " Include this item if we'll accept any items,
" or if we only want items with additional edits, and this has them. " or if we only want items with additional edits, and this has them.
if !get(l:info, 'additional_edits_only', 0) if !get(l:info, 'additional_edits_only', 0)
\|| has_key(l:result, 'user_data') \|| has_key(l:user_data, 'code_actions')
call add(l:results, l:result) call add(l:results, l:result)
endif endif
endfor endfor
@ -983,30 +988,29 @@ function! ale#completion#Queue() abort
endfunction endfunction
function! ale#completion#HandleUserData(completed_item) abort function! ale#completion#HandleUserData(completed_item) abort
let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '')
if l:source isnot# 'ale-automatic'
\&& l:source isnot# 'ale-manual'
\&& l:source isnot# 'ale-callback'
\&& l:source isnot# 'ale-import'
return
endif
let l:user_data_json = get(a:completed_item, 'user_data', '') let l:user_data_json = get(a:completed_item, 'user_data', '')
let l:user_data = !empty(l:user_data_json)
if empty(l:user_data_json) \ ? json_decode(l:user_data_json)
return \ : v:null
endif
let l:user_data = json_decode(l:user_data_json)
if type(l:user_data) isnot v:t_dict if type(l:user_data) isnot v:t_dict
\|| get(l:user_data, '_ale_completion_item', 0) isnot 1
return return
endif endif
for l:code_action in get(l:user_data, 'codeActions', []) let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '')
call ale#code_action#HandleCodeAction(l:code_action, v:false)
endfor if l:source is# 'ale-automatic'
\|| l:source is# 'ale-manual'
\|| l:source is# 'ale-callback'
\|| l:source is# 'ale-import'
\|| l:source is# 'ale-omnifunc'
for l:code_action in get(l:user_data, 'code_actions', [])
call ale#code_action#HandleCodeAction(l:code_action, v:false)
endfor
endif
silent doautocmd <nomodeline> User ALECompletePost
endfunction endfunction
function! ale#completion#Done() abort function! ale#completion#Done() abort

View file

@ -458,6 +458,10 @@ function! s:StopCurrentJobs(buffer, clear_lint_file_jobs) abort
endif endif
endfunction endfunction
function! ale#engine#Stop(buffer) abort
call s:StopCurrentJobs(a:buffer, 1)
endfunction
function! s:RemoveProblemsForDisabledLinters(buffer, linters) abort function! s:RemoveProblemsForDisabledLinters(buffer, linters) abort
" Figure out which linters are still enabled, and remove " Figure out which linters are still enabled, and remove
" problems for linters which are no longer enabled. " problems for linters which are no longer enabled.

View file

@ -75,7 +75,10 @@ function! ale#fix#ApplyFixes(buffer, output) abort
if l:data.lines_before != l:lines if l:data.lines_before != l:lines
call remove(g:ale_fix_buffer_data, a:buffer) call remove(g:ale_fix_buffer_data, a:buffer)
execute 'echoerr ''The file was changed before fixing finished'''
if !l:data.ignore_file_changed_errors
execute 'echoerr ''The file was changed before fixing finished'''
endif
return return
endif endif
@ -329,6 +332,7 @@ function! ale#fix#InitBufferData(buffer, fixing_flag) abort
\ 'lines_before': getbufline(a:buffer, 1, '$'), \ 'lines_before': getbufline(a:buffer, 1, '$'),
\ 'done': 0, \ 'done': 0,
\ 'should_save': a:fixing_flag is# 'save_file', \ 'should_save': a:fixing_flag is# 'save_file',
\ 'ignore_file_changed_errors': a:fixing_flag is# '!',
\ 'temporary_directory_list': [], \ 'temporary_directory_list': [],
\} \}
endfunction endfunction
@ -337,19 +341,23 @@ endfunction
" "
" Returns 0 if no fixes can be applied, and 1 if fixing can be done. " Returns 0 if no fixes can be applied, and 1 if fixing can be done.
function! ale#fix#Fix(buffer, fixing_flag, ...) abort function! ale#fix#Fix(buffer, fixing_flag, ...) abort
if a:fixing_flag isnot# '' && a:fixing_flag isnot# 'save_file' if a:fixing_flag isnot# ''
throw "fixing_flag must be either '' or 'save_file'" \&& a:fixing_flag isnot# '!'
\&& a:fixing_flag isnot# 'save_file'
throw "fixing_flag must be '', '!', or 'save_file'"
endif endif
try try
let l:callback_list = s:GetCallbacks(a:buffer, a:fixing_flag, a:000) let l:callback_list = s:GetCallbacks(a:buffer, a:fixing_flag, a:000)
catch /E700\|BADNAME/ catch /E700\|BADNAME/
let l:function_name = join(split(split(v:exception, ':')[3])) if a:fixing_flag isnot# '!'
let l:echo_message = printf( let l:function_name = join(split(split(v:exception, ':')[3]))
\ 'There is no fixer named `%s`. Check :ALEFixSuggest', let l:echo_message = printf(
\ l:function_name, \ 'There is no fixer named `%s`. Check :ALEFixSuggest',
\) \ l:function_name,
execute 'echom l:echo_message' \)
execute 'echom l:echo_message'
endif
return 0 return 0
endtry endtry

View file

@ -147,6 +147,8 @@ ALE offers several options for controlling which linters are run.
* Disabling only a subset of linters. - |g:ale_linters_ignore| * Disabling only a subset of linters. - |g:ale_linters_ignore|
* Disabling LSP linters and `tsserver`. - |g:ale_disable_lsp| * Disabling LSP linters and `tsserver`. - |g:ale_disable_lsp|
You can stop ALE any currently running linters with the |ALELintStop| command.
Any existing problems will be kept.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
3.1 Linting On Other Machines *ale-lint-other-machines* 3.1 Linting On Other Machines *ale-lint-other-machines*
@ -532,6 +534,9 @@ 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 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. can be useful when your code already contains something you need to import.
You can execute other commands whenever ALE inserts some completion text with
the |ALECompletePost| event.
When working with TypeScript files, ALE can remove warnings from your When working with TypeScript files, ALE can remove warnings from your
completions by setting the |g:ale_completion_tsserver_remove_warnings| completions by setting the |g:ale_completion_tsserver_remove_warnings|
variable to 1. variable to 1.
@ -2976,6 +2981,10 @@ ALEFix *ALEFix*
Fix problems with the current buffer. See |ale-fix| for more information. Fix problems with the current buffer. See |ale-fix| for more information.
If the command is run with a bang (`:ALEFix!`), all warnings will be
suppressed, including warnings about no fixers being defined, and warnings
about not being able to apply fixes to a file because it has been changed.
A plug mapping `<Plug>(ale_fix)` is defined for this command. A plug mapping `<Plug>(ale_fix)` is defined for this command.
@ -3115,6 +3124,13 @@ ALELint *ALELint*
A plug mapping `<Plug>(ale_lint)` is defined for this command. A plug mapping `<Plug>(ale_lint)` is defined for this command.
ALELintStop *ALELintStop*
Stop any currently running jobs for checking the current buffer.
Any problems from previous linter results will continue to be shown.
ALEPrevious *ALEPrevious* ALEPrevious *ALEPrevious*
ALEPreviousWrap *ALEPreviousWrap* ALEPreviousWrap *ALEPreviousWrap*
ALENext *ALENext* ALENext *ALENext*
@ -3980,6 +3996,23 @@ g:ale_want_results_buffer *g:ale_want_results_buffer*
figure out which buffer other sources should lint. figure out which buffer other sources should lint.
ALECompletePost *ALECompletePost-autocmd*
*ALECompletePost*
This |User| autocmd is triggered after ALE inserts an item on
|CompleteDone|. This event can be used to run commands after a buffer
is changed by ALE as the result of completion. For example, |ALEFix| can
be configured to run automatically when completion is done: >
augroup FixAfterComplete
autocmd!
" Run ALEFix when completion items are added.
autocmd User ALECompletePost ALEFix!
" If ALE starts fixing a file, stop linters running for now.
autocmd User ALEFixPre ALELintStop
augroup END
<
ALELintPre *ALELintPre-autocmd* ALELintPre *ALELintPre-autocmd*
*ALELintPre* *ALELintPre*
ALELintPost *ALELintPost-autocmd* ALELintPost *ALELintPost-autocmd*

View file

@ -195,6 +195,8 @@ command! -bar ALEStopAllLSPs :call ale#lsp#reset#StopAllLSPs()
" A command for linting manually. " A command for linting manually.
command! -bar ALELint :call ale#Queue(0, 'lint_file') command! -bar ALELint :call ale#Queue(0, 'lint_file')
" Stop current jobs when linting.
command! -bar ALELintStop :call ale#engine#Stop(bufnr(''))
" Define a command to get information about current filetype. " Define a command to get information about current filetype.
command! -bar ALEInfo :call ale#debugging#Info() command! -bar ALEInfo :call ale#debugging#Info()
@ -204,7 +206,7 @@ command! -bar ALEInfoToClipboard :call ale#debugging#InfoToClipboard()
command! -bar -nargs=1 ALEInfoToFile :call ale#debugging#InfoToFile(<f-args>) command! -bar -nargs=1 ALEInfoToFile :call ale#debugging#InfoToFile(<f-args>)
" Fix problems in files. " Fix problems in files.
command! -bar -nargs=* -complete=customlist,ale#fix#registry#CompleteFixers ALEFix :call ale#fix#Fix(bufnr(''), '', <f-args>) command! -bar -bang -nargs=* -complete=customlist,ale#fix#registry#CompleteFixers ALEFix :call ale#fix#Fix(bufnr(''), '<bang>', <f-args>)
" Suggest registered functions to use for fixing problems. " Suggest registered functions to use for fixing problems.
command! -bar ALEFixSuggest :call ale#fix#registry#Suggest(&filetype) command! -bar ALEFixSuggest :call ale#fix#registry#Suggest(&filetype)

View file

@ -49,12 +49,13 @@ class Source(Base):
if event == 'Async': if event == 'Async':
result = self.vim.call('ale#completion#GetCompletionResult') result = self.vim.call('ale#completion#GetCompletionResult')
return result or [] return result or []
if context.get('is_refresh'): if context.get('is_refresh'):
self.vim.command( self.vim.command(
"call ale#completion#GetCompletions('ale-callback', " + \ "call ale#completion#GetCompletions('ale-callback', "
"{'callback': {completions -> deoplete#auto_complete() }})" + "{'callback': {completions -> deoplete#auto_complete() }})"
) )
return [] return []

View file

@ -0,0 +1,35 @@
Before:
let g:complete_post_triggered = 0
augroup VaderTest
autocmd!
autocmd User ALECompletePost let g:complete_post_triggered = 1
augroup END
After:
unlet! b:ale_completion_info
unlet! g:complete_post_triggered
augroup VaderTest
autocmd!
augroup END
augroup! VaderTest
Execute(ALECompletePost should not be triggered when completion is cancelled):
call ale#completion#HandleUserData({})
Assert !g:complete_post_triggered
Execute(ALECompletePost should not be triggered when tools other than ALE insert completions):
call ale#completion#HandleUserData({'user_data': ''})
call ale#completion#HandleUserData({'user_data': '{}'})
Assert !g:complete_post_triggered
Execute(ALECompletePost should be triggered when ALE inserts completions):
call ale#completion#HandleUserData({
\ 'user_data': json_encode({'_ale_completion_item': 1}),
\})
Assert g:complete_post_triggered

View file

@ -407,41 +407,75 @@ Execute(HandleUserData should call ale#code_action#HandleCodeAction):
AssertEqual g:handle_code_action_called, 0 AssertEqual g:handle_code_action_called, 0
call ale#completion#HandleUserData({ call ale#completion#HandleUserData({
\ 'user_data': '' \ 'user_data': ''
\}) \})
AssertEqual g:handle_code_action_called, 0 AssertEqual g:handle_code_action_called, 0
call ale#completion#HandleUserData({ call ale#completion#HandleUserData({
\ 'user_data': '{}' \ 'user_data': json_encode({}),
\}) \})
AssertEqual g:handle_code_action_called, 0 AssertEqual g:handle_code_action_called, 0
call ale#completion#HandleUserData({ call ale#completion#HandleUserData({
\ 'user_data': '{"codeActions": []}' \ 'user_data': json_encode({
\ '_ale_completion_item': 1,
\ 'code_actions': [],
\ }),
\}) \})
AssertEqual g:handle_code_action_called, 0 AssertEqual g:handle_code_action_called, 0
call ale#completion#HandleUserData({ call ale#completion#HandleUserData({
\ 'user_data': '{"codeActions": [{"description":"", "changes": []}]}' \ 'user_data': json_encode({
\ '_ale_completion_item': 1,
\ 'code_actions': [
\ {'description': '', 'changes': []},
\ ],
\ }),
\}) \})
AssertEqual g:handle_code_action_called, 1 AssertEqual g:handle_code_action_called, 1
let b:ale_completion_info = {'source': 'ale-automatic'} let b:ale_completion_info = {'source': 'ale-automatic'}
call ale#completion#HandleUserData({ call ale#completion#HandleUserData({
\ 'user_data': '{"codeActions": [{"description":"", "changes": []}]}' \ 'user_data': json_encode({
\ '_ale_completion_item': 1,
\ 'code_actions': [
\ {'description': '', 'changes': []},
\ ],
\ }),
\}) \})
AssertEqual g:handle_code_action_called, 2 AssertEqual g:handle_code_action_called, 2
let b:ale_completion_info = {'source': 'ale-callback'} let b:ale_completion_info = {'source': 'ale-callback'}
call ale#completion#HandleUserData({ call ale#completion#HandleUserData({
\ 'user_data': '{"codeActions": [{"description":"", "changes": []}]}' \ 'user_data': json_encode({
\ '_ale_completion_item': 1,
\ 'code_actions': [
\ {'description': '', 'changes': []},
\ ],
\ }),
\}) \})
AssertEqual g:handle_code_action_called, 3 AssertEqual g:handle_code_action_called, 3
let b:ale_completion_info = {'source': 'ale-omnifunc'}
call ale#completion#HandleUserData({
\ 'user_data': json_encode({
\ '_ale_completion_item': 1,
\ 'code_actions': [
\ {'description': '', 'changes': []},
\ ],
\ }),
\})
AssertEqual g:handle_code_action_called, 4
Execute(ale#code_action#HandleCodeAction should not be called when when source is not ALE): Execute(ale#code_action#HandleCodeAction should not be called when when source is not ALE):
call MockHandleCodeAction() call MockHandleCodeAction()
let b:ale_completion_info = {'source': 'syntastic'} let b:ale_completion_info = {'source': 'syntastic'}
call ale#completion#HandleUserData({ call ale#completion#HandleUserData({
\ 'user_data': '{"codeActions": [{"description":"", "changes": []}]}' \ 'user_data': json_encode({
\ '_ale_completion_item': 1,
\ 'code_actions': [
\ {'description': '', 'changes': []},
\ ],
\ }),
\}) \})
AssertEqual g:handle_code_action_called, 0 AssertEqual g:handle_code_action_called, 0

View file

@ -12,34 +12,34 @@ After:
Execute(Should handle Rust completion results correctly): Execute(Should handle Rust completion results correctly):
AssertEqual AssertEqual
\ [ \ [
\ {'word': 'new', 'menu': 'pub fn new() -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'new', 'menu': 'pub fn new() -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'with_capacity', 'menu': 'pub fn with_capacity(capacity: usize) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'with_capacity', 'menu': 'pub fn with_capacity(capacity: usize) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'from_utf8', 'menu': 'pub fn from_utf8(vec: Vec<u8>) -> Result<String, FromUtf8Error>', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_utf8', 'menu': 'pub fn from_utf8(vec: Vec<u8>) -> Result<String, FromUtf8Error>', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'from_utf8_lossy', 'menu': 'pub fn from_utf8_lossy<''a>(v: &''a [u8]) -> Cow<''a, str>', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_utf8_lossy', 'menu': 'pub fn from_utf8_lossy<''a>(v: &''a [u8]) -> Cow<''a, str>', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'from_utf16', 'menu': 'pub fn from_utf16(v: &[u16]) -> Result<String, FromUtf16Error>', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_utf16', 'menu': 'pub fn from_utf16(v: &[u16]) -> Result<String, FromUtf16Error>', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'from_utf16_lossy', 'menu': 'pub fn from_utf16_lossy(v: &[u16]) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_utf16_lossy', 'menu': 'pub fn from_utf16_lossy(v: &[u16]) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'from_raw_parts', 'menu': 'pub unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_raw_parts', 'menu': 'pub unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'from_utf8_unchecked', 'menu': 'pub unsafe fn from_utf8_unchecked(bytes: Vec<u8>) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_utf8_unchecked', 'menu': 'pub unsafe fn from_utf8_unchecked(bytes: Vec<u8>) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'from_iter', 'menu': 'fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_iter', 'menu': 'fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'from_iter', 'menu': 'fn from_iter<I: IntoIterator<Item = &''a char>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_iter', 'menu': 'fn from_iter<I: IntoIterator<Item = &''a char>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'from_iter', 'menu': 'fn from_iter<I: IntoIterator<Item = &''a str>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_iter', 'menu': 'fn from_iter<I: IntoIterator<Item = &''a str>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'from_iter', 'menu': 'fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_iter', 'menu': 'fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'from_iter', 'menu': 'fn from_iter<I: IntoIterator<Item = Cow<''a, str>>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_iter', 'menu': 'fn from_iter<I: IntoIterator<Item = Cow<''a, str>>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'Searcher', 'menu': 'type Searcher = <&''b str as Pattern<''a>>::Searcher;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'Searcher', 'menu': 'type Searcher = <&''b str as Pattern<''a>>::Searcher;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'default', 'menu': 'fn default() -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'default', 'menu': 'fn default() -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'Output', 'menu': 'type Output = String;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'Output', 'menu': 'type Output = String;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'Target', 'menu': 'type Target = str;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'Target', 'menu': 'type Target = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'Err', 'menu': 'type Err = ParseError;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'Err', 'menu': 'type Err = ParseError;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'from_str', 'menu': 'fn from_str(s: &str) -> Result<String, ParseError>', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_str', 'menu': 'fn from_str(s: &str) -> Result<String, ParseError>', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'from', 'menu': 'fn from(s: &''a str) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from', 'menu': 'fn from(s: &''a str) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'from', 'menu': 'fn from(s: Box<str>) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from', 'menu': 'fn from(s: Box<str>) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'from', 'menu': 'fn from(s: Cow<''a, str>) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from', 'menu': 'fn from(s: Cow<''a, str>) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\], \],
\ ale#completion#ParseLSPCompletions({ \ ale#completion#ParseLSPCompletions({
\ "jsonrpc":"2.0", \ "jsonrpc":"2.0",
@ -195,7 +195,7 @@ Execute(Should handle Python completion results correctly):
AssertEqual AssertEqual
\ [ \ [
\ {'word': 'what', 'menu': 'example-python-project.bar.Bar', 'info': "what()\n\n", 'kind': 'f', 'icase': 1}, \ {'word': 'what', 'menu': 'example-python-project.bar.Bar', 'info': "what()\n\n", 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ ], \ ],
\ ale#completion#ParseLSPCompletions({ \ ale#completion#ParseLSPCompletions({
\ "jsonrpc":"2.0", \ "jsonrpc":"2.0",
@ -399,7 +399,7 @@ Execute(Should handle Python completion results correctly):
\ } \ }
\ }) \ })
Execute(Should handle Python completion results correctly): Execute(Should handle extra Python completion results correctly):
let b:ale_completion_info = { let b:ale_completion_info = {
\ 'completion_filter': 'ale#completion#python#CompletionItemFilter', \ 'completion_filter': 'ale#completion#python#CompletionItemFilter',
\ 'prefix': 'mig', \ 'prefix': 'mig',
@ -407,8 +407,8 @@ Execute(Should handle Python completion results correctly):
AssertEqual AssertEqual
\ [ \ [
\ {'word': 'migrations', 'menu': 'xxx', 'info': 'migrations', 'kind': 'f', 'icase': 1}, \ {'word': 'migrations', 'menu': 'xxx', 'info': 'migrations', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'MigEngine', 'menu': 'xxx', 'info': 'mig engine', 'kind': 'f', 'icase': 1}, \ {'word': 'MigEngine', 'menu': 'xxx', 'info': 'mig engine', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ ], \ ],
\ ale#completion#ParseLSPCompletions({ \ ale#completion#ParseLSPCompletions({
\ 'jsonrpc': '2.0', \ 'jsonrpc': '2.0',
@ -441,7 +441,7 @@ Execute(Should handle Python completion results correctly):
Execute(Should handle missing keys): Execute(Should handle missing keys):
AssertEqual AssertEqual
\ [ \ [
\ {'word': 'x', 'menu': '', 'info': '', 'kind': 'v', 'icase': 1}, \ {'word': 'x', 'menu': '', 'info': '', 'kind': 'v', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ ], \ ],
\ ale#completion#ParseLSPCompletions({ \ ale#completion#ParseLSPCompletions({
\ 'jsonrpc': '2.0', \ 'jsonrpc': '2.0',
@ -459,7 +459,7 @@ Execute(Should handle missing keys):
Execute(Should handle documentation in the markdown format): Execute(Should handle documentation in the markdown format):
AssertEqual AssertEqual
\ [ \ [
\ {'word': 'migrations', 'menu': 'xxx', 'info': 'Markdown documentation', 'kind': 'f', 'icase': 1}, \ {'word': 'migrations', 'menu': 'xxx', 'info': 'Markdown documentation', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ ], \ ],
\ ale#completion#ParseLSPCompletions({ \ ale#completion#ParseLSPCompletions({
\ 'jsonrpc': '2.0', \ 'jsonrpc': '2.0',
@ -483,7 +483,7 @@ Execute(Should handle documentation in the markdown format):
Execute(Should handle completion messages with textEdit objects): Execute(Should handle completion messages with textEdit objects):
AssertEqual AssertEqual
\ [ \ [
\ {'word': 'next_callback', 'menu': 'PlayTimeCallback', 'info': '', 'kind': 'v', 'icase': 1}, \ {'word': 'next_callback', 'menu': 'PlayTimeCallback', 'info': '', 'kind': 'v', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ ], \ ],
\ ale#completion#ParseLSPCompletions({ \ ale#completion#ParseLSPCompletions({
\ 'id': 226, \ 'id': 226,
@ -514,7 +514,7 @@ Execute(Should handle completion messages with textEdit objects):
Execute(Should handle completion messages with the deprecated insertText attribute): Execute(Should handle completion messages with the deprecated insertText attribute):
AssertEqual AssertEqual
\ [ \ [
\ {'word': 'next_callback', 'menu': 'PlayTimeCallback', 'info': '', 'kind': 'v', 'icase': 1}, \ {'word': 'next_callback', 'menu': 'PlayTimeCallback', 'info': '', 'kind': 'v', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ ], \ ],
\ ale#completion#ParseLSPCompletions({ \ ale#completion#ParseLSPCompletions({
\ 'id': 226, \ 'id': 226,
@ -547,7 +547,8 @@ Execute(Should handle completion messages with additionalTextEdits when ale_comp
\ 'kind': 'v', \ 'kind': 'v',
\ 'icase': 1, \ 'icase': 1,
\ 'user_data': json_encode({ \ 'user_data': json_encode({
\ 'codeActions': [ \ '_ale_completion_item': 1,
\ 'code_actions': [
\ { \ {
\ 'description': 'completion', \ 'description': 'completion',
\ 'changes': [ \ 'changes': [
@ -658,6 +659,7 @@ Execute(Should still handle completion messages with empty additionalTextEdits w
\ 'info': '', \ 'info': '',
\ 'kind': 'v', \ 'kind': 'v',
\ 'icase': 1, \ 'icase': 1,
\ 'user_data': json_encode({'_ale_completion_item': 1}),
\ } \ }
\ ], \ ],
\ ale#completion#ParseLSPCompletions({ \ ale#completion#ParseLSPCompletions({

View file

@ -90,6 +90,7 @@ Execute(TypeScript completion details responses should be parsed correctly):
\ 'info': '', \ 'info': '',
\ 'kind': 'v', \ 'kind': 'v',
\ 'icase': 1, \ 'icase': 1,
\ 'user_data': json_encode({'_ale_completion_item': 1}),
\ 'dup': g:ale_completion_autoimport, \ 'dup': g:ale_completion_autoimport,
\ }, \ },
\ { \ {
@ -98,6 +99,7 @@ Execute(TypeScript completion details responses should be parsed correctly):
\ 'info': 'foo bar baz', \ 'info': 'foo bar baz',
\ 'kind': 'v', \ 'kind': 'v',
\ 'icase': 1, \ 'icase': 1,
\ 'user_data': json_encode({'_ale_completion_item': 1}),
\ 'dup': g:ale_completion_autoimport, \ 'dup': g:ale_completion_autoimport,
\ }, \ },
\ { \ {
@ -106,6 +108,7 @@ Execute(TypeScript completion details responses should be parsed correctly):
\ 'info': '', \ 'info': '',
\ 'kind': 'v', \ 'kind': 'v',
\ 'icase': 1, \ 'icase': 1,
\ 'user_data': json_encode({'_ale_completion_item': 1}),
\ 'dup': g:ale_completion_autoimport, \ 'dup': g:ale_completion_autoimport,
\ }, \ },
\ ], \ ],
@ -179,7 +182,8 @@ Execute(Entries without details should be included in the responses):
\ 'kind': 'v', \ 'kind': 'v',
\ 'icase': 1, \ 'icase': 1,
\ 'user_data': json_encode({ \ 'user_data': json_encode({
\ 'codeActions': [{ \ '_ale_completion_item': 1,
\ 'code_actions': [{
\ 'description': 'import { def } from "./Foo";', \ 'description': 'import { def } from "./Foo";',
\ 'changes': [], \ 'changes': [],
\ }], \ }],
@ -192,6 +196,7 @@ Execute(Entries without details should be included in the responses):
\ 'info': 'foo bar baz', \ 'info': 'foo bar baz',
\ 'kind': 'v', \ 'kind': 'v',
\ 'icase': 1, \ 'icase': 1,
\ 'user_data': json_encode({'_ale_completion_item': 1}),
\ 'dup': g:ale_completion_autoimport, \ 'dup': g:ale_completion_autoimport,
\ }, \ },
\ { \ {
@ -199,6 +204,7 @@ Execute(Entries without details should be included in the responses):
\ 'menu': '', \ 'menu': '',
\ 'info': '', \ 'info': '',
\ 'kind': 'v', \ 'kind': 'v',
\ 'user_data': json_encode({'_ale_completion_item': 1}),
\ 'icase': 1, \ 'icase': 1,
\ }, \ },
\ ], \ ],
@ -260,7 +266,8 @@ Execute(Default imports should be handled correctly):
\ 'kind': 't', \ 'kind': 't',
\ 'icase': 1, \ 'icase': 1,
\ 'user_data': json_encode({ \ 'user_data': json_encode({
\ 'codeActions': [{ \ '_ale_completion_item': 1,
\ 'code_actions': [{
\ 'description': 'Import default ''abcd'' from module "./foo"', \ 'description': 'Import default ''abcd'' from module "./foo"',
\ 'changes': [], \ 'changes': [],
\ }], \ }],

View file

@ -196,6 +196,10 @@ After:
" Clear the messages between tests. " Clear the messages between tests.
echomsg '' echomsg ''
if !exists('g:ale_command_wrapper')
let g:ale_command_wrapper = ''
endif
Given testft (A file with three lines): Given testft (A file with three lines):
a a
b b
@ -206,6 +210,13 @@ Execute(ALEFix should complain when there are no functions to call):
call ale#test#FlushJobs() call ale#test#FlushJobs()
AssertEqual 'No fixers have been defined. Try :ALEFixSuggest', GetLastMessage() AssertEqual 'No fixers have been defined. Try :ALEFixSuggest', GetLastMessage()
Execute(ALEFix should not complain when the command is run with a bang):
echom 'none'
ALEFix!
call ale#test#FlushJobs()
AssertEqual 'none', GetLastMessage()
Execute(ALEFix should apply simple functions): Execute(ALEFix should apply simple functions):
let g:ale_fixers.testft = ['AddCarets'] let g:ale_fixers.testft = ['AddCarets']
ALEFix ALEFix
@ -715,6 +726,19 @@ Execute(ale#fix#InitBufferData() should set up the correct data):
\ 'done': 0, \ 'done': 0,
\ 'lines_before': ['a', 'b', 'c'], \ 'lines_before': ['a', 'b', 'c'],
\ 'should_save': 1, \ 'should_save': 1,
\ 'ignore_file_changed_errors': 0,
\ },
\}, g:ale_fix_buffer_data
call ale#fix#InitBufferData(bufnr(''), '!')
AssertEqual {
\ bufnr(''): {
\ 'temporary_directory_list': [],
\ 'done': 0,
\ 'lines_before': ['a', 'b', 'c'],
\ 'should_save': 0,
\ 'ignore_file_changed_errors': 1,
\ }, \ },
\}, g:ale_fix_buffer_data \}, g:ale_fix_buffer_data

View file

@ -0,0 +1,27 @@
Before:
Save g:ale_buffer_info
let g:ale_buffer_info = {}
call ale#linter#PreventLoading('testft')
call ale#linter#Define('testft', {
\ 'name': 'testlinter',
\ 'callback': {-> []},
\ 'executable': has('win32') ? 'cmd' : 'true',
\ 'command': 'sleep 9001',
\})
After:
Restore
call ale#linter#Reset()
Given testft (An empty file):
Execute(ALELintStop should stop ALE from linting):
ALELint
Assert ale#engine#IsCheckingBuffer(bufnr('')), 'ALE did not start checking the buffer'
ALELintStop
Assert !ale#engine#IsCheckingBuffer(bufnr('')), 'ALELintStop didn''t work'