#2132 - lint and fix with ale#command#Run

A new function is added here which will later be modified for public use
in linter and fixer callbacks. All linting and fixing now goes through
this new function, to prove that it works in all cases.
This commit is contained in:
w0rp 2019-02-06 22:00:11 +00:00
parent 3e11cbd18d
commit 81c73da3b9
No known key found for this signature in database
GPG key ID: 0FC1ECAA8C81CD83
25 changed files with 561 additions and 434 deletions

View file

@ -3,33 +3,37 @@
" managing files during linting and fixing cycles. " managing files during linting and fixing cycles.
" This dictionary holds lists of files and directories to remove later. " This dictionary holds lists of files and directories to remove later.
if !exists('s:managed_data') if !exists('s:buffer_data')
let s:managed_data = {} let s:buffer_data = {}
endif endif
" Used to get the data in tests. " Used to get the data in tests.
function! ale#command#GetData() abort function! ale#command#GetData() abort
return deepcopy(s:managed_data) return deepcopy(s:buffer_data)
endfunction endfunction
function! ale#command#ClearData() abort function! ale#command#ClearData() abort
let s:managed_data = {} let s:buffer_data = {}
endfunction
function! ale#command#InitData(buffer) abort
if !has_key(s:buffer_data, a:buffer)
let s:buffer_data[a:buffer] = {
\ 'jobs': {},
\ 'file_list': [],
\ 'directory_list': [],
\}
endif
endfunction endfunction
function! ale#command#ManageFile(buffer, file) abort function! ale#command#ManageFile(buffer, file) abort
if !has_key(s:managed_data, a:buffer) call ale#command#InitData(a:buffer)
let s:managed_data[a:buffer] = {'file_list': [], 'directory_list': []} call add(s:buffer_data[a:buffer].file_list, a:file)
endif
call add(s:managed_data[a:buffer].file_list, a:file)
endfunction endfunction
function! ale#command#ManageDirectory(buffer, directory) abort function! ale#command#ManageDirectory(buffer, directory) abort
if !has_key(s:managed_data, a:buffer) call ale#command#InitData(a:buffer)
let s:managed_data[a:buffer] = {'file_list': [], 'directory_list': []} call add(s:buffer_data[a:buffer].directory_list, a:directory)
endif
call add(s:managed_data[a:buffer].directory_list, a:directory)
endfunction endfunction
function! ale#command#CreateFile(buffer) abort function! ale#command#CreateFile(buffer) abort
@ -61,17 +65,9 @@ function! ale#command#CreateDirectory(buffer) abort
endfunction endfunction
function! ale#command#RemoveManagedFiles(buffer) abort function! ale#command#RemoveManagedFiles(buffer) abort
let l:info = get(s:managed_data, a:buffer, {}) let l:info = get(s:buffer_data, a:buffer, {})
if !empty(l:info) if !empty(l:info) && empty(l:info.jobs)
\&& (
\ !exists('*ale#engine#IsCheckingBuffer')
\ || !ale#engine#IsCheckingBuffer(a:buffer)
\)
\&& (
\ !has_key(g:ale_fix_buffer_data, a:buffer)
\ || g:ale_fix_buffer_data[a:buffer].done
\)
" We can't delete anything in a sandbox, so wait until we escape from " We can't delete anything in a sandbox, so wait until we escape from
" it to delete temporary files and directories. " it to delete temporary files and directories.
if ale#util#InSandbox() if ale#util#InSandbox()
@ -91,7 +87,7 @@ function! ale#command#RemoveManagedFiles(buffer) abort
call delete(l:directory, 'rf') call delete(l:directory, 'rf')
endfor endfor
call remove(s:managed_data, a:buffer) call remove(s:buffer_data, a:buffer)
endif endif
endfunction endfunction
@ -187,3 +183,148 @@ function! ale#command#FormatCommand(buffer, executable, command, pipe_file_if_ne
return [l:temporary_file, l:command, l:file_created] return [l:temporary_file, l:command, l:file_created]
endfunction endfunction
function! ale#command#StopJobs(buffer, job_type) abort
let l:info = get(s:buffer_data, a:buffer, {})
if !empty(l:info)
let l:new_map = {}
for [l:job_id, l:job_type] in items(l:info.jobs)
let l:job_id = str2nr(l:job_id)
if a:job_type is# 'all' || a:job_type is# l:job_type
call ale#job#Stop(l:job_id)
else
let l:new_map[l:job_id] = l:job_type
endif
endfor
let l:info.jobs = l:new_map
endif
endfunction
function! s:GatherOutput(line_list, job_id, line) abort
call add(a:line_list, a:line)
endfunction
function! s:ExitCallback(buffer, line_list, Callback, data) abort
if !has_key(s:buffer_data, a:buffer)
return
endif
let l:jobs = s:buffer_data[a:buffer].jobs
if !has_key(l:jobs, a:data.job_id)
return
endif
let l:job_type = remove(l:jobs, a:data.job_id)
if g:ale_history_enabled
call ale#history#SetExitCode(a:buffer, a:data.job_id, a:data.exit_code)
" Log the output of the command for ALEInfo if we should.
if g:ale_history_log_output && a:data.log_output is 1
call ale#history#RememberOutput(
\ a:buffer,
\ a:data.job_id,
\ a:line_list[:]
\)
endif
endif
" If the callback starts any new jobs, use the same job type for them.
call setbufvar(a:buffer, 'ale_job_type', l:job_type)
call a:Callback(a:buffer, a:line_list, a:data)
endfunction
function! ale#command#Run(buffer, command, options) abort
let l:Callback = a:options.callback
let l:output_stream = get(a:options, 'output_stream', 'stdout')
let l:line_list = []
let [l:temporary_file, l:command, l:file_created] = ale#command#FormatCommand(
\ a:buffer,
\ get(a:options, 'executable', ''),
\ a:command,
\ get(a:options, 'read_buffer', 0),
\ get(a:options, 'input', v:null),
\)
let l:command = ale#job#PrepareCommand(a:buffer, l:command)
let l:job_options = {
\ 'exit_cb': {job_id, exit_code -> s:ExitCallback(
\ a:buffer,
\ l:line_list,
\ l:Callback,
\ {
\ 'job_id': job_id,
\ 'exit_code': exit_code,
\ 'temporary_file': l:temporary_file,
\ 'log_output': get(a:options, 'log_output', 1),
\ }
\ )},
\ 'mode': 'nl',
\}
if l:output_stream is# 'stdout'
let l:job_options.out_cb = function('s:GatherOutput', [l:line_list])
elseif l:output_stream is# 'stderr'
let l:job_options.err_cb = function('s:GatherOutput', [l:line_list])
elseif l:output_stream is# 'both'
let l:job_options.out_cb = function('s:GatherOutput', [l:line_list])
let l:job_options.err_cb = function('s:GatherOutput', [l:line_list])
endif
let l:status = 'failed'
if get(g:, 'ale_run_synchronously') == 1
if get(g:, 'ale_emulate_job_failure') == 1
let l:job_id = 0
else
" Generate a fake job ID for tests.
let s:fake_job_id = get(s:, 'fake_job_id', 0) + 1
let l:job_id = s:fake_job_id
endif
else
let l:job_id = ale#job#Start(l:command, l:job_options)
endif
if l:job_id
let l:status = 'started'
let l:job_type = getbufvar(a:buffer, 'ale_job_type', 'all')
call ale#command#InitData(a:buffer)
let s:buffer_data[a:buffer].jobs[l:job_id] = l:job_type
endif
if g:ale_history_enabled
call ale#history#Add(a:buffer, l:status, l:job_id, l:command)
endif
if get(g:, 'ale_run_synchronously') == 1 && l:job_id
" Run a command synchronously if this test option is set.
call extend(l:line_list, systemlist(
\ type(l:command) is v:t_list
\ ? join(l:command[0:1]) . ' ' . ale#Escape(l:command[2])
\ : l:command
\))
" Don't capture output when the callbacks aren't set.
if !has_key(l:job_options, 'out_cb')
\&& !has_key(l:job_options, 'err_cb')
let l:line_list = []
endif
if !exists('g:ale_run_synchronously_callbacks')
let g:ale_run_synchronously_callbacks = []
endif
call add(
\ g:ale_run_synchronously_callbacks,
\ {-> l:job_options.exit_cb(l:job_id, v:shell_error)}
\)
endif
return l:job_id ? v:true : v:false
endfunction

View file

