ruby-lsp 0.11.1 → 0.12.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp +2 -1
  4. data/exe/ruby-lsp-doctor +16 -0
  5. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +5 -1
  6. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +205 -0
  7. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +28 -108
  8. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +6 -6
  9. data/lib/ruby_indexer/lib/ruby_indexer/visitor.rb +160 -64
  10. data/lib/ruby_indexer/ruby_indexer.rb +1 -0
  11. data/lib/ruby_indexer/test/classes_and_modules_test.rb +49 -16
  12. data/lib/ruby_indexer/test/constant_test.rb +111 -30
  13. data/lib/ruby_indexer/test/index_test.rb +15 -0
  14. data/lib/ruby_indexer/test/method_test.rb +73 -0
  15. data/lib/ruby_indexer/test/test_case.rb +5 -1
  16. data/lib/ruby_lsp/addon.rb +8 -8
  17. data/lib/ruby_lsp/document.rb +14 -14
  18. data/lib/ruby_lsp/executor.rb +89 -53
  19. data/lib/ruby_lsp/internal.rb +8 -2
  20. data/lib/ruby_lsp/listener.rb +6 -6
  21. data/lib/ruby_lsp/requests/base_request.rb +1 -9
  22. data/lib/ruby_lsp/requests/code_action_resolve.rb +3 -3
  23. data/lib/ruby_lsp/requests/code_lens.rb +30 -30
  24. data/lib/ruby_lsp/requests/completion.rb +83 -32
  25. data/lib/ruby_lsp/requests/definition.rb +21 -15
  26. data/lib/ruby_lsp/requests/diagnostics.rb +1 -1
  27. data/lib/ruby_lsp/requests/document_highlight.rb +508 -31
  28. data/lib/ruby_lsp/requests/document_link.rb +24 -17
  29. data/lib/ruby_lsp/requests/document_symbol.rb +42 -42
  30. data/lib/ruby_lsp/requests/folding_ranges.rb +84 -82
  31. data/lib/ruby_lsp/requests/hover.rb +22 -17
  32. data/lib/ruby_lsp/requests/inlay_hints.rb +6 -6
  33. data/lib/ruby_lsp/requests/selection_ranges.rb +13 -105
  34. data/lib/ruby_lsp/requests/semantic_highlighting.rb +92 -92
  35. data/lib/ruby_lsp/requests/support/annotation.rb +3 -3
  36. data/lib/ruby_lsp/requests/support/common.rb +5 -5
  37. data/lib/ruby_lsp/requests/support/dependency_detector.rb +12 -4
  38. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +15 -6
  39. data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +10 -7
  40. data/lib/ruby_lsp/requests/support/sorbet.rb +28 -28
  41. data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -4
  42. data/lib/ruby_lsp/requests.rb +0 -1
  43. data/lib/ruby_lsp/setup_bundler.rb +8 -5
  44. metadata +19 -17
  45. data/lib/ruby_lsp/event_emitter.rb +0 -351
  46. data/lib/ruby_lsp/requests/support/highlight_target.rb +0 -118
@@ -2,65 +2,139 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyIndexer
5
- class IndexVisitor < YARP::Visitor
5
+ class IndexVisitor < Prism::Visitor
6
6
  extend T::Sig
7
7
 
8
- sig { params(index: Index, parse_result: YARP::ParseResult, file_path: String).void }
8
+ sig { params(index: Index, parse_result: Prism::ParseResult, file_path: String).void }
9
9
  def initialize(index, parse_result, file_path)
10
10
  @index = index
11
- @parse_result = parse_result
12
11
  @file_path = file_path
13
12
  @stack = T.let([], T::Array[String])
14
13
  @comments_by_line = T.let(
15
14
  parse_result.comments.to_h do |c|
16
15
  [c.location.start_line, c]
17
16
  end,
18
- T::Hash[Integer, YARP::Comment],
17
+ T::Hash[Integer, Prism::Comment],
19
18
  )
20
19
 
21
20
  super()
