summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorw0rp <devw0rp@gmail.com>2017-05-30 21:32:51 +0100
committerw0rp <devw0rp@gmail.com>2017-05-30 21:32:51 +0100
commit6ec965c8e4618c14b05b05bd554b3fed9c1191e1 (patch)
tree1c2dcf20cbb3ee0d5c538b6d0760e5f0ea2fd755
parentbc317a7be5e5c9db85b59c6377f3a9b9f034535c (diff)
downloadale-6ec965c8e4618c14b05b05bd554b3fed9c1191e1.zip
#591 Support fixing files on save
-rw-r--r--autoload/ale/events.vim14
-rw-r--r--autoload/ale/fix.vim96
-rw-r--r--doc/ale.txt17
-rw-r--r--plugin/ale.vim11
-rw-r--r--test/test_ale_fix.vader108
-rw-r--r--test/test_ale_init_au_groups.vader23
6 files changed, 234 insertions, 35 deletions
diff --git a/autoload/ale/events.vim b/autoload/ale/events.vim
new file mode 100644
index 00000000..f8020a16
--- /dev/null
+++ b/autoload/ale/events.vim
@@ -0,0 +1,14 @@
+" Author: w0rp <devw0rp@gmail.com>
+
+function! ale#events#SaveEvent() abort
+ let l:should_lint = g:ale_enabled && g:ale_lint_on_save
+
+ if g:ale_fix_on_save
+ let l:will_fix = ale#fix#Fix('save_file')
+ let l:should_lint = l:should_lint && !l:will_fix
+ endif
+
+ if l:should_lint
+ call ale#Queue(0, 'lint_file')
+ endif
+endfunction
diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim
index ac401f90..33f97ce4 100644
--- a/autoload/ale/fix.vim
+++ b/autoload/ale/fix.vim
@@ -26,21 +26,34 @@ function! ale#fix#ApplyQueuedFixes() abort
endif
call remove(g:ale_fix_buffer_data, l:buffer)
- call setline(1, l:data.output)
- let l:start_line = len(l:data.output) + 1
- let l:end_line = len(l:data.lines_before)
+ if l:data.changes_made
+ call setline(1, l:data.output)
- if l:end_line >= l:start_line
- let l:save = winsaveview()
- silent execute l:start_line . ',' . l:end_line . 'd'
- call winrestview(l:save)
+ let l:start_line = len(l:data.output) + 1
+ let l:end_line = len(l:data.lines_before)
+
+ if l:end_line >= l:start_line
+ let l:save = winsaveview()
+ silent execute l:start_line . ',' . l:end_line . 'd'
+ call winrestview(l:save)
+ endif
+
+ if l:data.should_save
+ set nomodified
+ endif
+ endif
+
+ if l:data.should_save
+ let l:should_lint = g:ale_fix_on_save
+ else
+ let l:should_lint = l:data.changes_made
endif
" If ALE linting is enabled, check for problems with the file again after
" fixing problems.
- if g:ale_enabled
- call ale#Queue(g:ale_lint_delay)
+ if g:ale_enabled && l:should_lint
+ call ale#Queue(0, l:data.should_save ? 'lint_file' : '')
endif
endfunction
@@ -49,14 +62,9 @@ function! ale#fix#ApplyFixes(buffer, output) abort
let l:data = g:ale_fix_buffer_data[a:buffer]
let l:data.output = a:output
+ let l:data.changes_made = l:data.lines_before != l:data.output
- if l:data.lines_before == l:data.output
- " Don't modify the buffer if nothing has changed.
- call remove(g:ale_fix_buffer_data, a:buffer)
- return
- endif
-
- if bufexists(a:buffer)
+ if l:data.changes_made && bufexists(a:buffer)
let l:lines = getbufline(a:buffer, 1, '$')
if l:data.lines_before != l:lines
@@ -66,7 +74,13 @@ function! ale#fix#ApplyFixes(buffer, output) abort
endif
let l:data.done = 1
- else
+ endif
+
+ if l:data.changes_made && l:data.should_save
+ call writefile(a:output, l:data.filename)
+ endif
+
+ if !bufexists(a:buffer)
" Remove the buffer data when it doesn't exist.
call remove(g:ale_fix_buffer_data, a:buffer)
endif
@@ -265,7 +279,6 @@ function! s:GetCallbacks() abort
endfor
if empty(l:callback_list)
- echoerr 'No fixers have been defined. Try :ALEFixSuggest'
return []
endif
@@ -288,33 +301,56 @@ function! s:GetCallbacks() abort
return l:corrected_list
endfunction
-function! ale#fix#Fix() abort
+function! ale#fix#InitBufferData(buffer, fixing_flag) abort
+ " The 'done' flag tells the function for applying changes when fixing
+ " is complete.
+ let g:ale_fix_buffer_data[a:buffer] = {
+ \ 'lines_before': getbufline(a:buffer, 1, '$'),
+ \ 'filename': expand('#' . a:buffer . ':p'),
+ \ 'done': 0,
+ \ 'should_save': a:fixing_flag ==# 'save_file',
+ \ 'temporary_directory_list': [],
+ \}
+endfunction
+
+" Accepts an optional argument for what to do when fixing.
+"
+" Returns 0 if no fixes can be applied, and 1 if fixing can be done.
+function! ale#fix#Fix(...) abort
+ if len(a:0) > 1
+ throw 'too many arguments!'
+ endif
+
+ let l:fixing_flag = get(a:000, 0, '')
+
+ if l:fixing_flag !=# '' && l:fixing_flag !=# 'save_file'
+ throw "fixing_flag must be either '' or 'save_file'"
+ endif
+
let l:callback_list = s:GetCallbacks()
if empty(l:callback_list)
- return
+ if l:fixing_flag ==# ''
+ echoerr 'No fixers have been defined. Try :ALEFixSuggest'
+ endif
+
+ return 0
endif
let l:buffer = bufnr('')
- let l:input = getbufline(l:buffer, 1, '$')
" Clean up any files we might have left behind from a previous run.
call ale#fix#RemoveManagedFiles(l:buffer)
-
- " The 'done' flag tells the function for applying changes when fixing
- " is complete.
- let g:ale_fix_buffer_data[l:buffer] = {
- \ 'lines_before': l:input,
- \ 'done': 0,
- \ 'temporary_directory_list': [],
- \}
+ call ale#fix#InitBufferData(l:buffer, l:fixing_flag)
call s:RunFixer({
\ 'buffer': l:buffer,
- \ 'input': l:input,
+ \ 'input': g:ale_fix_buffer_data[l:buffer].lines_before,
\ 'callback_index': 0,
\ 'callback_list': l:callback_list,
\})
+
+ return 1
endfunction
" Set up an autocmd command to try and apply buffer fixes when available.
diff --git a/doc/ale.txt b/doc/ale.txt
index 8fb048e6..1e3ac0fb 100644
--- a/doc/ale.txt
+++ b/doc/ale.txt
@@ -311,6 +311,18 @@ g:ale_fixers *g:ale_fixers*
This variable can be overriden with variables in each buffer.
+g:ale_fix_on_save *g:ale_fix_on_save*
+
+ Type: |Number|
+ Default: `0`
+
+ When set to 1, ALE will fix files when they are saved.
+
+ If |g:ale_lint_on_save| is set to 1, files will be checked with linters
+ after files are fixed, only when the buffer is open, or re-opened. Changes
+ to the file will saved to the file on disk.
+
+
g:ale_history_enabled *g:ale_history_enabled*
Type: |Number|
@@ -770,6 +782,11 @@ upon some lines immediately, then run `eslint` from the ALE registry, and
then call a lambda function which will remove every single line comment
from the file.
+Files can be fixed automatically with the following options, which are all off
+by default.
+
+|g:ale_fix_on_save| - Fix files when they are saved.
+
===============================================================================
5. Integration Documentation *ale-integrations*
diff --git a/plugin/ale.vim b/plugin/ale.vim
index b5991549..1f9df896 100644
--- a/plugin/ale.vim
+++ b/plugin/ale.vim
@@ -89,6 +89,8 @@ let g:ale_lint_on_save = get(g:, 'ale_lint_on_save', 1)
" This flag can be set to 1 to enable linting when the filetype is changed.
let g:ale_lint_on_filetype_changed = get(g:, 'ale_lint_on_filetype_changed', 1)
+call ale#Set('fix_on_save', 0)
+
" This flag may be set to 0 to disable ale. After ale is loaded, :ALEToggle
" should be used instead.
let g:ale_enabled = get(g:, 'ale_enabled', 1)
@@ -218,8 +220,8 @@ function! ALEInitAuGroups() abort
augroup ALERunOnSaveGroup
autocmd!
- if g:ale_enabled && g:ale_lint_on_save
- autocmd BufWrite * call ale#Queue(0, 'lint_file')
+ if (g:ale_enabled && g:ale_lint_on_save) || g:ale_fix_on_save
+ autocmd BufWrite * call ale#events#SaveEvent()
endif
augroup END
@@ -242,10 +244,13 @@ function! ALEInitAuGroups() abort
augroup END
if !g:ale_enabled
+ if !g:ale_fix_on_save
+ augroup! ALERunOnSaveGroup
+ endif
+
augroup! ALEPatternOptionsGroup
augroup! ALERunOnTextChangedGroup
augroup! ALERunOnEnterGroup
- augroup! ALERunOnSaveGroup
augroup! ALERunOnInsertLeave
augroup! ALECursorGroup
endif
diff --git a/test/test_ale_fix.vader b/test/test_ale_fix.vader
index dfe79443..b4ffc062 100644
--- a/test/test_ale_fix.vader
+++ b/test/test_ale_fix.vader
@@ -1,6 +1,15 @@
Before:
- Save g:ale_fixers, &shell, g:ale_enabled
+ Save g:ale_fixers
+ Save &shell
+ Save g:ale_enabled
+ Save g:ale_fix_on_save
+ Save g:ale_lint_on_save
+ Save g:ale_echo_cursor
+
+ silent! cd /testplugin/test
+
let g:ale_enabled = 0
+ let g:ale_echo_cursor = 0
let g:ale_run_synchronously = 1
let g:ale_fixers = {
\ 'testft': [],
@@ -33,6 +42,19 @@ Before:
return ['a', 'b']
endfunction
+ function! TestCallback(buffer, output)
+ return [{'lnum': 1, 'col': 1, 'text': 'xxx'}]
+ endfunction
+
+ function! SetUpLinters()
+ call ale#linter#Define('testft', {
+ \ 'name': 'testlinter',
+ \ 'callback': 'TestCallback',
+ \ 'executable': 'true',
+ \ 'command': 'true',
+ \})
+ endfunction
+
After:
Restore
unlet! g:ale_run_synchronously
@@ -44,7 +66,14 @@ After:
delfunction CatLine
delfunction ReplaceWithTempFile
delfunction RemoveLastLine
+ delfunction TestCallback
+ delfunction SetUpLinters
call ale#fix#registry#ResetToDefaults()
+ call ale#linter#Reset()
+
+ if filereadable('fix_test_file')
+ call delete('fix_test_file')
+ endif
Given testft (A file with three lines):
a
@@ -185,3 +214,80 @@ Execute(ALEFix should user buffer-local fixer settings):
Expect(There should be only two lines):
a
b
+
+Given testft (A file with three lines):
+ a
+ b
+ c
+
+Execute(ALEFix should save files on the save event):
+ let g:ale_fix_on_save = 1
+ let g:ale_lint_on_save = 1
+ let g:ale_enabled = 1
+
+ noautocmd silent file fix_test_file
+
+ let g:ale_fixers.testft = ['AddDollars']
+
+ call SetUpLinters()
+ call ale#events#SaveEvent()
+
+ " We should save the file.
+ Assert filereadable('fix_test_file'), 'The file cannot be read'
+ AssertEqual ['$a', '$b', '$c'], readfile('fix_test_file')
+ Assert !&modified, 'The was marked as ''modified'''
+
+ " We have run the linter.
+ AssertEqual [{
+ \ 'bufnr': bufnr('%'),
+ \ 'lnum': 1,
+ \ 'vcol': 0,
+ \ 'col': 1,
+ \ 'text': 'xxx',
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ 'pattern': '',
+ \ 'valid': 1,
+ \}], getloclist(0)
+
+Expect(The buffer should be modified):
+ $a
+ $b
+ $c
+
+Given testft (A file with three lines):
+ a
+ b
+ c
+
+Execute(ALEFix should still lint with no linters to be applied):
+ let g:ale_fix_on_save = 1
+ let g:ale_lint_on_save = 1
+ let g:ale_enabled = 1
+
+ noautocmd silent file fix_test_file
+
+ let g:ale_fixers.testft = []
+
+ call SetUpLinters()
+ call ale#events#SaveEvent()
+
+ Assert !filereadable('fix_test_file'), 'The file should not have been saved'
+
+ " We have run the linter.
+ AssertEqual [{
+ \ 'bufnr': bufnr('%'),
+ \ 'lnum': 1,
+ \ 'vcol': 0,
+ \ 'col': 1,
+ \ 'text': 'xxx',
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ 'pattern': '',
+ \ 'valid': 1,
+ \}], getloclist(0)
+
+Expect(The buffer should be the same):
+ a
+ b
+ c
diff --git a/test/test_ale_init_au_groups.vader b/test/test_ale_init_au_groups.vader
index 0134f762..532232b3 100644
--- a/test/test_ale_init_au_groups.vader
+++ b/test/test_ale_init_au_groups.vader
@@ -31,6 +31,7 @@ Before:
return l:matches
endfunction
+ Save g:ale_enabled
Save g:ale_lint_on_text_changed
Save g:ale_lint_on_insert_leave
Save g:ale_pattern_options_enabled
@@ -38,6 +39,7 @@ Before:
Save g:ale_lint_on_filetype_changed
Save g:ale_lint_on_save
Save g:ale_echo_cursor
+ Save g:ale_fix_on_save
After:
delfunction CheckAutocmd
@@ -138,14 +140,33 @@ Execute (g:ale_lint_on_filetype_changed = 1 should bind FileType, and required b
Execute (g:ale_lint_on_save = 0 should bind no events):
let g:ale_lint_on_save = 0
+ let g:ale_fix_on_save = 0
AssertEqual [], CheckAutocmd('ALERunOnSaveGroup')
Execute (g:ale_lint_on_save = 1 should bind no events):
let g:ale_lint_on_save = 1
+ let g:ale_fix_on_save = 0
AssertEqual [
- \ 'BufWritePre * call ale#Queue(0, ''lint_file'')',
+ \ 'BufWritePre * call ale#events#SaveEvent()',
+ \], CheckAutocmd('ALERunOnSaveGroup')
+
+Execute (g:ale_lint_on_save = 0 and g:ale_fix_on_save = 1 should bind events):
+ let g:ale_lint_on_save = 0
+ let g:ale_fix_on_save = 1
+
+ AssertEqual [
+ \ 'BufWritePre * call ale#events#SaveEvent()',
+ \], CheckAutocmd('ALERunOnSaveGroup')
+
+Execute (g:ale_fix_on_save = 1 should bind events even when ALE is disabled):
+ let g:ale_enabled = 0
+ let g:ale_lint_on_save = 0
+ let g:ale_fix_on_save = 1
+
+ AssertEqual [
+ \ 'BufWritePre * call ale#events#SaveEvent()',
\], CheckAutocmd('ALERunOnSaveGroup')
Execute (g:ale_echo_cursor = 0 should bind no events):