# frozen_string_literal: true
require 'prism'
module IRB
class SourceFinder
class EvaluationError < StandardError; end
class Source
attr_reader :file, :line
def initialize(file, line, ast_source = nil)
@file = file
@line = line
@ast_source = ast_source
end
def file_exist?
File.exist?(@file)
end
def binary_file?
# If the line is zero, it means that the target's source is probably in a binary file.
@line.zero?
end
def file_content
@file_content ||= File.read(@file)
end
def colorized_content
if !binary_file? && file_exist?
# To correctly colorize, we need to colorize full content and extract the relevant lines.
colored_lines = IRB::Color.colorize_code(file_content).lines
# Handle wrong line number case: line_no passed to eval is wrong, file is edited after load, etc
return if colored_lines.size < @line
colored_lines[@line - 1...find_end].join
elsif @ast_source
IRB::Color.colorize_code(@ast_source)
end
end
private
def find_end
code = file_content
lines = code.lines[(@line - 1)..-1]
lines.each_with_index do |line, index|
sub_code = lines.take(index + 1).join
return @line + index if Prism.parse_success?(sub_code)
end
@line
end
end
private_constant :Source
def initialize(irb_context)
@irb_context = irb_context
end
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(?