ruby-lsp 0.11.2 → 0.12.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp +11 -2
  4. data/exe/ruby-lsp-check +2 -1
  5. data/exe/ruby-lsp-doctor +15 -0
  6. data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +125 -0
  7. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +10 -2
  8. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +205 -0
  9. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +23 -106
  10. data/lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb +1 -1
  11. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +6 -6
  12. data/lib/ruby_indexer/lib/ruby_indexer/visitor.rb +101 -49
  13. data/lib/ruby_indexer/ruby_indexer.rb +4 -3
  14. data/lib/ruby_indexer/test/classes_and_modules_test.rb +49 -16
  15. data/lib/ruby_indexer/test/constant_test.rb +99 -36
  16. data/lib/ruby_indexer/test/index_test.rb +1 -1
  17. data/lib/ruby_indexer/test/method_test.rb +73 -0
  18. data/lib/ruby_indexer/test/test_case.rb +5 -1
  19. data/lib/ruby_lsp/addon.rb +8 -8
  20. data/lib/ruby_lsp/document.rb +14 -14
  21. data/lib/ruby_lsp/executor.rb +89 -53
  22. data/lib/ruby_lsp/internal.rb +7 -2
  23. data/lib/ruby_lsp/listener.rb +6 -6
  24. data/lib/ruby_lsp/requests/base_request.rb +1 -9
  25. data/lib/ruby_lsp/requests/code_action_resolve.rb +3 -3
  26. data/lib/ruby_lsp/requests/code_lens.rb +47 -31
  27. data/lib/ruby_lsp/requests/completion.rb +83 -32
  28. data/lib/ruby_lsp/requests/definition.rb +21 -15
  29. data/lib/ruby_lsp/requests/diagnostics.rb +1 -1
  30. data/lib/ruby_lsp/requests/document_highlight.rb +508 -31
  31. data/lib/ruby_lsp/requests/document_link.rb +24 -17
  32. data/lib/ruby_lsp/requests/document_symbol.rb +42 -42
  33. data/lib/ruby_lsp/requests/folding_ranges.rb +83 -77
  34. data/lib/ruby_lsp/requests/hover.rb +22 -17
  35. data/lib/ruby_lsp/requests/inlay_hints.rb +6 -6
  36. data/lib/ruby_lsp/requests/selection_ranges.rb +13 -105
  37. data/lib/ruby_lsp/requests/semantic_highlighting.rb +92 -92
  38. data/lib/ruby_lsp/requests/support/annotation.rb +3 -3
  39. data/lib/ruby_lsp/requests/support/common.rb +5 -5
  40. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +21 -7
  41. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +19 -0
  42. data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +10 -7
  43. data/lib/ruby_lsp/requests/support/sorbet.rb +28 -28
  44. data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -4
  45. data/lib/ruby_lsp/requests.rb +0 -1
  46. data/lib/ruby_lsp/setup_bundler.rb +26 -17
  47. metadata +20 -17
  48. data/lib/ruby_lsp/event_emitter.rb +0 -351
  49. data/lib/ruby_lsp/requests/support/highlight_target.rb +0 -118
@@ -2,10 +2,10 @@
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
11
  @file_path = file_path
@@ -14,95 +14,127 @@ module RubyIndexer
14
14
  parse_result.comments.to_h do |c|
15
15
  [c.location.start_line, c]
16
16
  end,
17
- T::Hash[Integer, YARP::Comment],
17
+ T::Hash[Integer, Prism::Comment],
18
18
  )
19
19
 
20
20
  super()
21
21
  end
22
22
 
23
- sig { override.params(node: YARP::ClassNode).void }
23
+ sig { override.params(node: Prism::ClassNode).void }
24
24
  def visit_class_node(node)
25
- add_index_entry(node, Index::Entry::Class)
25
+ add_class_entry(node)
26
26
  end
27
27
 
28
- sig { override.params(node: YARP::ModuleNode).void }
28
+ sig { override.params(node: Prism::ModuleNode).void }
29
29
  def visit_module_node(node)
30
- add_index_entry(node, Index::Entry::Module)
30
+ add_module_entry(node)
31
31
  end
32
32
 
