ruby-lsp 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -4
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp-check +1 -1
  5. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +35 -5
  6. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +141 -5
  7. data/lib/ruby_indexer/lib/ruby_indexer/visitor.rb +66 -18
  8. data/lib/ruby_indexer/test/classes_and_modules_test.rb +39 -16
  9. data/lib/ruby_indexer/test/configuration_test.rb +2 -0
  10. data/lib/ruby_indexer/test/constant_test.rb +213 -11
  11. data/lib/ruby_indexer/test/index_test.rb +20 -0
  12. data/lib/ruby_lsp/{extension.rb → addon.rb} +27 -25
  13. data/lib/ruby_lsp/check_docs.rb +7 -8
  14. data/lib/ruby_lsp/document.rb +35 -38
  15. data/lib/ruby_lsp/event_emitter.rb +239 -77
  16. data/lib/ruby_lsp/executor.rb +45 -55
  17. data/lib/ruby_lsp/internal.rb +2 -3
  18. data/lib/ruby_lsp/listener.rb +8 -7
  19. data/lib/ruby_lsp/parameter_scope.rb +33 -0
  20. data/lib/ruby_lsp/requests/base_request.rb +3 -3
  21. data/lib/ruby_lsp/requests/code_action_resolve.rb +14 -14
  22. data/lib/ruby_lsp/requests/code_lens.rb +39 -63
  23. data/lib/ruby_lsp/requests/completion.rb +54 -32
  24. data/lib/ruby_lsp/requests/definition.rb +30 -27
  25. data/lib/ruby_lsp/requests/diagnostics.rb +26 -3
  26. data/lib/ruby_lsp/requests/document_highlight.rb +18 -19
  27. data/lib/ruby_lsp/requests/document_link.rb +53 -10
  28. data/lib/ruby_lsp/requests/document_symbol.rb +82 -75
  29. data/lib/ruby_lsp/requests/folding_ranges.rb +199 -222
  30. data/lib/ruby_lsp/requests/formatting.rb +5 -6
  31. data/lib/ruby_lsp/requests/hover.rb +33 -22
  32. data/lib/ruby_lsp/requests/inlay_hints.rb +2 -3
  33. data/lib/ruby_lsp/requests/selection_ranges.rb +65 -40
  34. data/lib/ruby_lsp/requests/semantic_highlighting.rb +187 -145
  35. data/lib/ruby_lsp/requests/show_syntax_tree.rb +3 -4
  36. data/lib/ruby_lsp/requests/support/annotation.rb +18 -17
  37. data/lib/ruby_lsp/requests/support/common.rb +17 -26
  38. data/lib/ruby_lsp/requests/support/dependency_detector.rb +67 -42
  39. data/lib/ruby_lsp/requests/support/highlight_target.rb +64 -45
  40. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +9 -4
  41. data/lib/ruby_lsp/requests/support/selection_range.rb +5 -4
  42. data/lib/ruby_lsp/requests/support/sorbet.rb +2 -57
  43. data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +7 -1
  44. data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -1
  45. data/lib/ruby_lsp/server.rb +6 -44
  46. data/lib/ruby_lsp/setup_bundler.rb +22 -11
  47. data/lib/ruby_lsp/utils.rb +2 -12
  48. metadata +11 -30
@@ -14,8 +14,8 @@ module RubyIndexer
14
14
  end
15
15
  RUBY
16
16
 
17
- assert_entry("FOO", Index::Entry::Constant, "/fake/path/foo.rb:0-0:0-6")
18
- assert_entry("Bar::FOO", Index::Entry::Constant, "/fake/path/foo.rb:3-2:3-8")
17
+ assert_entry("FOO", Index::Entry::Constant, "/fake/path/foo.rb:0-0:0-7")
18
+ assert_entry("Bar::FOO", Index::Entry::Constant, "/fake/path/foo.rb:3-2:3-9")
19
19
  end
