From b8a90cf48861da046333a942b9be334bafa54782 Mon Sep 17 00:00:00 2001 From: Max Jacobson Date: Fri, 19 Sep 2025 19:14:52 -0400 Subject: [PATCH 1/2] Add --editor-mode flag when invoking rubocop Since RuboCop 1.61.0 (released in February 2024), RuboCop accepts an `--editor-mode` flag which improves editor integrations like ale. Some of RuboCop's auto-corrections can be surprising or annoying to run on save. When RuboCop is running via an LSP or when the `--editor-mode` flag is passed, it will understand that it is running in an editor, and it will hold off on making changes that might be surprising or annoying. For example, if I write ```ruby def call results = some_process end ``` This has an unused variable, and RuboCop will remove the unused variable when you run it. However, if you're in the middle of editing, you may not want it to remove that unused variable, because you may be about to add a usage of it. More context: - PR which introduced it: https://github.com/rubocop/rubocop/pull/12682 - Release notes for 1.61: https://github.com/rubocop/rubocop/releases/tag/v1.61.0 - Docs: https://docs.rubocop.org/rubocop/1.80/configuration.html#contextual This will be a breaking change for anyone who is running an old version of RuboCop, because the flag will not exist for them. If they would like to opt out of this change, they can set an option to omit the flag. I think this ought to be enabled by default so that people will get this benefit out of the box. In the meantime, I am opting into this behavior by setting this option: ```vim let g:ale_ruby_rubocop_options = "--editor-mode" ``` So I appreciate that this seam was already introduced. --- autoload/ale/fixers/rubocop.vim | 3 +++ doc/ale-ruby.txt | 12 ++++++++++++ test/fixers/test_rubocop_fixer_callback.vader | 18 +++++++++++++++--- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/autoload/ale/fixers/rubocop.vim b/autoload/ale/fixers/rubocop.vim index 5a1b79591a..3f7d1a776b 100644 --- a/autoload/ale/fixers/rubocop.vim +++ b/autoload/ale/fixers/rubocop.vim @@ -1,6 +1,7 @@ call ale#Set('ruby_rubocop_options', '') call ale#Set('ruby_rubocop_auto_correct_all', 0) call ale#Set('ruby_rubocop_executable', 'rubocop') +call ale#Set('ruby_rubocop_editor_mode', '1') " Rubocop fixer outputs diagnostics first and then the fixed " output. These are delimited by a "=======" string that we @@ -23,10 +24,12 @@ function! ale#fixers#rubocop#GetCommand(buffer) abort let l:executable = ale#Var(a:buffer, 'ruby_rubocop_executable') let l:options = ale#Var(a:buffer, 'ruby_rubocop_options') let l:auto_correct_all = ale#Var(a:buffer, 'ruby_rubocop_auto_correct_all') + let l:editor_mode= ale#Var(a:buffer, 'ruby_rubocop_editor_mode') return ale#ruby#EscapeExecutable(l:executable, 'rubocop') \ . (!empty(l:options) ? ' ' . l:options : '') \ . (l:auto_correct_all ? ' --auto-correct-all' : ' --auto-correct') + \ . (l:editor_mode ? ' --editor-mode' : '') \ . ' --force-exclusion --stdin %s' endfunction diff --git a/doc/ale-ruby.txt b/doc/ale-ruby.txt index 57f17dd528..3a2f368d51 100644 --- a/doc/ale-ruby.txt +++ b/doc/ale-ruby.txt @@ -187,6 +187,18 @@ g:ale_ruby_rubocop_auto_correct_all This variable can be changed to make rubocop to correct all offenses (unsafe). + *ale-options.ruby_rubocop_editor_mode* + *g:ale_ruby_rubocop_editor_mode* + *b:ale_ruby_rubocop_editor_mode* +ruby_rubocop_editor_mode +g:ale_ruby_rubocop_editor_mode + Type: |Number| + Default: `1` + + This variable can be changed to 0 to run rubocop without the `--editor-mode` + flag (if using RuboCop < v1.61.0). + + =============================================================================== ruby *ale-ruby-ruby* diff --git a/test/fixers/test_rubocop_fixer_callback.vader b/test/fixers/test_rubocop_fixer_callback.vader index f7b0eb60c1..cd44e915b0 100644 --- a/test/fixers/test_rubocop_fixer_callback.vader +++ b/test/fixers/test_rubocop_fixer_callback.vader @@ -20,7 +20,7 @@ Execute(The rubocop callback should return the correct default values): \ { \ 'process_with': 'ale#fixers#rubocop#PostProcess', \ 'command': ale#Escape(g:ale_ruby_rubocop_executable) - \ . ' --auto-correct --force-exclusion --stdin %s', + \ . ' --auto-correct --editor-mode --force-exclusion --stdin %s', \ }, \ ale#fixers#rubocop#Fix(bufnr('')) @@ -33,7 +33,7 @@ Execute(The rubocop callback should include custom rubocop options): \ 'process_with': 'ale#fixers#rubocop#PostProcess', \ 'command': ale#Escape(g:ale_ruby_rubocop_executable) \ . ' --except Lint/Debugger' - \ . ' --auto-correct --force-exclusion --stdin %s', + \ . ' --auto-correct --editor-mode --force-exclusion --stdin %s', \ }, \ ale#fixers#rubocop#Fix(bufnr('')) @@ -45,7 +45,7 @@ Execute(The rubocop callback should use auto-correct-all option when set): \ { \ 'process_with': 'ale#fixers#rubocop#PostProcess', \ 'command': ale#Escape(g:ale_ruby_rubocop_executable) - \ . ' --auto-correct-all --force-exclusion --stdin %s' + \ . ' --auto-correct-all --editor-mode --force-exclusion --stdin %s' \ }, \ ale#fixers#rubocop#Fix(bufnr('')) @@ -87,3 +87,15 @@ Execute(The rubocop post-processor should remove diagnostics content): \ ' ''forrest'',', \ ' ''run'']', \ ]) + +Execute(The rubocop callback should not use editor-mode option when configured not to): + let g:ale_ruby_rubocop_editor_mode = 0 + call ale#test#SetFilename('../test-files/ruby/with_config/dummy.rb') + + AssertEqual + \ { + \ 'process_with': 'ale#fixers#rubocop#PostProcess', + \ 'command': ale#Escape(g:ale_ruby_rubocop_executable) + \ . ' --auto-correct-all --force-exclusion --stdin %s' + \ }, + \ ale#fixers#rubocop#Fix(bufnr('')) From b4b228c86872723a025b7a1bb60cf61028c54ff9 Mon Sep 17 00:00:00 2001 From: Max Jacobson Date: Tue, 23 Sep 2025 18:17:06 -0400 Subject: [PATCH 2/2] Make this a non-breaking change This will detect the current rubocop version and auto-enable --editor-mode for newer version of rubocop without affecting users of older versions of rubocop. --- autoload/ale/fixers/rubocop.vim | 23 ++++++--- doc/ale-ruby.txt | 12 ----- test/fixers/test_rubocop_fixer_callback.vader | 47 +++++++++---------- 3 files changed, 38 insertions(+), 44 deletions(-) diff --git a/autoload/ale/fixers/rubocop.vim b/autoload/ale/fixers/rubocop.vim index 3f7d1a776b..c0d4315e53 100644 --- a/autoload/ale/fixers/rubocop.vim +++ b/autoload/ale/fixers/rubocop.vim @@ -1,7 +1,6 @@ call ale#Set('ruby_rubocop_options', '') call ale#Set('ruby_rubocop_auto_correct_all', 0) call ale#Set('ruby_rubocop_executable', 'rubocop') -call ale#Set('ruby_rubocop_editor_mode', '1') " Rubocop fixer outputs diagnostics first and then the fixed " output. These are delimited by a "=======" string that we @@ -20,11 +19,11 @@ function! ale#fixers#rubocop#PostProcess(buffer, output) abort return a:output[l:line :] endfunction -function! ale#fixers#rubocop#GetCommand(buffer) abort +function! ale#fixers#rubocop#GetCommand(buffer, version) abort let l:executable = ale#Var(a:buffer, 'ruby_rubocop_executable') let l:options = ale#Var(a:buffer, 'ruby_rubocop_options') let l:auto_correct_all = ale#Var(a:buffer, 'ruby_rubocop_auto_correct_all') - let l:editor_mode= ale#Var(a:buffer, 'ruby_rubocop_editor_mode') + let l:editor_mode = ale#semver#GTE(a:version, [1, 61, 0]) return ale#ruby#EscapeExecutable(l:executable, 'rubocop') \ . (!empty(l:options) ? ' ' . l:options : '') @@ -33,9 +32,21 @@ function! ale#fixers#rubocop#GetCommand(buffer) abort \ . ' --force-exclusion --stdin %s' endfunction -function! ale#fixers#rubocop#Fix(buffer) abort +function! ale#fixers#rubocop#GetCommandForVersion(buffer, version) abort return { - \ 'command': ale#fixers#rubocop#GetCommand(a:buffer), - \ 'process_with': 'ale#fixers#rubocop#PostProcess' + \ 'command': ale#fixers#rubocop#GetCommand(a:buffer, a:version), + \ 'process_with': 'ale#fixers#rubocop#PostProcess' \} endfunction + +function! ale#fixers#rubocop#Fix(buffer) abort + let l:executable = ale#Var(a:buffer, 'ruby_rubocop_executable') + let l:command = l:executable . ale#Pad('--version') + + return ale#semver#RunWithVersionCheck( + \ a:buffer, + \ l:executable, + \ l:command, + \ function('ale#fixers#rubocop#GetCommandForVersion'), + \) +endfunction diff --git a/doc/ale-ruby.txt b/doc/ale-ruby.txt index 3a2f368d51..57f17dd528 100644 --- a/doc/ale-ruby.txt +++ b/doc/ale-ruby.txt @@ -187,18 +187,6 @@ g:ale_ruby_rubocop_auto_correct_all This variable can be changed to make rubocop to correct all offenses (unsafe). - *ale-options.ruby_rubocop_editor_mode* - *g:ale_ruby_rubocop_editor_mode* - *b:ale_ruby_rubocop_editor_mode* -ruby_rubocop_editor_mode -g:ale_ruby_rubocop_editor_mode - Type: |Number| - Default: `1` - - This variable can be changed to 0 to run rubocop without the `--editor-mode` - flag (if using RuboCop < v1.61.0). - - =============================================================================== ruby *ale-ruby-ruby* diff --git a/test/fixers/test_rubocop_fixer_callback.vader b/test/fixers/test_rubocop_fixer_callback.vader index cd44e915b0..303aa30f00 100644 --- a/test/fixers/test_rubocop_fixer_callback.vader +++ b/test/fixers/test_rubocop_fixer_callback.vader @@ -1,53 +1,47 @@ Before: - Save g:ale_ruby_rubocop_executable - Save g:ale_ruby_rubocop_options - - " Use an invalid global executable, so we don't match it. - let g:ale_ruby_rubocop_executable = 'xxxinvalid' - let g:ale_ruby_rubocop_options = '' - - call ale#test#SetDirectory('/testplugin/test/fixers') + call ale#assert#SetUpFixerTest('ruby', 'rubocop') After: - Restore - - call ale#test#RestoreDirectory() + call ale#assert#TearDownFixerTest() Execute(The rubocop callback should return the correct default values): call ale#test#SetFilename('../test-files/ruby/dummy.rb') - AssertEqual + GivenCommandOutput ['1.61.0'] + + AssertFixer \ { \ 'process_with': 'ale#fixers#rubocop#PostProcess', \ 'command': ale#Escape(g:ale_ruby_rubocop_executable) \ . ' --auto-correct --editor-mode --force-exclusion --stdin %s', - \ }, - \ ale#fixers#rubocop#Fix(bufnr('')) + \ } Execute(The rubocop callback should include custom rubocop options): let g:ale_ruby_rubocop_options = '--except Lint/Debugger' call ale#test#SetFilename('../test-files/ruby/with_config/dummy.rb') - AssertEqual + GivenCommandOutput ['1.61.0'] + + AssertFixer \ { \ 'process_with': 'ale#fixers#rubocop#PostProcess', \ 'command': ale#Escape(g:ale_ruby_rubocop_executable) \ . ' --except Lint/Debugger' \ . ' --auto-correct --editor-mode --force-exclusion --stdin %s', - \ }, - \ ale#fixers#rubocop#Fix(bufnr('')) + \ } Execute(The rubocop callback should use auto-correct-all option when set): let g:ale_ruby_rubocop_auto_correct_all = 1 call ale#test#SetFilename('../test-files/ruby/with_config/dummy.rb') - AssertEqual + GivenCommandOutput ['1.61.0'] + + AssertFixer \ { \ 'process_with': 'ale#fixers#rubocop#PostProcess', \ 'command': ale#Escape(g:ale_ruby_rubocop_executable) \ . ' --auto-correct-all --editor-mode --force-exclusion --stdin %s' - \ }, - \ ale#fixers#rubocop#Fix(bufnr('')) + \ } Execute(The rubocop post-processor should remove diagnostics content): AssertEqual @@ -88,14 +82,15 @@ Execute(The rubocop post-processor should remove diagnostics content): \ ' ''run'']', \ ]) -Execute(The rubocop callback should not use editor-mode option when configured not to): - let g:ale_ruby_rubocop_editor_mode = 0 +Execute(The rubocop callback should not use editor-mode option with older versions): call ale#test#SetFilename('../test-files/ruby/with_config/dummy.rb') - AssertEqual + GivenCommandOutput ['1.59.0'] + + AssertFixer \ { \ 'process_with': 'ale#fixers#rubocop#PostProcess', \ 'command': ale#Escape(g:ale_ruby_rubocop_executable) - \ . ' --auto-correct-all --force-exclusion --stdin %s' - \ }, - \ ale#fixers#rubocop#Fix(bufnr('')) + \ . ' --auto-correct --force-exclusion --stdin %s' + \ } +