ruby-lsp 0.17.6 → 0.17.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7299237468ae17d80028a1454903f58b1be4ff34428a6c8a538530b5caad54d1
4
- data.tar.gz: 26351e5be1671219a9b3624076b2f50fb68c5838303fe1a021eecb39c4e3e9f1
3
+ metadata.gz: 671bab4fdbd77d1980781f55874d6cd448e1dcd11a6f534a176dfb00b098d2eb
4
+ data.tar.gz: bd12fd2ef58588b3b5386fec48c0f561140efff180988f1971b67f635f0e1042
5
5
  SHA512:
6
- metadata.gz: 54cc76a3506a268398cfc51645a70300e2d14bda5b39c12274863270f70ed666b33bfebdf50dac8e013410e8ddba5645f4f435863af795d268710e0ccf9d8814
7
- data.tar.gz: 95e52a4aa6f68785495f7435fe54cc395c062809ad3754703f808c68d2599bc34a619578bbb8453cb54de88a8312ac49eff8dd2e72a82bc04554c7b659c477cd
6
+ metadata.gz: f3a2a3115e1745ad7263b7e113ef13795f9a1ed1ee118128a9ab2aaad703b17551acbd929f820700e6650efb76421418a13a78270ac8d3aaff080edc46731aee
7
+ data.tar.gz: d29bb4d6c60727eef1a10905762a6d0c8946b7afc8aac6148ef970c8d752365f1577403497702f52a7211689cde7940b3d7cd1e1e0eb03d038e0dc6f7ceed738
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.17.6
1
+ 0.17.7
@@ -331,14 +331,17 @@ module RubyIndexer
331
331
  params(
332
332
  method_name: String,
333
333
  receiver_name: String,
334
+ inherited_only: T::Boolean,
334
335
  ).returns(T.nilable(T::Array[T.any(Entry::Member, Entry::MethodAlias)]))
335
336
  end
336
- def resolve_method(method_name, receiver_name)
337
+ def resolve_method(method_name, receiver_name, inherited_only: false)
337
338
  method_entries = self[method_name]
338
339
  return unless method_entries
339
340
 
340
341
  ancestors = linearized_ancestors_of(receiver_name.delete_prefix("::"))
341
342
  ancestors.each do |ancestor|
343
+ next if inherited_only && ancestor == receiver_name
344
+
342
345
  found = method_entries.filter_map do |entry|
343
346
  case entry
344
347
  when Entry::Member, Entry::MethodAlias
@@ -285,6 +285,43 @@ module RubyIndexer
285
285
  assert_includes(second_entry.comments, "Hello from second `bar`")
286
286
  end
287
287
 
288
+ def test_resolve_method_inherited_only
289
+ index(<<~RUBY)
290
+ class Bar
291
+ def baz; end
292
+ end
293
+
294
+ class Foo < Bar
295
+ def baz; end
296
+ end
297
+ RUBY
298
+
299
+ entry = T.must(@index.resolve_method("baz", "Foo", inherited_only: true).first)
300
+
301
+ assert_equal("Bar", T.must(entry.owner).name)
302
+ end
303
+
304
+ def test_resolve_method_inherited_only_for_prepended_module
305
+ index(<<~RUBY)
306
+ module Bar
307
+ def baz
308
+ super
309
+ end
310
+ end
311
+
312
+ class Foo
313
+ prepend Bar
314
+
315
+ def baz; end
316
+ end
317
+ RUBY
318
+
319
+ # This test is just to document the fact that we don't yet support resolving inherited methods for modules that
320
+ # are prepended. The only way to support this is to find all namespaces that have the module a subtype, so that we
321
+ # can show the results for everywhere the module has been prepended.
322
+ assert_nil(@index.resolve_method("baz", "Bar", inherited_only: true))
323
+ end
324
+
288
325
  def test_prefix_search_for_methods
289
326
  index(<<~RUBY)
290
327
  module Foo
@@ -127,8 +127,18 @@ module RubyLsp
127
127
  parent = T.let(nil, T.nilable(Prism::Node))
128
128
  nesting_nodes = T.let(
129
129
  [],
130
- T::Array[T.any(Prism::ClassNode, Prism::ModuleNode, Prism::SingletonClassNode, Prism::DefNode)],
130
+ T::Array[T.any(
131
+ Prism::ClassNode,
132
+ Prism::ModuleNode,
133
+ Prism::SingletonClassNode,
134
+ Prism::DefNode,
135
+ Prism::BlockNode,
136
+ Prism::LambdaNode,
137
+ Prism::ProgramNode,
138
+ )],
131
139
  )
