diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index 1b3ca1a8..02b02699 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -160,6 +160,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['php'], \ 'description': 'Fix PHP files with php-cs-fixer.', \ }, +\ 'astyle': { +\ 'function': 'ale#fixers#astyle#Fix', +\ 'suggested_filetypes': ['c', 'cpp'], +\ 'description': 'Fix C/C++ with astyle.', +\ }, \ 'clangtidy': { \ 'function': 'ale#fixers#clangtidy#Fix', \ 'suggested_filetypes': ['c', 'cpp', 'objc'], diff --git a/autoload/ale/fixers/astyle.vim b/autoload/ale/fixers/astyle.vim new file mode 100644 index 00000000..04e4b69a --- /dev/null +++ b/autoload/ale/fixers/astyle.vim @@ -0,0 +1,59 @@ +" Author: James Kim +" Description: Fix C/C++ files with astyle. + +function! s:set_variables() abort + for l:ft in ['c', 'cpp'] + call ale#Set(l:ft . '_astyle_executable', 'astyle') + call ale#Set(l:ft . '_astyle_project_options', '') + endfor +endfunction + +call s:set_variables() + + +function! ale#fixers#astyle#Var(buffer, name) abort + let l:ft = getbufvar(str2nr(a:buffer), '&filetype') + let l:ft = l:ft =~# 'cpp' ? 'cpp' : 'c' + + return ale#Var(a:buffer, l:ft . '_astyle_' . a:name) +endfunction + +" Try to find a project options file. +function! ale#fixers#astyle#FindProjectOptions(buffer) abort + let l:proj_options = ale#fixers#astyle#Var(a:buffer, 'project_options') + + " If user has set project options variable then use it and skip any searching. + " This would allow users to use project files named differently than .astylerc. + if !empty(l:proj_options) + return l:proj_options + endif + + " Try to find nearest .astylerc file. + let l:proj_options = fnamemodify(ale#path#FindNearestFile(a:buffer, '.astylerc'), ':t') + + if !empty(l:proj_options) + return l:proj_options + endif + + " Try to find nearest _astylerc file. + let l:proj_options = fnamemodify(ale#path#FindNearestFile(a:buffer, '_astylerc'), ':t') + + if !empty(l:proj_options) + return l:proj_options + endif + + " If no project options file is found return an empty string. + return '' +endfunction + +function! ale#fixers#astyle#Fix(buffer) abort + let l:executable = ale#fixers#astyle#Var(a:buffer, 'executable') + let l:proj_options = ale#fixers#astyle#FindProjectOptions(a:buffer) + let l:command = ' --stdin=' + + return { + \ 'command': ale#Escape(l:executable) + \ . (empty(l:proj_options) ? '' : ' --project=' . l:proj_options) + \ . l:command + \} +endfunction diff --git a/autoload/ale/handlers/markdownlint.vim b/autoload/ale/handlers/markdownlint.vim index daaa1d66..5cef20ee 100644 --- a/autoload/ale/handlers/markdownlint.vim +++ b/autoload/ale/handlers/markdownlint.vim @@ -2,15 +2,22 @@ " Description: Adds support for markdownlint function! ale#handlers#markdownlint#Handle(buffer, lines) abort - let l:pattern=': \(\d*\): \(MD\d\{3}\)\(\/\)\([A-Za-z0-9-]\+\)\(.*\)$' + let l:pattern=': \?\(\d\+\)\(:\(\d\+\)\?\)\? \(MD\d\{3}/[A-Za-z0-9-]\+\) \(.*\)$' let l:output=[] for l:match in ale#util#GetMatches(a:lines, l:pattern) - call add(l:output, { + let l:result = ({ \ 'lnum': l:match[1] + 0, - \ 'text': '(' . l:match[2] . l:match[3] . l:match[4] . ')' . l:match[5], + \ 'code': l:match[4], + \ 'text': l:match[5], \ 'type': 'W', \}) + + if len(l:match[3]) > 0 + let l:result.col = (l:match[3] + 0) + endif + + call add(l:output, l:result) endfor return l:output diff --git a/doc/ale-c.txt b/doc/ale-c.txt index c9eb79db..efc26f93 100644 --- a/doc/ale-c.txt +++ b/doc/ale-c.txt @@ -59,6 +59,30 @@ g:ale_c_parse_makefile *g:ale_c_parse_makefile* build flags to use for different files. +=============================================================================== +astyle *ale-c-astyle* + +g:ale_c_astyle_executable *g:ale_c_astyle_executable* + *b:ale_c_astyle_executable* + Type: |String| + Default: `'astyle'` + + This variable can be changed to use a different executable for astyle. + + +g:ale_c_astyle_project_options *g:ale_c_astyle_project_options* + *b:ale_c_astyle_project_options* + Type: |String| + Default: `''` + + This variable can be changed to use an option file for project level + configurations. Provide only the filename of the option file that should be + present at the project's root directory. + + For example, if .astylrc is specified, the file is searched in the parent + directories of the source file's directory. + + =============================================================================== clang *ale-c-clang* diff --git a/doc/ale-cpp.txt b/doc/ale-cpp.txt index ead3be28..fbe31370 100644 --- a/doc/ale-cpp.txt +++ b/doc/ale-cpp.txt @@ -13,6 +13,30 @@ The following C options also apply to some C++ linters too. * |g:ale_c_parse_compile_commands| +=============================================================================== +astyle *ale-cpp-astyle* + +g:ale_cpp_astyle_executable *g:ale_cpp_astyle_executable* + *b:ale_cpp_astyle_executable* + Type: |String| + Default: `'astyle'` + + This variable can be changed to use a different executable for astyle. + + +g:ale_cpp_astyle_project_options *g:ale_cpp_astyle_project_options* + *b:ale_cpp_astyle_project_options* + Type: |String| + Default: `''` + + This variable can be changed to use an option file for project level + configurations. Provide only the filename of the option file that should be + present at the project's root directory. + + For example, if .astylrc is specified, the file is searched in the parent + directories of the source file's directory. + + =============================================================================== clang *ale-cpp-clang* diff --git a/doc/ale-java.txt b/doc/ale-java.txt index 2e736d90..0debc1af 100644 --- a/doc/ale-java.txt +++ b/doc/ale-java.txt @@ -172,7 +172,7 @@ The javalsp linter automatically detects external depenencies for Maven and Gradle projects. In case the javalsp fails to detect some of them, you can specify them setting a dictionary to |g:ale_java_javalsp_config| variable. > - let g:ale_java_javalsp_executable = + let g:ale_java_javalsp_config = \ { \ 'java': { \ 'externalDependencies': [ diff --git a/doc/ale-sh.txt b/doc/ale-sh.txt index 7897d0ce..c06f737a 100644 --- a/doc/ale-sh.txt +++ b/doc/ale-sh.txt @@ -22,7 +22,7 @@ g:ale_sh_bashate_options *g:ale_sh_bashate_options* example to ignore the indentation rule: > - let g:ale_sh_shellcheck_options = '-i E003' + let g:ale_sh_bashate_options = '-i E003' < =============================================================================== diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index 20731fba..cfb1b6e2 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -46,6 +46,7 @@ Notes: * `shellcheck` * `shfmt` * C + * `astyle` * `ccls` * `clang` * `clangd` @@ -63,6 +64,7 @@ Notes: * `mcsc`!! * `uncrustify` * C++ (filetype cpp) + * `astyle` * `ccls` * `clang` * `clangcheck`!! diff --git a/doc/ale.txt b/doc/ale.txt index f549ffda..71f69ac0 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -2285,6 +2285,7 @@ documented in additional help files. bib.....................................|ale-bib-options| bibclean..............................|ale-bib-bibclean| c.......................................|ale-c-options| + astyle................................|ale-c-astyle| clang.................................|ale-c-clang| clangd................................|ale-c-clangd| clang-format..........................|ale-c-clangformat| @@ -2307,6 +2308,7 @@ documented in additional help files. cmakelint.............................|ale-cmake-cmakelint| cmake-format..........................|ale-cmake-cmakeformat| cpp.....................................|ale-cpp-options| + astyle................................|ale-cpp-astyle| clang.................................|ale-cpp-clang| clangd................................|ale-cpp-clangd| clangcheck............................|ale-cpp-clangcheck| diff --git a/supported-tools.md b/supported-tools.md index cfc5430e..595391cc 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -55,6 +55,7 @@ formatting. * [shellcheck](https://www.shellcheck.net/) * [shfmt](https://github.com/mvdan/sh) * C + * [astyle](http://astyle.sourceforge.net/) * [ccls](https://github.com/MaskRay/ccls) * [clang](http://clang.llvm.org/) * [clangd](https://clang.llvm.org/extra/clangd.html) @@ -72,6 +73,7 @@ formatting. * [mcsc](http://www.mono-project.com/docs/about-mono/languages/csharp/) :floppy_disk: see:`help ale-cs-mcsc` for details and configuration * [uncrustify](https://github.com/uncrustify/uncrustify) * C++ (filetype cpp) + * [astyle](http://astyle.sourceforge.net/) * [ccls](https://github.com/MaskRay/ccls) * [clang](http://clang.llvm.org/) * [clangcheck](http://clang.llvm.org/docs/ClangCheck.html) :floppy_disk: diff --git a/test/fixers/test_astyle_fixer_callback.vader b/test/fixers/test_astyle_fixer_callback.vader new file mode 100644 index 00000000..cbec4493 --- /dev/null +++ b/test/fixers/test_astyle_fixer_callback.vader @@ -0,0 +1,78 @@ +Before: + Save g:ale_c_astyle_executable + Save g:ale_c_astyle_project_options + Save g:ale_cpp_astyle_project_options + + " Use an invalid global executable, so we don't match it. + let g:ale_c_astyle_executable = 'xxxinvalid' + let g:ale_cpp_astyle_executable = 'invalidpp' + let g:ale_c_astyle_project_options = '' + let g:ale_cpp_astyle_project_options = '' + + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The astyle callback should return the correct default values): + " Because this file doesn't exist, no astylrc config + " exists near it. Therefore, project_options is empty. + call ale#test#SetFilename('../c_files/testfile.c') + + AssertEqual + \ { + \ 'command': ale#Escape(g:ale_c_astyle_executable) + \ . ' --stdin=' + \ }, + \ ale#fixers#astyle#Fix(bufnr('')) + +Execute(The astyle callback should support cpp files): + " Because this file doesn't exist, no astylrc config + " exists near it. Therefore, project_options is empty. + call ale#test#SetFilename('../cpp_files/dummy.cpp') + set filetype=cpp " The test fails without this + + AssertEqual + \ { + \ 'command': ale#Escape(g:ale_cpp_astyle_executable) + \ . ' --stdin=' + \ }, + \ ale#fixers#astyle#Fix(bufnr('')) + +Execute(The astyle callback should support cpp files with option file set): + call ale#test#SetFilename('../cpp_files/dummy.cpp') + let g:ale_cpp_astyle_project_options = '.astylerc_cpp' + set filetype=cpp " The test fails without this + + AssertEqual + \ { + \ 'command': ale#Escape('invalidpp') + \ . ' --project=' . g:ale_cpp_astyle_project_options + \ . ' --stdin=' + \ }, + \ ale#fixers#astyle#Fix(bufnr('')) + +Execute(The astyle callback should return the correct default values with an option file set): + call ale#test#SetFilename('../c_files/testfile.c') + let g:ale_c_astyle_project_options = '.astylerc_c' + + AssertEqual + \ { + \ 'command': ale#Escape('xxxinvalid') + \ . ' --project=' . g:ale_c_astyle_project_options + \ . ' --stdin=' + \ }, + \ ale#fixers#astyle#Fix(bufnr('')) + +Execute(The astyle callback should find nearest default option file _astylrc): + call ale#test#SetFilename('../test_c_projects/makefile_project/subdir/file.c') + + AssertEqual + \ { + \ 'command': ale#Escape('xxxinvalid') + \ . ' --project=_astylerc' + \ . ' --stdin=' + \ }, + \ ale#fixers#astyle#Fix(bufnr('')) diff --git a/test/handler/test_markdownlint_handler.vader b/test/handler/test_markdownlint_handler.vader index db6acc66..8e9f77a0 100644 --- a/test/handler/test_markdownlint_handler.vader +++ b/test/handler/test_markdownlint_handler.vader @@ -4,21 +4,74 @@ Before: After: call ale#linter#Reset() -Execute(The Markdownlint handler should parse output correctly): +Execute(The Markdownlint handler should parse pre v0.19.0 output with single digit line correctly): AssertEqual \ [ \ { \ 'lnum': 1, - \ 'text': '(MD002/first-header-h1) First header should be a top level header [Expected: h1; Actual: h2]', - \ 'type': 'W' - \ }, - \ { - \ 'lnum': 298, - \ 'text': '(MD033/no-inline-html) Inline HTML [Element: p]', + \ 'code': 'MD013/line-length', + \ 'text': 'Line length [Expected: 80; Actual: 114]', \ 'type': 'W' \ } \ ], \ ale#handlers#markdownlint#Handle(0, [ - \ 'README.md: 1: MD002/first-header-h1 First header should be a top level header [Expected: h1; Actual: h2]', - \ 'README.md: 298: MD033/no-inline-html Inline HTML [Element: p]' + \ 'README.md: 1: MD013/line-length Line length [Expected: 80; Actual: 114]' + \ ]) + +Execute(The Markdownlint handler should parse pre v0.19.0 output with multi digit line correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 100, + \ 'code': 'MD013/line-length', + \ 'text': 'Line length [Expected: 80; Actual: 114]', + \ 'type': 'W' + \ } + \ ], + \ ale#handlers#markdownlint#Handle(0, [ + \ 'README.md: 100: MD013/line-length Line length [Expected: 80; Actual: 114]' + \ ]) + +Execute(The Markdownlint handler should parse post v0.19.0 output with single digit line correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'code': 'MD013/line-length', + \ 'text': 'Line length [Expected: 80; Actual: 114]', + \ 'type': 'W' + \ } + \ ], + \ ale#handlers#markdownlint#Handle(0, [ + \ 'README.md:1 MD013/line-length Line length [Expected: 80; Actual: 114]' + \ ]) + +Execute(The Markdownlint handler should parse post v0.19.0 output with multi digit line correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 100, + \ 'code': 'MD013/line-length', + \ 'text': 'Line length [Expected: 80; Actual: 114]', + \ 'type': 'W' + \ } + \ ], + \ ale#handlers#markdownlint#Handle(0, [ + \ 'README.md:100 MD013/line-length Line length [Expected: 80; Actual: 114]' + \ ]) + + +Execute(The Markdownlint handler should parse post v0.22.0 output with column correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 10, + \ 'col': 20, + \ 'code': 'MD013/line-length', + \ 'text': 'Line length [Expected: 80; Actual: 114]', + \ 'type': 'W' + \ } + \ ], + \ ale#handlers#markdownlint#Handle(0, [ + \ 'README.md:10:20 MD013/line-length Line length [Expected: 80; Actual: 114]' \ ]) diff --git a/test/test_c_projects/makefile_project/_astylerc b/test/test_c_projects/makefile_project/_astylerc new file mode 100644 index 00000000..e69de29b