20
20
 
21
21
  def test_constant_or_writes
@@ -27,8 +27,8 @@ module RubyIndexer
27
27
  end
28
28
  RUBY
29
29
 
30
- assert_entry("FOO", Index::Entry::Constant, "/fake/path/foo.rb:0-0:0-8")
31
- assert_entry("Bar::FOO", Index::Entry::Constant, "/fake/path/foo.rb:3-2:3-10")
30
+ assert_entry("FOO", Index::Entry::Constant, "/fake/path/foo.rb:0-0:0-9")
31
+ assert_entry("Bar::FOO", Index::Entry::Constant, "/fake/path/foo.rb:3-2:3-11")
32
32
  end
33
33
 
34
34
  def test_constant_path_writes
@@ -45,10 +45,10 @@ module RubyIndexer
45
45
  A::BAZ = 1
46
46
  RUBY
47
47
 
48
- assert_entry("A::FOO", Index::Entry::Constant, "/fake/path/foo.rb:1-2:1-8")
49
- assert_entry("BAR", Index::Entry::Constant, "/fake/path/foo.rb:2-2:2-10")
50
- assert_entry("A::B::FOO", Index::Entry::Constant, "/fake/path/foo.rb:5-4:5-10")
51
- assert_entry("A::BAZ", Index::Entry::Constant, "/fake/path/foo.rb:9-0:9-9")
48
+ assert_entry("A::FOO", Index::Entry::Constant, "/fake/path/foo.rb:1-2:1-9")
49
+ assert_entry("BAR", Index::Entry::Constant, "/fake/path/foo.rb:2-2:2-11")
50
+ assert_entry("A::B::FOO", Index::Entry::Constant, "/fake/path/foo.rb:5-4:5-11")
51
+ assert_entry("A::BAZ", Index::Entry::Constant, "/fake/path/foo.rb:9-0:9-10")
52
52
  end
53
53
 
54
54
  def test_constant_path_or_writes
@@ -61,9 +61,9 @@ module RubyIndexer
61
61
  A::BAZ ||= 1
62
62
  RUBY
63
63
 
64
- assert_entry("A::FOO", Index::Entry::Constant, "/fake/path/foo.rb:1-2:1-10")
65
- assert_entry("BAR", Index::Entry::Constant, "/fake/path/foo.rb:2-2:2-12")
66
- assert_entry("A::BAZ", Index::Entry::Constant, "/fake/path/foo.rb:5-0:5-11")
64
+ assert_entry("A::FOO", Index::Entry::Constant, "/fake/path/foo.rb:1-2:1-11")
65
+ assert_entry("BAR", Index::Entry::Constant, "/fake/path/foo.rb:2-2:2-13")
66
+ assert_entry("A::BAZ", Index::Entry::Constant, "/fake/path/foo.rb:5-0:5-12")
67
67
  end
68
68
 
69
69
  def test_comments_for_constants
@@ -104,5 +104,207 @@ module RubyIndexer
104
104
 
105
105
  assert_no_entry
106
106
  end
