ruby-lsp 0.23.1 → 0.23.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/exe/ruby-lsp-launcher +18 -9
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +89 -58
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +21 -10
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +36 -0
- data/lib/ruby_indexer/ruby_indexer.rb +1 -0
- data/lib/ruby_indexer/test/index_test.rb +2 -1
- data/lib/ruby_indexer/test/instance_variables_test.rb +20 -0
- data/lib/ruby_indexer/test/method_test.rb +86 -8
- data/lib/ruby_lsp/base_server.rb +11 -21
- data/lib/ruby_lsp/document.rb +62 -9
- data/lib/ruby_lsp/erb_document.rb +5 -3
- data/lib/ruby_lsp/global_state.rb +9 -0
- data/lib/ruby_lsp/listeners/definition.rb +7 -2
- data/lib/ruby_lsp/rbs_document.rb +2 -2
- data/lib/ruby_lsp/requests/code_action_resolve.rb +2 -6
- data/lib/ruby_lsp/requests/completion.rb +2 -1
- data/lib/ruby_lsp/requests/definition.rb +1 -1
- data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
- data/lib/ruby_lsp/requests/hover.rb +1 -1
- data/lib/ruby_lsp/requests/prepare_rename.rb +1 -1
- data/lib/ruby_lsp/requests/references.rb +1 -1
- data/lib/ruby_lsp/requests/rename.rb +3 -3
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +1 -4
- data/lib/ruby_lsp/requests/signature_help.rb +1 -1
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +3 -3
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +13 -13
- data/lib/ruby_lsp/requests/workspace_symbol.rb +2 -2
- data/lib/ruby_lsp/ruby_document.rb +75 -6
- data/lib/ruby_lsp/server.rb +69 -49
- data/lib/ruby_lsp/store.rb +7 -7
- data/lib/ruby_lsp/type_inferrer.rb +39 -17
- data/lib/ruby_lsp/utils.rb +43 -0
- metadata +3 -2
data/lib/ruby_lsp/base_server.rb
CHANGED
@@ -18,22 +18,21 @@ module RubyLsp
|
|
18
18
|
@incoming_queue = T.let(Thread::Queue.new, Thread::Queue)
|
19
19
|
@outgoing_queue = T.let(Thread::Queue.new, Thread::Queue)
|
20
20
|
@cancelled_requests = T.let([], T::Array[Integer])
|
21
|
-
@mutex = T.let(Mutex.new, Mutex)
|
22
21
|
@worker = T.let(new_worker, Thread)
|
23
22
|
@current_request_id = T.let(1, Integer)
|
24
|
-
@
|
23
|
+
@global_state = T.let(GlobalState.new, GlobalState)
|
24
|
+
@store = T.let(Store.new(@global_state), Store)
|
25
25
|
@outgoing_dispatcher = T.let(
|
26
26
|
Thread.new do
|
27
27
|
unless @test_mode
|
28
28
|
while (message = @outgoing_queue.pop)
|
29
|
-
@
|
29
|
+
@global_state.synchronize { @writer.write(message.to_hash) }
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end,
|
33
33
|
Thread,
|
34
34
|
)
|
35
35
|
|
36
|
-
@global_state = T.let(GlobalState.new, GlobalState)
|
37
36
|
Thread.main.priority = 1
|
38
37
|
|
39
38
|
# We read the initialize request in `exe/ruby-lsp` to be able to determine the workspace URI where Bundler should
|
@@ -51,7 +50,7 @@ module RubyLsp
|
|
51
50
|
# source. Altering the source reference during parsing will put the parser in an invalid internal state, since
|
52
51
|
# it started parsing with one source but then it changed in the middle. We don't want to do this for text
|
53
52
|
# synchronization notifications
|
54
|
-
@
|
53
|
+
@global_state.synchronize do
|
55
54
|
uri = message.dig(:params, :textDocument, :uri)
|
56
55
|
|
57
56
|
if uri
|
@@ -91,22 +90,17 @@ module RubyLsp
|
|
91
90
|
# The following requests need to be executed in the main thread directly to avoid concurrency issues. Everything
|
92
91
|
# else is pushed into the incoming queue
|
93
92
|
case method
|
94
|
-
when "initialize", "initialized", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange"
|
95
|
-
"$/cancelRequest"
|
93
|
+
when "initialize", "initialized", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange"
|
96
94
|
process_message(message)
|
97
95
|
when "shutdown"
|
98
|
-
@
|
96
|
+
@global_state.synchronize do
|
99
97
|
send_log_message("Shutting down Ruby LSP...")
|
100
98
|
shutdown
|
101
99
|
run_shutdown
|
102
100
|
@writer.write(Result.new(id: message[:id], response: nil).to_hash)
|
103
101
|
end
|
104
102
|
when "exit"
|
105
|
-
@
|
106
|
-
status = @incoming_queue.closed? ? 0 : 1
|
107
|
-
send_log_message("Shutdown complete with status #{status}")
|
108
|
-
exit(status)
|
109
|
-
end
|
103
|
+
@global_state.synchronize { exit(@incoming_queue.closed? ? 0 : 1) }
|
110
104
|
else
|
111
105
|
@incoming_queue << message
|
112
106
|
end
|
@@ -121,8 +115,8 @@ module RubyLsp
|
|
121
115
|
@outgoing_queue.close
|
122
116
|
@cancelled_requests.clear
|
123
117
|
|
124
|
-
@worker.
|
125
|
-
@outgoing_dispatcher.
|
118
|
+
@worker.terminate
|
119
|
+
@outgoing_dispatcher.terminate
|
126
120
|
@store.clear
|
127
121
|
end
|
128
122
|
|
@@ -157,13 +151,9 @@ module RubyLsp
|
|
157
151
|
id = message[:id]
|
158
152
|
|
159
153
|
# Check if the request was cancelled before trying to process it
|
160
|
-
@
|
154
|
+
@global_state.synchronize do
|
161
155
|
if id && @cancelled_requests.include?(id)
|
162
|
-
send_message(
|
163
|
-
id: id,
|
164
|
-
code: Constant::ErrorCodes::REQUEST_CANCELLED,
|
165
|
-
message: "Request #{id} was cancelled",
|
166
|
-
))
|
156
|
+
send_message(Result.new(id: id, response: nil))
|
167
157
|
@cancelled_requests.delete(id)
|
168
158
|
next
|
169
159
|
end
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -40,19 +40,24 @@ module RubyLsp
|
|
40
40
|
sig { returns(Encoding) }
|
41
41
|
attr_reader :encoding
|
42
42
|
|
43
|
+
sig { returns(T.nilable(Edit)) }
|
44
|
+
attr_reader :last_edit
|
45
|
+
|
43
46
|
sig { returns(T.any(Interface::SemanticTokens, Object)) }
|
44
47
|
attr_accessor :semantic_tokens
|
45
48
|
|
46
|
-
sig { params(source: String, version: Integer, uri: URI::Generic,
|
47
|
-
def initialize(source:, version:, uri:,
|
49
|
+
sig { params(source: String, version: Integer, uri: URI::Generic, global_state: GlobalState).void }
|
50
|
+
def initialize(source:, version:, uri:, global_state:)
|
51
|
+
@source = source
|
52
|
+
@version = version
|
53
|
+
@global_state = global_state
|
48
54
|
@cache = T.let(Hash.new(EMPTY_CACHE), T::Hash[String, T.untyped])
|
49
55
|
@semantic_tokens = T.let(EMPTY_CACHE, T.any(Interface::SemanticTokens, Object))
|
50
|
-
@encoding = T.let(encoding, Encoding)
|
51
|
-
@source = T.let(source, String)
|
52
|
-
@version = T.let(version, Integer)
|
56
|
+
@encoding = T.let(global_state.encoding, Encoding)
|
53
57
|
@uri = T.let(uri, URI::Generic)
|
54
58
|
@needs_parsing = T.let(true, T::Boolean)
|
55
59
|
@parse_result = T.let(T.unsafe(nil), ParseResultType)
|
60
|
+
@last_edit = T.let(nil, T.nilable(Edit))
|
56
61
|
parse!
|
57
62
|
end
|
58
63
|
|
@@ -64,7 +69,6 @@ module RubyLsp
|
|
64
69
|
sig { abstract.returns(LanguageId) }
|
65
70
|
def language_id; end
|
66
71
|
|
67
|
-
# TODO: remove this method once all non-positional requests have been migrated to the listener pattern
|
68
72
|
sig do
|
69
73
|
type_parameters(:T)
|
70
74
|
.params(
|
@@ -106,6 +110,19 @@ module RubyLsp
|
|
106
110
|
@version = version
|
107
111
|
@needs_parsing = true
|
108
112
|
@cache.clear
|
113
|
+
|
114
|
+
last_edit = edits.last
|
115
|
+
return unless last_edit
|
116
|
+
|
117
|
+
last_edit_range = last_edit[:range]
|
118
|
+
|
119
|
+
@last_edit = if last_edit_range[:start] == last_edit_range[:end]
|
120
|
+
Insert.new(last_edit_range)
|
121
|
+
elsif last_edit[:text].empty?
|
122
|
+
Delete.new(last_edit_range)
|
123
|
+
else
|
124
|
+
Replace.new(last_edit_range)
|
125
|
+
end
|
109
126
|
end
|
110
127
|
|
111
128
|
# Returns `true` if the document was parsed and `false` if nothing needed parsing
|
@@ -115,16 +132,52 @@ module RubyLsp
|
|
115
132
|
sig { abstract.returns(T::Boolean) }
|
116
133
|
def syntax_error?; end
|
117
134
|
|
135
|
+
sig { returns(T::Boolean) }
|
136
|
+
def past_expensive_limit?
|
137
|
+
@source.length > MAXIMUM_CHARACTERS_FOR_EXPENSIVE_FEATURES
|
138
|
+
end
|
139
|
+
|
140
|
+
sig do
|
141
|
+
params(
|
142
|
+
start_pos: T::Hash[Symbol, T.untyped],
|
143
|
+
end_pos: T.nilable(T::Hash[Symbol, T.untyped]),
|
144
|
+
).returns([Integer, T.nilable(Integer)])
|
145
|
+
end
|
146
|
+
def find_index_by_position(start_pos, end_pos = nil)
|
147
|
+
@global_state.synchronize do
|
148
|
+
scanner = create_scanner
|
149
|
+
start_index = scanner.find_char_position(start_pos)
|
150
|
+
end_index = scanner.find_char_position(end_pos) if end_pos
|
151
|
+
[start_index, end_index]
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
118
157
|
sig { returns(Scanner) }
|
119
158
|
def create_scanner
|
120
159
|
Scanner.new(@source, @encoding)
|
121
160
|
end
|
122
161
|
|
123
|
-
|
124
|
-
|
125
|
-
|
162
|
+
class Edit
|
163
|
+
extend T::Sig
|
164
|
+
extend T::Helpers
|
165
|
+
|
166
|
+
abstract!
|
167
|
+
|
168
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
169
|
+
attr_reader :range
|
170
|
+
|
171
|
+
sig { params(range: T::Hash[Symbol, T.untyped]).void }
|
172
|
+
def initialize(range)
|
173
|
+
@range = range
|
174
|
+
end
|
126
175
|
end
|
127
176
|
|
177
|
+
class Insert < Edit; end
|
178
|
+
class Replace < Edit; end
|
179
|
+
class Delete < Edit; end
|
180
|
+
|
128
181
|
class Scanner
|
129
182
|
extend T::Sig
|
130
183
|
|
@@ -19,8 +19,8 @@ module RubyLsp
|
|
19
19
|
end
|
20
20
|
attr_reader :code_units_cache
|
21
21
|
|
22
|
-
sig { params(source: String, version: Integer, uri: URI::Generic,
|
23
|
-
def initialize(source:, version:, uri:,
|
22
|
+
sig { params(source: String, version: Integer, uri: URI::Generic, global_state: GlobalState).void }
|
23
|
+
def initialize(source:, version:, uri:, global_state:)
|
24
24
|
# This has to be initialized before calling super because we call `parse` in the parent constructor, which
|
25
25
|
# overrides this with the proper virtual host language source
|
26
26
|
@host_language_source = T.let("", String)
|
@@ -63,9 +63,11 @@ module RubyLsp
|
|
63
63
|
).returns(NodeContext)
|
64
64
|
end
|
65
65
|
def locate_node(position, node_types: [])
|
66
|
+
char_position, _ = find_index_by_position(position)
|
67
|
+
|
66
68
|
RubyDocument.locate(
|
67
69
|
@parse_result.value,
|
68
|
-
|
70
|
+
char_position,
|
69
71
|
code_units_cache: @code_units_cache,
|
70
72
|
node_types: node_types,
|
71
73
|
)
|
@@ -29,6 +29,9 @@ module RubyLsp
|
|
29
29
|
sig { returns(ClientCapabilities) }
|
30
30
|
attr_reader :client_capabilities
|
31
31
|
|
32
|
+
sig { returns(URI::Generic) }
|
33
|
+
attr_reader :workspace_uri
|
34
|
+
|
32
35
|
sig { void }
|
33
36
|
def initialize
|
34
37
|
@workspace_uri = T.let(URI::Generic.from_path(path: Dir.pwd), URI::Generic)
|
@@ -53,6 +56,12 @@ module RubyLsp
|
|
53
56
|
)
|
54
57
|
@client_capabilities = T.let(ClientCapabilities.new, ClientCapabilities)
|
55
58
|
@enabled_feature_flags = T.let({}, T::Hash[Symbol, T::Boolean])
|
59
|
+
@mutex = T.let(Mutex.new, Mutex)
|
60
|
+
end
|
61
|
+
|
62
|
+
sig { type_parameters(:T).params(block: T.proc.returns(T.type_parameter(:T))).returns(T.type_parameter(:T)) }
|
63
|
+
def synchronize(&block)
|
64
|
+
@mutex.synchronize(&block)
|
56
65
|
end
|
57
66
|
|
58
67
|
sig { params(addon_name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
@@ -330,7 +330,8 @@ module RubyLsp
|
|
330
330
|
|
331
331
|
methods.each do |target_method|
|
332
332
|
uri = target_method.uri
|
333
|
-
|
333
|
+
full_path = uri.full_path
|
334
|
+
next if sorbet_level_true_or_higher?(@sorbet_level) && (!full_path || not_in_dependencies?(full_path))
|
334
335
|
|
335
336
|
@response_builder << Interface::LocationLink.new(
|
336
337
|
target_uri: uri.to_s,
|
@@ -403,7 +404,11 @@ module RubyLsp
|
|
403
404
|
# additional behavior on top of jumping to RBIs. The only sigil where Sorbet cannot handle constants is typed
|
404
405
|
# ignore
|
405
406
|
uri = entry.uri
|
406
|
-
|
407
|
+
full_path = uri.full_path
|
408
|
+
|
409
|
+
if @sorbet_level != RubyDocument::SorbetLevel::Ignore && (!full_path || not_in_dependencies?(full_path))
|
410
|
+
next
|
411
|
+
end
|
407
412
|
|
408
413
|
@response_builder << Interface::LocationLink.new(
|
409
414
|
target_uri: uri.to_s,
|
@@ -8,8 +8,8 @@ module RubyLsp
|
|
8
8
|
|
9
9
|
ParseResultType = type_member { { fixed: T::Array[RBS::AST::Declarations::Base] } }
|
10
10
|
|
11
|
-
sig { params(source: String, version: Integer, uri: URI::Generic,
|
12
|
-
def initialize(source:, version:, uri:,
|
11
|
+
sig { params(source: String, version: Integer, uri: URI::Generic, global_state: GlobalState).void }
|
12
|
+
def initialize(source:, version:, uri:, global_state:)
|
13
13
|
@syntax_error = T.let(false, T::Boolean)
|
14
14
|
super
|
15
15
|
end
|
@@ -92,9 +92,7 @@ module RubyLsp
|
|
92
92
|
source_range = @code_action.dig(:data, :range)
|
93
93
|
return Error::EmptySelection if source_range[:start] == source_range[:end]
|
94
94
|
|
95
|
-
|
96
|
-
start_index = scanner.find_char_position(source_range[:start])
|
97
|
-
end_index = scanner.find_char_position(source_range[:end])
|
95
|
+
start_index, end_index = @document.find_index_by_position(source_range[:start], source_range[:end])
|
98
96
|
extracted_source = T.must(@document.source[start_index...end_index])
|
99
97
|
|
100
98
|
# Find the closest statements node, so that we place the refactor in a valid position
|
@@ -192,9 +190,7 @@ module RubyLsp
|
|
192
190
|
source_range = @code_action.dig(:data, :range)
|
193
191
|
return Error::EmptySelection if source_range[:start] == source_range[:end]
|
194
192
|
|
195
|
-
|
196
|
-
start_index = scanner.find_char_position(source_range[:start])
|
197
|
-
end_index = scanner.find_char_position(source_range[:end])
|
193
|
+
start_index, end_index = @document.find_index_by_position(source_range[:start], source_range[:end])
|
198
194
|
extracted_source = T.must(@document.source[start_index...end_index])
|
199
195
|
|
200
196
|
# Find the closest method declaration node, so that we place the refactor in a valid position
|
@@ -40,7 +40,8 @@ module RubyLsp
|
|
40
40
|
@dispatcher = dispatcher
|
41
41
|
# Completion always receives the position immediately after the character that was just typed. Here we adjust it
|
42
42
|
# back by 1, so that we find the right node
|
43
|
-
char_position = document.
|
43
|
+
char_position, _ = document.find_index_by_position(params[:position])
|
44
|
+
char_position -= 1
|
44
45
|
delegate_request_if_needed!(global_state, document, char_position)
|
45
46
|
|
46
47
|
node_context = RubyDocument.locate(
|
@@ -29,7 +29,7 @@ module RubyLsp
|
|
29
29
|
)
|
30
30
|
@dispatcher = dispatcher
|
31
31
|
|
32
|
-
char_position = document.
|
32
|
+
char_position, _ = document.find_index_by_position(position)
|
33
33
|
delegate_request_if_needed!(global_state, document, char_position)
|
34
34
|
|
35
35
|
node_context = RubyDocument.locate(
|
@@ -25,7 +25,7 @@ module RubyLsp
|
|
25
25
|
end
|
26
26
|
def initialize(global_state, document, position, dispatcher)
|
27
27
|
super()
|
28
|
-
char_position = document.
|
28
|
+
char_position, _ = document.find_index_by_position(position)
|
29
29
|
delegate_request_if_needed!(global_state, document, char_position)
|
30
30
|
|
31
31
|
node_context = RubyDocument.locate(
|
@@ -34,7 +34,7 @@ module RubyLsp
|
|
34
34
|
def initialize(document, global_state, position, dispatcher, sorbet_level)
|
35
35
|
super()
|
36
36
|
|
37
|
-
char_position = document.
|
37
|
+
char_position, _ = document.find_index_by_position(position)
|
38
38
|
delegate_request_if_needed!(global_state, document, char_position)
|
39
39
|
|
40
40
|
node_context = RubyDocument.locate(
|
@@ -24,7 +24,7 @@ module RubyLsp
|
|
24
24
|
|
25
25
|
sig { override.returns(T.nilable(Interface::Range)) }
|
26
26
|
def perform
|
27
|
-
char_position = @document.
|
27
|
+
char_position, _ = @document.find_index_by_position(@position)
|
28
28
|
|
29
29
|
node_context = RubyDocument.locate(
|
30
30
|
@document.parse_result.value,
|
@@ -30,7 +30,7 @@ module RubyLsp
|
|
30
30
|
sig { override.returns(T::Array[Interface::Location]) }
|
31
31
|
def perform
|
32
32
|
position = @params[:position]
|
33
|
-
char_position = @document.
|
33
|
+
char_position, _ = @document.find_index_by_position(position)
|
34
34
|
|
35
35
|
node_context = RubyDocument.locate(
|
36
36
|
@document.parse_result.value,
|
@@ -40,7 +40,7 @@ module RubyLsp
|
|
40
40
|
|
41
41
|
sig { override.returns(T.nilable(Interface::WorkspaceEdit)) }
|
42
42
|
def perform
|
43
|
-
char_position = @document.
|
43
|
+
char_position, _ = @document.find_index_by_position(@position)
|
44
44
|
|
45
45
|
node_context = RubyDocument.locate(
|
46
46
|
@document.parse_result.value,
|
@@ -116,8 +116,8 @@ module RubyLsp
|
|
116
116
|
T.must(@global_state.index[fully_qualified_name]).each do |entry|
|
117
117
|
# Do not rename files that are not part of the workspace
|
118
118
|
uri = entry.uri
|
119
|
-
file_path =
|
120
|
-
next unless file_path
|
119
|
+
file_path = uri.full_path
|
120
|
+
next unless file_path&.start_with?(@global_state.workspace_path)
|
121
121
|
|
122
122
|
case entry
|
123
123
|
when RubyIndexer::Entry::Class, RubyIndexer::Entry::Module, RubyIndexer::Entry::Constant,
|
@@ -31,10 +31,7 @@ module RubyLsp
|
|
31
31
|
sig { returns(String) }
|
32
32
|
def ast_for_range
|
33
33
|
range = T.must(@range)
|
34
|
-
|
35
|
-
scanner = @document.create_scanner
|
36
|
-
start_char = scanner.find_char_position(range[:start])
|
37
|
-
end_char = scanner.find_char_position(range[:end])
|
34
|
+
start_char, end_char = @document.find_index_by_position(range[:start], range[:end])
|
38
35
|
|
39
36
|
queue = @tree.statements.body.dup
|
40
37
|
found_nodes = []
|
@@ -36,7 +36,7 @@ module RubyLsp
|
|
36
36
|
def initialize(document, global_state, position, context, dispatcher, sorbet_level) # rubocop:disable Metrics/ParameterLists
|
37
37
|
super()
|
38
38
|
|
39
|
-
char_position = document.
|
39
|
+
char_position, _ = document.find_index_by_position(position)
|
40
40
|
delegate_request_if_needed!(global_state, document, char_position)
|
41
41
|
|
42
42
|
node_context = RubyDocument.locate(
|
@@ -31,7 +31,7 @@ module RubyLsp
|
|
31
31
|
|
32
32
|
# TODO: avoid passing document once we have alternative ways to get at
|
33
33
|
# encoding and file source
|
34
|
-
sig { params(document: RubyDocument, offense: RuboCop::Cop::Offense, uri: URI::Generic).void }
|
34
|
+
sig { params(document: RubyDocument, offense: ::RuboCop::Cop::Offense, uri: URI::Generic).void }
|
35
35
|
def initialize(document, offense, uri)
|
36
36
|
@document = document
|
37
37
|
@offense = offense
|
@@ -48,7 +48,7 @@ module RubyLsp
|
|
48
48
|
code_actions
|
49
49
|
end
|
50
50
|
|
51
|
-
sig { params(config: RuboCop::Config).returns(Interface::Diagnostic) }
|
51
|
+
sig { params(config: ::RuboCop::Config).returns(Interface::Diagnostic) }
|
52
52
|
def to_lsp_diagnostic(config)
|
53
53
|
# highlighted_area contains the begin and end position of the first line
|
54
54
|
# This ensures that multiline offenses don't clutter the editor
|
@@ -90,7 +90,7 @@ module RubyLsp
|
|
90
90
|
RUBOCOP_TO_LSP_SEVERITY[@offense.severity.name]
|
91
91
|
end
|
92
92
|
|
93
|
-
sig { params(config: RuboCop::Config).returns(T.nilable(Interface::CodeDescription)) }
|
93
|
+
sig { params(config: ::RuboCop::Config).returns(T.nilable(Interface::CodeDescription)) }
|
94
94
|
def code_description(config)
|
95
95
|
cop = RuboCopRunner.find_cop_by_name(@offense.cop_name)
|
96
96
|
return unless cop
|
@@ -40,10 +40,10 @@ module RubyLsp
|
|
40
40
|
For more details, run RuboCop on the command line.
|
41
41
|
EOS
|
42
42
|
|
43
|
-
sig { params(rubocop_error: T.any(RuboCop::ErrorWithAnalyzedFileLocation, StandardError)).void }
|
43
|
+
sig { params(rubocop_error: T.any(::RuboCop::ErrorWithAnalyzedFileLocation, StandardError)).void }
|
44
44
|
def initialize(rubocop_error)
|
45
45
|
message = case rubocop_error
|
46
|
-
when RuboCop::ErrorWithAnalyzedFileLocation
|
46
|
+
when ::RuboCop::ErrorWithAnalyzedFileLocation
|
47
47
|
format(MESSAGE, "for the #{rubocop_error.cop.name} cop")
|
48
48
|
when StandardError
|
49
49
|
format(MESSAGE, rubocop_error.message)
|
@@ -53,7 +53,7 @@ module RubyLsp
|
|
53
53
|
end
|
54
54
|
|
55
55
|
# :nodoc:
|
56
|
-
class RuboCopRunner < RuboCop::Runner
|
56
|
+
class RuboCopRunner < ::RuboCop::Runner
|
57
57
|
extend T::Sig
|
58
58
|
|
59
59
|
class ConfigurationError < StandardError; end
|
@@ -68,14 +68,14 @@ module RubyLsp
|
|
68
68
|
T::Array[String],
|
69
69
|
)
|
70
70
|
|
71
|
-
sig { returns(T::Array[RuboCop::Cop::Offense]) }
|
71
|
+
sig { returns(T::Array[::RuboCop::Cop::Offense]) }
|
72
72
|
attr_reader :offenses
|
73
73
|
|
74
74
|
sig { returns(::RuboCop::Config) }
|
75
75
|
attr_reader :config_for_working_directory
|
76
76
|
|
77
77
|
begin
|
78
|
-
RuboCop::Options.new.parse(["--raise-cop-error"])
|
78
|
+
::RuboCop::Options.new.parse(["--raise-cop-error"])
|
79
79
|
DEFAULT_ARGS << "--raise-cop-error"
|
80
80
|
rescue OptionParser::InvalidOption
|
81
81
|
# older versions of RuboCop don't support this flag
|
@@ -85,7 +85,7 @@ module RubyLsp
|
|
85
85
|
sig { params(args: String).void }
|
86
86
|
def initialize(*args)
|
87
87
|
@options = T.let({}, T::Hash[Symbol, T.untyped])
|
88
|
-
@offenses = T.let([], T::Array[RuboCop::Cop::Offense])
|
88
|
+
@offenses = T.let([], T::Array[::RuboCop::Cop::Offense])
|
89
89
|
@errors = T.let([], T::Array[String])
|
90
90
|
@warnings = T.let([], T::Array[String])
|
91
91
|
|
@@ -113,9 +113,9 @@ module RubyLsp
|
|
113
113
|
# RuboCop rescues interrupts and then sets the `@aborting` variable to true. We don't want them to be rescued,
|
114
114
|
# so here we re-raise in case RuboCop received an interrupt.
|
115
115
|
raise Interrupt if aborting?
|
116
|
-
rescue RuboCop::Runner::InfiniteCorrectionLoop => error
|
116
|
+
rescue ::RuboCop::Runner::InfiniteCorrectionLoop => error
|
117
117
|
raise Formatting::Error, error.message
|
118
|
-
rescue RuboCop::ValidationError => error
|
118
|
+
rescue ::RuboCop::ValidationError => error
|
119
119
|
raise ConfigurationError, error.message
|
120
120
|
rescue StandardError => error
|
121
121
|
raise InternalRuboCopError, error
|
@@ -129,25 +129,25 @@ module RubyLsp
|
|
129
129
|
class << self
|
130
130
|
extend T::Sig
|
131
131
|
|
132
|
-
sig { params(cop_name: String).returns(T.nilable(T.class_of(RuboCop::Cop::Base))) }
|
132
|
+
sig { params(cop_name: String).returns(T.nilable(T.class_of(::RuboCop::Cop::Base))) }
|
133
133
|
def find_cop_by_name(cop_name)
|
134
134
|
cop_registry[cop_name]&.first
|
135
135
|
end
|
136
136
|
|
137
137
|
private
|
138
138
|
|
139
|
-
sig { returns(T::Hash[String, [T.class_of(RuboCop::Cop::Base)]]) }
|
139
|
+
sig { returns(T::Hash[String, [T.class_of(::RuboCop::Cop::Base)]]) }
|
140
140
|
def cop_registry
|
141
141
|
@cop_registry ||= T.let(
|
142
|
-
RuboCop::Cop::Registry.global.to_h,
|
143
|
-
T.nilable(T::Hash[String, [T.class_of(RuboCop::Cop::Base)]]),
|
142
|
+
::RuboCop::Cop::Registry.global.to_h,
|
143
|
+
T.nilable(T::Hash[String, [T.class_of(::RuboCop::Cop::Base)]]),
|
144
144
|
)
|
145
145
|
end
|
146
146
|
end
|
147
147
|
|
148
148
|
private
|
149
149
|
|
150
|
-
sig { params(_file: String, offenses: T::Array[RuboCop::Cop::Offense]).void }
|
150
|
+
sig { params(_file: String, offenses: T::Array[::RuboCop::Cop::Offense]).void }
|
151
151
|
def file_finished(_file, offenses)
|
152
152
|
@offenses = offenses
|
153
153
|
end
|
@@ -22,10 +22,10 @@ module RubyLsp
|
|
22
22
|
def perform
|
23
23
|
@index.fuzzy_search(@query).filter_map do |entry|
|
24
24
|
uri = entry.uri
|
25
|
-
file_path =
|
25
|
+
file_path = uri.full_path
|
26
26
|
|
27
27
|
# We only show symbols declared in the workspace
|
28
|
-
in_dependencies = !not_in_dependencies?(file_path)
|
28
|
+
in_dependencies = file_path && !not_in_dependencies?(file_path)
|
29
29
|
next if in_dependencies
|
30
30
|
|
31
31
|
# We should never show private symbols when searching the entire workspace
|
@@ -8,6 +8,22 @@ module RubyLsp
|
|
8
8
|
|
9
9
|
ParseResultType = type_member { { fixed: Prism::ParseResult } }
|
10
10
|
|
11
|
+
METHODS_THAT_CHANGE_DECLARATIONS = [
|
12
|
+
:private_constant,
|
13
|
+
:attr_reader,
|
14
|
+
:attr_writer,
|
15
|
+
:attr_accessor,
|
16
|
+
:alias_method,
|
17
|
+
:include,
|
18
|
+
:prepend,
|
19
|
+
:extend,
|
20
|
+
:public,
|
21
|
+
:protected,
|
22
|
+
:private,
|
23
|
+
:module_function,
|
24
|
+
:private_class_method,
|
25
|
+
].freeze
|
26
|
+
|
11
27
|
class SorbetLevel < T::Enum
|
12
28
|
enums do
|
13
29
|
None = new("none")
|
@@ -142,8 +158,8 @@ module RubyLsp
|
|
142
158
|
end
|
143
159
|
attr_reader :code_units_cache
|
144
160
|
|
145
|
-
sig { params(source: String, version: Integer, uri: URI::Generic,
|
146
|
-
def initialize(source:, version:, uri:,
|
161
|
+
sig { params(source: String, version: Integer, uri: URI::Generic, global_state: GlobalState).void }
|
162
|
+
def initialize(source:, version:, uri:, global_state:)
|
147
163
|
super
|
148
164
|
@code_units_cache = T.let(@parse_result.code_units_cache(@encoding), T.any(
|
149
165
|
T.proc.params(arg0: Integer).returns(Integer),
|
@@ -198,9 +214,8 @@ module RubyLsp
|
|
198
214
|
).returns(T.nilable(Prism::Node))
|
199
215
|
end
|
200
216
|
def locate_first_within_range(range, node_types: [])
|
201
|
-
|
202
|
-
|
203
|
-
end_position = scanner.find_char_position(range[:end])
|
217
|
+
start_position, end_position = find_index_by_position(range[:start], range[:end])
|
218
|
+
|
204
219
|
desired_range = (start_position...end_position)
|
205
220
|
queue = T.let(@parse_result.value.child_nodes.compact, T::Array[T.nilable(Prism::Node)])
|
206
221
|
|
@@ -232,12 +247,66 @@ module RubyLsp
|
|
232
247
|
).returns(NodeContext)
|
233
248
|
end
|
234
249
|
def locate_node(position, node_types: [])
|
250
|
+
char_position, _ = find_index_by_position(position)
|
251
|
+
|
235
252
|
RubyDocument.locate(
|
236
253
|
@parse_result.value,
|
237
|
-
|
254
|
+
char_position,
|
238
255
|
code_units_cache: @code_units_cache,
|
239
256
|
node_types: node_types,
|
240
257
|
)
|
241
258
|
end
|
259
|
+
|
260
|
+
sig { returns(T::Boolean) }
|
261
|
+
def last_edit_may_change_declarations?
|
262
|
+
# This method controls when we should index documents. If there's no recent edit and the document has just been
|
263
|
+
# opened, we need to index it
|
264
|
+
return true unless @last_edit
|
265
|
+
|
266
|
+
case @last_edit
|
267
|
+
when Delete
|
268
|
+
# Not optimized yet. It's not trivial to identify that a declaration has been removed since the source is no
|
269
|
+
# longer there and we don't remember the deleted text
|
270
|
+
true
|
271
|
+
when Insert, Replace
|
272
|
+
position_may_impact_declarations?(@last_edit.range[:start])
|
273
|
+
else
|
274
|
+
false
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
private
|
279
|
+
|
280
|
+
sig { params(position: T::Hash[Symbol, Integer]).returns(T::Boolean) }
|
281
|
+
def position_may_impact_declarations?(position)
|
282
|
+
node_context = locate_node(position)
|
283
|
+
node_at_edit = node_context.node
|
284
|
+
|
285
|
+
# Adjust to the parent when editing the constant of a class/module declaration
|
286
|
+
if node_at_edit.is_a?(Prism::ConstantReadNode) || node_at_edit.is_a?(Prism::ConstantPathNode)
|
287
|
+
node_at_edit = node_context.parent
|
288
|
+
end
|
289
|
+
|
290
|
+
case node_at_edit
|
291
|
+
when Prism::ClassNode, Prism::ModuleNode, Prism::SingletonClassNode, Prism::DefNode,
|
292
|
+
Prism::ConstantPathWriteNode, Prism::ConstantPathOrWriteNode, Prism::ConstantPathOperatorWriteNode,
|
293
|
+
Prism::ConstantPathAndWriteNode, Prism::ConstantOrWriteNode, Prism::ConstantWriteNode,
|
294
|
+
Prism::ConstantAndWriteNode, Prism::ConstantOperatorWriteNode, Prism::GlobalVariableAndWriteNode,
|
295
|
+
Prism::GlobalVariableOperatorWriteNode, Prism::GlobalVariableOrWriteNode, Prism::GlobalVariableTargetNode,
|
296
|
+
Prism::GlobalVariableWriteNode, Prism::InstanceVariableWriteNode, Prism::InstanceVariableAndWriteNode,
|
297
|
+
Prism::InstanceVariableOperatorWriteNode, Prism::InstanceVariableOrWriteNode,
|
298
|
+
Prism::InstanceVariableTargetNode, Prism::AliasMethodNode
|
299
|
+
true
|
300
|
+
when Prism::MultiWriteNode
|
301
|
+
[*node_at_edit.lefts, *node_at_edit.rest, *node_at_edit.rights].any? do |node|
|
302
|
+
node.is_a?(Prism::ConstantTargetNode) || node.is_a?(Prism::ConstantPathTargetNode)
|
303
|
+
end
|
304
|
+
when Prism::CallNode
|
305
|
+
receiver = node_at_edit.receiver
|
306
|
+
(!receiver || receiver.is_a?(Prism::SelfNode)) && METHODS_THAT_CHANGE_DECLARATIONS.include?(node_at_edit.name)
|
307
|
+
else
|
308
|
+
false
|
309
|
+
end
|
310
|
+
end
|
242
311
|
end
|
243
312
|
end
|