ruby-lsp 0.19.0 → 0.20.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp-check +1 -1
  4. data/lib/core_ext/uri.rb +2 -2
  5. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +85 -36
  6. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +5 -1
  7. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +46 -98
  8. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +7 -6
  9. data/lib/ruby_indexer/lib/ruby_indexer/location.rb +22 -0
  10. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +20 -5
  11. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +76 -14
  12. data/lib/ruby_indexer/test/classes_and_modules_test.rb +12 -0
  13. data/lib/ruby_indexer/test/enhancements_test.rb +5 -7
  14. data/lib/ruby_indexer/test/global_variable_test.rb +49 -0
  15. data/lib/ruby_indexer/test/index_test.rb +3 -0
  16. data/lib/ruby_indexer/test/rbs_indexer_test.rb +14 -0
  17. data/lib/ruby_indexer/test/reference_finder_test.rb +162 -6
  18. data/lib/ruby_lsp/erb_document.rb +20 -2
  19. data/lib/ruby_lsp/internal.rb +3 -1
  20. data/lib/ruby_lsp/listeners/definition.rb +20 -0
  21. data/lib/ruby_lsp/listeners/folding_ranges.rb +3 -3
  22. data/lib/ruby_lsp/requests/code_action_resolve.rb +16 -4
  23. data/lib/ruby_lsp/requests/completion.rb +1 -0
  24. data/lib/ruby_lsp/requests/definition.rb +2 -0
  25. data/lib/ruby_lsp/requests/document_highlight.rb +5 -1
  26. data/lib/ruby_lsp/requests/hover.rb +1 -0
  27. data/lib/ruby_lsp/requests/range_formatting.rb +57 -0
  28. data/lib/ruby_lsp/requests/references.rb +146 -0
  29. data/lib/ruby_lsp/requests/rename.rb +16 -9
  30. data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -1
  31. data/lib/ruby_lsp/requests/signature_help.rb +6 -1
  32. data/lib/ruby_lsp/requests/support/common.rb +1 -1
  33. data/lib/ruby_lsp/requests/support/formatter.rb +3 -0
  34. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +6 -0
  35. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +6 -6
  36. data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +8 -0
  37. data/lib/ruby_lsp/response_builders/document_symbol.rb +2 -2
  38. data/lib/ruby_lsp/response_builders/hover.rb +2 -2
  39. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +14 -8
  40. data/lib/ruby_lsp/response_builders/signature_help.rb +2 -2
  41. data/lib/ruby_lsp/ruby_document.rb +44 -8
  42. data/lib/ruby_lsp/server.rb +63 -2
  43. data/lib/ruby_lsp/type_inferrer.rb +1 -1
  44. metadata +8 -5
@@ -44,6 +44,8 @@ module RubyIndexer
44
44
  when RBS::AST::Declarations::Constant
45
45
  namespace_nesting = declaration.name.namespace.path.map(&:to_s)
46
46
  handle_constant(declaration, namespace_nesting, pathname.to_s)
47
+ when RBS::AST::Declarations::Global
48
+ handle_global_variable(declaration, pathname)
47
49
  else # rubocop:disable Style/EmptyElse
48
50
  # Other kinds not yet handled
49
51
  end
@@ -59,9 +61,9 @@ module RubyIndexer
59
61
  comments = comments_to_string(declaration)
60
62
  entry = if declaration.is_a?(RBS::AST::Declarations::Class)
61
63
  parent_class = declaration.super_class&.name&.name&.to_s
62
- Entry::Class.new(nesting, file_path, location, location, comments, @index.configuration.encoding, parent_class)
64
+ Entry::Class.new(nesting, file_path, location, location, comments, parent_class)
63
65
  else
64
- Entry::Module.new(nesting, file_path, location, location, comments, @index.configuration.encoding)
66
+ Entry::Module.new(nesting, file_path, location, location, comments)
65
67
  end
66
68
  add_declaration_mixins_to_entry(declaration, entry)
67
69
  @index.add(entry)
@@ -134,7 +136,6 @@ module RubyIndexer
134
136
  location,
135
137
  location,
136
138
  comments,
137
- @index.configuration.encoding,
138
139
  signatures,
139
140
  visibility,
140
141
  real_owner,