22
21
  end
23
22
 
24
- sig { void }
25
- def run
26
- visit(@parse_result.value)
27
- end
28
-
29
- sig { params(node: T.nilable(YARP::Node)).void }
30
- def visit(node)
31
- case node
32
- when YARP::ProgramNode, YARP::StatementsNode
33
- visit_child_nodes(node)
34
- when YARP::ClassNode
35
- add_index_entry(node, Index::Entry::Class)
36
- when YARP::ModuleNode
37
- add_index_entry(node, Index::Entry::Module)
38
- when YARP::ConstantWriteNode, YARP::ConstantOrWriteNode
39
- name = fully_qualify_name(node.name.to_s)
40
- add_constant(node, name)
41
- when YARP::ConstantPathWriteNode, YARP::ConstantPathOrWriteNode, YARP::ConstantPathOperatorWriteNode,
42
- YARP::ConstantPathAndWriteNode
43
-
44
- # ignore variable constants like `var::FOO` or `self.class::FOO`
45
- return unless node.target.parent.nil? || node.target.parent.is_a?(YARP::ConstantReadNode)
46
-
47
- name = fully_qualify_name(node.target.location.slice)
48
- add_constant(node, name)
49
- when YARP::CallNode
50
- message = node.message
51
- handle_private_constant(node) if message == "private_constant"
23
+ sig { override.params(node: Prism::ClassNode).void }
24
+ def visit_class_node(node)
25
+ add_class_entry(node)
26
+ end
27
+
28
+ sig { override.params(node: Prism::ModuleNode).void }
29
+ def visit_module_node(node)
30
+ add_module_entry(node)
31
+ end
32
+
33
+ sig { override.params(node: Prism::MultiWriteNode).void }
34
+ def visit_multi_write_node(node)
35
+ value = node.value
36
+ values = value.is_a?(Prism::ArrayNode) && value.opening_loc ? value.elements : []
37
+
38
+ node.targets.each_with_index do |target, i|
39
+ current_value = values[i]
40
+ # The moment we find a splat on the right hand side of the assignment, we can no longer figure out which value
41
+ # gets assigned to what
42
+ values.clear if current_value.is_a?(Prism::SplatNode)
43
+
44
+ case target
45
+ when Prism::ConstantTargetNode
46
+ add_constant(target, fully_qualify_name(target.name.to_s), current_value)
47
+ when Prism::ConstantPathTargetNode
48
+ add_constant(target, fully_qualify_name(target.slice), current_value)
49
+ end
52
50
  end
53
51
  end
54
52
 
