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,
\ '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')
let l:result.user_data = json_encode({
\ 'codeActions': l:suggestion.codeActions,
\ })
let l:user_data.code_actions = l:suggestion.codeActions
endif
let l:result.user_data = json_encode(l:user_data)
" 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')
\|| has_key(l:user_data, 'code_actions')
call add(l:results, l:result)
endif
endfor
@ -534,6 +536,7 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
\ 'icase': 1,
\ 'menu': '',
\ 'info': '',
\ 'user_data': json_encode({'_ale_completion_item': 1}),
\})
endfor
endif
@ -610,6 +613,8 @@ function! ale#completion#ParseLSPCompletions(response) abort
\ 'menu': get(l:item, 'detail', ''),
\ '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')
let l:text_changes = []
@ -629,24 +634,24 @@ function! ale#completion#ParseLSPCompletions(response) abort
endfor
if !empty(l:text_changes)
let l:result.user_data = json_encode({
\ 'codeActions': [{
\ 'description': 'completion',
\ 'changes': [
\ {
\ 'fileName': expand('#' . l:buffer . ':p'),
\ 'textChanges': l:text_changes,
\ }
\ ],
\ }],
\})
let l:user_data.code_actions = [{
\ 'description': 'completion',
\ 'changes': [
\ {
\ 'fileName': expand('#' . l:buffer . ':p'),
\ 'textChanges': l:text_changes,
\ },
\ ],
\}]
endif
endif
let l:result.user_data = json_encode(l:user_data)
" 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')
\|| has_key(l:user_data, 'code_actions')
call add(l:results, l:result)
endif
endfor
@ -983,30 +988,29 @@ function! ale#completion#Queue() abort
endfunction
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', '')
if empty(l:user_data_json)
return
endif
let l:user_data = json_decode(l:user_data_json)
let l:user_data = !empty(l:user_data_json)
\ ? json_decode(l:user_data_json)
\ : v:null
if type(l:user_data) isnot v:t_dict
\|| get(l:user_data, '_ale_completion_item', 0) isnot 1
return
endif
for l:code_action in get(l:user_data, 'codeActions', [])
call ale#code_action#HandleCodeAction(l:code_action, v:false)
endfor
let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '')
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
function! ale#completion#Done() abort

View File

@ -458,6 +458,10 @@ function! s:StopCurrentJobs(buffer, clear_lint_file_jobs) abort
endif
endfunction
function! ale#engine#Stop(buffer) abort
call s:StopCurrentJobs(a:buffer, 1)
endfunction
function! s:RemoveProblemsForDisabledLinters(buffer, linters) abort
" Figure out which linters are still enabled, and remove
" 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
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
endif
@ -329,6 +332,7 @@ function! ale#fix#InitBufferData(buffer, fixing_flag) abort
\ 'lines_before': getbufline(a:buffer, 1, '$'),
\ 'done': 0,
\ 'should_save': a:fixing_flag is# 'save_file',
\ 'ignore_file_changed_errors': a:fixing_flag is# '!',
\ 'temporary_directory_list': [],
\}
endfunction
@ -337,19 +341,23 @@ endfunction
"
" Returns 0 if no fixes can be applied, and 1 if fixing can be done.
function! ale#fix#Fix(buffer, fixing_flag, ...) abort
if a:fixing_flag isnot# '' && a:fixing_flag isnot# 'save_file'
throw "fixing_flag must be either '' or 'save_file'"
if a:fixing_flag isnot# ''
\&& a:fixing_flag isnot# '!'
\&& a:fixing_flag isnot# 'save_file'
throw "fixing_flag must be '', '!', or 'save_file'"
endif
try
let l:callback_list = s:GetCallbacks(a:buffer, a:fixing_flag, a:000)
catch /E700\|BADNAME/
let l:function_name = join(split(split(v:exception, ':')[3]))
let l:echo_message = printf(
\ 'There is no fixer named `%s`. Check :ALEFixSuggest',
\ l:function_name,
\)
execute 'echom l:echo_message'
if a:fixing_flag isnot# '!'
let l:function_name = join(split(split(v:exception, ':')[3]))
let l:echo_message = printf(
\ 'There is no fixer named `%s`. Check :ALEFixSuggest',
\ l:function_name,
\)
execute 'echom l:echo_message'
endif
return 0
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 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*
@ -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
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
completions by setting the |g:ale_completion_tsserver_remove_warnings|
variable to 1.
@ -2976,6 +2981,10 @@ ALEFix *ALEFix*
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.
@ -3115,6 +3124,13 @@ ALELint *ALELint*
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*
ALEPreviousWrap *ALEPreviousWrap*
ALENext *ALENext*
@ -3980,6 +3996,23 @@ g:ale_want_results_buffer *g:ale_want_results_buffer*
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*
ALELintPost *ALELintPost-autocmd*

View File

@ -195,6 +195,8 @@ command! -bar ALEStopAllLSPs :call ale#lsp#reset#StopAllLSPs()
" A command for linting manually.
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.
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>)
" 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.
command! -bar ALEFixSuggest :call ale#fix#registry#Suggest(&filetype)

View File

@ -49,12 +49,13 @@ class Source(Base):
if event == 'Async':
result = self.vim.call('ale#completion#GetCompletionResult')
return result or []
if context.get('is_refresh'):
self.vim.command(
"call ale#completion#GetCompletions('ale-callback', " + \
"{'callback': {completions -> deoplete#auto_complete() }})"
"call ale#completion#GetCompletions('ale-callback', "
+ "{'callback': {completions -> deoplete#auto_complete() }})"
)
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
call ale#completion#HandleUserData({
\ 'user_data': ''
\ 'user_data': ''
\})
AssertEqual g:handle_code_action_called, 0
call ale#completion#HandleUserData({
\ 'user_data': '{}'
\ 'user_data': json_encode({}),
\})
AssertEqual g:handle_code_action_called, 0
call ale#completion#HandleUserData({
\ 'user_data': '{"codeActions": []}'
\ 'user_data': json_encode({
\ '_ale_completion_item': 1,
\ 'code_actions': [],
\ }),
\})
AssertEqual g:handle_code_action_called, 0
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
let b:ale_completion_info = {'source': 'ale-automatic'}
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
let b:ale_completion_info = {'source': 'ale-callback'}
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
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):
call MockHandleCodeAction()
let b:ale_completion_info = {'source': 'syntastic'}
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

View File

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

View File

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

View File

@ -196,6 +196,10 @@ After:
" Clear the messages between tests.
echomsg ''
if !exists('g:ale_command_wrapper')
let g:ale_command_wrapper = ''
endif
Given testft (A file with three lines):
a
b
@ -206,6 +210,13 @@ Execute(ALEFix should complain when there are no functions to call):
call ale#test#FlushJobs()
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):
let g:ale_fixers.testft = ['AddCarets']
ALEFix
@ -715,6 +726,19 @@ Execute(ale#fix#InitBufferData() should set up the correct data):
\ 'done': 0,
\ 'lines_before': ['a', 'b', 'c'],
\ '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

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'