#2132 - Implement deferred command handling for linters
This commit is contained in:
parent
a8b987a1c3
commit
ffa45fa3fb
9 changed files with 102 additions and 11 deletions
|
@ -59,13 +59,18 @@ function! ale#assert#Linter(expected_executable, expected_command) abort
|
||||||
endif
|
endif
|
||||||
else
|
else
|
||||||
let l:command = ale#linter#GetCommand(l:buffer, l:linter)
|
let l:command = ale#linter#GetCommand(l:buffer, l:linter)
|
||||||
|
|
||||||
|
while ale#command#IsDeferred(l:command)
|
||||||
|
call ale#test#FlushJobs()
|
||||||
|
let l:command = l:command.value
|
||||||
|
endwhile
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if type(l:command) is v:t_string
|
if type(l:command) is v:t_string
|
||||||
" Replace %e with the escaped executable, so tests keep passing after
|
" Replace %e with the escaped executable, so tests keep passing after
|
||||||
" linters are changed to use %e.
|
" linters are changed to use %e.
|
||||||
let l:command = substitute(l:command, '%e', '\=ale#Escape(l:executable)', 'g')
|
let l:command = substitute(l:command, '%e', '\=ale#Escape(l:executable)', 'g')
|
||||||
else
|
elseif type(l:command) is v:t_list
|
||||||
call map(l:command, 'substitute(v:val, ''%e'', ''\=ale#Escape(l:executable)'', ''g'')')
|
call map(l:command, 'substitute(v:val, ''%e'', ''\=ale#Escape(l:executable)'', ''g'')')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
|
@ -320,6 +320,10 @@ function! ale#command#Run(buffer, command, Callback, ...) abort
|
||||||
call ale#history#Add(a:buffer, l:status, l:job_id, l:command)
|
call ale#history#Add(a:buffer, l:status, l:job_id, l:command)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if !l:job_id
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
|
|
||||||
" We'll return this Dictionary. A `result_callback` can be assigned to it
|
" We'll return this Dictionary. A `result_callback` can be assigned to it
|
||||||
" later for capturing the result of a:Callback.
|
" later for capturing the result of a:Callback.
|
||||||
"
|
"
|
||||||
|
|
|
@ -422,8 +422,16 @@ endfunction
|
||||||
|
|
||||||
" Run a job.
|
" Run a job.
|
||||||
"
|
"
|
||||||
" Returns 1 when the job was started successfully.
|
" Returns 1 when a job was started successfully.
|
||||||
function! s:RunJob(command, options) abort
|
function! s:RunJob(command, options) abort
|
||||||
|
if ale#command#IsDeferred(a:command)
|
||||||
|
let a:command.result_callback = {
|
||||||
|
\ command -> s:RunJob(command, a:options)
|
||||||
|
\}
|
||||||
|
|
||||||
|
return 1
|
||||||
|
endif
|
||||||
|
|
||||||
let l:command = a:command
|
let l:command = a:command
|
||||||
|
|
||||||
if empty(l:command)
|
if empty(l:command)
|
||||||
|
@ -451,7 +459,7 @@ function! s:RunJob(command, options) abort
|
||||||
\})
|
\})
|
||||||
|
|
||||||
" Only proceed if the job is being run.
|
" Only proceed if the job is being run.
|
||||||
if !l:result._deferred_job_id
|
if empty(l:result)
|
||||||
return 0
|
return 0
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
|
@ -179,7 +179,7 @@ function! s:RunJob(options) abort
|
||||||
\ 'log_output': 0,
|
\ 'log_output': 0,
|
||||||
\})
|
\})
|
||||||
|
|
||||||
return l:result._deferred_job_id != 0
|
return !empty(l:result)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:RunFixer(options) abort
|
function! s:RunFixer(options) abort
|
||||||
|
|
|
@ -177,7 +177,8 @@ function! ale#linter#PreProcess(filetype, linter) abort
|
||||||
let l:obj.command = a:linter.command
|
let l:obj.command = a:linter.command
|
||||||
|
|
||||||
if type(l:obj.command) isnot v:t_string
|
if type(l:obj.command) isnot v:t_string
|
||||||
throw '`command` must be a string if defined'
|
\&& type(l:obj.command) isnot v:t_func
|
||||||
|
throw '`command` must be a String or Function if defined'
|
||||||
endif
|
endif
|
||||||
else
|
else
|
||||||
throw 'Either `command`, `executable_callback`, `command_chain` '
|
throw 'Either `command`, `executable_callback`, `command_chain` '
|
||||||
|
@ -489,9 +490,13 @@ endfunction
|
||||||
" Given a buffer and linter, get the command String for the linter.
|
" Given a buffer and linter, get the command String for the linter.
|
||||||
" The command_chain key is not supported.
|
" The command_chain key is not supported.
|
||||||
function! ale#linter#GetCommand(buffer, linter) abort
|
function! ale#linter#GetCommand(buffer, linter) abort
|
||||||
return has_key(a:linter, 'command_callback')
|
let l:Command = has_key(a:linter, 'command_callback')
|
||||||
\ ? ale#util#GetFunction(a:linter.command_callback)(a:buffer)
|
\ ? function(a:linter.command_callback)
|
||||||
\ : a:linter.command
|
\ : a:linter.command
|
||||||
|
|
||||||
|
return type(l:Command) is v:t_func
|
||||||
|
\ ? l:Command(a:buffer)
|
||||||
|
\ : l:Command
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Given a buffer and linter, get the address for connecting to the server.
|
" Given a buffer and linter, get the address for connecting to the server.
|
||||||
|
|
|
@ -3122,7 +3122,10 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
|
||||||
used in place of `executable` when more complicated
|
used in place of `executable` when more complicated
|
||||||
processing is needed.
|
processing is needed.
|
||||||
|
|
||||||
`command` A |String| for an executable to run asynchronously.
|
`command` A |String| for a command to run asynchronously, or a
|
||||||
|
|Funcref| for a function to call for computing the
|
||||||
|
command, accepting a buffer number.
|
||||||
|
|
||||||
This command will be fed the lines from the buffer to
|
This command will be fed the lines from the buffer to
|
||||||
check, and will produce the lines of output given to
|
check, and will produce the lines of output given to
|
||||||
the `callback`.
|
the `callback`.
|
||||||
|
|
46
test/test_deferred_command_string.vader
Normal file
46
test/test_deferred_command_string.vader
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
Before:
|
||||||
|
Save g:ale_run_synchronously
|
||||||
|
Save g:ale_emulate_job_failure
|
||||||
|
Save g:ale_buffer_info
|
||||||
|
|
||||||
|
let g:ale_run_synchronously = 1
|
||||||
|
let g:ale_buffer_info = {}
|
||||||
|
let b:ale_history = []
|
||||||
|
|
||||||
|
call ale#linter#Reset()
|
||||||
|
call ale#assert#SetUpLinterTestCommands()
|
||||||
|
call ale#linter#Define('foobar', {
|
||||||
|
\ 'name': 'lint_file_linter',
|
||||||
|
\ 'callback': 'LintFileCallback',
|
||||||
|
\ 'executable': 'echo',
|
||||||
|
\ 'command': {b -> ale#command#Run(b, 'echo', {-> ale#command#Run(b, 'echo', {-> 'foo'})})},
|
||||||
|
\ 'read_buffer': 0,
|
||||||
|
\})
|
||||||
|
|
||||||
|
After:
|
||||||
|
Restore
|
||||||
|
|
||||||
|
call ale#assert#TearDownLinterTest()
|
||||||
|
|
||||||
|
Given foobar (Some imaginary filetype):
|
||||||
|
Execute(It should be possible to compute an executable to check based on the result of commands):
|
||||||
|
AssertLinter 'echo', 'foo'
|
||||||
|
|
||||||
|
ALELint
|
||||||
|
call ale#test#FlushJobs()
|
||||||
|
|
||||||
|
AssertEqual
|
||||||
|
\ 1,
|
||||||
|
\ len(filter(copy(b:ale_history), 'string(v:val.command) =~# ''foo'''))
|
||||||
|
|
||||||
|
Execute(It handle the deferred command failing):
|
||||||
|
let g:ale_emulate_job_failure = 1
|
||||||
|
|
||||||
|
AssertLinter 'echo', 0
|
||||||
|
|
||||||
|
ALELint
|
||||||
|
call ale#test#FlushJobs()
|
||||||
|
|
||||||
|
AssertEqual
|
||||||
|
\ 0,
|
||||||
|
\ len(filter(copy(b:ale_history), 'string(v:val.command) =~# ''foo'''))
|
|
@ -1,9 +1,11 @@
|
||||||
Before:
|
Before:
|
||||||
Save g:ale_run_synchronously
|
Save g:ale_run_synchronously
|
||||||
|
Save g:ale_emulate_job_failure
|
||||||
Save g:ale_buffer_info
|
Save g:ale_buffer_info
|
||||||
|
|
||||||
let g:ale_run_synchronously = 1
|
let g:ale_run_synchronously = 1
|
||||||
let g:ale_buffer_info = {}
|
let g:ale_buffer_info = {}
|
||||||
|
let b:ale_history = []
|
||||||
|
|
||||||
call ale#linter#Reset()
|
call ale#linter#Reset()
|
||||||
call ale#assert#SetUpLinterTestCommands()
|
call ale#assert#SetUpLinterTestCommands()
|
||||||
|
@ -22,8 +24,6 @@ After:
|
||||||
|
|
||||||
Given foobar (Some imaginary filetype):
|
Given foobar (Some imaginary filetype):
|
||||||
Execute(It should be possible to compute an executable to check based on the result of commands):
|
Execute(It should be possible to compute an executable to check based on the result of commands):
|
||||||
let b:ale_history = []
|
|
||||||
|
|
||||||
AssertLinter 'foo', 'echo'
|
AssertLinter 'foo', 'echo'
|
||||||
|
|
||||||
ALELint
|
ALELint
|
||||||
|
@ -32,3 +32,15 @@ Execute(It should be possible to compute an executable to check based on the res
|
||||||
AssertEqual
|
AssertEqual
|
||||||
\ [{'status': 0, 'job_id': 'executable', 'command': 'foo'}],
|
\ [{'status': 0, 'job_id': 'executable', 'command': 'foo'}],
|
||||||
\ filter(copy(b:ale_history), 'v:val.job_id is# ''executable''')
|
\ filter(copy(b:ale_history), 'v:val.job_id is# ''executable''')
|
||||||
|
|
||||||
|
Execute(It handle the deferred command failing):
|
||||||
|
let g:ale_emulate_job_failure = 1
|
||||||
|
|
||||||
|
AssertLinter 0, 'echo'
|
||||||
|
|
||||||
|
ALELint
|
||||||
|
call ale#test#FlushJobs()
|
||||||
|
|
||||||
|
AssertEqual
|
||||||
|
\ [],
|
||||||
|
\ filter(copy(b:ale_history), 'v:val.job_id is# ''executable''')
|
||||||
|
|
|
@ -82,7 +82,15 @@ Execute (PreProcess should throw when command is not a string):
|
||||||
\ 'executable': 'echo',
|
\ 'executable': 'echo',
|
||||||
\ 'command': [],
|
\ 'command': [],
|
||||||
\})
|
\})
|
||||||
AssertEqual '`command` must be a string if defined', g:vader_exception
|
AssertEqual '`command` must be a String or Function if defined', g:vader_exception
|
||||||
|
|
||||||
|
Execute (PreProcess should allow command to be a callback):
|
||||||
|
call ale#linter#PreProcess('testft', {
|
||||||
|
\ 'name': 'foo',
|
||||||
|
\ 'callback': 'SomeFunction',
|
||||||
|
\ 'executable': 'echo',
|
||||||
|
\ 'command': function('type'),
|
||||||
|
\})
|
||||||
|
|
||||||
Execute (PreProcess should throw when command_callback is not a callback):
|
Execute (PreProcess should throw when command_callback is not a callback):
|
||||||
AssertThrows call ale#linter#PreProcess('testft', {
|
AssertThrows call ale#linter#PreProcess('testft', {
|
||||||
|
|
Reference in a new issue