aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/reline.rb62
-rw-r--r--lib/reline/history.rb60
-rw-r--r--test/test_history.rb273
3 files changed, 335 insertions, 60 deletions
diff --git a/lib/reline.rb b/lib/reline.rb
index bf8967c561..5bb47d5d3f 100644
--- a/lib/reline.rb
+++ b/lib/reline.rb
@@ -5,6 +5,7 @@ require 'reline/config'
require 'reline/key_actor'
require 'reline/key_stroke'
require 'reline/line_editor'
+require 'reline/history'
module Reline
Key = Struct.new('Key', :char, :combined_char, :with_meta)
@@ -26,66 +27,7 @@ module Reline
@@line_editor = Reline::LineEditor.new(@@config)
@@ambiguous_width = nil
- HISTORY = Class.new(Array) {
- def initialize(config)
- @config = config
- end
-
- def to_s
- 'HISTORY'
- end
-
- def delete_at(index)
- index = check_index(index)
- super(index)
- end
-
- def [](index)
- index = check_index(index)
- super(index)
- end
-
- def []=(index, val)
- index = check_index(index)
- super(index, String.new(val, encoding: Encoding::default_external))
- end
-
- def concat(*val)
- val.each do |v|
- push(*v)
- end
- end
-
- def push(*val)
- diff = size + val.size - @config.history_size
- if diff > 0
- if diff <= size
- shift(diff)
- else
- diff -= size
- clear
- val.shift(diff)
- end
- end
- super(*(val.map{ |v| String.new(v, encoding: Encoding::default_external) }))
- end
-
- def <<(val)
- shift if size + 1 > @config.history_size
- super(String.new(val, encoding: Encoding::default_external))
- end
-
- private def check_index(index)
- index += size if index < 0
- raise RangeError.new("index=<#{index}>") if index < -@config.history_size or @config.history_size < index
- raise IndexError.new("index=<#{index}>") if index < 0 or size <= index
- index
- end
-
- private def set_config(config)
- @config = config
- end
- }.new(@@config)
+ HISTORY = History.new(@@config)
@@completion_append_character = nil
def self.completion_append_character
diff --git a/lib/reline/history.rb b/lib/reline/history.rb
new file mode 100644
index 0000000000..d988230941
--- /dev/null
+++ b/lib/reline/history.rb
@@ -0,0 +1,60 @@
+class Reline::History < Array
+ def initialize(config)
+ @config = config
+ end
+
+ def to_s
+ 'HISTORY'
+ end
+
+ def delete_at(index)
+ index = check_index(index)
+ super(index)
+ end
+
+ def [](index)
+ index = check_index(index)
+ super(index)
+ end
+
+ def []=(index, val)
+ index = check_index(index)
+ super(index, String.new(val, encoding: Encoding::default_external))
+ end
+
+ def concat(*val)
+ val.each do |v|
+ push(*v)
+ end
+ end
+
+ def push(*val)
+ diff = size + val.size - @config.history_size
+ if diff > 0
+ if diff <= size
+ shift(diff)
+ else
+ diff -= size
+ clear
+ val.shift(diff)
+ end
+ end
+ super(*(val.map{ |v| String.new(v, encoding: Encoding::default_external) }))
+ end
+
+ def <<(val)
+ shift if size + 1 > @config.history_size
+ super(String.new(val, encoding: Encoding::default_external))
+ end
+
+ private def check_index(index)
+ index += size if index < 0
+ raise RangeError.new("index=<#{index}>") if index < -@config.history_size or @config.history_size < index
+ raise IndexError.new("index=<#{index}>") if index < 0 or size <= index
+ index
+ end
+
+ private def set_config(config)
+ @config = config
+ end
+end
diff --git a/test/test_history.rb b/test/test_history.rb
new file mode 100644
index 0000000000..b38d9a3433
--- /dev/null
+++ b/test/test_history.rb
@@ -0,0 +1,273 @@
+require_relative 'helper'
+require "reline/history"
+
+class Reline::KeyStroke::Test < Reline::TestCase
+ def test_ancestors
+ assert_equal(Reline::History.ancestors.include?(Array), true)
+ end
+
+ def test_to_s
+ history = history_new
+ expected = "HISTORY"
+ assert_equal(expected, history.to_s)
+ end
+
+ def test_get
+ history, lines = lines = history_new_and_push_history(5)
+ lines.each_with_index do |s, i|
+ assert_external_string_equal(s, history[i])
+ end
+ end
+
+ def test_get__negative
+ history, lines = lines = history_new_and_push_history(5)
+ (1..5).each do |i|
+ assert_equal(lines[-i], history[-i])
+ end
+ end
+
+ def test_get__out_of_range
+ history, _ = history_new_and_push_history(5)
+ invalid_indexes = [5, 6, 100, -6, -7, -100]
+ invalid_indexes.each do |i|
+ assert_raise(IndexError, "i=<#{i}>") do
+ history[i]
+ end
+ end
+
+ invalid_indexes = [100_000_000_000_000_000_000,
+ -100_000_000_000_000_000_000]
+ invalid_indexes.each do |i|
+ assert_raise(RangeError, "i=<#{i}>") do
+ history[i]
+ end
+ end
+ end
+
+ def test_set
+ begin
+ history, _ = history_new_and_push_history(5)
+ 5.times do |i|
+ expected = "set: #{i}"
+ history[i] = expected
+ assert_external_string_equal(expected, history[i])
+ end
+ rescue NotImplementedError
+ end
+ end
+
+ def test_set__out_of_range
+ history = history_new
+ assert_raise(IndexError, NotImplementedError, "index=<0>") do
+ history[0] = "set: 0"
+ end
+
+ history, _ = history_new_and_push_history(5)
+ invalid_indexes = [5, 6, 100, -6, -7, -100]
+ invalid_indexes.each do |i|
+ assert_raise(IndexError, NotImplementedError, "index=<#{i}>") do
+ history[i] = "set: #{i}"
+ end
+ end
+
+ invalid_indexes = [100_000_000_000_000_000_000,
+ -100_000_000_000_000_000_000]
+ invalid_indexes.each do |i|
+ assert_raise(RangeError, NotImplementedError, "index=<#{i}>") do
+ history[i] = "set: #{i}"
+ end
+ end
+ end
+
+ def test_push
+ history = history_new
+ 5.times do |i|
+ s = i.to_s
+ assert_equal(history, history.push(s))
+ assert_external_string_equal(s, history[i])
+ end
+ assert_equal(5, history.length)
+ end
+
+ def test_push__operator
+ history = history_new
+ 5.times do |i|
+ s = i.to_s
+ assert_equal(history, history << s)
+ assert_external_string_equal(s, history[i])
+ end
+ assert_equal(5, history.length)
+ end
+
+ def test_push__plural
+ history = history_new
+ assert_equal(history, history.push("0", "1", "2", "3", "4"))
+ (0..4).each do |i|
+ assert_external_string_equal(i.to_s, history[i])
+ end
+ assert_equal(5, history.length)
+
+ assert_equal(history, history.push("5", "6", "7", "8", "9"))
+ (5..9).each do |i|
+ assert_external_string_equal(i.to_s, history[i])
+ end
+ assert_equal(10, history.length)
+ end
+
+ def test_pop
+ history = history_new
+ begin
+ assert_equal(nil, history.pop)
+
+ history, lines = lines = history_new_and_push_history(5)
+ (1..5).each do |i|
+ assert_external_string_equal(lines[-i], history.pop)
+ assert_equal(lines.length - i, history.length)
+ end
+
+ assert_equal(nil, history.pop)
+ rescue NotImplementedError
+ end
+ end
+
+ def test_shift
+ history = history_new
+ begin
+ assert_equal(nil, history.shift)
+
+ history, lines = lines = history_new_and_push_history(5)
+ (0..4).each do |i|
+ assert_external_string_equal(lines[i], history.shift)
+ assert_equal(lines.length - (i + 1), history.length)
+ end
+
+ assert_equal(nil, history.shift)
+ rescue NotImplementedError
+ end
+ end
+
+ def test_each
+ history = history_new
+ e = history.each do |s|
+ assert(false) # not reachable
+ end
+ assert_equal(history, e)
+ history, lines = lines = history_new_and_push_history(5)
+ i = 0
+ e = history.each do |s|
+ assert_external_string_equal(history[i], s)
+ assert_external_string_equal(lines[i], s)
+ i += 1
+ end
+ assert_equal(history, e)
+ end
+
+ def test_each__enumerator
+ history = history_new
+ e = history.each
+ assert_instance_of(Enumerator, e)
+ end
+
+ def test_length
+ history = history_new
+ assert_equal(0, history.length)
+ push_history(history, 1)
+ assert_equal(1, history.length)
+ push_history(history, 4)
+ assert_equal(5, history.length)
+ history.clear
+ assert_equal(0, history.length)
+ end
+
+ def test_empty_p
+ history = history_new
+ 2.times do
+ assert(history.empty?)
+ history.push("s")
+ assert_equal(false, history.empty?)
+ history.clear
+ assert(history.empty?)
+ end
+ end
+
+ def test_delete_at
+ begin
+ history, lines = lines = history_new_and_push_history(5)
+ (0..4).each do |i|
+ assert_external_string_equal(lines[i], history.delete_at(0))
+ end
+ assert(history.empty?)
+
+ history, lines = lines = history_new_and_push_history(5)
+ (1..5).each do |i|
+ assert_external_string_equal(lines[lines.length - i], history.delete_at(-1))
+ end
+ assert(history.empty?)
+
+ history, lines = lines = history_new_and_push_history(5)
+ assert_external_string_equal(lines[0], history.delete_at(0))
+ assert_external_string_equal(lines[4], history.delete_at(3))
+ assert_external_string_equal(lines[1], history.delete_at(0))
+ assert_external_string_equal(lines[3], history.delete_at(1))
+ assert_external_string_equal(lines[2], history.delete_at(0))
+ assert(history.empty?)
+ rescue NotImplementedError
+ end
+ end
+
+ def test_delete_at__out_of_range
+ history = history_new
+ assert_raise(IndexError, NotImplementedError, "index=<0>") do
+ history.delete_at(0)
+ end
+
+ history, _ = history_new_and_push_history(5)
+ invalid_indexes = [5, 6, 100, -6, -7, -100]
+ invalid_indexes.each do |i|
+ assert_raise(IndexError, NotImplementedError, "index=<#{i}>") do
+ history.delete_at(i)
+ end
+ end
+
+ invalid_indexes = [100_000_000_000_000_000_000,
+ -100_000_000_000_000_000_000]
+ invalid_indexes.each do |i|
+ assert_raise(RangeError, NotImplementedError, "index=<#{i}>") do
+ history.delete_at(i)
+ end
+ end
+ end
+
+ private
+
+ def history_new(history_size: 10)
+ Reline::History.new(Struct.new(:history_size).new(history_size))
+ end
+
+ def push_history(history, num)
+ lines = []
+ num.times do |i|
+ s = "a"
+ i.times do
+ s = s.succ
+ end
+ lines.push("#{i + 1}:#{s}")
+ end
+ history.push(*lines)
+ return history, lines
+ end
+
+ def history_new_and_push_history(num)
+ history = history_new(history_size: 100)
+ return push_history(history, num)
+ end
+
+ def assert_external_string_equal(expected, actual)
+ assert_equal(expected, actual)
+ assert_equal(get_default_internal_encoding, actual.encoding)
+ end
+
+ def get_default_internal_encoding
+ return Encoding.default_internal || Encoding.find("locale")
+ end
+end