-
Notifications
You must be signed in to change notification settings - Fork 175
/
Copy pathrubocop_runner.rb
144 lines (119 loc) · 4.34 KB
/
rubocop_runner.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# typed: strict
# frozen_string_literal: true
begin
require "rubocop"
rescue LoadError
return
end
begin
gem("rubocop", ">= 1.4.0")
rescue LoadError
raise StandardError, "Incompatible RuboCop version. Ruby LSP requires >= 1.4.0"
end
if RuboCop.const_defined?(:LSP) # This condition will be removed when requiring RuboCop >= 1.61.
RuboCop::LSP.enable
end
module RubyLsp
module Requests
module Support
class InternalRuboCopError < StandardError
extend T::Sig
MESSAGE = <<~EOS
An internal error occurred %s.
Updating to a newer version of RuboCop may solve this.
For more details, run RuboCop on the command line.
EOS
sig { params(rubocop_error: T.any(RuboCop::ErrorWithAnalyzedFileLocation, StandardError)).void }
def initialize(rubocop_error)
message = case rubocop_error
when RuboCop::ErrorWithAnalyzedFileLocation
format(MESSAGE, "for the #{rubocop_error.cop.name} cop")
when StandardError
format(MESSAGE, rubocop_error.message)
end
super(message)
end
end
# :nodoc:
class RuboCopRunner < RuboCop::Runner
extend T::Sig
class ConfigurationError < StandardError; end
sig { returns(T::Array[RuboCop::Cop::Offense]) }
attr_reader :offenses
sig { returns(::RuboCop::Config) }
attr_reader :config_for_working_directory
DEFAULT_ARGS = T.let(
[
"--stderr", # Print any output to stderr so that our stdout does not get polluted
"--force-exclusion",
"--format",
"RuboCop::Formatter::BaseFormatter", # Suppress any output by using the base formatter
],
T::Array[String],
)
begin
RuboCop::Options.new.parse(["--raise-cop-error"])
DEFAULT_ARGS << "--raise-cop-error"
rescue OptionParser::InvalidOption
# older versions of RuboCop don't support this flag
end
DEFAULT_ARGS.freeze
sig { params(args: String).void }
def initialize(*args)
@options = T.let({}, T::Hash[Symbol, T.untyped])
@offenses = T.let([], T::Array[RuboCop::Cop::Offense])
@errors = T.let([], T::Array[String])
@warnings = T.let([], T::Array[String])
args += DEFAULT_ARGS
rubocop_options = ::RuboCop::Options.new.parse(args).first
config_store = ::RuboCop::ConfigStore.new
@config_for_working_directory = T.let(config_store.for_pwd, ::RuboCop::Config)
super(rubocop_options, config_store)
end
sig { params(path: String, contents: String).void }
def run(path, contents)
# Clear Runner state between runs since we get a single instance of this class
# on every use site.
@errors = []
@warnings = []
@offenses = []
@options[:stdin] = contents
super([path])
# RuboCop rescues interrupts and then sets the `@aborting` variable to true. We don't want them to be rescued,
# so here we re-raise in case RuboCop received an interrupt.
raise Interrupt if aborting?
rescue RuboCop::Runner::InfiniteCorrectionLoop => error
raise Formatting::Error, error.message
rescue RuboCop::ValidationError => error
raise ConfigurationError, error.message
rescue StandardError => error
raise InternalRuboCopError, error
end
sig { returns(String) }
def formatted_source
@options[:stdin]
end
class << self
extend T::Sig
sig { params(cop_name: String).returns(T.nilable(T.class_of(RuboCop::Cop::Base))) }
def find_cop_by_name(cop_name)
cop_registry[cop_name]&.first
end
private
sig { returns(T::Hash[String, [T.class_of(RuboCop::Cop::Base)]]) }
def cop_registry
@cop_registry ||= T.let(
RuboCop::Cop::Registry.global.to_h,
T.nilable(T::Hash[String, [T.class_of(RuboCop::Cop::Base)]]),
)
end
end
private
sig { params(_file: String, offenses: T::Array[RuboCop::Cop::Offense]).void }
def file_finished(_file, offenses)
@offenses = offenses
end
end
end
end
end