summaryrefslogtreecommitdiff
path: root/lib/irb
diff options
context:
space:
mode:
authorTakashi Kokubun <takashikkbn@gmail.com>2021-03-31 22:49:51 -0700
committergit <svn-admin@ruby-lang.org>2021-04-02 16:40:06 +0900
commit9e336f73fb7d37f3b09e360f8204828bbca51cd5 (patch)
tree3327a48e892556036bc6d11f43cbfe9c376b25a9 /lib/irb
parent43e0677c6de891c184da4ec66a03005d0824d5d0 (diff)
[ruby/irb] Add show_source command
https://github.com/ruby/irb/commit/108cb04352
Diffstat (limited to 'lib/irb')
-rw-r--r--lib/irb/cmd/show_source.rb86
-rw-r--r--lib/irb/extend-command.rb5
2 files changed, 91 insertions, 0 deletions
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(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\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((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\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
@@ -136,6 +136,11 @@ module IRB # :nodoc:
],
[
+ :irb_show_source, :ShowSource, "irb/cmd/show_source",
+ [:show_source, NO_OVERRIDE],
+ ],
+
+ [
:irb_whereami, :Whereami, "irb/cmd/whereami",
[:whereami, NO_OVERRIDE],
],