140
+
141
+ nesting_nodes << node if node.is_a?(Prism::ProgramNode)
132
142
  call_node = T.let(nil, T.nilable(Prism::CallNode))
133
143
 
134
144
  until queue.empty?
@@ -158,11 +168,8 @@ module RubyLsp
158
168
  # Keep track of the nesting where we found the target. This is used to determine the fully qualified name of the
159
169
  # target when it is a constant
160
170
  case candidate
161
- when Prism::ClassNode, Prism::ModuleNode
162
- nesting_nodes << candidate
163
- when Prism::SingletonClassNode
164
- nesting_nodes << candidate
165
- when Prism::DefNode
171
+ when Prism::ClassNode, Prism::ModuleNode, Prism::SingletonClassNode, Prism::DefNode, Prism::BlockNode,
172
+ Prism::LambdaNode
166
173
  nesting_nodes << candidate
167
174
  end
168
175
 
@@ -203,24 +210,7 @@ module RubyLsp
203
210
  end
204
211
  end
205
212
 
206
- nesting = []
207
- surrounding_method = T.let(nil, T.nilable(String))
208
-
209
- nesting_nodes.each do |node|
210
- case node
211
- when Prism::ClassNode, Prism::ModuleNode
212
- nesting << node.constant_path.slice
213
- when Prism::SingletonClassNode
214
- nesting << "<Class:#{nesting.last}>"
215
- when Prism::DefNode
216
- surrounding_method = node.name.to_s
217
- next unless node.receiver.is_a?(Prism::SelfNode)
218
-
219
- nesting << "<Class:#{nesting.last}>"
220
- end
221
- end
222
-
223
- NodeContext.new(closest, parent, nesting, call_node, surrounding_method)
213
+ NodeContext.new(closest, parent, nesting_nodes, call_node)
224
214
  end
225
215
 
226
216
  sig { returns(T::Boolean) }
@@ -69,7 +69,7 @@ module RubyLsp
69
69
  @formatter = detect_formatter(direct_dependencies, all_dependencies) if @formatter == "auto"
70
70
 
71
71
  specified_linters = options.dig(:initializationOptions, :linters)
72
- @linters = specified_linters || detect_linters(direct_dependencies)
72
+ @linters = specified_linters || detect_linters(direct_dependencies, all_dependencies)
73
73
  @test_library = detect_test_library(direct_dependencies)
74
74
  @has_type_checker = detect_typechecker(direct_dependencies)
75
75
 
@@ -127,10 +127,14 @@ module RubyLsp
127
127
 
128
128
  # Try to detect if there are linters in the project's dependencies. For auto-detection, we always only consider a
129
129
  # single linter. To have multiple linters running, the user must configure them manually
130
- sig { params(dependencies: T::Array[String]).returns(T::Array[String]) }
131
- def detect_linters(dependencies)
130
+ sig { params(dependencies: T::Array[String], all_dependencies: T::Array[String]).returns(T::Array[String]) }
131
+ def detect_linters(dependencies, all_dependencies)
132
132
  linters = []
133
- linters << "rubocop" if dependencies.any?(/^rubocop/)
133
+
134
+ if dependencies.any?(/^rubocop/) || (all_dependencies.include?("rubocop") && dot_rubocop_yml_present)
135
+ linters << "rubocop"
136
+ end
137
+
134
138
  linters
135
139
  end
136
140
 
@@ -277,6 +277,8 @@ module RubyLsp
277
277
 
278
278
  sig { params(node: Prism::CallNode, name: String).void }
279
279
  def complete_methods(node, name)
280
+ add_local_completions(node, name)
281
+
280
282
  type = @type_inferrer.infer_receiver_type(@node_context)
281
283
  return unless type
282
284
 
@@ -322,6 +324,31 @@ module RubyLsp
322
324
  # We have not indexed this namespace, so we can't provide any completions
323
325
  end
324
326
 
