Skip to content

Commit 8b194c7

Browse files
committed
Change our internal RuboCop integration identifier
1 parent 77337ef commit 8b194c7

File tree

9 files changed

+107
-30
lines changed

9 files changed

+107
-30
lines changed

lib/ruby_lsp/global_state.rb

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,20 @@ def apply_options(options)
9494
@workspace_uri = URI(workspace_uri) if workspace_uri
9595

9696
specified_formatter = options.dig(:initializationOptions, :formatter)
97+
rubocop_has_addon = Gem::Requirement.new(">= 1.70.0").satisfied_by?(Gem::Version.new(::RuboCop::Version::STRING))
9798

9899
if specified_formatter
99100
@formatter = specified_formatter
100101

101102
if specified_formatter != "auto"
102103
notifications << Notification.window_log_message("Using formatter specified by user: #{@formatter}")
103104
end
105+
106+
# If the user had originally configured to use `rubocop`, but their version doesn't provide the add-on yet,
107+
# fallback to the internal integration
108+
if specified_formatter == "rubocop" && !rubocop_has_addon
109+
@formatter = "rubocop_internal"
110+
end
104111
end
105112

106113
if @formatter == "auto"
@@ -109,6 +116,23 @@ def apply_options(options)
109116
end
110117

111118
specified_linters = options.dig(:initializationOptions, :linters)
119+
120+
if specified_formatter == "rubocop" || specified_linters&.include?("rubocop")
121+
notifications << Notification.window_log_message(<<~MESSAGE, type: Constant::MessageType::WARNING)
122+
Formatter is configured to be `rubocop`. As of RuboCop v1.70.0, this identifier activates the add-on
123+
implemented in the rubocop gem itself instead of the internal integrations provided by the Ruby LSP.
124+
125+
If you wish to use the internal integration, please configure the formatter as `rubocop_internal`.
126+
MESSAGE
127+
end
128+
129+
# If the user had originally configured to use `rubocop`, but their version doesn't provide the add-on yet,
130+
# fallback to the internal integration
131+
if specified_linters&.include?("rubocop") && !rubocop_has_addon
132+
specified_linters.delete("rubocop")
133+
specified_linters << "rubocop_internal"
134+
end
135+
112136
@linters = specified_linters || detect_linters(direct_dependencies, all_dependencies)
113137

114138
notifications << if specified_linters
@@ -185,13 +209,13 @@ def supports_watching_files
185209
sig { params(direct_dependencies: T::Array[String], all_dependencies: T::Array[String]).returns(String) }
186210
def detect_formatter(direct_dependencies, all_dependencies)
187211
# NOTE: Intentionally no $ at end, since we want to match rubocop-shopify, etc.
188-
return "rubocop" if direct_dependencies.any?(/^rubocop/)
212+
return "rubocop_internal" if direct_dependencies.any?(/^rubocop/)
189213

190214
syntax_tree_is_direct_dependency = direct_dependencies.include?("syntax_tree")
191215
return "syntax_tree" if syntax_tree_is_direct_dependency
192216

193217
rubocop_is_transitive_dependency = all_dependencies.include?("rubocop")
194-
return "rubocop" if dot_rubocop_yml_present && rubocop_is_transitive_dependency
218+
return "rubocop_internal" if dot_rubocop_yml_present && rubocop_is_transitive_dependency
195219

196220
"none"
197221
end
@@ -203,7 +227,7 @@ def detect_linters(dependencies, all_dependencies)
203227
linters = []
204228

205229
if dependencies.any?(/^rubocop/) || (all_dependencies.include?("rubocop") && dot_rubocop_yml_present)
206-
linters << "rubocop"
230+
linters << "rubocop_internal"
207231
end
208232

209233
linters

lib/ruby_lsp/server.rb

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ def run_initialized
338338
unless @setup_error
339339
if defined?(Requests::Support::RuboCopFormatter)
340340
begin
341-
@global_state.register_formatter("rubocop", Requests::Support::RuboCopFormatter.new)
341+
@global_state.register_formatter("rubocop_internal", Requests::Support::RuboCopFormatter.new)
342342
rescue ::RuboCop::Error => e
343343
# The user may have provided unknown config switches in .rubocop or
344344
# is trying to load a non-existent config file.
@@ -1040,7 +1040,7 @@ def handle_rubocop_config_change(uri)
10401040
return unless defined?(Requests::Support::RuboCopFormatter)
10411041

