ruby-lsp 0.22.1 → 0.23.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/exe/ruby-lsp +10 -9
- data/exe/ruby-lsp-check +5 -5
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +26 -20
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +88 -22
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +60 -30
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +73 -55
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +16 -14
- data/lib/{core_ext → ruby_indexer/lib/ruby_indexer}/uri.rb +29 -3
- data/lib/ruby_indexer/ruby_indexer.rb +1 -1
- data/lib/ruby_indexer/test/class_variables_test.rb +140 -0
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +11 -6
- data/lib/ruby_indexer/test/configuration_test.rb +116 -51
- data/lib/ruby_indexer/test/enhancements_test.rb +2 -2
- data/lib/ruby_indexer/test/index_test.rb +72 -43
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
- data/lib/ruby_indexer/test/reference_finder_test.rb +1 -1
- data/lib/ruby_indexer/test/test_case.rb +2 -2
- data/lib/ruby_indexer/test/uri_test.rb +72 -0
- data/lib/ruby_lsp/addon.rb +9 -0
- data/lib/ruby_lsp/base_server.rb +15 -6
- data/lib/ruby_lsp/document.rb +10 -1
- data/lib/ruby_lsp/internal.rb +1 -1
- data/lib/ruby_lsp/listeners/code_lens.rb +8 -4
- data/lib/ruby_lsp/listeners/completion.rb +73 -4
- data/lib/ruby_lsp/listeners/definition.rb +73 -17
- data/lib/ruby_lsp/listeners/document_symbol.rb +12 -1
- data/lib/ruby_lsp/listeners/folding_ranges.rb +1 -1
- data/lib/ruby_lsp/listeners/hover.rb +57 -0
- data/lib/ruby_lsp/requests/completion.rb +6 -0
- data/lib/ruby_lsp/requests/completion_resolve.rb +2 -1
- data/lib/ruby_lsp/requests/definition.rb +6 -0
- data/lib/ruby_lsp/requests/prepare_rename.rb +51 -0
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +1 -1
- data/lib/ruby_lsp/requests/rename.rb +14 -4
- data/lib/ruby_lsp/requests/support/common.rb +1 -5
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +1 -1
- data/lib/ruby_lsp/requests/workspace_symbol.rb +3 -2
- data/lib/ruby_lsp/scripts/compose_bundle.rb +1 -1
- data/lib/ruby_lsp/server.rb +42 -7
- data/lib/ruby_lsp/setup_bundler.rb +31 -41
- data/lib/ruby_lsp/test_helper.rb +45 -11
- data/lib/ruby_lsp/type_inferrer.rb +22 -0
- data/lib/ruby_lsp/utils.rb +3 -0
- metadata +7 -8
- data/lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb +0 -29
@@ -0,0 +1,72 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "test_helper"
|
5
|
+
|
6
|
+
module RubyIndexer
|
7
|
+
class URITest < Minitest::Test
|
8
|
+
def test_from_path_on_unix
|
9
|
+
uri = URI::Generic.from_path(path: "/some/unix/path/to/file.rb")
|
10
|
+
assert_equal("/some/unix/path/to/file.rb", uri.path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_from_path_on_windows
|
14
|
+
uri = URI::Generic.from_path(path: "C:/some/windows/path/to/file.rb")
|
15
|
+
assert_equal("/C:/some/windows/path/to/file.rb", uri.path)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_from_path_on_windows_with_lowercase_drive
|
19
|
+
uri = URI::Generic.from_path(path: "c:/some/windows/path/to/file.rb")
|
20
|
+
assert_equal("/c:/some/windows/path/to/file.rb", uri.path)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_to_standardized_path_on_unix
|
24
|
+
uri = URI::Generic.from_path(path: "/some/unix/path/to/file.rb")
|
25
|
+
assert_equal(uri.path, uri.to_standardized_path)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_to_standardized_path_on_windows
|
29
|
+
uri = URI::Generic.from_path(path: "C:/some/windows/path/to/file.rb")
|
30
|
+
assert_equal("C:/some/windows/path/to/file.rb", uri.to_standardized_path)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_to_standardized_path_on_windows_with_lowercase_drive
|
34
|
+
uri = URI::Generic.from_path(path: "c:/some/windows/path/to/file.rb")
|
35
|
+
assert_equal("c:/some/windows/path/to/file.rb", uri.to_standardized_path)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_to_standardized_path_on_windows_with_received_uri
|
39
|
+
uri = URI("file:///c%3A/some/windows/path/to/file.rb")
|
40
|
+
assert_equal("c:/some/windows/path/to/file.rb", uri.to_standardized_path)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_plus_signs_are_properly_unescaped
|
44
|
+
path = "/opt/rubies/3.3.0/lib/ruby/3.3.0+0/pathname.rb"
|
45
|
+
uri = URI::Generic.from_path(path: path)
|
46
|
+
assert_equal(path, uri.to_standardized_path)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_from_path_with_fragment
|
50
|
+
uri = URI::Generic.from_path(path: "/some/unix/path/to/file.rb", fragment: "L1,3-2,9")
|
51
|
+
assert_equal("file:///some/unix/path/to/file.rb#L1,3-2,9", uri.to_s)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_from_path_windows_long_file_paths
|
55
|
+
uri = URI::Generic.from_path(path: "//?/C:/hostedtoolcache/windows/Ruby/3.3.1/x64/lib/ruby/3.3.0/open-uri.rb")
|
56
|
+
assert_equal("C:/hostedtoolcache/windows/Ruby/3.3.1/x64/lib/ruby/3.3.0/open-uri.rb", uri.to_standardized_path)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_from_path_computes_require_path_when_load_path_entry_is_given
|
60
|
+
uri = URI::Generic.from_path(path: "/some/unix/path/to/file.rb", load_path_entry: "/some/unix/path")
|
61
|
+
assert_equal("to/file", uri.require_path)
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_allows_adding_require_path_with_load_path_entry
|
65
|
+
uri = URI::Generic.from_path(path: "/some/unix/path/to/file.rb")
|
66
|
+
assert_nil(uri.require_path)
|
67
|
+
|
68
|
+
uri.add_require_path_from_load_entry("/some/unix/path")
|
69
|
+
assert_equal("to/file", uri.require_path)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/ruby_lsp/addon.rb
CHANGED
@@ -97,6 +97,15 @@ module RubyLsp
|
|
97
97
|
errors
|
98
98
|
end
|
99
99
|
|
100
|
+
# Unloads all add-ons. Only intended to be invoked once when shutting down the Ruby LSP server
|
101
|
+
sig { void }
|
102
|
+
def unload_addons
|
103
|
+
@addons.each(&:deactivate)
|
104
|
+
@addons.clear
|
105
|
+
@addon_classes.clear
|
106
|
+
@file_watcher_addons.clear
|
107
|
+
end
|
108
|
+
|
100
109
|
# Get a reference to another add-on object by name and version. If an add-on exports an API that can be used by
|
101
110
|
# other add-ons, this is the way to get access to that API.
|
102
111
|
#
|
data/lib/ruby_lsp/base_server.rb
CHANGED
@@ -91,14 +91,13 @@ module RubyLsp
|
|
91
91
|
# The following requests need to be executed in the main thread directly to avoid concurrency issues. Everything
|
92
92
|
# else is pushed into the incoming queue
|
93
93
|
case method
|
94
|
-
when "initialize", "initialized", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange"
|
94
|
+
when "initialize", "initialized", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange",
|
95
|
+
"$/cancelRequest"
|
95
96
|
process_message(message)
|
96
97
|
when "shutdown"
|
97
|
-
send_log_message("Shutting down Ruby LSP...")
|
98
|
-
|
99
|
-
shutdown
|
100
|
-
|
101
98
|
@mutex.synchronize do
|
99
|
+
send_log_message("Shutting down Ruby LSP...")
|
100
|
+
shutdown
|
102
101
|
run_shutdown
|
103
102
|
@writer.write(Result.new(id: message[:id], response: nil).to_hash)
|
104
103
|
end
|
@@ -133,6 +132,12 @@ module RubyLsp
|
|
133
132
|
@outgoing_queue.pop
|
134
133
|
end
|
135
134
|
|
135
|
+
# This method is only intended to be used in tests! Pushes a message to the incoming queue directly
|
136
|
+
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
137
|
+
def push_message(message)
|
138
|
+
@incoming_queue << message
|
139
|
+
end
|
140
|
+
|
136
141
|
sig { abstract.params(message: T::Hash[Symbol, T.untyped]).void }
|
137
142
|
def process_message(message); end
|
138
143
|
|
@@ -154,7 +159,11 @@ module RubyLsp
|
|
154
159
|
# Check if the request was cancelled before trying to process it
|
155
160
|
@mutex.synchronize do
|
156
161
|
if id && @cancelled_requests.include?(id)
|
157
|
-
send_message(
|
162
|
+
send_message(Error.new(
|
163
|
+
id: id,
|
164
|
+
code: Constant::ErrorCodes::REQUEST_CANCELLED,
|
165
|
+
message: "Request #{id} was cancelled",
|
166
|
+
))
|
158
167
|
@cancelled_requests.delete(id)
|
159
168
|
next
|
160
169
|
end
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -15,6 +15,7 @@ module RubyLsp
|
|
15
15
|
extend T::Helpers
|
16
16
|
extend T::Generic
|
17
17
|
|
18
|
+
class LocationNotFoundError < StandardError; end
|
18
19
|
ParseResultType = type_member
|
19
20
|
|
20
21
|
# This maximum number of characters for providing expensive features, like semantic highlighting and diagnostics.
|
@@ -144,7 +145,15 @@ module RubyLsp
|
|
144
145
|
def find_char_position(position)
|
145
146
|
# Find the character index for the beginning of the requested line
|
146
147
|
until @current_line == position[:line]
|
147
|
-
|
148
|
+
until LINE_BREAK == @source[@pos]
|
149
|
+
@pos += 1
|
150
|
+
|
151
|
+
if @pos >= @source.length
|
152
|
+
# Pack the code points back into the original string to provide context in the error message
|
153
|
+
raise LocationNotFoundError, "Requested position: #{position}\nSource:\n\n#{@source.pack("U*")}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
148
157
|
@pos += 1
|
149
158
|
@current_line += 1
|
150
159
|
end
|
data/lib/ruby_lsp/internal.rb
CHANGED
@@ -26,7 +26,6 @@ require "fileutils"
|
|
26
26
|
require "ruby-lsp"
|
27
27
|
require "ruby_lsp/base_server"
|
28
28
|
require "ruby_indexer/ruby_indexer"
|
29
|
-
require "core_ext/uri"
|
30
29
|
require "ruby_lsp/utils"
|
31
30
|
require "ruby_lsp/static_docs"
|
32
31
|
require "ruby_lsp/scope"
|
@@ -78,6 +77,7 @@ require "ruby_lsp/requests/hover"
|
|
78
77
|
require "ruby_lsp/requests/inlay_hints"
|
79
78
|
require "ruby_lsp/requests/on_type_formatting"
|
80
79
|
require "ruby_lsp/requests/prepare_type_hierarchy"
|
80
|
+
require "ruby_lsp/requests/prepare_rename"
|
81
81
|
require "ruby_lsp/requests/range_formatting"
|
82
82
|
require "ruby_lsp/requests/references"
|
83
83
|
require "ruby_lsp/requests/rename"
|
@@ -15,7 +15,7 @@ module RubyLsp
|
|
15
15
|
"bundle exec ruby"
|
16
16
|
rescue Bundler::GemfileNotFound
|
17
17
|
"ruby"
|
18
|
-
end
|
18
|
+
end,
|
19
19
|
String,
|
20
20
|
)
|
21
21
|
ACCESS_MODIFIERS = T.let([:public, :private, :protected], T::Array[Symbol])
|
@@ -198,7 +198,7 @@ module RubyLsp
|
|
198
198
|
|
199
199
|
@response_builder << create_code_lens(
|
200
200
|
node,
|
201
|
-
title: "Run",
|
201
|
+
title: "▶ Run",
|
202
202
|
command_name: "rubyLsp.runTest",
|
203
203
|
arguments: arguments,
|
204
204
|
data: { type: "test", **grouping_data },
|
@@ -206,7 +206,7 @@ module RubyLsp
|
|
206
206
|
|
207
207
|
@response_builder << create_code_lens(
|
208
208
|
node,
|
209
|
-
title: "Run In Terminal",
|
209
|
+
title: "▶ Run In Terminal",
|
210
210
|
command_name: "rubyLsp.runTestInTerminal",
|
211
211
|
arguments: arguments,
|
212
212
|
data: { type: "test_in_terminal", **grouping_data },
|
@@ -229,7 +229,11 @@ module RubyLsp
|
|
229
229
|
).returns(String)
|
230
230
|
end
|
231
231
|
def generate_test_command(group_stack: [], spec_name: nil, method_name: nil)
|
232
|
-
|
232
|
+
path = T.must(@path)
|
233
|
+
command = BASE_COMMAND
|
234
|
+
command += " -Itest" if File.fnmatch?("**/test/**/*", path, File::FNM_PATHNAME)
|
235
|
+
command += " -Ispec" if File.fnmatch?("**/spec/**/*", path, File::FNM_PATHNAME)
|
236
|
+
command += " #{path}"
|
233
237
|
|
234
238
|
case @global_state.test_library
|
235
239
|
when "minitest"
|
@@ -97,6 +97,12 @@ module RubyLsp
|
|
97
97
|
:on_instance_variable_operator_write_node_enter,
|
98
98
|
:on_instance_variable_or_write_node_enter,
|
99
99
|
:on_instance_variable_target_node_enter,
|
100
|
+
:on_class_variable_and_write_node_enter,
|
101
|
+
:on_class_variable_operator_write_node_enter,
|
102
|
+
:on_class_variable_or_write_node_enter,
|
103
|
+
:on_class_variable_read_node_enter,
|
104
|
+
:on_class_variable_target_node_enter,
|
105
|
+
:on_class_variable_write_node_enter,
|
100
106
|
)
|
101
107
|
end
|
102
108
|
|
@@ -110,13 +116,14 @@ module RubyLsp
|
|
110
116
|
name = constant_name(node)
|
111
117
|
return if name.nil?
|
112
118
|
|
119
|
+
range = range_from_location(node.location)
|
113
120
|
candidates = @index.constant_completion_candidates(name, @node_context.nesting)
|
114
121
|
candidates.each do |entries|
|
115
122
|
complete_name = T.must(entries.first).name
|
116
123
|
@response_builder << build_entry_completion(
|
117
124
|
complete_name,
|
118
125
|
name,
|
119
|
-
|
126
|
+
range,
|
120
127
|
entries,
|
121
128
|
top_level?(complete_name),
|
122
129
|
)
|
@@ -246,6 +253,36 @@ module RubyLsp
|
|
246
253
|
handle_instance_variable_completion(node.name.to_s, node.location)
|
247
254
|
end
|
248
255
|
|
256
|
+
sig { params(node: Prism::ClassVariableAndWriteNode).void }
|
257
|
+
def on_class_variable_and_write_node_enter(node)
|
258
|
+
handle_class_variable_completion(node.name.to_s, node.name_loc)
|
259
|
+
end
|
260
|
+
|
261
|
+
sig { params(node: Prism::ClassVariableOperatorWriteNode).void }
|
262
|
+
def on_class_variable_operator_write_node_enter(node)
|
263
|
+
handle_class_variable_completion(node.name.to_s, node.name_loc)
|
264
|
+
end
|
265
|
+
|
266
|
+
sig { params(node: Prism::ClassVariableOrWriteNode).void }
|
267
|
+
def on_class_variable_or_write_node_enter(node)
|
268
|
+
handle_class_variable_completion(node.name.to_s, node.name_loc)
|
269
|
+
end
|
270
|
+
|
271
|
+
sig { params(node: Prism::ClassVariableTargetNode).void }
|
272
|
+
def on_class_variable_target_node_enter(node)
|
273
|
+
handle_class_variable_completion(node.name.to_s, node.location)
|
274
|
+
end
|
275
|
+
|
276
|
+
sig { params(node: Prism::ClassVariableReadNode).void }
|
277
|
+
def on_class_variable_read_node_enter(node)
|
278
|
+
handle_class_variable_completion(node.name.to_s, node.location)
|
279
|
+
end
|
280
|
+
|
281
|
+
sig { params(node: Prism::ClassVariableWriteNode).void }
|
282
|
+
def on_class_variable_write_node_enter(node)
|
283
|
+
handle_class_variable_completion(node.name.to_s, node.name_loc)
|
284
|
+
end
|
285
|
+
|
249
286
|
private
|
250
287
|
|
251
288
|
sig { params(name: String, range: Interface::Range).void }
|
@@ -326,6 +363,37 @@ module RubyLsp
|
|
326
363
|
end
|
327
364
|
end
|
328
365
|
|
366
|
+
sig { params(name: String, location: Prism::Location).void }
|
367
|
+
def handle_class_variable_completion(name, location)
|
368
|
+
type = @type_inferrer.infer_receiver_type(@node_context)
|
369
|
+
return unless type
|
370
|
+
|
371
|
+
range = range_from_location(location)
|
372
|
+
|
373
|
+
@index.class_variable_completion_candidates(name, type.name).each do |entry|
|
374
|
+
variable_name = entry.name
|
375
|
+
|
376
|
+
label_details = Interface::CompletionItemLabelDetails.new(
|
377
|
+
description: entry.file_name,
|
378
|
+
)
|
379
|
+
|
380
|
+
@response_builder << Interface::CompletionItem.new(
|
381
|
+
label: variable_name,
|
382
|
+
label_details: label_details,
|
383
|
+
text_edit: Interface::TextEdit.new(
|
384
|
+
range: range,
|
385
|
+
new_text: variable_name,
|
386
|
+
),
|
387
|
+
kind: Constant::CompletionItemKind::FIELD,
|
388
|
+
data: {
|
389
|
+
owner_name: entry.owner&.name,
|
390
|
+
},
|
391
|
+
)
|
392
|
+
end
|
393
|
+
rescue RubyIndexer::Index::NonExistingNamespaceError
|
394
|
+
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
|
395
|
+
end
|
396
|
+
|
329
397
|
sig { params(name: String, location: Prism::Location).void }
|
330
398
|
def handle_instance_variable_completion(name, location)
|
331
399
|
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
@@ -335,6 +403,7 @@ module RubyLsp
|
|
335
403
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
336
404
|
return unless type
|
337
405
|
|
406
|
+
range = range_from_location(location)
|
338
407
|
@index.instance_variable_completion_candidates(name, type.name).each do |entry|
|
339
408
|
variable_name = entry.name
|
340
409
|
|
@@ -346,7 +415,7 @@ module RubyLsp
|
|
346
415
|
label: variable_name,
|
347
416
|
label_details: label_details,
|
348
417
|
text_edit: Interface::TextEdit.new(
|
349
|
-
range:
|
418
|
+
range: range,
|
350
419
|
new_text: variable_name,
|
351
420
|
),
|
352
421
|
kind: Constant::CompletionItemKind::FIELD,
|
@@ -368,9 +437,9 @@ module RubyLsp
|
|
368
437
|
|
369
438
|
return unless path_node_to_complete.is_a?(Prism::StringNode)
|
370
439
|
|
371
|
-
|
440
|
+
matched_uris = @index.search_require_paths(path_node_to_complete.content)
|
372
441
|
|
373
|
-
|
442
|
+
matched_uris.map!(&:require_path).sort!.each do |path|
|
374
443
|
@response_builder << build_completion(T.must(path), path_node_to_complete)
|
375
444
|
end
|
376
445
|
end
|
@@ -55,6 +55,12 @@ module RubyLsp
|
|
55
55
|
:on_symbol_node_enter,
|
56
56
|
:on_super_node_enter,
|
57
57
|
:on_forwarding_super_node_enter,
|
58
|
+
:on_class_variable_and_write_node_enter,
|
59
|
+
:on_class_variable_operator_write_node_enter,
|
60
|
+
:on_class_variable_or_write_node_enter,
|
61
|
+
:on_class_variable_read_node_enter,
|
62
|
+
:on_class_variable_target_node_enter,
|
63
|
+
:on_class_variable_write_node_enter,
|
58
64
|
)
|
59
65
|
end
|
60
66
|
|
@@ -196,6 +202,36 @@ module RubyLsp
|
|
196
202
|
handle_super_node_definition
|
197
203
|
end
|
198
204
|
|
205
|
+
sig { params(node: Prism::ClassVariableAndWriteNode).void }
|
206
|
+
def on_class_variable_and_write_node_enter(node)
|
207
|
+
handle_class_variable_definition(node.name.to_s)
|
208
|
+
end
|
209
|
+
|
210
|
+
sig { params(node: Prism::ClassVariableOperatorWriteNode).void }
|
211
|
+
def on_class_variable_operator_write_node_enter(node)
|
212
|
+
handle_class_variable_definition(node.name.to_s)
|
213
|
+
end
|
214
|
+
|
215
|
+
sig { params(node: Prism::ClassVariableOrWriteNode).void }
|
216
|
+
def on_class_variable_or_write_node_enter(node)
|
217
|
+
handle_class_variable_definition(node.name.to_s)
|
218
|
+
end
|
219
|
+
|
220
|
+
sig { params(node: Prism::ClassVariableTargetNode).void }
|
221
|
+
def on_class_variable_target_node_enter(node)
|
222
|
+
handle_class_variable_definition(node.name.to_s)
|
223
|
+
end
|
224
|
+
|
225
|
+
sig { params(node: Prism::ClassVariableReadNode).void }
|
226
|
+
def on_class_variable_read_node_enter(node)
|
227
|
+
handle_class_variable_definition(node.name.to_s)
|
228
|
+
end
|
229
|
+
|
230
|
+
sig { params(node: Prism::ClassVariableWriteNode).void }
|
231
|
+
def on_class_variable_write_node_enter(node)
|
232
|
+
handle_class_variable_definition(node.name.to_s)
|
233
|
+
end
|
234
|
+
|
199
235
|
private
|
200
236
|
|
201
237
|
sig { void }
|
@@ -223,7 +259,7 @@ module RubyLsp
|
|
223
259
|
location = entry.location
|
224
260
|
|
225
261
|
@response_builder << Interface::Location.new(
|
226
|
-
uri:
|
262
|
+
uri: entry.uri.to_s,
|
227
263
|
range: Interface::Range.new(
|
228
264
|
start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
|
229
265
|
end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
|
@@ -232,6 +268,24 @@ module RubyLsp
|
|
232
268
|
end
|
233
269
|
end
|
234
270
|
|
271
|
+
sig { params(name: String).void }
|
272
|
+
def handle_class_variable_definition(name)
|
273
|
+
type = @type_inferrer.infer_receiver_type(@node_context)
|
274
|
+
return unless type
|
275
|
+
|
276
|
+
entries = @index.resolve_class_variable(name, type.name)
|
277
|
+
return unless entries
|
278
|
+
|
279
|
+
entries.each do |entry|
|
280
|
+
@response_builder << Interface::Location.new(
|
281
|
+
uri: entry.uri.to_s,
|
282
|
+
range: range_from_location(entry.location),
|
283
|
+
)
|
284
|
+
end
|
285
|
+
rescue RubyIndexer::Index::NonExistingNamespaceError
|
286
|
+
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
|
287
|
+
end
|
288
|
+
|
235
289
|
sig { params(name: String).void }
|
236
290
|
def handle_instance_variable_definition(name)
|
237
291
|
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
@@ -248,7 +302,7 @@ module RubyLsp
|
|
248
302
|
location = entry.location
|
249
303
|
|
250
304
|
@response_builder << Interface::Location.new(
|
251
|
-
uri:
|
305
|
+
uri: entry.uri.to_s,
|
252
306
|
range: Interface::Range.new(
|
253
307
|
start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
|
254
308
|
end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
|
@@ -275,11 +329,11 @@ module RubyLsp
|
|
275
329
|
return unless methods
|
276
330
|
|
277
331
|
methods.each do |target_method|
|
278
|
-
|
279
|
-
next if sorbet_level_true_or_higher?(@sorbet_level) && not_in_dependencies?(
|
332
|
+
uri = target_method.uri
|
333
|
+
next if sorbet_level_true_or_higher?(@sorbet_level) && not_in_dependencies?(T.must(uri.full_path))
|
280
334
|
|
281
335
|
@response_builder << Interface::LocationLink.new(
|
282
|
-
target_uri:
|
336
|
+
target_uri: uri.to_s,
|
283
337
|
target_range: range_from_location(target_method.location),
|
284
338
|
target_selection_range: range_from_location(target_method.name_location),
|
285
339
|
)
|
@@ -290,20 +344,22 @@ module RubyLsp
|
|
290
344
|
def handle_require_definition(node, message)
|
291
345
|
case message
|
292
346
|
when :require
|
293
|
-
entry = @index.search_require_paths(node.content).find do |
|
294
|
-
|
347
|
+
entry = @index.search_require_paths(node.content).find do |uri|
|
348
|
+
uri.require_path == node.content
|
295
349
|
end
|
296
350
|
|
297
351
|
if entry
|
298
352
|
candidate = entry.full_path
|
299
353
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
354
|
+
if candidate
|
355
|
+
@response_builder << Interface::Location.new(
|
356
|
+
uri: URI::Generic.from_path(path: candidate).to_s,
|
357
|
+
range: Interface::Range.new(
|
358
|
+
start: Interface::Position.new(line: 0, character: 0),
|
359
|
+
end: Interface::Position.new(line: 0, character: 0),
|
360
|
+
),
|
361
|
+
)
|
362
|
+
end
|
307
363
|
end
|
308
364
|
when :require_relative
|
309
365
|
required_file = "#{node.content}.rb"
|
@@ -346,11 +402,11 @@ module RubyLsp
|
|
346
402
|
# If the project has Sorbet, then we only want to handle go to definition for constants defined in gems, as an
|
347
403
|
# additional behavior on top of jumping to RBIs. The only sigil where Sorbet cannot handle constants is typed
|
348
404
|
# ignore
|
349
|
-
|
350
|
-
next if @sorbet_level != RubyDocument::SorbetLevel::Ignore && not_in_dependencies?(
|
405
|
+
uri = entry.uri
|
406
|
+
next if @sorbet_level != RubyDocument::SorbetLevel::Ignore && not_in_dependencies?(T.must(uri.full_path))
|
351
407
|
|
352
408
|
@response_builder << Interface::LocationLink.new(
|
353
|
-
target_uri:
|
409
|
+
target_uri: uri.to_s,
|
354
410
|
target_range: range_from_location(entry.location),
|
355
411
|
target_selection_range: range_from_location(entry.name_location),
|
356
412
|
)
|
@@ -41,6 +41,7 @@ module RubyLsp
|
|
41
41
|
:on_module_node_enter,
|
42
42
|
:on_module_node_leave,
|
43
43
|
:on_instance_variable_write_node_enter,
|
44
|
+
:on_instance_variable_target_node_enter,
|
44
45
|
:on_instance_variable_operator_write_node_enter,
|
45
46
|
:on_instance_variable_or_write_node_enter,
|
46
47
|
:on_instance_variable_and_write_node_enter,
|
@@ -272,6 +273,16 @@ module RubyLsp
|
|
272
273
|
)
|
273
274
|
end
|
274
275
|
|
276
|
+
sig { params(node: Prism::InstanceVariableTargetNode).void }
|
277
|
+
def on_instance_variable_target_node_enter(node)
|
278
|
+
create_document_symbol(
|
279
|
+
name: node.name.to_s,
|
280
|
+
kind: Constant::SymbolKind::FIELD,
|
281
|
+
range_location: node.location,
|
282
|
+
selection_range_location: node.location,
|
283
|
+
)
|
284
|
+
end
|
285
|
+
|
275
286
|
sig { params(node: Prism::InstanceVariableOperatorWriteNode).void }
|
276
287
|
def on_instance_variable_operator_write_node_enter(node)
|
277
288
|
create_document_symbol(
|
@@ -329,7 +340,7 @@ module RubyLsp
|
|
329
340
|
).returns(Interface::DocumentSymbol)
|
330
341
|
end
|
331
342
|
def create_document_symbol(name:, kind:, range_location:, selection_range_location:)
|
332
|
-
name = "<blank>" if name.empty?
|
343
|
+
name = "<blank>" if name.strip.empty?
|
333
344
|
symbol = Interface::DocumentSymbol.new(
|
334
345
|
name: name,
|
335
346
|
kind: kind,
|
@@ -195,7 +195,7 @@ module RubyLsp
|
|
195
195
|
def push_comment_ranges
|
196
196
|
# Group comments that are on consecutive lines and then push ranges for each group that has at least 2 comments
|
197
197
|
@comments.chunk_while do |this, other|
|
198
|
-
this.location.end_line + 1 == other.location.start_line
|
198
|
+
this.location.end_line + 1 == other.location.start_line && !this.trailing? && !other.trailing?
|
199
199
|
end.each do |chunk|
|
200
200
|
next if chunk.length == 1
|
201
201
|
|
@@ -31,6 +31,12 @@ module RubyLsp
|
|
31
31
|
Prism::SuperNode,
|
32
32
|
Prism::ForwardingSuperNode,
|
33
33
|
Prism::YieldNode,
|
34
|
+
Prism::ClassVariableAndWriteNode,
|
35
|
+
Prism::ClassVariableOperatorWriteNode,
|
36
|
+
Prism::ClassVariableOrWriteNode,
|
37
|
+
Prism::ClassVariableReadNode,
|
38
|
+
Prism::ClassVariableTargetNode,
|
39
|
+
Prism::ClassVariableWriteNode,
|
34
40
|
],
|
35
41
|
T::Array[T.class_of(Prism::Node)],
|
36
42
|
)
|
@@ -85,6 +91,12 @@ module RubyLsp
|
|
85
91
|
:on_string_node_enter,
|
86
92
|
:on_interpolated_string_node_enter,
|
87
93
|
:on_yield_node_enter,
|
94
|
+
:on_class_variable_and_write_node_enter,
|
95
|
+
:on_class_variable_operator_write_node_enter,
|
96
|
+
:on_class_variable_or_write_node_enter,
|
97
|
+
:on_class_variable_read_node_enter,
|
98
|
+
:on_class_variable_target_node_enter,
|
99
|
+
:on_class_variable_write_node_enter,
|
88
100
|
)
|
89
101
|
end
|
90
102
|
|
@@ -215,6 +227,36 @@ module RubyLsp
|
|
215
227
|
handle_keyword_documentation(node.keyword)
|
216
228
|
end
|
217
229
|
|
230
|
+
sig { params(node: Prism::ClassVariableAndWriteNode).void }
|
231
|
+
def on_class_variable_and_write_node_enter(node)
|
232
|
+
handle_class_variable_hover(node.name.to_s)
|
233
|
+
end
|
234
|
+
|
235
|
+
sig { params(node: Prism::ClassVariableOperatorWriteNode).void }
|
236
|
+
def on_class_variable_operator_write_node_enter(node)
|
237
|
+
handle_class_variable_hover(node.name.to_s)
|
238
|
+
end
|
239
|
+
|
240
|
+
sig { params(node: Prism::ClassVariableOrWriteNode).void }
|
241
|
+
def on_class_variable_or_write_node_enter(node)
|
242
|
+
handle_class_variable_hover(node.name.to_s)
|
243
|
+
end
|
244
|
+
|
245
|
+
sig { params(node: Prism::ClassVariableTargetNode).void }
|
246
|
+
def on_class_variable_target_node_enter(node)
|
247
|
+
handle_class_variable_hover(node.name.to_s)
|
248
|
+
end
|
249
|
+
|
250
|
+
sig { params(node: Prism::ClassVariableReadNode).void }
|
251
|
+
def on_class_variable_read_node_enter(node)
|
252
|
+
handle_class_variable_hover(node.name.to_s)
|
253
|
+
end
|
254
|
+
|
255
|
+
sig { params(node: Prism::ClassVariableWriteNode).void }
|
256
|
+
def on_class_variable_write_node_enter(node)
|
257
|
+
handle_class_variable_hover(node.name.to_s)
|
258
|
+
end
|
259
|
+
|
218
260
|
private
|
219
261
|
|
220
262
|
sig { params(node: T.any(Prism::InterpolatedStringNode, Prism::StringNode)).void }
|
@@ -317,6 +359,21 @@ module RubyLsp
|
|
317
359
|
end
|
318
360
|
end
|
319
361
|
|
362
|
+
sig { params(name: String).void }
|
363
|
+
def handle_class_variable_hover(name)
|
364
|
+
type = @type_inferrer.infer_receiver_type(@node_context)
|
365
|
+
return unless type
|
366
|
+
|
367
|
+
entries = @index.resolve_class_variable(name, type.name)
|
368
|
+
return unless entries
|
369
|
+
|
370
|
+
categorized_markdown_from_index_entries(name, entries).each do |category, content|
|
371
|
+
@response_builder.push(content, category: category)
|
372
|
+
end
|
373
|
+
rescue RubyIndexer::Index::NonExistingNamespaceError
|
374
|
+
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
|
375
|
+
end
|
376
|
+
|
320
377
|
sig { params(name: String, location: Prism::Location).void }
|
321
378
|
def generate_hover(name, location)
|
322
379
|
entries = @index.resolve(name, @node_context.nesting)
|
@@ -62,6 +62,12 @@ module RubyLsp
|
|
62
62
|
Prism::InstanceVariableOrWriteNode,
|
63
63
|
Prism::InstanceVariableTargetNode,
|
64
64
|
Prism::InstanceVariableWriteNode,
|
65
|
+
Prism::ClassVariableAndWriteNode,
|
66
|
+
Prism::ClassVariableOperatorWriteNode,
|
67
|
+
Prism::ClassVariableOrWriteNode,
|
68
|
+
Prism::ClassVariableReadNode,
|
69
|
+
Prism::ClassVariableTargetNode,
|
70
|
+
Prism::ClassVariableWriteNode,
|
65
71
|
],
|
66
72
|
code_units_cache: document.code_units_cache,
|
67
73
|
)
|
@@ -49,7 +49,8 @@ module RubyLsp
|
|
49
49
|
if owner_name
|
50
50
|
entries = entries.select do |entry|
|
51
51
|
(entry.is_a?(RubyIndexer::Entry::Member) || entry.is_a?(RubyIndexer::Entry::InstanceVariable) ||
|
52
|
-
entry.is_a?(RubyIndexer::Entry::MethodAlias)
|
52
|
+
entry.is_a?(RubyIndexer::Entry::MethodAlias) || entry.is_a?(RubyIndexer::Entry::ClassVariable)) &&
|
53
|
+
entry.owner&.name == owner_name
|
53
54
|
end
|
54
55
|
end
|
55
56
|
|
@@ -56,6 +56,12 @@ module RubyLsp
|
|
56
56
|
Prism::StringNode,
|
57
57
|
Prism::SuperNode,
|
58
58
|
Prism::ForwardingSuperNode,
|
59
|
+
Prism::ClassVariableAndWriteNode,
|
60
|
+
Prism::ClassVariableOperatorWriteNode,
|
61
|
+
Prism::ClassVariableOrWriteNode,
|
62
|
+
Prism::ClassVariableReadNode,
|
63
|
+
Prism::ClassVariableTargetNode,
|
64
|
+
Prism::ClassVariableWriteNode,
|
59
65
|
],
|
60
66
|
code_units_cache: document.code_units_cache,
|
61
67
|
)
|