diff --git a/ale_linters/kotlin/ktlint.vim b/ale_linters/kotlin/ktlint.vim index f474e845..b8920aa7 100644 --- a/ale_linters/kotlin/ktlint.vim +++ b/ale_linters/kotlin/ktlint.vim @@ -1,54 +1,10 @@ " Author: Francis Agyapong " Description: Lint kotlin files using ktlint -call ale#Set('kotlin_ktlint_executable', 'ktlint') -call ale#Set('kotlin_ktlint_rulesets', []) -call ale#Set('kotlin_ktlint_format', 0) - - -function! ale_linters#kotlin#ktlint#GetCommand(buffer) abort - let l:executable = ale#Var(a:buffer, 'kotlin_ktlint_executable') - let l:file_path = expand('#' . a:buffer . ':p') - let l:options = '' - - " Formmatted content written to original file, not sure how to handle - " if ale#Var(a:buffer, 'kotlin_ktlint_format') - " let l:options = l:options . ' --format' - " endif - - for l:ruleset in ale#Var(a:buffer, 'kotlin_ktlint_rulesets') - let l:options = l:options . ' --ruleset ' . l:ruleset - endfor - - return l:executable . ' ' . l:options . ' ' . l:file_path -endfunction - -function! ale_linters#kotlin#ktlint#Handle(buffer, lines) abort - let l:message_pattern = '^\(.*\):\([0-9]\+\):\([0-9]\+\):\s\+\(.*\)' - let l:output = [] - - for l:match in ale#util#GetMatches(a:lines, l:message_pattern) - let l:line = l:match[2] + 0 - let l:column = l:match[3] + 0 - let l:text = l:match[4] - - let l:type = l:text =~? 'not a valid kotlin file' ? 'E' : 'W' - - call add(l:output, { - \ 'lnum': l:line, - \ 'col': l:column, - \ 'text': l:text, - \ 'type': l:type - \}) - endfor - - return l:output -endfunction - call ale#linter#Define('kotlin', { \ 'name': 'ktlint', \ 'executable': 'ktlint', -\ 'command_callback': 'ale_linters#kotlin#ktlint#GetCommand', -\ 'callback': 'ale_linters#kotlin#ktlint#Handle', +\ 'command_callback': 'ale#handlers#ktlint#GetCommand', +\ 'callback': 'ale#handlers#ktlint#Handle', \ 'lint_file': 1 \}) diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index a7422f22..ba6d43a0 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -270,6 +270,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['hcl', 'terraform'], \ 'description': 'Fix tf and hcl files with terraform fmt.', \ }, +\ 'ktlint': { +\ 'function': 'ale#fixers#ktlint#Fix', +\ 'suggested_filetypes': ['kt'], +\ 'description': 'Fix Kotlin files with ktlint.', +\ }, \} " Reset the function registry to the default entries. diff --git a/autoload/ale/fixers/ktlint.vim b/autoload/ale/fixers/ktlint.vim new file mode 100644 index 00000000..cb975d6c --- /dev/null +++ b/autoload/ale/fixers/ktlint.vim @@ -0,0 +1,9 @@ +" Author: Michael Phillips +" Description: Fix Kotlin files with ktlint. + +function! ale#fixers#ktlint#Fix(buffer) abort + return { + \ 'command': ale#handlers#ktlint#GetCommand(a:buffer) . ' --format', + \ 'read_temporary_file': 1, + \} +endfunction diff --git a/autoload/ale/handlers/ktlint.vim b/autoload/ale/handlers/ktlint.vim new file mode 100644 index 00000000..ad999485 --- /dev/null +++ b/autoload/ale/handlers/ktlint.vim @@ -0,0 +1,45 @@ +" Author: Michael Phillips +" Description: Handler functions for ktlint. + +call ale#Set('kotlin_ktlint_executable', 'ktlint') +call ale#Set('kotlin_ktlint_rulesets', []) +call ale#Set('kotlin_ktlint_options', '') + +function! ale#handlers#ktlint#GetCommand(buffer) abort + let l:executable = ale#Var(a:buffer, 'kotlin_ktlint_executable') + let l:options = ale#Var(a:buffer, 'kotlin_ktlint_options') + let l:rulesets = ale#handlers#ktlint#GetRulesets(a:buffer) + + return ale#Escape(l:executable) + \ . (empty(l:options) ? '' : ' ' . l:options) + \ . (empty(l:rulesets) ? '' : ' ' . l:rulesets) + \ . ' %t' +endfunction + +function! ale#handlers#ktlint#GetRulesets(buffer) abort + let l:rulesets = map(ale#Var(a:buffer, 'kotlin_ktlint_rulesets'), '''--ruleset '' . v:val') + + return join(l:rulesets, ' ') +endfunction + +function! ale#handlers#ktlint#Handle(buffer, lines) abort + let l:message_pattern = '^\(.*\):\([0-9]\+\):\([0-9]\+\):\s\+\(.*\)' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:message_pattern) + let l:line = l:match[2] + 0 + let l:column = l:match[3] + 0 + let l:text = l:match[4] + + let l:type = l:text =~? 'not a valid kotlin file' ? 'E' : 'W' + + call add(l:output, { + \ 'lnum': l:line, + \ 'col': l:column, + \ 'text': l:text, + \ 'type': l:type + \}) + endfor + + return l:output +endfunction diff --git a/doc/ale-kotlin.txt b/doc/ale-kotlin.txt index 9f9fd16e..4028531f 100644 --- a/doc/ale-kotlin.txt +++ b/doc/ale-kotlin.txt @@ -84,9 +84,17 @@ g:ale_kotlin_ktlint_rulesets *g:ale_kotlin_ktlint_rulesets* This list should contain paths to ruleset jars and/or strings of maven artifact triples. Example: > - let g:ale_kotlin_ktlint_rulesets = ['/path/to/custom-rulset.jar', + let g:ale_kotlin_ktlint_rulesets = ['/path/to/custom-ruleset.jar', 'com.ktlint.rulesets:mycustomrule:1.0.0'] +g:ale_kotlin_ktlint_options *g:ale_kotlin_ktlint_options* + Type: |String| + Default: `''` + + Additional options to pass to ktlint for both linting and fixing. Example: + > + let g:ale_kotlin_ktlint_options = '--android' + =============================================================================== languageserver *ale-kotlin-languageserver* diff --git a/test/fixers/test_ktlint_fixer_callback.vader b/test/fixers/test_ktlint_fixer_callback.vader new file mode 100644 index 00000000..47b37788 --- /dev/null +++ b/test/fixers/test_ktlint_fixer_callback.vader @@ -0,0 +1,44 @@ +Before: + Save g:ale_kotlin_ktlint_executable + Save g:ale_kotlin_ktlint_options + Save g:ale_kotlin_ktlint_rulesets + + " Use an invalid global executable, so we don't match it. + let g:ale_kotlin_ktlint_executable = 'xxxinvalid' + let g:ale_kotlin_ktlint_options = '' + let g:ale_kotlin_ktlint_rulesets = [] + + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The ktlint callback should return the correct default values): + call ale#test#SetFilename('../kotlin_files/testfile.kt') + + AssertEqual + \ { + \ 'command': ale#Escape('xxxinvalid') + \ . ' %t' + \ . ' --format', + \ 'read_temporary_file': 1, + \ }, + \ ale#fixers#ktlint#Fix(bufnr('')) + +Execute(The ktlint callback should include custom ktlint options): + let g:ale_kotlin_ktlint_options = "--android" + let g:ale_kotlin_ktlint_rulesets = ['/path/to/custom/ruleset.jar'] + call ale#test#SetFilename('../kotlin_files/testfile.kt') + + AssertEqual + \ { + \ 'command': ale#Escape('xxxinvalid') + \ . ' ' . g:ale_kotlin_ktlint_options + \ . ' --ruleset /path/to/custom/ruleset.jar' + \ . ' %t' + \ . ' --format', + \ 'read_temporary_file': 1, + \ }, + \ ale#fixers#ktlint#Fix(bufnr('')) diff --git a/test/handler/test_ktlint_handler.vader b/test/handler/test_ktlint_handler.vader new file mode 100644 index 00000000..f0d634e6 --- /dev/null +++ b/test/handler/test_ktlint_handler.vader @@ -0,0 +1,21 @@ +Before: + Save g:ale_kotlin_ktlint_rulesets + + let g:ale_kotlin_ktlint_rulesets = [] + +After: + Restore + +Execute(The ktlint handler method GetRulesets should properly parse custom rulesets): + let g:ale_kotlin_ktlint_rulesets = ['/path/to/custom/ruleset.jar', '/path/to/other/ruleset.jar'] + + AssertEqual + \ '--ruleset /path/to/custom/ruleset.jar --ruleset /path/to/other/ruleset.jar', + \ ale#handlers#ktlint#GetRulesets(bufnr('')) + +Execute(The ktlint handler method GetRulesets should return an empty string when no rulesets have been configured): + let g:ale_kotlin_ktlint_rulesets = [] + + AssertEqual + \ '', + \ ale#handlers#ktlint#GetRulesets(bufnr('')) diff --git a/test/kotlin_files/testfile.kt b/test/kotlin_files/testfile.kt new file mode 100644 index 00000000..e69de29b