From ea2b9bba90be17bc7885fcf9b7cfb21e7621a6e3 Mon Sep 17 00:00:00 2001 From: Vinicius Stock Date: Tue, 26 Nov 2024 16:05:32 -0500 Subject: [PATCH] Swap all usages of indexable path for URI --- exe/ruby-lsp | 6 +- exe/ruby-lsp-check | 6 +- .../lib/ruby_indexer/configuration.rb | 2 +- lib/ruby_indexer/lib/ruby_indexer/index.rb | 69 ++++++++------- .../lib/ruby_indexer/indexable_path.rb | 29 ------- lib/ruby_indexer/lib/ruby_indexer/uri.rb | 8 ++ lib/ruby_indexer/ruby_indexer.rb | 1 - .../test/classes_and_modules_test.rb | 17 ++-- lib/ruby_indexer/test/configuration_test.rb | 72 ++++++++-------- lib/ruby_indexer/test/index_test.rb | 84 ++++++++++--------- .../test/reference_finder_test.rb | 2 +- lib/ruby_indexer/test/test_case.rb | 2 +- lib/ruby_indexer/test/uri_test.rb | 8 ++ lib/ruby_lsp/listeners/completion.rb | 4 +- lib/ruby_lsp/listeners/definition.rb | 20 +++-- lib/ruby_lsp/server.rb | 8 +- lib/ruby_lsp/test_helper.rb | 2 +- rakelib/index.rake | 2 +- test/requests/completion_test.rb | 9 +- test/requests/definition_expectations_test.rb | 49 +++++------ test/requests/references_test.rb | 2 +- test/requests/rename_test.rb | 6 +- test/requests/workspace_symbol_test.rb | 12 +-- test/server_test.rb | 4 +- test/type_inferrer_test.rb | 2 +- 25 files changed, 209 insertions(+), 217 deletions(-) delete mode 100644 lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb diff --git a/exe/ruby-lsp b/exe/ruby-lsp index 31e1acf69..938ffe7a0 100755 --- a/exe/ruby-lsp +++ b/exe/ruby-lsp @@ -139,9 +139,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.full_path}" + index.index_single(uri) end return end diff --git a/exe/ruby-lsp-check b/exe/ruby-lsp-check index 031342eed..0032293ea 100755 --- a/exe/ruby-lsp-check +++ b/exe/ruby-lsp-check @@ -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.full_path] = e ensure print("\033[M\033[0KIndexed #{i + 1}/#{indexables.length}") unless ENV["CI"] end diff --git a/lib/ruby_indexer/lib/ruby_indexer/configuration.rb b/lib/ruby_indexer/lib/ruby_indexer/configuration.rb index 5a9650513..294eab0ef 100644 --- a/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +++ b/lib/ruby_indexer/lib/ruby_indexer/configuration.rb @@ -152,7 +152,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, T.must(indexable.full_path), File::FNM_PATHNAME | File::FNM_EXTGLOB) end end diff --git a/lib/ruby_indexer/lib/ruby_indexer/index.rb b/lib/ruby_indexer/lib/ruby_indexer/index.rb index f46be2756..73ed455ef 100644 --- a/lib/ruby_indexer/lib/ruby_indexer/index.rb +++ b/lib/ruby_indexer/lib/ruby_indexer/index.rb @@ -35,7 +35,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[URI::Generic].new, PrefixTree[URI::Generic]) # Holds the linearized ancestors list for every namespace @ancestors = T.let({}, T::Hash[String, T::Array[String]]) @@ -55,11 +55,14 @@ def register_included_hook(module_name, &hook) (@included_hooks[module_name] ||= []) << hook end - sig { params(indexable: IndexablePath).void } - def delete(indexable) + sig { params(uri: URI::Generic).void } + def delete(uri) + full_path = uri.full_path + return unless full_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[indexable.full_path]&.each do |entry| + @files_to_entries[full_path]&.each do |entry| name = entry.name entries = @entries[name] next unless entries @@ -77,9 +80,9 @@ def delete(indexable) end end - @files_to_entries.delete(indexable.full_path) + @files_to_entries.delete(full_path) - require_path = indexable.require_path + require_path = uri.require_path @require_paths_tree.delete(require_path) if require_path end @@ -97,7 +100,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[URI::Generic]) } def search_require_paths(query) @require_paths_tree.search(query) end @@ -342,16 +345,16 @@ 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[URI::Generic], 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) # When troubleshooting an indexing issue, e.g. through irb, it's not obvious that `index_all` will augment the # existing index values, meaning it may contain 'stale' entries. This check ensures that the user is aware of this # behavior and can take appropriate action. @@ -363,21 +366,24 @@ 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 = (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, collect_comments: false) + index_single(uri, collect_comments: false) end end - sig { params(indexable_path: IndexablePath, source: T.nilable(String), collect_comments: T::Boolean).void } - def index_single(indexable_path, source = nil, collect_comments: true) - content = source || File.read(indexable_path.full_path) + 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 = source || File.read(full_path) dispatcher = Prism::Dispatcher.new result = Prism.parse(content) @@ -385,15 +391,15 @@ def index_single(indexable_path, source = nil, collect_comments: true) self, dispatcher, result, - indexable_path.full_path, + full_path, collect_comments: collect_comments, ) 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| @@ -405,7 +411,7 @@ def index_single(indexable_path, source = nil, collect_comments: true) # 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 #{T.must(full_path)}: #{e.message}" else raise end @@ -600,16 +606,19 @@ 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: URI::Generic).void } + def handle_change(uri) + full_path = uri.full_path + return unless full_path + + original_entries = @files_to_entries[full_path] - delete(indexable) - index_single(indexable) + delete(uri) + index_single(uri) - updated_entries = @files_to_entries[indexable.full_path] + updated_entries = @files_to_entries[full_path] return unless original_entries && updated_entries diff --git a/lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb b/lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb deleted file mode 100644 index 2d2b1d585..000000000 --- a/lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb +++ /dev/null @@ -1,29 +0,0 @@ -# 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 diff --git a/lib/ruby_indexer/lib/ruby_indexer/uri.rb b/lib/ruby_indexer/lib/ruby_indexer/uri.rb index 77708a1b9..e21584e72 100644 --- a/lib/ruby_indexer/lib/ruby_indexer/uri.rb +++ b/lib/ruby_indexer/lib/ruby_indexer/uri.rb @@ -45,6 +45,14 @@ def from_path(path:, fragment: nil, scheme: "file", load_path_entry: nil) sig { returns(T.nilable(String)) } attr_accessor :require_path + sig { params(load_path_entry: String).void } + def add_require_path_from_load_entry(load_path_entry) + path = to_standardized_path + return unless path + + self.require_path = path.delete_prefix("#{load_path_entry}/").delete_suffix(".rb") + end + sig { returns(T.nilable(String)) } def to_standardized_path parsed_path = path diff --git a/lib/ruby_indexer/ruby_indexer.rb b/lib/ruby_indexer/ruby_indexer.rb index 86a37ffde..926d6819c 100644 --- a/lib/ruby_indexer/ruby_indexer.rb +++ b/lib/ruby_indexer/ruby_indexer.rb @@ -5,7 +5,6 @@ require "did_you_mean" require "ruby_indexer/lib/ruby_indexer/uri" -require "ruby_indexer/lib/ruby_indexer/indexable_path" require "ruby_indexer/lib/ruby_indexer/declaration_listener" require "ruby_indexer/lib/ruby_indexer/reference_finder" require "ruby_indexer/lib/ruby_indexer/enhancement" diff --git a/lib/ruby_indexer/test/classes_and_modules_test.rb b/lib/ruby_indexer/test/classes_and_modules_test.rb index 4e3abdf20..b49112ac7 100644 --- a/lib/ruby_indexer/test/classes_and_modules_test.rb +++ b/lib/ruby_indexer/test/classes_and_modules_test.rb @@ -200,7 +200,7 @@ class Foo assert_entry("Foo", Entry::Class, "/fake/path/foo.rb:0-0:1-3") - @index.delete(IndexablePath.new(nil, "/fake/path/foo.rb")) + @index.delete(URI::Generic.from_path(path: "/fake/path/foo.rb")) refute_entry("Foo") assert_no_indexed_entries @@ -618,10 +618,12 @@ class ::Qux end def test_lazy_comment_fetching_uses_correct_line_breaks_for_rendering - path = "lib/ruby_lsp/node_context.rb" - indexable = IndexablePath.new("#{Dir.pwd}/lib", path) + uri = URI::Generic.from_path( + load_path_entry: "#{Dir.pwd}/lib", + path: "#{Dir.pwd}/lib/ruby_lsp/node_context.rb", + ) - @index.index_single(indexable, collect_comments: false) + @index.index_single(uri, collect_comments: false) entry = @index["RubyLsp::NodeContext"].first @@ -632,9 +634,12 @@ def test_lazy_comment_fetching_uses_correct_line_breaks_for_rendering end def test_lazy_comment_fetching_does_not_fail_if_file_gets_deleted - indexable = IndexablePath.new("#{Dir.pwd}/lib", "lib/ruby_lsp/does_not_exist.rb") + uri = URI::Generic.from_path( + load_path_entry: "#{Dir.pwd}/lib", + path: "#{Dir.pwd}/lib/ruby_lsp/does_not_exist.rb", + ) - @index.index_single(indexable, <<~RUBY, collect_comments: false) + @index.index_single(uri, <<~RUBY, collect_comments: false) class Foo end RUBY diff --git a/lib/ruby_indexer/test/configuration_test.rb b/lib/ruby_indexer/test/configuration_test.rb index bf0e4533e..2dd5b758e 100644 --- a/lib/ruby_indexer/test/configuration_test.rb +++ b/lib/ruby_indexer/test/configuration_test.rb @@ -13,59 +13,56 @@ def setup def test_load_configuration_executes_configure_block @config.apply_config({ "excluded_patterns" => ["**/fixtures/**/*.rb"] }) - indexables = @config.indexables + uris = @config.indexables - assert(indexables.none? { |indexable| indexable.full_path.include?("test/fixtures") }) - assert(indexables.none? { |indexable| indexable.full_path.include?("minitest-reporters") }) - assert(indexables.none? { |indexable| indexable.full_path.include?("ansi") }) - assert(indexables.any? { |indexable| indexable.full_path.include?("sorbet-runtime") }) - assert(indexables.none? { |indexable| indexable.full_path == __FILE__ }) + assert(uris.none? { |uri| uri.full_path.include?("test/fixtures") }) + assert(uris.none? { |uri| uri.full_path.include?("minitest-reporters") }) + assert(uris.none? { |uri| uri.full_path.include?("ansi") }) + assert(uris.any? { |uri| uri.full_path.include?("sorbet-runtime") }) + assert(uris.none? { |uri| uri.full_path == __FILE__ }) end def test_indexables_have_expanded_full_paths @config.apply_config({ "included_patterns" => ["**/*.rb"] }) - indexables = @config.indexables + uris = @config.indexables # All paths should be expanded - assert(indexables.all? { |indexable| File.absolute_path?(indexable.full_path) }) + assert(uris.all? { |uri| File.absolute_path?(uri.full_path) }) end def test_indexables_only_includes_gem_require_paths - indexables = @config.indexables + uris = @config.indexables Bundler.locked_gems.specs.each do |lazy_spec| next if lazy_spec.name == "ruby-lsp" spec = Gem::Specification.find_by_name(lazy_spec.name) - assert(indexables.none? { |indexable| indexable.full_path.start_with?("#{spec.full_gem_path}/test/") }) + assert(uris.none? { |uri| uri.full_path.start_with?("#{spec.full_gem_path}/test/") }) rescue Gem::MissingSpecError # Transitive dependencies might be missing when running tests on Windows end end def test_indexables_does_not_include_default_gem_path_when_in_bundle - indexables = @config.indexables - - assert( - indexables.none? { |indexable| indexable.full_path.start_with?("#{RbConfig::CONFIG["rubylibdir"]}/psych") }, - ) + uris = @config.indexables + assert(uris.none? { |uri| uri.full_path.start_with?("#{RbConfig::CONFIG["rubylibdir"]}/psych") }) end def test_indexables_includes_default_gems - indexables = @config.indexables.map(&:full_path) + paths = @config.indexables.map(&:full_path) - assert_includes(indexables, "#{RbConfig::CONFIG["rubylibdir"]}/pathname.rb") - assert_includes(indexables, "#{RbConfig::CONFIG["rubylibdir"]}/ipaddr.rb") - assert_includes(indexables, "#{RbConfig::CONFIG["rubylibdir"]}/erb.rb") + assert_includes(paths, "#{RbConfig::CONFIG["rubylibdir"]}/pathname.rb") + assert_includes(paths, "#{RbConfig::CONFIG["rubylibdir"]}/ipaddr.rb") + assert_includes(paths, "#{RbConfig::CONFIG["rubylibdir"]}/erb.rb") end def test_indexables_includes_project_files - indexables = @config.indexables.map(&:full_path) + paths = @config.indexables.map(&:full_path) Dir.glob("#{Dir.pwd}/lib/**/*.rb").each do |path| next if path.end_with?("_test.rb") - assert_includes(indexables, path) + assert_includes(paths, path) end end @@ -78,9 +75,9 @@ def test_indexables_avoids_duplicates_if_bundle_path_is_inside_project end def test_indexables_does_not_include_gems_own_installed_files - indexables = @config.indexables - indexables_inside_bundled_lsp = indexables.select do |indexable| - indexable.full_path.start_with?(Bundler.bundle_path.join("gems", "ruby-lsp").to_s) + uris = @config.indexables + indexables_inside_bundled_lsp = uris.select do |uri| + uri.full_path.start_with?(Bundler.bundle_path.join("gems", "ruby-lsp").to_s) end assert_empty( @@ -93,17 +90,16 @@ def test_indexables_does_not_include_gems_own_installed_files def test_indexables_does_not_include_non_ruby_files_inside_rubylibdir path = Pathname.new(RbConfig::CONFIG["rubylibdir"]).join("extra_file.txt").to_s FileUtils.touch(path) - indexables = @config.indexables - assert(indexables.none? { |indexable| indexable.full_path == path }) + uris = @config.indexables + assert(uris.none? { |uri| uri.full_path == path }) ensure FileUtils.rm(T.must(path)) end def test_paths_are_unique - indexables = @config.indexables - - assert_equal(indexables.uniq.length, indexables.length) + uris = @config.indexables + assert_equal(uris.uniq.length, uris.length) end def test_configuration_raises_for_unknown_keys @@ -141,9 +137,9 @@ def test_indexables_respect_given_workspace_path @config.apply_config({ "excluded_patterns" => ["ignore/**/*.rb"] }) @config.workspace_path = dir - indexables = @config.indexables - assert(indexables.none? { |indexable| indexable.full_path.start_with?(File.join(dir, "ignore")) }) + uris = @config.indexables + assert(uris.none? { |uri| uri.full_path.start_with?(File.join(dir, "ignore")) }) # After switching the workspace path, all indexables will be found in one of these places: # - The new workspace path @@ -151,11 +147,11 @@ def test_indexables_respect_given_workspace_path # - Bundled gems # - Default gems assert( - indexables.all? do |i| - i.full_path.start_with?(dir) || - i.full_path.start_with?(File.join(Dir.pwd, "lib")) || - i.full_path.start_with?(Bundler.bundle_path.to_s) || - i.full_path.start_with?(RbConfig::CONFIG["rubylibdir"]) + uris.all? do |u| + u.full_path.start_with?(dir) || + u.full_path.start_with?(File.join(Dir.pwd, "lib")) || + u.full_path.start_with?(Bundler.bundle_path.to_s) || + u.full_path.start_with?(RbConfig::CONFIG["rubylibdir"]) end, ) end @@ -166,8 +162,8 @@ def test_includes_top_level_files FileUtils.touch(File.join(dir, "find_me.rb")) @config.workspace_path = dir - indexables = @config.indexables - assert(indexables.find { |i| File.basename(i.full_path) == "find_me.rb" }) + uris = @config.indexables + assert(uris.find { |u| File.basename(u.full_path) == "find_me.rb" }) end end end diff --git a/lib/ruby_indexer/test/index_test.rb b/lib/ruby_indexer/test/index_test.rb index 69b5b68af..96bf768b2 100644 --- a/lib/ruby_indexer/test/index_test.rb +++ b/lib/ruby_indexer/test/index_test.rb @@ -6,11 +6,11 @@ module RubyIndexer class IndexTest < TestCase def test_deleting_one_entry_for_a_class - @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY) class Foo end RUBY - @index.index_single(IndexablePath.new(nil, "/fake/path/other_foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/other_foo.rb"), <<~RUBY) class Foo end RUBY @@ -18,13 +18,13 @@ class Foo entries = @index["Foo"] assert_equal(2, entries.length) - @index.delete(IndexablePath.new(nil, "/fake/path/other_foo.rb")) + @index.delete(URI::Generic.from_path(path: "/fake/path/other_foo.rb")) entries = @index["Foo"] assert_equal(1, entries.length) end def test_deleting_all_entries_for_a_class - @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY) class Foo end RUBY @@ -32,13 +32,13 @@ class Foo entries = @index["Foo"] assert_equal(1, entries.length) - @index.delete(IndexablePath.new(nil, "/fake/path/foo.rb")) + @index.delete(URI::Generic.from_path(path: "/fake/path/foo.rb")) entries = @index["Foo"] assert_nil(entries) end def test_index_resolve - @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY) class Bar; end module Foo @@ -72,7 +72,7 @@ class Something end def test_accessing_with_colon_colon_prefix - @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY) class Bar; end module Foo @@ -92,7 +92,7 @@ class Something end def test_fuzzy_search - @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY) class Zws; end module Qtl @@ -120,18 +120,22 @@ class Something end def test_index_single_ignores_directories - FileUtils.mkdir("lib/this_is_a_dir.rb") - @index.index_single(IndexablePath.new(nil, "lib/this_is_a_dir.rb")) - ensure - FileUtils.rm_r("lib/this_is_a_dir.rb") + path = "#{Dir.pwd}/lib/this_is_a_dir.rb" + FileUtils.mkdir(path) + + begin + @index.index_single(URI::Generic.from_path(path: path)) + ensure + FileUtils.rm_r(path) + end end def test_searching_for_require_paths - @index.index_single(IndexablePath.new("/fake", "/fake/path/foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb", load_path_entry: "/fake"), <<~RUBY) class Foo end RUBY - @index.index_single(IndexablePath.new("/fake", "/fake/path/other_foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/other_foo.rb", load_path_entry: "/fake"), <<~RUBY) class Foo end RUBY @@ -140,11 +144,11 @@ class Foo end def test_searching_for_entries_based_on_prefix - @index.index_single(IndexablePath.new("/fake", "/fake/path/foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb", load_path_entry: "/fake"), <<~RUBY) class Foo::Bizw end RUBY - @index.index_single(IndexablePath.new("/fake", "/fake/path/other_foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/other_foo.rb", load_path_entry: "/fake"), <<~RUBY) class Foo::Bizw end @@ -160,7 +164,7 @@ class Foo::Bizt end def test_resolve_normalizes_top_level_names - @index.index_single(IndexablePath.new("/fake", "/fake/path/foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb", load_path_entry: "/fake"), <<~RUBY) class Bar; end module Foo @@ -180,7 +184,7 @@ class Bar; end end def test_resolving_aliases_to_non_existing_constants_with_conflicting_names - @index.index_single(IndexablePath.new("/fake", "/fake/path/foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb", load_path_entry: "/fake"), <<~RUBY) class Bar end @@ -343,18 +347,18 @@ def test_indexing_prism_fixtures_succeeds raise "Prism fixtures not found. Run `git submodule update --init` to fetch them." end - fixtures = Dir.glob("test/fixtures/prism/test/prism/fixtures/**/*.txt") + fixtures = Dir.glob("#{Dir.pwd}/test/fixtures/prism/test/prism/fixtures/**/*.txt") fixtures.each do |fixture| - indexable_path = IndexablePath.new("", fixture) - @index.index_single(indexable_path) + uri = URI::Generic.from_path(path: fixture) + @index.index_single(uri) end refute_empty(@index) end def test_index_single_does_not_fail_for_non_existing_file - @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb")) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb")) entries_after_indexing = @index.names assert_equal(@default_indexed_entries.keys, entries_after_indexing) end @@ -782,8 +786,8 @@ class Bar end RUBY - indexable_path = IndexablePath.new(nil, File.join(dir, "foo.rb")) - @index.index_single(indexable_path) + uri = URI::Generic.from_path(path: File.join(dir, "foo.rb")) + @index.index_single(uri) assert_equal(["Bar", "Foo", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Bar")) @@ -796,7 +800,7 @@ class Bar end RUBY - @index.handle_change(indexable_path) + @index.handle_change(uri) assert_empty(@index.instance_variable_get(:@ancestors)) assert_equal(["Bar", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Bar")) end @@ -816,8 +820,8 @@ class Bar end RUBY - indexable_path = IndexablePath.new(nil, File.join(dir, "foo.rb")) - @index.index_single(indexable_path) + uri = URI::Generic.from_path(path: File.join(dir, "foo.rb")) + @index.index_single(uri) assert_equal(["Bar", "Foo", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Bar")) @@ -833,7 +837,7 @@ def baz; end end RUBY - @index.handle_change(indexable_path) + @index.handle_change(uri) refute_empty(@index.instance_variable_get(:@ancestors)) assert_equal(["Bar", "Foo", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Bar")) end @@ -852,8 +856,8 @@ class Bar < Foo end RUBY - indexable_path = IndexablePath.new(nil, File.join(dir, "foo.rb")) - @index.index_single(indexable_path) + uri = URI::Generic.from_path(path: File.join(dir, "foo.rb")) + @index.index_single(uri) assert_equal(["Bar", "Foo", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Bar")) @@ -866,7 +870,7 @@ class Bar end RUBY - @index.handle_change(indexable_path) + @index.handle_change(uri) assert_empty(@index.instance_variable_get(:@ancestors)) assert_equal(["Bar", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Bar")) end @@ -1300,7 +1304,7 @@ class Bar < ::BasicObject; end end def test_resolving_method_inside_singleton_context - @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY) module Foo class Bar class << self @@ -1321,7 +1325,7 @@ def found_me!; end end def test_resolving_constants_in_singleton_contexts - @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY) module Foo class Bar CONST = 3 @@ -1346,7 +1350,7 @@ class << self end def test_resolving_instance_variables_in_singleton_contexts - @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY) module Foo class Bar @a = 123 @@ -1376,7 +1380,7 @@ def hello end def test_instance_variable_completion_in_singleton_contexts - @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY) module Foo class Bar @a = 123 @@ -1622,7 +1626,7 @@ def bar end def test_linearizing_singleton_ancestors_of_singleton_when_class_has_parent - @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY) class Foo; end class Bar < Foo @@ -1673,7 +1677,7 @@ def test_linearizing_singleton_object end def test_extend_self - @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY) module Foo def bar end @@ -1705,7 +1709,7 @@ def baz end def test_linearizing_singleton_ancestors - @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY) module First end @@ -1746,7 +1750,7 @@ class << self end def test_linearizing_singleton_ancestors_when_class_has_parent - @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY) class Foo; end class Bar < Foo @@ -1776,7 +1780,7 @@ class << self end def test_linearizing_a_module_singleton_class - @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY) module A; end RUBY diff --git a/lib/ruby_indexer/test/reference_finder_test.rb b/lib/ruby_indexer/test/reference_finder_test.rb index 0d4627a8f..7f673a85e 100644 --- a/lib/ruby_indexer/test/reference_finder_test.rb +++ b/lib/ruby_indexer/test/reference_finder_test.rb @@ -231,7 +231,7 @@ def find_method_references(method_name, source) def find_references(target, source) file_path = "/fake.rb" index = Index.new - index.index_single(IndexablePath.new(nil, file_path), source) + index.index_single(URI::Generic.from_path(path: file_path), source) parse_result = Prism.parse(source) dispatcher = Prism::Dispatcher.new finder = ReferenceFinder.new(target, index, dispatcher) diff --git a/lib/ruby_indexer/test/test_case.rb b/lib/ruby_indexer/test/test_case.rb index 88e284300..e949d34e8 100644 --- a/lib/ruby_indexer/test/test_case.rb +++ b/lib/ruby_indexer/test/test_case.rb @@ -14,7 +14,7 @@ def setup private def index(source) - @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), source) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), source) end def assert_entry(expected_name, type, expected_location, visibility: nil) diff --git a/lib/ruby_indexer/test/uri_test.rb b/lib/ruby_indexer/test/uri_test.rb index 0427ef6c5..be9c0af71 100644 --- a/lib/ruby_indexer/test/uri_test.rb +++ b/lib/ruby_indexer/test/uri_test.rb @@ -60,5 +60,13 @@ def test_from_path_computes_require_path_when_load_path_entry_is_given uri = URI::Generic.from_path(path: "/some/unix/path/to/file.rb", load_path_entry: "/some/unix/path") assert_equal("to/file", uri.require_path) end + + def test_allows_adding_require_path_with_load_path_entry + uri = URI::Generic.from_path(path: "/some/unix/path/to/file.rb") + assert_nil(uri.require_path) + + uri.add_require_path_from_load_entry("/some/unix/path") + assert_equal("to/file", uri.require_path) + end end end diff --git a/lib/ruby_lsp/listeners/completion.rb b/lib/ruby_lsp/listeners/completion.rb index ea4c71fe2..da976619d 100644 --- a/lib/ruby_lsp/listeners/completion.rb +++ b/lib/ruby_lsp/listeners/completion.rb @@ -368,9 +368,9 @@ def complete_require(node) return unless path_node_to_complete.is_a?(Prism::StringNode) - matched_indexable_paths = @index.search_require_paths(path_node_to_complete.content) + matched_uris = @index.search_require_paths(path_node_to_complete.content) - matched_indexable_paths.map!(&:require_path).sort!.each do |path| + matched_uris.map!(&:require_path).sort!.each do |path| @response_builder << build_completion(T.must(path), path_node_to_complete) end end diff --git a/lib/ruby_lsp/listeners/definition.rb b/lib/ruby_lsp/listeners/definition.rb index 6413cdabe..752362092 100644 --- a/lib/ruby_lsp/listeners/definition.rb +++ b/lib/ruby_lsp/listeners/definition.rb @@ -290,20 +290,22 @@ def handle_method_definition(message, receiver_type, inherited_only: false) def handle_require_definition(node, message) case message when :require - entry = @index.search_require_paths(node.content).find do |indexable_path| - indexable_path.require_path == node.content + entry = @index.search_require_paths(node.content).find do |uri| + uri.require_path == node.content end if entry candidate = entry.full_path - @response_builder << Interface::Location.new( - uri: URI::Generic.from_path(path: candidate).to_s, - range: Interface::Range.new( - start: Interface::Position.new(line: 0, character: 0), - end: Interface::Position.new(line: 0, character: 0), - ), - ) + if candidate + @response_builder << Interface::Location.new( + uri: URI::Generic.from_path(path: candidate).to_s, + range: Interface::Range.new( + start: Interface::Position.new(line: 0, character: 0), + end: Interface::Position.new(line: 0, character: 0), + ), + ) + end end when :require_relative required_file = "#{node.content}.rb" diff --git a/lib/ruby_lsp/server.rb b/lib/ruby_lsp/server.rb index 5f939190c..6d6a7b97f 100644 --- a/lib/ruby_lsp/server.rb +++ b/lib/ruby_lsp/server.rb @@ -981,15 +981,15 @@ def workspace_did_change_watched_files(message) next unless file_path.end_with?(".rb") load_path_entry = $LOAD_PATH.find { |load_path| file_path.start_with?(load_path) } - indexable = RubyIndexer::IndexablePath.new(load_path_entry, file_path) + uri.add_require_path_from_load_entry(load_path_entry) if load_path_entry case change[:type] when Constant::FileChangeType::CREATED - index.index_single(indexable) + index.index_single(uri) when Constant::FileChangeType::CHANGED - index.handle_change(indexable) + index.handle_change(uri) when Constant::FileChangeType::DELETED - index.delete(indexable) + index.delete(uri) end end diff --git a/lib/ruby_lsp/test_helper.rb b/lib/ruby_lsp/test_helper.rb index fb21805ac..ea91a4b34 100644 --- a/lib/ruby_lsp/test_helper.rb +++ b/lib/ruby_lsp/test_helper.rb @@ -39,7 +39,7 @@ def with_server(source = nil, uri = Kernel.URI("file:///fake.rb"), stub_no_typec end server.global_state.index.index_single( - RubyIndexer::IndexablePath.new(nil, T.must(uri.to_standardized_path)), + URI::Generic.from_path(path: T.must(uri.to_standardized_path)), source, ) server.load_addons(include_project_addons: false) if load_addons diff --git a/rakelib/index.rake b/rakelib/index.rake index 27e00d07a..6b715d904 100644 --- a/rakelib/index.rake +++ b/rakelib/index.rake @@ -85,7 +85,7 @@ task "index:topgems": ["download:topgems"] do errors = Dir[File.join(directory, "**", "*.rb")].filter_map do |filepath| print(".") code = File.read(filepath) - index.index_single(RubyIndexer::IndexablePath.new(nil, filepath), code) + index.index_single(URI::Generic.from_path(path: filepath), code) nil rescue => e errors << { message: e.message, file: filepath } diff --git a/test/requests/completion_test.rb b/test/requests/completion_test.rb index 245bc850b..ac441e569 100644 --- a/test/requests/completion_test.rb +++ b/test/requests/completion_test.rb @@ -1596,14 +1596,11 @@ def with_file_structure(server, &block) ]) index = server.global_state.index - indexables = Dir.glob(File.join(tmpdir, "**", "*.rb")).map! do |path| - RubyIndexer::IndexablePath.new(tmpdir, path) - end - - indexables.each do |indexable| - index.index_single(indexable) + uris = Dir.glob(File.join(tmpdir, "**", "*.rb")).map! do |path| + URI::Generic.from_path(load_path_entry: tmpdir, path: path) end + uris.each { |uri| index.index_single(uri) } block.call(tmpdir) ensure $LOAD_PATH.delete(tmpdir) diff --git a/test/requests/definition_expectations_test.rb b/test/requests/definition_expectations_test.rb index d24d178c5..340153760 100644 --- a/test/requests/definition_expectations_test.rb +++ b/test/requests/definition_expectations_test.rb @@ -15,27 +15,26 @@ def run_expectations(source) index = server.global_state.index index.index_single( - RubyIndexer::IndexablePath.new( - "#{Dir.pwd}/lib", - File.expand_path( + URI::Generic.from_path( + load_path_entry: "#{Dir.pwd}/lib", + path: File.expand_path( "../../test/fixtures/class_reference_target.rb", __dir__, ), ), ) index.index_single( - RubyIndexer::IndexablePath.new( - nil, - File.expand_path( + URI::Generic.from_path( + path: File.expand_path( "../../test/fixtures/constant_reference_target.rb", __dir__, ), ), ) index.index_single( - RubyIndexer::IndexablePath.new( - "#{Dir.pwd}/lib", - File.expand_path( + URI::Generic.from_path( + load_path_entry: "#{Dir.pwd}/lib", + path: File.expand_path( "../../lib/ruby_lsp/server.rb", __dir__, ), @@ -76,12 +75,7 @@ def run_expectations(source) def test_jumping_to_default_gems with_server("Pathname") do |server, uri| index = server.global_state.index - index.index_single( - RubyIndexer::IndexablePath.new( - nil, - "#{RbConfig::CONFIG["rubylibdir"]}/pathname.rb", - ), - ) + index.index_single(URI::Generic.from_path(path: "#{RbConfig::CONFIG["rubylibdir"]}/pathname.rb")) server.process_message( id: 1, method: "textDocument/definition", @@ -165,15 +159,14 @@ def test_jumping_to_default_require_of_a_gem with_server("require \"bundler\"") do |server, uri| index = server.global_state.index - bundler_uri = URI::Generic.from_path(path: "#{RbConfig::CONFIG["rubylibdir"]}/bundler.rb") - index.index_single( - RubyIndexer::IndexablePath.new(RbConfig::CONFIG["rubylibdir"], T.must(bundler_uri.to_standardized_path)), + bundler_uri = URI::Generic.from_path( + path: "#{RbConfig::CONFIG["rubylibdir"]}/bundler.rb", + load_path_entry: RbConfig::CONFIG["rubylibdir"], ) + index.index_single(bundler_uri) Dir.glob("#{RbConfig::CONFIG["rubylibdir"]}/bundler/*.rb").each do |path| - index.index_single( - RubyIndexer::IndexablePath.new(RbConfig::CONFIG["rubylibdir"], path), - ) + index.index_single(URI::Generic.from_path(load_path_entry: RbConfig::CONFIG["rubylibdir"], path: path)) end server.process_message( @@ -238,9 +231,9 @@ def test_definition_addons with_server(source, stub_no_typechecker: true) do |server, uri| server.global_state.index.index_single( - RubyIndexer::IndexablePath.new( - "#{Dir.pwd}/lib", - File.expand_path( + URI::Generic.from_path( + load_path_entry: "#{Dir.pwd}/lib", + path: File.expand_path( "../../test/fixtures/class_reference_target.rb", __dir__, ), @@ -319,7 +312,7 @@ def foo; end }, }) index = server.global_state.index - index.index_single(RubyIndexer::IndexablePath.new(nil, T.must(second_uri.to_standardized_path)), second_source) + index.index_single(URI::Generic.from_path(path: T.must(second_uri.to_standardized_path)), second_source) server.process_message( id: 1, @@ -388,12 +381,12 @@ class Foo with_server(source) do |server, uri| server.global_state.index.index_single( - RubyIndexer::IndexablePath.new(nil, "/fake/path/bar.rb"), <<~RUBY + URI::Generic.from_path(path: "/fake/path/bar.rb"), <<~RUBY class Foo::Bar; end RUBY ) server.global_state.index.index_single( - RubyIndexer::IndexablePath.new(nil, "/fake/path/baz.rb"), <<~RUBY + URI::Generic.from_path(path: "/fake/path/baz.rb"), <<~RUBY class Foo::Bar; end RUBY ) @@ -593,7 +586,7 @@ def test_definitions_are_listed_in_erb_files_as_unknown_receiver with_server(source, URI("/fake.erb")) do |server, uri| server.global_state.index.index_single( - RubyIndexer::IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY + URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY class Bar def foo; end diff --git a/test/requests/references_test.rb b/test/requests/references_test.rb index 859d2ce7e..beff7c772 100644 --- a/test/requests/references_test.rb +++ b/test/requests/references_test.rb @@ -18,7 +18,7 @@ def find_references(fixture_path, position) source = File.read(fixture_path) path = File.expand_path(fixture_path) global_state = RubyLsp::GlobalState.new - global_state.index.index_single(RubyIndexer::IndexablePath.new(nil, path), source) + global_state.index.index_single(URI::Generic.from_path(path: path), source) store = RubyLsp::Store.new document = RubyLsp::RubyDocument.new(source: source, version: 1, uri: URI::Generic.from_path(path: path)) diff --git a/test/requests/rename_test.rb b/test/requests/rename_test.rb index 643c926b4..5d115ad8e 100644 --- a/test/requests/rename_test.rb +++ b/test/requests/rename_test.rb @@ -35,8 +35,8 @@ def test_renaming_conflict }, }) path = File.expand_path(fixture_path) - global_state.index.index_single(RubyIndexer::IndexablePath.new(nil, path), source) - global_state.index.index_single(RubyIndexer::IndexablePath.new(nil, "/fake.rb"), <<~RUBY) + global_state.index.index_single(URI::Generic.from_path(path: path), source) + global_state.index.index_single(URI::Generic.from_path(path: "/fake.rb"), <<~RUBY) class Conflicting end RUBY @@ -69,7 +69,7 @@ def expect_renames(fixture_path, new_fixture_path, expected, position, new_name) }, }) path = File.expand_path(fixture_path) - global_state.index.index_single(RubyIndexer::IndexablePath.new(nil, path), source) + global_state.index.index_single(URI::Generic.from_path(path: path), source) store = RubyLsp::Store.new document = RubyLsp::RubyDocument.new(source: source, version: 1, uri: URI::Generic.from_path(path: path)) diff --git a/test/requests/workspace_symbol_test.rb b/test/requests/workspace_symbol_test.rb index 76bf165a2..7e9441b99 100644 --- a/test/requests/workspace_symbol_test.rb +++ b/test/requests/workspace_symbol_test.rb @@ -11,7 +11,7 @@ def setup end def test_returns_index_entries_based_on_query - @index.index_single(RubyIndexer::IndexablePath.new(nil, "/fake.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake.rb"), <<~RUBY) class Foo; end module Bar; end @@ -32,7 +32,7 @@ module Bar; end end def test_fuzzy_matches_symbols - @index.index_single(RubyIndexer::IndexablePath.new(nil, "/fake.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake.rb"), <<~RUBY) class Foo; end module Bar; end @@ -53,7 +53,7 @@ module Bar; end end def test_symbols_include_container_name - @index.index_single(RubyIndexer::IndexablePath.new(nil, "/fake.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake.rb"), <<~RUBY) module Foo class Bar; end end @@ -66,14 +66,14 @@ class Bar; end end def test_does_not_include_symbols_from_dependencies - @index.index_single(RubyIndexer::IndexablePath.new(nil, "#{RbConfig::CONFIG["rubylibdir"]}/pathname.rb")) + @index.index_single(URI::Generic.from_path(path: "#{RbConfig::CONFIG["rubylibdir"]}/pathname.rb")) result = RubyLsp::Requests::WorkspaceSymbol.new(@global_state, "Pathname").perform assert_empty(result) end def test_does_not_include_private_constants - @index.index_single(RubyIndexer::IndexablePath.new(nil, "/fake.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake.rb"), <<~RUBY) class Foo CONSTANT = 1 private_constant(:CONSTANT) @@ -86,7 +86,7 @@ class Foo end def test_returns_method_symbols - @index.index_single(RubyIndexer::IndexablePath.new(nil, "/fake.rb"), <<~RUBY) + @index.index_single(URI::Generic.from_path(path: "/fake.rb"), <<~RUBY) class Foo attr_reader :baz diff --git a/test/server_test.rb b/test/server_test.rb index bbeed3e0b..db5b95485 100644 --- a/test/server_test.rb +++ b/test/server_test.rb @@ -439,8 +439,8 @@ def test_backtrace_is_printed_to_stderr_on_exceptions end def test_changed_file_only_indexes_ruby - @server.global_state.index.expects(:index_single).once.with do |indexable| - indexable.full_path == "/foo.rb" + @server.global_state.index.expects(:index_single).once.with do |uri| + uri.full_path == "/foo.rb" end @server.process_message({ method: "workspace/didChangeWatchedFiles", diff --git a/test/type_inferrer_test.rb b/test/type_inferrer_test.rb index 123786099..cb0651df6 100644 --- a/test/type_inferrer_test.rb +++ b/test/type_inferrer_test.rb @@ -406,7 +406,7 @@ def self.foo private def index_and_locate(source, position) - @index.index_single(RubyIndexer::IndexablePath.new(nil, "/fake/path/foo.rb"), source) + @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), source) document = RubyLsp::RubyDocument.new( source: source, version: 1,