summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--autoload/ale/engine.vim3
-rw-r--r--autoload/ale/fix.vim55
-rw-r--r--autoload/ale/handlers/python.vim6
-rw-r--r--plugin/ale.vim7
-rw-r--r--test/test_ale_fix.vader109
-rw-r--r--test/test_ale_toggle.vader3
-rw-r--r--test/test_eslint_executable_detection.vader8
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