Skip to content

Commit

Permalink
Start accepting any URI scheme in the index
Browse files Browse the repository at this point in the history
  • Loading branch information
vinistock committed Nov 28, 2024
1 parent ce50f49 commit a2083e2
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 57 deletions.
13 changes: 9 additions & 4 deletions lib/ruby_indexer/lib/ruby_indexer/entry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,24 @@ def private?

sig { returns(String) }
def file_name
File.basename(file_path)
if @uri.scheme == "untitled"
T.must(@uri.opaque)
else
File.basename(T.must(file_path))
end
end

sig { returns(String) }
sig { returns(T.nilable(String)) }
def file_path
T.must(@uri.full_path)
@uri.full_path
end

sig { returns(String) }
def comments
@comments ||= begin
# Parse only the comments based on the file path, which is much faster than parsing the entire file
parsed_comments = Prism.parse_file_comments(file_path)
path = file_path
parsed_comments = path ? Prism.parse_file_comments(path) : []

# Group comments based on whether they belong to a single block of comments
grouped = parsed_comments.slice_when do |left, right|
Expand Down
71 changes: 33 additions & 38 deletions lib/ruby_indexer/lib/ruby_indexer/index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ def initialize

# Holds references to where entries where discovered so that we can easily delete them
# {
# "/my/project/foo.rb" => [#<Entry::Class>, #<Entry::Class>],
# "/my/project/bar.rb" => [#<Entry::Class>],
# "file:///my/project/foo.rb" => [#<Entry::Class>, #<Entry::Class>],
# "file:///my/project/bar.rb" => [#<Entry::Class>],
# "untitled:Untitled-1" => [#<Entry::Class>],
# }
@files_to_entries = T.let({}, T::Hash[String, T::Array[Entry]])
@uris_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[URI::Generic].new, PrefixTree[URI::Generic])
Expand All @@ -57,12 +58,10 @@ def register_included_hook(module_name, &hook)

sig { params(uri: URI::Generic).void }
def delete(uri)
full_path = uri.full_path
return unless full_path

key = uri.to_s
# 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[full_path]&.each do |entry|
@uris_to_entries[key]&.each do |entry|
name = entry.name
entries = @entries[name]
next unless entries
Expand All @@ -80,7 +79,7 @@ def delete(uri)
end
end

@files_to_entries.delete(full_path)
@uris_to_entries.delete(key)

require_path = uri.require_path
@require_paths_tree.delete(require_path) if require_path
Expand All @@ -91,7 +90,7 @@ def add(entry, skip_prefix_tree: false)
name = entry.name

(@entries[name] ||= []) << entry
(@files_to_entries[entry.file_path] ||= []) << entry
(@uris_to_entries[entry.uri.to_s] ||= []) << entry
@entries_tree.insert(name, T.must(@entries[name])) unless skip_prefix_tree
end

Expand Down Expand Up @@ -380,38 +379,36 @@ def index_all(uris: @configuration.indexable_uris, &block)

sig { params(uri: URI::Generic, source: T.nilable(String), collect_comments: T::Boolean).void }
def index_single(uri, source = nil, collect_comments: true)
full_path = uri.full_path
return unless full_path
content = case uri.scheme
when "file"
full_path = uri.full_path
source || ((full_path = uri.full_path) && File.read(full_path))
when "untitled"
source
end

unless content
$stderr.puts("No content specified for #{uri}. This is likely a bug")
return
end

content = source || File.read(full_path)
dispatcher = Prism::Dispatcher.new

result = Prism.parse(content)
listener = DeclarationListener.new(
self,
dispatcher,
result,
uri,
collect_comments: collect_comments,
)
listener = DeclarationListener.new(self, dispatcher, result, uri, collect_comments: collect_comments)
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

if indexing_errors.any?
indexing_errors.each do |error|
$stderr.puts error
end
end
indexing_errors = listener.indexing_errors.uniq
indexing_errors.each { |error| $stderr.puts error } if indexing_errors.any?
rescue Errno::EISDIR, Errno::ENOENT
# If `path` is a directory, just ignore it and continue indexing. If the file doesn't exist, then we also ignore
# it
rescue SystemStackError => e
if e.backtrace&.first&.include?("prism")
$stderr.puts "Prism error indexing #{T.must(full_path)}: #{e.message}"
$stderr.puts "Prism error indexing #{uri}: #{e.message}"
else
raise
end
Expand Down Expand Up @@ -608,17 +605,15 @@ def instance_variable_completion_candidates(name, owner_name)