@ -5,20 +5,10 @@
" Remapping of linter problems. " Remapping of linter problems.
let g:ale_type_map = get(g:, 'ale_type_map', {}) let g:ale_type_map = get(g:, 'ale_type_map', {})
" Stores information for each job including:
"
" linter: The linter dictionary for the job.
" buffer: The buffer number for the job.
" output: The array of lines for the output of the job.
if !has_key(s:, 'job_info_map')
let s:job_info_map = {}
endif
if !has_key(s:, 'executable_cache_map') if !has_key(s:, 'executable_cache_map')
let s:executable_cache_map = {} let s:executable_cache_map = {}
endif endif
function! ale#engine#CleanupEveryBuffer() abort function! ale#engine#CleanupEveryBuffer() abort
for l:key in keys(g:ale_buffer_info) for l:key in keys(g:ale_buffer_info)
" The key could be a filename or a buffer number, so try and " The key could be a filename or a buffer number, so try and
@ -71,11 +61,9 @@ endfunction
function! ale#engine#InitBufferInfo(buffer) abort function! ale#engine#InitBufferInfo(buffer) abort
if !has_key(g:ale_buffer_info, a:buffer) if !has_key(g:ale_buffer_info, a:buffer)
" job_list will hold the list of job IDs
" active_linter_list will hold the list of active linter names " active_linter_list will hold the list of active linter names
" loclist holds the loclist items after all jobs have completed. " loclist holds the loclist items after all jobs have completed.
let g:ale_buffer_info[a:buffer] = { let g:ale_buffer_info[a:buffer] = {
\ 'job_list': [],
\ 'active_linter_list': [], \ 'active_linter_list': [],
\ 'active_other_sources_list': [], \ 'active_other_sources_list': [],
\ 'loclist': [], \ 'loclist': [],
@ -121,12 +109,6 @@ function! ale#engine#CreateDirectory(buffer) abort
return ale#command#CreateDirectory(a:buffer) return ale#command#CreateDirectory(a:buffer)
endfunction endfunction
function! s:GatherOutput(job_id, line) abort
if has_key(s:job_info_map, a:job_id)
call add(s:job_info_map[a:job_id].output, a:line)
endif
endfunction
function! ale#engine#HandleLoclist(linter_name, buffer, loclist, from_other_source) abort function! ale#engine#HandleLoclist(linter_name, buffer, loclist, from_other_source) abort
let l:info = get(g:ale_buffer_info, a:buffer, {}) let l:info = get(g:ale_buffer_info, a:buffer, {})
@ -137,7 +119,7 @@ function! ale#engine#HandleLoclist(linter_name, buffer, loclist, from_other_sour
if !a:from_other_source if !a:from_other_source
" Remove this linter from the list of active linters. " Remove this linter from the list of active linters.
" This may have already been done when the job exits. " This may have already been done when the job exits.
call filter(l:info.active_linter_list, 'v:val isnot# a:linter_name') call filter(l:info.active_linter_list, 'v:val.name isnot# a:linter_name')
endif endif
" Make some adjustments to the loclists to fix common problems, and also " Make some adjustments to the loclists to fix common problems, and also
@ -170,27 +152,19 @@ function! ale#engine#HandleLoclist(linter_name, buffer, loclist, from_other_sour
call ale#engine#SetResults(a:buffer, l:info.loclist) call ale#engine#SetResults(a:buffer, l:info.loclist)
endfunction endfunction
function! s:HandleExit(job_id, exit_code) abort function! s:HandleExit(job_info, buffer, output, data) abort
if !has_key(s:job_info_map, a:job_id) let l:buffer_info = get(g:ale_buffer_info, a:buffer)
if empty(l:buffer_info)
return return
endif endif
let l:job_info = s:job_info_map[a:job_id] let l:linter = a:job_info.linter
let l:linter = l:job_info.linter let l:executable = a:job_info.executable
let l:output = l:job_info.output let l:next_chain_index = a:job_info.next_chain_index
let l:buffer = l:job_info.buffer
let l:executable = l:job_info.executable
let l:next_chain_index = l:job_info.next_chain_index
if g:ale_history_enabled
call ale#history#SetExitCode(l:buffer, a:job_id, a:exit_code)
endif
" Remove this job from the list. " Remove this job from the list.
call ale#job#Stop(a:job_id) call filter(l:buffer_info.active_linter_list, 'v:val.name isnot# l:linter.name')
call remove(s:job_info_map, a:job_id)
call filter(g:ale_buffer_info[l:buffer].job_list, 'v:val isnot# a:job_id')
call filter(g:ale_buffer_info[l:buffer].active_linter_list, 'v:val isnot# l:linter.name')
" Stop here if we land in the handle for a job completing if we're in " Stop here if we land in the handle for a job completing if we're in
" a sandbox. " a sandbox.
@ -198,29 +172,24 @@ function! s:HandleExit(job_id, exit_code) abort
return return
endif endif
if has('nvim') && !empty(l:output) && empty(l:output[-1]) if has('nvim') && !empty(a:output) && empty(a:output[-1])
call remove(l:output, -1) call remove(a:output, -1)
endif endif
if l:next_chain_index < len(get(l:linter, 'command_chain', [])) if l:next_chain_index < len(get(l:linter, 'command_chain', []))
call s:InvokeChain(l:buffer, l:executable, l:linter, l:next_chain_index, l:output) call s:InvokeChain(a:buffer, l:executable, l:linter, l:next_chain_index, a:output)
return return
endif endif
" Log the output of the command for ALEInfo if we should.
if g:ale_history_enabled && g:ale_history_log_output
call ale#history#RememberOutput(l:buffer, a:job_id, l:output[:])
endif
try try
let l:loclist = ale#util#GetFunction(l:linter.callback)(l:buffer, l:output) let l:loclist = ale#util#GetFunction(l:linter.callback)(a:buffer, a:output)
" Handle the function being unknown, or being deleted. " Handle the function being unknown, or being deleted.
catch /E700/ catch /E700/
let l:loclist = [] let l:loclist = []
endtry endtry
call ale#engine#HandleLoclist(l:linter.name, l:buffer, l:loclist, 0) call ale#engine#HandleLoclist(l:linter.name, a:buffer, l:loclist, 0)
endfunction endfunction
function! ale#engine#SetResults(buffer, loclist) abort function! ale#engine#SetResults(buffer, loclist) abort
@ -431,7 +400,7 @@ function! s:RunJob(options) abort
let l:command = a:options.command let l:command = a:options.command
if empty(l:command) if empty(l:command)
return 0 return v:false
endif endif
let l:executable = a:options.executable let l:executable = a:options.executable
@ -442,87 +411,37 @@ function! s:RunJob(options) abort
let l:read_buffer = a:options.read_buffer let l:read_buffer = a:options.read_buffer
let l:info = g:ale_buffer_info[l:buffer] let l:info = g:ale_buffer_info[l:buffer]
let [l:temporary_file, l:command, l:file_created] = ale#command#FormatCommand( let l:run = ale#command#Run(l:buffer, l:command, {
\ l:buffer, \ 'output_stream': l:output_stream,
\ l:executable, \ 'executable': l:executable,
\ l:command, \ 'read_buffer': l:read_buffer,
\ l:read_buffer, \ 'log_output': l:next_chain_index >= len(get(l:linter, 'command_chain', [])),
\ v:null, \ 'callback': function('s:HandleExit', [{
\) \ 'linter': l:linter,
\ 'executable': l:executable,
if l:file_created \ 'next_chain_index': l:next_chain_index,
" If a temporary filename has been formatted in to the command, then \ }]),
" we do not need to send the Vim buffer to the command. \})
let l:read_buffer = 0
endif
let l:command = ale#job#PrepareCommand(l:buffer, l:command)
let l:job_options = {
\ 'mode': 'nl',
\ 'exit_cb': function('s:HandleExit'),
\}
if l:output_stream is# 'stderr'
let l:job_options.err_cb = function('s:GatherOutput')
elseif l:output_stream is# 'both'
let l:job_options.out_cb = function('s:GatherOutput')
let l:job_options.err_cb = function('s:GatherOutput')
else
let l:job_options.out_cb = function('s:GatherOutput')
endif
if get(g:, 'ale_run_synchronously') == 1
" Find a unique Job value to use, which will be the same as the ID for
" running commands synchronously. This is only for test code.
let l:job_id = len(s:job_info_map) + 1
while has_key(s:job_info_map, l:job_id)
let l:job_id += 1
endwhile
else
let l:job_id = ale#job#Start(l:command, l:job_options)
endif
let l:status = 'failed'
" Only proceed if the job is being run. " Only proceed if the job is being run.
if l:job_id if l:run
" Add the job to the list of jobs, so we can track them. let l:found = 0
call add(l:info.job_list, l:job_id)
if index(l:info.active_linter_list, l:linter.name) < 0 for l:other_linter in l:info.active_linter_list
call add(l:info.active_linter_list, l:linter.name) if l:other_linter.name is# l:linter.name
let l:found = 1
break
endif endif
endfor
let l:status = 'started' if !l:found
" Store the ID for the job in the map to read back again. call add(l:info.active_linter_list, l:linter)
let s:job_info_map[l:job_id] = { endif
\ 'linter': l:linter,
\ 'buffer': l:buffer,
\ 'executable': l:executable,
\ 'output': [],
\ 'next_chain_index': l:next_chain_index,
\}
silent doautocmd <nomodeline> User ALEJobStarted silent doautocmd <nomodeline> User ALEJobStarted
endif endif
if g:ale_history_enabled return l:run
call ale#history#Add(l:buffer, l:status, l:job_id, l:command)
endif
if get(g:, 'ale_run_synchronously') == 1
" Run a command synchronously if this test option is set.
let s:job_info_map[l:job_id].output = systemlist(
\ type(l:command) is v:t_list
\ ? join(l:command[0:1]) . ' ' . ale#Escape(l:command[2])
\ : l:command
\)
call l:job_options.exit_cb(l:job_id, v:shell_error)
endif
return l:job_id != 0
endfunction endfunction
" Determine which commands to run for a link in a command chain, or " Determine which commands to run for a link in a command chain, or
@ -599,35 +518,26 @@ function! s:InvokeChain(buffer, executable, linter, chain_index, input) abort
endfunction endfunction
function! s:StopCurrentJobs(buffer, include_lint_file_jobs) abort function! s:StopCurrentJobs(buffer, include_lint_file_jobs) abort
call ale#command#StopJobs(a:buffer, 'linter')
if a:include_lint_file_jobs
call ale#command#StopJobs(a:buffer, 'file_linter')
endif
let l:info = get(g:ale_buffer_info, a:buffer, {}) let l:info = get(g:ale_buffer_info, a:buffer, {})
let l:new_job_list = []
let l:new_active_linter_list = [] let l:new_active_linter_list = []
for l:job_id in get(l:info, 'job_list', []) for l:linter in get(l:info, 'active_linter_list', [])
let l:job_info = get(s:job_info_map, l:job_id, {}) " Keep jobs for linting files when we're only linting buffers.
if !a:include_lint_file_jobs && get(l:linter, 'lint_file')
if !empty(l:job_info) call add(l:new_active_linter_list, l:linter)
if a:include_lint_file_jobs || !l:job_info.linter.lint_file
call ale#job#Stop(l:job_id)
call remove(s:job_info_map, l:job_id)
else
call add(l:new_job_list, l:job_id)
" Linters with jobs still running are still active.
call add(l:new_active_linter_list, l:job_info.linter.name)
endif
endif endif
endfor endfor
" Remove duplicates from the active linter list.
call uniq(sort(l:new_active_linter_list))
" Update the List, so it includes only the jobs we still need.
let l:info.job_list = l:new_job_list
" Update the active linter list, clearing out anything not running. " Update the active linter list, clearing out anything not running.
let l:info.active_linter_list = l:new_active_linter_list let l:info.active_linter_list = l:new_active_linter_list
endfunction 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.
@ -687,6 +597,10 @@ function! s:RunLinter(buffer, linter) abort
else else
let l:executable = ale#linter#GetExecutable(a:buffer, a:linter) let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
" Use different job types for file or linter jobs.
let l:job_type = a:linter.lint_file ? 'file_linter' : 'linter'
call setbufvar(a:buffer, 'ale_job_type', l:job_type)
if ale#engine#IsExecutable(a:buffer, l:executable) if ale#engine#IsExecutable(a:buffer, l:executable)
return s:InvokeChain(a:buffer, l:executable, a:linter, 0, []) return s:InvokeChain(a:buffer, l:executable, a:linter, 0, [])
endif endif
@ -757,90 +671,3 @@ function! ale#engine#GetLoclist(buffer) abort
return g:ale_buffer_info[a:buffer].loclist return g:ale_buffer_info[a:buffer].loclist
endfunction endfunction
" This function can be called with a timeout to wait for all jobs to finish.
" If the jobs to not finish in the given number of milliseconds,
" an exception will be thrown.
"
" The time taken will be a very rough approximation, and more time may be
" permitted than is specified.
function! ale#engine#WaitForJobs(deadline) abort
let l:start_time = ale#events#ClockMilliseconds()
if l:start_time == 0
throw 'Failed to read milliseconds from the clock!'
endif
let l:job_list = []
" Gather all of the jobs from every buffer.
for l:info in values(g:ale_buffer_info)
call extend(l:job_list, get(l:info, 'job_list', []))
endfor
" NeoVim has a built-in API for this, so use that.
if has('nvim')
let l:nvim_code_list = jobwait(l:job_list, a:deadline)
if index(l:nvim_code_list, -1) >= 0
throw 'Jobs did not complete on time!'
endif
return
endif
let l:should_wait_more = 1
while l:should_wait_more
let l:should_wait_more = 0
for l:job_id in l:job_list
if ale#job#IsRunning(l:job_id)
let l:now = ale#events#ClockMilliseconds()
if l:now - l:start_time > a:deadline
" Stop waiting after a timeout, so we don't wait forever.
throw 'Jobs did not complete on time!'
endif
" Wait another 10 milliseconds
let l:should_wait_more = 1
sleep 10ms
break
endif
endfor
endwhile
" Sleep for a small amount of time after all jobs finish.
" This seems to be enough to let handlers after jobs end run, and
" prevents the occasional failure where this function exits after jobs
" end, but before handlers are run.
sleep 10ms
" We must check the buffer data again to see if new jobs started
" for command_chain linters.
let l:has_new_jobs = 0
" Check again to see if any jobs are running.
for l:info in values(g:ale_buffer_info)
for l:job_id in get(l:info, 'job_list', [])
if ale#job#IsRunning(l:job_id)
let l:has_new_jobs = 1
break
endif
endfor
endfor
if l:has_new_jobs
" We have to wait more. Offset the timeout by the time taken so far.
let l:now = ale#events#ClockMilliseconds()
let l:new_deadline = a:deadline - (l:now - l:start_time)
if l:new_deadline <= 0
" Enough time passed already, so stop immediately.
throw 'Jobs did not complete on time!'
endif
call ale#engine#WaitForJobs(l:new_deadline)
endif
endfunction

View file

@ -1,13 +1,3 @@
if !has_key(s:, 'job_info_map')
let s:job_info_map = {}
endif
function! s:GatherOutput(job_id, line) abort
if has_key(s:job_info_map, a:job_id)
call add(s:job_info_map[a:job_id].output, a:line)
endif
endfunction
" Apply fixes queued up for buffers which may be hidden. " Apply fixes queued up for buffers which may be hidden.
" Vim doesn't let you modify hidden buffers. " Vim doesn't let you modify hidden buffers.
function! ale#fix#ApplyQueuedFixes() abort function! ale#fix#ApplyQueuedFixes() abort
@ -94,52 +84,48 @@ function! ale#fix#ApplyFixes(buffer, output) abort
call ale#fix#ApplyQueuedFixes() call ale#fix#ApplyQueuedFixes()
endfunction endfunction
function! s:HandleExit(job_id, exit_code) abort function! s:HandleExit(job_info, buffer, job_output, data) abort
if !has_key(s:job_info_map, a:job_id) let l:buffer_info = get(g:ale_fix_buffer_data, a:buffer, {})
if empty(l:buffer_info)
return return
endif endif
let l:job_info = remove(s:job_info_map, a:job_id) if a:job_info.read_temporary_file
let l:buffer = l:job_info.buffer let l:output = !empty(a:data.temporary_file)
\ ? readfile(a:data.temporary_file)
if g:ale_history_enabled \ : []
call ale#history#SetExitCode(l:buffer, a:job_id, a:exit_code) else
let l:output = a:job_output
endif endif
if has_key(l:job_info, 'file_to_read') let l:ChainCallback = get(a:job_info, 'chain_with', v:null)
let l:job_info.output = readfile(l:job_info.file_to_read) let l:ProcessWith = get(a:job_info, 'process_with', v:null)
endif
let l:ChainCallback = get(l:job_info, 'chain_with', v:null)
let l:ProcessWith = get(l:job_info, 'process_with', v:null)
" Post-process the output with a function if we have one. " Post-process the output with a function if we have one.
if l:ProcessWith isnot v:null if l:ProcessWith isnot v:null
let l:job_info.output = call( let l:output = call(l:ProcessWith, [a:buffer, l:output])
\ ale#util#GetFunction(l:ProcessWith),
\ [l:buffer, l:job_info.output]
\)
endif endif
" Use the output of the job for changing the file if it isn't empty, " Use the output of the job for changing the file if it isn't empty,
" otherwise skip this job and use the input from before. " otherwise skip this job and use the input from before.
" "
" We'll use the input from before for chained commands. " We'll use the input from before for chained commands.
if l:ChainCallback is v:null && !empty(split(join(l:job_info.output))) if l:ChainCallback is v:null && !empty(split(join(l:output)))
let l:input = l:job_info.output let l:input = l:output
else else
let l:input = l:job_info.input let l:input = a:job_info.input
endif endif
let l:next_index = l:ChainCallback is v:null let l:next_index = l:ChainCallback is v:null
\ ? l:job_info.callback_index + 1 \ ? a:job_info.callback_index + 1
\ : l:job_info.callback_index \ : a:job_info.callback_index
call s:RunFixer({ call s:RunFixer({
\ 'buffer': l:buffer, \ 'buffer': a:buffer,
\ 'input': l:input, \ 'input': l:input,
\ 'output': l:job_info.output, \ 'output': l:output,
\ 'callback_list': l:job_info.callback_list, \ 'callback_list': a:job_info.callback_list,
\ 'callback_index': l:next_index, \ 'callback_index': l:next_index,
\ 'chain_callback': l:ChainCallback, \ 'chain_callback': l:ChainCallback,
\}) \})
@ -149,15 +135,13 @@ function! s:RunJob(options) abort
let l:buffer = a:options.buffer let l:buffer = a:options.buffer
let l:command = a:options.command let l:command = a:options.command
let l:input = a:options.input let l:input = a:options.input
let l:output_stream = a:options.output_stream
let l:read_temporary_file = a:options.read_temporary_file
let l:ChainWith = a:options.chain_with let l:ChainWith = a:options.chain_with
let l:read_buffer = a:options.read_buffer let l:read_buffer = a:options.read_buffer
if empty(l:command) if empty(l:command)
" If there's nothing further to chain the command with, stop here. " If there's nothing further to chain the command with, stop here.
if l:ChainWith is v:null if l:ChainWith is v:null
return 0 return v:false
endif endif
" If there's another chained callback to run, then run that. " If there's another chained callback to run, then run that.
@ -170,87 +154,30 @@ function! s:RunJob(options) abort
\ 'output': [], \ 'output': [],
\}) \})
return 1 return v:true
endif endif
let [l:temporary_file, l:command, l:file_created] = ale#command#FormatCommand( let l:output_stream = a:options.output_stream
\ l:buffer,
\ '',
\ l:command,
\ l:read_buffer,
\ l:input,
\)
let l:command = ale#job#PrepareCommand(l:buffer, l:command) if a:options.read_temporary_file
let l:job_options = { let l:output_stream = 'none'
\ 'mode': 'nl', endif
\ 'exit_cb': function('s:HandleExit'),
\}
let l:job_info = { return ale#command#Run(l:buffer, l:command, {
\ 'buffer': l:buffer, \ 'output_stream': l:output_stream,
\ 'executable': '',
\ 'read_buffer': l:read_buffer,
\ 'input': l:input,
\ 'log_output': 0,
\ 'callback': function('s:HandleExit', [{
\ 'input': l:input, \ 'input': l:input,
\ 'output': [],
\ 'chain_with': l:ChainWith, \ 'chain_with': l:ChainWith,
\ 'callback_index': a:options.callback_index, \ 'callback_index': a:options.callback_index,
\ 'callback_list': a:options.callback_list, \ 'callback_list': a:options.callback_list,
\ 'process_with': a:options.process_with, \ 'process_with': a:options.process_with,
\} \ 'read_temporary_file': a:options.read_temporary_file,
\ }]),
if l:read_temporary_file \})
" TODO: Check that a temporary file is set here.
let l:job_info.file_to_read = l:temporary_file
elseif l:output_stream is# 'stderr'
let l:job_options.err_cb = function('s:GatherOutput')
elseif l:output_stream is# 'both'
let l:job_options.out_cb = function('s:GatherOutput')
let l:job_options.err_cb = function('s:GatherOutput')
else
let l:job_options.out_cb = function('s:GatherOutput')
endif
if get(g:, 'ale_emulate_job_failure') == 1
let l:job_id = 0
elseif get(g:, 'ale_run_synchronously') == 1
" Find a unique Job value to use, which will be the same as the ID for
" running commands synchronously. This is only for test code.
let l:job_id = len(s:job_info_map) + 1
while has_key(s:job_info_map, l:job_id)
let l:job_id += 1
endwhile
else
let l:job_id = ale#job#Start(l:command, l:job_options)
endif
let l:status = l:job_id ? 'started' : 'failed'
if g:ale_history_enabled
call ale#history#Add(l:buffer, l:status, l:job_id, l:command)
endif
if l:job_id == 0
return 0
endif
let s:job_info_map[l:job_id] = l:job_info
if get(g:, 'ale_run_synchronously') == 1
" Run a command synchronously if this test option is set.
let l:output = systemlist(
\ type(l:command) is v:t_list
\ ? join(l:command[0:1]) . ' ' . ale#Escape(l:command[2])
\ : l:command
\)
if !l:read_temporary_file
let s:job_info_map[l:job_id].output = l:output
endif
call l:job_options.exit_cb(l:job_id, v:shell_error)
endif
return 1
endfunction endfunction
function! s:RunFixer(options) abort function! s:RunFixer(options) abort
@ -259,6 +186,9 @@ function! s:RunFixer(options) abort
let l:index = a:options.callback_index let l:index = a:options.callback_index
let l:ChainCallback = get(a:options, 'chain_callback', v:null) let l:ChainCallback = get(a:options, 'chain_callback', v:null)
" Record new jobs started as fixer jobs.
call setbufvar(l:buffer, 'ale_job_type', 'fixer')
while len(a:options.callback_list) > l:index while len(a:options.callback_list) > l:index
let l:Function = l:ChainCallback isnot v:null let l:Function = l:ChainCallback isnot v:null
\ ? ale#util#GetFunction(l:ChainCallback) \ ? ale#util#GetFunction(l:ChainCallback)
@ -419,16 +349,7 @@ function! ale#fix#Fix(buffer, fixing_flag, ...) abort
return 0 return 0
endif endif
for l:job_id in keys(s:job_info_map) call ale#command#StopJobs(a:buffer, 'fixer')
call remove(s:job_info_map, l:job_id)
call ale#job#Stop(l:job_id)
endfor
" Mark the buffer as `done` so files can be removed.
if has_key(g:ale_fix_buffer_data, a:buffer)
let g:ale_fix_buffer_data[a:buffer].done = 1
endif
" Clean up any files we might have left behind from a previous run. " Clean up any files we might have left behind from a previous run.
call ale#command#RemoveManagedFiles(a:buffer) call ale#command#RemoveManagedFiles(a:buffer)
call ale#fix#InitBufferData(a:buffer, a:fixing_flag) call ale#fix#InitBufferData(a:buffer, a:fixing_flag)

