diff --git a/lib/ruby_indexer/lib/ruby_indexer/configuration.rb b/lib/ruby_indexer/lib/ruby_indexer/configuration.rb index 368c9177c5..36baff2858 100644 --- a/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +++ b/lib/ruby_indexer/lib/ruby_indexer/configuration.rb @@ -24,11 +24,11 @@ def initialize @excluded_gems = T.let(excluded_gem_names, T::Array[String]) @included_gems = T.let([], T::Array[String]) - @excluded_patterns = T.let(["**/*_test.rb"], T::Array[String]) + @excluded_patterns = T.let([File.join("**", "*_test.rb")], T::Array[String]) path = Bundler.settings["path"] - @excluded_patterns << "#{File.expand_path(path, Dir.pwd)}/**/*.rb" if path + @excluded_patterns << File.join(File.expand_path(path, Dir.pwd), "**", "*.rb") if path - @included_patterns = T.let(["#{Dir.pwd}/**/*.rb"], T::Array[String]) + @included_patterns = T.let([File.join(Dir.pwd, "**", "*.rb")], T::Array[String]) @excluded_magic_comments = T.let( [ "frozen_string_literal:", @@ -61,8 +61,8 @@ def load_config raise e, "Syntax error while loading .index.yml configuration: #{e.message}" end - sig { returns(T::Array[String]) } - def files_to_index + sig { returns(T::Array[Indexable]) } + def indexables excluded_gems = @excluded_gems - @included_gems locked_gems = Bundler.locked_gems&.specs @@ -70,19 +70,22 @@ def files_to_index # having duplicates if BUNDLE_PATH is set to a folder inside the project structure # Add user specified patterns - files_to_index = @included_patterns.flat_map do |pattern| - Dir.glob(pattern, File::FNM_PATHNAME | File::FNM_EXTGLOB) + indexables = @included_patterns.flat_map do |pattern| + Dir.glob(pattern, File::FNM_PATHNAME | File::FNM_EXTGLOB).map! do |path| + base_path = $LOAD_PATH.find { |load_path| path.start_with?(load_path) } + Indexable.new(base_path, path) + end end # Remove user specified patterns - files_to_index.reject! do |path| + indexables.reject! do |indexable| @excluded_patterns.any? do |pattern| - File.fnmatch?(pattern, path, File::FNM_PATHNAME | File::FNM_EXTGLOB) + File.fnmatch?(pattern, indexable.full_path, File::FNM_PATHNAME | File::FNM_EXTGLOB) end end # Add default gems to the list of files to be indexed - Dir.glob("#{RbConfig::CONFIG["rubylibdir"]}/*").each do |default_path| + Dir.glob(File.join(RbConfig::CONFIG["rubylibdir"], "*")).each do |default_path| # The default_path might be a Ruby file or a folder with the gem's name. For example: # bundler/ # bundler.rb @@ -103,10 +106,14 @@ def files_to_index if pathname.directory? # If the default_path is a directory, we index all the Ruby files in it - files_to_index.concat(Dir.glob("#{default_path}/**/*.rb", File::FNM_PATHNAME | File::FNM_EXTGLOB)) + indexables.concat( + Dir.glob(File.join(default_path, "**", "*.rb"), File::FNM_PATHNAME | File::FNM_EXTGLOB).map! do |path| + Indexable.new(RbConfig::CONFIG["rubylibdir"], path) + end, + ) else # If the default_path is a Ruby file, we index it - files_to_index << default_path + indexables << Indexable.new(RbConfig::CONFIG["rubylibdir"], default_path) end end @@ -121,15 +128,20 @@ def files_to_index # duplicates or accidentally ignoring exclude patterns next if spec.full_gem_path == Dir.pwd - files_to_index.concat(Dir.glob("#{spec.full_gem_path}/{#{spec.require_paths.join(",")}}/**/*.rb")) + indexables.concat( + spec.require_paths.flat_map do |require_path| + base_path = File.join(spec.full_gem_path, require_path) + Dir.glob(File.join(base_path, "**", "*.rb")).map! { |path| Indexable.new(base_path, path) } + end, + ) rescue Gem::MissingSpecError # If a gem is scoped only to some specific platform, then its dependencies may not be installed either, but they # are still listed in locked_gems. We can't index them because they are not installed for the platform, so we # just ignore if they're missing end - files_to_index.uniq! - files_to_index + indexables.uniq! + indexables end sig { returns(Regexp) } diff --git a/lib/ruby_indexer/test/configuration_test.rb b/lib/ruby_indexer/test/configuration_test.rb index 1aba2dd70b..f0005f9d6f 100644 --- a/lib/ruby_indexer/test/configuration_test.rb +++ b/lib/ruby_indexer/test/configuration_test.rb @@ -11,55 +11,57 @@ def setup def test_load_configuration_executes_configure_block @config.load_config - files_to_index = @config.files_to_index + indexables = @config.indexables - assert(files_to_index.none? { |path| path.include?("test/fixtures") }) - assert(files_to_index.none? { |path| path.include?("minitest-reporters") }) - assert(files_to_index.none? { |path| path == __FILE__ }) + 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 == __FILE__ }) end - def test_files_to_index_only_includes_gem_require_paths + def test_indexables_only_includes_gem_require_paths @config.load_config - files_to_index = @config.files_to_index + indexables = @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(files_to_index.none? { |path| path.start_with?("#{spec.full_gem_path}/test/") }) + assert(indexables.none? { |indexable| indexable.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_files_to_index_does_not_include_default_gem_path_when_in_bundle + def test_indexables_does_not_include_default_gem_path_when_in_bundle @config.load_config - files_to_index = @config.files_to_index + indexables = @config.indexables - assert(files_to_index.none? { |path| path.start_with?("#{RbConfig::CONFIG["rubylibdir"]}/psych") }) + assert( + indexables.none? { |indexable| indexable.full_path.start_with?("#{RbConfig::CONFIG["rubylibdir"]}/psych") }, + ) end - def test_files_to_index_includes_default_gems + def test_indexables_includes_default_gems @config.load_config - files_to_index = @config.files_to_index + indexables = @config.indexables.map(&:full_path) - assert_includes(files_to_index, "#{RbConfig::CONFIG["rubylibdir"]}/pathname.rb") - assert_includes(files_to_index, "#{RbConfig::CONFIG["rubylibdir"]}/ipaddr.rb") - assert_includes(files_to_index, "#{RbConfig::CONFIG["rubylibdir"]}/abbrev.rb") + assert_includes(indexables, "#{RbConfig::CONFIG["rubylibdir"]}/pathname.rb") + assert_includes(indexables, "#{RbConfig::CONFIG["rubylibdir"]}/ipaddr.rb") + assert_includes(indexables, "#{RbConfig::CONFIG["rubylibdir"]}/abbrev.rb") end - def test_files_to_index_includes_project_files + def test_indexables_includes_project_files @config.load_config - files_to_index = @config.files_to_index + indexables = @config.indexables.map(&:full_path) Dir.glob("#{Dir.pwd}/lib/**/*.rb").each do |path| next if path.end_with?("_test.rb") - assert_includes(files_to_index, path) + assert_includes(indexables, path) end end - def test_files_to_index_avoids_duplicates_if_bundle_path_is_inside_project + def test_indexables_avoids_duplicates_if_bundle_path_is_inside_project Bundler.settings.set_global("path", "vendor/bundle") config = Configuration.new config.load_config @@ -69,18 +71,22 @@ def test_files_to_index_avoids_duplicates_if_bundle_path_is_inside_project Bundler.settings.set_global("path", nil) end - def test_files_to_index_does_not_include_gems_own_installed_files + def test_indexables_does_not_include_gems_own_installed_files @config.load_config - files_to_index = @config.files_to_index + indexables = @config.indexables - assert(files_to_index.none? { |path| path.start_with?(Bundler.bundle_path.join("gems", "ruby-lsp").to_s) }) + assert( + indexables.none? do |indexable| + indexable.full_path.start_with?(Bundler.bundle_path.join("gems", "ruby-lsp").to_s) + end, + ) end def test_paths_are_unique @config.load_config - files_to_index = @config.files_to_index + indexables = @config.indexables - assert_equal(files_to_index.uniq.length, files_to_index.length) + assert_equal(indexables.uniq.length, indexables.length) end def test_configuration_raises_for_unknown_keys