107
+
108
+ def test_private_constant_indexing
109
+ index(<<~RUBY)
110
+ class A
111
+ B = 1
112
+ private_constant(:B)
113
+
114
+ C = 2
115
+ private_constant("C")
116
+
117
+ D = 1
118
+ end
119
+ RUBY
120
+
121
+ b_const = @index["A::B"].first
122
+ assert_equal(:private, b_const.visibility)
123
+
124
+ c_const = @index["A::C"].first
125
+ assert_equal(:private, c_const.visibility)
126
+
127
+ d_const = @index["A::D"].first
128
+ assert_equal(:public, d_const.visibility)
129
+ end
130
+
131
+ def test_marking_constants_as_private_reopening_namespaces
132
+ index(<<~RUBY)
133
+ module A
134
+ module B
135
+ CONST_A = 1
136
+ private_constant(:CONST_A)
137
+
138
+ CONST_B = 2
139
+ CONST_C = 3
140
+ end
141
+
142
+ module B
143
+ private_constant(:CONST_B)
144
+ end
145
+ end
146
+
147
+ module A
148
+ module B
149
+ private_constant(:CONST_C)
150
+ end
151
+ end
152
+ RUBY
153
+
154
+ a_const = @index["A::B::CONST_A"].first
155
+ assert_equal(:private, a_const.visibility)
156
+
157
+ b_const = @index["A::B::CONST_B"].first
158
+ assert_equal(:private, b_const.visibility)
159
+
160
+ c_const = @index["A::B::CONST_C"].first
161
+ assert_equal(:private, c_const.visibility)
162
+ end
163
+
164
+ def test_marking_constants_as_private_with_receiver
165
+ index(<<~RUBY)
166
+ module A
167
+ module B
168
+ CONST_A = 1
169
+ CONST_B = 2
170
+ end
171
+
172
+ B.private_constant(:CONST_A)
173
+ end
174
+
175
+ A::B.private_constant(:CONST_B)
176
+ RUBY
177
+
178
+ a_const = @index["A::B::CONST_A"].first
179
+ assert_equal(:private, a_const.visibility)
180
+
181
+ b_const = @index["A::B::CONST_B"].first
182
+ assert_equal(:private, b_const.visibility)
183
+ end
184
+
185
+ def test_indexing_constant_aliases
186
+ index(<<~RUBY)
187
+ module A
188
+ module B
189
+ module C
190
+ end
191
+ end
192
+
193
+ FIRST = B::C
194
+ end
195
+
196
+ SECOND = A::FIRST
197
+ RUBY
198
+
199
+ unresolve_entry = @index["A::FIRST"].first
200
+ assert_instance_of(Index::Entry::UnresolvedAlias, unresolve_entry)
201
+ assert_equal(["A"], unresolve_entry.nesting)
202
+ assert_equal("B::C", unresolve_entry.target)
203
+
204
+ resolved_entry = @index.resolve("A::FIRST", []).first
205
+ assert_instance_of(Index::Entry::Alias, resolved_entry)
206
+ assert_equal("A::B::C", resolved_entry.target)
207
+ end
208
+
209
+ def test_aliasing_namespaces
210
+ index(<<~RUBY)
211
+ module A
212
+ module B
213
+ module C
214
+ end
215
+ end
216
+
217
+ ALIAS = B
218
+ end
219
+
220
+ module Other
221
+ ONE_MORE = A::ALIAS
222
+ end
223
+ RUBY
224
+
225
+ unresolve_entry = @index["A::ALIAS"].first
226
+ assert_instance_of(Index::Entry::UnresolvedAlias, unresolve_entry)
227
+ assert_equal(["A"], unresolve_entry.nesting)
228
+ assert_equal("B", unresolve_entry.target)
229
+
230
+ resolved_entry = @index.resolve("ALIAS", ["A"]).first
231
+ assert_instance_of(Index::Entry::Alias, resolved_entry)
232
+ assert_equal("A::B", resolved_entry.target)
233
+
234
+ resolved_entry = @index.resolve("ALIAS::C", ["A"]).first
235
+ assert_instance_of(Index::Entry::Module, resolved_entry)
236
+ assert_equal("A::B::C", resolved_entry.name)
237
+
238
+ unresolve_entry = @index["Other::ONE_MORE"].first
239
+ assert_instance_of(Index::Entry::UnresolvedAlias, unresolve_entry)
240
+ assert_equal(["Other"], unresolve_entry.nesting)
241
+ assert_equal("A::ALIAS", unresolve_entry.target)
242
+
243
+ resolved_entry = @index.resolve("Other::ONE_MORE::C", []).first
244
+ assert_instance_of(Index::Entry::Module, resolved_entry)
245
+ end
246
+
247
+ def test_indexing_same_line_constant_aliases
248
+ index(<<~RUBY)
249
+ module A
250
+ B = C = 1
251
+ D = E ||= 1
252
+ F = G::H &&= 1
253
+ I::J = K::L = M = 1
254
+ end
255
+ RUBY
256
+
257
+ # B and C
258
+ unresolve_entry = @index["A::B"].first
259
+ assert_instance_of(Index::Entry::UnresolvedAlias, unresolve_entry)
260
+ assert_equal(["A"], unresolve_entry.nesting)
261
+ assert_equal("C", unresolve_entry.target)
262
+
263
+ resolved_entry = @index.resolve("A::B", []).first
264
+ assert_instance_of(Index::Entry::Alias, resolved_entry)
265
+ assert_equal("A::C", resolved_entry.target)
266
+
267
+ constant = @index["A::C"].first
268
+ assert_instance_of(Index::Entry::Constant, constant)
269
+
270
+ # D and E
271
+ unresolve_entry = @index["A::D"].first
272
+ assert_instance_of(Index::Entry::UnresolvedAlias, unresolve_entry)
273
+ assert_equal(["A"], unresolve_entry.nesting)
274
+ assert_equal("E", unresolve_entry.target)
275
+
276
+ resolved_entry = @index.resolve("A::D", []).first
277
+ assert_instance_of(Index::Entry::Alias, resolved_entry)
278
+ assert_equal("A::E", resolved_entry.target)
279
+
280
+ # F and G::H
281
+ unresolve_entry = @index["A::F"].first
282
+ assert_instance_of(Index::Entry::UnresolvedAlias, unresolve_entry)
283
+ assert_equal(["A"], unresolve_entry.nesting)
284
+ assert_equal("G::H", unresolve_entry.target)
285
+
286
+ resolved_entry = @index.resolve("A::F", []).first
287
+ assert_instance_of(Index::Entry::Alias, resolved_entry)
288
+ assert_equal("A::G::H", resolved_entry.target)
289
+
290
+ # I::J, K::L and M
291
+ unresolve_entry = @index["A::I::J"].first
292
+ assert_instance_of(Index::Entry::UnresolvedAlias, unresolve_entry)
293
+ assert_equal(["A"], unresolve_entry.nesting)
294
+ assert_equal("K::L", unresolve_entry.target)
295
+
296
+ resolved_entry = @index.resolve("A::I::J", []).first
297
+ assert_instance_of(Index::Entry::Alias, resolved_entry)
298
+ assert_equal("A::K::L", resolved_entry.target)
299
+
300
+ # When we are resolving A::I::J, we invoke `resolve("K::L", ["A"])`, which recursively resolves A::K::L too.
301
+ # Therefore, both A::I::J and A::K::L point to A::M by the end of the previous resolve invocation
302
+ resolved_entry = @index["A::K::L"].first
303
+ assert_instance_of(Index::Entry::Alias, resolved_entry)
304
+ assert_equal("A::M", resolved_entry.target)
305
+
306
+ constant = @index["A::M"].first
307
+ assert_instance_of(Index::Entry::Constant, constant)
308
+ end
107
309
  end
