ruby-lsp 0.22.1 → 0.23.0
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 +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
|
)
|