@@ -267,7 +268,21 @@ module RubyIndexer
267
268
  file_path,
268
269
  to_ruby_indexer_location(declaration.location),
269
270
  comments_to_string(declaration),
270
- @index.configuration.encoding,
271
+ ))
272
+ end
273
+
274
+ sig { params(declaration: RBS::AST::Declarations::Global, pathname: Pathname).void }
275
+ def handle_global_variable(declaration, pathname)
276
+ name = declaration.name.to_s
277
+ file_path = pathname.to_s
278
+ location = to_ruby_indexer_location(declaration.location)
279
+ comments = comments_to_string(declaration)
280
+
281
+ @index.add(Entry::GlobalVariable.new(
282
+ name,
283
+ file_path,
284
+ location,
285
+ comments,
271
286
  ))
272
287
  end
273
288
 
@@ -283,7 +298,6 @@ module RubyIndexer
283
298
  file_path,
284
299
  to_ruby_indexer_location(member.location),
285
300
  comments,
286
- @index.configuration.encoding,
287
301
  )
288
302
 
289
303
  @index.add(entry)
@@ -294,6 +308,7 @@ module RubyIndexer
294
308
  RBS::AST::Declarations::Class,
295
309
  RBS::AST::Declarations::Module,
296
310
  RBS::AST::Declarations::Constant,
311
+ RBS::AST::Declarations::Global,
297
312
  RBS::AST::Members::MethodDefinition,
298
313
  RBS::AST::Members::Alias,
299
314
  )).returns(T.nilable(String))
@@ -5,6 +5,38 @@ module RubyIndexer
5
5
  class ReferenceFinder
6
6
  extend T::Sig
7
7
 
8
+ class Target
9
+ extend T::Helpers
10
+
11
+ abstract!
12
+ end
13
+
14
+ class ConstTarget < Target
15
+ extend T::Sig
16
+
17
+ sig { returns(String) }
18
+ attr_reader :fully_qualified_name
19
+
20
+ sig { params(fully_qualified_name: String).void }
21
+ def initialize(fully_qualified_name)
22
+ super()
23
+ @fully_qualified_name = fully_qualified_name
24
+ end
25
+ end
26
+
27
+ class MethodTarget < Target
28
+ extend T::Sig
29
+
30
+ sig { returns(String) }
31
+ attr_reader :method_name
32
+
33
+ sig { params(method_name: String).void }
34
+ def initialize(method_name)
35
+ super()
36
+ @method_name = method_name
37
+ end
38
+ end
39
+
8
40
  class Reference
9
41
  extend T::Sig
10
42
 
@@ -14,26 +46,29 @@ module RubyIndexer
14
46
  sig { returns(Prism::Location) }
15
47
  attr_reader :location
16
48
 
17
- sig { params(name: String, location: Prism::Location).void }
18
- def initialize(name, location)
49
+ sig { returns(T::Boolean) }
50
+ attr_reader :declaration
51
+
52
+ sig { params(name: String, location: Prism::Location, declaration: T::Boolean).void }
53
+ def initialize(name, location, declaration:)
19
54
  @name = name
20
55
  @location = location
56
+ @declaration = declaration
21
57
  end
22
58
  end
23
59
 
24
- sig { returns(T::Array[Reference]) }
25
- attr_reader :references
26
-
27
60
  sig do
28
61
  params(
29
- fully_qualified_name: String,
62
+ target: Target,
30
63
  index: RubyIndexer::Index,
31
64
  dispatcher: Prism::Dispatcher,
65
+ include_declarations: T::Boolean,
32
66
  ).void
33
67
  end
34
- def initialize(fully_qualified_name, index, dispatcher)
35
- @fully_qualified_name = fully_qualified_name
68
+ def initialize(target, index, dispatcher, include_declarations: true)
69
+ @target = target
36
70
  @index = index
71
+ @include_declarations = include_declarations
37
72
  @stack = T.let([], T::Array[String])
38
73
  @references = T.let([], T::Array[Reference])
39
74
 
@@ -59,17 +94,25 @@ module RubyIndexer
59
94
  :on_constant_or_write_node_enter,
60
95
  :on_constant_and_write_node_enter,
61
96
  :on_constant_operator_write_node_enter,