327
+ sig { params(node: Prism::CallNode, name: String).void }
328
+ def add_local_completions(node, name)
329
+ return if @global_state.has_type_checker
330
+
331
+ # If the call node has a receiver, then it cannot possibly be a local variable
332
+ return if node.receiver
333
+
334
+ range = range_from_location(T.must(node.message_loc))
335
+
336
+ @node_context.locals_for_scope.each do |local|
337
+ local_name = local.to_s
338
+ next unless local_name.start_with?(name)
339
+
340
+ @response_builder << Interface::CompletionItem.new(
341
+ label: local_name,
342
+ filter_text: local_name,
343
+ text_edit: Interface::TextEdit.new(range: range, new_text: local_name),
344
+ kind: Constant::CompletionItemKind::VARIABLE,
345
+ data: {
346
+ skip_resolve: true,
347
+ },
348
+ )
349
+ end
350
+ end
351
+
325
352
  sig { params(label: String, node: Prism::StringNode).returns(Interface::CompletionItem) }
326
353
  def build_completion(label, node)
327
354
  # We should use the content location as we only replace the content and not the delimiters of the string
@@ -46,6 +46,8 @@ module RubyLsp
46
46
  :on_instance_variable_or_write_node_enter,
47
47
  :on_instance_variable_target_node_enter,
48
48
  :on_string_node_enter,
49
+ :on_super_node_enter,
50
+ :on_forwarding_super_node_enter,
49
51
  )
50
52
  end
51
53
 
@@ -133,8 +135,30 @@ module RubyLsp
133
135
  handle_instance_variable_definition(node.name.to_s)
134
136
  end
135
137
 
138
+ sig { params(node: Prism::SuperNode).void }
139
+ def on_super_node_enter(node)
140
+ handle_super_node_definition
141
+ end
142
+
143
+ sig { params(node: Prism::ForwardingSuperNode).void }
144
+ def on_forwarding_super_node_enter(node)
145
+ handle_super_node_definition
146
+ end
147
+
136
148
  private
137
149
 
150
+ sig { void }
151
+ def handle_super_node_definition
152
+ surrounding_method = @node_context.surrounding_method
153
+ return unless surrounding_method
154
+
155
+ handle_method_definition(
156
+ surrounding_method,
157
+ @type_inferrer.infer_receiver_type(@node_context),
158
+ inherited_only: true,
159
+ )
160
+ end
161
+
138
162
  sig { params(name: String).void }
139
163
  def handle_instance_variable_definition(name)
140
164
  type = @type_inferrer.infer_receiver_type(@node_context)
@@ -158,10 +182,10 @@ module RubyLsp
158
182
  # If by any chance we haven't indexed the owner, then there's no way to find the right declaration
159
183
  end
160
184
 
161
- sig { params(message: String, receiver_type: T.nilable(String)).void }
162
- def handle_method_definition(message, receiver_type)
185
+ sig { params(message: String, receiver_type: T.nilable(String), inherited_only: T::Boolean).void }
186
+ def handle_method_definition(message, receiver_type, inherited_only: false)
163
187
  methods = if receiver_type
164
- @index.resolve_method(message, receiver_type)
188
+ @index.resolve_method(message, receiver_type, inherited_only: inherited_only)
165
189
  else
166
190
  # If the method doesn't have a receiver, then we provide a few candidates to jump to
167
191
  # But we don't want to provide too many candidates, as it can be overwhelming
@@ -21,6 +21,8 @@ module RubyLsp
21
21
  Prism::InstanceVariableWriteNode,
22
22
  Prism::SymbolNode,
23
23
  Prism::StringNode,
24
+ Prism::SuperNode,
25
+ Prism::ForwardingSuperNode,
24
26
  ],
25
27
  T::Array[T.class_of(Prism::Node)],
26
28
  )
@@ -64,6 +66,8 @@ module RubyLsp
64
66
  :on_instance_variable_operator_write_node_enter,
65
67
  :on_instance_variable_or_write_node_enter,
66
68
  :on_instance_variable_target_node_enter,
69
+ :on_super_node_enter,
70
+ :on_forwarding_super_node_enter,
67
71
  )
68
72
  end
69
73
 
@@ -106,17 +110,7 @@ module RubyLsp
106
110
  message = node.message
107
111
  return unless message
108
112
 
109
- type = @type_inferrer.infer_receiver_type(@node_context)
110
- return unless type
111
-
112
- methods = @index.resolve_method(message, type)
113
- return unless methods
114
-
115
- title = "#{message}#{T.must(methods.first).decorated_parameters}"
116
-
117
- categorized_markdown_from_index_entries(title, methods).each do |category, content|
118
- @response_builder.push(content, category: category)
119
- end
113
+ handle_method_hover(message)
120
114
  end
121
115
 
122
116
  sig { params(node: Prism::InstanceVariableReadNode).void }
