ruby-lsp 0.10.0 → 0.11.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 (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