55
- # Override to avoid using `map` instead of `each`
56
- sig { params(nodes: T::Array[T.nilable(YARP::Node)]).void }
57
- def visit_all(nodes)
58
- nodes.each { |node| visit(node) }
53
+ sig { override.params(node: Prism::ConstantPathWriteNode).void }
54
+ def visit_constant_path_write_node(node)
55
+ # ignore variable constants like `var::FOO` or `self.class::FOO`
56
+ target = node.target
57
+ return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
58
+
59
+ name = fully_qualify_name(target.location.slice)
60
+ add_constant(node, name)
61
+ end
62
+
63
+ sig { override.params(node: Prism::ConstantPathOrWriteNode).void }
64
+ def visit_constant_path_or_write_node(node)
65
+ # ignore variable constants like `var::FOO` or `self.class::FOO`
66
+ target = node.target
67
+ return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
68
+
69
+ name = fully_qualify_name(target.location.slice)
70
+ add_constant(node, name)
71
+ end
72
+
73
+ sig { override.params(node: Prism::ConstantPathOperatorWriteNode).void }
74
+ def visit_constant_path_operator_write_node(node)
75
+ # ignore variable constants like `var::FOO` or `self.class::FOO`
76
+ target = node.target
77
+ return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
78
+
79
+ name = fully_qualify_name(target.location.slice)
80
+ add_constant(node, name)
81
+ end
82
+
83
+ sig { override.params(node: Prism::ConstantPathAndWriteNode).void }
84
+ def visit_constant_path_and_write_node(node)
85
+ # ignore variable constants like `var::FOO` or `self.class::FOO`
86
+ target = node.target
87
+ return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
88
+
89
+ name = fully_qualify_name(target.location.slice)
90
+ add_constant(node, name)
91
+ end
92
+
93
+ sig { override.params(node: Prism::ConstantWriteNode).void }
94
+ def visit_constant_write_node(node)
95
+ name = fully_qualify_name(node.name.to_s)
96
+ add_constant(node, name)
97
+ end
98
+
99
+ sig { override.params(node: Prism::ConstantOrWriteNode).void }
100
+ def visit_constant_or_write_node(node)
101
+ name = fully_qualify_name(node.name.to_s)
102
+ add_constant(node, name)
103
+ end
104
+
105
+ sig { override.params(node: Prism::ConstantAndWriteNode).void }
106
+ def visit_constant_and_write_node(node)
107
+ name = fully_qualify_name(node.name.to_s)
108
+ add_constant(node, name)
109
+ end
110
+
111
+ sig { override.params(node: Prism::ConstantOperatorWriteNode).void }
112
+ def visit_constant_operator_write_node(node)
113
+ name = fully_qualify_name(node.name.to_s)
114
+ add_constant(node, name)
115
+ end
116
+
117
+ sig { override.params(node: Prism::CallNode).void }
118
+ def visit_call_node(node)
119
+ message = node.message
120
+ handle_private_constant(node) if message == "private_constant"
121
+ end
122
+
123
+ sig { override.params(node: Prism::DefNode).void }
124
+ def visit_def_node(node)
125
+ method_name = node.name.to_s
126
+ comments = collect_comments(node)
127
+ case node.receiver
128
+ when nil
129
+ @index << Entry::InstanceMethod.new(method_name, @file_path, node.location, comments, node.parameters)
130
+ when Prism::SelfNode
131
+ @index << Entry::SingletonMethod.new(method_name, @file_path, node.location, comments, node.parameters)
132
+ end
59
133
  end
60
134
 
61
135
  private
62
136
 
63
- sig { params(node: YARP::CallNode).void }
137
+ sig { params(node: Prism::CallNode).void }
64
138
  def handle_private_constant(node)
65
139
  arguments = node.arguments&.arguments
66
140
  return unless arguments
@@ -68,9 +142,9 @@ module RubyIndexer
68
142
  first_argument = arguments.first
69
143
 
70
144
  name = case first_argument
71
- when YARP::StringNode
145
+ when Prism::StringNode
72
146
  first_argument.content
73
- when YARP::SymbolNode
147
+ when Prism::SymbolNode
74
148
  first_argument.value
75
149
  end
76
150
 
@@ -88,56 +162,78 @@ module RubyIndexer
88
162
  sig do
89
163
  params(
90
164
  node: T.any(
91
- YARP::ConstantWriteNode,
92
- YARP::ConstantOrWriteNode,
93
- YARP::ConstantPathWriteNode,
94
- YARP::ConstantPathOrWriteNode,
95
- YARP::ConstantPathOperatorWriteNode,
96
- YARP::ConstantPathAndWriteNode,
165
+ Prism::ConstantWriteNode,
166
+ Prism::ConstantOrWriteNode,
167
+ Prism::ConstantAndWriteNode,
168
+ Prism::ConstantOperatorWriteNode,
169
+ Prism::ConstantPathWriteNode,
170
+ Prism::ConstantPathOrWriteNode,
171
+ Prism::ConstantPathOperatorWriteNode,
172
+ Prism::ConstantPathAndWriteNode,
173
+ Prism::ConstantTargetNode,
174
+ Prism::ConstantPathTargetNode,
97
175
  ),
98
176
  name: String,
177
+ value: T.nilable(Prism::Node),
99
178
  ).void
100
179
  end
