ruby-lsp 0.16.6 → 0.16.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d725a0a2fbd58820e2a87efae85564498221feb8410292055b0caafd889284e6
4
- data.tar.gz: 5376ca8a9d08ca0883fca3aa19a1a178102eb60f49ab3fe5f07bb1a58ba63ddf
3
+ metadata.gz: ebc166637de670ea02144e74ccb2e99ae1a9824c9f99995380d81bed99ff6a29
4
+ data.tar.gz: 48b7ef9364f4d1a8044042c8476414e87da5b38ca4871683c16d06636fc8e411
5
5
  SHA512:
6
- metadata.gz: 81fbb2e5018ad55d8888c98e277e7ac6ed8b45314627b1a3e3dabde2cb8aac0284dc70487a694226e5a08817feb82b64daadfcb955e9642b288e575189a56015
7
- data.tar.gz: f32ef084e4412752cc32df55da0dd7b985d3826db110bf91ba3473773043c50345ceb597432483a0a75f1790cabfca796fa9581d3ba86edff96a8642fb2312f2
6
+ metadata.gz: 80c4f791e7dfef66623f5190eaba4dc76c745a27f74238d912c34711c408803ec952d5ec3e8041ebb6410b99c74d2f3d8274db5dffa8f187e48a7032a3960086
7
+ data.tar.gz: 941209321fb263b9a22cbe27102e8761c6ef127902e2b74f760fec1a8701d96e5bcef5f01c704e7215e061437cac2577c38e100464cccfe03909aa42ee1e318e
data/README.md CHANGED
@@ -33,7 +33,7 @@ The Ruby LSP features include
33
33
  - Completion for classes, modules, constants and require paths
34
34
  - Fuzzy search classes, modules and constants anywhere in the project and its dependencies (workspace symbol)
35
35
 
36
- Adding method support for definition, completion, hover and workspace symbol is planned, but not yet completed.
36
+ Adding method support for definition, completion, hover and workspace symbol is partially supported, but not yet complete. Follow progress in https://github.com/Shopify/ruby-lsp/issues/899
37
37
 
