diff --git a/lib/irb/history.rb b/lib/irb/history.rb index 1b0f2bc59..4d360e625 100644 --- a/lib/irb/history.rb +++ b/lib/irb/history.rb @@ -76,7 +76,11 @@ def save_history begin ensure_history_file_permissions(history_file) - File.open(history_file, (append_history ? "a" : "w"), 0o600, encoding: IRB.conf[:LC_MESSAGES]&.encoding) do |f| + # Write the entries in the encoding they already carry. Forcing the + # locale's encoding raises Encoding::UndefinedConversionError on a + # non-ASCII entry under an ASCII locale, and the rescues below do not + # catch it, so IRB aborts on exit and the whole session is lost. + File.open(history_file, (append_history ? "a" : "w"), 0o600) do |f| hist = history.map { |l| l.scrub.split("\n").join("\\\n") } unless append_history || History.infinite? diff --git a/test/irb/test_history.rb b/test/irb/test_history.rb index e600c74cb..979611414 100644 --- a/test/irb/test_history.rb +++ b/test/irb/test_history.rb @@ -163,6 +163,23 @@ def test_history_different_encodings INPUT end + def test_history_saves_non_ascii_input_under_an_ascii_locale + IRB.conf[:SAVE_HISTORY] = 2 + IRB.conf[:LC_MESSAGES] = IRB::Locale.new("en_US.ASCII") + ENV["HOME"] = @tmpdir + + io = TestInputMethodWithRelineHistory.new + io.class::HISTORY.clear + io.class::HISTORY << "puts 'h\u00e9llo'" + + assert_nothing_raised do + io.save_history + end + + assert_equal("puts 'h\u00e9llo'\n", + File.read(IRB.rc_file("_history"), encoding: Encoding::UTF_8)) + end + def test_history_does_not_raise_when_history_file_directory_does_not_exist backup_history_file = IRB.conf[:HISTORY_FILE] IRB.conf[:SAVE_HISTORY] = 1