10421042
send_log_message("Reloading RuboCop since #{uri} changed")
1043-
@global_state.register_formatter("rubocop", Requests::Support::RuboCopFormatter.new)
1043+
@global_state.register_formatter("rubocop_internal", Requests::Support::RuboCopFormatter.new)
10441044

10451045
# Clear all existing diagnostics since the config changed. This has to happen under a mutex because the `state`
10461046
# hash cannot be mutated during iteration or that will throw an error
@@ -1211,16 +1211,15 @@ def end_progress(id)
12111211
sig { void }
12121212
def check_formatter_is_available
12131213
return if @setup_error
1214-
# Warn of an unavailable `formatter` setting, e.g. `rubocop` on a project which doesn't have RuboCop.
1215-
# Syntax Tree will always be available via Ruby LSP so we don't need to check for it.
1216-
return unless @global_state.formatter == "rubocop"
1214+
# Warn of an unavailable `formatter` setting, e.g. `rubocop_internal` on a project which doesn't have RuboCop.
1215+
return unless @global_state.formatter == "rubocop_internal"
12171216

12181217
unless defined?(RubyLsp::Requests::Support::RuboCopRunner)
12191218
@global_state.formatter = "none"
12201219

12211220
send_message(
12221221
Notification.window_show_message(
1223-
"Ruby LSP formatter is set to `rubocop` but RuboCop was not found in the Gemfile or gemspec.",
1222+
"Ruby LSP formatter is set to `rubocop_internal` but RuboCop was not found in the Gemfile or gemspec.",
12241223
type: Constant::MessageType::ERROR,
12251224
),
12261225
)

test/global_state_test.rb

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,14 @@ def test_apply_option_selects_formatter
6363
def test_applying_auto_formatter_invokes_detection
6464
state = GlobalState.new
6565
state.apply_options({ initializationOptions: { formatter: "auto" } })
66-
assert_equal("rubocop", state.formatter)
66+
assert_equal("rubocop_internal", state.formatter)
6767
end
6868

6969
def test_applying_auto_formatter_with_rubocop_extension
7070
state = GlobalState.new
7171
stub_direct_dependencies("rubocop-rails" => "1.2.3")
7272
state.apply_options({ initializationOptions: { formatter: "auto" } })
73-
assert_equal("rubocop", state.formatter)
73+
assert_equal("rubocop_internal", state.formatter)
7474
end
7575

7676
def test_applying_auto_formatter_with_rubocop_as_transitive_dependency
@@ -82,7 +82,7 @@ def test_applying_auto_formatter_with_rubocop_as_transitive_dependency
8282

8383
state.apply_options({ initializationOptions: { formatter: "auto" } })
8484

85-
assert_equal("rubocop", state.formatter)
85+
assert_equal("rubocop_internal", state.formatter)
8686
end
8787

8888
def test_applying_auto_formatter_with_rubocop_as_transitive_dependency_without_config
@@ -155,15 +155,15 @@ def test_linter_specification
155155
initializationOptions: { linters: ["rubocop", "brakeman"] },
156156
})
157157

158-
assert_equal(["rubocop", "brakeman"], state.instance_variable_get(:@linters))
158+
assert_equal(["brakeman", "rubocop_internal"], state.instance_variable_get(:@linters))
159159
end
160160

161161
def test_linter_auto_detection
162162
stub_direct_dependencies("rubocop" => "1.2.3")
163163
state = GlobalState.new
164164
state.apply_options({})
165165

166-
assert_equal(["rubocop"], state.instance_variable_get(:@linters))
166+
assert_equal(["rubocop_internal"], state.instance_variable_get(:@linters))
167167
end
168168

169169
def test_specifying_empty_linters
@@ -185,7 +185,7 @@ def test_linter_auto_detection_with_rubocop_as_transitive_dependency
185185

186186
state.apply_options({})
187187

188-
assert_includes(state.instance_variable_get(:@linters), "rubocop")
188+
assert_includes(state.instance_variable_get(:@linters), "rubocop_internal")
189189
end
190190