97
+ :on_call_node_enter,
62
98
  )
63
99
  end
64
100
 
101
+ sig { returns(T::Array[Reference]) }
102
+ def references
103
+ return @references if @include_declarations
104
+
105
+ @references.reject(&:declaration)
106
+ end
107
+
65
108
  sig { params(node: Prism::ClassNode).void }
66
109
  def on_class_node_enter(node)
67
110
  constant_path = node.constant_path
68
111
  name = constant_path.slice
69
112
  nesting = actual_nesting(name)
70
113
 
71
- if nesting.join("::") == @fully_qualified_name
72
- @references << Reference.new(name, constant_path.location)
114
+ if @target.is_a?(ConstTarget) && nesting.join("::") == @target.fully_qualified_name
115
+ @references << Reference.new(name, constant_path.location, declaration: true)
73
116
  end
74
117
 
75
118
  @stack << name
@@ -86,8 +129,8 @@ module RubyIndexer
86
129
  name = constant_path.slice
87
130
  nesting = actual_nesting(name)
88
131
 
89
- if nesting.join("::") == @fully_qualified_name
90
- @references << Reference.new(name, constant_path.location)
132
+ if @target.is_a?(ConstTarget) && nesting.join("::") == @target.fully_qualified_name
133
+ @references << Reference.new(name, constant_path.location, declaration: true)
91
134
  end
92
135
 
93
136
  @stack << name
@@ -203,6 +246,10 @@ module RubyIndexer
203
246
 
204
247
  sig { params(node: Prism::DefNode).void }
205
248
  def on_def_node_enter(node)
249
+ if @target.is_a?(MethodTarget) && (name = node.name.to_s) == @target.method_name
250
+ @references << Reference.new(name, node.name_loc, declaration: true)
251
+ end
252
+
206
253
  if node.receiver.is_a?(Prism::SelfNode)
207
254
  @stack << "<Class:#{@stack.last}>"
208
255
  end
@@ -215,6 +262,13 @@ module RubyIndexer
215
262
  end
216
263
  end
217
264
 
265
+ sig { params(node: Prism::CallNode).void }
266
+ def on_call_node_enter(node)
267
+ if @target.is_a?(MethodTarget) && (name = node.name.to_s) == @target.method_name
268
+ @references << Reference.new(name, T.must(node.message_loc), declaration: false)
269
+ end
270
+ end
271
+
218
272
  private
219
273
 
220
274
  sig { params(name: String).returns(T::Array[String]) }
@@ -233,13 +287,21 @@ module RubyIndexer
233
287
 
234
288
  sig { params(name: String, location: Prism::Location).void }
235
289
  def collect_constant_references(name, location)
290
+ return unless @target.is_a?(ConstTarget)
291
+
236
292
  entries = @index.resolve(name, @stack)
237
293
  return unless entries
238
294
 
295
+ previous_reference = @references.last
296
+
239
297
  entries.each do |entry|
240
- next unless entry.name == @fully_qualified_name
298
+ next unless entry.name == @target.fully_qualified_name
299
+
300
+ # When processing a class/module declaration, we eagerly handle the constant reference. To avoid duplicates,
301
+ # when we find the constant node defining the namespace, then we have to check if it wasn't already added
302
+ next if previous_reference&.location == location
241
303
 
242
- @references << Reference.new(name, location)
304
+ @references << Reference.new(name, location, declaration: false)
243
305
  end
244
306
  end
245
307
 
@@ -630,5 +630,17 @@ module RubyIndexer
630
630
  its namespace nesting, and the surrounding CallNode (e.g. a method call).
631
631
  COMMENTS
632
632
  end
633
+
634
+ def test_lazy_comment_fetching_does_not_fail_if_file_gets_deleted
635
+ indexable = IndexablePath.new("#{Dir.pwd}/lib", "lib/ruby_lsp/does_not_exist.rb")
636
+
637
+ @index.index_single(indexable, <<~RUBY, collect_comments: false)
638
+ class Foo
639
+ end
640
+ RUBY
641
+
642
+ entry = @index["Foo"].first
643
+ assert_empty(entry.comments)
644
+ end
633
645
  end
634
646
  end
