ruby-lsp 0.11.1 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
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