101
- def add_constant(node, name)
102
- value = node.value
180
+ def add_constant(node, name, value = nil)
181
+ value = node.value unless node.is_a?(Prism::ConstantTargetNode) || node.is_a?(Prism::ConstantPathTargetNode)
103
182
  comments = collect_comments(node)
104
183
 
105
184
  @index << case value
106
- when YARP::ConstantReadNode, YARP::ConstantPathNode
107
- Index::Entry::UnresolvedAlias.new(value.slice, @stack.dup, name, @file_path, node.location, comments)
108
- when YARP::ConstantWriteNode, YARP::ConstantAndWriteNode, YARP::ConstantOrWriteNode,
109
- YARP::ConstantOperatorWriteNode
185
+ when Prism::ConstantReadNode, Prism::ConstantPathNode
186
+ Entry::UnresolvedAlias.new(value.slice, @stack.dup, name, @file_path, node.location, comments)
187
+ when Prism::ConstantWriteNode, Prism::ConstantAndWriteNode, Prism::ConstantOrWriteNode,
188
+ Prism::ConstantOperatorWriteNode
110
189
 
111
190
  # If the right hand side is another constant assignment, we need to visit it because that constant has to be
112
191
  # indexed too
113
192
  visit(value)
114
- Index::Entry::UnresolvedAlias.new(value.name.to_s, @stack.dup, name, @file_path, node.location, comments)
115
- when YARP::ConstantPathWriteNode, YARP::ConstantPathOrWriteNode, YARP::ConstantPathOperatorWriteNode,
116
- YARP::ConstantPathAndWriteNode
193
+ Entry::UnresolvedAlias.new(value.name.to_s, @stack.dup, name, @file_path, node.location, comments)
194
+ when Prism::ConstantPathWriteNode, Prism::ConstantPathOrWriteNode, Prism::ConstantPathOperatorWriteNode,
195
+ Prism::ConstantPathAndWriteNode
117
196
 
118
197
  visit(value)
119
- Index::Entry::UnresolvedAlias.new(value.target.slice, @stack.dup, name, @file_path, node.location, comments)
198
+ Entry::UnresolvedAlias.new(value.target.slice, @stack.dup, name, @file_path, node.location, comments)
120
199
  else
121
- Index::Entry::Constant.new(name, @file_path, node.location, comments)
200
+ Entry::Constant.new(name, @file_path, node.location, comments)
122
201
  end
123
202
  end
124
203
 
125
- sig { params(node: T.any(YARP::ClassNode, YARP::ModuleNode), klass: T.class_of(Index::Entry)).void }
126
- def add_index_entry(node, klass)
204
+ sig { params(node: Prism::ModuleNode).void }
205
+ def add_module_entry(node)
127
206
  name = node.constant_path.location.slice
128
-
129
- unless /^[A-Z:]/.match?(name)
130
- return visit_child_nodes(node)
131
- end
207
+ return visit_child_nodes(node) unless /^[A-Z:]/.match?(name)
132
208
 
133
209
  comments = collect_comments(node)
134
- @index << klass.new(fully_qualify_name(name), @file_path, node.location, comments)
210
+
211
+ @index << Entry::Module.new(fully_qualify_name(name), @file_path, node.location, comments)
135
212
  @stack << name
136
213
  visit_child_nodes(node)
137
214
  @stack.pop
138
215
  end
139
216
 
140
- sig { params(node: YARP::Node).returns(T::Array[String]) }
217
+ sig { params(node: Prism::ClassNode).void }
218
+ def add_class_entry(node)
219
+ name = node.constant_path.location.slice
220
+ return visit_child_nodes(node) unless /^[A-Z:]/.match?(name)
221
+
222
+ comments = collect_comments(node)
223
+
224
+ superclass = node.superclass
225
+ parent_class = case superclass
226
+ when Prism::ConstantReadNode, Prism::ConstantPathNode
227
+ superclass.slice
228
+ end
229
+
230
+ @index << Entry::Class.new(fully_qualify_name(name), @file_path, node.location, comments, parent_class)
231
+ @stack << name
232
+ visit(node.body)
233
+ @stack.pop
234
+ end
235
+
236
+ sig { params(node: Prism::Node).returns(T::Array[String]) }
141
237
  def collect_comments(node)