33
- sig { override.params(node: YARP::ConstantPathWriteNode).void }
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
50
+ end
51
+ end
52
+
53
+ sig { override.params(node: Prism::ConstantPathWriteNode).void }
34
54
  def visit_constant_path_write_node(node)
35
55
  # ignore variable constants like `var::FOO` or `self.class::FOO`
36
56
  target = node.target
37
- return unless target.parent.nil? || target.parent.is_a?(YARP::ConstantReadNode)
57
+ return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
38
58
 
39
59
  name = fully_qualify_name(target.location.slice)
40
60
  add_constant(node, name)
41
61
  end
42
62
 
43
- sig { override.params(node: YARP::ConstantPathOrWriteNode).void }
63
+ sig { override.params(node: Prism::ConstantPathOrWriteNode).void }
44
64
  def visit_constant_path_or_write_node(node)
45
65
  # ignore variable constants like `var::FOO` or `self.class::FOO`
46
66
  target = node.target
47
- return unless target.parent.nil? || target.parent.is_a?(YARP::ConstantReadNode)
67
+ return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
48
68
 
49
69
  name = fully_qualify_name(target.location.slice)
50
70
  add_constant(node, name)
51
71
  end
52
72
 
53
- sig { override.params(node: YARP::ConstantPathOperatorWriteNode).void }
73
+ sig { override.params(node: Prism::ConstantPathOperatorWriteNode).void }
54
74
  def visit_constant_path_operator_write_node(node)
55
75
  # ignore variable constants like `var::FOO` or `self.class::FOO`
56
76
  target = node.target
57
- return unless target.parent.nil? || target.parent.is_a?(YARP::ConstantReadNode)
77
+ return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
58
78
 
59
79
  name = fully_qualify_name(target.location.slice)
60
80
  add_constant(node, name)
61
81
  end
62
82
 
63
- sig { override.params(node: YARP::ConstantPathAndWriteNode).void }
83
+ sig { override.params(node: Prism::ConstantPathAndWriteNode).void }
64
84
  def visit_constant_path_and_write_node(node)
65
85
  # ignore variable constants like `var::FOO` or `self.class::FOO`
66
86
  target = node.target
67
- return unless target.parent.nil? || target.parent.is_a?(YARP::ConstantReadNode)
87
+ return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
68
88
 
69
89
  name = fully_qualify_name(target.location.slice)
70
90
  add_constant(node, name)
71
91
  end
72
92
 
73
- sig { override.params(node: YARP::ConstantWriteNode).void }
93
+ sig { override.params(node: Prism::ConstantWriteNode).void }
74
94
  def visit_constant_write_node(node)
75
95
  name = fully_qualify_name(node.name.to_s)
76
96
  add_constant(node, name)
77
97
  end
78
98
 
79
- sig { override.params(node: YARP::ConstantOrWriteNode).void }
99
+ sig { override.params(node: Prism::ConstantOrWriteNode).void }
80
100
  def visit_constant_or_write_node(node)
81
101
  name = fully_qualify_name(node.name.to_s)
82
102
  add_constant(node, name)
83
103
  end
84
104
 
85
- sig { override.params(node: YARP::ConstantAndWriteNode).void }
105
+ sig { override.params(node: Prism::ConstantAndWriteNode).void }
86
106
  def visit_constant_and_write_node(node)
87
107
  name = fully_qualify_name(node.name.to_s)
88
108
  add_constant(node, name)
89
109
  end
90
110
 
91
- sig { override.params(node: YARP::ConstantOperatorWriteNode).void }
111
+ sig { override.params(node: Prism::ConstantOperatorWriteNode).void }
92
112
  def visit_constant_operator_write_node(node)
93
113
  name = fully_qualify_name(node.name.to_s)
94
114
  add_constant(node, name)
95
115
  end
96
116
 
97
- sig { override.params(node: YARP::CallNode).void }
117
+ sig { override.params(node: Prism::CallNode).void }
98
118
  def visit_call_node(node)
99
119
  message = node.message
100
120
  handle_private_constant(node) if message == "private_constant"
101
121
  end
102
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
133
+ end
134
+
103
135
  private
104
136
 
105
- sig { params(node: YARP::CallNode).void }
137
+ sig { params(node: Prism::CallNode).void }
106
138
  def handle_private_constant(node)