38
38
  See complete information about features [here](https://shopify.github.io/ruby-lsp/RubyLsp/Requests.html).
39
39
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.16.6
1
+ 0.16.7
data/exe/ruby-lsp CHANGED
@@ -18,6 +18,10 @@ parser = OptionParser.new do |opts|
18
18
  options[:debug] = true
19
19
  end
20
20
 
21
+ opts.on("--time-index", "Measure the time it takes to index the project") do
22
+ options[:time_index] = true
23
+ end
24
+
21
25
  opts.on(
22
26
  "--branch [BRANCH]",
23
27
  "Launch the Ruby LSP using the specified branch rather than the release version",
@@ -80,14 +84,27 @@ if options[:debug]
80
84
  end
81
85
 
82
86
  begin
83
- original_stdout = $stdout
84
- $stdout = $stderr
87
+ ENV.delete("RUBY_DEBUG_IRB_CONSOLE")
85
88
  require "debug/open_nonstop"
86
89
  rescue LoadError
87
90
  $stderr.puts("You need to install the debug gem to use the --debug flag")
88
- ensure
89
- $stdout = original_stdout
90
91
  end
91
92
  end
92
93
 
94
+ if options[:time_index]
95
+ require "benchmark"
96
+
97
+ index = RubyIndexer::Index.new
98
+
99
+ result = Benchmark.realtime { index.index_all }
100
+ entries = index.instance_variable_get(:@entries)
101
+ entries_by_entry_type = entries.values.flatten.group_by(&:class)
102
+
103
+ puts <<~MSG
104
+ Ruby LSP v#{RubyLsp::VERSION}: Indexing took #{result.round(5)} seconds and generated:
105
+ - #{entries_by_entry_type.map { |k, v| "#{k.name.split("::").last}: #{v.size}" }.join("\n- ")}
106
+ MSG
107
+ return
108
+ end
109
+
93
110
  RubyLsp::Server.new.start
data/exe/ruby-lsp-check CHANGED
@@ -47,9 +47,7 @@ index = RubyIndexer::Index.new
47
47
  indexables = RubyIndexer.configuration.indexables
48
48
 
49
49
  indexables.each_with_index do |indexable, i|
50
- result = Prism.parse(File.read(indexable.full_path))
51
- collector = RubyIndexer::Collector.new(index, result, indexable.full_path)
52
- collector.collect(result.value)
50
+ index.index_single(indexable)
53
51
  rescue => e
54
52
  errors[indexable.full_path] = e
55
53
  ensure
data/exe/ruby-lsp-doctor CHANGED
@@ -19,8 +19,5 @@ puts "Globbing for indexable files"
19
19
 
20
20
  RubyIndexer.configuration.indexables.each do |indexable|
21
21
  puts "indexing: #{indexable.full_path}"
22
- content = File.read(indexable.full_path)
23
- result = Prism.parse(content)
24
- collector = RubyIndexer::Collector.new(index, result, indexable.full_path)
25
- collector.collect(result.value)
22
+ index.index_single(indexable)
26
23
  end
data/lib/core_ext/uri.rb CHANGED
@@ -11,6 +11,9 @@ module URI
11
11
  # On Windows, if the path begins with the disk name, we need to add a leading slash to make it a valid URI
12
12
  escaped_path = if /^[A-Z]:/i.match?(path)
13
13
  DEFAULT_PARSER.escape("/#{path}")
14
+ elsif path.start_with?("//?/")
15
+ # Some paths on Windows start with "//?/". This is a special prefix that allows for long file paths
16
+ DEFAULT_PARSER.escape(path.delete_prefix("//?"))
14
17
  else
15
18
  DEFAULT_PARSER.escape(path)
16
19
  end
@@ -2,79 +2,103 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyIndexer
5
- class Collector
5
+ class DeclarationListener
6
6
  extend T::Sig
7
7
 
8
- LEAVE_EVENT = T.let(Object.new.freeze, Object)
9
-
10
- sig { params(index: Index, parse_result: Prism::ParseResult, file_path: String).void }
11
- def initialize(index, parse_result, file_path)
8
+ sig do
9
+ params(index: Index, dispatcher: Prism::Dispatcher, parse_result: Prism::ParseResult, file_path: String).void
10
+ end
11
+ def initialize(index, dispatcher, parse_result, file_path)
12
12
  @index = index
13
13
  @file_path = file_path
14
- @stack = T.let([], T::Array[String])
15
14
  @comments_by_line = T.let(
16
15
  parse_result.comments.to_h do |c|
17
16
  [c.location.start_line, c]
18
17
  end,
19
18
  T::Hash[Integer, Prism::Comment],
20
19
  )
21
- @queue = T.let([], T::Array[Object])
22
- @current_owner = T.let(nil, T.nilable(Entry::Namespace))
20
+ @inside_def = T.let(false, T::Boolean)
21
+
22
+ # The nesting stack we're currently inside. Used to determine the fully qualified name of constants, but only
23
+ # stored by unresolved aliases which need the original nesting to be lazily resolved
24
+ @stack = T.let([], T::Array[String])
23
25
 
24
- super()
26
+ # A stack of namespace entries that represent where we currently are. Used to properly assign methods to an owner
27
+ @owner_stack = T.let([], T::Array[Entry::Namespace])
28
+
29
+ dispatcher.register(
30
+ self,
31
+ :on_class_node_enter,
32
+ :on_class_node_leave,
33
+ :on_module_node_enter,
34
+ :on_module_node_leave,
35
+ :on_def_node_enter,
36
+ :on_def_node_leave,
37
+ :on_call_node_enter,
38
+ :on_multi_write_node_enter,
39
+ :on_constant_path_write_node_enter,
40
+ :on_constant_path_or_write_node_enter,
41
+ :on_constant_path_operator_write_node_enter,
42
+ :on_constant_path_and_write_node_enter,
43
+ :on_constant_or_write_node_enter,
44
+ :on_constant_write_node_enter,
45
+ :on_constant_or_write_node_enter,
46
+ :on_constant_and_write_node_enter,
47
+ :on_constant_operator_write_node_enter,
48
+ )
25
49
  end
26
50
 
27
- sig { params(node: Prism::Node).void }
28
- def collect(node)
29
- @queue = [node]
30
-
31
- until @queue.empty?
32
- node_or_event = @queue.shift
33
-
34
- case node_or_event
35
- when Prism::ProgramNode
36
- @queue << node_or_event.statements
37
- when Prism::StatementsNode
38
- T.unsafe(@queue).prepend(*node_or_event.body)
39
- when Prism::ClassNode
40
- add_class_entry(node_or_event)
41
- when Prism::ModuleNode
42
- add_module_entry(node_or_event)
43
- when Prism::MultiWriteNode
44
- handle_multi_write_node(node_or_event)
45
- when Prism::ConstantPathWriteNode
46
- handle_constant_path_write_node(node_or_event)
47
- when Prism::ConstantPathOrWriteNode
48
- handle_constant_path_or_write_node(node_or_event)
49
- when Prism::ConstantPathOperatorWriteNode
50
- handle_constant_path_operator_write_node(node_or_event)
51
- when Prism::ConstantPathAndWriteNode
52
- handle_constant_path_and_write_node(node_or_event)
53
- when Prism::ConstantWriteNode
54
- handle_constant_write_node(node_or_event)
55
- when Prism::ConstantOrWriteNode
56
- name = fully_qualify_name(node_or_event.name.to_s)
57
- add_constant(node_or_event, name)
58
- when Prism::ConstantAndWriteNode
59
- name = fully_qualify_name(node_or_event.name.to_s)
60
- add_constant(node_or_event, name)
61
- when Prism::ConstantOperatorWriteNode
62
- name = fully_qualify_name(node_or_event.name.to_s)
63
- add_constant(node_or_event, name)
64
- when Prism::CallNode
65
- handle_call_node(node_or_event)
66
- when Prism::DefNode
67
- handle_def_node(node_or_event)
68
- when LEAVE_EVENT
69
- @stack.pop
70
- end
51
+ sig { params(node: Prism::ClassNode).void }
52
+ def on_class_node_enter(node)
53
+ name = node.constant_path.location.slice
54
+
55
+ comments = collect_comments(node)
56
+
57
+ superclass = node.superclass
58
+ parent_class = case superclass
59
+ when Prism::ConstantReadNode, Prism::ConstantPathNode
60
+ superclass.slice
71
61
  end
62
+
63
+ entry = Entry::Class.new(
64
+ fully_qualify_name(name),
65
+ @file_path,
66
+ node.location,
67
+ comments,
68
+ parent_class,
69
+ )
70
+
71
+ @owner_stack << entry
72
+ @index << entry
73
+ @stack << name
72
74
  end
73
75
 
74
- private
76
+ sig { params(node: Prism::ClassNode).void }
77
+ def on_class_node_leave(node)
78
+ @stack.pop
79
+ @owner_stack.pop
80
+ end
81
+
82
+ sig { params(node: Prism::ModuleNode).void }
83
+ def on_module_node_enter(node)
84
+ name = node.constant_path.location.slice
85
+
86
+ comments = collect_comments(node)
87
+ entry = Entry::Module.new(fully_qualify_name(name), @file_path, node.location, comments)
88
+
89
+ @owner_stack << entry
90
+ @index << entry
91
+ @stack << name
92
+ end
93
+
94
+ sig { params(node: Prism::ModuleNode).void }
95
+ def on_module_node_leave(node)
96
+ @stack.pop
97
+ @owner_stack.pop
98
+ end
75
99
 
76
100
  sig { params(node: Prism::MultiWriteNode).void }
77
- def handle_multi_write_node(node)
101
+ def on_multi_write_node_enter(node)
78
102
  value = node.value
79
103
  values = value.is_a?(Prism::ArrayNode) && value.opening_loc ? value.elements : []
80
104
 
@@ -94,7 +118,7 @@ module RubyIndexer
94
118
  end
95
119
 
96
120
  sig { params(node: Prism::ConstantPathWriteNode).void }
97
- def handle_constant_path_write_node(node)
121
+ def on_constant_path_write_node_enter(node)
98
122
  # ignore variable constants like `var::FOO` or `self.class::FOO`
99
123
  target = node.target
100
124
  return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
@@ -104,7 +128,7 @@ module RubyIndexer
104
128
  end
105
129
 
106
130
  sig { params(node: Prism::ConstantPathOrWriteNode).void }
107
- def handle_constant_path_or_write_node(node)
131
+ def on_constant_path_or_write_node_enter(node)
108
132
  # ignore variable constants like `var::FOO` or `self.class::FOO`
109
133
  target = node.target
110
134
  return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
@@ -114,7 +138,7 @@ module RubyIndexer
114
138
  end
115
139
 
116
140
  sig { params(node: Prism::ConstantPathOperatorWriteNode).void }
117
- def handle_constant_path_operator_write_node(node)
141
+ def on_constant_path_operator_write_node_enter(node)
118
142
  # ignore variable constants like `var::FOO` or `self.class::FOO`
119
143
  target = node.target
120
144
  return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
@@ -124,7 +148,7 @@ module RubyIndexer
124
148
  end
125
149
 
126
150
  sig { params(node: Prism::ConstantPathAndWriteNode).void }
127
- def handle_constant_path_and_write_node(node)
151
+ def on_constant_path_and_write_node_enter(node)
128
152
  # ignore variable constants like `var::FOO` or `self.class::FOO`
129
153
  target = node.target
130
154
  return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
@@ -134,13 +158,31 @@ module RubyIndexer
134
158
  end
135
159
 
136
160
  sig { params(node: Prism::ConstantWriteNode).void }
137
- def handle_constant_write_node(node)
161
+ def on_constant_write_node_enter(node)
162
+ name = fully_qualify_name(node.name.to_s)
163
+ add_constant(node, name)
164
+ end
165
+
166
+ sig { params(node: Prism::ConstantOrWriteNode).void }
167
+ def on_constant_or_write_node_enter(node)
168
+ name = fully_qualify_name(node.name.to_s)
169
+ add_constant(node, name)
170
+ end
171
+
172
+ sig { params(node: Prism::ConstantAndWriteNode).void }
173
+ def on_constant_and_write_node_enter(node)
174
+ name = fully_qualify_name(node.name.to_s)
175
+ add_constant(node, name)
176
+ end
177
+
178
+ sig { params(node: Prism::ConstantOperatorWriteNode).void }
179
+ def on_constant_operator_write_node_enter(node)
138
180
  name = fully_qualify_name(node.name.to_s)
139
181
  add_constant(node, name)
140
182
  end
141
183
 
142
184
  sig { params(node: Prism::CallNode).void }
143
- def handle_call_node(node)
185
+ def on_call_node_enter(node)
144
186
  message = node.name
145
187
 
146
188
  case message
@@ -153,14 +195,15 @@ module RubyIndexer
153
195
  when :attr_accessor
154
196
  handle_attribute(node, reader: true, writer: true)
155
197
  when :include
156
- handle_include(node)
198
+ handle_module_operation(node, :included_modules)
157
199
  when :prepend
158
- handle_prepend(node)
200
+ handle_module_operation(node, :prepended_modules)
159
201
  end
160
202
  end
161
203
 
162
204
  sig { params(node: Prism::DefNode).void }
163
- def handle_def_node(node)
205
+ def on_def_node_enter(node)
206
+ @inside_def = true
164
207
  method_name = node.name.to_s
165
208
  comments = collect_comments(node)
166
209
  case node.receiver
@@ -171,7 +214,7 @@ module RubyIndexer
171
214
  node.location,
172
215
  comments,
173
216
  node.parameters,
174
- @current_owner,
217
+ @owner_stack.last,
175
218
  )
176
219
  when Prism::SelfNode
177
220
  @index << Entry::SingletonMethod.new(
@@ -180,11 +223,18 @@ module RubyIndexer
180
223
  node.location,
181
224
  comments,
182
225
  node.parameters,
183
- @current_owner,
226
+ @owner_stack.last,
184
227
  )
185
228
  end
186
229
  end
187
230
 
231
+ sig { params(node: Prism::DefNode).void }
232
+ def on_def_node_leave(node)
233
+ @inside_def = false
234
+ end
235
+
236
+ private
237
+
188
238
  sig { params(node: Prism::CallNode).void }
189
239
  def handle_private_constant(node)
190
240
  arguments = node.arguments&.arguments
@@ -240,62 +290,16 @@ module RubyIndexer
240
290
 
241
291
  # If the right hand side is another constant assignment, we need to visit it because that constant has to be
242
292
  # indexed too
243
- @queue.prepend(value)
244
293
  Entry::UnresolvedAlias.new(value.name.to_s, @stack.dup, name, @file_path, node.location, comments)
245
294
  when Prism::ConstantPathWriteNode, Prism::ConstantPathOrWriteNode, Prism::ConstantPathOperatorWriteNode,
246
295
  Prism::ConstantPathAndWriteNode
247
296
 
248
- @queue.prepend(value)
249
297
  Entry::UnresolvedAlias.new(value.target.slice, @stack.dup, name, @file_path, node.location, comments)
250
298
  else
251
299
  Entry::Constant.new(name, @file_path, node.location, comments)
252
300
  end
253
301
  end
254
302
 
255
- sig { params(node: Prism::ModuleNode).void }
256
- def add_module_entry(node)
257
- name = node.constant_path.location.slice
258
- unless /^[A-Z:]/.match?(name)
259
- @queue << node.body
260
- return
261
- end
262
-
263
- comments = collect_comments(node)
264
- @current_owner = Entry::Module.new(fully_qualify_name(name), @file_path, node.location, comments)
265
- @index << @current_owner
266
- @stack << name
267
- @queue.prepend(node.body, LEAVE_EVENT)
268
- end
269
-
270
- sig { params(node: Prism::ClassNode).void }
271
- def add_class_entry(node)
272
- name = node.constant_path.location.slice
273
-
274
- unless /^[A-Z:]/.match?(name)
275
- @queue << node.body
276
- return
277
- end
278
-
279
- comments = collect_comments(node)
280
-
281
- superclass = node.superclass
282
- parent_class = case superclass
283
- when Prism::ConstantReadNode, Prism::ConstantPathNode
284
- superclass.slice
285
- end
286
-
287
- @current_owner = Entry::Class.new(
288
- fully_qualify_name(name),
289
- @file_path,
290
- node.location,
291
- comments,
292
- parent_class,
293
- )
294
- @index << @current_owner
295
- @stack << name
296
- @queue.prepend(node.body, LEAVE_EVENT)
297
- end
298
-
299
303
  sig { params(node: Prism::Node).returns(T::Array[String]) }
300
304
  def collect_comments(node)
301
305
  comments = []
@@ -350,24 +354,17 @@ module RubyIndexer
350
354
 
351
355
  next unless name && loc
352
356
 
353
- @index << Entry::Accessor.new(name, @file_path, loc, comments, @current_owner) if reader
354
- @index << Entry::Accessor.new("#{name}=", @file_path, loc, comments, @current_owner) if writer
357
+ @index << Entry::Accessor.new(name, @file_path, loc, comments, @owner_stack.last) if reader
358
+ @index << Entry::Accessor.new("#{name}=", @file_path, loc, comments, @owner_stack.last) if writer
355
359
  end
356
360
  end
357
361
 
358
- sig { params(node: Prism::CallNode).void }
359
- def handle_include(node)
360
- handle_module_operation(node, :included_modules)
361
- end
362
-
363
- sig { params(node: Prism::CallNode).void }
364
- def handle_prepend(node)
365
- handle_module_operation(node, :prepended_modules)
366
- end
367
-
368
362
  sig { params(node: Prism::CallNode, operation: Symbol).void }
369
363
  def handle_module_operation(node, operation)
370
- return unless @current_owner
364
+ return if @inside_def
365
+
366
+ owner = @owner_stack.last
367
+ return unless owner
371
368
 
372
369
  arguments = node.arguments&.arguments
373
370
  return unless arguments
@@ -381,7 +378,7 @@ module RubyIndexer
381
378
  # If a constant path reference is dynamic or missing parts, we can't
382
379
  # index it
383
380
  end
384
- collection = operation == :included_modules ? @current_owner.included_modules : @current_owner.prepended_modules
381
+ collection = operation == :included_modules ? owner.included_modules : owner.prepended_modules
385
382
  collection.concat(names)
386
383
  end
387
384
  end
@@ -185,9 +185,11 @@ module RubyIndexer
185
185
  sig { params(indexable_path: IndexablePath, source: T.nilable(String)).void }
186
186
  def index_single(indexable_path, source = nil)
187
187
  content = source || File.read(indexable_path.full_path)
188
+ dispatcher = Prism::Dispatcher.new
189
+
188
190
  result = Prism.parse(content)
189
- collector = Collector.new(self, result, indexable_path.full_path)
190
- collector.collect(result.value)
191
+ DeclarationListener.new(self, dispatcher, result, indexable_path.full_path)
192
+ dispatcher.dispatch(result.value)
191
193
 
192
194
  require_path = indexable_path.require_path
193
195
  @require_paths_tree.insert(require_path, indexable_path) if require_path
@@ -5,7 +5,7 @@ require "yaml"
5
5
  require "did_you_mean"
6
6
 
7
7
  require "ruby_indexer/lib/ruby_indexer/indexable_path"
8
- require "ruby_indexer/lib/ruby_indexer/collector"
8
+ require "ruby_indexer/lib/ruby_indexer/declaration_listener"
9
9
  require "ruby_indexer/lib/ruby_indexer/index"
10
10
  require "ruby_indexer/lib/ruby_indexer/entry"
11
11
  require "ruby_indexer/lib/ruby_indexer/configuration"
@@ -14,6 +14,15 @@ module RubyIndexer
14
14
  assert_entry("Foo", Entry::Class, "/fake/path/foo.rb:0-0:1-3")
15
15
  end
16
16
 
17
+ def test_conditional_class
18
+ index(<<~RUBY)
19
+ class Foo
20
+ end if condition
21
+ RUBY
22
+
23
+ assert_entry("Foo", Entry::Class, "/fake/path/foo.rb:0-0:1-3")
24
+ end
25
+
17
26
  def test_class_with_statements
18
27
  index(<<~RUBY)
19
28
  class Foo
@@ -60,7 +69,23 @@ module RubyIndexer
60
69
  end
61
70
  RUBY
62
71
 
72
+ assert_entry("self::Bar", Entry::Class, "/fake/path/foo.rb:0-0:1-3")
73
+ end
74
+
75
+ def test_dynamically_namespaced_class_doesnt_affect_other_classes
76
+ index(<<~RUBY)
77
+ class Foo
78
+ class self::Bar
79
+ end
80
+
81
+ class Bar
82
+ end
83
+ end
84
+ RUBY
85
+
63
86
  refute_entry("self::Bar")
87
+ assert_entry("Foo", Entry::Class, "/fake/path/foo.rb:0-0:6-3")
88
+ assert_entry("Foo::Bar", Entry::Class, "/fake/path/foo.rb:4-2:5-5")
64
89
  end
65
90
 
66
91
  def test_empty_statements_module
@@ -72,6 +97,15 @@ module RubyIndexer
72
97
  assert_entry("Foo", Entry::Module, "/fake/path/foo.rb:0-0:1-3")
73
98
  end
74
99
 
100
+ def test_conditional_module
101
+ index(<<~RUBY)
102
+ module Foo
103
+ end if condition
104
+ RUBY
105
+
106
+ assert_entry("Foo", Entry::Module, "/fake/path/foo.rb:0-0:1-3")
107
+ end
108
+
75
109
  def test_module_with_statements
76
110
  index(<<~RUBY)
77
111
  module Foo
@@ -106,7 +140,23 @@ module RubyIndexer
106
140
  end
107
141
  RUBY
108
142
 
109
- refute_entry("self::Bar")
143
+ assert_entry("self::Bar", Entry::Module, "/fake/path/foo.rb:0-0:1-3")
144
+ end
145
+
146
+ def test_dynamically_namespaced_module_doesnt_affect_other_modules
147
+ index(<<~RUBY)
148
+ module Foo
149
+ class self::Bar
150
+ end
151
+
152
+ module Bar
153
+ end
154
+ end
155
+ RUBY
156
+
157
+ assert_entry("Foo::self::Bar", Entry::Class, "/fake/path/foo.rb:1-2:2-5")
158
+ assert_entry("Foo", Entry::Module, "/fake/path/foo.rb:0-0:6-3")
159
+ assert_entry("Foo::Bar", Entry::Module, "/fake/path/foo.rb:4-2:5-5")
110
160
  end
111
161
 
112
162
  def test_nested_modules_and_classes
@@ -12,10 +12,13 @@ module RubyIndexer
12
12
  class ::Bar
13
13
  FOO = 2
14
14
  end
15
+
16
+ BAR = 3 if condition
15
17
  RUBY
16
18
 
17
19
  assert_entry("FOO", Entry::Constant, "/fake/path/foo.rb:0-0:0-7")
18
20
  assert_entry("Bar::FOO", Entry::Constant, "/fake/path/foo.rb:3-2:3-9")
21
+ assert_entry("BAR", Entry::Constant, "/fake/path/foo.rb:6-0:6-7")
19
22
  end
20
23
 
21
24
  def test_constant_or_writes
@@ -16,6 +16,17 @@ module RubyIndexer
16
16
  assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
17
17
  end
18
18
 
19
+ def test_conditional_method
20
+ index(<<~RUBY)
21
+ class Foo
22
+ def bar
23
+ end if condition
24
+ end
25
+ RUBY
26
+
27
+ assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
28
+ end
29
+
19
30
  def test_singleton_method_using_self_receiver
20
31
  index(<<~RUBY)
21
32
  class Foo
@@ -38,6 +49,28 @@ module RubyIndexer
38
49
  assert_no_entry("bar")
39
50
  end
40
51
 
52
+ def test_method_under_dynamic_class_or_module
53
+ index(<<~RUBY)
54
+ module Foo
55
+ class self::Bar
56
+ def bar
57
+ end
58
+ end
59
+ end
60
+
61
+ module Bar
62
+ def bar
63
+ end
64
+ end
65
+ RUBY
66
+
67
+ assert_equal(2, @index["bar"].length)
68
+ first_entry = T.must(@index["bar"].first)
69
+ assert_equal("Foo::self::Bar", first_entry.owner.name)
70
+ second_entry = T.must(@index["bar"].last)
71
+ assert_equal("Bar", second_entry.owner.name)
72
+ end
73
+
41
74
  def test_method_with_parameters
42
75
  index(<<~RUBY)
43
76
  class Foo
@@ -285,5 +318,28 @@ module RubyIndexer
285
318
 
286
319
  assert_no_entry("bar")
287
320
  end
321
+
322
+ def test_properly_tracks_multiple_levels_of_nesting
323
+ index(<<~RUBY)
324
+ module Foo
325
+ def first; end
326
+
327
+ module Bar
328
+ def second; end
329
+ end
330
+
331
+ def third; end
332
+ end
333
+ RUBY
334
+
335
+ entry = T.must(@index["first"]&.first)
336
+ assert_equal("Foo", T.must(entry.owner).name)
337
+
338
+ entry = T.must(@index["second"]&.first)
339
+ assert_equal("Foo::Bar", T.must(entry.owner).name)
340
+
341
+ entry = T.must(@index["third"]&.first)
342
+ assert_equal("Foo", T.must(entry.owner).name)
343
+ end
288
344
  end
289
345
  end
@@ -42,6 +42,8 @@ module RubyLsp
42
42
  @group_stack = T.let([], T::Array[String])
43
43
  @group_id = T.let(1, Integer)
44
44
  @group_id_stack = T.let([], T::Array[Integer])
45
+ # We want to avoid adding code lenses for nested definitions
46
+ @def_depth = T.let(0, Integer)
45
47
 
46
48
  dispatcher.register(
47
49
  self,
@@ -50,6 +52,7 @@ module RubyLsp
50
52
  :on_module_node_enter,
51
53
  :on_module_node_leave,
52
54
  :on_def_node_enter,
55
+ :on_def_node_leave,
53
56
  :on_call_node_enter,
54
57
  :on_call_node_leave,
55
58
  )
@@ -88,6 +91,9 @@ module RubyLsp
88
91
 
89
92
  sig { params(node: Prism::DefNode).void }
90
93
  def on_def_node_enter(node)
94
+ @def_depth += 1
95
+ return if @def_depth > 1
96
+
91
97
  class_name = @group_stack.last
92
98
  return unless class_name&.end_with?("Test")
93
99
 
@@ -105,6 +111,11 @@ module RubyLsp
105
111
  end
106
112
  end
107
113
 
114
+ sig { params(node: Prism::DefNode).void }
115
+ def on_def_node_leave(node)
116
+ @def_depth -= 1
117
+ end
118
+
108
119
  sig { params(node: Prism::ModuleNode).void }
109
120
  def on_module_node_enter(node)
110
121
  if (path = namespace_constant_name(node))
@@ -47,7 +47,7 @@ module RubyLsp
47
47
  @response_builder << build_entry_completion(
48
48
  complete_name,
49
49
  name,
50
- node,
50
+ range_from_location(node.location),
51
51
  entries,
52
52
  top_level?(complete_name),
53
53
  )
@@ -62,6 +62,53 @@ module RubyLsp
62
62
  name = constant_name(node)
63
63
  return if name.nil?
64
64
 
65
+ constant_path_completion(name, range_from_location(node.location))
66
+ end
67
+
68
+ sig { params(node: Prism::CallNode).void }
69
+ def on_call_node_enter(node)
70
+ receiver = node.receiver
71
+
72
+ # When writing `Foo::`, the AST assigns a method call node (because you can use that syntax to invoke singleton
73
+ # methods). However, in addition to providing method completion, we also need to show possible constant
74
+ # completions
75
+ if (receiver.is_a?(Prism::ConstantReadNode) || receiver.is_a?(Prism::ConstantPathNode)) &&
76
+ node.call_operator == "::"
77
+
78
+ name = constant_name(receiver)
79
+
80
+ if name
81
+ start_loc = node.location
82
+ end_loc = T.must(node.call_operator_loc)
83
+
84
+ constant_path_completion(
85
+ "#{name}::",
86
+ Interface::Range.new(
87
+ start: Interface::Position.new(line: start_loc.start_line - 1, character: start_loc.start_column),
88
+ end: Interface::Position.new(line: end_loc.end_line - 1, character: end_loc.end_column),
89
+ ),
90
+ )
91
+ return
92
+ end
93
+ end
94
+
95
+ name = node.message
96
+ return unless name
97
+
98
+ case name
99
+ when "require"
100
+ complete_require(node)
101
+ when "require_relative"
102
+ complete_require_relative(node)
103
+ else
104
+ complete_self_receiver_method(node, name) if !@typechecker_enabled && self_receiver?(node)
105
+ end
106
+ end
107
+
108
+ private
109
+
110
+ sig { params(name: String, range: Interface::Range).void }
111
+ def constant_path_completion(name, range)
65
112
  top_level_reference = if name.start_with?("::")
66
113
  name = name.delete_prefix("::")
67
114
  true
@@ -71,8 +118,13 @@ module RubyLsp
71
118
 
72
119
  # If we're trying to provide completion for an aliased namespace, we need to first discover it's real name in
73
120
  # order to find which possible constants match the desired search
74
- *namespace, incomplete_name = name.split("::")
75
- aliased_namespace = T.must(namespace).join("::")
121
+ aliased_namespace = if name.end_with?("::")
122
+ name.delete_suffix("::")
123
+ else
124
+ *namespace, incomplete_name = name.split("::")
125
+ T.must(namespace).join("::")
126
+ end
127
+
76
128
  namespace_entries = @index.resolve(aliased_namespace, @nesting)
77
129
  return unless namespace_entries
78
130
 
@@ -88,37 +140,19 @@ module RubyLsp
88
140
  first_entry = T.must(entries.first)
89
141
  next if first_entry.visibility == :private && !first_entry.name.start_with?("#{@nesting}::")
90
142
 
91
- constant_name = T.must(first_entry.name.split("::").last)
92
-
143
+ constant_name = first_entry.name.delete_prefix("#{real_namespace}::")
93
144
  full_name = aliased_namespace.empty? ? constant_name : "#{aliased_namespace}::#{constant_name}"
94
145
 
95
146
  @response_builder << build_entry_completion(
96
147
  full_name,
97
148
  name,
98
- node,
149
+ range,
99
150
  entries,
100
151
  top_level_reference || top_level?(T.must(entries.first).name),
101
152
  )
102
153
  end
103
154
  end
104
155
 
105
- sig { params(node: Prism::CallNode).void }
106
- def on_call_node_enter(node)
107
- name = node.message
108
- return unless name
109
-
110
- case name
111
- when "require"
112
- complete_require(node)
113
- when "require_relative"
114
- complete_require_relative(node)
115
- else
116
- complete_self_receiver_method(node, name) if !@typechecker_enabled && self_receiver?(node)
117
- end
118
- end
119
-
120
- private
121
-
122
156
  sig { params(node: Prism::CallNode).void }
123
157
  def complete_require(node)
124
158
  arguments_node = node.arguments
@@ -223,12 +257,12 @@ module RubyLsp
223
257
  params(
224
258
  real_name: String,
225
259
  incomplete_name: String,
226
- node: Prism::Node,
260
+ range: Interface::Range,
227
261
  entries: T::Array[RubyIndexer::Entry],
228
262
  top_level: T::Boolean,
229
263
  ).returns(Interface::CompletionItem)
230
264
  end
231
- def build_entry_completion(real_name, incomplete_name, node, entries, top_level)
265
+ def build_entry_completion(real_name, incomplete_name, range, entries, top_level)
232
266
  first_entry = T.must(entries.first)
233
267
  kind = case first_entry
234
268
  when RubyIndexer::Entry::Class
@@ -263,20 +297,22 @@ module RubyLsp
263
297
  #
264
298
  # Foo::B # --> completion inserts `Bar` instead of `Foo::Bar`
265
299
  # end
266
- @nesting.each do |namespace|
267
- prefix = "#{namespace}::"
268
- shortened_name = insertion_text.delete_prefix(prefix)
269
-
270
- # If a different entry exists for the shortened name, then there's a conflict and we should not shorten it
271
- conflict_name = "#{@nesting.join("::")}::#{shortened_name}"
272
- break if real_name != conflict_name && @index[conflict_name]
273
-
274
- insertion_text = shortened_name
275
-
276
- # If the user is typing a fully qualified name `Foo::Bar::Baz`, then we should not use the short name (e.g.:
277
- # `Baz`) as filtering. So we only shorten the filter text if the user is not including the namespaces in their
278
- # typing
279
- filter_text.delete_prefix!(prefix) unless incomplete_name.start_with?(prefix)
300
+ unless @nesting.join("::").start_with?(incomplete_name)
301
+ @nesting.each do |namespace|
302
+ prefix = "#{namespace}::"
303
+ shortened_name = insertion_text.delete_prefix(prefix)
304
+
305
+ # If a different entry exists for the shortened name, then there's a conflict and we should not shorten it
306
+ conflict_name = "#{@nesting.join("::")}::#{shortened_name}"
307
+ break if real_name != conflict_name && @index[conflict_name]
308
+
309
+ insertion_text = shortened_name
310
+
311
+ # If the user is typing a fully qualified name `Foo::Bar::Baz`, then we should not use the short name (e.g.:
312
+ # `Baz`) as filtering. So we only shorten the filter text if the user is not including the namespaces in
313
+ # their typing
314
+ filter_text.delete_prefix!(prefix) unless incomplete_name.start_with?(prefix)
315
+ end
280
316
  end
281
317
 
282
318
  # When using a top level constant reference (e.g.: `::Bar`), the editor includes the `::` as part of the filter.
@@ -286,7 +322,7 @@ module RubyLsp
286
322
  label: real_name,
287
323
  filter_text: filter_text,
288
324
  text_edit: Interface::TextEdit.new(
289
- range: range_from_node(node),
325
+ range: range,
290
326
  new_text: insertion_text,
291
327
  ),
292
328
  kind: kind,
@@ -30,6 +30,7 @@ module RubyLsp
30
30
  dispatcher.register(
31
31
  self,
32
32
  :on_call_node_enter,
33
+ :on_block_argument_node_enter,
33
34
  :on_constant_read_node_enter,
34
35
  :on_constant_path_node_enter,
35
36
  )
@@ -42,10 +43,21 @@ module RubyLsp
42
43
  if message == :require || message == :require_relative
43
44
  handle_require_definition(node)
44
45
  else
45
- handle_method_definition(node)
46
+ handle_method_definition(message.to_s, self_receiver?(node))
46
47
  end
47
48
  end
48
49
 
50
+ sig { params(node: Prism::BlockArgumentNode).void }
51
+ def on_block_argument_node_enter(node)
52
+ expression = node.expression
53
+ return unless expression.is_a?(Prism::SymbolNode)
54
+
55
+ value = expression.value
56
+ return unless value
57
+
58
+ handle_method_definition(value, false)
59
+ end
60
+
49
61
  sig { params(node: Prism::ConstantPathNode).void }
50
62
  def on_constant_path_node_enter(node)
51
63
  name = constant_name(node)
@@ -64,12 +76,9 @@ module RubyLsp
64
76
 
65
77
  private
66
78
 
67
- sig { params(node: Prism::CallNode).void }
68
- def handle_method_definition(node)
69
- message = node.message
70
- return unless message
71
-
72
- methods = if self_receiver?(node)
79
+ sig { params(message: String, self_receiver: T::Boolean).void }
80
+ def handle_method_definition(message, self_receiver)
81
+ methods = if self_receiver
73
82
  @index.resolve_method(message, @nesting.join("::"))
74
83
  else
75
84
  # If the method doesn't have a receiver, then we provide a few candidates to jump to
@@ -271,7 +271,7 @@ module RubyLsp
271
271
  def on_constant_path_node_enter(node)
272
272
  return unless matches?(node, CONSTANT_PATH_NODES)
273
273
 
274
- add_highlight(Constant::DocumentHighlightKind::READ, node.location)
274
+ add_highlight(Constant::DocumentHighlightKind::READ, node.name_loc)
275
275
  end
276
276
 
277
277
  sig { params(node: Prism::ConstantReadNode).void }
@@ -30,7 +30,7 @@ module RubyLsp
30
30
  lookup[spec.name] = {}
31
31
  lookup[spec.name][spec.version.to_s] = {}
32
32
 
33
- Dir.glob("**/*.rb", base: "#{spec.full_gem_path}/").each do |path|
33
+ Dir.glob("**/*.rb", base: "#{spec.full_gem_path.delete_prefix("//?/")}/").each do |path|
34
34
  lookup[spec.name][spec.version.to_s][path] = "#{spec.full_gem_path}/#{path}"
35
35
  end
36
36
  end
@@ -58,6 +58,7 @@ module RubyLsp
58
58
  :on_constant_operator_write_node_enter,
59
59
  :on_constant_or_write_node_enter,
60
60
  :on_constant_target_node_enter,
61
+ :on_constant_path_node_enter,
61
62
  :on_local_variable_and_write_node_enter,
62
63
  :on_local_variable_operator_write_node_enter,
63
64
  :on_local_variable_or_write_node_enter,
@@ -302,17 +303,64 @@ module RubyLsp
302
303
  def on_class_node_enter(node)
303
304
  return unless visible?(node, @range)
304
305
 
305
- @response_builder.add_token(node.constant_path.location, :class, [:declaration])
306
+ constant_path = node.constant_path
307
+
308
+ if constant_path.is_a?(Prism::ConstantReadNode)
309
+ @response_builder.add_token(constant_path.location, :class, [:declaration])
310
+ else
311
+ each_constant_path_part(constant_path) do |part|
312
+ loc = case part
313
+ when Prism::ConstantPathNode
314
+ part.name_loc
315
+ when Prism::ConstantReadNode
316
+ part.location
317
+ end
318
+ next unless loc
319
+
320
+ @response_builder.add_token(loc, :class, [:declaration])
321
+ end
322
+ end
306
323
 
307
324
  superclass = node.superclass
308
- @response_builder.add_token(superclass.location, :class) if superclass
325
+
326
+ if superclass.is_a?(Prism::ConstantReadNode)
327
+ @response_builder.add_token(superclass.location, :class)
328
+ elsif superclass
329
+ each_constant_path_part(superclass) do |part|
330
+ loc = case part
331
+ when Prism::ConstantPathNode
332
+ part.name_loc
333
+ when Prism::ConstantReadNode
334
+ part.location
335
+ end
336
+ next unless loc
337
+
338
+ @response_builder.add_token(loc, :class)
339
+ end
340
+ end
309
341
  end
310
342
 
311
343
  sig { params(node: Prism::ModuleNode).void }
312
344
  def on_module_node_enter(node)
313
345
  return unless visible?(node, @range)
314
346
 
315
- @response_builder.add_token(node.constant_path.location, :namespace, [:declaration])
347
+ constant_path = node.constant_path
348
+
349
+ if constant_path.is_a?(Prism::ConstantReadNode)
350
+ @response_builder.add_token(constant_path.location, :namespace, [:declaration])
351
+ else
352
+ each_constant_path_part(constant_path) do |part|
353
+ loc = case part
354
+ when Prism::ConstantPathNode
355
+ part.name_loc
356
+ when Prism::ConstantReadNode
357
+ part.location
358
+ end
359
+ next unless loc
360
+
361
+ @response_builder.add_token(loc, :namespace, [:declaration])
362
+ end
363
+ end
316
364
  end
317
365
 
318
366
  sig { params(node: Prism::ImplicitNode).void }
@@ -327,6 +375,14 @@ module RubyLsp
327
375
  @inside_implicit_node = false
328
376
  end
329
377
 
378
+ sig { params(node: Prism::ConstantPathNode).void }
379
+ def on_constant_path_node_enter(node)
380
+ return if @inside_implicit_node
381
+ return unless visible?(node, @range)
382
+
383
+ @response_builder.add_token(node.name_loc, :namespace)
384
+ end
385
+
330
386
  private
331
387
 
332
388
  # Textmate provides highlighting for a subset of these special Ruby-specific methods. We want to utilize that
@@ -11,6 +11,7 @@ module RubyLsp
11
11
  # suggests possible completions according to what the developer is typing.
12
12
  #
13
13
  # Currently supported targets:
14
+ #
14
15
  # - Classes
15
16
  # - Modules
16
17
  # - Constants
@@ -34,7 +35,7 @@ module RubyLsp
34
35
  def provider
35
36
  Interface::CompletionOptions.new(
36
37
  resolve_provider: true,
37
- trigger_characters: ["/", "\"", "'"],
38
+ trigger_characters: ["/", "\"", "'", ":"],
38
39
  completion_item: {
39
40
  labelDetailsSupport: true,
40
41
  },
@@ -36,20 +36,27 @@ module RubyLsp
36
36
  @item = item
37
37
  end
38
38
 
39
- sig { override.returns(Interface::CompletionItem) }
39
+ sig { override.returns(T::Hash[Symbol, T.untyped]) }
40
40
  def perform
41
+ # Based on the spec https://microsoft.github.io/language-server-protocol/specification#textDocument_completion,
42
+ # a completion resolve request must always return the original completion item without modifying ANY fields
43
+ # other than label details and documentation. If we modify anything, the completion behaviour might be broken.
44
+ #
45
+ # For example, forgetting to return the `insertText` included in the original item will make the editor use the
46
+ # `label` for the text edit instead
41
47
  label = @item[:label]
42
48
  entries = @index[label] || []
43
- Interface::CompletionItem.new(
44
- label: label,
45
- label_details: Interface::CompletionItemLabelDetails.new(
46
- description: entries.take(MAX_DOCUMENTATION_ENTRIES).map(&:file_name).join(","),
47
- ),
48
- documentation: Interface::MarkupContent.new(
49
- kind: "markdown",
50
- value: markdown_from_index_entries(label, entries, MAX_DOCUMENTATION_ENTRIES),
51
- ),
49
+
50
+ @item[:labelDetails] = Interface::CompletionItemLabelDetails.new(
51
+ description: entries.take(MAX_DOCUMENTATION_ENTRIES).map(&:file_name).join(","),
52
+ )
53
+
54
+ @item[:documentation] = Interface::MarkupContent.new(
55
+ kind: "markdown",
56
+ value: markdown_from_index_entries(label, entries, MAX_DOCUMENTATION_ENTRIES),
52
57
  )
58
+
59
+ @item
53
60
  end
54
61
  end
55
62
  end
@@ -12,6 +12,7 @@ module RubyLsp
12
12
  # definition of the symbol under the cursor.
13
13
  #
14
14
  # Currently supported targets:
15
+ #
15
16
  # - Classes
16
17
  # - Modules
17
18
  # - Constants
@@ -47,7 +48,7 @@ module RubyLsp
47
48
 
48
49
  target, parent, nesting = document.locate_node(
49
50
  position,
50
- node_types: [Prism::CallNode, Prism::ConstantReadNode, Prism::ConstantPathNode],
51
+ node_types: [Prism::CallNode, Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::BlockArgumentNode],
51
52
  )
52
53
 
53
54
  if target.is_a?(Prism::ConstantReadNode) && parent.is_a?(Prism::ConstantPathNode)
@@ -50,12 +50,12 @@ module RubyLsp
50
50
  params(
51
51
  node: Prism::Node,
52
52
  title: String,
53
- command_name: String,
53
+ command_name: T.nilable(String),
54
54
  arguments: T.nilable(T::Array[T.untyped]),
55
55
  data: T.nilable(T::Hash[T.untyped, T.untyped]),
56
56
  ).returns(Interface::CodeLens)
57
57
  end
58
- def create_code_lens(node, title:, command_name:, arguments:, data:)
58
+ def create_code_lens(node, title:, command_name: nil, arguments: nil, data: nil)
59
59
  range = range_from_node(node)
60
60
 
61
61
  Interface::CodeLens.new(
@@ -167,6 +167,24 @@ module RubyLsp
167
167
  constant_name(path)
168
168
  end
169
169
  end
170
+
171
+ # Iterates over each part of a constant path, so that we can easily push response items for each section of the
172
+ # name. For example, for `Foo::Bar::Baz`, this method will invoke the block with `Foo`, then `Bar` and finally
173
+ # `Baz`.
174
+ sig do
175
+ params(
176
+ node: Prism::Node,
177
+ block: T.proc.params(part: Prism::Node).void,
178
+ ).void
179
+ end
180
+ def each_constant_path_part(node, &block)
181
+ current = T.let(node, T.nilable(Prism::Node))
182
+
183
+ while current.is_a?(Prism::ConstantPathNode)
184
+ block.call(current)
185
+ current = current.parent
186
+ end
187
+ end
170
188
  end
171
189
  end
172
190
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.6
4
+ version: 0.16.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-04-26 00:00:00.000000000 Z
11
+ date: 2024-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: language_server-protocol
@@ -30,20 +30,20 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.23.0
33
+ version: 0.29.0
34
34
  - - "<"
35
35
  - !ruby/object:Gem::Version
36
- version: '0.28'
36
+ version: '0.30'
37
37
  type: :runtime
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
41
41
  - - ">="
42
42
  - !ruby/object:Gem::Version
43
- version: 0.23.0
43
+ version: 0.29.0
44
44
  - - "<"
45
45
  - !ruby/object:Gem::Version
46
- version: '0.28'
46
+ version: '0.30'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: sorbet-runtime
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -78,8 +78,8 @@ files:
78
78
  - lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb
79
79
  - lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb
80
80
  - lib/ruby-lsp.rb
81
- - lib/ruby_indexer/lib/ruby_indexer/collector.rb
82
81
  - lib/ruby_indexer/lib/ruby_indexer/configuration.rb
82
+ - lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb
83
83
  - lib/ruby_indexer/lib/ruby_indexer/entry.rb
84
84
  - lib/ruby_indexer/lib/ruby_indexer/index.rb
85
85
  - lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb
@@ -178,7 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
178
  - !ruby/object:Gem::Version
179
179
  version: '0'
180
180
  requirements: []
181
- rubygems_version: 3.5.9
181
+ rubygems_version: 3.5.10
182
182
  signing_key:
183
183
  specification_version: 4
184
184
  summary: An opinionated language server for Ruby