ruby-lsp 0.16.5 → 0.16.7

Sign up to get free protection for your applications and to get access to all the features.
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