@@ -9,14 +9,14 @@ module RubyIndexer
9
9
  enhancement_class = Class.new do
10
10
  include Enhancement
11
11
 
12
- def on_call_node(index, owner, node, file_path)
12
+ def on_call_node(index, owner, node, file_path, code_units_cache)
13
13
  return unless owner
14
14
  return unless node.name == :extend
15
15
 
16
16
  arguments = node.arguments&.arguments
17
17
  return unless arguments
18
18
 
19
- location = node.location
19
+ location = Location.from_prism_location(node.location, code_units_cache)
20
20
 
21
21
  arguments.each do |node|
22
22
  next unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
@@ -39,7 +39,6 @@ module RubyIndexer
39
39
  location,
40
40
  location,
41
41
  nil,
42
- index.configuration.encoding,
43
42
  [Entry::Signature.new([Entry::RequiredParameter.new(name: :a)])],
44
43
  Entry::Visibility::PUBLIC,
45
44
  owner,
@@ -102,7 +101,7 @@ module RubyIndexer
102
101
  enhancement_class = Class.new do
103
102
  include Enhancement
104
103
 
105
- def on_call_node(index, owner, node, file_path)
104
+ def on_call_node(index, owner, node, file_path, code_units_cache)
106
105
  return unless owner
107
106
 
108
107
  name = node.name
@@ -114,7 +113,7 @@ module RubyIndexer
114
113
  association_name = arguments.first
115
114
  return unless association_name.is_a?(Prism::SymbolNode)
116
115
 
117
- location = association_name.location
116
+ location = Location.from_prism_location(association_name.location, code_units_cache)
118
117
 
