ruby-lsp 0.17.8 → 0.17.9
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/lib/ruby_indexer/lib/ruby_indexer/index.rb +28 -0
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +14 -26
- data/lib/ruby_indexer/test/index_test.rb +15 -0
- data/lib/ruby_lsp/document.rb +27 -4
- data/lib/ruby_lsp/global_state.rb +2 -1
- data/lib/ruby_lsp/listeners/completion.rb +53 -33
- data/lib/ruby_lsp/listeners/definition.rb +23 -11
- data/lib/ruby_lsp/listeners/hover.rb +21 -9
- data/lib/ruby_lsp/listeners/signature_help.rb +13 -6
- data/lib/ruby_lsp/requests/completion.rb +3 -3
- data/lib/ruby_lsp/requests/completion_resolve.rb +10 -3
- data/lib/ruby_lsp/requests/definition.rb +3 -3
- data/lib/ruby_lsp/requests/hover.rb +3 -3
- data/lib/ruby_lsp/requests/signature_help.rb +3 -3
- data/lib/ruby_lsp/requests/support/common.rb +11 -2
- data/lib/ruby_lsp/server.rb +9 -7
- data/lib/ruby_lsp/store.rb +6 -1
- data/lib/ruby_lsp/test_helper.rb +1 -1
- data/lib/ruby_lsp/type_inferrer.rb +48 -10
- data/lib/ruby_lsp/utils.rb +1 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3deccbc9952a61d46392be2f050a4684b108e7cd3da0085a4e8997168dfc021
|
4
|
+
data.tar.gz: d1b9a804d9e67aa2d23b39f12c79c6e1d5ba940ec360170623f7e0870e7d6c78
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe2e99145d0f268644a5d8feeff49146e635998ab8ff3eff18f47b1f555bb249b296966d0a431c930ab6ebca58032ed954a338f7c075300869545711bcfae50e
|
7
|
+
data.tar.gz: 36f253a202755a49e33b8d6c4906aa0acfebe9fe52772519684d433649d7fbadfee186274eba94027017535e55438333759477e2dbb87a7da7e9ead0bc98aaa7
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.17.
|
1
|
+
0.17.9
|
@@ -84,6 +84,34 @@ module RubyIndexer
|
|
84
84
|
@require_paths_tree.search(query)
|
85
85
|
end
|
86
86
|
|
87
|
+
# Searches for a constant based on an unqualified name and returns the first possible match regardless of whether
|
88
|
+
# there are more possible matching entries
|
89
|
+
sig do
|
90
|
+
params(
|
91
|
+
name: String,
|
92
|
+
).returns(T.nilable(T::Array[T.any(
|
93
|
+
Entry::Namespace,
|
94
|
+
Entry::Alias,
|
95
|
+
Entry::UnresolvedAlias,
|
96
|
+
Entry::Constant,
|
97
|
+
)]))
|
98
|
+
end
|
99
|
+
def first_unqualified_const(name)
|
100
|
+
_name, entries = @entries.find do |const_name, _entries|
|
101
|
+
const_name.end_with?(name)
|
102
|
+
end
|
103
|
+
|
104
|
+
T.cast(
|
105
|
+
entries,
|
106
|
+
T.nilable(T::Array[T.any(
|
107
|
+
Entry::Namespace,
|
108
|
+
Entry::Alias,
|
109
|
+
Entry::UnresolvedAlias,
|
110
|
+
Entry::Constant,
|
111
|
+
)]),
|
112
|
+
)
|
113
|
+
end
|
114
|
+
|
87
115
|
# Searches entries in the index based on an exact prefix, intended for providing autocomplete. All possible matches
|
88
116
|
# to the prefix are returned. The return is an array of arrays, where each entry is the array of entries for a given
|
89
117
|
# name match. For example:
|
@@ -37,45 +37,33 @@ module RubyIndexer
|
|
37
37
|
sig { params(declaration: RBS::AST::Declarations::Base, pathname: Pathname).void }
|
38
38
|
def process_declaration(declaration, pathname)
|
39
39
|
case declaration
|
40
|
-
when RBS::AST::Declarations::Class
|
41
|
-
|
42
|
-
when RBS::AST::Declarations::Module
|
43
|
-
handle_module_declaration(declaration, pathname)
|
40
|
+
when RBS::AST::Declarations::Class, RBS::AST::Declarations::Module
|
41
|
+
handle_class_or_module_declaration(declaration, pathname)
|
44
42
|
else # rubocop:disable Style/EmptyElse
|
45
43
|
# Other kinds not yet handled
|
46
44
|
end
|
47
45
|
end
|
48
46
|
|
49
|
-
sig
|
50
|
-
|
51
|
-
nesting = [declaration.name.name.to_s]
|
52
|
-
file_path = pathname.to_s
|
53
|
-
location = to_ruby_indexer_location(declaration.location)
|
54
|
-
comments = Array(declaration.comment&.string)
|
55
|
-
parent_class = declaration.super_class&.name&.name&.to_s
|
56
|
-
class_entry = Entry::Class.new(nesting, file_path, location, location, comments, parent_class)
|
57
|
-
add_declaration_mixins_to_entry(declaration, class_entry)
|
58
|
-
@index.add(class_entry)
|
59
|
-
declaration.members.each do |member|
|
60
|
-
next unless member.is_a?(RBS::AST::Members::MethodDefinition)
|
61
|
-
|
62
|
-
handle_method(member, class_entry)
|
63
|
-
end
|
47
|
+
sig do
|
48
|
+
params(declaration: T.any(RBS::AST::Declarations::Class, RBS::AST::Declarations::Module), pathname: Pathname).void
|
64
49
|
end
|
65
|
-
|
66
|
-
sig { params(declaration: RBS::AST::Declarations::Module, pathname: Pathname).void }
|
67
|
-
def handle_module_declaration(declaration, pathname)
|
50
|
+
def handle_class_or_module_declaration(declaration, pathname)
|
68
51
|
nesting = [declaration.name.name.to_s]
|
69
52
|
file_path = pathname.to_s
|
70
53
|
location = to_ruby_indexer_location(declaration.location)
|
71
54
|
comments = Array(declaration.comment&.string)
|
72
|
-
|
73
|
-
|
74
|
-
|
55
|
+
entry = if declaration.is_a?(RBS::AST::Declarations::Class)
|
56
|
+
parent_class = declaration.super_class&.name&.name&.to_s
|
57
|
+
Entry::Class.new(nesting, file_path, location, location, comments, parent_class)
|
58
|
+
else
|
59
|
+
Entry::Module.new(nesting, file_path, location, location, comments)
|
60
|
+
end
|
61
|
+
add_declaration_mixins_to_entry(declaration, entry)
|
62
|
+
@index.add(entry)
|
75
63
|
declaration.members.each do |member|
|
76
64
|
next unless member.is_a?(RBS::AST::Members::MethodDefinition)
|
77
65
|
|
78
|
-
handle_method(member,
|
66
|
+
handle_method(member, entry)
|
79
67
|
end
|
80
68
|
end
|
81
69
|
|
@@ -1542,6 +1542,21 @@ module RubyIndexer
|
|
1542
1542
|
assert_empty(@index.method_completion_candidates("bar", "Foo"))
|
1543
1543
|
end
|
1544
1544
|
|
1545
|
+
def test_first_unqualified_const
|
1546
|
+
index(<<~RUBY)
|
1547
|
+
module Foo
|
1548
|
+
class Bar; end
|
1549
|
+
end
|
1550
|
+
|
1551
|
+
module Baz
|
1552
|
+
class Bar; end
|
1553
|
+
end
|
1554
|
+
RUBY
|
1555
|
+
|
1556
|
+
entry = T.must(@index.first_unqualified_const("Bar")&.first)
|
1557
|
+
assert_equal("Foo::Bar", entry.name)
|
1558
|
+
end
|
1559
|
+
|
1545
1560
|
def test_completion_does_not_duplicate_overridden_methods
|
1546
1561
|
index(<<~RUBY)
|
1547
1562
|
class Foo
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -10,6 +10,16 @@ module RubyLsp
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
+
class SorbetLevel < T::Enum
|
14
|
+
enums do
|
15
|
+
None = new("none")
|
16
|
+
Ignore = new("ignore")
|
17
|
+
False = new("false")
|
18
|
+
True = new("true")
|
19
|
+
Strict = new("strict")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
13
23
|
extend T::Sig
|
14
24
|
extend T::Helpers
|
15
25
|
|
@@ -213,10 +223,23 @@ module RubyLsp
|
|
213
223
|
NodeContext.new(closest, parent, nesting_nodes, call_node)
|
214
224
|
end
|
215
225
|
|
216
|
-
sig { returns(
|
217
|
-
def
|
218
|
-
parse_result.magic_comments.
|
219
|
-
comment.key == "typed"
|
226
|
+
sig { returns(SorbetLevel) }
|
227
|
+
def sorbet_level
|
228
|
+
sigil = parse_result.magic_comments.find do |comment|
|
229
|
+
comment.key == "typed"
|
230
|
+
end&.value
|
231
|
+
|
232
|
+
case sigil
|
233
|
+
when "ignore"
|
234
|
+
SorbetLevel::Ignore
|
235
|
+
when "false"
|
236
|
+
SorbetLevel::False
|
237
|
+
when "true"
|
238
|
+
SorbetLevel::True
|
239
|
+
when "strict", "strong"
|
240
|
+
SorbetLevel::Strict
|
241
|
+
else
|
242
|
+
SorbetLevel::None
|
220
243
|
end
|
221
244
|
end
|
222
245
|
|
@@ -36,10 +36,10 @@ module RubyLsp
|
|
36
36
|
@test_library = T.let("minitest", String)
|
37
37
|
@has_type_checker = T.let(true, T::Boolean)
|
38
38
|
@index = T.let(RubyIndexer::Index.new, RubyIndexer::Index)
|
39
|
-
@type_inferrer = T.let(TypeInferrer.new(@index), TypeInferrer)
|
40
39
|
@supported_formatters = T.let({}, T::Hash[String, Requests::Support::Formatter])
|
41
40
|
@supports_watching_files = T.let(false, T::Boolean)
|
42
41
|
@experimental_features = T.let(false, T::Boolean)
|
42
|
+
@type_inferrer = T.let(TypeInferrer.new(@index, @experimental_features), TypeInferrer)
|
43
43
|
end
|
44
44
|
|
45
45
|
sig { params(identifier: String, instance: Requests::Support::Formatter).void }
|
@@ -90,6 +90,7 @@ module RubyLsp
|
|
90
90
|
end
|
91
91
|
|
92
92
|
@experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled) || false
|
93
|
+
@type_inferrer.experimental_features = @experimental_features
|
93
94
|
end
|
94
95
|
|
95
96
|
sig { returns(String) }
|
@@ -56,7 +56,7 @@ module RubyLsp
|
|
56
56
|
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem],
|
57
57
|
global_state: GlobalState,
|
58
58
|
node_context: NodeContext,
|
59
|
-
|
59
|
+
sorbet_level: Document::SorbetLevel,
|
60
60
|
dispatcher: Prism::Dispatcher,
|
61
61
|
uri: URI::Generic,
|
62
62
|
trigger_character: T.nilable(String),
|
@@ -66,7 +66,7 @@ module RubyLsp
|
|
66
66
|
response_builder,
|
67
67
|
global_state,
|
68
68
|
node_context,
|
69
|
-
|
69
|
+
sorbet_level,
|
70
70
|
dispatcher,
|
71
71
|
uri,
|
72
72
|
trigger_character
|
@@ -76,7 +76,7 @@ module RubyLsp
|
|
76
76
|
@index = T.let(global_state.index, RubyIndexer::Index)
|
77
77
|
@type_inferrer = T.let(global_state.type_inferrer, TypeInferrer)
|
78
78
|
@node_context = node_context
|
79
|
-
@
|
79
|
+
@sorbet_level = sorbet_level
|
80
80
|
@uri = uri
|
81
81
|
@trigger_character = trigger_character
|
82
82
|
|
@@ -97,7 +97,9 @@ module RubyLsp
|
|
97
97
|
# Handle completion on regular constant references (e.g. `Bar`)
|
98
98
|
sig { params(node: Prism::ConstantReadNode).void }
|
99
99
|
def on_constant_read_node_enter(node)
|
100
|
-
|
100
|
+
# The only scenario where Sorbet doesn't provide constant completion is on ignored files. Even if the file has
|
101
|
+
# no sigil, Sorbet will still provide completion for constants
|
102
|
+
return if @sorbet_level != Document::SorbetLevel::Ignore
|
101
103
|
|
102
104
|
name = constant_name(node)
|
103
105
|
return if name.nil?
|
@@ -118,7 +120,9 @@ module RubyLsp
|
|
118
120
|
# Handle completion on namespaced constant references (e.g. `Foo::Bar`)
|
119
121
|
sig { params(node: Prism::ConstantPathNode).void }
|
120
122
|
def on_constant_path_node_enter(node)
|
121
|
-
|
123
|
+
# The only scenario where Sorbet doesn't provide constant completion is on ignored files. Even if the file has
|
124
|
+
# no sigil, Sorbet will still provide completion for constants
|
125
|
+
return if @sorbet_level != Document::SorbetLevel::Ignore
|
122
126
|
|
123
127
|
name = constant_name(node)
|
124
128
|
return if name.nil?
|
@@ -128,28 +132,32 @@ module RubyLsp
|
|
128
132
|
|
129
133
|
sig { params(node: Prism::CallNode).void }
|
130
134
|
def on_call_node_enter(node)
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
135
|
+
# The only scenario where Sorbet doesn't provide constant completion is on ignored files. Even if the file has
|
136
|
+
# no sigil, Sorbet will still provide completion for constants
|
137
|
+
if @sorbet_level == Document::SorbetLevel::Ignore
|
138
|
+
receiver = node.receiver
|
139
|
+
|
140
|
+
# When writing `Foo::`, the AST assigns a method call node (because you can use that syntax to invoke
|
141
|
+
# singleton methods). However, in addition to providing method completion, we also need to show possible
|
142
|
+
# constant completions
|
143
|
+
if (receiver.is_a?(Prism::ConstantReadNode) || receiver.is_a?(Prism::ConstantPathNode)) &&
|
144
|
+
node.call_operator == "::"
|
145
|
+
|
146
|
+
name = constant_name(receiver)
|
147
|
+
|
148
|
+
if name
|
149
|
+
start_loc = node.location
|
150
|
+
end_loc = T.must(node.call_operator_loc)
|
151
|
+
|
152
|
+
constant_path_completion(
|
153
|
+
"#{name}::",
|
154
|
+
Interface::Range.new(
|
155
|
+
start: Interface::Position.new(line: start_loc.start_line - 1, character: start_loc.start_column),
|
156
|
+
end: Interface::Position.new(line: end_loc.end_line - 1, character: end_loc.end_column),
|
157
|
+
),
|
158
|
+
)
|
159
|
+
return
|
160
|
+
end
|
153
161
|
end
|
154
162
|
end
|
155
163
|
|
@@ -162,7 +170,7 @@ module RubyLsp
|
|
162
170
|
when "require_relative"
|
163
171
|
complete_require_relative(node)
|
164
172
|
else
|
165
|
-
complete_methods(node, name)
|
173
|
+
complete_methods(node, name)
|
166
174
|
end
|
167
175
|
end
|
168
176
|
|
@@ -247,10 +255,14 @@ module RubyLsp
|
|
247
255
|
|
248
256
|
sig { params(name: String, location: Prism::Location).void }
|
249
257
|
def handle_instance_variable_completion(name, location)
|
258
|
+
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
259
|
+
# to provide all features for them
|
260
|
+
return if @sorbet_level == Document::SorbetLevel::Strict
|
261
|
+
|
250
262
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
251
263
|
return unless type
|
252
264
|
|
253
|
-
@index.instance_variable_completion_candidates(name, type).each do |entry|
|
265
|
+
@index.instance_variable_completion_candidates(name, type.name).each do |entry|
|
254
266
|
variable_name = entry.name
|
255
267
|
|
256
268
|
label_details = Interface::CompletionItemLabelDetails.new(
|
@@ -321,12 +333,16 @@ module RubyLsp
|
|
321
333
|
|
322
334
|
sig { params(node: Prism::CallNode, name: String).void }
|
323
335
|
def complete_methods(node, name)
|
324
|
-
# If the node has a receiver, then we don't need to provide local nor keyword completions
|
325
|
-
|
336
|
+
# If the node has a receiver, then we don't need to provide local nor keyword completions. Sorbet can provide
|
337
|
+
# local and keyword completion for any file with a Sorbet level of true or higher
|
338
|
+
if !sorbet_level_true_or_higher?(@sorbet_level) && !node.receiver
|
326
339
|
add_local_completions(node, name)
|
327
340
|
add_keyword_completions(node, name)
|
328
341
|
end
|
329
342
|
|
343
|
+
# Sorbet can provide completion for methods invoked on self on typed true or higher files
|
344
|
+
return if sorbet_level_true_or_higher?(@sorbet_level) && self_receiver?(node)
|
345
|
+
|
330
346
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
331
347
|
return unless type
|
332
348
|
|
@@ -350,8 +366,11 @@ module RubyLsp
|
|
350
366
|
|
351
367
|
return unless range
|
352
368
|
|
353
|
-
|
369
|
+
guessed_type = type.name
|
370
|
+
|
371
|
+
@index.method_completion_candidates(method_name, type.name).each do |entry|
|
354
372
|
entry_name = entry.name
|
373
|
+
owner_name = entry.owner&.name
|
355
374
|
|
356
375
|
label_details = Interface::CompletionItemLabelDetails.new(
|
357
376
|
description: entry.file_name,
|
@@ -364,7 +383,8 @@ module RubyLsp
|
|
364
383
|
text_edit: Interface::TextEdit.new(range: range, new_text: entry_name),
|
365
384
|
kind: Constant::CompletionItemKind::METHOD,
|
366
385
|
data: {
|
367
|
-
owner_name:
|
386
|
+
owner_name: owner_name,
|
387
|
+
guessed_type: guessed_type,
|
368
388
|
},
|
369
389
|
)
|
370
390
|
end
|
@@ -20,10 +20,10 @@ module RubyLsp
|
|
20
20
|
uri: URI::Generic,
|
21
21
|
node_context: NodeContext,
|
22
22
|
dispatcher: Prism::Dispatcher,
|
23
|
-
|
23
|
+
sorbet_level: Document::SorbetLevel,
|
24
24
|
).void
|
25
25
|
end
|
26
|
-
def initialize(response_builder, global_state, language_id, uri, node_context, dispatcher,
|
26
|
+
def initialize(response_builder, global_state, language_id, uri, node_context, dispatcher, sorbet_level) # rubocop:disable Metrics/ParameterLists
|
27
27
|
@response_builder = response_builder
|
28
28
|
@global_state = global_state
|
29
29
|
@index = T.let(global_state.index, RubyIndexer::Index)
|
@@ -31,7 +31,7 @@ module RubyLsp
|
|
31
31
|
@language_id = language_id
|
32
32
|
@uri = uri
|
33
33
|
@node_context = node_context
|
34
|
-
@
|
34
|
+
@sorbet_level = sorbet_level
|
35
35
|
|
36
36
|
dispatcher.register(
|
37
37
|
self,
|
@@ -53,6 +53,11 @@ module RubyLsp
|
|
53
53
|
|
54
54
|
sig { params(node: Prism::CallNode).void }
|
55
55
|
def on_call_node_enter(node)
|
56
|
+
# Sorbet can handle go to definition for methods invoked on self on typed true or higher
|
57
|
+
if (@sorbet_level == Document::SorbetLevel::True || @sorbet_level == Document::SorbetLevel::Strict) &&
|
58
|
+
self_receiver?(node)
|
59
|
+
end
|
60
|
+
|
56
61
|
message = node.message
|
57
62
|
return unless message
|
58
63
|
|
@@ -60,7 +65,7 @@ module RubyLsp
|
|
60
65
|
|
61
66
|
# Until we can properly infer the receiver type in erb files (maybe with ruby-lsp-rails),
|
62
67
|
# treating method calls' type as `nil` will allow users to get some completion support first
|
63
|
-
if @language_id == Document::LanguageId::ERB && inferrer_receiver_type == "Object"
|
68
|
+
if @language_id == Document::LanguageId::ERB && inferrer_receiver_type&.name == "Object"
|
64
69
|
inferrer_receiver_type = nil
|
65
70
|
end
|
66
71
|
|
@@ -149,6 +154,9 @@ module RubyLsp
|
|
149
154
|
|
150
155
|
sig { void }
|
151
156
|
def handle_super_node_definition
|
157
|
+
# Sorbet can handle super hover on typed true or higher
|
158
|
+
return if sorbet_level_true_or_higher?(@sorbet_level)
|
159
|
+
|
152
160
|
surrounding_method = @node_context.surrounding_method
|
153
161
|
return unless surrounding_method
|
154
162
|
|
@@ -161,10 +169,14 @@ module RubyLsp
|
|
161
169
|
|
162
170
|
sig { params(name: String).void }
|
163
171
|
def handle_instance_variable_definition(name)
|
172
|
+
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
173
|
+
# to provide all features for them
|
174
|
+
return if @sorbet_level == Document::SorbetLevel::Strict
|
175
|
+
|
164
176
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
165
177
|
return unless type
|
166
178
|
|
167
|
-
entries = @index.resolve_instance_variable(name, type)
|
179
|
+
entries = @index.resolve_instance_variable(name, type.name)
|
168
180
|
return unless entries
|
169
181
|
|
170
182
|
entries.each do |entry|
|
@@ -182,10 +194,10 @@ module RubyLsp
|
|
182
194
|
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
|
183
195
|
end
|
184
196
|
|
185
|
-
sig { params(message: String, receiver_type: T.nilable(
|
197
|
+
sig { params(message: String, receiver_type: T.nilable(TypeInferrer::Type), inherited_only: T::Boolean).void }
|
186
198
|
def handle_method_definition(message, receiver_type, inherited_only: false)
|
187
199
|
methods = if receiver_type
|
188
|
-
@index.resolve_method(message, receiver_type, inherited_only: inherited_only)
|
200
|
+
@index.resolve_method(message, receiver_type.name, inherited_only: inherited_only)
|
189
201
|
else
|
190
202
|
# If the method doesn't have a receiver, then we provide a few candidates to jump to
|
191
203
|
# But we don't want to provide too many candidates, as it can be overwhelming
|
@@ -196,7 +208,7 @@ module RubyLsp
|
|
196
208
|
|
197
209
|
methods.each do |target_method|
|
198
210
|
file_path = target_method.file_path
|
199
|
-
next if @
|
211
|
+
next if sorbet_level_true_or_higher?(@sorbet_level) && not_in_dependencies?(file_path)
|
200
212
|
|
201
213
|
@response_builder << Interface::LocationLink.new(
|
202
214
|
target_uri: URI::Generic.from_path(path: file_path).to_s,
|
@@ -253,10 +265,10 @@ module RubyLsp
|
|
253
265
|
|
254
266
|
entries.each do |entry|
|
255
267
|
# If the project has Sorbet, then we only want to handle go to definition for constants defined in gems, as an
|
256
|
-
# additional behavior on top of jumping to RBIs.
|
257
|
-
#
|
268
|
+
# additional behavior on top of jumping to RBIs. The only sigil where Sorbet cannot handle constants is typed
|
269
|
+
# ignore
|
258
270
|
file_path = entry.file_path
|
259
|
-
next if @
|
271
|
+
next if @sorbet_level != Document::SorbetLevel::Ignore && not_in_dependencies?(file_path)
|
260
272
|
|
261
273
|
@response_builder << Interface::LocationLink.new(
|
262
274
|
target_uri: URI::Generic.from_path(path: file_path).to_s,
|
@@ -42,17 +42,17 @@ module RubyLsp
|
|
42
42
|
uri: URI::Generic,
|
43
43
|
node_context: NodeContext,
|
44
44
|
dispatcher: Prism::Dispatcher,
|
45
|
-
|
45
|
+
sorbet_level: Document::SorbetLevel,
|
46
46
|
).void
|
47
47
|
end
|
48
|
-
def initialize(response_builder, global_state, uri, node_context, dispatcher,
|
48
|
+
def initialize(response_builder, global_state, uri, node_context, dispatcher, sorbet_level) # rubocop:disable Metrics/ParameterLists
|
49
49
|
@response_builder = response_builder
|
50
50
|
@global_state = global_state
|
51
51
|
@index = T.let(global_state.index, RubyIndexer::Index)
|
52
52
|
@type_inferrer = T.let(global_state.type_inferrer, TypeInferrer)
|
53
53
|
@path = T.let(uri.to_standardized_path, T.nilable(String))
|
54
54
|
@node_context = node_context
|
55
|
-
@
|
55
|
+
@sorbet_level = sorbet_level
|
56
56
|
|
57
57
|
dispatcher.register(
|
58
58
|
self,
|
@@ -73,7 +73,7 @@ module RubyLsp
|
|
73
73
|
|
74
74
|
sig { params(node: Prism::ConstantReadNode).void }
|
75
75
|
def on_constant_read_node_enter(node)
|
76
|
-
return if @
|
76
|
+
return if @sorbet_level != Document::SorbetLevel::Ignore
|
77
77
|
|
78
78
|
name = constant_name(node)
|
79
79
|
return if name.nil?
|
@@ -83,14 +83,14 @@ module RubyLsp
|
|
83
83
|
|
84
84
|
sig { params(node: Prism::ConstantWriteNode).void }
|
85
85
|
def on_constant_write_node_enter(node)
|
86
|
-
return if @
|
86
|
+
return if @sorbet_level != Document::SorbetLevel::Ignore
|
87
87
|
|
88
88
|
generate_hover(node.name.to_s, node.name_loc)
|
89
89
|
end
|
90
90
|
|
91
91
|
sig { params(node: Prism::ConstantPathNode).void }
|
92
92
|
def on_constant_path_node_enter(node)
|
93
|
-
return if @
|
93
|
+
return if @sorbet_level != Document::SorbetLevel::Ignore
|
94
94
|
|
95
95
|
name = constant_name(node)
|
96
96
|
return if name.nil?
|
@@ -105,7 +105,7 @@ module RubyLsp
|
|
105
105
|
return
|
106
106
|
end
|
107
107
|
|
108
|
-
return if @
|
108
|
+
return if sorbet_level_true_or_higher?(@sorbet_level) && self_receiver?(node)
|
109
109
|
|
110
110
|
message = node.message
|
111
111
|
return unless message
|
@@ -157,6 +157,9 @@ module RubyLsp
|
|
157
157
|
|
158
158
|
sig { void }
|
159
159
|
def handle_super_node_hover
|
160
|
+
# Sorbet can handle super hover on typed true or higher
|
161
|
+
return if sorbet_level_true_or_higher?(@sorbet_level)
|
162
|
+
|
160
163
|
surrounding_method = @node_context.surrounding_method
|
161
164
|
return unless surrounding_method
|
162
165
|
|
@@ -168,11 +171,16 @@ module RubyLsp
|
|
168
171
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
169
172
|
return unless type
|
170
173
|
|
171
|
-
methods = @index.resolve_method(message, type, inherited_only: inherited_only)
|
174
|
+
methods = @index.resolve_method(message, type.name, inherited_only: inherited_only)
|
172
175
|
return unless methods
|
173
176
|
|
174
177
|
title = "#{message}#{T.must(methods.first).decorated_parameters}"
|
175
178
|
|
179
|
+
if type.is_a?(TypeInferrer::GuessedType)
|
180
|
+
title << "\n\nGuessed receiver: #{type.name}"
|
181
|
+
@response_builder.push("[Learn more about guessed types](#{GUESSED_TYPES_URL})\n", category: :links)
|
182
|
+
end
|
183
|
+
|
176
184
|
categorized_markdown_from_index_entries(title, methods).each do |category, content|
|
177
185
|
@response_builder.push(content, category: category)
|
178
186
|
end
|
@@ -180,10 +188,14 @@ module RubyLsp
|
|
180
188
|
|
181
189
|
sig { params(name: String).void }
|
182
190
|
def handle_instance_variable_hover(name)
|
191
|
+
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
192
|
+
# to provide all features for them
|
193
|
+
return if @sorbet_level == Document::SorbetLevel::Strict
|
194
|
+
|
183
195
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
184
196
|
return unless type
|
185
197
|
|
186
|
-
entries = @index.resolve_instance_variable(name, type)
|
198
|
+
entries = @index.resolve_instance_variable(name, type.name)
|
187
199
|
return unless entries
|
188
200
|
|
189
201
|
categorized_markdown_from_index_entries(name, entries).each do |category, content|
|
@@ -13,11 +13,11 @@ module RubyLsp
|
|
13
13
|
global_state: GlobalState,
|
14
14
|
node_context: NodeContext,
|
15
15
|
dispatcher: Prism::Dispatcher,
|
16
|
-
|
16
|
+
sorbet_level: Document::SorbetLevel,
|
17
17
|
).void
|
18
18
|
end
|
19
|
-
def initialize(response_builder, global_state, node_context, dispatcher,
|
20
|
-
@
|
19
|
+
def initialize(response_builder, global_state, node_context, dispatcher, sorbet_level)
|
20
|
+
@sorbet_level = sorbet_level
|
21
21
|
@response_builder = response_builder
|
22
22
|
@global_state = global_state
|
23
23
|
@index = T.let(global_state.index, RubyIndexer::Index)
|
@@ -28,7 +28,7 @@ module RubyLsp
|
|
28
28
|
|
29
29
|
sig { params(node: Prism::CallNode).void }
|
30
30
|
def on_call_node_enter(node)
|
31
|
-
return if @
|
31
|
+
return if sorbet_level_true_or_higher?(@sorbet_level)
|
32
32
|
|
33
33
|
message = node.message
|
34
34
|
return unless message
|
@@ -36,7 +36,7 @@ module RubyLsp
|
|
36
36
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
37
37
|
return unless type
|
38
38
|
|
39
|
-
methods = @index.resolve_method(message, type)
|
39
|
+
methods = @index.resolve_method(message, type.name)
|
40
40
|
return unless methods
|
41
41
|
|
42
42
|
target_method = methods.first
|
@@ -61,6 +61,13 @@ module RubyLsp
|
|
61
61
|
active_parameter += 1
|
62
62
|
end
|
63
63
|
|
64
|
+
title = +""
|
65
|
+
|
66
|
+
extra_links = if type.is_a?(TypeInferrer::GuessedType)
|
67
|
+
title << "\n\nGuessed receiver: #{type.name}"
|
68
|
+
"[Learn more about guessed types](#{GUESSED_TYPES_URL})"
|
69
|
+
end
|
70
|
+
|
64
71
|
signature_help = Interface::SignatureHelp.new(
|
65
72
|
signatures: [
|
66
73
|
Interface::SignatureInformation.new(
|
@@ -68,7 +75,7 @@ module RubyLsp
|
|
68
75
|
parameters: parameters.map { |param| Interface::ParameterInformation.new(label: param.name) },
|
69
76
|
documentation: Interface::MarkupContent.new(
|
70
77
|
kind: "markdown",
|
71
|
-
value: markdown_from_index_entries(
|
78
|
+
value: markdown_from_index_entries(title, methods, extra_links: extra_links),
|
72
79
|
),
|
73
80
|
),
|
74
81
|
],
|
@@ -49,11 +49,11 @@ module RubyLsp
|
|
49
49
|
document: Document,
|
50
50
|
global_state: GlobalState,
|
51
51
|
params: T::Hash[Symbol, T.untyped],
|
52
|
-
|
52
|
+
sorbet_level: Document::SorbetLevel,
|
53
53
|
dispatcher: Prism::Dispatcher,
|
54
54
|
).void
|
55
55
|
end
|
56
|
-
def initialize(document, global_state, params,
|
56
|
+
def initialize(document, global_state, params, sorbet_level, dispatcher)
|
57
57
|
super()
|
58
58
|
@target = T.let(nil, T.nilable(Prism::Node))
|
59
59
|
@dispatcher = dispatcher
|
@@ -84,7 +84,7 @@ module RubyLsp
|
|
84
84
|
@response_builder,
|
85
85
|
global_state,
|
86
86
|
node_context,
|
87
|
-
|
87
|
+
sorbet_level,
|
88
88
|
dispatcher,
|
89
89
|
document.uri,
|
90
90
|
params.dig(:context, :triggerCharacter),
|
@@ -47,7 +47,7 @@ module RubyLsp
|
|
47
47
|
#
|
48
48
|
# For example, forgetting to return the `insertText` included in the original item will make the editor use the
|
49
49
|
# `label` for the text edit instead
|
50
|
-
label = @item[:label]
|
50
|
+
label = @item[:label].dup
|
51
51
|
entries = @index[label] || []
|
52
52
|
|
53
53
|
owner_name = @item.dig(:data, :owner_name)
|
@@ -62,12 +62,19 @@ module RubyLsp
|
|
62
62
|
first_entry = T.must(entries.first)
|
63
63
|
|
64
64
|
if first_entry.is_a?(RubyIndexer::Entry::Member)
|
65
|
-
label = "#{label}#{first_entry.decorated_parameters}"
|
65
|
+
label = +"#{label}#{first_entry.decorated_parameters}"
|
66
|
+
end
|
67
|
+
|
68
|
+
guessed_type = @item.dig(:data, :guessed_type)
|
69
|
+
|
70
|
+
extra_links = if guessed_type
|
71
|
+
label << "\n\nGuessed receiver: #{guessed_type}"
|
72
|
+
"[Learn more about guessed types](#{GUESSED_TYPES_URL})"
|
66
73
|
end
|
67
74
|
|
68
75
|
@item[:documentation] = Interface::MarkupContent.new(
|
69
76
|
kind: "markdown",
|
70
|
-
value: markdown_from_index_entries(label, entries, MAX_DOCUMENTATION_ENTRIES),
|
77
|
+
value: markdown_from_index_entries(label, entries, MAX_DOCUMENTATION_ENTRIES, extra_links: extra_links),
|
71
78
|
)
|
72
79
|
|
73
80
|
@item
|
@@ -36,10 +36,10 @@ module RubyLsp
|
|
36
36
|
global_state: GlobalState,
|
37
37
|
position: T::Hash[Symbol, T.untyped],
|
38
38
|
dispatcher: Prism::Dispatcher,
|
39
|
-
|
39
|
+
sorbet_level: Document::SorbetLevel,
|
40
40
|
).void
|
41
41
|
end
|
42
|
-
def initialize(document, global_state, position, dispatcher,
|
42
|
+
def initialize(document, global_state, position, dispatcher, sorbet_level)
|
43
43
|
super()
|
44
44
|
@response_builder = T.let(
|
45
45
|
ResponseBuilders::CollectionResponseBuilder[T.any(Interface::Location, Interface::LocationLink)].new,
|
@@ -96,7 +96,7 @@ module RubyLsp
|
|
96
96
|
document.uri,
|
97
97
|
node_context,
|
98
98
|
dispatcher,
|
99
|
-
|
99
|
+
sorbet_level,
|
100
100
|
)
|
101
101
|
|
102
102
|
Addon.addons.each do |addon|
|
@@ -36,10 +36,10 @@ module RubyLsp
|
|
36
36
|
global_state: GlobalState,
|
37
37
|
position: T::Hash[Symbol, T.untyped],
|
38
38
|
dispatcher: Prism::Dispatcher,
|
39
|
-
|
39
|
+
sorbet_level: Document::SorbetLevel,
|
40
40
|
).void
|
41
41
|
end
|
42
|
-
def initialize(document, global_state, position, dispatcher,
|
42
|
+
def initialize(document, global_state, position, dispatcher, sorbet_level)
|
43
43
|
super()
|
44
44
|
node_context = document.locate_node(position, node_types: Listeners::Hover::ALLOWED_TARGETS)
|
45
45
|
target = node_context.node
|
@@ -65,7 +65,7 @@ module RubyLsp
|
|
65
65
|
@target = T.let(target, T.nilable(Prism::Node))
|
66
66
|
uri = document.uri
|
67
67
|
@response_builder = T.let(ResponseBuilders::Hover.new, ResponseBuilders::Hover)
|
68
|
-
Listeners::Hover.new(@response_builder, global_state, uri, node_context, dispatcher,
|
68
|
+
Listeners::Hover.new(@response_builder, global_state, uri, node_context, dispatcher, sorbet_level)
|
69
69
|
Addon.addons.each do |addon|
|
70
70
|
addon.create_hover_listener(@response_builder, node_context, dispatcher)
|
71
71
|
end
|
@@ -46,10 +46,10 @@ module RubyLsp
|
|
46
46
|
position: T::Hash[Symbol, T.untyped],
|
47
47
|
context: T.nilable(T::Hash[Symbol, T.untyped]),
|
48
48
|
dispatcher: Prism::Dispatcher,
|
49
|
-
|
49
|
+
sorbet_level: Document::SorbetLevel,
|
50
50
|
).void
|
51
51
|
end
|
52
|
-
def initialize(document, global_state, position, context, dispatcher,
|
52
|
+
def initialize(document, global_state, position, context, dispatcher, sorbet_level) # rubocop:disable Metrics/ParameterLists
|
53
53
|
super()
|
54
54
|
node_context = document.locate_node(
|
55
55
|
{ line: position[:line], character: position[:character] },
|
@@ -61,7 +61,7 @@ module RubyLsp
|
|
61
61
|
@target = T.let(target, T.nilable(Prism::Node))
|
62
62
|
@dispatcher = dispatcher
|
63
63
|
@response_builder = T.let(ResponseBuilders::SignatureHelp.new, ResponseBuilders::SignatureHelp)
|
64
|
-
Listeners::SignatureHelp.new(@response_builder, global_state, node_context, dispatcher,
|
64
|
+
Listeners::SignatureHelp.new(@response_builder, global_state, node_context, dispatcher, sorbet_level)
|
65
65
|
end
|
66
66
|
|
67
67
|
sig { override.returns(T.nilable(Interface::SignatureHelp)) }
|
@@ -130,13 +130,17 @@ module RubyLsp
|
|
130
130
|
title: String,
|
131
131
|
entries: T.any(T::Array[RubyIndexer::Entry], RubyIndexer::Entry),
|
132
132
|
max_entries: T.nilable(Integer),
|
133
|
+
extra_links: T.nilable(String),
|
133
134
|
).returns(String)
|
134
135
|
end
|
135
|
-
def markdown_from_index_entries(title, entries, max_entries = nil)
|
136
|
+
def markdown_from_index_entries(title, entries, max_entries = nil, extra_links: nil)
|
136
137
|
categorized_markdown = categorized_markdown_from_index_entries(title, entries, max_entries)
|
137
138
|
|
139
|
+
markdown = +(categorized_markdown[:title] || "")
|
140
|
+
markdown << "\n\n#{extra_links}" if extra_links
|
141
|
+
|
138
142
|
<<~MARKDOWN.chomp
|
139
|
-
#{
|
143
|
+
#{markdown}
|
140
144
|
|
141
145
|
#{categorized_markdown[:links]}
|
142
146
|
|
@@ -204,6 +208,11 @@ module RubyLsp
|
|
204
208
|
Constant::SymbolKind::FIELD
|
205
209
|
end
|
206
210
|
end
|
211
|
+
|
212
|
+
sig { params(sorbet_level: Document::SorbetLevel).returns(T::Boolean) }
|
213
|
+
def sorbet_level_true_or_higher?(sorbet_level)
|
214
|
+
sorbet_level == Document::SorbetLevel::True || sorbet_level == Document::SorbetLevel::Strict
|
215
|
+
end
|
207
216
|
end
|
208
217
|
end
|
209
218
|
end
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -477,15 +477,17 @@ module RubyLsp
|
|
477
477
|
@global_state,
|
478
478
|
params[:position],
|
479
479
|
dispatcher,
|
480
|
-
|
480
|
+
sorbet_level(document),
|
481
481
|
).perform,
|
482
482
|
),
|
483
483
|
)
|
484
484
|
end
|
485
485
|
|
486
|
-
sig { params(document: Document).returns(
|
487
|
-
def
|
488
|
-
@global_state.has_type_checker
|
486
|
+
sig { params(document: Document).returns(Document::SorbetLevel) }
|
487
|
+
def sorbet_level(document)
|
488
|
+
return Document::SorbetLevel::Ignore unless @global_state.has_type_checker
|
489
|
+
|
490
|
+
document.sorbet_level
|
489
491
|
end
|
490
492
|
|
491
493
|
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
@@ -594,7 +596,7 @@ module RubyLsp
|
|
594
596
|
document,
|
595
597
|
@global_state,
|
596
598
|
params,
|
597
|
-
|
599
|
+
sorbet_level(document),
|
598
600
|
dispatcher,
|
599
601
|
).perform,
|
600
602
|
),
|
@@ -624,7 +626,7 @@ module RubyLsp
|
|
624
626
|
params[:position],
|
625
627
|
params[:context],
|
626
628
|
dispatcher,
|
627
|
-
|
629
|
+
sorbet_level(document),
|
628
630
|
).perform,
|
629
631
|
),
|
630
632
|
)
|
@@ -644,7 +646,7 @@ module RubyLsp
|
|
644
646
|
@global_state,
|
645
647
|
params[:position],
|
646
648
|
dispatcher,
|
647
|
-
|
649
|
+
sorbet_level(document),
|
648
650
|
).perform,
|
649
651
|
),
|
650
652
|
)
|
data/lib/ruby_lsp/store.rb
CHANGED
@@ -38,13 +38,18 @@ module RubyLsp
|
|
38
38
|
document = @state[uri.to_s]
|
39
39
|
return document unless document.nil?
|
40
40
|
|
41
|
-
path
|
41
|
+
# For unsaved files (`untitled:Untitled-1` uris), there's no path to read from. If we don't have the untitled file
|
42
|
+
# already present in the store, then we have to raise non existing document error
|
43
|
+
path = uri.to_standardized_path
|
44
|
+
raise NonExistingDocumentError, uri.to_s unless path
|
45
|
+
|
42
46
|
ext = File.extname(path)
|
43
47
|
language_id = if ext == ".erb" || ext == ".rhtml"
|
44
48
|
Document::LanguageId::ERB
|
45
49
|
else
|
46
50
|
Document::LanguageId::Ruby
|
47
51
|
end
|
52
|
+
|
48
53
|
set(uri: uri, source: File.binread(path), version: 0, language_id: language_id)
|
49
54
|
T.must(@state[uri.to_s])
|
50
55
|
rescue Errno::ENOENT
|
data/lib/ruby_lsp/test_helper.rb
CHANGED
@@ -21,7 +21,7 @@ module RubyLsp
|
|
21
21
|
&block)
|
22
22
|
server = RubyLsp::Server.new(test_mode: true)
|
23
23
|
server.global_state.stubs(:has_type_checker).returns(false) if stub_no_typechecker
|
24
|
-
server.global_state.apply_options({})
|
24
|
+
server.global_state.apply_options({ initializationOptions: { experimentalFeaturesEnabled: true } })
|
25
25
|
language_id = uri.to_s.end_with?(".erb") ? "erb" : "ruby"
|
26
26
|
|
27
27
|
if source
|
@@ -7,12 +7,16 @@ module RubyLsp
|
|
7
7
|
class TypeInferrer
|
8
8
|
extend T::Sig
|
9
9
|
|
10
|
-
sig { params(
|
11
|
-
|
10
|
+
sig { params(experimental_features: T::Boolean).returns(T::Boolean) }
|
11
|
+
attr_writer :experimental_features
|
12
|
+
|
13
|
+
sig { params(index: RubyIndexer::Index, experimental_features: T::Boolean).void }
|
14
|
+
def initialize(index, experimental_features = true)
|
12
15
|
@index = index
|
16
|
+
@experimental_features = experimental_features
|
13
17
|
end
|
14
18
|
|
15
|
-
sig { params(node_context: NodeContext).returns(T.nilable(
|
19
|
+
sig { params(node_context: NodeContext).returns(T.nilable(Type)) }
|
16
20
|
def infer_receiver_type(node_context)
|
17
21
|
node = node_context.node
|
18
22
|
|
@@ -28,7 +32,7 @@ module RubyLsp
|
|
28
32
|
|
29
33
|
private
|
30
34
|
|
31
|
-
sig { params(node: Prism::CallNode, node_context: NodeContext).returns(T.nilable(
|
35
|
+
sig { params(node: Prism::CallNode, node_context: NodeContext).returns(T.nilable(Type)) }
|
32
36
|
def infer_receiver_for_call_node(node, node_context)
|
33
37
|
receiver = node.receiver
|
34
38
|
|
@@ -47,23 +51,40 @@ module RubyLsp
|
|
47
51
|
return unless name
|
48
52
|
|
49
53
|
*parts, last = name.split("::")
|
50
|
-
return "#{last}::<Class:#{last}>" if parts.empty?
|
54
|
+
return Type.new("#{last}::<Class:#{last}>") if parts.empty?
|
55
|
+
|
56
|
+
Type.new("#{parts.join("::")}::#{last}::<Class:#{last}>")
|
57
|
+
else
|
58
|
+
return unless @experimental_features
|
59
|
+
|
60
|
+
raw_receiver = node.receiver&.slice
|
51
61
|
|
52
|
-
|
62
|
+
if raw_receiver
|
63
|
+
guessed_name = raw_receiver
|
64
|
+
.delete_prefix("@")
|
65
|
+
.delete_prefix("@@")
|
66
|
+
.split("_")
|
67
|
+
.map(&:capitalize)
|
68
|
+
.join
|
69
|
+
|
70
|
+
entries = @index.resolve(guessed_name, node_context.nesting) || @index.first_unqualified_const(guessed_name)
|
71
|
+
name = entries&.first&.name
|
72
|
+
GuessedType.new(name) if name
|
73
|
+
end
|
53
74
|
end
|
54
75
|
end
|
55
76
|
|
56
|
-
sig { params(node_context: NodeContext).returns(
|
77
|
+
sig { params(node_context: NodeContext).returns(Type) }
|
57
78
|
def self_receiver_handling(node_context)
|
58
79
|
nesting = node_context.nesting
|
59
80
|
# If we're at the top level, then the invocation is happening on `<main>`, which is a special singleton that
|
60
81
|
# inherits from Object
|
61
|
-
return "Object" if nesting.empty?
|
62
|
-
return node_context.fully_qualified_name if node_context.surrounding_method
|
82
|
+
return Type.new("Object") if nesting.empty?
|
83
|
+
return Type.new(node_context.fully_qualified_name) if node_context.surrounding_method
|
63
84
|
|
64
85
|
# If we're not inside a method, then we're inside the body of a class or module, which is a singleton
|
65
86
|
# context
|
66
|
-
"#{nesting.join("::")}::<Class:#{nesting.last}>"
|
87
|
+
Type.new("#{nesting.join("::")}::<Class:#{nesting.last}>")
|
67
88
|
end
|
68
89
|
|
69
90
|
sig do
|
@@ -80,5 +101,22 @@ module RubyLsp
|
|
80
101
|
Prism::ConstantPathNode::MissingNodesInConstantPathError
|
81
102
|
nil
|
82
103
|
end
|
104
|
+
|
105
|
+
# A known type
|
106
|
+
class Type
|
107
|
+
extend T::Sig
|
108
|
+
|
109
|
+
sig { returns(String) }
|
110
|
+
attr_reader :name
|
111
|
+
|
112
|
+
sig { params(name: String).void }
|
113
|
+
def initialize(name)
|
114
|
+
@name = name
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# A type that was guessed based on the receiver raw name
|
119
|
+
class GuessedType < Type
|
120
|
+
end
|
83
121
|
end
|
84
122
|
end
|
data/lib/ruby_lsp/utils.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-lsp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.17.
|
4
|
+
version: 0.17.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-07-
|
11
|
+
date: 2024-07-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: language_server-protocol
|
@@ -204,7 +204,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
204
204
|
- !ruby/object:Gem::Version
|
205
205
|
version: '0'
|
206
206
|
requirements: []
|
207
|
-
rubygems_version: 3.5.
|
207
|
+
rubygems_version: 3.5.16
|
208
208
|
signing_key:
|
209
209
|
specification_version: 4
|
210
210
|
summary: An opinionated language server for Ruby
|