# 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: URI::Generic).void }
def handle_change(uri)
full_path = uri.full_path
return unless full_path

original_entries = @files_to_entries[full_path]
sig { params(uri: URI::Generic, source: T.nilable(String)).void }
def handle_change(uri, source = nil)
key = uri.to_s
original_entries = @uris_to_entries[key]

delete(uri)
index_single(uri)
index_single(uri, source)

updated_entries = @files_to_entries[full_path]
updated_entries = @uris_to_entries[key]

return unless original_entries && updated_entries

Expand Down Expand Up @@ -684,12 +679,12 @@ def existing_or_new_singleton_class(name)

sig do
type_parameters(:T).params(
path: String,
uri: String,
type: T.nilable(T::Class[T.all(T.type_parameter(:T), Entry)]),
).returns(T.nilable(T.any(T::Array[Entry], T::Array[T.type_parameter(:T)])))
end
def entries_for(path, type = nil)
entries = @files_to_entries[path]
def entries_for(uri, type = nil)
entries = @uris_to_entries[uri.to_s]
return entries unless type

entries&.grep(type)
Expand Down
31 changes: 28 additions & 3 deletions lib/ruby_indexer/test/index_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1889,13 +1889,13 @@ def self.my_singleton_def; end
end
RUBY

entries = @index.entries_for("/fake/path/foo.rb", Entry)
entries = @index.entries_for("file:///fake/path/foo.rb", Entry)
assert_equal(["Foo", "Bar", "my_def", "Bar::<Class:Bar>", "my_singleton_def"], entries.map(&:name))

entries = @index.entries_for("/fake/path/foo.rb", RubyIndexer::Entry::Namespace)
entries = @index.entries_for("file:///fake/path/foo.rb", RubyIndexer::Entry::Namespace)
assert_equal(["Foo", "Bar", "Bar::<Class:Bar>"], entries.map(&:name))

entries = @index.entries_for("/fake/path/foo.rb")
entries = @index.entries_for("file:///fake/path/foo.rb")
assert_equal(["Foo", "Bar", "my_def", "Bar::<Class:Bar>", "my_singleton_def"], entries.map(&:name))
end

Expand Down Expand Up @@ -2066,5 +2066,30 @@ def test_prevents_multiple_calls_to_index_all
@index.index_all
end
end

def test_index_can_handle_entries_from_untitled_scheme
uri = URI("untitled:Untitled-1")

index(<<~RUBY, uri: uri)
class Foo
end
RUBY

entry = @index["Foo"]&.first
refute_nil(entry, "Expected indexer to be able to handle unsaved URIs")
assert_equal("untitled:Untitled-1", entry.uri.to_s)
assert_equal("Untitled-1", entry.file_name)
assert_nil(entry.file_path)

@index.handle_change(uri, <<~RUBY)
# I added this comment!
class Foo
end
RUBY

entry = @index["Foo"]&.first
refute_nil(entry, "Expected indexer to be able to handle unsaved URIs")
assert_equal("I added this comment!", entry.comments)
end
end
end
4 changes: 2 additions & 2 deletions lib/ruby_indexer/test/test_case.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ def setup

private

def index(source)
@index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), source)
def index(source, uri: URI::Generic.from_path(path: "/fake/path/foo.rb"))
@index.index_single(uri, source)
end

def assert_entry(expected_name, type, expected_location, visibility: nil)
Expand Down
20 changes: 10 additions & 10 deletions lib/ruby_lsp/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,18 @@ def with_server(source = nil, uri = Kernel.URI("file:///fake.rb"), stub_no_typec
})
end

server.global_state.index.index_single(
URI::Generic.from_path(path: T.must(uri.to_standardized_path)),
source,
)
server.global_state.index.index_single(uri, source)
server.load_addons(include_project_addons: false) if load_addons
block.call(server, uri)
ensure
if load_addons
RubyLsp::Addon.addons.each(&:deactivate)
RubyLsp::Addon.addons.clear

begin
block.call(server, uri)
ensure
if load_addons
RubyLsp::Addon.addons.each(&:deactivate)
RubyLsp::Addon.addons.clear
end
server.run_shutdown
end
T.must(server).run_shutdown
end
end
end

0 comments on commit a2083e2

Please sign in to comment.