Skip to content

Commit 1681ada

Browse files
Handle Concurrent Sessions and Saving Readline::HISTORY (#651)
* handle concurrent sessions and saving Readline::HISTORY, fixes #510 * separate tests * don't mutate the HISTORY object on the class * avoid repeated .to_i calls * remove intermediary history array * work with array, fix test comment --------- Co-authored-by: Stan Lo <stan001212@gmail.com>
1 parent ebc684c commit 1681ada

File tree

2 files changed

+60
-37
lines changed

2 files changed

+60
-37
lines changed

lib/irb/history.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ def reset_history_counter
1010

1111
def load_history
1212
history = self.class::HISTORY
13+
1314
if history_file = IRB.conf[:HISTORY_FILE]
1415
history_file = File.expand_path(history_file)
1516
end
@@ -32,7 +33,8 @@ def load_history
3233
end
3334

3435
def save_history
35-
history = self.class::HISTORY
36+
history = self.class::HISTORY.to_a
37+
3638
if num = IRB.conf[:SAVE_HISTORY] and (num = num.to_i) != 0
3739
if history_file = IRB.conf[:HISTORY_FILE]
3840
history_file = File.expand_path(history_file)

test/irb/test_history.rb

Lines changed: 57 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,16 @@ def teardown
1717
IRB.conf[:RC_NAME_GENERATOR] = nil
1818
end
1919

20-
class TestInputMethodWithHistory < TestInputMethod
21-
HISTORY = Array.new
20+
class TestInputMethodWithRelineHistory < TestInputMethod
21+
# When IRB.conf[:USE_MULTILINE] is true, IRB::RelineInputMethod uses Reline::History
22+
HISTORY = Reline::History.new(Reline.core.config)
23+
24+
include IRB::HistorySavingAbility
25+
end
26+
27+
class TestInputMethodWithReadlineHistory < TestInputMethod
28+
# When IRB.conf[:USE_MULTILINE] is false, IRB::ReadlineInputMethod uses Readline::HISTORY
29+
HISTORY = Readline::HISTORY
2230

2331
include IRB::HistorySavingAbility
2432
end
@@ -102,35 +110,16 @@ def test_history_save_minus_as_infinity
102110
INPUT
103111
end
104112

105-
def test_history_concurrent_use
113+
def test_history_concurrent_use_reline
106114
omit "Skip Editline" if /EditLine/n.match(Readline::VERSION)
107115
IRB.conf[:SAVE_HISTORY] = 1
108-
assert_history(<<~EXPECTED_HISTORY, <<~INITIAL_HISTORY, <<~INPUT) do |history_file|
109-
exit
110-
5
111-
exit
112-
EXPECTED_HISTORY
113-
1
114-
2
115-
3
116-
4
117-
INITIAL_HISTORY
118-
5
119-
exit
120-
INPUT
121-
assert_history(<<~EXPECTED_HISTORY2, <<~INITIAL_HISTORY2, <<~INPUT2)
122-
exit
123-
EXPECTED_HISTORY2
124-
1
125-
2
126-
3
127-
4
128-
INITIAL_HISTORY2
129-
5
130-
exit
131-
INPUT2
132-
File.utime(File.atime(history_file), File.mtime(history_file) + 2, history_file)
133-
end
116+
history_concurrent_use_for_input_method(TestInputMethodWithRelineHistory)
117+
end
118+
119+
def test_history_concurrent_use_readline
120+
omit "Skip Editline" if /EditLine/n.match(Readline::VERSION)
121+
IRB.conf[:SAVE_HISTORY] = 1
122+
history_concurrent_use_for_input_method(TestInputMethodWithReadlineHistory)
134123
end
135124

136125
def test_history_concurrent_use_not_present
@@ -141,10 +130,11 @@ def test_history_concurrent_use_not_present
141130
IRB.conf[:SAVE_HISTORY] = 1
142131
Dir.mktmpdir("test_irb_history_") do |tmpdir|
143132
ENV["HOME"] = tmpdir
144-
io = TestInputMethodWithHistory.new
133+
io = TestInputMethodWithRelineHistory.new
145134
io.class::HISTORY.clear
146135
io.load_history
147-
io.class::HISTORY.concat(%w"line1 line2")
136+
io.class::HISTORY << 'line1'
137+
io.class::HISTORY << 'line2'
148138

149139
history_file = IRB.rc_file("_history")
150140
assert_not_send [File, :file?, history_file]
@@ -160,7 +150,36 @@ def test_history_concurrent_use_not_present
160150

161151
private
162152

163-
def assert_history(expected_history, initial_irb_history, input)
153+
def history_concurrent_use_for_input_method(input_method)
154+
assert_history(<<~EXPECTED_HISTORY, <<~INITIAL_HISTORY, <<~INPUT, input_method) do |history_file|
155+
exit
156+
5
157+
exit
158+
EXPECTED_HISTORY
159+
1
160+
2
161+
3
162+
4
163+
INITIAL_HISTORY
164+
5
165+
exit
166+
INPUT
167+
assert_history(<<~EXPECTED_HISTORY2, <<~INITIAL_HISTORY2, <<~INPUT2, input_method)
168+
exit
169+
EXPECTED_HISTORY2
170+
1
171+
2
172+
3
173+
4
174+
INITIAL_HISTORY2
175+
5
176+
exit
177+
INPUT2
178+
File.utime(File.atime(history_file), File.mtime(history_file) + 2, history_file)
179+
end
180+
end
181+
182+
def assert_history(expected_history, initial_irb_history, input, input_method = TestInputMethodWithRelineHistory)
164183
backup_verbose, $VERBOSE = $VERBOSE, nil
165184
backup_home = ENV["HOME"]
166185
backup_xdg_config_home = ENV.delete("XDG_CONFIG_HOME")
@@ -172,15 +191,17 @@ def assert_history(expected_history, initial_irb_history, input)
172191
f.write(initial_irb_history)
173192
end
174193

175-
io = TestInputMethodWithHistory.new
194+
io = input_method.new
176195
io.class::HISTORY.clear
177196
io.load_history
178197
if block_given?
179-
history = io.class::HISTORY.dup
198+
previous_history = []
199+
io.class::HISTORY.each { |line| previous_history << line }
180200
yield IRB.rc_file("_history")
181-
io.class::HISTORY.replace(history)
201+
io.class::HISTORY.clear
202+
previous_history.each { |line| io.class::HISTORY << line }
182203
end
183-
io.class::HISTORY.concat(input.split)
204+
input.split.each { |line| io.class::HISTORY << line }
184205
io.save_history
185206

186207
io.load_history

0 commit comments

Comments
 (0)