#591 Support fixing files on save
This commit is contained in:
parent
bc317a7be5
commit
6ec965c8e4
14
autoload/ale/events.vim
Normal file
14
autoload/ale/events.vim
Normal file
@ -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
|
@ -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.
|
||||
|
17
doc/ale.txt
17
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*
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user