Skip to content

Commit 26690fe

Browse files
committed
Performance improvements
1 parent c75541a commit 26690fe

File tree

9 files changed

+159
-90
lines changed

9 files changed

+159
-90
lines changed

lib/ruby_indexer/lib/ruby_indexer/entry.rb

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,24 @@ def private?
4141
@visibility == :private
4242
end
4343

44+
#: -> bool
45+
def resolved?
46+
true
47+
end
48+
49+
#: -> bool
50+
def in_dependencies?
51+
@in_dependencies ||= file_path && (
52+
::RubyLsp::BUNDLE_PATH && file_path.start_with?(
53+
::RubyLsp::BUNDLE_PATH, #: as !nil
54+
) ||
55+
file_path.start_with?(RbConfig::CONFIG["rubylibdir"])
56+
)
57+
end
58+
4459
#: -> String
4560
def file_name
46-
if @uri.scheme == "untitled"
61+
@file_name ||= if @uri.scheme == "untitled"
4762
@uri.opaque #: as !nil
4863
else
4964
File.basename(
@@ -54,7 +69,7 @@ def file_name
5469

5570
#: -> String?
5671
def file_path
57-
@uri.full_path
72+
@file_path ||= @uri.full_path
5873
end
5974

6075
#: -> String
@@ -373,6 +388,11 @@ def initialize(target, nesting, name, uri, location, comments) # rubocop:disable
373388
@target = target
374389
@nesting = nesting
375390
end
391+
392+
#: -> Bool
393+
def resolved?
394+
false
395+
end
376396
end
377397

378398
# Alias represents a resolved alias, which points to an existing constant target
@@ -386,11 +406,16 @@ def initialize(target, unresolved_alias)
386406
unresolved_alias.name,
387407
unresolved_alias.uri,
388408
unresolved_alias.location,
389-
unresolved_alias.comments,
409+
nil,
390410
)
391411

392412
@visibility = unresolved_alias.visibility
393413
@target = target
414+
@unresolved_alias = unresolved_alias
415+
end
416+
417+
def comments
418+
@comments ||= @unresolved_alias.comments
394419
end
395420
end
396421

@@ -439,6 +464,11 @@ def initialize(new_name, old_name, owner, uri, location, comments) # rubocop:dis
439464
@old_name = old_name
440465
@owner = owner
441466
end
467+
468+
#: -> Bool
469+
def resolved?
470+
false
471+
end
442472
end
443473

444474
# A method alias is a resolved alias entry that points to the exact method target it refers to
@@ -451,19 +481,25 @@ class MethodAlias < Entry
451481

452482
#: ((Member | MethodAlias) target, UnresolvedMethodAlias unresolved_alias) -> void
453483
def initialize(target, unresolved_alias)
454-
full_comments = +"Alias for #{target.name}\n"
455-
full_comments << "#{unresolved_alias.comments}\n"
456-
full_comments << target.comments
457-
458484
super(
459485
unresolved_alias.new_name,
460486
unresolved_alias.uri,
461487
unresolved_alias.location,
462-
full_comments,
488+
nil,
463489
)
464490

465491
@target = target
466492
@owner = unresolved_alias.owner #: Entry::Namespace?
493+
@unresolved_alias = unresolved_alias
494+
end
495+
496+
#: -> String
497+
def comments
498+
@comments ||= <<~COMMENTS.chomp
499+
Alias for #{@target.name}
500+
#{@unresolved_alias.comments}
501+
#{@target.comments}
502+
COMMENTS
467503
end
468504

469505
#: -> String

lib/ruby_indexer/lib/ruby_indexer/index.rb

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,7 @@ def fuzzy_search(query)
203203

204204
entries
205205
end
206-
return entries.flat_map do |entries|
207-
skip_unresolved_entries(entries)
208-
end
206+
return entries.flatten
209207
end
210208

211209
normalized_query = query.gsub("::", "").downcase
@@ -389,6 +387,8 @@ def index_single(uri, source, collect_comments: true)
389387
require_path = uri.require_path
390388
@require_paths_tree.insert(require_path, uri) if require_path
391389

390+
process_unresolved_entries
391+
392392
indexing_errors = listener.indexing_errors.uniq
393393
indexing_errors.each { |error| $stderr.puts(error) } if indexing_errors.any?
394394
rescue SystemStackError => e
@@ -731,6 +731,20 @@ def entries_for(uri, type = nil)
731731
entries&.grep(type)
732732
end
733733