119
118
  index.add(Entry::Method.new(
120
119
  T.must(association_name.value),
@@ -122,7 +121,6 @@ module RubyIndexer
122
121
  location,
123
122
  location,
124
123
  nil,
125
- index.configuration.encoding,
126
124
  [],
127
125
  Entry::Visibility::PUBLIC,
128
126
  owner,
@@ -166,7 +164,7 @@ module RubyIndexer
166
164
  enhancement_class = Class.new do
167
165
  include Enhancement
168
166
 
169
- def on_call_node(index, owner, node, file_path)
167
+ def on_call_node(index, owner, node, file_path, code_units_cache)
170
168
  raise "Error"
171
169
  end
172
170
 
@@ -0,0 +1,49 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "test_case"
5
+
6
+ module RubyIndexer
7
+ class GlobalVariableTest < TestCase
8
+ def test_global_variable_and_write
9
+ index(<<~RUBY)
10
+ $foo &&= 1
11
+ RUBY
12
+
13
+ assert_entry("$foo", Entry::GlobalVariable, "/fake/path/foo.rb:0-0:0-4")
14
+ end
15
+
16
+ def test_global_variable_operator_write
17
+ index(<<~RUBY)
18
+ $foo += 1
19
+ RUBY
20
+
21
+ assert_entry("$foo", Entry::GlobalVariable, "/fake/path/foo.rb:0-0:0-4")
22
+ end
23
+
24
+ def test_global_variable_or_write
25
+ index(<<~RUBY)
26
+ $foo ||= 1
27
+ RUBY
28
+
29
+ assert_entry("$foo", Entry::GlobalVariable, "/fake/path/foo.rb:0-0:0-4")
30
+ end
31
+
32
+ def test_global_variable_target_node
33
+ index(<<~RUBY)
34
+ $foo, $bar = 1
35
+ RUBY
36
+
37
+ assert_entry("$foo", Entry::GlobalVariable, "/fake/path/foo.rb:0-0:0-4")
38
+ assert_entry("$bar", Entry::GlobalVariable, "/fake/path/foo.rb:0-6:0-10")
39
+ end
40
+
41
+ def test_global_variable_write
42
+ index(<<~RUBY)
43
+ $foo = 1
44
+ RUBY
45
+
46
+ assert_entry("$foo", Entry::GlobalVariable, "/fake/path/foo.rb:0-0:0-4")
47
+ end
48
+ end
49
+ end
@@ -1858,6 +1858,9 @@ module RubyIndexer
1858
1858
 
1859
1859
  entries = @index.entries_for("/fake/path/foo.rb", RubyIndexer::Entry::Namespace)
1860
1860
  assert_equal(["Foo", "Bar", "Bar::<Class:Bar>"], entries.map(&:name))
1861
+
1862
+ entries = @index.entries_for("/fake/path/foo.rb")
1863
+ assert_equal(["Foo", "Bar", "my_def", "Bar::<Class:Bar>", "my_singleton_def"], entries.map(&:name))
1861
1864
  end
1862
1865
 
1863
1866
  def test_entries_for_returns_nil_if_no_matches
@@ -76,6 +76,20 @@ module RubyIndexer
76
76
  assert_operator(entry.location.end_column, :>, 0)
77
77
  end
78
78
 
79
+ def test_index_global_declaration
80
+ entries = @index["$DEBUG"]
81
+ refute_nil(entries)
82
+ assert_equal(1, entries.length)
83
+
84
+ entry = entries.first
85
+
86
+ assert_instance_of(Entry::GlobalVariable, entry)
87
+ assert_equal("$DEBUG", entry.name)
88
+ assert_match(%r{/gems/rbs-.*/core/global_variables.rbs}, entry.file_path)
89
+ assert_operator(entry.location.start_column, :<, entry.location.end_column)
90
+ assert_equal(entry.location.start_line, entry.location.end_line)
91
+ end
92
+
79
93
  def test_attaches_correct_owner_to_singleton_methods
80
94
  entries = @index["basename"]
81
95
  refute_nil(entries)
@@ -6,7 +6,7 @@ require "test_helper"
6
6
  module RubyIndexer
7
7
  class ReferenceFinderTest < Minitest::Test
8
8
  def test_finds_constant_references
9
- refs = find_references("Foo::Bar", <<~RUBY)
9
+ refs = find_const_references("Foo::Bar", <<~RUBY)
10
10
  module Foo
11
11
  class Bar
12
12
  end
@@ -28,7 +28,7 @@ module RubyIndexer
28
28
  end
29
29
 
30
30
  def test_finds_constant_references_inside_singleton_contexts
31
- refs = find_references("Foo::<Class:Foo>::Bar", <<~RUBY)
31
+ refs = find_const_references("Foo::<Class:Foo>::Bar", <<~RUBY)
32
32
  class Foo
33
33
  class << self
34
34
  class Bar
@@ -47,7 +47,7 @@ module RubyIndexer
47
47
  end
48
48
 
49
49
  def test_finds_top_level_constant_references
50
- refs = find_references("Bar", <<~RUBY)
50
+ refs = find_const_references("Bar", <<~RUBY)
51
51
  class Bar
52
52
  end
53
53
 
@@ -70,17 +70,173 @@ module RubyIndexer
70
70
  assert_equal(8, refs[2].location.start_line)
71
71
  end
72
72
 
73
+ def test_finds_method_references
74
+ refs = find_method_references("foo", <<~RUBY)
75
+ class Bar
76
+ def foo
77
+ end
78
+
79
+ def baz
80
+ foo
81
+ end
82
+ end
83
+ RUBY
84
+
85
+ assert_equal(2, refs.size)
86
+
87
+ assert_equal("foo", refs[0].name)
88
+ assert_equal(2, refs[0].location.start_line)
89
+
90
+ assert_equal("foo", refs[1].name)
91
+ assert_equal(6, refs[1].location.start_line)
92
+ end
93
+
94
+ def test_does_not_mismatch_on_readers_and_writers
95
+ refs = find_method_references("foo", <<~RUBY)
96
+ class Bar
97
+ def foo
98
+ end
99
+
100
+ def foo=(value)
101
+ end
102
+
103
+ def baz
104
+ self.foo = 1
105
+ self.foo
106
+ end
107
+ end
108
+ RUBY
109
+
110
+ # We want to match `foo` but not `foo=`
111
+ assert_equal(2, refs.size)
112
+
113
+ assert_equal("foo", refs[0].name)
114
+ assert_equal(2, refs[0].location.start_line)
115
+
116
+ assert_equal("foo", refs[1].name)
117
+ assert_equal(10, refs[1].location.start_line)
118
+ end
119
+
120
+ def test_matches_writers
121
+ refs = find_method_references("foo=", <<~RUBY)
122
+ class Bar
123
+ def foo
124
+ end
125
+
126
+ def foo=(value)
127
+ end
128
+
129
+ def baz
130
+ self.foo = 1
131
+ self.foo
132
+ end
133
+ end
134
+ RUBY
135
+
136
+ # We want to match `foo=` but not `foo`
137
+ assert_equal(2, refs.size)
138
+
139
+ assert_equal("foo=", refs[0].name)
140
+ assert_equal(5, refs[0].location.start_line)
141
+
142
+ assert_equal("foo=", refs[1].name)
143
+ assert_equal(9, refs[1].location.start_line)
144
+ end
145
+
146
+ def test_find_inherited_methods
147
+ refs = find_method_references("foo", <<~RUBY)
148
+ class Bar
149
+ def foo
150
+ end
151
+ end
152
+
153
+ class Baz < Bar
154
+ super.foo
155
+ end
156
+ RUBY
157
+
158
+ assert_equal(2, refs.size)
159
+
160
+ assert_equal("foo", refs[0].name)
161
+ assert_equal(2, refs[0].location.start_line)
162
+
163
+ assert_equal("foo", refs[1].name)
164
+ assert_equal(7, refs[1].location.start_line)
165
+ end
166
+
167
+ def test_finds_methods_created_in_mixins
168
+ refs = find_method_references("foo", <<~RUBY)
169
+ module Mixin
170
+ def foo
171
+ end
172
+ end
173
+
174
+ class Bar
175
+ include Mixin
176
+ end
177
+
178
+ Bar.foo
179
+ RUBY
180
+
181
+ assert_equal(2, refs.size)
182
+
183
+ assert_equal("foo", refs[0].name)
184
+ assert_equal(2, refs[0].location.start_line)
185
+
186
+ assert_equal("foo", refs[1].name)
187
+ assert_equal(10, refs[1].location.start_line)
188
+ end
189
+
190
+ def test_finds_singleton_methods
191
+ # The current implementation matches on both `Bar.foo` and `Bar#foo` even though they are different
192
+
193
+ refs = find_method_references("foo", <<~RUBY)
194
+ class Bar
195
+ class << self
196
+ def foo
197
+ end
198
+ end
199
+
200
+ def foo
201
+ end
202
+ end
203
+
204
+ Bar.foo
205
+ RUBY
206
+
207
+ assert_equal(3, refs.size)
208
+
209
+ assert_equal("foo", refs[0].name)
210
+ assert_equal(3, refs[0].location.start_line)
211
+
212
+ assert_equal("foo", refs[1].name)
213
+ assert_equal(7, refs[1].location.start_line)
214
+
215
+ assert_equal("foo", refs[2].name)
216
+ assert_equal(11, refs[2].location.start_line)
217
+ end
218
+
73
219
  private
74
220
 
75
- def find_references(fully_qualified_name, source)
221
+ def find_const_references(const_name, source)
222
+ target = ReferenceFinder::ConstTarget.new(const_name)
223
+ find_references(target, source)
224
+ end
225
+
226
+ def find_method_references(method_name, source)
227
+ target = ReferenceFinder::MethodTarget.new(method_name)
228
+ find_references(target, source)
229
+ end
230
+
231
+ def find_references(target, source)
76
232
  file_path = "/fake.rb"
77
233
  index = Index.new
78
234
  index.index_single(IndexablePath.new(nil, file_path), source)
79
235
  parse_result = Prism.parse(source)
80
236
  dispatcher = Prism::Dispatcher.new
81
- finder = ReferenceFinder.new(fully_qualified_name, index, dispatcher)
237
+ finder = ReferenceFinder.new(target, index, dispatcher)
82
238
  dispatcher.visit(parse_result.value)
83
- finder.references.uniq(&:location)
239
+ finder.references
84
240
  end
85
241
  end
86
242
  end
@@ -6,10 +6,18 @@ module RubyLsp
6
6
  extend T::Sig
7
7
  extend T::Generic
8
8
 
9
+ ParseResultType = type_member { { fixed: Prism::ParseResult } }
10
+
9
11
  sig { returns(String) }
10
12
  attr_reader :host_language_source
11
13
 
12
- ParseResultType = type_member { { fixed: Prism::ParseResult } }
14
+ sig do
15
+ returns(T.any(
16
+ T.proc.params(arg0: Integer).returns(Integer),
17
+ Prism::CodeUnitsCache,
18
+ ))
19
+ end
20
+ attr_reader :code_units_cache
13
21
 
14
22
  sig { params(source: String, version: Integer, uri: URI::Generic, encoding: Encoding).void }
15
23
  def initialize(source:, version:, uri:, encoding: Encoding::UTF_8)
@@ -17,6 +25,10 @@ module RubyLsp
17
25
  # overrides this with the proper virtual host language source
18
26
  @host_language_source = T.let("", String)
19
27
  super
28
+ @code_units_cache = T.let(@parse_result.code_units_cache(@encoding), T.any(
29
+ T.proc.params(arg0: Integer).returns(Integer),
30
+ Prism::CodeUnitsCache,
31
+ ))
20
32
  end
21
33
 
22
34
  sig { override.returns(T::Boolean) }
@@ -30,6 +42,7 @@ module RubyLsp
30
42
  # Use partial script to avoid syntax errors in ERB files where keywords may be used without the full context in
31
43
  # which they will be evaluated
32
44
  @parse_result = Prism.parse(scanner.ruby, partial_script: true)
45
+ @code_units_cache = @parse_result.code_units_cache(@encoding)
33
46
  true
34
47
  end
35
48
 
@@ -50,7 +63,12 @@ module RubyLsp
50
63
  ).returns(NodeContext)
51
64
  end
52
65
  def locate_node(position, node_types: [])
53
- RubyDocument.locate(@parse_result.value, create_scanner.find_char_position(position), node_types: node_types)
66
+ RubyDocument.locate(
67
+ @parse_result.value,
68
+ create_scanner.find_char_position(position),
69
+ code_units_cache: @code_units_cache,
70
+ node_types: node_types,
71
+ )
54
72
  end
55
73
 
56
74
  sig { params(char_position: Integer).returns(T.nilable(T::Boolean)) }
@@ -75,10 +75,12 @@ require "ruby_lsp/requests/hover"
75
75
  require "ruby_lsp/requests/inlay_hints"
76
76
  require "ruby_lsp/requests/on_type_formatting"
77
77
  require "ruby_lsp/requests/prepare_type_hierarchy"
78
+ require "ruby_lsp/requests/range_formatting"
79
+ require "ruby_lsp/requests/references"
80
+ require "ruby_lsp/requests/rename"
78
81
  require "ruby_lsp/requests/selection_ranges"
79
82
  require "ruby_lsp/requests/semantic_highlighting"
80
83
  require "ruby_lsp/requests/show_syntax_tree"
81
84
  require "ruby_lsp/requests/signature_help"
82
85
  require "ruby_lsp/requests/type_hierarchy_supertypes"
83
86
  require "ruby_lsp/requests/workspace_symbol"
84
- require "ruby_lsp/requests/rename"
@@ -39,6 +39,7 @@ module RubyLsp
39
39
  :on_block_argument_node_enter,
40
40
  :on_constant_read_node_enter,
41
41
  :on_constant_path_node_enter,
42
+ :on_global_variable_read_node_enter,
42
43
  :on_instance_variable_read_node_enter,
43
44
  :on_instance_variable_write_node_enter,
44
45
  :on_instance_variable_and_write_node_enter,
@@ -120,6 +121,25 @@ module RubyLsp
120
121
  find_in_index(name)
121
122
  end
122
123
 
124
+ sig { params(node: Prism::GlobalVariableReadNode).void }
125
+ def on_global_variable_read_node_enter(node)
126
+ entries = @index[node.name.to_s]
127
+
128
+ return unless entries
129
+
130
+ entries.each do |entry|
131
+ location = entry.location
132
+
133
+ @response_builder << Interface::Location.new(
134
+ uri: URI::Generic.from_path(path: entry.file_path).to_s,
135
+ range: Interface::Range.new(
136
+ start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
137
+ end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
138
+ ),
139
+ )
140
+ end
141
+ end
142
+
123
143
  sig { params(node: Prism::InstanceVariableReadNode).void }
124
144
  def on_instance_variable_read_node_enter(node)
125
145
  handle_instance_variable_definition(node.name.to_s)