class Solargraph::DocMap


A collection of pins generated from required gems.

def self.gems_in_memory

Returns:
  • (Hash{Gem::Specification => Array[Pin::Base]}) -
def self.gems_in_memory
  @gems_in_memory ||= {}
end

def change_gemspec_version gemspec, version

Returns:
  • (Gem::Specification) -

Parameters:
  • version (Gem::Version) --
  • gemspec (Gem::Specification) --
def change_gemspec_version gemspec, version
  Gem::Specification.find_by_name(gemspec.name, "= #{version}")
rescue Gem::MissingSpecError
  Solargraph.logger.info "Gem #{gemspec.name} version #{version} not found. Using #{gemspec.version} instead"
  gemspec
end

def dependencies

Returns:
  • (Set) -
def dependencies
  @dependencies ||= (gemspecs.flat_map { |spec| fetch_dependencies(spec) } - gemspecs).to_set
end

def fetch_dependencies gemspec

Returns:
  • (Array) -

Parameters:
  • gemspec (Gem::Specification) --
def fetch_dependencies gemspec
  only_runtime_dependencies(gemspec).each_with_object(Set.new) do |spec, deps|
    Solargraph.logger.info "Adding #{spec.name} dependency for #{gemspec.name}"
    dep = Gem::Specification.find_by_name(spec.name, spec.requirement)
    deps.merge fetch_dependencies(dep) if deps.add?(dep)
  rescue Gem::MissingSpecError
    Solargraph.logger.warn "Gem dependency #{spec.name} #{spec.requirements} for #{gemspec.name} not found."
  end.to_a
end

def gemspec_or_preference gemspec

Returns:
  • (Gem::Specification, nil) -

Parameters:
  • gemspec (Gem::Specification, nil) --
def gemspec_or_preference gemspec
  return gemspec unless gemspec && preference_map.key?(gemspec.name)
  return gemspec if gemspec.version == preference_map[gemspec.name].version
  change_gemspec_version gemspec, preference_map[by_path.name].version
end

def gemspecs

Returns:
  • (Array) -
def gemspecs
  @gemspecs ||= required_gem_map.values.compact
end

def generate

Returns:
  • (void) -
def generate
  @pins = []
  @uncached_gemspecs = []
  required_gem_map.each do |path, gemspec|
    if gemspec
      try_cache gemspec
    else
      try_stdlib_map path
    end
  end
  dependencies.each { |dep| try_cache dep }
  @uncached_gemspecs.uniq!
end

def initialize(requires, preferences, rbs_path = nil)

Parameters:
  • rbs_path (String, Pathname, nil) --
  • preferences (Array) --
  • requires (Array) --
def initialize(requires, preferences, rbs_path = nil)
  @requires = requires.compact
  @preferences = preferences.compact
  @rbs_path = rbs_path
  generate
end

def only_runtime_dependencies gemspec

Returns:
  • (Array) -

Parameters:
  • gemspec (Gem::Specification) --
def only_runtime_dependencies gemspec
  gemspec.dependencies - gemspec.development_dependencies
end

def preference_map

Returns:
  • (Hash{String => Gem::Specification}) -
def preference_map
  @preference_map ||= preferences.to_h { |gemspec| [gemspec.name, gemspec] }
end

def required_gem_map

Returns:
  • (Hash{String => Gem::Specification, nil}) -
def required_gem_map
  @required_gem_map ||= requires.to_h { |path| [path, resolve_path_to_gemspec(path)] }
end

def resolve_path_to_gemspec path

Returns:
  • (Gem::Specification, nil) -

Parameters:
  • path (String) --
def resolve_path_to_gemspec path
  return nil if path.empty?
  gemspec = Gem::Specification.find_by_path(path)
  if gemspec.nil?
    gem_name_guess = path.split('/').first
    begin
      # this can happen when the gem is included via a local path in

      # a Gemfile; Gem doesn't try to index the paths in that case.

      #

      # See if we can make a good guess:

      potential_gemspec = Gem::Specification.find_by_name(gem_name_guess)
      file = "lib/#{path}.rb"
      gemspec = potential_gemspec if potential_gemspec.files.any? { |gemspec_file| file == gemspec_file }
    rescue Gem::MissingSpecError
      Solargraph.logger.debug "Require path #{path} could not be resolved to a gem via find_by_path or guess of #{gem_name_guess}"
      nil
    end
  end
  gemspec_or_preference gemspec
end

def try_cache gemspec

Returns:
  • (void) -

Parameters:
  • gemspec (Gem::Specification) --
def try_cache gemspec
  return if try_gem_in_memory(gemspec)
  cache_file = File.join('gems', "#{gemspec.name}-#{gemspec.version}.ser")
  if Cache.exist?(cache_file)
    cached = Cache.load(cache_file)
    gempins = update_from_collection(gemspec, cached)
    self.class.gems_in_memory[gemspec] = gempins
    @pins.concat gempins
  else
    Solargraph.logger.debug "No pin cache for #{gemspec.name} #{gemspec.version}"
    @uncached_gemspecs.push gemspec
  end
end

def try_gem_in_memory gemspec

Returns:
  • (Boolean) -

Parameters:
  • gemspec (Gem::Specification) --
def try_gem_in_memory gemspec
  gempins = DocMap.gems_in_memory[gemspec]
  return false unless gempins
  Solargraph.logger.debug "Found #{gemspec.name} #{gemspec.version} in memory"
  @pins.concat gempins
  true
end

def try_stdlib_map path

Returns:
  • (void) -

Parameters:
  • path (String) -- require path that might be in the RBS stdlib collection
def try_stdlib_map path
  map = RbsMap::StdlibMap.load(path)
  if map.resolved?
    Solargraph.logger.debug "Loading stdlib pins for #{path}"
    @pins.concat map.pins
  else
    # @todo Temporarily ignoring unresolved `require 'set'`

    Solargraph.logger.debug "Require path #{path} could not be resolved" unless path == 'set'
  end
end

def unresolved_requires

Returns:
  • (Array) -
def unresolved_requires
  @unresolved_requires ||= required_gem_map.select { |_, gemspec| gemspec.nil? }.keys
end

def update_from_collection gemspec, gempins

def update_from_collection gemspec, gempins
  return gempins unless @rbs_path && File.directory?(@rbs_path)
  return gempins if RbsMap.new(gemspec.name, gemspec.version).resolved?
  rbs_map = RbsMap.new(gemspec.name, gemspec.version, directories: [@rbs_path])
  return gempins unless rbs_map.resolved?
  Solargraph.logger.info "Updating #{gemspec.name} #{gemspec.version} from collection"
  GemPins.combine(gempins, rbs_map)
end