142
238
  comments = []
143
239
 
@@ -7,6 +7,7 @@ require "did_you_mean"
7
7
  require "ruby_indexer/lib/ruby_indexer/indexable_path"
8
8
  require "ruby_indexer/lib/ruby_indexer/visitor"
9
9
  require "ruby_indexer/lib/ruby_indexer/index"
10
+ require "ruby_indexer/lib/ruby_indexer/entry"
10
11
  require "ruby_indexer/lib/ruby_indexer/configuration"
11
12
  require "ruby_indexer/lib/ruby_indexer/prefix_tree"
12
13
 
@@ -11,7 +11,7 @@ module RubyIndexer
11
11
  end
12
12
  RUBY
13
13
 
14
- assert_entry("Foo", Index::Entry::Class, "/fake/path/foo.rb:0-0:1-3")
14
+ assert_entry("Foo", Entry::Class, "/fake/path/foo.rb:0-0:1-3")
15
15
  end
16
16
 
17
17
  def test_class_with_statements
@@ -21,7 +21,7 @@ module RubyIndexer
21
21
  end
22
22
  RUBY
23
23
 
24
- assert_entry("Foo", Index::Entry::Class, "/fake/path/foo.rb:0-0:2-3")
24
+ assert_entry("Foo", Entry::Class, "/fake/path/foo.rb:0-0:2-3")
25
25
  end
26
26
 
27
27
  def test_colon_colon_class
@@ -30,7 +30,7 @@ module RubyIndexer
30
30
  end
31
31
  RUBY
32
32
 
33
- assert_entry("Foo", Index::Entry::Class, "/fake/path/foo.rb:0-0:1-3")
33
+ assert_entry("Foo", Entry::Class, "/fake/path/foo.rb:0-0:1-3")
34
34
  end
35
35
 
36
36
  def test_colon_colon_class_inside_class
@@ -41,8 +41,8 @@ module RubyIndexer
41
41
  end
42
42
  RUBY
43
43
 
44
- assert_entry("Bar", Index::Entry::Class, "/fake/path/foo.rb:0-0:3-3")
45
- assert_entry("Foo", Index::Entry::Class, "/fake/path/foo.rb:1-2:2-5")
44
+ assert_entry("Bar", Entry::Class, "/fake/path/foo.rb:0-0:3-3")
45
+ assert_entry("Foo", Entry::Class, "/fake/path/foo.rb:1-2:2-5")
46
46
  end
47
47
 
48
48
  def test_namespaced_class
@@ -51,7 +51,7 @@ module RubyIndexer
51
51
  end
52
52
  RUBY
53
53
 
54
- assert_entry("Foo::Bar", Index::Entry::Class, "/fake/path/foo.rb:0-0:1-3")
54
+ assert_entry("Foo::Bar", Entry::Class, "/fake/path/foo.rb:0-0:1-3")
55
55
  end
56
56
 
57
57
  def test_dynamically_namespaced_class
@@ -69,7 +69,7 @@ module RubyIndexer
69
69
  end
70
70
  RUBY
71
71
 
72
- assert_entry("Foo", Index::Entry::Module, "/fake/path/foo.rb:0-0:1-3")
72
+ assert_entry("Foo", Entry::Module, "/fake/path/foo.rb:0-0:1-3")
73
73
  end
74
74
 
75
75
  def test_module_with_statements
@@ -79,7 +79,7 @@ module RubyIndexer
79
79
  end
80
80
  RUBY
81
81
 
82
- assert_entry("Foo", Index::Entry::Module, "/fake/path/foo.rb:0-0:2-3")
82
+ assert_entry("Foo", Entry::Module, "/fake/path/foo.rb:0-0:2-3")
83
83
  end
84
84
 
85
85
  def test_colon_colon_module
