summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorw0rp <devw0rp@gmail.com>2019-02-21 21:24:41 +0000
committerw0rp <devw0rp@gmail.com>2019-02-21 21:24:41 +0000
commitffa45fa3fb44ade28c64aa8f0a21acd71c903a2a (patch)
tree350f4b45b17a1862844a14856c4063c0d219f0bd
parenta8b987a1c31f297622f0038230d23404e7c2ad50 (diff)
downloadale-ffa45fa3fb44ade28c64aa8f0a21acd71c903a2a.zip
#2132 - Implement deferred command handling for linters
-rw-r--r--autoload/ale/assert.vim7
-rw-r--r--autoload/ale/command.vim4
-rw-r--r--autoload/ale/engine.vim12
-rw-r--r--autoload/ale/fix.vim2
-rw-r--r--autoload/ale/linter.vim11
-rw-r--r--doc/ale.txt5
-rw-r--r--test/test_deferred_command_string.vader46
-rw-r--r--test/test_deferred_executable_string.vader16
-rw-r--r--test/test_linter_defintion_processing.vader10
9 files changed, 102 insertions, 11 deletions
diff --git a/autoload/ale/assert.vim b/autoload/ale/assert.vim
index ef2dab9e..1b004649 100644
--- a/autoload/ale/assert.vim
+++ b/autoload/ale/assert.vim
@@ -59,13 +59,18 @@ function! ale#assert#Linter(expected_executable, expected_command) abort
endif
else
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
if type(l:command) is v:t_string
" Replace %e with the escaped executable, so tests keep passing after
" linters are changed to use %e.
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'')')
endif
diff --git a/autoload/ale/command.vim b/autoload/ale/command.vim
index 7a66dc77..33ce577c 100644
--- a/autoload/ale/command.vim
+++ b/autoload/ale/command.vim
@@ -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)
endif
+ if !l:job_id
+ return 0
+ endif
+
" We'll return this Dictionary. A `result_callback` can be assigned to it
" later for capturing the result of a:Callback.
"
diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim
index b4035422..340f6913 100644
--- a/autoload/ale/engine.vim
+++ b/autoload/ale/engine.vim
@@ -422,8 +422,16 @@ endfunction
" 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
+ 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
if empty(l:command)
@@ -451,7 +459,7 @@ function! s:RunJob(command, options) abort
\})
" Only proceed if the job is being run.
- if !l:result._deferred_job_id
+ if empty(l:result)
return 0
endif
diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim
index 7c428f52..48874a8a 100644
--- a/autoload/ale/fix.vim
+++ b/autoload/ale/fix.vim
@@ -179,7 +179,7 @@ function! s:RunJob(options) abort
\ 'log_output': 0,
\})
- return l:result._deferred_job_id != 0
+ return !empty(l:result)
endfunction
function! s:RunFixer(options) abort
diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim
index 86ee506c..8c9f83ad 100644
--- a/autoload/ale/linter.vim
+++ b/autoload/ale/linter.vim
@@ -177,7 +177,8 @@ function! ale#linter#PreProcess(filetype, linter) abort
let l:obj.command = a:linter.command
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
else
throw 'Either `command`, `executable_callback`, `command_chain` '
@@ -489,9 +490,13 @@ endfunction
" Given a buffer and linter, get the command String for the linter.
" The command_chain key is not supported.
function! ale#linter#GetCommand(buffer, linter) abort
- return has_key(a:linter, 'command_callback')
- \ ? ale#util#GetFunction(a:linter.command_callback)(a:buffer)
+ let l:Command = has_key(a:linter, 'command_callback')
+ \ ? function(a:linter.command_callback)
\ : a:linter.command
+
+ return type(l:Command) is v:t_func
+ \ ? l:Command(a:buffer)
+ \ : l:Command
endfunction
" Given a buffer and linter, get the address for connecting to the server.
diff --git a/doc/ale.txt b/doc/ale.txt
index 22639251..083b0507 100644
--- a/doc/ale.txt
+++ b/doc/ale.txt
@@ -3122,7 +3122,10 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
used in place of `executable` when more complicated
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
check, and will produce the lines of output given to
the `callback`.
diff --git a/test/test_deferred_command_string.vader b/test/test_deferred_command_string.vader
new file mode 100644
index 00000000..4d0c8977
--- /dev/null
+++ b/test/test_deferred_command_string.vader
@@ -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'''))
diff --git a/test/test_deferred_executable_string.vader b/test/test_deferred_executable_string.vader
index ad2e752b..3bdc5251 100644
--- a/test/test_deferred_executable_string.vader
+++ b/test/test_deferred_executable_string.vader
@@ -1,9 +1,11 @@
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()
@@ -22,8 +24,6 @@ After:
Given foobar (Some imaginary filetype):
Execute(It should be possible to compute an executable to check based on the result of commands):
- let b:ale_history = []
-
AssertLinter 'foo', 'echo'
ALELint
@@ -32,3 +32,15 @@ Execute(It should be possible to compute an executable to check based on the res
AssertEqual
\ [{'status': 0, 'job_id': 'executable', 'command': 'foo'}],
\ 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''')
diff --git a/test/test_linter_defintion_processing.vader b/test/test_linter_defintion_processing.vader
index 321c6212..0f54cf0e 100644
--- a/test/test_linter_defintion_processing.vader
+++ b/test/test_linter_defintion_processing.vader
@@ -82,7 +82,15 @@ Execute (PreProcess should throw when command is not a string):
\ 'executable': 'echo',
\ '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):
AssertThrows call ale#linter#PreProcess('testft', {