Skip to content

Commit 8e1bc9e

Browse files
committed
Refactor RuboCop diagnostics for better encapsulation
The code description addition for RuboCop diagnostics has added a cop lookup to the diagnostics runner, but I feel like that was an abstraction added at the wrong layer. What we really want to cache is the registry as a hash lookup but we want to do that as late as possible to ensure that we have loaded the full registry. For that reason, I moved the actual cop lookup by cop name to the RuboCop runner class as a class method, which uses a memoized registry hash under the hood. This way, we can keep the code of the RuboCop diagnostics class clean and with less conditional code. I've also taken the liberty to extract other calculation logic from inside the `to_lsp_diagnostic` method to separate methods, to make the intent more clear.
1 parent 51593f4 commit 8e1bc9e

File tree

2 files changed

+37
-16
lines changed

2 files changed

+37
-16
lines changed

lib/ruby_lsp/requests/support/rubocop_diagnostic.rb

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,6 @@ class RuboCopDiagnostic
1919
T::Hash[Symbol, Integer],
2020
)
2121

22-
# Cache cops to attach URLs to diagnostics. Only built-in cops for now.
23-
COP_TO_DOC_URL = T.let(
24-
RuboCop::Cop::Registry.global.to_h,
25-
T::Hash[String, [T.class_of(RuboCop::Cop::Base)]],
26-
)
27-
2822
sig { params(offense: RuboCop::Cop::Offense, uri: URI::Generic).void }
2923
def initialize(offense, uri)
3024
@offense = offense
@@ -53,16 +47,6 @@ def to_lsp_code_action
5347

5448
sig { returns(Interface::Diagnostic) }
5549
def to_lsp_diagnostic
56-
severity = RUBOCOP_TO_LSP_SEVERITY[@offense.severity.name]
57-
message = @offense.message
58-
59-
message += "\n\nThis offense is not auto-correctable.\n" unless @offense.correctable?
60-
61-
cop = COP_TO_DOC_URL[@offense.cop_name]&.first
62-
if cop&.documentation_url
63-
code_description = { href: cop.documentation_url }
64-
end
65-
6650
Interface::Diagnostic.new(
6751
message: message,
6852
source: "RuboCop",
@@ -88,6 +72,24 @@ def to_lsp_diagnostic
8872

8973
private
9074

75+
sig { returns(String) }
76+
def message
77+
message = @offense.message
78+
message += "\n\nThis offense is not auto-correctable.\n" unless @offense.correctable?
79+
message
80+
end
81+
82+
sig { returns(T.nilable(Integer)) }
83+
def severity
84+
RUBOCOP_TO_LSP_SEVERITY[@offense.severity.name]
85+
end
86+
87+
sig { returns(T.nilable(Interface::CodeDescription)) }
88+
def code_description
89+
doc_url = RuboCopRunner.find_cop_by_name(@offense.cop_name)&.documentation_url
90+
Interface::CodeDescription.new(href: doc_url) if doc_url
91+
end
92+
9193
sig { returns(T::Array[Interface::TextEdit]) }
9294
def offense_replacements
9395
@offense.corrector.as_replacements.map do |range, replacement|

lib/ruby_lsp/requests/support/rubocop_runner.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,25 @@ def formatted_source
101101
@options[:stdin]
102102
end
103103

104+
class << self
105+
extend T::Sig
106+
107+
sig { params(cop_name: String).returns(T.nilable(T.class_of(RuboCop::Cop::Base))) }
108+
def find_cop_by_name(cop_name)
109+
cop_registry[cop_name]&.first
110+
end
111+
112+
private
113+
114+
sig { returns(T::Hash[String, [T.class_of(RuboCop::Cop::Base)]]) }
115+
def cop_registry
116+
@cop_registry ||= T.let(
117+
RuboCop::Cop::Registry.global.to_h,
118+
T.nilable(T::Hash[String, [T.class_of(RuboCop::Cop::Base)]]),
119+
)
120+
end
121+
end
122+
104123
private
105124

106125
sig { params(_file: String, offenses: T::Array[RuboCop::Cop::Offense]).void }

0 commit comments

Comments
 (0)