aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStan Lo <stan001212@gmail.com>2023-08-09 15:57:47 +0100
committergit <svn-admin@ruby-lang.org>2023-08-09 14:57:52 +0000
commitab0f90f1f5583a64a125701e3b08f6620f029eb6 (patch)
tree2998b14ea2cdb106ea71e5beba2aa38257ac13fe
parent6acfc50bccf0c201f77c274281ac33920a0a6923 (diff)
downloadruby-ab0f90f1f5583a64a125701e3b08f6620f029eb6.tar.gz
[ruby/irb] Fix nested IRB sessions' history saving
(https://github.com/ruby/irb/pull/652) 1. Dynamically including `HistorySavingAbility` makes things unnecessarily complicated and should be avoided. 2. Because both `Reline` and `Readline` use a single `HISTORY` constant to store history data. When nesting IRB sessions, only the first IRB session should handle history loading and saving so we can avoid duplicating history. 3. History saving callback should NOT be stored in `IRB.conf` as it's recreated every time `IRB.setup` is called, which would happen when nesting IRB sessions. https://github.com/ruby/irb/commit/0fef0ae160
-rw-r--r--lib/irb.rb8
-rw-r--r--lib/irb/context.rb10
-rw-r--r--lib/irb/history.rb6
-rw-r--r--lib/irb/input-method.rb12
-rw-r--r--test/irb/test_history.rb88
5 files changed, 101 insertions, 23 deletions
diff --git a/lib/irb.rb b/lib/irb.rb
index 1f86a0f386..839115d649 100644
--- a/lib/irb.rb
+++ b/lib/irb.rb
@@ -482,9 +482,16 @@ module IRB
end
def run(conf = IRB.conf)
+ in_nested_session = !!conf[:MAIN_CONTEXT]
conf[:IRB_RC].call(context) if conf[:IRB_RC]
conf[:MAIN_CONTEXT] = context
+ save_history = !in_nested_session && conf[:SAVE_HISTORY] && context.io.support_history_saving?
+
+ if save_history
+ context.io.load_history
+ end
+
prev_trap = trap("SIGINT") do
signal_handle
end
@@ -496,6 +503,7 @@ module IRB
ensure
trap("SIGINT", prev_trap)
conf[:AT_EXIT].each{|hook| hook.call}
+ context.io.save_history if save_history
end
end
diff --git a/lib/irb/context.rb b/lib/irb/context.rb
index bf5b4c5708..18125ff6fb 100644
--- a/lib/irb/context.rb
+++ b/lib/irb/context.rb
@@ -8,7 +8,6 @@ require_relative "workspace"
require_relative "inspector"
require_relative "input-method"
require_relative "output-method"
-require_relative "history"
module IRB
# A class that wraps the current state of the irb session, including the
@@ -130,8 +129,6 @@ module IRB
else
@io = input_method
end
- self.save_history = IRB.conf[:SAVE_HISTORY] if IRB.conf[:SAVE_HISTORY]
-
@extra_doc_dirs = IRB.conf[:EXTRA_DOC_DIRS]
@echo = IRB.conf[:ECHO]
@@ -154,13 +151,6 @@ module IRB
def save_history=(val)
IRB.conf[:SAVE_HISTORY] = val
-
- if val
- context = (IRB.conf[:MAIN_CONTEXT] || self)
- if context.io.support_history_saving? && !context.io.singleton_class.include?(HistorySavingAbility)
- context.io.extend(HistorySavingAbility)
- end
- end
end
def save_history
diff --git a/lib/irb/history.rb b/lib/irb/history.rb
index e18ff516b2..516890ac05 100644
--- a/lib/irb/history.rb
+++ b/lib/irb/history.rb
@@ -1,9 +1,7 @@
module IRB
module HistorySavingAbility # :nodoc:
- def HistorySavingAbility.extended(obj)
- IRB.conf[:AT_EXIT].push proc{obj.save_history}
- obj.load_history
- obj
+ def support_history_saving?
+ true
end
def load_history
diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb
index 4dc43abba3..245b935668 100644
--- a/lib/irb/input-method.rb
+++ b/lib/irb/input-method.rb
@@ -5,6 +5,7 @@
#
require_relative 'completion'
+require_relative "history"
require 'io/console'
require 'reline'
@@ -167,6 +168,8 @@ module IRB
include ::Readline
end
+ include HistorySavingAbility
+
# Creates a new input method object using Readline
def initialize
self.class.initialize_readline
@@ -219,10 +222,6 @@ module IRB
true
end
- def support_history_saving?
- true
- end
-
# Returns the current line number for #io.
#
# #line counts the number of times #gets is called.
@@ -250,6 +249,7 @@ module IRB
class RelineInputMethod < InputMethod
HISTORY = Reline::HISTORY
+ include HistorySavingAbility
# Creates a new input method object using Reline
def initialize
IRB.__send__(:set_encoding, Reline.encoding_system_needs.name, override: false)
@@ -451,10 +451,6 @@ module IRB
str += " and #{inputrc_path}" if File.exist?(inputrc_path)
str
end
-
- def support_history_saving?
- true
- end
end
class ReidlineInputMethod < RelineInputMethod
diff --git a/test/irb/test_history.rb b/test/irb/test_history.rb
index f0dfa03c6f..39f9e82750 100644
--- a/test/irb/test_history.rb
+++ b/test/irb/test_history.rb
@@ -1,9 +1,12 @@
# frozen_string_literal: false
require 'irb'
require 'readline'
+require "tempfile"
require_relative "helper"
+return if RUBY_PLATFORM.match?(/solaris|mswin|mingw/i)
+
module TestIRB
class HistoryTest < TestCase
def setup
@@ -205,4 +208,87 @@ module TestIRB
end
end
end
-end if not RUBY_PLATFORM.match?(/solaris|mswin|mingw/i)
+
+ class NestedIRBHistoryTest < IntegrationTestCase
+ def test_history_saving_with_nested_sessions
+ write_history ""
+
+ write_ruby <<~'RUBY'
+ def foo
+ binding.irb
+ end
+
+ binding.irb
+ RUBY
+
+ run_ruby_file do
+ type "'outer session'"
+ type "foo"
+ type "'inner session'"
+ type "exit"
+ type "'outer session again'"
+ type "exit"
+ end
+
+ assert_equal <<~HISTORY, @history_file.open.read
+ 'outer session'
+ foo
+ 'inner session'
+ exit
+ 'outer session again'
+ exit
+ HISTORY
+ end
+
+ def test_history_saving_with_nested_sessions_and_prior_history
+ write_history <<~HISTORY
+ old_history_1
+ old_history_2
+ old_history_3
+ HISTORY
+
+ write_ruby <<~'RUBY'
+ def foo
+ binding.irb
+ end
+
+ binding.irb
+ RUBY
+
+ run_ruby_file do
+ type "'outer session'"
+ type "foo"
+ type "'inner session'"
+ type "exit"
+ type "'outer session again'"
+ type "exit"
+ end
+
+ assert_equal <<~HISTORY, @history_file.open.read
+ old_history_1
+ old_history_2
+ old_history_3
+ 'outer session'
+ foo
+ 'inner session'
+ exit
+ 'outer session again'
+ exit
+ HISTORY
+ end
+
+ private
+
+ def write_history(history)
+ @history_file = Tempfile.new('irb_history')
+ @history_file.write(history)
+ @history_file.close
+ @irbrc = Tempfile.new('irbrc')
+ @irbrc.write <<~RUBY
+ IRB.conf[:HISTORY_FILE] = "#{@history_file.path}"
+ RUBY
+ @irbrc.close
+ @envs['IRBRC'] = @irbrc.path
+ end
+ end
+end