diff options
-rw-r--r-- | autoload/ale/engine.vim | 3 | ||||
-rw-r--r-- | autoload/ale/fix.vim | 55 | ||||
-rw-r--r-- | autoload/ale/handlers/python.vim | 6 | ||||
-rw-r--r-- | plugin/ale.vim | 7 | ||||
-rw-r--r-- | test/test_ale_fix.vader | 109 | ||||
-rw-r--r-- | test/test_ale_toggle.vader | 3 | ||||
-rw-r--r-- | test/test_eslint_executable_detection.vader | 8 |
7 files changed, 174 insertions, 17 deletions
diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim index af074c00..e13562a0 100644 --- a/autoload/ale/engine.vim +++ b/autoload/ale/engine.vim @@ -405,8 +405,7 @@ function! s:RunJob(options) abort \ : l:command \) - " TODO, get the exit system of the shell call and pass it on here. - call l:job_options.exit_cb(l:job_id, 0) + call l:job_options.exit_cb(l:job_id, v:shell_error) endif endfunction diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim index 50a426bd..6ed750ce 100644 --- a/autoload/ale/fix.vim +++ b/autoload/ale/fix.vim @@ -1,3 +1,6 @@ +" FIXME: Switch to using the global buffer data dictionary instead. +" Cleanup will work better if there isn't a second Dictionary we have to work +" with. let s:buffer_data = {} let s:job_info_map = {} @@ -23,8 +26,6 @@ function! ale#fix#ApplyQueuedFixes() abort return endif - echom l:data.output[0] - call setline(1, l:data.output) let l:start_line = len(l:data.output) + 1 @@ -145,11 +146,42 @@ function! s:RunJob(options) abort let l:job_options.out_cb = function('s:GatherOutput') endif - let l:job_id = ale#job#Start(l:command, l:job_options) + 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 - " TODO: Check that the job runs, and skip to the next item if it does not. + 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 + + 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) == type([]) + \ ? 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 function! s:RunFixer(options) abort @@ -158,7 +190,7 @@ function! s:RunFixer(options) abort let l:index = a:options.callback_index while len(a:options.callback_list) > l:index - let l:result = function(a:options.callback_list[l:index])(l:buffer, l:input) + let l:result = function(a:options.callback_list[l:index])(l:buffer, copy(l:input)) if type(l:result) == type(0) && l:result == 0 " When `0` is returned, skip this item. @@ -167,9 +199,7 @@ function! s:RunFixer(options) abort let l:input = l:result let l:index += 1 else - " TODO: Check the return value here, and skip an index if - " the job fails. - call s:RunJob({ + let l:job_ran = s:RunJob({ \ 'buffer': l:buffer, \ 'command': l:result.command, \ 'output_stream': get(l:result, 'output_stream', 'stdout'), @@ -178,8 +208,13 @@ function! s:RunFixer(options) abort \ 'callback_index': l:index, \}) - " Stop here, we will handle exit later on. - return + 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 diff --git a/autoload/ale/handlers/python.vim b/autoload/ale/handlers/python.vim index 85e2f203..33ee3c9d 100644 --- a/autoload/ale/handlers/python.vim +++ b/autoload/ale/handlers/python.vim @@ -35,3 +35,9 @@ function! ale#handlers#python#HandlePEP8Format(buffer, lines) abort return l:output endfunction + +function! ale#handlers#python#AutoPEP8(buffer, lines) abort + return { + \ 'command': 'autopep8 -' + \} +endfunction diff --git a/plugin/ale.vim b/plugin/ale.vim index 0e8c369f..28b8bebf 100644 --- a/plugin/ale.vim +++ b/plugin/ale.vim @@ -60,6 +60,9 @@ let g:ale_filetype_blacklist = ['nerdtree', 'unite', 'tags'] " This Dictionary configures which linters are enabled for which languages. let g:ale_linters = get(g:, 'ale_linters', {}) +" This Dictionary configures which functions will be used for fixing problems. +let g:ale_fixers = get(g:, 'ale_fixers', {}) + " This Dictionary allows users to set up filetype aliases for new filetypes. let g:ale_linter_aliases = get(g:, 'ale_linter_aliases', {}) @@ -276,6 +279,9 @@ command! -bar ALEInfo :call ale#debugging#Info() " The same, but copy output to your clipboard. command! -bar ALEInfoToClipboard :call ale#debugging#InfoToClipboard() +" Fix problems in files. +command! -bar ALEFix :call ale#fix#Fix() + " <Plug> mappings for commands nnoremap <silent> <Plug>(ale_previous) :ALEPrevious<Return> nnoremap <silent> <Plug>(ale_previous_wrap) :ALEPreviousWrap<Return> @@ -284,6 +290,7 @@ nnoremap <silent> <Plug>(ale_next_wrap) :ALENextWrap<Return> nnoremap <silent> <Plug>(ale_toggle) :ALEToggle<Return> nnoremap <silent> <Plug>(ale_lint) :ALELint<Return> nnoremap <silent> <Plug>(ale_detail) :ALEDetail<Return> +nnoremap <silent> <Plug>(ale_fix) :ALEFix<Return> " Housekeeping diff --git a/test/test_ale_fix.vader b/test/test_ale_fix.vader new file mode 100644 index 00000000..50e0e065 --- /dev/null +++ b/test/test_ale_fix.vader @@ -0,0 +1,109 @@ +Before: + Save g:ale_fixers, &shell + let g:ale_run_synchronously = 1 + let g:ale_fixers = { + \ 'testft': [], + \} + let &shell = '/bin/bash' + + function AddCarets(buffer, lines) abort + " map() is applied to the original lines here. + " This way, we can ensure that defensive copies are made. + return map(a:lines, '''^'' . v:val') + endfunction + + function AddDollars(buffer, lines) abort + return map(a:lines, '''$'' . v:val') + endfunction + + function DoNothing(buffer, lines) abort + return 0 + endfunction + + function CatLine(buffer, lines) abort + return {'command': 'cat - <(echo d)'} + endfunction + + function ReplaceWithTempFile(buffer, lines) abort + return {'command': 'echo x > %t', 'read_temporary_file': 1} + endfunction + +After: + Restore + unlet! g:ale_run_synchronously + unlet! g:ale_emulate_job_failure + delfunction AddCarets + delfunction AddDollars + delfunction DoNothing + delfunction CatLine + delfunction ReplaceWithTempFile + +Given testft (A file with three lines): + a + b + c + +Execute(ALEFix should complain when there are no functions to call): + AssertThrows ALEFix + AssertEqual 'Vim(echoerr):No fixers have been defined for filetype: testft', g:vader_exception + +Execute(ALEFix should apply simple functions): + let g:ale_fixers.testft = ['AddCarets'] + ALEFix + +Expect(The first function should be used): + ^a + ^b + ^c + +Execute(ALEFix should apply simple functions in a chain): + let g:ale_fixers.testft = ['AddCarets', 'AddDollars'] + ALEFix + +Expect(Both functions should be used): + $^a + $^b + $^c + +Execute(ALEFix should allow 0 to be returned to skip functions): + let g:ale_fixers.testft = ['DoNothing', 'AddDollars'] + ALEFix + +Expect(Only the second function should be applied): + $a + $b + $c + +Execute(ALEFix should allow commands to be run): + let g:ale_fixers.testft = ['CatLine'] + ALEFix + +Expect(An extra line should be added): + a + b + c + d + +Execute(ALEFix should allow temporary files to be read): + let g:ale_fixers.testft = ['ReplaceWithTempFile'] + ALEFix + +Expect(The line we wrote to the temporary file should be used here): + x + +Execute(ALEFix should allow jobs and simple functions to be combined): + let g:ale_fixers.testft = ['ReplaceWithTempFile', 'AddDollars'] + ALEFix + +Expect(The lines from the temporary file should be modified): + $x + +Execute(ALEFix should skip commands when jobs fail to run): + let g:ale_emulate_job_failure = 1 + let g:ale_fixers.testft = ['CatLine', 'AddDollars'] + ALEFix + +Expect(Only the second function should be applied): + $a + $b + $c diff --git a/test/test_ale_toggle.vader b/test/test_ale_toggle.vader index 5d27c864..3546ad71 100644 --- a/test/test_ale_toggle.vader +++ b/test/test_ale_toggle.vader @@ -11,6 +11,7 @@ Before: \ 'valid': 1, \}] let g:expected_groups = [ + \ 'ALEBufferFixGroup', \ 'ALECleanupGroup', \ 'ALECursorGroup', \ 'ALEHighlightBufferGroup', @@ -101,7 +102,7 @@ Execute(ALEToggle should reset everything and then run again): AssertEqual [], getloclist(0) AssertEqual [], ale#sign#FindCurrentSigns(bufnr('%')) AssertEqual [], getmatches() - AssertEqual ['ALECleanupGroup', 'ALEHighlightBufferGroup'], ParseAuGroups() + AssertEqual ['ALEBufferFixGroup', 'ALECleanupGroup', 'ALEHighlightBufferGroup'], ParseAuGroups() " Toggle ALE on, everything should be set up and run again. ALEToggle diff --git a/test/test_eslint_executable_detection.vader b/test/test_eslint_executable_detection.vader index e963ae1c..03bb89e8 100644 --- a/test/test_eslint_executable_detection.vader +++ b/test/test_eslint_executable_detection.vader @@ -20,7 +20,7 @@ Execute(create-react-app directories should be detected correctly): AssertEqual \ g:dir . '/eslint-test-files/react-app/node_modules/eslint/bin/eslint.js', - \ ale_linters#javascript#eslint#GetExecutable(bufnr('')) + \ ale#handlers#eslint#GetExecutable(bufnr('')) :q @@ -31,7 +31,7 @@ Execute(use-global should override create-react-app detection): AssertEqual \ 'eslint_d', - \ ale_linters#javascript#eslint#GetExecutable(bufnr('')) + \ ale#handlers#eslint#GetExecutable(bufnr('')) :q @@ -40,7 +40,7 @@ Execute(other app directories should be detected correctly): AssertEqual \ g:dir . '/eslint-test-files/node_modules/.bin/eslint', - \ ale_linters#javascript#eslint#GetExecutable(bufnr('')) + \ ale#handlers#eslint#GetExecutable(bufnr('')) :q @@ -51,6 +51,6 @@ Execute(use-global should override other app directories): AssertEqual \ 'eslint_d', - \ ale_linters#javascript#eslint#GetExecutable(bufnr('')) + \ ale#handlers#eslint#GetExecutable(bufnr('')) :q |