View file

@ -99,7 +99,8 @@ function! s:VimCloseCallback(channel) abort
if job_status(l:job) is# 'dead' if job_status(l:job) is# 'dead'
try try
if !empty(l:info) && has_key(l:info, 'exit_cb') if !empty(l:info) && has_key(l:info, 'exit_cb')
call ale#util#GetFunction(l:info.exit_cb)(l:job_id, get(l:info, 'exit_code', 1)) " We have to remove the callback, so we don't call it twice.
call ale#util#GetFunction(remove(l:info, 'exit_cb'))(l:job_id, get(l:info, 'exit_code', 1))
endif endif
finally finally
" Automatically forget about the job after it's done. " Automatically forget about the job after it's done.
@ -124,7 +125,8 @@ function! s:VimExitCallback(job, exit_code) abort
if ch_status(job_getchannel(a:job)) is# 'closed' if ch_status(job_getchannel(a:job)) is# 'closed'
try try
if !empty(l:info) && has_key(l:info, 'exit_cb') if !empty(l:info) && has_key(l:info, 'exit_cb')
call ale#util#GetFunction(l:info.exit_cb)(l:job_id, a:exit_code) " We have to remove the callback, so we don't call it twice.
call ale#util#GetFunction(remove(l:info, 'exit_cb'))(l:job_id, a:exit_code)
endif endif
finally finally
" Automatically forget about the job after it's done. " Automatically forget about the job after it's done.

View file