@@ -149,8 +143,41 @@ module RubyLsp
149
143
  handle_instance_variable_hover(node.name.to_s)
150
144
  end
151
145
 
146
+ sig { params(node: Prism::SuperNode).void }
147
+ def on_super_node_enter(node)
148
+ handle_super_node_hover
149
+ end
150
+
151
+ sig { params(node: Prism::ForwardingSuperNode).void }
152
+ def on_forwarding_super_node_enter(node)
153
+ handle_super_node_hover
154
+ end
155
+
152
156
  private
153
157
 
158
+ sig { void }
159
+ def handle_super_node_hover
160
+ surrounding_method = @node_context.surrounding_method
161
+ return unless surrounding_method
162
+
163
+ handle_method_hover(surrounding_method, inherited_only: true)
164
+ end
165
+
166
+ sig { params(message: String, inherited_only: T::Boolean).void }
167
+ def handle_method_hover(message, inherited_only: false)
168
+ type = @type_inferrer.infer_receiver_type(@node_context)
169
+ return unless type
170
+
171
+ methods = @index.resolve_method(message, type, inherited_only: inherited_only)
172
+ return unless methods
173
+
174
+ title = "#{message}#{T.must(methods.first).decorated_parameters}"
175
+
176
+ categorized_markdown_from_index_entries(title, methods).each do |category, content|
177
+ @response_builder.push(content, category: category)
178
+ end
179
+ end
180
+
154
181
  sig { params(name: String).void }
155
182
  def handle_instance_variable_hover(name)
156
183
  type = @type_inferrer.infer_receiver_type(@node_context)
@@ -23,22 +23,82 @@ module RubyLsp
23
23
  params(
24
24
  node: T.nilable(Prism::Node),
25
25
  parent: T.nilable(Prism::Node),
26
- nesting: T::Array[String],
26
+ nesting_nodes: T::Array[T.any(
27
+ Prism::ClassNode,
28
+ Prism::ModuleNode,
29
+ Prism::SingletonClassNode,
30
+ Prism::DefNode,
31
+ Prism::BlockNode,
32
+ Prism::LambdaNode,
33
+ Prism::ProgramNode,
34
+ )],
27
35
  call_node: T.nilable(Prism::CallNode),
28
- surrounding_method: T.nilable(String),
29
36
  ).void
30
37
  end
31
- def initialize(node, parent, nesting, call_node, surrounding_method)
38
+ def initialize(node, parent, nesting_nodes, call_node)
32
39
  @node = node
33
40
  @parent = parent
34
- @nesting = nesting
41
+ @nesting_nodes = nesting_nodes
35
42
  @call_node = call_node
36
- @surrounding_method = surrounding_method
43
+
44
+ nesting, surrounding_method = handle_nesting_nodes(nesting_nodes)
45
+ @nesting = T.let(nesting, T::Array[String])
46
+ @surrounding_method = T.let(surrounding_method, T.nilable(String))
37
47
  end
38
48
 
39
49
  sig { returns(String) }
40
50
  def fully_qualified_name
41
51
  @fully_qualified_name ||= T.let(@nesting.join("::"), T.nilable(String))
42
52
  end
53
+
54
+ sig { returns(T::Array[Symbol]) }
55
+ def locals_for_scope
56
+ locals = []
57
+
58
+ @nesting_nodes.each do |node|
59
+ if node.is_a?(Prism::ClassNode) || node.is_a?(Prism::ModuleNode) || node.is_a?(Prism::SingletonClassNode) ||
60
+ node.is_a?(Prism::DefNode)
61
+ locals.clear
62
+ end
63
+
64
+ locals.concat(node.locals)
65
+ end
66
+
67
+ locals
68
+ end
69
+
70
+ private
71
+
72
+ sig do
73
+ params(nodes: T::Array[T.any(
74
+ Prism::ClassNode,
75
+ Prism::ModuleNode,
76
+ Prism::SingletonClassNode,
77
+ Prism::DefNode,
78
+ Prism::BlockNode,
79
+ Prism::LambdaNode,
80
+ Prism::ProgramNode,
81
+ )]).returns([T::Array[String], T.nilable(String)])
82
+ end
83
+ def handle_nesting_nodes(nodes)
84
+ nesting = []
85
+ surrounding_method = T.let(nil, T.nilable(String))
86
+
87
+ @nesting_nodes.each do |node|
88
+ case node
89
+ when Prism::ClassNode, Prism::ModuleNode
90
+ nesting << node.constant_path.slice
91
+ when Prism::SingletonClassNode
92
+ nesting << "<Class:#{nesting.last}>"
93
+ when Prism::DefNode
94
+ surrounding_method = node.name.to_s
95
+ next unless node.receiver.is_a?(Prism::SelfNode)
96
+
97
+ nesting << "<Class:#{nesting.last}>"
98
+ end
99
+ end
100
+
101
+ [nesting, surrounding_method]
102
+ end
43
103
  end
