[前][次][番号順一覧][スレッド一覧]

ruby-changes:65732

From: Takashi <ko1@a...>
Date: Fri, 2 Apr 2021 16:40:25 +0900 (JST)
Subject: [ruby-changes:65732] 9e336f73fb (master): [ruby/irb] Add show_source command

https://git.ruby-lang.org/ruby.git/commit/?id=9e336f73fb

From 9e336f73fb7d37f3b09e360f8204828bbca51cd5 Mon Sep 17 00:00:00 2001
From: Takashi Kokubun <takashikkbn@g...>
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 +++
 test/irb/test_cmd.rb       | 17 +++++++++
 3 files changed, 108 insertions(+)
 create mode 100644 lib/irb/cmd/show_source.rb

diff --git a/lib/irb/cmd/show_source.rb b/lib/irb/cmd/show_source.rb
new file mode 100644
index 0000000..0bd40b7
--- /dev/null
+++ b/lib/irb/cmd/show_source.rb
@@ -0,0 +1,86 @@ https://github.com/ruby/ruby/blob/trunk/lib/irb/cmd/show_source.rb#L1
+# 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 f50068d..339e9e6 100644
--- a/lib/irb/extend-command.rb
+++ b/lib/irb/extend-command.rb
@@ -136,6 +136,11 @@ module IRB # :nodoc: https://github.com/ruby/ruby/blob/trunk/lib/irb/extend-command.rb#L136
       ],
 
       [
+        :irb_show_source, :ShowSource, "irb/cmd/show_source",
+        [:show_source, NO_OVERRIDE],
+      ],
+
+      [
         :irb_whereami, :Whereami, "irb/cmd/whereami",
         [:whereami, NO_OVERRIDE],
       ],
diff --git a/test/irb/test_cmd.rb b/test/irb/test_cmd.rb
index 8f46a27..044d852 100644
--- a/test/irb/test_cmd.rb
+++ b/test/irb/test_cmd.rb
@@ -392,6 +392,23 @@ module TestIRB https://github.com/ruby/ruby/blob/trunk/test/irb/test_cmd.rb#L392
       assert_match(/^instance variables:\s+@a\n/m, out)
     end
 
+    def test_show_source
+      input = TestInputMethod.new([
+        "show_source 'IRB.conf'\n",
+      ])
+      IRB.init_config(nil)
+      workspace = IRB::WorkSpace.new(self)
+      irb = IRB::Irb.new(workspace, input)
+      IRB.conf[:VERBOSE] = false
+      IRB.conf[:MAIN_CONTEXT] = irb.context
+      irb.context.return_format = "=> %s\n"
+      out, err = capture_output do
+        irb.eval_input
+      end
+      assert_empty err
+      assert_match(%r[/irb\.rb], out)
+    end
+
     def test_whereami
       input = TestInputMethod.new([
         "whereami\n",
-- 
cgit v1.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]