191191
def test_type_checker_is_detected_based_on_transitive_sorbet_static
@@ -249,6 +249,58 @@ def test_enabled_feature_always_returns_true_if_all_are_enabled
249249
assert(state.enabled_feature?(:whatever))
250250
end
251251

252+
def test_notifies_the_user_when_using_rubocop_addon_through_linters
253+
::RuboCop::Version.const_set(:STRING, "1.70.0")
254+
255+
state = GlobalState.new
256+
notifications = state.apply_options({ initializationOptions: { linters: ["rubocop"] } })
257+
258+
log = notifications.find do |n|
259+
n.method == "window/logMessage" && T.unsafe(n.params).message.include?("RuboCop v1.70.0")
260+
end
261+
refute_nil(log)
262+
assert_equal(["rubocop"], state.instance_variable_get(:@linters))
263+
end
264+
265+
def test_notifies_the_user_when_using_rubocop_addon_through_formatter
266+
::RuboCop::Version.const_set(:STRING, "1.70.0")
267+
268+
state = GlobalState.new
269+
notifications = state.apply_options({ initializationOptions: { formatter: "rubocop" } })
270+
271+
log = notifications.find do |n|
272+
n.method == "window/logMessage" && T.unsafe(n.params).message.include?("RuboCop v1.70.0")
273+
end
274+
refute_nil(log)
275+
assert_equal("rubocop", state.formatter)
276+
end
277+
278+
def test_falls_back_to_internal_integration_for_linters_if_rubocop_has_no_addon
279+
::RuboCop::Version.const_set(:STRING, "1.68.0")
280+
281+
state = GlobalState.new
282+
notifications = state.apply_options({ initializationOptions: { linters: ["rubocop"] } })
283+
284+
log = notifications.find do |n|
285+
n.method == "window/logMessage" && T.unsafe(n.params).message.include?("RuboCop v1.70.0")
286+
end
287+
refute_nil(log)
288+
assert_equal(["rubocop_internal"], state.instance_variable_get(:@linters))
289+
end
290+
291+
def test_falls_back_to_internal_integration_for_formatters_if_rubocop_has_no_addon
292+
::RuboCop::Version.const_set(:STRING, "1.68.0")
293+
294+
state = GlobalState.new
295+
notifications = state.apply_options({ initializationOptions: { formatter: "rubocop" } })
296+
297+
log = notifications.find do |n|
298+
n.method == "window/logMessage" && T.unsafe(n.params).message.include?("RuboCop v1.70.0")
299+
end
300+
refute_nil(log)
301+
assert_equal("rubocop_internal", state.formatter)
302+
end
303+
252304
private
253305

254306
def stub_direct_dependencies(dependencies)

test/requests/code_actions_formatting_test.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ def assert_fixtures_match(name, diagnostic_code, code_action_title)
7070
def assert_corrects_to_expected(diagnostic_code, code_action_title, source, expected)
7171
global_state = RubyLsp::GlobalState.new
7272
global_state.apply_options({
73-
initializationOptions: { linters: ["rubocop"] },
73+
initializationOptions: { linters: ["rubocop_internal"] },
7474
})
7575
global_state.register_formatter(
76-
"rubocop",
76+
"rubocop_internal",
7777
RubyLsp::Requests::Support::RuboCopFormatter.new,
7878
)
7979

test/requests/diagnostics_expectations_test.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ class DiagnosticsExpectationsTest < ExpectationsTestRunner
1010
def run_expectations(source)
1111
result = T.let(nil, T.nilable(T::Array[RubyLsp::Interface::Diagnostic]))
1212
@global_state.apply_options({
13-
initializationOptions: { linters: ["rubocop"] },
13+
initializationOptions: { linters: ["rubocop_internal"] },
1414
})
1515
@global_state.register_formatter(
16-
"rubocop",
16+
"rubocop_internal",
1717
RubyLsp::Requests::Support::RuboCopFormatter.new,
1818
)
1919