108
310
  end
@@ -158,5 +158,25 @@ module RubyIndexer
158
158
  results = @index.prefix_search("Ba", ["Foo"]).map { |entries| entries.map(&:name) }
159
159
  assert_equal([["Foo::Bar", "Foo::Bar"], ["Foo::Baz"]], results)
160
160
  end
161
+
162
+ def test_resolve_normalizes_top_level_names
163
+ @index.index_single(IndexablePath.new("/fake", "/fake/path/foo.rb"), <<~RUBY)
164
+ class Bar; end
165
+
166
+ module Foo
167
+ class Bar; end
168
+ end
169
+ RUBY
170
+
171
+ entries = @index.resolve("::Foo::Bar", [])
172
+ refute_nil(entries)
173
+
174
+ assert_equal("Foo::Bar", entries.first.name)
175
+
176
+ entries = @index.resolve("::Bar", ["Foo"])
177
+ refute_nil(entries)
178
+
179
+ assert_equal("Bar", entries.first.name)
180
+ end
161
181
  end
162
182
  end
@@ -2,24 +2,24 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyLsp
5
- # To register an extension, inherit from this class and implement both `name` and `activate`
5
+ # To register an addon, inherit from this class and implement both `name` and `activate`
6
6
  #
7
7
  # # Example
