Before: 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_set_lists_synchronously = 1 let g:ale_fix_buffer_data = {} let g:ale_fixers = { \ 'testft': [], \} if !has('win32') let &shell = '/bin/bash' endif call ale#test#SetDirectory('/testplugin/test') call ale#test#SetFilename('test.txt') 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 CatLineOneArg(buffer) abort return {'command': 'cat - <(echo d)'} endfunction function ReplaceWithTempFile(buffer, lines) abort return {'command': 'echo x > %t', 'read_temporary_file': 1} endfunction function RemoveLastLine(buffer, lines) abort return ['a', 'b'] endfunction function RemoveLastLineOneArg(buffer) abort 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 function GetLastMessage() redir => l:output silent mess redir END let l:lines = split(l:output, "\n") return empty(l:lines) ? '' : l:lines[-1] endfunction After: Restore unlet! g:ale_run_synchronously unlet! g:ale_set_lists_synchronously unlet! g:ale_emulate_job_failure unlet! b:ale_fixers unlet! b:ale_fix_on_save delfunction AddCarets delfunction AddDollars delfunction DoNothing delfunction CatLine delfunction CatLineOneArg delfunction ReplaceWithTempFile delfunction RemoveLastLine delfunction RemoveLastLineOneArg delfunction TestCallback delfunction SetUpLinters delfunction GetLastMessage call ale#test#RestoreDirectory() call ale#fix#registry#ResetToDefaults() call ale#linter#Reset() setlocal buftype=nofile if filereadable('fix_test_file') call delete('fix_test_file') endif call setloclist(0, []) let g:ale_fix_buffer_data = {} " Clear the messages between tests. echomsg '' Given testft (A file with three lines): a b c Execute(ALEFix should complain when there are no functions to call): ALEFix AssertEqual 'No fixers have been defined. Try :ALEFixSuggest', GetLastMessage() 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): if has('win32') " Just skip this test on Windows, we can't run it. call setline(1, ['a', 'b', 'c', 'd']) else let g:ale_fixers.testft = ['CatLine'] ALEFix endif Expect(An extra line should be added): a b c d Execute(ALEFix should allow temporary files to be read): if has('win32') " Just skip this test on Windows, we can't run it. call setline(1, ['x']) 2,3d else let g:ale_fixers.testft = ['ReplaceWithTempFile'] ALEFix endif 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): if has('win32') " Just skip this test on Windows, we can't run it. call setline(1, ['$x']) 2,3d else let g:ale_fixers.testft = ['ReplaceWithTempFile', 'AddDollars'] ALEFix endif Expect(The lines from the temporary file should be modified): $x Execute(ALEFix should send lines modified by functions to jobs): if has('win32') " Just skip this test on Windows, we can't run it. call setline(1, ['$a', '$b', '$c', 'd']) else let g:ale_fixers.testft = ['AddDollars', 'CatLine'] ALEFix endif Expect(The lines should first be modified by the function, then the job): $a $b $c d 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 Execute(ALEFix should handle strings for selecting a single function): let g:ale_fixers.testft = 'AddCarets' ALEFix Expect(The first function should be used): ^a ^b ^c Execute(ALEFix should use functions from the registry): call ale#fix#registry#Add('add_carets', 'AddCarets', [], 'Add some carets') let g:ale_fixers.testft = ['add_carets'] ALEFix Expect(The registry function should be used): ^a ^b ^c Execute(ALEFix should be able to remove the last line for files): let g:ale_fixers.testft = ['RemoveLastLine'] ALEFix Expect(There should be only two lines): a b Execute(ALEFix should accept funcrefs): let g:ale_fixers.testft = [function('RemoveLastLine')] ALEFix Expect(There should be only two lines): a b Execute(ALEFix should accept lambdas): if has('nvim') " NeoVim 0.1.7 can't interpret lambdas correctly, so just set the lines " to make the test pass. call setline(1, ['a', 'b', 'c', 'd']) else let g:ale_fixers.testft = [{buffer, lines -> lines + ['d']}] ALEFix endif Expect(There should be an extra line): a b c d Execute(ALEFix should user buffer-local fixer settings): let g:ale_fixers.testft = ['AddCarets', 'AddDollars'] let b:ale_fixers = {'testft': ['RemoveLastLine']} ALEFix 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 call writefile(getline(1, '$'), 'fix_test_file') let g:ale_fixers.testft = ['AddDollars'] " We have to set the buftype to empty so the file will be written. setlocal buftype= call SetUpLinters() call ale#events#SaveEvent(bufnr('')) " We should save the file. AssertEqual ['$a', '$b', '$c'], readfile('fix_test_file') Assert !&modified, 'The was marked as ''modified''' if !has('win32') " We should have run the linter. AssertEqual [{ \ 'bufnr': bufnr('%'), \ 'lnum': 1, \ 'vcol': 0, \ 'col': 1, \ 'text': 'xxx', \ 'type': 'E', \ 'nr': -1, \ 'pattern': '', \ 'valid': 1, \}], getloclist(0) endif 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(bufnr('')) Assert !filereadable('fix_test_file'), 'The file should not have been saved' if !has('win32') " 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) endif Expect(The buffer should be the same): a b c Execute(ALEFix should still lint when nothing was fixed on save): 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 = ['DoNothing'] call SetUpLinters() call ale#events#SaveEvent(bufnr('')) Assert !filereadable('fix_test_file'), 'The file should not have been saved' if !has('win32') " We should have run the linter. AssertEqual [{ \ 'bufnr': bufnr('%'), \ 'lnum': 1, \ 'vcol': 0, \ 'col': 1, \ 'text': 'xxx', \ 'type': 'E', \ 'nr': -1, \ 'pattern': '', \ 'valid': 1, \}], getloclist(0) endif Expect(The buffer should be the same): a b c Given testft (A file with three lines): a b c Execute(ale#fix#InitBufferData() should set up the correct data): noautocmd silent file fix_test_file call ale#fix#InitBufferData(bufnr(''), 'save_file') AssertEqual { \ bufnr(''): { \ 'temporary_directory_list': [], \ 'vars': b:, \ 'filename': ale#path#Winify(getcwd() . '/fix_test_file'), \ 'done': 0, \ 'lines_before': ['a', 'b', 'c'], \ 'should_save': 1, \ }, \}, g:ale_fix_buffer_data Execute(ALEFix simple functions should be able to accept one argument, the buffer): let g:ale_fixers.testft = ['RemoveLastLineOneArg'] ALEFix Expect(There should be only two lines): a b Execute(b:ale_fix_on_save = 1 should override g:ale_fix_on_save = 0): let g:ale_fix_on_save = 0 let b:ale_fix_on_save = 1 let g:ale_fixers.testft = ['RemoveLastLineOneArg'] call ale#events#SaveEvent(bufnr('')) Expect(There should be only two lines): a b Execute(b:ale_fix_on_save = 0 should override g:ale_fix_on_save = 1): let g:ale_fix_on_save = 1 let b:ale_fix_on_save = 0 let g:ale_fixers.testft = ['RemoveLastLineOneArg'] call ale#events#SaveEvent(bufnr('')) Expect(The lines should be the same): a b c Execute(ALEFix functions returning jobs should be able to accept one argument): if has('win32') " Just skip this test on Windows, we can't run it. call setline(1, ['a', 'b', 'c', 'd']) else let g:ale_fixers.testft = ['CatLine'] ALEFix endif Expect(An extra line should be added): a b c d Execute(ALE should print a message telling you something isn't a valid fixer when you type some nonsense): let g:ale_fixers.testft = ['CatLine', 'invalidname'] ALEFix AssertEqual 'There is no fixer named `invalidname`. Check :ALEFixSuggest', GetLastMessage()