diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim index a14638c7..b8674606 100644 --- a/autoload/ale/fix.vim +++ b/autoload/ale/fix.vim @@ -131,35 +131,55 @@ function! s:HandleExit(job_info, buffer, job_output, data) abort \}) endfunction -function! s:RunJob(options) abort - let l:buffer = a:options.buffer - let l:command = a:options.command - let l:input = a:options.input - let l:ChainWith = a:options.chain_with - let l:read_buffer = a:options.read_buffer +function! s:RunJob(result, options) abort + if ale#command#IsDeferred(a:result) + let a:result.result_callback = {x -> s:RunJob(x, a:options)} - if empty(l:command) - " If there's nothing further to chain the command with, stop here. - if l:ChainWith is v:null - return v:false + return + endif + + let l:buffer = a:options.buffer + let l:input = a:options.input + + if a:result is 0 || type(a:result) is v:t_list + if type(a:result) is v:t_list + let l:input = a:result endif - " If there's another chained callback to run, then run that. call s:RunFixer({ \ 'buffer': l:buffer, \ 'input': l:input, - \ 'callback_index': a:options.callback_index, + \ 'callback_index': a:options.callback_index + 1, + \ 'callback_list': a:options.callback_list, + \}) + + return + endif + + let l:command = get(a:result, 'command', '') + let l:ChainWith = get(a:result, 'chain_with', v:null) + + if empty(l:command) + " If the command is empty, skip to the next item, or call the + " chain_with function. + call s:RunFixer({ + \ 'buffer': l:buffer, + \ 'input': l:input, + \ 'callback_index': a:options.callback_index + (l:ChainWith is v:null), \ 'callback_list': a:options.callback_list, \ 'chain_callback': l:ChainWith, \ 'output': [], \}) - return v:true + return endif - let l:output_stream = a:options.output_stream + let l:read_temporary_file = get(a:result, 'read_temporary_file', 0) + " Default to piping the buffer for the last fixer in the chain. + let l:read_buffer = get(a:result, 'read_buffer', l:ChainWith is v:null) + let l:output_stream = get(a:result, 'output_stream', 'stdout') - if a:options.read_temporary_file + if l:read_temporary_file let l:output_stream = 'none' endif @@ -168,10 +188,10 @@ function! s:RunJob(options) abort \ 'chain_with': l:ChainWith, \ 'callback_index': a:options.callback_index, \ 'callback_list': a:options.callback_list, - \ 'process_with': a:options.process_with, - \ 'read_temporary_file': a:options.read_temporary_file, + \ 'process_with': get(a:result, 'process_with', v:null), + \ 'read_temporary_file': l:read_temporary_file, \}]) - let l:result = ale#command#Run(l:buffer, l:command, l:Callback, { + let l:run_result = ale#command#Run(l:buffer, l:command, l:Callback, { \ 'output_stream': l:output_stream, \ 'executable': '', \ 'read_buffer': l:read_buffer, @@ -179,70 +199,54 @@ function! s:RunJob(options) abort \ 'log_output': 0, \}) - return !empty(l:result) + if empty(l:run_result) + call s:RunFixer({ + \ 'buffer': l:buffer, + \ 'input': l:input, + \ 'callback_index': a:options.callback_index + 1, + \ 'callback_list': a:options.callback_list, + \}) + endif endfunction function! s:RunFixer(options) abort let l:buffer = a:options.buffer let l:input = a:options.input let l:index = a:options.callback_index + + if len(a:options.callback_list) <= l:index + call ale#fix#ApplyFixes(l:buffer, l:input) + + return + endif + let l:ChainCallback = get(a:options, 'chain_callback', v:null) + let l:Function = l:ChainCallback isnot v:null + \ ? ale#util#GetFunction(l:ChainCallback) + \ : a:options.callback_list[l:index] + " Record new jobs started as fixer jobs. call setbufvar(l:buffer, 'ale_job_type', 'fixer') - while len(a:options.callback_list) > l:index - let l:Function = l:ChainCallback isnot v:null - \ ? ale#util#GetFunction(l:ChainCallback) - \ : a:options.callback_list[l:index] + if l:ChainCallback isnot v:null + " Chained commands accept (buffer, output, [input]) + let l:result = ale#util#FunctionArgCount(l:Function) == 2 + \ ? call(l:Function, [l:buffer, a:options.output]) + \ : call(l:Function, [l:buffer, a:options.output, copy(l:input)]) + else + " Regular fixer commands accept (buffer, [input]) + let l:result = ale#util#FunctionArgCount(l:Function) == 1 + \ ? call(l:Function, [l:buffer]) + \ : call(l:Function, [l:buffer, copy(l:input)]) + endif - if l:ChainCallback isnot v:null - " Chained commands accept (buffer, output, [input]) - let l:result = ale#util#FunctionArgCount(l:Function) == 2 - \ ? call(l:Function, [l:buffer, a:options.output]) - \ : call(l:Function, [l:buffer, a:options.output, copy(l:input)]) - else - " Chained commands accept (buffer, [input]) - let l:result = ale#util#FunctionArgCount(l:Function) == 1 - \ ? call(l:Function, [l:buffer]) - \ : call(l:Function, [l:buffer, copy(l:input)]) - endif - - if type(l:result) is v:t_number && l:result == 0 - " When `0` is returned, skip this item. - let l:index += 1 - elseif type(l:result) is v:t_list - let l:input = l:result - let l:index += 1 - else - let l:ChainWith = get(l:result, 'chain_with', v:null) - " Default to piping the buffer for the last fixer in the chain. - let l:read_buffer = get(l:result, 'read_buffer', l:ChainWith is v:null) - - let l:job_ran = s:RunJob({ - \ 'buffer': l:buffer, - \ 'command': l:result.command, - \ 'input': l:input, - \ 'output_stream': get(l:result, 'output_stream', 'stdout'), - \ 'read_temporary_file': get(l:result, 'read_temporary_file', 0), - \ 'read_buffer': l:read_buffer, - \ 'chain_with': l:ChainWith, - \ 'callback_list': a:options.callback_list, - \ 'callback_index': l:index, - \ 'process_with': get(l:result, 'process_with', v:null), - \}) - - if !l:job_ran - " The job failed to run, so skip to the next item. - let l:index += 1 - else - " Stop here, we will handle exit later on. - return - endif - endif - endwhile - - call ale#fix#ApplyFixes(l:buffer, l:input) + call s:RunJob(l:result, { + \ 'buffer': l:buffer, + \ 'input': l:input, + \ 'callback_list': a:options.callback_list, + \ 'callback_index': l:index, + \}) endfunction function! s:AddSubCallbacks(full_list, callbacks) abort diff --git a/test/fix/test_ale_fix.vader b/test/fix/test_ale_fix.vader index bf18345d..e5ecd46c 100644 --- a/test/fix/test_ale_fix.vader +++ b/test/fix/test_ale_fix.vader @@ -180,6 +180,7 @@ Before: After: Restore + unlet! g:test_filename unlet! g:ale_run_synchronously unlet! g:ale_set_lists_synchronously unlet! g:ale_run_synchronously_callbacks @@ -224,8 +225,8 @@ After: setlocal buftype=nofile - if filereadable('fix_test_file') - call delete('fix_test_file') + if exists('g:test_filename') && filereadable(g:test_filename) + call delete(g:test_filename) endif call setloclist(0, []) @@ -479,8 +480,9 @@ Execute(ALEFix should fix files on the save event): let g:ale_lint_on_save = 1 let g:ale_enabled = 1 - noautocmd silent file fix_test_file - call writefile(getline(1, '$'), 'fix_test_file') + let g:test_filename = tempname() + execute 'noautocmd silent file ' . fnameescape(g:test_filename) + call writefile(getline(1, '$'), g:test_filename) let g:ale_fixers.testft = ['AddDollars'] @@ -492,8 +494,8 @@ Execute(ALEFix should fix files on the save event): call ale#test#FlushJobs() " We should save the file. - AssertEqual ['$a', '$b', '$c'], readfile('fix_test_file') - Assert !&modified, 'The was marked as ''modified''' + AssertEqual ['$a', '$b', '$c'], readfile(g:test_filename) + Assert !&modified, 'The file was marked as ''modified''' if !has('win32') " We should have run the linter. @@ -520,8 +522,9 @@ Execute(ALEFix should not fix files on :wq): let g:ale_lint_on_save = 1 let g:ale_enabled = 1 - noautocmd silent file fix_test_file - call writefile(getline(1, '$'), 'fix_test_file') + let g:test_filename = tempname() + execute 'noautocmd silent file ' . fnameescape(g:test_filename) + call writefile(getline(1, '$'), g:test_filename) let g:ale_fixers.testft = ['AddDollars'] @@ -534,7 +537,7 @@ Execute(ALEFix should not fix files on :wq): call ale#events#SaveEvent(bufnr('')) " We should save the file. - AssertEqual ['a', 'b', 'c'], readfile('fix_test_file') + AssertEqual ['a', 'b', 'c'], readfile(g:test_filename) Assert &modified, 'The was not marked as ''modified''' " We should not run the linter. @@ -555,7 +558,8 @@ Execute(ALEFix should still lint with no linters to be applied): let g:ale_lint_on_save = 1 let g:ale_enabled = 1 - noautocmd silent file fix_test_file + let g:test_filename = tempname() + execute 'noautocmd silent file ' . fnameescape(g:test_filename) let g:ale_fixers.testft = [] @@ -563,7 +567,7 @@ Execute(ALEFix should still lint with no linters to be applied): call ale#events#SaveEvent(bufnr('')) call ale#test#FlushJobs() - Assert !filereadable('fix_test_file'), 'The file should not have been saved' + Assert !filereadable(g:test_filename), 'The file should not have been saved' if !has('win32') " We have run the linter. @@ -590,7 +594,8 @@ Execute(ALEFix should still lint when nothing was fixed on save): let g:ale_lint_on_save = 1 let g:ale_enabled = 1 - noautocmd silent file fix_test_file + let g:test_filename = tempname() + execute 'noautocmd silent file ' . fnameescape(g:test_filename) let g:ale_fixers.testft = ['DoNothing'] @@ -598,7 +603,7 @@ Execute(ALEFix should still lint when nothing was fixed on save): call ale#events#SaveEvent(bufnr('')) call ale#test#FlushJobs() - Assert !filereadable('fix_test_file'), 'The file should not have been saved' + Assert !filereadable(g:test_filename), 'The file should not have been saved' if !has('win32') " We should have run the linter. @@ -626,7 +631,8 @@ Given testft (A file with three lines): c Execute(ale#fix#InitBufferData() should set up the correct data): - noautocmd silent file fix_test_file + let g:test_filename = tempname() + execute 'noautocmd silent file ' . fnameescape(g:test_filename) call ale#fix#InitBufferData(bufnr(''), 'save_file')