734+
#: -> voide
735+
def process_unresolved_entries
736+
@entries.values.each do |entries|
737+
entries.each do |entry|
738+
case entry
739+
when Entry::UnresolvedConstantAlias
740+
resolve_alias(entry, [])
741+
when Entry::UnresolvedMethodAlias
742+
resolve_method_alias(entry, entry.owner&.name || "", [])
743+
end
744+
end
745+
end
746+
end
747+
734748
private
735749

736750
# Always returns the linearized ancestors for the attached class, regardless of whether `name` refers to a singleton

lib/ruby_indexer/lib/ruby_indexer/uri.rb

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,37 @@ class Generic
1212
# NOTE: We also define this in the shim
1313
PARSER = const_defined?(:RFC2396_PARSER) ? RFC2396_PARSER : DEFAULT_PARSER
1414

15+
# This unsafe regex is the same one used in the URI::RFC2396_REGEXP class with the exception of the fact that we
16+
# do not include colon as a safe character. VS Code URIs always escape colons and we need to ensure we do the
17+
# same to avoid inconsistencies in our URIs, which are used to identify resources
18+
UNSAFE_REGEX = %r{[^\-_.!~*'()a-zA-Z\d;/?@&=+$,\[\]]}
19+
1520
class << self
1621
#: (path: String, ?fragment: String?, ?scheme: String, ?load_path_entry: String?) -> URI::Generic
17-
def from_path(path:, fragment: nil, scheme: "file", load_path_entry: nil)
18-
# This unsafe regex is the same one used in the URI::RFC2396_REGEXP class with the exception of the fact that we
19-
# do not include colon as a safe character. VS Code URIs always escape colons and we need to ensure we do the
20-
# same to avoid inconsistencies in our URIs, which are used to identify resources
21-
unsafe_regex = %r{[^\-_.!~*'()a-zA-Z\d;/?@&=+$,\[\]]}
22-
22+
def from_win_path(path:, fragment: nil, scheme: "file", load_path_entry: nil)
2323
# On Windows, if the path begins with the disk name, we need to add a leading slash to make it a valid URI
2424
escaped_path = if /^[A-Z]:/i.match?(path)
25-
PARSER.escape("/#{path}", unsafe_regex)
25+
PARSER.escape("/#{path}", UNSAFE_REGEX)
2626
elsif path.start_with?("//?/")
2727
# Some paths on Windows start with "//?/". This is a special prefix that allows for long file paths
28-
PARSER.escape(path.delete_prefix("//?"), unsafe_regex)
28+
PARSER.escape(path.delete_prefix("//?"), UNSAFE_REGEX)
2929
else
30-
PARSER.escape(path, unsafe_regex)
30+
PARSER.escape(path, UNSAFE_REGEX)
31+
end
32+
33+
uri = build(scheme: scheme, path: escaped_path, fragment: fragment)
34+
35+
if load_path_entry
36+
uri.require_path = path.delete_prefix("#{load_path_entry}/").delete_suffix(".rb")
3137
end
3238

39+
uri
40+
end
41+
42+
#: (path: String, ?fragment: String?, ?scheme: String, ?load_path_entry: String?) -> URI::Generic
43+
def from_unix_path(path:, fragment: nil, scheme: "file", load_path_entry: nil)
44+
escaped_path = PARSER.escape(path, UNSAFE_REGEX)
45+
3346
uri = build(scheme: scheme, path: escaped_path, fragment: fragment)
3447

3548
if load_path_entry
@@ -38,6 +51,12 @@ def from_path(path:, fragment: nil, scheme: "file", load_path_entry: nil)
3851

3952
uri
4053
end
54+
55+
if Gem.win_platform?
56+
alias_method :from_path, :from_win_path
57+
else
58+
alias_method :from_path, :from_unix_path
59+
end
4160
end
4261

4362
#: String?
@@ -51,22 +70,43 @@ def add_require_path_from_load_entry(load_path_entry)
5170
self.require_path = path.delete_prefix("#{load_path_entry}/").delete_suffix(".rb")
5271
end
5372

54-
#: -> String?
55-
def to_standardized_path
73+
# On Windows, when we're getting the file system path back from the URI, we need to remove the leading forward
74+
# slash
75+
def to_standardized_win_path
5676
parsed_path = path
77+
5778
return unless parsed_path
5879

80+
# we can bail out parsing if there is nothing to unscape
81+
return parsed_path unless parsed_path.match?(/%[0-9A-Fa-f]{2}/)
82+
5983
unescaped_path = PARSER.unescape(parsed_path)
6084

61-
# On Windows, when we're getting the file system path back from the URI, we need to remove the leading forward
62-
# slash
6385
if %r{^/[A-Z]:}i.match?(unescaped_path)
6486
unescaped_path.delete_prefix("/")
6587
else
6688
unescaped_path
6789
end
6890
end
6991

92+
#: -> String?
93+
def to_standardized_unix_path
94+
unscapped_path = path
95+
return unless unscapped_path
96+
97+
# we can bail out parsing if there is nothing to unscape
98+
return unscapped_path unless unscapped_path.match?(/%[0-9A-Fa-f]{2}/)
99+
100+
PARSER.unescape(unscapped_path)
101+
end
102+
103+
104+
if Gem.win_platform?
105+
alias_method :to_standardized_path, :to_standardized_win_path
106+
else
107+
alias_method :to_standardized_path, :to_standardized_unix_path
108+
end
109+
70110
alias_method :full_path, :to_standardized_path
71111
end
72112
end

lib/ruby_indexer/test/constant_test.rb

Lines changed: 12 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,9 @@ module C
207207
SECOND = A::FIRST
208208
RUBY
209209

210-
unresolve_entry = @index["A::FIRST"]&.first #: as Entry::UnresolvedConstantAlias
211-
assert_instance_of(Entry::UnresolvedConstantAlias, unresolve_entry)
212-
assert_equal(["A"], unresolve_entry.nesting)
213-
assert_equal("B::C", unresolve_entry.target)
210+
resolved_entry = @index["A::FIRST"]&.first #: as Entry::ConstantAlias
211+
assert_instance_of(Entry::ConstantAlias, resolved_entry)
212+
assert_equal("A::B::C", resolved_entry.target)
214213

215214
resolved_entry = @index.resolve("A::FIRST", [])&.first #: as Entry::ConstantAlias
216215
assert_instance_of(Entry::ConstantAlias, resolved_entry)
@@ -233,23 +232,17 @@ module Other
233232
end
234233
RUBY
235234

236-
unresolve_entry = @index["A::ALIAS"]&.first #: as Entry::UnresolvedConstantAlias
237-
assert_instance_of(Entry::UnresolvedConstantAlias, unresolve_entry)
238-
assert_equal(["A"], unresolve_entry.nesting)
239-
assert_equal("B", unresolve_entry.target)
240-
241-
resolved_entry = @index.resolve("ALIAS", ["A"])&.first #: as Entry::ConstantAlias
235+
resolved_entry = @index["A::ALIAS"]&.first #: as Entry::ConstantAlias
242236
assert_instance_of(Entry::ConstantAlias, resolved_entry)
243237
assert_equal("A::B", resolved_entry.target)
244238

245239
resolved_entry = @index.resolve("ALIAS::C", ["A"])&.first #: as Entry::Module
246240
assert_instance_of(Entry::Module, resolved_entry)
247241
assert_equal("A::B::C", resolved_entry.name)
248242

249-
unresolve_entry = @index["Other::ONE_MORE"]&.first #: as Entry::UnresolvedConstantAlias
250-
assert_instance_of(Entry::UnresolvedConstantAlias, unresolve_entry)
251-
assert_equal(["Other"], unresolve_entry.nesting)
252-
assert_equal("A::ALIAS", unresolve_entry.target)
243+
resolved_entry = @index["Other::ONE_MORE"]&.first #: as Entry::ConstantAlias
244+
assert_instance_of(Entry::ConstantAlias, resolved_entry)
245+
assert_equal("A::ALIAS", resolved_entry.target)
253246

254247
resolved_entry = @index.resolve("Other::ONE_MORE::C", [])&.first
255248
assert_instance_of(Entry::Module, resolved_entry)
@@ -266,45 +259,25 @@ module A
266259
RUBY
267260

268261
# B and C
269-
unresolve_entry = @index["A::B"]&.first #: as Entry::UnresolvedConstantAlias
270-
assert_instance_of(Entry::UnresolvedConstantAlias, unresolve_entry)
271-
assert_equal(["A"], unresolve_entry.nesting)
272-
assert_equal("C", unresolve_entry.target)
273-
274-
resolved_entry = @index.resolve("A::B", [])&.first #: as Entry::ConstantAlias
262+
resolved_entry = @index["A::B"]&.first #: as Entry::ConstantAlias
275263
assert_instance_of(Entry::ConstantAlias, resolved_entry)
276264
assert_equal("A::C", resolved_entry.target)
277265

278266
constant = @index["A::C"]&.first #: as Entry::Constant
279267
assert_instance_of(Entry::Constant, constant)
280268

281269
# D and E
282-
unresolve_entry = @index["A::D"]&.first #: as Entry::UnresolvedConstantAlias
283-
assert_instance_of(Entry::UnresolvedConstantAlias, unresolve_entry)
284-
assert_equal(["A"], unresolve_entry.nesting)
285-
assert_equal("E", unresolve_entry.target)
286-
287-
resolved_entry = @index.resolve("A::D", [])&.first #: as Entry::ConstantAlias
270+
resolved_entry = @index["A::D"]&.first #: as Entry::ConstantAlias
288271
assert_instance_of(Entry::ConstantAlias, resolved_entry)
289272
assert_equal("A::E", resolved_entry.target)
290273

291274
# F and G::H
292-
unresolve_entry = @index["A::F"]&.first #: as Entry::UnresolvedConstantAlias
293-
assert_instance_of(Entry::UnresolvedConstantAlias, unresolve_entry)
294-
assert_equal(["A"], unresolve_entry.nesting)
295-
assert_equal("G::H", unresolve_entry.target)
296-
297-
resolved_entry = @index.resolve("A::F", [])&.first #: as Entry::ConstantAlias
275+
resolved_entry = @index["A::F"]&.first #: as Entry::ConstantAlias
298276
assert_instance_of(Entry::ConstantAlias, resolved_entry)
299277
assert_equal("A::G::H", resolved_entry.target)
300278

301279
# I::J, K::L and M
302-
unresolve_entry = @index["A::I::J"]&.first #: as Entry::UnresolvedConstantAlias
303-
assert_instance_of(Entry::UnresolvedConstantAlias, unresolve_entry)
304-
assert_equal(["A"], unresolve_entry.nesting)
305-
assert_equal("K::L", unresolve_entry.target)
306-
307-
resolved_entry = @index.resolve("A::I::J", [])&.first #: as Entry::ConstantAlias
280+
resolved_entry = @index["A::I::J"]&.first #: as Entry::ConstantAlias
308281
assert_instance_of(Entry::ConstantAlias, resolved_entry)
309282
assert_equal("A::K::L", resolved_entry.target)
310283

@@ -356,7 +329,7 @@ module Real
356329
assert_entry("A::D::E", Entry::UnresolvedConstantAlias, "/fake/path/foo.rb:2-2:2-6")
357330
assert_entry("A::F::G", Entry::Constant, "/fake/path/foo.rb:2-8:2-12")
358331
assert_entry("A::H", Entry::Constant, "/fake/path/foo.rb:3-2:3-3")
359-
assert_entry("A::I::J", Entry::UnresolvedConstantAlias, "/fake/path/foo.rb:3-5:3-9")
332+
assert_entry("A::I::J", Entry::ConstantAlias, "/fake/path/foo.rb:3-5:3-9")
360333
assert_entry("A::K", Entry::Constant, "/fake/path/foo.rb:4-2:4-3")
361334
assert_entry("A::L", Entry::Constant, "/fake/path/foo.rb:4-5:4-6")
362335
end

lib/ruby_indexer/test/method_test.rb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -575,9 +575,13 @@ def third_method; end
575575
assert_equal("Foo", entry.owner&.name)
576576
end
577577

578-
def test_keeps_track_of_aliases
578+
def test_keeps_track_of_aliases_and_comments
579579
index(<<~RUBY)
580580
class Foo
581+
# A comment
582+
def to_s
583+
end
584+
# Whatever Comment
581585
alias whatever to_s
582586
alias_method :foo, :to_a
583587
alias_method "bar", "to_a"
@@ -588,9 +592,10 @@ class Foo
588592
end
589593
RUBY
590594

591-
assert_entry("whatever", Entry::UnresolvedMethodAlias, "/fake/path/foo.rb:1-8:1-16")
592-
assert_entry("foo", Entry::UnresolvedMethodAlias, "/fake/path/foo.rb:2-15:2-19")
593-
assert_entry("bar", Entry::UnresolvedMethodAlias, "/fake/path/foo.rb:3-15:3-20")
595+
assert_entry("whatever", Entry::MethodAlias, "/fake/path/foo.rb:5-8:5-16")
596+
assert_equal("Alias for to_s\nWhatever Comment\nA comment", @index["whatever"]&.first&.comments)
597+
assert_entry("foo", Entry::UnresolvedMethodAlias, "/fake/path/foo.rb:6-15:6-19")
598+
assert_entry("bar", Entry::UnresolvedMethodAlias, "/fake/path/foo.rb:7-15:7-20")
594599
# Foo plus 3 valid aliases
595600
assert_equal(4, @index.length - @default_indexed_entries.length)
596601
end

0 commit comments

Comments
 (0)