mirror of
https://github.com/dense-analysis/ale.git
synced 2025-12-07 05:04:28 +08:00
Add initial ameba (crystal linter) support (#2174)
* Add initial ameba (crystal linter) support Note that this depends on saved file as `ameba` does not have STDIN support * Fix formatting of crystal linter documentation * Add tests for ameba executable customization
This commit is contained in:
@@ -115,7 +115,7 @@ formatting.
|
|||||||
| CloudFormation | [cfn-python-lint](https://github.com/awslabs/cfn-python-lint) |
|
| CloudFormation | [cfn-python-lint](https://github.com/awslabs/cfn-python-lint) |
|
||||||
| CMake | [cmakelint](https://github.com/richq/cmake-lint), [cmake-format](https://github.com/cheshirekow/cmake_format) |
|
| CMake | [cmakelint](https://github.com/richq/cmake-lint), [cmake-format](https://github.com/cheshirekow/cmake_format) |
|
||||||
| CoffeeScript | [coffee](http://coffeescript.org/), [coffeelint](https://www.npmjs.com/package/coffeelint) |
|
| CoffeeScript | [coffee](http://coffeescript.org/), [coffeelint](https://www.npmjs.com/package/coffeelint) |
|
||||||
| Crystal | [crystal](https://crystal-lang.org/) !! |
|
| Crystal | [ameba](https://github.com/veelenga/ameba) !!, [crystal](https://crystal-lang.org/) !! |
|
||||||
| CSS | [csslint](http://csslint.net/), [prettier](https://github.com/prettier/prettier), [stylelint](https://github.com/stylelint/stylelint) |
|
| CSS | [csslint](http://csslint.net/), [prettier](https://github.com/prettier/prettier), [stylelint](https://github.com/stylelint/stylelint) |
|
||||||
| Cucumber | [cucumber](https://cucumber.io/) |
|
| Cucumber | [cucumber](https://cucumber.io/) |
|
||||||
| Cython (pyrex filetype) | [cython](http://cython.org/) |
|
| Cython (pyrex filetype) | [cython](http://cython.org/) |
|
||||||
|
|||||||
56
ale_linters/crystal/ameba.vim
Normal file
56
ale_linters/crystal/ameba.vim
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
" Author: Harrison Bachrach - https://github.com/HarrisonB
|
||||||
|
" Description: Ameba, a linter for crystal files
|
||||||
|
|
||||||
|
call ale#Set('crystal_ameba_executable', 'bin/ameba')
|
||||||
|
|
||||||
|
function! ale_linters#crystal#ameba#GetCommand(buffer) abort
|
||||||
|
let l:executable = ale#Var(a:buffer, 'crystal_ameba_executable')
|
||||||
|
|
||||||
|
return ale#Escape(l:executable)
|
||||||
|
\ . ' --format json '
|
||||||
|
\ . ale#Escape(expand('#' . a:buffer . ':p'))
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
call ale#linter#Define('crystal', {
|
||||||
|
\ 'name': 'ameba',
|
||||||
|
\ 'executable_callback': ale#VarFunc('crystal_ameba_executable'),
|
||||||
|
\ 'command_callback': 'ale_linters#crystal#ameba#GetCommand',
|
||||||
|
\ 'callback': 'ale_linters#crystal#ameba#HandleAmebaOutput',
|
||||||
|
\})
|
||||||
|
|
||||||
|
" Handle output from ameba
|
||||||
|
function! ale_linters#crystal#ameba#HandleAmebaOutput(buffer, lines) abort
|
||||||
|
if len(a:lines) == 0
|
||||||
|
return []
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:errors = ale#util#FuzzyJSONDecode(a:lines[0], {})
|
||||||
|
|
||||||
|
if !has_key(l:errors, 'summary')
|
||||||
|
\|| l:errors['summary']['issues_count'] == 0
|
||||||
|
\|| empty(l:errors['sources'])
|
||||||
|
return []
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:output = []
|
||||||
|
|
||||||
|
for l:error in l:errors['sources'][0]['issues']
|
||||||
|
let l:start_col = str2nr(l:error['location']['column'])
|
||||||
|
let l:end_col = str2nr(l:error['end_location']['column'])
|
||||||
|
|
||||||
|
if !l:end_col
|
||||||
|
let l:end_col = l:start_col + 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
call add(l:output, {
|
||||||
|
\ 'lnum': str2nr(l:error['location']['line']),
|
||||||
|
\ 'col': l:start_col,
|
||||||
|
\ 'end_col': l:end_col,
|
||||||
|
\ 'code': l:error['rule_name'],
|
||||||
|
\ 'text': l:error['message'],
|
||||||
|
\ 'type': 'W',
|
||||||
|
\})
|
||||||
|
endfor
|
||||||
|
|
||||||
|
return l:output
|
||||||
|
endfunction
|
||||||
@@ -436,7 +436,7 @@ Notes:
|
|||||||
* CloudFormation: `cfn-python-lint`
|
* CloudFormation: `cfn-python-lint`
|
||||||
* CMake: `cmakelint`, `cmake-format`
|
* CMake: `cmakelint`, `cmake-format`
|
||||||
* CoffeeScript: `coffee`, `coffeelint`
|
* CoffeeScript: `coffee`, `coffeelint`
|
||||||
* Crystal: `crystal`!!
|
* Crystal: `ameba`!!, `crystal`!!
|
||||||
* CSS: `csslint`, `prettier`, `stylelint`
|
* CSS: `csslint`, `prettier`, `stylelint`
|
||||||
* Cucumber: `cucumber`
|
* Cucumber: `cucumber`
|
||||||
* Cython (pyrex filetype): `cython`
|
* Cython (pyrex filetype): `cython`
|
||||||
|
|||||||
20
test/command_callback/test_ameba_command_callback.vader
Normal file
20
test/command_callback/test_ameba_command_callback.vader
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
Before:
|
||||||
|
call ale#assert#SetUpLinterTest('crystal', 'ameba')
|
||||||
|
call ale#test#SetFilename('dummy.cr')
|
||||||
|
|
||||||
|
let g:ale_crystal_ameba_executable = 'bin/ameba'
|
||||||
|
|
||||||
|
After:
|
||||||
|
call ale#assert#TearDownLinterTest()
|
||||||
|
|
||||||
|
Execute(Executable should default to bin/ameba):
|
||||||
|
AssertLinter 'bin/ameba', ale#Escape('bin/ameba')
|
||||||
|
\ . ' --format json '
|
||||||
|
\ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.cr'))
|
||||||
|
|
||||||
|
Execute(Should be able to set a custom executable):
|
||||||
|
let g:ale_crystal_ameba_executable = 'ameba'
|
||||||
|
|
||||||
|
AssertLinter 'ameba' , ale#Escape('ameba')
|
||||||
|
\ . ' --format json '
|
||||||
|
\ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.cr'))
|
||||||
44
test/handler/test_ameba_handler.vader
Normal file
44
test/handler/test_ameba_handler.vader
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
Before:
|
||||||
|
runtime ale_linters/crystal/ameba.vim
|
||||||
|
|
||||||
|
After:
|
||||||
|
unlet! g:lines
|
||||||
|
call ale#linter#Reset()
|
||||||
|
|
||||||
|
Execute(The ameba handler should parse lines correctly):
|
||||||
|
AssertEqual
|
||||||
|
\ [
|
||||||
|
\ {
|
||||||
|
\ 'lnum': 24,
|
||||||
|
\ 'col': 28,
|
||||||
|
\ 'end_col': 29,
|
||||||
|
\ 'text': 'Trailing whitespace detected',
|
||||||
|
\ 'code': 'Layout/TrailingWhitespace',
|
||||||
|
\ 'type': 'W',
|
||||||
|
\ },
|
||||||
|
\ ],
|
||||||
|
\ ale_linters#crystal#ameba#HandleAmebaOutput(123, [
|
||||||
|
\ '{"sources":[{"path":"my_file_with_issues.cr","issues":[{"rule_name":"Layout/TrailingWhitespace","message":"Trailing whitespace detected","location":{"line":24,"column":28},"end_location":{"line":null,"column":null}}]},{"path":"my_file_without_issues.cr","issues":[]}],"metadata":{"ameba_version":"0.8.1","crystal_version":"0.26.1"},"summary":{"target_sources_count":2,"issues_count":1}}'
|
||||||
|
\ ])
|
||||||
|
|
||||||
|
Execute(The ameba handler should handle when files are checked and no offenses are found):
|
||||||
|
AssertEqual
|
||||||
|
\ [],
|
||||||
|
\ ale_linters#crystal#ameba#HandleAmebaOutput(123, [
|
||||||
|
\ '{"sources":[{"path":"my_file_with_issues.cr",issues":[]},{"path":"my_file_without_issues.cr",issues":[]}],"metadata":{ameba_version":"0.8.1",crystal_version":"0.26.1"},"summary":{target_sources_count":2,issues_count":0}}'
|
||||||
|
\ ])
|
||||||
|
|
||||||
|
Execute(The ameba handler should handle when no files are checked):
|
||||||
|
AssertEqual
|
||||||
|
\ [],
|
||||||
|
\ ale_linters#crystal#ameba#HandleAmebaOutput(123, [
|
||||||
|
\ '{"sources":[],"metadata":{ameba_version":"0.8.1",crystal_version":"0.26.1"},"summary":{target_sources_count":0,issues_count":0}}'
|
||||||
|
\ ])
|
||||||
|
|
||||||
|
Execute(The ameba handler should handle blank output without any errors):
|
||||||
|
AssertEqual
|
||||||
|
\ [],
|
||||||
|
\ ale_linters#crystal#ameba#HandleAmebaOutput(123, ['{}'])
|
||||||
|
AssertEqual
|
||||||
|
\ [],
|
||||||
|
\ ale_linters#crystal#ameba#HandleAmebaOutput(123, [])
|
||||||
Reference in New Issue
Block a user