44
104
  end
@@ -38,6 +38,8 @@ module RubyLsp
38
38
 
39
39
  sig { override.returns(T::Hash[Symbol, T.untyped]) }
40
40
  def perform
41
+ return @item if @item.dig(:data, :skip_resolve)
42
+
41
43
  # Based on the spec https://microsoft.github.io/language-server-protocol/specification#textDocument_completion,
42
44
  # a completion resolve request must always return the original completion item without modifying ANY fields
43
45
  # other than detail and documentation (NOT labelDetails). If we modify anything, the completion behaviour might
@@ -62,6 +62,8 @@ module RubyLsp
62
62
  Prism::InstanceVariableWriteNode,
63
63
  Prism::SymbolNode,
64
64
  Prism::StringNode,
65
+ Prism::SuperNode,
66
+ Prism::ForwardingSuperNode,
65
67
  ],
66
68
  )
67
69
 
@@ -20,16 +20,9 @@ module RubyLsp
20
20
  when Prism::CallNode
21
21
  infer_receiver_for_call_node(node, node_context)
22
22
  when Prism::InstanceVariableReadNode, Prism::InstanceVariableAndWriteNode, Prism::InstanceVariableWriteNode,
23
- Prism::InstanceVariableOperatorWriteNode, Prism::InstanceVariableOrWriteNode, Prism::InstanceVariableTargetNode
24
- nesting = node_context.nesting
25
- # If we're at the top level, then the invocation is happening on `<main>`, which is a special singleton that
26
- # inherits from Object
27
- return "Object" if nesting.empty?
28
-
29
- fully_qualified_name = node_context.fully_qualified_name
30
- return fully_qualified_name if node_context.surrounding_method
31
-
32
- "#{fully_qualified_name}::<Class:#{nesting.last}>"
23
+ Prism::InstanceVariableOperatorWriteNode, Prism::InstanceVariableOrWriteNode, Prism::InstanceVariableTargetNode,
24
+ Prism::SuperNode, Prism::ForwardingSuperNode
25
+ self_receiver_handling(node_context)
33
26
  end
34
27
  end
35
28
 
@@ -41,15 +34,7 @@ module RubyLsp
41
34
 
42
35
  case receiver
43
36
  when Prism::SelfNode, nil
44
- nesting = node_context.nesting
45
- # If we're at the top level, then the invocation is happening on `<main>`, which is a special singleton that
46
- # inherits from Object
47
- return "Object" if nesting.empty?
48
- return node_context.fully_qualified_name if node_context.surrounding_method
49
-
50
- # If we're not inside a method, then we're inside the body of a class or module, which is a singleton
51
- # context
52
- "#{nesting.join("::")}::<Class:#{nesting.last}>"
37
+ self_receiver_handling(node_context)
53
38
  when Prism::ConstantPathNode, Prism::ConstantReadNode
54
39
  # When the receiver is a constant reference, we have to try to resolve it to figure out the right
55
40
  # receiver. But since the invocation is directly on the constant, that's the singleton context of that
@@ -68,6 +53,19 @@ module RubyLsp
68
53
  end
69
54
  end
70
55
 
56
+ sig { params(node_context: NodeContext).returns(String) }
57
+ def self_receiver_handling(node_context)
58
+ nesting = node_context.nesting
59
+ # If we're at the top level, then the invocation is happening on `<main>`, which is a special singleton that
60
+ # inherits from Object
61
+ return "Object" if nesting.empty?
62
+ return node_context.fully_qualified_name if node_context.surrounding_method
63
+
64
+ # If we're not inside a method, then we're inside the body of a class or module, which is a singleton
65
+ # context
66
+ "#{nesting.join("::")}::<Class:#{nesting.last}>"
67
+ end
68
+
71
69
  sig do
72
70
  params(
73
71
  node: T.any(
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.6
4
+ version: 0.17.7
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-10 00:00:00.000000000 Z
11
+ date: 2024-07-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: language_server-protocol