class RubyIndexer::Configuration
def indexable_uris
def indexable_uris excluded_gems = @excluded_gems - @included_gems locked_gems = Bundler.locked_gems&.specs # NOTE: indexing the patterns (both included and excluded) needs to happen before indexing gems, otherwise we risk # having duplicates if BUNDLE_PATH is set to a folder inside the project structure flags = File::FNM_PATHNAME | File::FNM_EXTGLOB uris = @included_patterns.flat_map do |pattern| load_path_entry = nil #: String? Dir.glob(File.join(@workspace_path, pattern), flags).map! do |path| # All entries for the same pattern match the same $LOAD_PATH entry. Since searching the $LOAD_PATH for every # entry is expensive, we memoize it until we find a path that doesn't belong to that $LOAD_PATH. This happens # on repositories that define multiple gems, like Rails. All frameworks are defined inside the current # workspace directory, but each one of them belongs to a different $LOAD_PATH entry if load_path_entry.nil? || !path.start_with?(load_path_entry) load_path_entry = $LOAD_PATH.find { |load_path| path.start_with?(load_path) } end URI::Generic.from_path(path: path, load_path_entry: load_path_entry) end end # If the patterns are relative, we make it relative to the workspace path. If they are absolute, then we shouldn't # concatenate anything excluded_patterns = @excluded_patterns.map do |pattern| if File.absolute_path?(pattern) pattern else File.join(@workspace_path, pattern) end end # Remove user specified patterns bundle_path = Bundler.settings["path"]&.gsub(/[\\]+/, "/") uris.reject! do |indexable| path = indexable.full_path #: as !nil next false if test_files_ignored_from_exclusion?(path, bundle_path) excluded_patterns.any? { |pattern| File.fnmatch?(pattern, path, flags) } end # Add default gems to the list of files to be indexed 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 # psych/ # psych.rb pathname = Pathname.new(default_path) short_name = pathname.basename.to_s.delete_suffix(".rb") # If the gem name is excluded, then we skip it next if excluded_gems.include?(short_name) # If the default gem is also a part of the bundle, we skip indexing the default one and index only the one in # the bundle, which won't be in `default_path`, but will be in `Bundler.bundle_path` instead next if locked_gems&.any? do |locked_spec| locked_spec.name == short_name && !Gem::Specification.find_by_name(short_name).full_gem_path.start_with?(RbConfig::CONFIG["rubylibprefix"]) rescue Gem::MissingSpecError # If a default gem is scoped to a specific platform, then `find_by_name` will raise. We want to skip those # cases true end if pathname.directory? # If the default_path is a directory, we index all the Ruby files in it uris.concat( Dir.glob(File.join(default_path, "**", "*.rb"), File::FNM_PATHNAME | File::FNM_EXTGLOB).map! do |path| URI::Generic.from_path(path: path, load_path_entry: RbConfig::CONFIG["rubylibdir"]) end, ) elsif pathname.extname == ".rb" # If the default_path is a Ruby file, we index it uris << URI::Generic.from_path(path: default_path, load_path_entry: RbConfig::CONFIG["rubylibdir"]) end end # Add the locked gems to the list of files to be indexed locked_gems&.each do |lazy_spec| next if excluded_gems.include?(lazy_spec.name) spec = Gem::Specification.find_by_name(lazy_spec.name) # When working on a gem, it will be included in the locked_gems list. Since these are the project's own files, # we have already included and handled exclude patterns for it and should not re-include or it'll lead to # duplicates or accidentally ignoring exclude patterns next if spec.full_gem_path == @workspace_path uris.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! do |path| URI::Generic.from_path(path: path, load_path_entry: load_path_entry) end 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 uris.uniq!(&:to_s) uris end