# class IRB::SourceFinder

### Public Class Methods

new(irb_context) click to toggle source

    # File lib/irb/source_finder.rb, line 66
    def initialize(irb_context)
      @irb_context = irb_context
    end

### Public Instance Methods

find_source(signature, super_level = 0) click to toggle source

    # File lib/irb/source_finder.rb, line 70
    def find_source(signature, super_level = 0)
      case signature
      when /\A(::)?[A-Z]\w*(::[A-Z]\w*)*\z/ # ConstName, ::ConstName, ConstPath::ConstName
        eval_receiver_or_owner(signature) # trigger autoload
        *parts, name = signature.split('::', -1)
        base =
          if parts.empty? # ConstName
            find_const_owner(name)
          elsif parts == [''] # ::ConstName
            Object
          else # ConstPath::ConstName
            eval_receiver_or_owner(parts.join('::'))
          end
        file, line = base.const_source_location(name)
      when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
        owner = eval_receiver_or_owner(Regexp.last_match[:owner])
        method = Regexp.last_match[:method]
        return unless owner.respond_to?(:instance_method)
        method = method_target(owner, super_level, method, "owner")
        file, line = method&.source_location
      when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method
        receiver = eval_receiver_or_owner(Regexp.last_match[:receiver] || 'self')
        method = Regexp.last_match[:method]
        return unless receiver.respond_to?(method, true)
        method = method_target(receiver, super_level, method, "receiver")
        file, line = method&.source_location
      end
      return unless file && line

      if File.exist?(file)
        Source.new(file, line)
      elsif method
        # Method defined with eval, probably in IRB session
        source = RubyVM::AbstractSyntaxTree.of(method)&.source rescue nil
        Source.new(file, line, source)
      end
    rescue EvaluationError
      nil
    end

### Private Instance Methods

eval_receiver_or_owner(code) click to toggle source

    # File lib/irb/source_finder.rb, line 127
    def eval_receiver_or_owner(code)
      context_binding = @irb_context.workspace.binding
      eval(code, context_binding)
    rescue NameError
      raise EvaluationError
    end

find_const_owner(name) click to toggle source

    # File lib/irb/source_finder.rb, line 134
    def find_const_owner(name)
      module_nesting = @irb_context.workspace.binding.eval('::Module.nesting')
      module_nesting.find { |mod| mod.const_defined?(name, false) } || module_nesting.find { |mod| mod.const_defined?(name) } || Object
    end

method_target(owner_receiver, super_level, method, type) click to toggle source

    # File lib/irb/source_finder.rb, line 112
    def method_target(owner_receiver, super_level, method, type)
      case type
      when "owner"
        target_method = owner_receiver.instance_method(method)
      when "receiver"
        target_method = owner_receiver.method(method)
      end
      super_level.times do |s|
        target_method = target_method.super_method if target_method
      end
      target_method
    rescue NameError
      nil
    end