@ -292,8 +292,17 @@ function! ale#lsp_linter#CheckWithLSP(buffer, linter) abort
endif endif
if l:notified if l:notified
if index(l:info.active_linter_list, a:linter.name) < 0 let l:found = 0
call add(l:info.active_linter_list, a:linter.name)
for l:other_linter in l:info.active_linter_list
if l:other_linter.name is# a:linter.name
let l:found = 1
break
endif
endfor
if !l:found
call add(l:info.active_linter_list, a:linter)
endif endif
endif endif

View file

@ -85,3 +85,103 @@ function! ale#test#GetPreviewWindowText() abort
endif endif
endfor endfor
endfunction endfunction
" This function can be called with a timeout to wait for all jobs to finish.
" If the jobs to not finish in the given number of milliseconds,
" an exception will be thrown.
"
" The time taken will be a very rough approximation, and more time may be
" permitted than is specified.
function! ale#test#WaitForJobs(deadline) abort
let l:start_time = ale#events#ClockMilliseconds()
if l:start_time == 0
throw 'Failed to read milliseconds from the clock!'
endif
let l:job_list = []
" Gather all of the jobs from every buffer.
for [l:buffer, l:data] in items(ale#command#GetData())
call extend(l:job_list, map(keys(l:data.jobs), 'str2nr(v:val)'))
endfor
" NeoVim has a built-in API for this, so use that.
if has('nvim')
let l:nvim_code_list = jobwait(l:job_list, a:deadline)
if index(l:nvim_code_list, -1) >= 0
throw 'Jobs did not complete on time!'
endif
return
endif
let l:should_wait_more = 1
while l:should_wait_more
let l:should_wait_more = 0
for l:job_id in l:job_list
if ale#job#IsRunning(l:job_id)
let l:now = ale#events#ClockMilliseconds()
if l:now - l:start_time > a:deadline
" Stop waiting after a timeout, so we don't wait forever.
throw 'Jobs did not complete on time!'
endif
" Wait another 10 milliseconds
let l:should_wait_more = 1
sleep 10ms
break
endif
endfor
endwhile
" Sleep for a small amount of time after all jobs finish.
" This seems to be enough to let handlers after jobs end run, and
" prevents the occasional failure where this function exits after jobs
" end, but before handlers are run.
sleep 10ms
" We must check the buffer data again to see if new jobs started
" for command_chain linters.
let l:has_new_jobs = 0
" Check again to see if any jobs are running.
for l:info in values(g:ale_buffer_info)
for [l:job_id, l:linter] in get(l:info, 'job_list', [])
if ale#job#IsRunning(l:job_id)
let l:has_new_jobs = 1
break
endif
endfor
endfor
if l:has_new_jobs
" We have to wait more. Offset the timeout by the time taken so far.
let l:now = ale#events#ClockMilliseconds()
let l:new_deadline = a:deadline - (l:now - l:start_time)
if l:new_deadline <= 0
" Enough time passed already, so stop immediately.
throw 'Jobs did not complete on time!'
endif
call ale#test#WaitForJobs(l:new_deadline)
endif
endfunction
function! ale#test#FlushJobs() abort
" The variable is checked for in a loop, as calling one series of
" callbacks can trigger a further series of callbacks.
while exists('g:ale_run_synchronously_callbacks')
let l:callbacks = g:ale_run_synchronously_callbacks
unlet g:ale_run_synchronously_callbacks
for l:Callback in l:callbacks
call l:Callback()
endfor
endwhile
endfunction

View file

@ -2669,9 +2669,9 @@ ale#command#ManageDirectory(buffer, directory) *ale#command#ManageDirectory()*
ale#command#ManageFile(buffer, filename) *ale#command#ManageFile()* ale#command#ManageFile(buffer, filename) *ale#command#ManageFile()*
Given a buffer number for a buffer currently running some linting tasks Given a buffer number for a buffer currently running some linting or fixing
and a filename, register a filename with ALE for automatic deletion after tasks and a filename, register a filename with ALE for automatic deletion
linting is complete, or when Vim exits. after linting or fixing is complete, or when Vim exits.
If Vim exits suddenly, ALE will try its best to remove temporary files, but If Vim exits suddenly, ALE will try its best to remove temporary files, but
ALE cannot guarantee with absolute certainty that the files will be removed. ALE cannot guarantee with absolute certainty that the files will be removed.
@ -2682,7 +2682,7 @@ ale#command#ManageFile(buffer, filename) *ale#command#ManageFile()*
files and symlinks given to this function. This is to prevent entire files and symlinks given to this function. This is to prevent entire
directories from being accidentally deleted, say in cases of writing directories from being accidentally deleted, say in cases of writing
`dir . '/' . filename` where `filename` is actually `''`, etc. ALE instead `dir . '/' . filename` where `filename` is actually `''`, etc. ALE instead
manages directories separetly with the |ale#command#ManageDirectory| function. manages directories separately with the |ale#command#ManageDirectory| function.
ale#engine#GetLoclist(buffer) *ale#engine#GetLoclist()* ale#engine#GetLoclist(buffer) *ale#engine#GetLoclist()*

View file

@ -11,6 +11,7 @@ Before:
let g:ale_enabled = 0 let g:ale_enabled = 0
let g:ale_echo_cursor = 0 let g:ale_echo_cursor = 0
let g:ale_run_synchronously = 1 let g:ale_run_synchronously = 1
unlet! g:ale_run_synchronously_callbacks
let g:ale_set_lists_synchronously = 1 let g:ale_set_lists_synchronously = 1
let g:ale_fix_buffer_data = {} let g:ale_fix_buffer_data = {}
let g:ale_fixers = { let g:ale_fixers = {
@ -59,6 +60,10 @@ Before:
return {'command': 'echo x > %t', 'read_temporary_file': 1} return {'command': 'echo x > %t', 'read_temporary_file': 1}
endfunction endfunction
function CatWithTempFile(buffer, done, lines) abort
return {'command': 'cat %t <(echo d)'}
endfunction
function RemoveLastLine(buffer, done, lines) abort function RemoveLastLine(buffer, done, lines) abort
return ['a', 'b'] return ['a', 'b']
endfunction endfunction
@ -177,6 +182,7 @@ After:
Restore Restore
unlet! g:ale_run_synchronously unlet! g:ale_run_synchronously
unlet! g:ale_set_lists_synchronously unlet! g:ale_set_lists_synchronously
unlet! g:ale_run_synchronously_callbacks
unlet! g:ale_emulate_job_failure unlet! g:ale_emulate_job_failure
unlet! b:ale_fixers unlet! b:ale_fixers
unlet! b:ale_fix_on_save unlet! b:ale_fix_on_save
@ -187,6 +193,7 @@ After:
delfunction CatLine delfunction CatLine
delfunction CatLineOneArg delfunction CatLineOneArg
delfunction ReplaceWithTempFile delfunction ReplaceWithTempFile
delfunction CatWithTempFile
delfunction RemoveLastLine delfunction RemoveLastLine
delfunction RemoveLastLineOneArg delfunction RemoveLastLineOneArg
delfunction TestCallback delfunction TestCallback
@ -235,11 +242,13 @@ Given testft (A file with three lines):
Execute(ALEFix should complain when there are no functions to call): Execute(ALEFix should complain when there are no functions to call):
ALEFix ALEFix
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 apply simple functions): Execute(ALEFix should apply simple functions):
let g:ale_fixers.testft = ['AddCarets'] let g:ale_fixers.testft = ['AddCarets']
ALEFix ALEFix
call ale#test#FlushJobs()
Expect(The first function should be used): Expect(The first function should be used):
^a ^a
@ -249,6 +258,7 @@ Expect(The first function should be used):
Execute(ALEFix should apply simple functions in a chain): Execute(ALEFix should apply simple functions in a chain):
let g:ale_fixers.testft = ['AddCarets', 'AddDollars'] let g:ale_fixers.testft = ['AddCarets', 'AddDollars']
ALEFix ALEFix
call ale#test#FlushJobs()
Expect(Both functions should be used): Expect(Both functions should be used):
$^a $^a
@ -258,6 +268,7 @@ Expect(Both functions should be used):
Execute(ALEFix should allow 0 to be returned to skip functions): Execute(ALEFix should allow 0 to be returned to skip functions):
let g:ale_fixers.testft = ['DoNothing', 'AddDollars'] let g:ale_fixers.testft = ['DoNothing', 'AddDollars']
ALEFix ALEFix
call ale#test#FlushJobs()
Expect(Only the second function should be applied): Expect(Only the second function should be applied):
$a $a
@ -268,6 +279,7 @@ Execute(The * fixers shouldn't be used if an empty list is set for fixers):
let g:ale_fixers.testft = [] let g:ale_fixers.testft = []
let g:ale_fixers['*'] = ['AddDollars'] let g:ale_fixers['*'] = ['AddDollars']
ALEFix ALEFix
call ale#test#FlushJobs()
Expect(Nothing should be changed): Expect(Nothing should be changed):
a a
@ -277,6 +289,7 @@ Expect(Nothing should be changed):
Execute(* fixers should be used if no filetype is matched): Execute(* fixers should be used if no filetype is matched):
let g:ale_fixers = {'*': ['AddDollars']} let g:ale_fixers = {'*': ['AddDollars']}
ALEFix ALEFix
call ale#test#FlushJobs()
Expect(The file should be changed): Expect(The file should be changed):
$a $a
@ -290,6 +303,7 @@ Execute(ALEFix should allow commands to be run):
else else
let g:ale_fixers.testft = ['CatLine'] let g:ale_fixers.testft = ['CatLine']
ALEFix ALEFix
call ale#test#FlushJobs()
endif endif
Expect(An extra line should be added): Expect(An extra line should be added):
@ -301,6 +315,7 @@ Expect(An extra line should be added):
Execute(ALEFix should use fixers passed in commandline when provided): Execute(ALEFix should use fixers passed in commandline when provided):
let g:ale_fixers.testft = ['RemoveLastLine'] let g:ale_fixers.testft = ['RemoveLastLine']
ALEFix AddCarets AddDollars ALEFix AddCarets AddDollars
call ale#test#FlushJobs()
Expect(Only fixers passed via command line should be run): Expect(Only fixers passed via command line should be run):
$^a $^a
@ -315,11 +330,28 @@ Execute(ALEFix should allow temporary files to be read):
else else
let g:ale_fixers.testft = ['ReplaceWithTempFile'] let g:ale_fixers.testft = ['ReplaceWithTempFile']
ALEFix ALEFix
call ale#test#FlushJobs()
endif endif
Expect(The line we wrote to the temporary file should be used here): Expect(The line we wrote to the temporary file should be used here):
x x
Execute(ALEFix should not read the temporary file when the option is not set):
if has('win32')
" Just skip this test on Windows, we can't run it.
call setline(1, ['a', 'b', 'c', 'd'])
else
let g:ale_fixers.testft = ['CatWithTempFile']
ALEFix
call ale#test#FlushJobs()
endif
Expect(An extra line should be added):
a
b
c
d
Execute(ALEFix should allow jobs and simple functions to be combined): Execute(ALEFix should allow jobs and simple functions to be combined):
if has('win32') if has('win32')
" Just skip this test on Windows, we can't run it. " Just skip this test on Windows, we can't run it.
@ -328,6 +360,7 @@ Execute(ALEFix should allow jobs and simple functions to be combined):
else else
let g:ale_fixers.testft = ['ReplaceWithTempFile', 'AddDollars'] let g:ale_fixers.testft = ['ReplaceWithTempFile', 'AddDollars']
ALEFix ALEFix
call ale#test#FlushJobs()
endif endif
Expect(The lines from the temporary file should be modified): Expect(The lines from the temporary file should be modified):
@ -340,6 +373,7 @@ Execute(ALEFix should send lines modified by functions to jobs):
else else
let g:ale_fixers.testft = ['AddDollars', 'CatLine'] let g:ale_fixers.testft = ['AddDollars', 'CatLine']
ALEFix ALEFix
call ale#test#FlushJobs()
endif endif
Expect(The lines should first be modified by the function, then the job): Expect(The lines should first be modified by the function, then the job):
@ -352,6 +386,7 @@ Execute(ALEFix should skip commands when jobs fail to run):
let g:ale_emulate_job_failure = 1 let g:ale_emulate_job_failure = 1
let g:ale_fixers.testft = ['CatLine', 'AddDollars'] let g:ale_fixers.testft = ['CatLine', 'AddDollars']
ALEFix ALEFix
call ale#test#FlushJobs()
Expect(Only the second function should be applied): Expect(Only the second function should be applied):
$a $a
@ -361,6 +396,7 @@ Expect(Only the second function should be applied):
Execute(ALEFix should handle strings for selecting a single function): Execute(ALEFix should handle strings for selecting a single function):
let g:ale_fixers.testft = 'AddCarets' let g:ale_fixers.testft = 'AddCarets'
ALEFix ALEFix
call ale#test#FlushJobs()
Expect(The first function should be used): Expect(The first function should be used):
^a ^a
@ -371,6 +407,7 @@ Execute(ALEFix should use functions from the registry):
call ale#fix#registry#Add('add_carets', 'AddCarets', [], 'Add some carets') call ale#fix#registry#Add('add_carets', 'AddCarets', [], 'Add some carets')
let g:ale_fixers.testft = ['add_carets'] let g:ale_fixers.testft = ['add_carets']
ALEFix ALEFix
call ale#test#FlushJobs()
Expect(The registry function should be used): Expect(The registry function should be used):
^a ^a
@ -380,6 +417,7 @@ Expect(The registry function should be used):
Execute(ALEFix should be able to remove the last line for files): Execute(ALEFix should be able to remove the last line for files):
let g:ale_fixers.testft = ['RemoveLastLine'] let g:ale_fixers.testft = ['RemoveLastLine']
ALEFix ALEFix
call ale#test#FlushJobs()
Expect(There should be only two lines): Expect(There should be only two lines):
a a
@ -388,6 +426,7 @@ Expect(There should be only two lines):
Execute(ALEFix should accept funcrefs): Execute(ALEFix should accept funcrefs):
let g:ale_fixers.testft = [function('RemoveLastLine')] let g:ale_fixers.testft = [function('RemoveLastLine')]
ALEFix ALEFix
call ale#test#FlushJobs()
Expect(There should be only two lines): Expect(There should be only two lines):
a a
@ -401,6 +440,7 @@ Execute(ALEFix should accept lambdas):
else else
let g:ale_fixers.testft = [{buffer, done, lines -> lines + ['d']}] let g:ale_fixers.testft = [{buffer, done, lines -> lines + ['d']}]
ALEFix ALEFix
call ale#test#FlushJobs()
endif endif
Expect(There should be an extra line): Expect(There should be an extra line):
@ -413,6 +453,7 @@ Execute(ALEFix should user buffer-local fixer settings):
let g:ale_fixers.testft = ['AddCarets', 'AddDollars'] let g:ale_fixers.testft = ['AddCarets', 'AddDollars']
let b:ale_fixers = {'testft': ['RemoveLastLine']} let b:ale_fixers = {'testft': ['RemoveLastLine']}
ALEFix ALEFix
call ale#test#FlushJobs()
Expect(There should be only two lines): Expect(There should be only two lines):
a a
@ -422,6 +463,7 @@ Execute(ALEFix should allow Lists to be used for buffer-local fixer settings):
let g:ale_fixers.testft = ['AddCarets', 'AddDollars'] let g:ale_fixers.testft = ['AddCarets', 'AddDollars']
let b:ale_fixers = ['RemoveLastLine'] let b:ale_fixers = ['RemoveLastLine']
ALEFix ALEFix
call ale#test#FlushJobs()
Expect(There should be only two lines): Expect(There should be only two lines):
a a
@ -447,6 +489,7 @@ Execute(ALEFix should fix files on the save event):
call SetUpLinters() call SetUpLinters()
call ale#events#SaveEvent(bufnr('')) call ale#events#SaveEvent(bufnr(''))
call ale#test#FlushJobs()
" We should save the file. " We should save the file.
AssertEqual ['$a', '$b', '$c'], readfile('fix_test_file') AssertEqual ['$a', '$b', '$c'], readfile('fix_test_file')
@ -518,6 +561,7 @@ Execute(ALEFix should still lint with no linters to be applied):
call SetUpLinters() call SetUpLinters()
call ale#events#SaveEvent(bufnr('')) call ale#events#SaveEvent(bufnr(''))
call ale#test#FlushJobs()
Assert !filereadable('fix_test_file'), 'The file should not have been saved' Assert !filereadable('fix_test_file'), 'The file should not have been saved'
@ -552,6 +596,7 @@ Execute(ALEFix should still lint when nothing was fixed on save):
call SetUpLinters() call SetUpLinters()
call ale#events#SaveEvent(bufnr('')) call ale#events#SaveEvent(bufnr(''))
call ale#test#FlushJobs()
Assert !filereadable('fix_test_file'), 'The file should not have been saved' Assert !filereadable('fix_test_file'), 'The file should not have been saved'
@ -597,6 +642,7 @@ Execute(ale#fix#InitBufferData() should set up the correct data):
Execute(ALEFix simple functions should be able to accept one argument, the buffer): Execute(ALEFix simple functions should be able to accept one argument, the buffer):
let g:ale_fixers.testft = ['RemoveLastLineOneArg'] let g:ale_fixers.testft = ['RemoveLastLineOneArg']
ALEFix ALEFix
call ale#test#FlushJobs()
Expect(There should be only two lines): Expect(There should be only two lines):
a a
@ -632,6 +678,7 @@ Execute(ALEFix functions returning jobs should be able to accept one argument):
else else
let g:ale_fixers.testft = ['CatLine'] let g:ale_fixers.testft = ['CatLine']
ALEFix ALEFix
call ale#test#FlushJobs()
endif endif
Expect(An extra line should be added): Expect(An extra line should be added):
@ -643,22 +690,26 @@ Expect(An extra line should be added):
Execute(ALE should print a message telling you something isn't a valid fixer when you type some nonsense): Execute(ALE should print a message telling you something isn't a valid fixer when you type some nonsense):
let g:ale_fixers.testft = ['CatLine', 'invalidname'] let g:ale_fixers.testft = ['CatLine', 'invalidname']
ALEFix ALEFix
call ale#test#FlushJobs()
AssertEqual 'There is no fixer named `invalidname`. Check :ALEFixSuggest', GetLastMessage() AssertEqual 'There is no fixer named `invalidname`. Check :ALEFixSuggest', GetLastMessage()
Execute(ALE should complain about invalid fixers with minuses in the name): Execute(ALE should complain about invalid fixers with minuses in the name):
let g:ale_fixers.testft = ['foo-bar'] let g:ale_fixers.testft = ['foo-bar']
ALEFix ALEFix
call ale#test#FlushJobs()
AssertEqual 'There is no fixer named `foo-bar`. Check :ALEFixSuggest', GetLastMessage() AssertEqual 'There is no fixer named `foo-bar`. Check :ALEFixSuggest', GetLastMessage()
Execute(ALE should tolerate valid fixers with minuses in the name): Execute(ALE should tolerate valid fixers with minuses in the name):
let g:ale_fixers.testft = ['prettier-standard'] let g:ale_fixers.testft = ['prettier-standard']
ALEFix ALEFix
call ale#test#FlushJobs()
Execute(Test fixing with chained callbacks): Execute(Test fixing with chained callbacks):
let g:ale_fixers.testft = ['FirstChainCallback'] let g:ale_fixers.testft = ['FirstChainCallback']
ALEFix ALEFix
call ale#test#FlushJobs()
" The buffer shouldn't be piped in for earlier commands in the chain. " The buffer shouldn't be piped in for earlier commands in the chain.
AssertEqual AssertEqual
@ -677,6 +728,7 @@ Expect(The echoed line should be added):
Execute(Test fixing with chained callback where the first command is skipped): Execute(Test fixing with chained callback where the first command is skipped):
let g:ale_fixers.testft = ['FirstChainCallbackSkipped'] let g:ale_fixers.testft = ['FirstChainCallbackSkipped']
ALEFix ALEFix
call ale#test#FlushJobs()
Expect(The default line should be added): Expect(The default line should be added):
a a
@ -687,6 +739,7 @@ Expect(The default line should be added):
Execute(Test fixing with chained callback where the second command is skipped): Execute(Test fixing with chained callback where the second command is skipped):
let g:ale_fixers.testft = ['FirstChainCallbackSecondSkipped'] let g:ale_fixers.testft = ['FirstChainCallbackSecondSkipped']
ALEFix ALEFix
call ale#test#FlushJobs()
Expect(The default line should be added): Expect(The default line should be added):
a a
@ -697,6 +750,7 @@ Expect(The default line should be added):
Execute(Test fixing with chained callback where the final callback is skipped): Execute(Test fixing with chained callback where the final callback is skipped):
let g:ale_fixers.testft = ['ChainWhereLastIsSkipped'] let g:ale_fixers.testft = ['ChainWhereLastIsSkipped']
ALEFix ALEFix
call ale#test#FlushJobs()
Expect(The lines should be the same): Expect(The lines should be the same):
a a
@ -706,6 +760,7 @@ Expect(The lines should be the same):
Execute(Empty output should be ignored): Execute(Empty output should be ignored):
let g:ale_fixers.testft = ['IgnoredEmptyOutput'] let g:ale_fixers.testft = ['IgnoredEmptyOutput']
ALEFix ALEFix
call ale#test#FlushJobs()
Expect(The lines should be the same): Expect(The lines should be the same):
a a
@ -715,6 +770,7 @@ Expect(The lines should be the same):
Execute(A temporary file shouldn't be piped into the command when disabled): Execute(A temporary file shouldn't be piped into the command when disabled):
let g:ale_fixers.testft = ['EchoLineNoPipe'] let g:ale_fixers.testft = ['EchoLineNoPipe']
ALEFix ALEFix
call ale#test#FlushJobs()
AssertEqual AssertEqual
\ string(ale#job#PrepareCommand(bufnr(''), 'echo new line')), \ string(ale#job#PrepareCommand(bufnr(''), 'echo new line')),
@ -731,6 +787,7 @@ Expect(The new line should be used):
Execute(Post-processing should work): Execute(Post-processing should work):
let g:ale_fixers.testft = ['FixWithJSONPostProcessing'] let g:ale_fixers.testft = ['FixWithJSONPostProcessing']
ALEFix ALEFix
call ale#test#FlushJobs()
Expect(The lines in the JSON should be used): Expect(The lines in the JSON should be used):
x x
@ -740,5 +797,7 @@ Expect(The lines in the JSON should be used):
Execute(ALEFix should apply autocmds): Execute(ALEFix should apply autocmds):
let g:ale_fixers.testft = ['AddCarets'] let g:ale_fixers.testft = ['AddCarets']
ALEFix ALEFix
call ale#test#FlushJobs()
AssertEqual g:pre_success, 1 AssertEqual g:pre_success, 1
AssertEqual g:post_success, 1 AssertEqual g:post_success, 1

View file

@ -18,6 +18,9 @@ Before:
endfunction endfunction
call ale#engine#InitBufferInfo(bufnr('')) call ale#engine#InitBufferInfo(bufnr(''))
" Call this function first, so we can be sure the module is loaded before we
" check if it exists.
call ale#lsp_linter#ClearLSPData()
call ale#linter#Define('testft', { call ale#linter#Define('testft', {
\ 'name': 'lsplinter', \ 'name': 'lsplinter',
@ -68,7 +71,10 @@ Execute(ALEStopAllLSPs should clear the loclist):
\ 'linter_name': 'otherlinter', \ 'linter_name': 'otherlinter',
\ }, \ },
\] \]
let g:ale_buffer_info[bufnr('')].active_linter_list = ['lsplinter', 'otherlinter'] let g:ale_buffer_info[bufnr('')].active_linter_list = [
\ {'name': 'lsplinter'},
\ {'name': 'otherlinter'},
\]
ALEStopAllLSPs ALEStopAllLSPs
@ -87,4 +93,6 @@ Execute(ALEStopAllLSPs should clear the loclist):
\] \]
" The LSP linter should be removed from the active linter list. " The LSP linter should be removed from the active linter list.
AssertEqual g:ale_buffer_info[bufnr('')].active_linter_list, ['otherlinter'] AssertEqual
\ ['otherlinter'],
\ map(copy(g:ale_buffer_info[bufnr('')].active_linter_list), 'v:val.name')

