summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpaulreece <96156234+paulreece@users.noreply.github.com>2023-11-28 09:56:47 -0500
committergit <svn-admin@ruby-lang.org>2023-11-28 14:56:51 +0000
commit891ce4614a7cab6eb76429a9972b5e8c2dc02a5d (patch)
treef31c36b4be59cf7a222a77c8a21caa809474e874
parent5fc71feb6ca8b62d51f9b6421cb26c9f1228be17 (diff)
[ruby/irb] This enhancement allows a user to add the -s flag if they
want to access a methods origin definition. It allows for chaining of multiple esses to further go up the classes as needed. (https://github.com/ruby/irb/pull/770) https://github.com/ruby/irb/commit/eec1329d5a
-rw-r--r--lib/irb/cmd/show_source.rb11
-rw-r--r--lib/irb/source_finder.rb30
-rw-r--r--test/irb/test_cmd.rb138
3 files changed, 171 insertions, 8 deletions
diff --git a/lib/irb/cmd/show_source.rb b/lib/irb/cmd/show_source.rb
index 49cab43fab..9a0364e3eb 100644
--- a/lib/irb/cmd/show_source.rb
+++ b/lib/irb/cmd/show_source.rb
@@ -27,11 +27,18 @@ module IRB
puts "Error: Expected a string but got #{str.inspect}"
return
end
-
- source = SourceFinder.new(@irb_context).find_source(str)
+ if str.include? " -s"
+ str, esses = str.split(" -")
+ s_count = esses.count("^s").zero? ? esses.size : 1
+ source = SourceFinder.new(@irb_context).find_source(str, s_count)
+ else
+ source = SourceFinder.new(@irb_context).find_source(str)
+ end
if source
show_source(source)
+ elsif s_count
+ puts "Error: Couldn't locate a super definition for #{str}"
else
puts "Error: Couldn't locate a definition for #{str}"
end
diff --git a/lib/irb/source_finder.rb b/lib/irb/source_finder.rb
index 959919e8ac..a9450d4571 100644
--- a/lib/irb/source_finder.rb
+++ b/lib/irb/source_finder.rb
@@ -16,7 +16,7 @@ module IRB
@irb_context = irb_context
end
- def find_source(signature)
+ def find_source(signature, s_count = nil)
context_binding = @irb_context.workspace.binding
case signature
when /\A[A-Z]\w*(::[A-Z]\w*)*\z/ # Const::Name
@@ -26,14 +26,13 @@ module IRB
when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
owner = eval(Regexp.last_match[:owner], context_binding)
method = Regexp.last_match[:method]
- if owner.respond_to?(:instance_method)
- methods = owner.instance_methods + owner.private_instance_methods
- file, line = owner.instance_method(method).source_location if methods.include?(method.to_sym)
- end
+ return unless owner.respond_to?(:instance_method)
+ file, line = method_target(owner, s_count, method, "owner")
when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method
receiver = eval(Regexp.last_match[:receiver] || 'self', context_binding)
method = Regexp.last_match[:method]
- file, line = receiver.method(method).source_location if receiver.respond_to?(method, true)
+ return unless receiver.respond_to?(method, true)
+ file, line = method_target(receiver, s_count, method, "receiver")
end
if file && line && File.exist?(file)
Source.new(file: file, first_line: line, last_line: find_end(file, line))
@@ -60,5 +59,24 @@ module IRB
end
first_line
end
+
+ def method_target(owner_receiver, s_count, method, type)
+ case type
+ when "owner"
+ target_method = owner_receiver.instance_method(method)
+ return target_method.source_location unless s_count
+ when "receiver"
+ if s_count
+ target_method = owner_receiver.class.instance_method(method)
+ else
+ target_method = method
+ return owner_receiver.method(method).source_location
+ end
+ end
+ s_count.times do |s|
+ target_method = target_method.super_method if target_method
+ end
+ target_method.nil? ? nil : target_method.source_location
+ end
end
end
diff --git a/test/irb/test_cmd.rb b/test/irb/test_cmd.rb
index 55373c2e8a..ee987920f3 100644
--- a/test/irb/test_cmd.rb
+++ b/test/irb/test_cmd.rb
@@ -466,6 +466,144 @@ module TestIRB
assert_match(%r[/irb\/init\.rb], out)
end
+ def test_show_source_method_s
+ code = <<~RUBY
+ class Baz
+ def foo
+ end
+ end
+
+ class Bar < Baz
+ def foo
+ super
+ end
+ end
+ RUBY
+ File.write("#{@tmpdir}/bazbar.rb", code)
+ out, err = execute_lines(
+ "irb_load '#{@tmpdir}/bazbar.rb'\n",
+ "show_source Bar#foo -s",
+ )
+ assert_match(%r[bazbar.rb:2\n\n def foo\n end\n\n=> nil\n], out)
+ File.delete("#{@tmpdir}/bazbar.rb")
+ end
+
+ def test_show_source_method_multiple_s
+ code = <<~RUBY
+ class Baz
+ def fob
+ end
+ end
+
+ class Bar < Baz
+ def fob
+ super
+ end
+ end
+
+ class Bob < Bar
+ def fob
+ super
+ end
+ end
+ RUBY
+ File.write("#{@tmpdir}/bazbarbob.rb", code)
+ out, err = execute_lines(
+ "irb_load '#{@tmpdir}/bazbarbob.rb'\n",
+ "show_source Bob#fob -ss",
+ )
+ assert_match(%r[bazbarbob.rb:2\n\n def fob\n end\n\n=> nil\n], out)
+ File.delete("#{@tmpdir}/bazbarbob.rb")
+ end
+
+ def test_show_source_method_no_instance_method
+ code = <<~RUBY
+ class Baz
+ end
+
+ class Bar < Baz
+ def fee
+ super
+ end
+ end
+ RUBY
+ File.write("#{@tmpdir}/bazbar.rb", code)
+ out, err = execute_lines(
+ "irb_load '#{@tmpdir}/bazbar.rb'\n",
+ "show_source Bar#fee -s",
+ )
+ assert_match(%r[Error: Couldn't locate a super definition for Bar#fee\n], out)
+ File.delete("#{@tmpdir}/bazbar.rb")
+ end
+
+ def test_show_source_method_exceeds_super_chain
+ code = <<~RUBY
+ class Baz
+ def fow
+ end
+ end
+
+ class Bar < Baz
+ def fow
+ super
+ end
+ end
+ RUBY
+ File.write("#{@tmpdir}/bazbar.rb", code)
+ out, err = execute_lines(
+ "irb_load '#{@tmpdir}/bazbar.rb'\n",
+ "show_source Bar#fow -ss",
+ )
+ assert_match(%r[Error: Couldn't locate a super definition for Bar#fow\n], out)
+ File.delete("#{@tmpdir}/bazbar.rb")
+ end
+
+ def test_show_source_method_accidental_characters
+ code = <<~RUBY
+ class Baz
+ def fol
+ end
+ end
+
+ class Bar < Baz
+ def fol
+ super
+ end
+ end
+ RUBY
+ File.write("#{@tmpdir}/bazbar.rb", code)
+ out, err = execute_lines(
+ "irb_load '#{@tmpdir}/bazbar.rb'\n",
+ "show_source Bar#fol -sddddd",
+ )
+
+ assert_match(%r[bazbar.rb:2\n\n def fol\n end\n\n=> nil\n], out)
+ File.delete("#{@tmpdir}/bazbar.rb")
+ end
+
+ def test_show_source_receiver_super
+ code = <<~RUBY
+ class Baz
+ def fot
+ end
+ end
+
+ class Bar < Baz
+ def fot
+ super
+ end
+ end
+ RUBY
+ File.write("#{@tmpdir}/bazbar.rb", code)
+ out, err = execute_lines(
+ "irb_load '#{@tmpdir}/bazbar.rb'\n",
+ "bar = Bar.new",
+ "show_source bar.fot -s"
+ )
+ assert_match(%r[bazbar.rb:2\n\n def fot\n end\n\n=> nil\n], out)
+ File.delete("#{@tmpdir}/bazbar.rb")
+ end
+
def test_show_source_string
out, err = execute_lines(
"show_source 'IRB.conf'\n",