8
8
  #
9
9
  # ```ruby
10
10
  # module MyGem
11
- # class MyExtension < Extension
11
+ # class MyAddon < Addon
12
12
  # def activate
13
13
  # # Perform any relevant initialization
14
14
  # end
15
15
  #
16
16
  # def name
17
- # "My extension name"
17
+ # "My addon name"
18
18
  # end
19
19
  # end
20
20
  # end
21
21
  # ```
22
- class Extension
22
+ class Addon
23
23
  extend T::Sig
24
24
  extend T::Helpers
25
25
 
@@ -28,37 +28,37 @@ module RubyLsp
28
28
  class << self
29
29
  extend T::Sig
30
30
 
31
- # Automatically track and instantiate extension classes
32
- sig { params(child_class: T.class_of(Extension)).void }
31
+ # Automatically track and instantiate addon classes
32
+ sig { params(child_class: T.class_of(Addon)).void }
33
33
  def inherited(child_class)
34
- extensions << child_class.new
34
+ addons << child_class.new
35
35
  super
36
36
  end
37
37
 
38
- sig { returns(T::Array[Extension]) }
39
- def extensions
40
- @extensions ||= T.let([], T.nilable(T::Array[Extension]))
38
+ sig { returns(T::Array[Addon]) }
39
+ def addons
40
+ @addons ||= T.let([], T.nilable(T::Array[Addon]))
41
41
  end
42
42
 
43
- # Discovers and loads all extensions. Returns the list of activated extensions
44
- sig { returns(T::Array[Extension]) }
45
- def load_extensions
46
- # Require all extensions entry points, which should be placed under
47
- # `some_gem/lib/ruby_lsp/your_gem_name/extension.rb`
48
- Gem.find_files("ruby_lsp/**/extension.rb").each do |extension|
49
- require File.expand_path(extension)
43
+ # Discovers and loads all addons. Returns the list of activated addons
44
+ sig { returns(T::Array[Addon]) }
45
+ def load_addons
46
+ # Require all addons entry points, which should be placed under
47
+ # `some_gem/lib/ruby_lsp/your_gem_name/addon.rb`
48
+ Gem.find_files("ruby_lsp/**/addon.rb").each do |addon|
49
+ require File.expand_path(addon)
50
50
  rescue => e
51
51
  warn(e.message)
52
52
  warn(e.backtrace.to_s) # rubocop:disable Lint/RedundantStringCoercion
53
53
  end
54
54
 
55
- # Activate each one of the discovered extensions. If any problems occur in the extensions, we don't want to
55
+ # Activate each one of the discovered addons. If any problems occur in the addons, we don't want to
56
56
  # fail to boot the server
57
- extensions.each do |extension|
58
- extension.activate
57
+ addons.each do |addon|
58
+ addon.activate
59
59
  nil
60
60
  rescue => e
61
- extension.add_error(e)
61
+ addon.add_error(e)
62
62
  end
63
63
  end
64
64
  end
@@ -92,17 +92,17 @@ module RubyLsp
92
92
  @errors.filter_map(&:backtrace).join("\n\n")
93
93
  end
94
94
 
95
- # Each extension should implement `MyExtension#activate` and use to perform any sort of initialization, such as
95
+ # Each addon should implement `MyAddon#activate` and use to perform any sort of initialization, such as
96
96
  # reading information into memory or even spawning a separate process
