Skip to content

Commit

Permalink
Revert "Replace IndexablePath with ResourceUri concept" (#2523)
Browse files Browse the repository at this point in the history
Revert "Replace IndexablePath with ResourceUri concept (#2423)"

This reverts commit 7572fde.
  • Loading branch information
vinistock authored Sep 4, 2024
1 parent 7572fde commit 9139678
Show file tree
Hide file tree
Showing 23 changed files with 201 additions and 296 deletions.
6 changes: 3 additions & 3 deletions exe/ruby-lsp
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,9 @@ if options[:doctor]

puts "Globbing for indexable files"

index.configuration.indexables.each do |uri|
puts "indexing: #{uri}"
index.index_single(uri)
index.configuration.indexables.each do |indexable|
puts "indexing: #{indexable.full_path}"
index.index_single(indexable)
end
return
end
Expand Down
6 changes: 3 additions & 3 deletions exe/ruby-lsp-check
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ puts "Verifying that indexing executes successfully. This may take a while..."
index = RubyIndexer::Index.new
indexables = index.configuration.indexables

indexables.each_with_index do |uri, i|
index.index_single(uri)
indexables.each_with_index do |indexable, i|
index.index_single(indexable)
rescue => e
errors[uri.to_standarized_path] = e
errors[indexable.full_path] = e
ensure
print("\033[M\033[0KIndexed #{i + 1}/#{indexables.length}") unless ENV["CI"]
end
Expand Down
14 changes: 7 additions & 7 deletions lib/ruby_indexer/lib/ruby_indexer/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def initialize
)
end

sig { returns(T::Array[FileUri]) }
sig { returns(T::Array[IndexablePath]) }
def indexables
excluded_gems = @excluded_gems - @included_gems
locked_gems = Bundler.locked_gems&.specs
Expand All @@ -74,7 +74,7 @@ def indexables
load_path_entry = $LOAD_PATH.find { |load_path| path.start_with?(load_path) }
end

ResourceUri.file(path, load_path_entry)
IndexablePath.new(load_path_entry, path)
end
end

Expand All @@ -91,7 +91,7 @@ def indexables
# Remove user specified patterns
indexables.reject! do |indexable|
excluded_patterns.any? do |pattern|
File.fnmatch?(pattern, indexable.to_standardized_path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
File.fnmatch?(pattern, indexable.full_path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
end
end

Expand Down Expand Up @@ -123,12 +123,12 @@ def indexables
# If the default_path is a directory, we index all the Ruby files in it
indexables.concat(
Dir.glob(File.join(default_path, "**", "*.rb"), File::FNM_PATHNAME | File::FNM_EXTGLOB).map! do |path|
ResourceUri.file(path, RbConfig::CONFIG["rubylibdir"])
IndexablePath.new(RbConfig::CONFIG["rubylibdir"], path)
end,
)
elsif pathname.extname == ".rb"
# If the default_path is a Ruby file, we index it
indexables << ResourceUri.file(default_path, RbConfig::CONFIG["rubylibdir"])
indexables << IndexablePath.new(RbConfig::CONFIG["rubylibdir"], default_path)
end
end

Expand All @@ -146,7 +146,7 @@ def indexables
indexables.concat(
spec.require_paths.flat_map do |require_path|
load_path_entry = File.join(spec.full_gem_path, require_path)
Dir.glob(File.join(load_path_entry, "**", "*.rb")).map! { |path| ResourceUri.file(path, load_path_entry) }
Dir.glob(File.join(load_path_entry, "**", "*.rb")).map! { |path| IndexablePath.new(load_path_entry, path) }
end,
)
rescue Gem::MissingSpecError
Expand All @@ -155,7 +155,7 @@ def indexables
# just ignore if they're missing
end

indexables.uniq!(&:to_s)
indexables.uniq!(&:full_path)
indexables
end

Expand Down
103 changes: 47 additions & 56 deletions lib/ruby_indexer/lib/ruby_indexer/index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def initialize
@files_to_entries = T.let({}, T::Hash[String, T::Array[Entry]])

# Holds all require paths for every indexed item so that we can provide autocomplete for requires
@require_paths_tree = T.let(PrefixTree[ResourceUri].new, PrefixTree[ResourceUri])
@require_paths_tree = T.let(PrefixTree[IndexablePath].new, PrefixTree[IndexablePath])

# Holds the linearized ancestors list for every namespace
@ancestors = T.let({}, T::Hash[String, T::Array[String]])
Expand Down Expand Up @@ -63,35 +63,31 @@ def register_included_hook(module_name, &hook)
(@included_hooks[module_name] ||= []) << hook
end

sig { params(uri: ResourceUri).void }
def delete(uri)
path = uri.to_standardized_path

if path
# For each constant discovered in `path`, delete the associated entry from the index. If there are no entries
# left, delete the constant from the index.
@files_to_entries[path]&.each do |entry|
name = entry.name
entries = @entries[name]
next unless entries

# Delete the specific entry from the list for this name
entries.delete(entry)

# If all entries were deleted, then remove the name from the hash and from the prefix tree. Otherwise, update
# the prefix tree with the current entries
if entries.empty?
@entries.delete(name)
@entries_tree.delete(name)
else
@entries_tree.insert(name, entries)
end
end
sig { params(indexable: IndexablePath).void }
def delete(indexable)
# For each constant discovered in `path`, delete the associated entry from the index. If there are no entries
# left, delete the constant from the index.
@files_to_entries[indexable.full_path]&.each do |entry|
name = entry.name
entries = @entries[name]
next unless entries

@files_to_entries.delete(path)
# Delete the specific entry from the list for this name
entries.delete(entry)

# If all entries were deleted, then remove the name from the hash and from the prefix tree. Otherwise, update
# the prefix tree with the current entries
if entries.empty?
@entries.delete(name)
@entries_tree.delete(name)
else
@entries_tree.insert(name, entries)
end
end

require_path = uri.require_path
@files_to_entries.delete(indexable.full_path)

require_path = indexable.require_path
@require_paths_tree.delete(require_path) if require_path
end

Expand All @@ -109,7 +105,7 @@ def [](fully_qualified_name)
@entries[fully_qualified_name.delete_prefix("::")]
end

sig { params(query: String).returns(T::Array[ResourceUri]) }
sig { params(query: String).returns(T::Array[IndexablePath]) }
def search_require_paths(query)
@require_paths_tree.search(query)
end
Expand Down Expand Up @@ -296,53 +292,49 @@ def resolve(name, nesting, seen_names = [])
nil
end

# Index all files for the given uris, which defaults to what is configured. A block can be used to track and control
# indexing progress. That block is invoked with the current progress percentage and should return `true` to continue
# indexing or `false` to stop indexing.
# Index all files for the given indexable paths, which defaults to what is configured. A block can be used to track
# and control indexing progress. That block is invoked with the current progress percentage and should return `true`
# to continue indexing or `false` to stop indexing.
sig do
params(
uris: T::Array[ResourceUri],
indexable_paths: T::Array[IndexablePath],
block: T.nilable(T.proc.params(progress: Integer).returns(T::Boolean)),
).void
end
def index_all(uris: @configuration.indexables, &block)
def index_all(indexable_paths: @configuration.indexables, &block)
RBSIndexer.new(self).index_ruby_core
# Calculate how many paths are worth 1% of progress
progress_step = (uris.length / 100.0).ceil
progress_step = (indexable_paths.length / 100.0).ceil

uris.each_with_index do |uri, index|
indexable_paths.each_with_index do |path, index|
if block && index % progress_step == 0
progress = (index / progress_step) + 1
break unless block.call(progress)
end

index_single(uri)
index_single(path)
end
end

sig { params(uri: ResourceUri, source: T.nilable(String)).void }
def index_single(uri, source = nil)
path = uri.to_standardized_path
# Remove once we support indexing non file URIs
return unless path

content = source || File.read(path)
sig { params(indexable_path: IndexablePath, source: T.nilable(String)).void }
def index_single(indexable_path, source = nil)
content = source || File.read(indexable_path.full_path)
dispatcher = Prism::Dispatcher.new

result = Prism.parse(content)
listener = DeclarationListener.new(
self,
dispatcher,
result,
path,
indexable_path.full_path,
enhancements: @enhancements,
)
dispatcher.dispatch(result.value)

indexing_errors = listener.indexing_errors.uniq

require_path = uri.require_path
@require_paths_tree.insert(require_path, uri) if require_path
require_path = indexable_path.require_path
@require_paths_tree.insert(require_path, indexable_path) if require_path

if indexing_errors.any?
indexing_errors.each do |error|
Expand All @@ -354,7 +346,7 @@ def index_single(uri, source = nil)
# it
rescue SystemStackError => e
if e.backtrace&.first&.include?("prism")
$stderr.puts "Prism error indexing #{uri}: #{e.message}"
$stderr.puts "Prism error indexing #{indexable_path.full_path}: #{e.message}"
else
raise
end
Expand Down Expand Up @@ -551,17 +543,16 @@ def instance_variable_completion_candidates(name, owner_name)
variables
end

# Synchronizes a change made to the given uri. This method will ensure that new declarations are indexed, removed
# declarations removed and that the ancestor linearization cache is cleared if necessary
sig { params(uri: ResourceUri).void }
def handle_change(uri)
path = T.must(uri.to_standardized_path)
original_entries = @files_to_entries[path]
# Synchronizes a change made to the given indexable path. This method will ensure that new declarations are indexed,
# removed declarations removed and that the ancestor linearization cache is cleared if necessary
sig { params(indexable: IndexablePath).void }
def handle_change(indexable)
original_entries = @files_to_entries[indexable.full_path]

delete(uri)
index_single(uri)
delete(indexable)
index_single(indexable)

updated_entries = @files_to_entries[path]
updated_entries = @files_to_entries[indexable.full_path]

return unless original_entries && updated_entries

Expand Down
29 changes: 29 additions & 0 deletions lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# typed: strict
# frozen_string_literal: true

module RubyIndexer
class IndexablePath
extend T::Sig

sig { returns(T.nilable(String)) }
attr_reader :require_path

sig { returns(String) }
attr_reader :full_path

# An IndexablePath is instantiated with a load_path_entry and a full_path. The load_path_entry is where the file can
# be found in the $LOAD_PATH, which we use to determine the require_path. The load_path_entry may be `nil` if the
# indexer is configured to go through files that do not belong in the $LOAD_PATH. For example,
# `sorbet/tapioca/require.rb` ends up being a part of the paths to be indexed because it's a Ruby file inside the
# project, but the `sorbet` folder is not a part of the $LOAD_PATH. That means that both its load_path_entry and
# require_path will be `nil`, since it cannot be required by the project
sig { params(load_path_entry: T.nilable(String), full_path: String).void }
def initialize(load_path_entry, full_path)
@full_path = full_path
@require_path = T.let(
load_path_entry ? full_path.delete_prefix("#{load_path_entry}/").delete_suffix(".rb") : nil,
T.nilable(String),
)
end
end
end
Loading

0 comments on commit 9139678

Please sign in to comment.