View file

@ -63,5 +63,6 @@ After:
Execute(The signs should be updated after linting is done): Execute(The signs should be updated after linting is done):
ALELint ALELint
call ale#test#FlushJobs()
AssertEqual [['1', 'ALEWarningSign'], ['2', 'ALEErrorSign']], CollectSigns() AssertEqual [['1', 'ALEWarningSign'], ['2', 'ALEErrorSign']], CollectSigns()

View file

@ -134,6 +134,7 @@ Given testft(A file with warnings/errors):
Execute(The current signs should be set for running a job): Execute(The current signs should be set for running a job):
ALELint ALELint
call ale#test#FlushJobs()
AssertEqual AssertEqual
\ [ \ [

View file

@ -66,7 +66,7 @@ Execute(Linters should run with the default options):
" where tests fail randomly. " where tests fail randomly.
for g:i in range(has('nvim-0.3') || has('win32') ? 5 : 1) for g:i in range(has('nvim-0.3') || has('win32') ? 5 : 1)
call ale#Queue(0, '') call ale#Queue(0, '')
call ale#engine#WaitForJobs(2000) call ale#test#WaitForJobs(2000)
let g:results = ale#test#GetLoclistWithoutModule() let g:results = ale#test#GetLoclistWithoutModule()
@ -110,7 +110,7 @@ Execute(Linters should run in PowerShell too):
\}) \})
call ale#Queue(0, '') call ale#Queue(0, '')
call ale#engine#WaitForJobs(4000) call ale#test#WaitForJobs(4000)
AssertEqual [ AssertEqual [
\ { \ {
@ -140,7 +140,7 @@ Execute(Linters should run in PowerShell too):
Execute(Previous errors should be removed when linters change): Execute(Previous errors should be removed when linters change):
call ale#Queue(0, '') call ale#Queue(0, '')
call ale#engine#WaitForJobs(2000) call ale#test#WaitForJobs(2000)
call ale#linter#Reset() call ale#linter#Reset()
@ -167,7 +167,7 @@ Execute(Previous errors should be removed when linters change):
" where tests fail randomly. " where tests fail randomly.
for g:i in range(has('nvim-0.3') || has('win32') ? 5 : 1) for g:i in range(has('nvim-0.3') || has('win32') ? 5 : 1)
call ale#Queue(0, '') call ale#Queue(0, '')
call ale#engine#WaitForJobs(2000) call ale#test#WaitForJobs(2000)
let g:results = ale#test#GetLoclistWithoutModule() let g:results = ale#test#GetLoclistWithoutModule()

View file

@ -1,7 +1,9 @@
Before: Before:
Save g:ale_buffer_info Save g:ale_buffer_info
Save g:ale_enabled
let g:ale_buffer_info = {} let g:ale_buffer_info = {}
let g:ale_enabled = 1
let g:expected_loclist = [{ let g:expected_loclist = [{
\ 'bufnr': bufnr('%'), \ 'bufnr': bufnr('%'),
@ -58,7 +60,7 @@ Execute(ALELint should run the linters):
" Try to run the linter a few times, as it fails randomly in NeoVim. " Try to run the linter a few times, as it fails randomly in NeoVim.
for b:i in range(5) for b:i in range(5)
ALELint ALELint
call ale#engine#WaitForJobs(2000) call ale#test#WaitForJobs(2000)
if !has('nvim') if !has('nvim')
" Sleep so the delayed list function can run. " Sleep so the delayed list function can run.

View file

@ -10,6 +10,7 @@ Before:
let g:ale_set_signs = 1 let g:ale_set_signs = 1
let g:ale_set_lists_synchronously = 1 let g:ale_set_lists_synchronously = 1
let g:ale_run_synchronously = 1 let g:ale_run_synchronously = 1
unlet! g:ale_run_synchronously_callbacks
let g:ale_pattern_options = {} let g:ale_pattern_options = {}
let g:ale_pattern_options_enabled = 1 let g:ale_pattern_options_enabled = 1
let g:ale_set_balloons = let g:ale_set_balloons =
@ -85,6 +86,7 @@ Before:
After: After:
Restore Restore
unlet! g:ale_run_synchronously_callbacks
unlet! g:expected_loclist unlet! g:expected_loclist
unlet! g:expected_groups unlet! g:expected_groups
unlet! b:ale_enabled unlet! b:ale_enabled
@ -113,6 +115,7 @@ Execute(ALEToggle should reset everything and then run again):
AssertEqual 'foobar', &filetype AssertEqual 'foobar', &filetype
ALELint ALELint
call ale#test#FlushJobs()
" First check that everything is there... " First check that everything is there...
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
@ -135,6 +138,7 @@ Execute(ALEToggle should reset everything and then run again):
" Toggle ALE on, everything should be set up and run again. " Toggle ALE on, everything should be set up and run again.
ALEToggle ALEToggle
call ale#test#FlushJobs()
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%'))
@ -157,6 +161,7 @@ Execute(ALEToggle should skip filename keys and preserve them):
\} \}
ALELint ALELint
call ale#test#FlushJobs()
" Now Toggle ALE off. " Now Toggle ALE off.
ALEToggle ALEToggle
@ -174,6 +179,7 @@ Execute(ALEToggle should skip filename keys and preserve them):
" Toggle ALE on again. " Toggle ALE on again.
ALEToggle ALEToggle
call ale#test#FlushJobs()
AssertEqual AssertEqual
\ { \ {
@ -188,15 +194,18 @@ Execute(ALEToggle should skip filename keys and preserve them):
Execute(ALEDisable should reset everything and stay disabled): Execute(ALEDisable should reset everything and stay disabled):
ALELint ALELint
call ale#test#FlushJobs()
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
ALEDisable ALEDisable
call ale#test#FlushJobs()
AssertEqual [], ale#test#GetLoclistWithoutModule() AssertEqual [], ale#test#GetLoclistWithoutModule()
AssertEqual 0, g:ale_enabled AssertEqual 0, g:ale_enabled
ALEDisable ALEDisable
call ale#test#FlushJobs()
AssertEqual [], ale#test#GetLoclistWithoutModule() AssertEqual [], ale#test#GetLoclistWithoutModule()
AssertEqual 0, g:ale_enabled AssertEqual 0, g:ale_enabled
@ -205,6 +214,7 @@ Execute(ALEEnable should enable ALE and lint again):
let g:ale_enabled = 0 let g:ale_enabled = 0
ALEEnable ALEEnable
call ale#test#FlushJobs()
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
AssertEqual 1, g:ale_enabled AssertEqual 1, g:ale_enabled
@ -213,6 +223,7 @@ Execute(ALEReset should reset everything for a buffer):
AssertEqual 'foobar', &filetype AssertEqual 'foobar', &filetype
ALELint ALELint
call ale#test#FlushJobs()
" First check that everything is there... " First check that everything is there...
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
@ -224,6 +235,7 @@ Execute(ALEReset should reset everything for a buffer):
" Now Toggle ALE off. " Now Toggle ALE off.
ALEReset ALEReset
call ale#test#FlushJobs()
" Everything should be cleared. " Everything should be cleared.
Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed' Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed'
@ -237,6 +249,7 @@ Execute(ALEToggleBuffer should reset everything and then run again):
AssertEqual 'foobar', &filetype AssertEqual 'foobar', &filetype
ALELint ALELint
call ale#test#FlushJobs()
" First check that everything is there... " First check that everything is there...
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
@ -257,6 +270,7 @@ Execute(ALEToggleBuffer should reset everything and then run again):
" Toggle ALE on, everything should be set up and run again. " Toggle ALE on, everything should be set up and run again.
ALEToggleBuffer ALEToggleBuffer
call ale#test#FlushJobs()
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%'))
@ -268,10 +282,12 @@ Execute(ALEToggleBuffer should reset everything and then run again):
Execute(ALEDisableBuffer should reset everything and stay disabled): Execute(ALEDisableBuffer should reset everything and stay disabled):
ALELint ALELint
call ale#test#FlushJobs()
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
ALEDisableBuffer ALEDisableBuffer
call ale#test#FlushJobs()
AssertEqual [], ale#test#GetLoclistWithoutModule() AssertEqual [], ale#test#GetLoclistWithoutModule()
AssertEqual 0, b:ale_enabled AssertEqual 0, b:ale_enabled
@ -280,6 +296,7 @@ Execute(ALEEnableBuffer should enable ALE and lint again):
let b:ale_enabled = 0 let b:ale_enabled = 0
ALEEnableBuffer ALEEnableBuffer
call ale#test#FlushJobs()
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
AssertEqual 1, b:ale_enabled AssertEqual 1, b:ale_enabled
@ -303,6 +320,7 @@ Execute(ALEResetBuffer should reset everything for a buffer):
AssertEqual 'foobar', &filetype AssertEqual 'foobar', &filetype
ALELint ALELint
call ale#test#FlushJobs()
" First check that everything is there... " First check that everything is there...
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
@ -314,6 +332,7 @@ Execute(ALEResetBuffer should reset everything for a buffer):
" Now Toggle ALE off. " Now Toggle ALE off.
ALEResetBuffer ALEResetBuffer
call ale#test#FlushJobs()
" Everything should be cleared. " Everything should be cleared.
Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed' Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed'

View file

@ -1,6 +1,7 @@
Before: Before:
Save &shell, g:ale_run_synchronously Save &shell, g:ale_run_synchronously
let g:ale_run_synchronously = 1 let g:ale_run_synchronously = 1
unlet! g:ale_run_synchronously_callbacks
if !has('win32') if !has('win32')
set shell=/bin/sh set shell=/bin/sh
@ -47,6 +48,7 @@ Before:
After: After:
Restore Restore
unlet! g:ale_run_synchronously_callbacks
unlet! g:first_echo_called unlet! g:first_echo_called
unlet! g:second_echo_called unlet! g:second_echo_called
unlet! g:final_callback_called unlet! g:final_callback_called
@ -63,6 +65,7 @@ Given foobar (Some imaginary filetype):
Execute(Check the results of running the chain): Execute(Check the results of running the chain):
AssertEqual 'foobar', &filetype AssertEqual 'foobar', &filetype
call ale#Queue(0) call ale#Queue(0)
call ale#test#FlushJobs()
Assert g:first_echo_called, 'The first chain item was not called' Assert g:first_echo_called, 'The first chain item was not called'
Assert g:second_echo_called, 'The second chain item was not called' Assert g:second_echo_called, 'The second chain item was not called'

View file

@ -3,7 +3,6 @@ Before:
Save g:ale_buffer_info Save g:ale_buffer_info
Save g:ale_echo_cursor Save g:ale_echo_cursor
Save g:ale_run_synchronously Save g:ale_run_synchronously
Save g:ale_run_synchronously
Save g:ale_set_highlights Save g:ale_set_highlights
Save g:ale_set_loclist Save g:ale_set_loclist
Save g:ale_set_quickfix Save g:ale_set_quickfix
@ -17,6 +16,7 @@ Before:
let g:ale_echo_cursor = 0 let g:ale_echo_cursor = 0
let g:ale_run_synchronously = 1 let g:ale_run_synchronously = 1
unlet! g:ale_run_synchronously_callbacks
call setloclist(0, []) call setloclist(0, [])
noautocmd let &filetype = 'foobar' noautocmd let &filetype = 'foobar'
@ -44,6 +44,8 @@ Before:
After: After:
Restore Restore
unlet! g:ale_run_synchronously_callbacks
delfunction TestCallback delfunction TestCallback
call ale#linter#Reset() call ale#linter#Reset()
@ -51,6 +53,7 @@ After:
Execute(Error should be removed when the filetype changes to something else we cannot check): Execute(Error should be removed when the filetype changes to something else we cannot check):
call ale#Queue(0) call ale#Queue(0)
call ale#test#FlushJobs()
sleep 1ms sleep 1ms
AssertEqual 1, len(ale#test#GetLoclistWithoutModule()) AssertEqual 1, len(ale#test#GetLoclistWithoutModule())
@ -58,6 +61,7 @@ Execute(Error should be removed when the filetype changes to something else we c
noautocmd let &filetype = 'foobar2' noautocmd let &filetype = 'foobar2'
call ale#Queue(0) call ale#Queue(0)
call ale#test#FlushJobs()
sleep 1ms sleep 1ms
" We should get some items from the second filetype. " We should get some items from the second filetype.
@ -66,6 +70,7 @@ Execute(Error should be removed when the filetype changes to something else we c
noautocmd let &filetype = 'xxx' noautocmd let &filetype = 'xxx'
call ale#Queue(0) call ale#Queue(0)
call ale#test#FlushJobs()
sleep 1ms sleep 1ms
AssertEqual 0, len(ale#test#GetLoclistWithoutModule()) AssertEqual 0, len(ale#test#GetLoclistWithoutModule())

View file

@ -13,6 +13,7 @@ Before:
let g:ale_echo_cursor = 0 let g:ale_echo_cursor = 0
let g:ale_enabled = 1 let g:ale_enabled = 1
let g:ale_run_synchronously = 1 let g:ale_run_synchronously = 1
unlet! g:ale_run_synchronously_callbacks
let g:ale_set_highlights = 0 let g:ale_set_highlights = 0
let g:ale_set_loclist = 0 let g:ale_set_loclist = 0
let g:ale_set_quickfix = 0 let g:ale_set_quickfix = 0
@ -41,6 +42,7 @@ Before:
After: After:
Restore Restore
unlet! g:ale_run_synchronously_callbacks
unlet! g:output unlet! g:output
delfunction TestCallback delfunction TestCallback
@ -56,5 +58,6 @@ Execute(ALE should be able to read the %t file):
AssertEqual 'foobar', &filetype AssertEqual 'foobar', &filetype
ALELint ALELint
call ale#test#FlushJobs()
AssertEqual ['foo', 'bar', 'baz'], g:output AssertEqual ['foo', 'bar', 'baz'], g:output

View file

@ -9,6 +9,7 @@ Before:
Save g:ale_set_signs Save g:ale_set_signs
let g:ale_run_synchronously = 1 let g:ale_run_synchronously = 1
unlet! g:ale_run_synchronously_callbacks
let g:ale_set_highlights = 1 let g:ale_set_highlights = 1
let g:ale_set_signs = 1 let g:ale_set_signs = 1
let g:ale_buffer_info = {} let g:ale_buffer_info = {}
@ -64,6 +65,7 @@ Before:
After: After:
Restore Restore
unlet! g:ale_run_synchronously_callbacks
unlet! g:items unlet! g:items
unlet! b:ale_enabled unlet! b:ale_enabled
@ -81,6 +83,7 @@ Given testft(A Javscript file with warnings/errors):
Execute(Highlights should be set when a linter runs): Execute(Highlights should be set when a linter runs):
ALELint ALELint
call ale#test#FlushJobs()
AssertEqual AssertEqual
\ [ \ [

View file

@ -2,6 +2,10 @@ Before:
Save g:ale_max_buffer_history_size Save g:ale_max_buffer_history_size
Save g:ale_history_log_output Save g:ale_history_log_output
Save g:ale_run_synchronously Save g:ale_run_synchronously
Save g:ale_enabled
let g:ale_enabled = 1
let g:ale_run_synchronously = 1
unlet! b:ale_fixers unlet! b:ale_fixers
unlet! b:ale_enabled unlet! b:ale_enabled
@ -68,13 +72,9 @@ Given foobar (Some imaginary filetype):
Execute(History should be set when commands are run): Execute(History should be set when commands are run):
AssertEqual 'foobar', &filetype AssertEqual 'foobar', &filetype
let g:expected_results = ['command', 'exit_code', 'job_id', 'status']
" Retry this test until it works. This one can randomly fail.
for g:i in range(has('nvim-0.3') || has('win32') ? 5 : 1)
let b:ale_history = [] let b:ale_history = []
call ale#Queue(0) ALELint
call ale#engine#WaitForJobs(2000) call ale#test#FlushJobs()
let g:history = filter( let g:history = filter(
\ copy(ale#history#Get(bufnr(''))), \ copy(ale#history#Get(bufnr(''))),
@ -82,13 +82,9 @@ Execute(History should be set when commands are run):
\) \)
AssertEqual 1, len(g:history) AssertEqual 1, len(g:history)
AssertEqual
if sort(keys(g:history[0])) == g:expected_results \ ['command', 'exit_code', 'job_id', 'status'],
break \ sort(keys(g:history[0]))
endif
endfor
AssertEqual g:expected_results, sort(keys(g:history[0]))
if has('win32') if has('win32')
AssertEqual 'cmd /s/c "echo command history test"', g:history[0].command AssertEqual 'cmd /s/c "echo command history test"', g:history[0].command
@ -106,8 +102,8 @@ Execute(History should be not set when disabled):
let g:ale_history_enabled = 0 let g:ale_history_enabled = 0
call ale#Queue(0) ALELint
call ale#engine#WaitForJobs(2000) call ale#test#FlushJobs()
AssertEqual [], ale#history#Get(bufnr('')) AssertEqual [], ale#history#Get(bufnr(''))
@ -115,24 +111,21 @@ Execute(History should include command output if logging is enabled):
AssertEqual 'foobar', &filetype AssertEqual 'foobar', &filetype
let g:ale_history_log_output = 1 let g:ale_history_log_output = 1
let g:expected_results = ['command history test']
" Retry this test until it works. This one can randomly fail. " Retry this test until it works. This one can randomly fail.
for g:i in range(has('nvim-0.3') || has('win32') ? 5 : 1)
let b:ale_history = [] let b:ale_history = []
call ale#Queue(0) ALELint
call ale#engine#WaitForJobs(2000) call ale#test#FlushJobs()
let g:history = ale#history#Get(bufnr('')) let g:history = ale#history#Get(bufnr(''))
AssertEqual 1, len(g:history) AssertEqual 1, len(g:history)
AssertEqual
if get(g:history[0], 'output', []) == g:expected_results \ ['command history test'],
break \ map(
endif \ copy(get(g:history[0], 'output', [])),
endfor \ 'substitute(v:val, ''[\r ]*$'', '''', ''g'')'
\ )
AssertEqual g:expected_results, get(g:history[0], 'output', [])
Execute(History items should be popped after going over the max): Execute(History items should be popped after going over the max):
let b:ale_history = map(range(20), '{''status'': ''started'', ''job_id'': v:val, ''command'': ''foobar''}') let b:ale_history = map(range(20), '{''status'': ''started'', ''job_id'': v:val, ''command'': ''foobar''}')
@ -169,10 +162,13 @@ Execute(The history should be updated when fixers are run):
let b:ale_fixers = {'foobar': ['TestFixer']} let b:ale_fixers = {'foobar': ['TestFixer']}
let b:ale_enabled = 0 let b:ale_enabled = 0
let g:ale_run_synchronously = 1
ALEFix ALEFix
AssertEqual ['started'], map(copy(b:ale_history), 'v:val.status')
call ale#test#FlushJobs()
AssertEqual ['finished'], map(copy(b:ale_history), 'v:val.status') AssertEqual ['finished'], map(copy(b:ale_history), 'v:val.status')
if has('win32') if has('win32')

View file

@ -8,6 +8,7 @@ Before:
let g:ale_buffer_info = {} let g:ale_buffer_info = {}
let g:ale_run_synchronously = 1 let g:ale_run_synchronously = 1
unlet! g:ale_run_synchronously_callbacks
let g:ale_set_lists_synchronously = 1 let g:ale_set_lists_synchronously = 1
let b:ale_save_event_fired = 0 let b:ale_save_event_fired = 0
@ -89,6 +90,7 @@ After:
Restore Restore
unlet! g:ale_run_synchronously_callbacks
unlet! b:ale_save_event_fired unlet! b:ale_save_event_fired
unlet! b:ale_enabled unlet! b:ale_enabled
unlet g:buffer_result unlet g:buffer_result
@ -111,6 +113,7 @@ Given foobar (Some imaginary filetype):
Execute(Running linters without 'lint_file' should run only buffer linters): Execute(Running linters without 'lint_file' should run only buffer linters):
call ale#Queue(0) call ale#Queue(0)
call ale#test#FlushJobs()
AssertEqual [ AssertEqual [
\ { \ {
@ -131,6 +134,7 @@ Execute(Running linters with 'lint_file' should run all linters):
Assert filereadable(expand('%:p')), 'The file was not readable' Assert filereadable(expand('%:p')), 'The file was not readable'
call ale#Queue(0, 'lint_file') call ale#Queue(0, 'lint_file')
call ale#test#FlushJobs()
AssertEqual [ AssertEqual [
\ { \ {
@ -163,6 +167,7 @@ Execute(Linter errors from files should be kept):
Assert filereadable(expand('%:p')), 'The file was not readable' Assert filereadable(expand('%:p')), 'The file was not readable'
call ale#Queue(0, 'lint_file') call ale#Queue(0, 'lint_file')
call ale#test#FlushJobs()
" Change the results for the buffer callback. " Change the results for the buffer callback.
let g:buffer_result = [ let g:buffer_result = [
@ -175,6 +180,7 @@ Execute(Linter errors from files should be kept):
\] \]
call ale#Queue(0) call ale#Queue(0)
call ale#test#FlushJobs()
AssertEqual [ AssertEqual [
\ { \ {
@ -202,6 +208,7 @@ Execute(Linter errors from files should be kept when no other linters are run):
Assert filereadable(expand('%:p')), 'The file was not readable' Assert filereadable(expand('%:p')), 'The file was not readable'
call ale#Queue(0, 'lint_file') call ale#Queue(0, 'lint_file')
call ale#test#FlushJobs()
AssertEqual [ AssertEqual [
\ { \ {
@ -240,11 +247,13 @@ Execute(The Save event should respect the buffer number):
Assert filereadable(expand('%:p')), 'The file was not readable' Assert filereadable(expand('%:p')), 'The file was not readable'
call ale#events#SaveEvent(bufnr('') + 1) call ale#events#SaveEvent(bufnr('') + 1)
call ale#test#FlushJobs()
" We shouldn't get any prblems yet. " We shouldn't get any prblems yet.
AssertEqual [], GetSimplerLoclist() AssertEqual [], GetSimplerLoclist()
call ale#events#SaveEvent(bufnr('')) call ale#events#SaveEvent(bufnr(''))
call ale#test#FlushJobs()
" We should get them now we used the right buffer number. " We should get them now we used the right buffer number.
AssertEqual [ AssertEqual [
@ -268,6 +277,7 @@ Execute(The Save event should set b:ale_save_event_fired to 1):
call ale#linter#Reset() call ale#linter#Reset()
call ale#events#SaveEvent(bufnr('')) call ale#events#SaveEvent(bufnr(''))
call ale#test#FlushJobs()
" This flag needs to be set so windows can be opened, etc. " This flag needs to be set so windows can be opened, etc.
AssertEqual 1, b:ale_save_event_fired AssertEqual 1, b:ale_save_event_fired
@ -276,6 +286,7 @@ Execute(b:ale_save_event_fired should be set to 0 when results are set):
let b:ale_save_event_fired = 1 let b:ale_save_event_fired = 1
call ale#engine#SetResults(bufnr(''), []) call ale#engine#SetResults(bufnr(''), [])
call ale#test#FlushJobs()
AssertEqual 0, b:ale_save_event_fired AssertEqual 0, b:ale_save_event_fired
@ -289,15 +300,18 @@ Execute(lint_file linters should stay running after checking without them):
" The lint_file linter should still be running. " The lint_file linter should still be running.
AssertEqual AssertEqual
\ ['lint_file_linter', 'buffer_linter'], \ ['lint_file_linter', 'buffer_linter'],
\ g:ale_buffer_info[bufnr('')].active_linter_list \ map(copy(g:ale_buffer_info[bufnr('')].active_linter_list), 'v:val.name')
" We should have 1 job for each linter. " We should have 1 job for each linter.
AssertEqual 2, len(g:ale_buffer_info[bufnr('')].job_list) AssertEqual
\ 2,
\ len(keys(get(get(ale#command#GetData(), bufnr(''), {}), 'jobs', {})))
call ale#engine#WaitForJobs(2000) call ale#test#WaitForJobs(2000)
Execute(The save event should not lint the buffer when ALE is disabled): Execute(The save event should not lint the buffer when ALE is disabled):
let g:ale_enabled = 0 let g:ale_enabled = 0
call ale#events#SaveEvent(bufnr('')) call ale#events#SaveEvent(bufnr(''))
call ale#test#FlushJobs()
AssertEqual [], GetSimplerLoclist() AssertEqual [], GetSimplerLoclist()
AssertEqual 0, b:ale_save_event_fired AssertEqual 0, b:ale_save_event_fired

View file

@ -50,6 +50,7 @@ Execute(The file changed event function should set b:ale_file_changed):
Execute(The file changed event function should lint the current buffer when it has changed): Execute(The file changed event function should lint the current buffer when it has changed):
set filetype=foobar set filetype=foobar
call ale#events#FileChangedEvent(bufnr('')) call ale#events#FileChangedEvent(bufnr(''))
call ale#test#FlushJobs()
AssertEqual [{ AssertEqual [{
\ 'bufnr': bufnr(''), \ 'bufnr': bufnr(''),
@ -68,6 +69,7 @@ Execute(The buffer should be checked after entering it after the file has change
set filetype=foobar set filetype=foobar
call ale#events#ReadOrEnterEvent(bufnr('')) call ale#events#ReadOrEnterEvent(bufnr(''))
call ale#test#FlushJobs()
AssertEqual [{ AssertEqual [{
\ 'bufnr': bufnr(''), \ 'bufnr': bufnr(''),

View file

@ -11,6 +11,6 @@ Given unite (A Unite.vim file):
Execute(Running ALE on a blacklisted file shouldn't change anything): Execute(Running ALE on a blacklisted file shouldn't change anything):
call ale#Queue(0) call ale#Queue(0)
call ale#engine#WaitForJobs(2000) call ale#test#WaitForJobs(2000)
AssertEqual {}, g:ale_buffer_info AssertEqual {}, g:ale_buffer_info

View file

@ -64,6 +64,7 @@ Given foobar (Some JavaScript with problems):
Execute(The loclist should be updated after linting is done): Execute(The loclist should be updated after linting is done):
ALELint ALELint
call ale#test#FlushJobs()
AssertEqual AssertEqual
\ [ \ [

View file

@ -58,6 +58,7 @@ Execute(No linting should be done on :wq or :x):
" First try just the SaveEvent, to be sure that we set errors in the test. " First try just the SaveEvent, to be sure that we set errors in the test.
call ale#events#SaveEvent(bufnr('')) call ale#events#SaveEvent(bufnr(''))
call ale#test#FlushJobs()
AssertEqual 1, len(ale#test#GetLoclistWithoutModule()) AssertEqual 1, len(ale#test#GetLoclistWithoutModule())
@ -65,6 +66,7 @@ Execute(No linting should be done on :wq or :x):
call setloclist(0, []) call setloclist(0, [])
call ale#events#QuitEvent(bufnr('')) call ale#events#QuitEvent(bufnr(''))
call ale#events#SaveEvent(bufnr('')) call ale#events#SaveEvent(bufnr(''))
call ale#test#FlushJobs()
AssertEqual [], ale#test#GetLoclistWithoutModule() AssertEqual [], ale#test#GetLoclistWithoutModule()
@ -73,11 +75,13 @@ Execute(No linting should be for :w after :q fails):
let g:ale_fix_on_save = 0 let g:ale_fix_on_save = 0
call ale#events#QuitEvent(bufnr('')) call ale#events#QuitEvent(bufnr(''))
call ale#test#FlushJobs()
" Simulate 2 seconds passing. " Simulate 2 seconds passing.
let b:ale_quitting -= 1000 let b:ale_quitting -= 1000
call ale#events#SaveEvent(bufnr('')) call ale#events#SaveEvent(bufnr(''))
call ale#test#FlushJobs()
AssertEqual 1, len(ale#test#GetLoclistWithoutModule()) AssertEqual 1, len(ale#test#GetLoclistWithoutModule())
@ -86,6 +90,7 @@ Execute(No linting should be done on :wq or :x after fixing files):
let g:ale_fix_on_save = 1 let g:ale_fix_on_save = 1
call ale#events#SaveEvent(bufnr('')) call ale#events#SaveEvent(bufnr(''))
call ale#test#FlushJobs()
AssertEqual 1, len(ale#test#GetLoclistWithoutModule()) AssertEqual 1, len(ale#test#GetLoclistWithoutModule())
@ -93,6 +98,7 @@ Execute(No linting should be done on :wq or :x after fixing files):
call setloclist(0, []) call setloclist(0, [])
call ale#events#QuitEvent(bufnr('')) call ale#events#QuitEvent(bufnr(''))
call ale#events#SaveEvent(bufnr('')) call ale#events#SaveEvent(bufnr(''))
call ale#test#FlushJobs()
AssertEqual [], ale#test#GetLoclistWithoutModule() AssertEqual [], ale#test#GetLoclistWithoutModule()
@ -101,10 +107,12 @@ Execute(Linting should be done after :q fails and fixing files):
let g:ale_fix_on_save = 1 let g:ale_fix_on_save = 1
call ale#events#QuitEvent(bufnr('')) call ale#events#QuitEvent(bufnr(''))
call ale#test#FlushJobs()
" Simulate 2 seconds passing. " Simulate 2 seconds passing.
let b:ale_quitting -= 1000 let b:ale_quitting -= 1000
call ale#events#SaveEvent(bufnr('')) call ale#events#SaveEvent(bufnr(''))
call ale#test#FlushJobs()
AssertEqual 1, len(ale#test#GetLoclistWithoutModule()) AssertEqual 1, len(ale#test#GetLoclistWithoutModule())

View file

@ -70,7 +70,7 @@ Execute(ALE should delete managed files/directories appropriately after linting)
AssertEqual 'foobar', &filetype AssertEqual 'foobar', &filetype
call ale#Queue(0) call ale#Queue(0)
call ale#engine#WaitForJobs(2000) call ale#test#FlushJobs()
Assert !filereadable(g:filename), 'The temporary file was not deleted' Assert !filereadable(g:filename), 'The temporary file was not deleted'
Assert !isdirectory(g:directory), 'The temporary directory was not deleted' Assert !isdirectory(g:directory), 'The temporary directory was not deleted'
@ -82,7 +82,7 @@ Execute(ALE should delete managed files even if no command is run):
let g:command = '' let g:command = ''
call ale#Queue(0) call ale#Queue(0)
call ale#engine#WaitForJobs(2000) call ale#test#WaitForJobs(2000)
Assert !filereadable(g:filename), 'The temporary file was not deleted' Assert !filereadable(g:filename), 'The temporary file was not deleted'
Assert !isdirectory(g:directory), 'The temporary directory was not deleted' Assert !isdirectory(g:directory), 'The temporary directory was not deleted'
@ -119,24 +119,26 @@ Execute(ALE should create and delete directories for ale#command#CreateDirectory
Assert !isdirectory(b:dir), 'The directory was not deleted' Assert !isdirectory(b:dir), 'The directory was not deleted'
Assert !isdirectory(b:dir2), 'The second directory was not deleted' Assert !isdirectory(b:dir2), 'The second directory was not deleted'
Execute(ale#command#ManageFile should add the file even if the buffer info hasn't be set yet): Execute(ale#command#ManageFile should add the file even if the buffer info hasn't been set yet):
call ale#command#ManageFile(bufnr(''), '/foo/bar') call ale#command#ManageFile(bufnr(''), '/foo/bar')
AssertEqual AssertEqual
\ { \ {
\ bufnr(''): { \ bufnr(''): {
\ 'jobs': {},
\ 'file_list': ['/foo/bar'], \ 'file_list': ['/foo/bar'],
\ 'directory_list': [], \ 'directory_list': [],
\ }, \ },
\ }, \ },
\ ale#command#GetData() \ ale#command#GetData()
Execute(ale#command#ManageDirectory should add the directory even if the buffer info hasn't be set yet): Execute(ale#command#ManageDirectory should add the directory even if the buffer info hasn't been set yet):
call ale#command#ManageDirectory(bufnr(''), '/foo/bar') call ale#command#ManageDirectory(bufnr(''), '/foo/bar')
AssertEqual AssertEqual
\ { \ {
\ bufnr(''): { \ bufnr(''): {
\ 'jobs': {},
\ 'file_list': [], \ 'file_list': [],
\ 'directory_list': ['/foo/bar'], \ 'directory_list': ['/foo/bar'],
\ }, \ },