97
97
  sig { abstract.void }
98
98
  def activate; end
99
99
 
100
- # Each extension should implement `MyExtension#deactivate` and use to perform any clean up, like shutting down a
100
+ # Each addon should implement `MyAddon#deactivate` and use to perform any clean up, like shutting down a
101
101
  # child process
102
102
  sig { abstract.void }
103
103
  def deactivate; end
104
104
 
105
- # Extensions should override the `name` method to return the extension name
105
+ # Addons should override the `name` method to return the addon name
106
106
  sig { abstract.returns(String) }
107
107
  def name; end
108
108
 
@@ -119,11 +119,13 @@ module RubyLsp
119
119
  # Creates a new Hover listener. This method is invoked on every Hover request
120
120
  sig do
121
121
  overridable.params(
122
+ nesting: T::Array[String],
123
+ index: RubyIndexer::Index,
122
124
  emitter: EventEmitter,
123
125
  message_queue: Thread::Queue,
124
126
  ).returns(T.nilable(Listener[T.nilable(Interface::Hover)]))
125
127
  end
126
- def create_hover_listener(emitter, message_queue); end
128
+ def create_hover_listener(nesting, index, emitter, message_queue); end
127
129
 
128
130
  # Creates a new DocumentSymbol listener. This method is invoked on every DocumentSymbol request
129
131
  sig do
@@ -5,9 +5,9 @@ require "ruby_lsp/internal"
5
5
  require "objspace"
6
6
 
7
7
  module RubyLsp
8
- # This rake task checks that all requests or extensions are fully documented. Add the rake task to your Rakefile and
9
- # specify the absolute path for all files that must be required in order to discover all listeners and their
10
- # related GIFs
8
+ # This rake task checks that all requests or addons are fully documented. Add the rake task to your Rakefile and
9
+ # specify the absolute path for all files that must be required in order to discover all listeners and their related
10
+ # GIFs
11
11
  #
12
12
  # # Rakefile
13
13
  # request_files = FileList.new("#{__dir__}/lib/ruby_lsp/requests/*.rb") do |fl|
@@ -53,8 +53,7 @@ module RubyLsp
53
53
  # documented
54
54
  features = ObjectSpace.each_object(Class).filter_map do |k|
55
55
  klass = T.unsafe(k)
56
- klass if klass < RubyLsp::Requests::BaseRequest ||
57
- (klass < RubyLsp::Listener && klass != RubyLsp::ExtensibleListener)
56
+ klass if klass < Requests::BaseRequest || (klass < Listener && klass != ExtensibleListener)
58
57
  end
59
58
 
60
59
  missing_docs = T.let(Hash.new { |h, k| h[k] = [] }, T::Hash[String, T::Array[String]])
@@ -82,14 +81,14 @@ module RubyLsp
82
81
  T.must(missing_docs[class_name]) << "No documentation found"
