ruby-lsp 0.16.5 → 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: bcd4426f17d6d429be733a9adcdf799e23c29689ad5fb69f9b9f7b3cd334bf9b
4
- data.tar.gz: 071d5c37e1acf83c07a5b48e85a3bc4a18fb63a20b54ff191158799cf4320803
3
+ metadata.gz: ebc166637de670ea02144e74ccb2e99ae1a9824c9f99995380d81bed99ff6a29
4
+ data.tar.gz: 48b7ef9364f4d1a8044042c8476414e87da5b38ca4871683c16d06636fc8e411
5
5
  SHA512:
6
- metadata.gz: 9edc44ec74f5f5d9b5f15fb7d22501126eb8f6bec3d9a69b3061946b369ecb0e453e45e9a0dc9df1482117dc738f7bd66005e2dcd0cd46e3aaad6f6658283766
7
- data.tar.gz: c4f9ddb4da9373544b2456632a0fb76bdbca5e5ddf5e874b0e6ab4f6aae13ad8b17ef9482873b37ae33bc7ec797faebde76bf5a6e9d86e6a4ddf798b4eb9f0df
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.5
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",
@@ -75,15 +79,32 @@ require "ruby_lsp/internal"
75
79
 
76
80
  if options[:debug]
77
81
  if ["x64-mingw-ucrt", "x64-mingw32"].include?(RUBY_PLATFORM)
78
- puts "Debugging is not supported on Windows"
82
+ $stderr.puts "Debugging is not supported on Windows"
79
83
  exit 1
80
84
  end
81
85
 
82
86
  begin
87
+ ENV.delete("RUBY_DEBUG_IRB_CONSOLE")
83
88
  require "debug/open_nonstop"
84
89
  rescue LoadError
85
- warn("You need to install the debug gem to use the --debug flag")
90
+ $stderr.puts("You need to install the debug gem to use the --debug flag")
86
91
  end
87
92
  end
88
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
+
89
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
@@ -56,6 +56,7 @@ module RubyIndexer
56
56
  load_path_entry = T.let(nil, T.nilable(String))
57
57
 
58
58
  Dir.glob(pattern, File::FNM_PATHNAME | File::FNM_EXTGLOB).map! do |path|
59
+ path = File.expand_path(path)
59
60
  # All entries for the same pattern match the same $LOAD_PATH entry. Since searching the $LOAD_PATH for every
60
61
  # entry is expensive, we memoize it until we find a path that doesn't belong to that $LOAD_PATH. This happens
61
62
  # on repositories that define multiple gems, like Rails. All frameworks are defined inside the Dir.pwd, but
@@ -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
@@ -61,28 +61,13 @@ module RubyIndexer
61
61
  abstract!
62
62
 
63
63
  sig { returns(T::Array[String]) }
64
- attr_accessor :included_modules
65
-
66
- sig { returns(T::Array[String]) }
67
- attr_accessor :prepended_modules
68
-
69
- sig do
70
- params(
71
- name: String,
72
- file_path: String,
73
- location: T.any(Prism::Location, RubyIndexer::Location),
74
- comments: T::Array[String],
75
- ).void
76
- end
77
- def initialize(name, file_path, location, comments)
78
- super(name, file_path, location, comments)
79
- @included_modules = T.let([], T::Array[String])
80
- @prepended_modules = T.let([], T::Array[String])
64
+ def included_modules
65
+ @included_modules ||= T.let([], T.nilable(T::Array[String]))
81
66
  end
82
67
 
83
- sig { returns(String) }
84
- def short_name
85
- T.must(@name.split("::").last)
68
+ sig { returns(T::Array[String]) }
69
+ def prepended_modules
70
+ @prepended_modules ||= T.let([], T.nilable(T::Array[String]))
86
71
  end
87
72
  end
88
73
 
@@ -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
@@ -20,6 +20,14 @@ module RubyIndexer
20
20
  assert(indexables.none? { |indexable| indexable.full_path == __FILE__ })
21
21
  end
22
22
 
23
+ def test_indexables_have_expanded_full_paths
24
+ @config.apply_config({ "included_patterns" => ["**/*.rb"] })
25
+ indexables = @config.indexables
26
+
27
+ # All paths should be expanded
28
+ assert(indexables.none? { |indexable| indexable.full_path.start_with?("lib/") })
29
+ end
30
+
23
31
  def test_indexables_only_includes_gem_require_paths
24
32
  indexables = @config.indexables
25
33
 
@@ -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