From 9e336f73fb7d37f3b09e360f8204828bbca51cd5 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Wed, 31 Mar 2021 22:49:51 -0700 Subject: [ruby/irb] Add show_source command https://github.com/ruby/irb/commit/108cb04352 --- lib/irb/cmd/show_source.rb | 86 ++++++++++++++++++++++++++++++++++++++++++++++ lib/irb/extend-command.rb | 5 +++ 2 files changed, 91 insertions(+) create mode 100644 lib/irb/cmd/show_source.rb (limited to 'lib/irb') diff --git a/lib/irb/cmd/show_source.rb b/lib/irb/cmd/show_source.rb new file mode 100644 index 0000000000..0bd40b7d4e --- /dev/null +++ b/lib/irb/cmd/show_source.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require_relative "nop" +require_relative "../color" +require_relative "../ruby-lex" + +# :stopdoc: +module IRB + module ExtendCommand + class ShowSource < Nop + def execute(str = nil) + unless str.is_a?(String) + puts "Error: Expected a string but got #{str.inspect}" + return + end + source = find_source(str) + if source && File.exist?(source.file) + show_source(source) + else + puts "Error: Couldn't locate a definition for #{str}" + end + nil + end + + private + + # @param [IRB::ExtendCommand::ShowSource::Source] source + def show_source(source) + puts + puts "#{bold("From")}: #{source.file}:#{source.first_line}" + puts + code = IRB::Color.colorize_code(File.read(source.file)) + puts code.lines[(source.first_line - 1)...source.last_line].join + puts + end + + def find_source(str) + case str + when /\A[A-Z]\w*(::[A-Z]\w*)*\z/ # Const::Name + eval(str, irb_context.workspace.binding) # trigger autoload + base = irb_context.workspace.binding.receiver.yield_self { |r| r.is_a?(Module) ? r : Object } + file, line = base.const_source_location(str) if base.respond_to?(:const_source_location) # Ruby 2.7+ + when /\A(?[A-Z]\w*(::[A-Z]\w*)*)#(?[^ :.]+)\z/ # Class#method + owner = eval(Regexp.last_match[:owner], irb_context.workspace.binding) + method = Regexp.last_match[:method] + if owner.respond_to?(:instance_method) && owner.instance_methods.include?(method.to_sym) + file, line = owner.instance_method(method).source_location + end + when /\A((?.+)(\.|::))?(?[^ :.]+)\z/ # method, receiver.method, receiver::method + receiver = eval(Regexp.last_match[:receiver] || 'self', irb_context.workspace.binding) + method = Regexp.last_match[:method] + file, line = receiver.method(method).source_location if receiver.respond_to?(method) + end + if file && line + Source.new(file: file, first_line: line, last_line: find_end(file, line)) + end + end + + def find_end(file, first_line) + return first_line unless File.exist?(file) + lex = RubyLex.new + code = +"" + File.read(file).lines[(first_line - 1)..-1].each_with_index do |line, i| + _ltype, _indent, continue, code_block_open = lex.check_state(code << line) + if !continue && !code_block_open + return first_line + i + end + end + first_line + end + + def bold(str) + Color.colorize(str, [:BOLD]) + end + + Source = Struct.new( + :file, # @param [String] - file name + :first_line, # @param [String] - first line + :last_line, # @param [String] - last line + keyword_init: true, + ) + private_constant :Source + end + end +end +# :startdoc: diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb index f50068d06c..339e9e6084 100644 --- a/lib/irb/extend-command.rb +++ b/lib/irb/extend-command.rb @@ -135,6 +135,11 @@ module IRB # :nodoc: [:measure, NO_OVERRIDE], ], + [ + :irb_show_source, :ShowSource, "irb/cmd/show_source", + [:show_source, NO_OVERRIDE], + ], + [ :irb_whereami, :Whereami, "irb/cmd/whereami", [:whereami, NO_OVERRIDE], -- cgit v1.2.3