From ab0e76dbd50dbe5bae08817baa56432889335b99 Mon Sep 17 00:00:00 2001 From: Eddie Lebow Date: Sun, 9 Jul 2017 10:48:04 -0400 Subject: [PATCH] Use rubocop's JSON output format (resolves #339) (#738) * Use rubocop's JSON output format (resolves #339) Rubocop's emacs formatter seems to have changed format in some not-so-ancient version. The JSON formatter should provide a more stable interface than parsing lines with a regex. The JSON formatter was introduced in mid-2013, so it should be safe to assume available in any reasonably-modern environment. The oldest currently-supported version of ruby (according to ruby-lang.org) was not supported by rubocop until 2014. * Rubocop: Use global function for GetType * Rubocop: Use scope prefix in GetType * Rubocop: Update command_callback test * Rubocop: add end_col to Handle --- ale_linters/ruby/rubocop.vim | 49 +++++++++++++------ .../test_rubocop_command_callback.vader | 6 +-- test/handler/test_rubocop_handler.vader | 10 ++-- 3 files changed, 41 insertions(+), 24 deletions(-) diff --git a/ale_linters/ruby/rubocop.vim b/ale_linters/ruby/rubocop.vim index 9b77f896..1756f450 100644 --- a/ale_linters/ruby/rubocop.vim +++ b/ale_linters/ruby/rubocop.vim @@ -1,5 +1,5 @@ -" Author: ynonp - https://github.com/ynonp -" Description: rubocop for Ruby files +" Author: ynonp - https://github.com/ynonp, Eddie Lebow https://github.com/elebow +" Description: RuboCop, a code style analyzer for Ruby files function! ale_linters#ruby#rubocop#GetCommand(buffer) abort let l:executable = ale#handlers#rubocop#GetExecutable(a:buffer) @@ -8,34 +8,51 @@ function! ale_linters#ruby#rubocop#GetCommand(buffer) abort \ : '' return ale#Escape(l:executable) . l:exec_args - \ . ' --format emacs --force-exclusion ' + \ . ' --format json --force-exclusion ' \ . ale#Var(a:buffer, 'ruby_rubocop_options') \ . ' --stdin ' . bufname(a:buffer) endfunction function! ale_linters#ruby#rubocop#Handle(buffer, lines) abort - " Matches patterns line the following: - " - " :83:29: C: Prefer single-quoted strings when you don't - " need string interpolation or special symbols. - let l:pattern = '\v:(\d+):(\d+): (.): (.+)' + if len(a:lines) == 0 + return [] + endif + + let l:errors = json_decode(a:lines[0]) + let l:output = [] - for l:match in ale#util#GetMatches(a:lines, l:pattern) - let l:text = l:match[4] - let l:type = l:match[3] - + for l:error in l:errors['files'][0]['offenses'] + let l:start_col = l:error['location']['column'] + 0 call add(l:output, { - \ 'lnum': l:match[1] + 0, - \ 'col': l:match[2] + 0, - \ 'text': l:text, - \ 'type': index(['F', 'E'], l:type) != -1 ? 'E' : 'W', + \ 'lnum': l:error['location']['line'] + 0, + \ 'col': l:start_col, + \ 'end_col': l:start_col + l:error['location']['length'] - 1, + \ 'text': l:error['message'], + \ 'type': ale_linters#ruby#rubocop#GetType(l:error['severity']), \}) endfor return l:output endfunction +function! ale_linters#ruby#rubocop#GetType(severity) abort + if a:severity ==? 'refactor' + return 'W' + elseif a:severity ==? 'convention' + return 'W' + elseif a:severity ==? 'warning' + return 'W' + elseif a:severity ==? 'error' + return 'E' + elseif a:severity ==? 'fatal' + return 'E' + else + echo 'Rubocop offense type unrecognized by ALE: ' + a:severity + return '' + endif +endfunction + call ale#linter#Define('ruby', { \ 'name': 'rubocop', \ 'executable_callback': 'ale#handlers#rubocop#GetExecutable', diff --git a/test/command_callback/test_rubocop_command_callback.vader b/test/command_callback/test_rubocop_command_callback.vader index cda0c230..db151800 100644 --- a/test/command_callback/test_rubocop_command_callback.vader +++ b/test/command_callback/test_rubocop_command_callback.vader @@ -3,17 +3,17 @@ Before: Execute(Executable should default to rubocop): AssertEqual - \ '''rubocop'' --format emacs --force-exclusion --stdin ''dummy.py''', + \ '''rubocop'' --format json --force-exclusion --stdin ''dummy.py''', \ ale_linters#ruby#rubocop#GetCommand(bufnr('')) Execute(Should be able to set a custom executable): let g:ale_ruby_rubocop_executable = 'bin/rubocop' AssertEqual - \ '''bin/rubocop'' --format emacs --force-exclusion --stdin ''dummy.py''', + \ '''bin/rubocop'' --format json --force-exclusion --stdin ''dummy.py''', \ ale_linters#ruby#rubocop#GetCommand(bufnr('')) Execute(Setting bundle appends 'exec rubocop'): let g:ale_ruby_rubocop_executable = 'path to/bundle' AssertEqual - \ '''path to/bundle'' exec rubocop --format emacs --force-exclusion --stdin ''dummy.py''', + \ '''path to/bundle'' exec rubocop --format json --force-exclusion --stdin ''dummy.py''', \ ale_linters#ruby#rubocop#GetCommand(bufnr('')) diff --git a/test/handler/test_rubocop_handler.vader b/test/handler/test_rubocop_handler.vader index 8fa8374a..396ca0ed 100644 --- a/test/handler/test_rubocop_handler.vader +++ b/test/handler/test_rubocop_handler.vader @@ -6,34 +6,34 @@ Execute(The rubocop handler should parse lines correctly): \ { \ 'lnum': 83, \ 'col': 29, + \ 'end_col': 35, \ 'text': 'Prefer single-quoted strings...', \ 'type': 'W', \ }, \ { \ 'lnum': 12, \ 'col': 2, + \ 'end_col': 2, \ 'text': 'Some error', \ 'type': 'E', \ }, \ { \ 'lnum': 10, \ 'col': 5, + \ 'end_col': 12, \ 'text': 'Regular warning', \ 'type': 'W', \ }, \ { \ 'lnum': 11, \ 'col': 1, + \ 'end_col': 1, \ 'text': 'Another error', \ 'type': 'E', \ }, \ ], \ ale_linters#ruby#rubocop#Handle(347, [ - \ 'This line should be ignored completely', - \ 'whatever:83:29: C: Prefer single-quoted strings...', - \ 'whatever:12:2: F: Some error', - \ 'whatever:10:5: W: Regular warning', - \ 'whatever:11:1: E: Another error', + \ '{"metadata":{"rubocop_version":"0.47.1","ruby_engine":"ruby","ruby_version":"2.1.5","ruby_patchlevel":"273","ruby_platform":"x86_64-linux-gnu"},"files":[{"path":"my_great_file.rb","offenses":[{"severity":"convention","message":"Prefer single-quoted strings...","cop_name":"Style/SomeCop","corrected":false,"location":{"line":83,"column":29,"length":7}},{"severity":"fatal","message":"Some error","cop_name":"Style/SomeOtherCop","corrected":false,"location":{"line":12,"column":2,"length":1}},{"severity":"warning","message":"Regular warning","cop_name":"Style/WarningCop","corrected":false,"location":{"line":10,"column":5,"length":8}},{"severity":"error","message":"Another error","cop_name":"Style/SpaceBeforeBlockBraces","corrected":false,"location":{"line":11,"column":1,"length":1}}]}],"summary":{"offense_count":4,"target_file_count":1,"inspected_file_count":1}}' \ ]) After: