Skip to content

Commit

Permalink
Replace IndexablePath with Uri concept
Browse files Browse the repository at this point in the history
  • Loading branch information
vinistock committed Sep 3, 2024
1 parent 32c7086 commit 74b63ab
Show file tree
Hide file tree
Showing 23 changed files with 296 additions and 201 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 |indexable|
puts "indexing: #{indexable.full_path}"
index.index_single(indexable)
index.configuration.indexables.each do |uri|
puts "indexing: #{uri}"
index.index_single(uri)
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 |indexable, i|
index.index_single(indexable)
indexables.each_with_index do |uri, i|
index.index_single(uri)
rescue => e
errors[indexable.full_path] = e
errors[uri.to_standarized_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[IndexablePath]) }
sig { returns(T::Array[FileUri]) }
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

IndexablePath.new(load_path_entry, path)
ResourceUri.file(path, load_path_entry)
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.full_path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
File.fnmatch?(pattern, indexable.to_standardized_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|
IndexablePath.new(RbConfig::CONFIG["rubylibdir"], path)
ResourceUri.file(path, RbConfig::CONFIG["rubylibdir"])
end,
)
elsif pathname.extname == ".rb"
# If the default_path is a Ruby file, we index it
indexables << IndexablePath.new(RbConfig::CONFIG["rubylibdir"], default_path)
indexables << ResourceUri.file(default_path, RbConfig::CONFIG["rubylibdir"])
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| IndexablePath.new(load_path_entry, path) }
Dir.glob(File.join(load_path_entry, "**", "*.rb")).map! { |path| ResourceUri.file(path, load_path_entry) }
end,
)
rescue Gem::MissingSpecError
Expand All @@ -155,7 +155,7 @@ def indexables
# just ignore if they're missing
end

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

Expand Down
103 changes: 56 additions & 47 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[IndexablePath].new, PrefixTree[IndexablePath])
@require_paths_tree = T.let(PrefixTree[ResourceUri].new, PrefixTree[ResourceUri])

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

# 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)
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
end

@files_to_entries.delete(indexable.full_path)
@files_to_entries.delete(path)
end

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

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

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

# 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.
# 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.
sig do
params(
indexable_paths: T::Array[IndexablePath],
uris: T::Array[ResourceUri],
block: T.nilable(T.proc.params(progress: Integer).returns(T::Boolean)),
).void
end
def index_all(indexable_paths: @configuration.indexables, &block)
def index_all(uris: @configuration.indexables, &block)
RBSIndexer.new(self).index_ruby_core
# Calculate how many paths are worth 1% of progress
progress_step = (indexable_paths.length / 100.0).ceil
progress_step = (uris.length / 100.0).ceil

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

index_single(path)
index_single(uri)
end
end

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)
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)
dispatcher = Prism::Dispatcher.new

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

indexing_errors = listener.indexing_errors.uniq

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

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

# 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]
# 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]

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

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

return unless original_entries && updated_entries

Expand Down
29 changes: 0 additions & 29 deletions lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb

This file was deleted.

Loading

0 comments on commit 74b63ab

Please sign in to comment.