83
82
  elsif !%r{\(https://microsoft.github.io/language-server-protocol/specification#.*\)}.match?(documentation)
84
83
  T.must(missing_docs[class_name]) << <<~DOCS
85
- Missing specification link. Requests and extensions should include a link to the LSP specification for the
84
+ Missing specification link. Requests and addons should include a link to the LSP specification for the
86
85
  related feature. For example:
87
86
 
88
87
  [Inlay hint](https://microsoft.github.io/language-server-protocol/specification#textDocument_inlayHint)
89
88
  DOCS
90
89
  elsif !documentation.include?("# Example")
91
90
  T.must(missing_docs[class_name]) << <<~DOCS
92
- Missing example. Requests and extensions should include a code example that explains what the feature does.
91
+ Missing example. Requests and addons should include a code example that explains what the feature does.
93
92
 
94
93
  # # Example
95
94
  # ```ruby
@@ -99,7 +98,7 @@ module RubyLsp
99
98
  DOCS
100
99
  elsif !/\[.* demo\]\(.*\.gif\)/.match?(documentation)
101
100
  T.must(missing_docs[class_name]) << <<~DOCS
102
- Missing demonstration GIF. Each request and extension must be documented with a GIF that shows the feature
101
+ Missing demonstration GIF. Each request and addon must be documented with a GIF that shows the feature
103
102
  working. For example:
104
103
 
105
104
  # [Inlay hint demo](../../inlay_hint.gif)
@@ -9,8 +9,8 @@ module RubyLsp
9
9
  RangeShape = T.type_alias { { start: PositionShape, end: PositionShape } }
10
10
  EditShape = T.type_alias { { range: RangeShape, text: String } }
11
11
 
12
- sig { returns(T.nilable(SyntaxTree::Node)) }
13
- attr_reader :tree
12
+ sig { returns(YARP::ParseResult) }
13
+ attr_reader :parse_result
14
14
 
15
15
  sig { returns(String) }
16
16
  attr_reader :source
@@ -28,11 +28,18 @@ module RubyLsp
28
28
  @source = T.let(source, String)
29
29
  @version = T.let(version, Integer)
30
30
  @uri = T.let(uri, URI::Generic)
31
- @unparsed_edits = T.let([], T::Array[EditShape])
32
- @syntax_error = T.let(false, T::Boolean)
33
- @tree = T.let(SyntaxTree.parse(@source), T.nilable(SyntaxTree::Node))
34
- rescue SyntaxTree::Parser::ParseError
35
- @syntax_error = true
31
+ @needs_parsing = T.let(false, T::Boolean)
32
+ @parse_result = T.let(YARP.parse(@source), YARP::ParseResult)
33
+ end
34
+
35
+ sig { returns(YARP::ProgramNode) }
36
+ def tree
37
+ @parse_result.value
38
+ end
39
+
40
+ sig { returns(T::Array[YARP::Comment]) }
41
+ def comments
42
+ @parse_result.comments
36
43
  end
37
44
 
38
45
  sig { params(other: Document).returns(T::Boolean) }
@@ -80,29 +87,21 @@ module RubyLsp
80
87
  end
81
88
 
82
89
  @version = version
83
- @unparsed_edits.concat(edits)
90
+ @needs_parsing = true
84
91
  @cache.clear
85
92
  end
86
93
 
87
94
  sig { void }
88
95
  def parse
89
- return if @unparsed_edits.empty?
96
+ return unless @needs_parsing
90
97
 
91
- @unparsed_edits.clear
92
- @tree = SyntaxTree.parse(@source)
93
- @syntax_error = false
94
- rescue SyntaxTree::Parser::ParseError
95
- @syntax_error = true
98
+ @needs_parsing = false
99
+ @parse_result = YARP.parse(@source)
96
100
  end
97
101
 
98
102
  sig { returns(T::Boolean) }
99
103
  def syntax_error?
100
- @syntax_error
101
- end
102
-
103
- sig { returns(T::Boolean) }
104
- def parsed?
105
- !@tree.nil?
104
+ @parse_result.failure?
106
105
  end
107
106
 
108
107
  sig { returns(Scanner) }
@@ -113,27 +112,25 @@ module RubyLsp
113
112
  sig do
114
113
  params(
115
114
  position: PositionShape,
116
- node_types: T::Array[T.class_of(SyntaxTree::Node)],
117
- ).returns([T.nilable(SyntaxTree::Node), T.nilable(SyntaxTree::Node), T::Array[String]])
115
+ node_types: T::Array[T.class_of(YARP::Node)],
116
+ ).returns([T.nilable(YARP::Node), T.nilable(YARP::Node), T::Array[String]])
118
117
  end
119
118
  def locate_node(position, node_types: [])
120
- return [nil, nil, []] unless parsed?
121
-
122
- locate(T.must(@tree), create_scanner.find_char_position(position), node_types: node_types)
119
+ locate(@parse_result.value, create_scanner.find_char_position(position), node_types: node_types)
123
120
  end
124
121
 
125
122
  sig do
126
123
  params(
127
- node: SyntaxTree::Node,
124
+ node: YARP::Node,
128
125
  char_position: Integer,
129
- node_types: T::Array[T.class_of(SyntaxTree::Node)],
130
- ).returns([T.nilable(SyntaxTree::Node), T.nilable(SyntaxTree::Node), T::Array[String]])
126
+ node_types: T::Array[T.class_of(YARP::Node)],
127
+ ).returns([T.nilable(YARP::Node), T.nilable(YARP::Node), T::Array[String]])
131
128
  end
132
129
  def locate(node, char_position, node_types: [])
133
- queue = T.let(node.child_nodes.compact, T::Array[T.nilable(SyntaxTree::Node)])
130
+ queue = T.let(node.child_nodes.compact, T::Array[T.nilable(YARP::Node)])
134
131
  closest = node
135
- parent = T.let(nil, T.nilable(SyntaxTree::Node))
136
- nesting = T.let([], T::Array[T.any(SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration)])
132
+ parent = T.let(nil, T.nilable(YARP::Node))
133
+ nesting = T.let([], T::Array[T.any(YARP::ClassNode, YARP::ModuleNode)])
137
134
 
138
135
  until queue.empty?
139
136
  candidate = queue.shift
@@ -144,24 +141,24 @@ module RubyLsp
144
141
  # Add the next child_nodes to the queue to be processed. The order here is important! We want to move in the
145
142
  # same order as the visiting mechanism, which means searching the child nodes before moving on to the next
146
143
  # sibling
147
- queue.unshift(*candidate.child_nodes)
144
+ T.unsafe(queue).unshift(*candidate.child_nodes)
148
145
 
149
146
  # Skip if the current node doesn't cover the desired position
150
147
  loc = candidate.location
151
- next unless (loc.start_char...loc.end_char).cover?(char_position)
148
+ next unless (loc.start_offset...loc.end_offset).cover?(char_position)
152
149
 
153
150
  # If the node's start character is already past the position, then we should've found the closest node
154
151
  # already
155
- break if char_position < loc.start_char
152
+ break if char_position < loc.start_offset
156
153
 
157
154
  # If the candidate starts after the end of the previous nesting level, then we've exited that nesting level and
158
155
  # need to pop the stack
159
156
  previous_level = nesting.last
160
- nesting.pop if previous_level && candidate.start_char > previous_level.end_char
157
+ nesting.pop if previous_level && loc.start_offset > previous_level.location.end_offset
161
158
 
162
159
  # Keep track of the nesting where we found the target. This is used to determine the fully qualified name of the
163
160
  # target when it is a constant
164
- if candidate.is_a?(SyntaxTree::ClassDeclaration) || candidate.is_a?(SyntaxTree::ModuleDeclaration)
161
+ if candidate.is_a?(YARP::ClassNode) || candidate.is_a?(YARP::ModuleNode)
165
162
  nesting << candidate
166
163
  end
167
164
 
@@ -170,13 +167,13 @@ module RubyLsp
170
167
 
171
168
  # If the current node is narrower than or equal to the previous closest node, then it is more precise
172
169
  closest_loc = closest.location
173
- if loc.end_char - loc.start_char <= closest_loc.end_char - closest_loc.start_char
170
+ if loc.end_offset - loc.start_offset <= closest_loc.end_offset - closest_loc.start_offset
174
171
  parent = closest
175
172
  closest = candidate
176
173
  end
177
174
  end
178
175
 
179
- [closest, parent, nesting.map { |n| n.constant.constant.value }]
176
+ [closest, parent, nesting.map { |n| n.constant_path.location.slice }]
180
177
  end
181
178
 
182
179
  class Scanner