@@ -88,7 +88,7 @@ module RubyIndexer
88
88
  end
89
89
  RUBY
90
90
 
91
- assert_entry("Foo", Index::Entry::Module, "/fake/path/foo.rb:0-0:1-3")
91
+ assert_entry("Foo", Entry::Module, "/fake/path/foo.rb:0-0:1-3")
92
92
  end
93
93
 
94
94
  def test_namespaced_module
@@ -97,7 +97,7 @@ module RubyIndexer
97
97
  end
98
98
  RUBY
99
99
 
100
- assert_entry("Foo::Bar", Index::Entry::Module, "/fake/path/foo.rb:0-0:1-3")
100
+ assert_entry("Foo::Bar", Entry::Module, "/fake/path/foo.rb:0-0:1-3")
101
101
  end
102
102
 
103
103
  def test_dynamically_namespaced_module
@@ -124,11 +124,11 @@ module RubyIndexer
124
124
  end
125
125
  RUBY
126
126
 
127
- assert_entry("Foo", Index::Entry::Module, "/fake/path/foo.rb:0-0:10-3")
128
- assert_entry("Foo::Bar", Index::Entry::Class, "/fake/path/foo.rb:1-2:2-5")
129
- assert_entry("Foo::Baz", Index::Entry::Module, "/fake/path/foo.rb:4-2:9-5")
130
- assert_entry("Foo::Baz::Qux", Index::Entry::Class, "/fake/path/foo.rb:5-4:8-7")
131
- assert_entry("Foo::Baz::Qux::Something", Index::Entry::Class, "/fake/path/foo.rb:6-6:7-9")
127
+ assert_entry("Foo", Entry::Module, "/fake/path/foo.rb:0-0:10-3")
128
+ assert_entry("Foo::Bar", Entry::Class, "/fake/path/foo.rb:1-2:2-5")
129
+ assert_entry("Foo::Baz", Entry::Module, "/fake/path/foo.rb:4-2:9-5")
130
+ assert_entry("Foo::Baz::Qux", Entry::Class, "/fake/path/foo.rb:5-4:8-7")
131
+ assert_entry("Foo::Baz::Qux::Something", Entry::Class, "/fake/path/foo.rb:6-6:7-9")
132
132
  end
133
133
 
134
134
  def test_deleting_from_index_based_on_file_path
@@ -137,7 +137,7 @@ module RubyIndexer
137
137
  end
138
138
  RUBY
139
139
 
140
- assert_entry("Foo", Index::Entry::Class, "/fake/path/foo.rb:0-0:1-3")
140
+ assert_entry("Foo", Entry::Class, "/fake/path/foo.rb:0-0:1-3")
141
141
 
142
142
  @index.delete(IndexablePath.new(nil, "/fake/path/foo.rb"))
143
143
  refute_entry("Foo")
@@ -239,5 +239,38 @@ module RubyIndexer
239
239
  d_const = @index["A::D"].first
240
240
  assert_equal(:public, d_const.visibility)
241
241
  end
242
+
243
+ def test_keeping_track_of_super_classes
244
+ index(<<~RUBY)
245
+ class Foo < Bar
246
+ end
247
+
248
+ class Baz
249
+ end
250
+
251
+ module Something
252
+ class Baz
253
+ end
254
+
255
+ class Qux < ::Baz
256
+ end
257
+ end
258
+
259
+ class FinalThing < Something::Baz
260
+ end
261
+ RUBY
262
+
263
+ foo = T.must(@index["Foo"].first)
264
+ assert_equal("Bar", foo.parent_class)
265
+
266
+ baz = T.must(@index["Baz"].first)
267
+ assert_nil(baz.parent_class)
268
+
269
+ qux = T.must(@index["Something::Qux"].first)
270
+ assert_equal("::Baz", qux.parent_class)
271
+
272
+ final_thing = T.must(@index["FinalThing"].first)
273
+ assert_equal("Something::Baz", final_thing.parent_class)
274
+ end
242
275
  end
243
276
  end