107
139
  arguments = node.arguments&.arguments
108
140
  return unless arguments
@@ -110,9 +142,9 @@ module RubyIndexer
110
142
  first_argument = arguments.first
111
143
 
112
144
  name = case first_argument
113
- when YARP::StringNode
145
+ when Prism::StringNode
114
146
  first_argument.content
115
- when YARP::SymbolNode
147
+ when Prism::SymbolNode
116
148
  first_argument.value
117
149
  end
118
150
 
@@ -130,58 +162,78 @@ module RubyIndexer
130
162
  sig do
131
163
  params(
132
164
  node: T.any(
133
- YARP::ConstantWriteNode,
134
- YARP::ConstantOrWriteNode,
135
- YARP::ConstantAndWriteNode,
136
- YARP::ConstantOperatorWriteNode,
137
- YARP::ConstantPathWriteNode,
138
- YARP::ConstantPathOrWriteNode,
139
- YARP::ConstantPathOperatorWriteNode,
140
- 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,
141
175
  ),
142
176
  name: String,
177
+ value: T.nilable(Prism::Node),
143
178
  ).void
144
179
  end
145
- def add_constant(node, name)
146
- 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)
147
182
  comments = collect_comments(node)
148
183
 
149
184
  @index << case value
150
- when YARP::ConstantReadNode, YARP::ConstantPathNode
151
- Index::Entry::UnresolvedAlias.new(value.slice, @stack.dup, name, @file_path, node.location, comments)
152
- when YARP::ConstantWriteNode, YARP::ConstantAndWriteNode, YARP::ConstantOrWriteNode,
153
- 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
154
189
 
155
190
  # If the right hand side is another constant assignment, we need to visit it because that constant has to be
156
191
  # indexed too
157
192
  visit(value)
158
- Index::Entry::UnresolvedAlias.new(value.name.to_s, @stack.dup, name, @file_path, node.location, comments)
159
- when YARP::ConstantPathWriteNode, YARP::ConstantPathOrWriteNode, YARP::ConstantPathOperatorWriteNode,
160
- 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
161
196
 
162
197
  visit(value)
163
- 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)
164
199
  else
165
- Index::Entry::Constant.new(name, @file_path, node.location, comments)
200
+ Entry::Constant.new(name, @file_path, node.location, comments)
166
201
  end
167
202
  end
168
203
 
169
- sig { params(node: T.any(YARP::ClassNode, YARP::ModuleNode), klass: T.class_of(Index::Entry)).void }
170
- def add_index_entry(node, klass)
204
+ sig { params(node: Prism::ModuleNode).void }
205
+ def add_module_entry(node)
171
206
  name = node.constant_path.location.slice
207
+ return visit_child_nodes(node) unless /^[A-Z:]/.match?(name)
172
208
 
173
- unless /^[A-Z:]/.match?(name)
174
- return visit_child_nodes(node)
175
- end
209
+ comments = collect_comments(node)
210
+
211
+ @index << Entry::Module.new(fully_qualify_name(name), @file_path, node.location, comments)
212
+ @stack << name
213
+ visit_child_nodes(node)
214
+ @stack.pop
215
+ end
216
+
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)
176
221
 
177
222
  comments = collect_comments(node)
178
- @index << klass.new(fully_qualify_name(name), @file_path, node.location, comments)
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)
179
231
  @stack << name
180
232
  visit(node.body)
181
233
  @stack.pop
182
234
  end
183
235
 
184
- sig { params(node: YARP::Node).returns(T::Array[String]) }
236
+ sig { params(node: Prism::Node).returns(T::Array[String]) }
185
237
  def collect_comments(node)
186
238
  comments = []
187
239
 
@@ -7,16 +7,17 @@ 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
 
13
14
  module RubyIndexer
15
+ @configuration = T.let(Configuration.new, Configuration)
16
+
14
17
  class << self
15
18
  extend T::Sig
16
19
 
17
20
  sig { returns(Configuration) }
18
- def configuration
19
- @configuration ||= T.let(Configuration.new, T.nilable(Configuration))
20
- end
21
+ attr_reader :configuration
21
22
  end
22
23
  end
@@ -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