diff options
author | paulreece <96156234+paulreece@users.noreply.github.com> | 2023-11-28 09:56:47 -0500 |
---|---|---|
committer | git <svn-admin@ruby-lang.org> | 2023-11-28 14:56:51 +0000 |
commit | 891ce4614a7cab6eb76429a9972b5e8c2dc02a5d (patch) | |
tree | f31c36b4be59cf7a222a77c8a21caa809474e874 | |
parent | 5fc71feb6ca8b62d51f9b6421cb26c9f1228be17 (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.rb | 11 | ||||
-rw-r--r-- | lib/irb/source_finder.rb | 30 | ||||
-rw-r--r-- | test/irb/test_cmd.rb | 138 |
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", |