summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS3
-rw-r--r--lib/irb.rb4
-rw-r--r--lib/irb/workspace.rb21
-rw-r--r--test/irb/test_workspace.rb78
4 files changed, 105 insertions, 1 deletions
diff --git a/NEWS b/NEWS
index ce0b866dad..adc77de479 100644
--- a/NEWS
+++ b/NEWS
@@ -158,6 +158,9 @@ with all sufficient information, see the ChangeLog file or Redmine
* IPAddr now rejects invalid address mask. [Bug #13399]
* IPAddr#ipv4_compat and #ipv4_compat? are deprecated. [Bug #13769]
+* IRB
+ * Show source around `binding.irb` on session start
+
* Net::HTTP
* Add more HTTP status classes
diff --git a/lib/irb.rb b/lib/irb.rb
index 3c549d2c6e..dcc160f140 100644
--- a/lib/irb.rb
+++ b/lib/irb.rb
@@ -706,6 +706,8 @@ class Binding
undef irb if method_defined?(:irb)
def irb
IRB.setup(eval("__FILE__"))
- IRB::Irb.new(IRB::WorkSpace.new(self)).run(IRB.conf)
+ workspace = IRB::WorkSpace.new(self)
+ STDOUT.print(workspace.code_around_binding)
+ IRB::Irb.new(workspace).run(IRB.conf)
end
end
diff --git a/lib/irb/workspace.rb b/lib/irb/workspace.rb
index 9051f96df0..5c1e97a981 100644
--- a/lib/irb/workspace.rb
+++ b/lib/irb/workspace.rb
@@ -107,6 +107,27 @@ EOF
bt
end
+ def code_around_binding
+ file = @binding.eval('__FILE__')
+ pos = @binding.eval('__LINE__') - 1
+ return nil unless File.exist?(file)
+
+ if defined?(SCRIPT_LINES__) && SCRIPT_LINES__.key?(file)
+ lines = SCRIPT_LINES__[file].split(/^/)
+ else
+ lines = File.readlines(file)
+ end
+
+ start_pos = [pos - 5, 0].max
+ end_pos = [pos + 5, lines.size - 1].min
+
+ body = (start_pos..end_pos).map do |current_pos|
+ lineno = "%#{end_pos.to_s.length}d" % (current_pos + 1)
+ " #{pos == current_pos ? '=>' : ' '} #{lineno}: #{lines[current_pos]}"
+ end.join
+ "\nFrom: #{file} @ line #{pos + 1} :\n\n#{body}\n"
+ end
+
def IRB.delete_caller
end
end
diff --git a/test/irb/test_workspace.rb b/test/irb/test_workspace.rb
new file mode 100644
index 0000000000..f725c55c06
--- /dev/null
+++ b/test/irb/test_workspace.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'tempfile'
+require 'irb/workspace'
+
+module TestIRB
+ class TestWorkSpace < Test::Unit::TestCase
+ def test_code_around_bindinig
+ Tempfile.create do |f|
+ code = <<~RUBY
+ # 1
+ # 2
+ IRB::WorkSpace.new(binding) # 3
+ # 4
+ # 5
+ RUBY
+ f.print(code)
+ f.close
+
+ workspace = eval(code, binding, f.path)
+ assert_equal(<<~EOS, workspace.code_around_binding)
+
+ From: #{f.path} @ line 3 :
+
+ 1: # 1
+ 2: # 2
+ => 3: IRB::WorkSpace.new(binding) # 3
+ 4: # 4
+ 5: # 5
+
+ EOS
+ end
+ end
+
+ def test_code_around_bindinig_with_script_lines__
+ with_script_lines do |script_lines|
+ Tempfile.create do |f|
+ code = "IRB::WorkSpace.new(binding)\n"
+ script_lines[f.path] = code
+
+ workspace = eval(code, binding, f.path)
+ assert_equal(<<~EOS, workspace.code_around_binding)
+
+ From: #{f.path} @ line 1 :
+
+ => 1: IRB::WorkSpace.new(binding)
+
+ EOS
+ end
+ end
+ end
+
+ def test_code_around_bindinig_on_irb
+ workspace = eval("IRB::WorkSpace.new(binding)", binding, "(irb)")
+ assert_equal(nil, workspace.code_around_binding)
+ end
+
+ private
+
+ def with_script_lines
+ script_lines = nil
+ debug_lines = {}
+ Object.class_eval do
+ if defined?(SCRIPT_LINES__)
+ script_lines = SCRIPT_LINES__
+ remove_const :SCRIPT_LINES__
+ end
+ const_set(:SCRIPT_LINES__, debug_lines)
+ end
+ yield debug_lines
+ ensure
+ Object.class_eval do
+ remove_const :SCRIPT_LINES__
+ const_set(:SCRIPT_LINES__, script_lines) if script_lines
+ end
+ end
+ end
+end