test/requests/diagnostics_test.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ def setup
88
@uri = URI("file:///fake/file.rb")
99
@global_state = RubyLsp::GlobalState.new
1010
@global_state.apply_options({
11-
initializationOptions: { linters: ["rubocop"] },
11+
initializationOptions: { linters: ["rubocop_internal"] },
1212
})
1313
@global_state.register_formatter(
14-
"rubocop",
14+
"rubocop_internal",
1515
RubyLsp::Requests::Support::RuboCopFormatter.new,
1616
)
1717
end
@@ -52,7 +52,7 @@ def foo
5252
klass = RubyLsp::Requests::Support::RuboCopFormatter
5353
RubyLsp::Requests::Support.send(:remove_const, :RuboCopFormatter)
5454

55-
@global_state.instance_variable_get(:@supported_formatters).delete("rubocop")
55+
@global_state.instance_variable_get(:@supported_formatters).delete("rubocop_internal")
5656

5757
diagnostics = T.must(RubyLsp::Requests::Diagnostics.new(@global_state, document).perform)
5858

test/requests/formatting_expectations_test.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ class FormattingExpectationsTest < ExpectationsTestRunner
88
expectations_tests RubyLsp::Requests::Formatting, "formatting"
99

1010
def run_expectations(source)
11-
@global_state.formatter = "rubocop"
11+
@global_state.formatter = "rubocop_internal"
1212
@global_state.register_formatter(
13-
"rubocop",
13+
"rubocop_internal",
1414
RubyLsp::Requests::Support::RuboCopFormatter.new,
1515
)
1616
document = RubyLsp::RubyDocument.new(

test/server_test.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ def test_server_info_includes_version
171171
end
172172

173173
def test_server_info_includes_formatter
174-
@server.global_state.expects(:formatter).twice.returns("rubocop")
174+
@server.global_state.expects(:formatter).twice.returns("rubocop_internal")
175175
capture_subprocess_io do
176176
@server.process_message({
177177
id: 1,
@@ -185,7 +185,7 @@ def test_server_info_includes_formatter
185185

186186
result = find_message(RubyLsp::Result, id: 1)
187187
hash = JSON.parse(result.response.to_json)
188-
assert_equal("rubocop", hash.dig("formatter"))
188+
assert_equal("rubocop_internal", hash.dig("formatter"))
189189
end
190190

191191
def test_initialized_recovers_from_indexing_failures
@@ -375,10 +375,10 @@ def test_handles_invalid_configuration
375375
def test_shows_error_if_formatter_set_to_rubocop_but_rubocop_not_available
376376
capture_subprocess_io do
377377
@server.process_message(id: 1, method: "initialize", params: {
378-
initializationOptions: { formatter: "rubocop" },
378+
initializationOptions: { formatter: "rubocop_internal" },
379379
})
380380

381-
@server.global_state.register_formatter("rubocop", RubyLsp::Requests::Support::RuboCopFormatter.new)
381+
@server.global_state.register_formatter("rubocop_internal", RubyLsp::Requests::Support::RuboCopFormatter.new)
382382
with_uninstalled_rubocop do
383383
@server.process_message({ method: "initialized" })
384384
end
@@ -388,7 +388,7 @@ def test_shows_error_if_formatter_set_to_rubocop_but_rubocop_not_available
388388
notification = find_message(RubyLsp::Notification, "window/showMessage")
389389

390390
assert_equal(
391-
"Ruby LSP formatter is set to `rubocop` but RuboCop was not found in the Gemfile or gemspec.",
391+
"Ruby LSP formatter is set to `rubocop_internal` but RuboCop was not found in the Gemfile or gemspec.",
392392
T.cast(notification.params, RubyLsp::Interface::ShowMessageParams).message,
393393
)
394394
end

vscode/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@
373373
"enum": [
374374
"auto",
375375
"rubocop",
376+
"rubocop_internal",
376377
"syntax_tree",
377378
"standard",
378379
"rubyfmt",
@@ -381,6 +382,7 @@
381382
"enumDescriptions": [
382383
"Automatically detect formatter",
383384
"RuboCop",
385+
"Ruby LSP RuboCop integration",
384386
"Syntax Tree",
385387
"Standard (supported by community addon)",
386388
"Rubyfmt (supported by community addon)",
@@ -393,7 +395,7 @@
393395
"type": "array",
394396
"examples": [
395397
[
396-
"rubocop"
398+
"rubocop_internal"
397399
]
398400
],
399401
"default": null

0 commit comments

Comments
 (0)