#2132 - Implement deferred command handling for linters

This commit is contained in:
w0rp 2019-02-21 21:24:41 +00:00
parent a8b987a1c3
commit ffa45fa3fb
No known key found for this signature in database
GPG key ID: 0FC1ECAA8C81CD83
9 changed files with 102 additions and 11 deletions

View file

@ -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

View file

@ -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.
" "

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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`.

View 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'''))

View file

@ -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''')

View file

@ -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', {