diff options
author | aycabta <aycabta@gmail.com> | 2019-04-27 14:53:09 +0900 |
---|---|---|
committer | aycabta <aycabta@gmail.com> | 2019-04-30 11:44:20 +0900 |
commit | 17350c7e5534c8678097d70698fe08614a6c3997 (patch) | |
tree | 0f41959093014a97d50aeb06a052f5450b4cb6b1 /lib/reline/kill_ring.rb | |
parent | eb45ba61160dbae412407f232fe9b3252eb99362 (diff) | |
download | ruby-17350c7e5534c8678097d70698fe08614a6c3997.tar.gz |
Add Reline as a fallback library for Readline
* lib/reine.rb, lib/reline/*: Reline is a readline stdlib compatible
library.
* lib/readline.rb: Readline uses a fallback to Reline when ext/readline
doesn't exist.
* tool/sync_default_gems.rb: add ruby/reline as a default gem.
* appveyor.yml: add "set RELINE_TEST_ENCODING=Windows-31J" for test suit
of Reline, and add "--exclude readline" to "nmake test-all" on Visual
Studio builds because of strange behavior.
* spec/ruby/library/readline/spec_helper.rb: skip Reline as with
RbReadline.
Diffstat (limited to 'lib/reline/kill_ring.rb')
-rw-r--r-- | lib/reline/kill_ring.rb | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/lib/reline/kill_ring.rb b/lib/reline/kill_ring.rb new file mode 100644 index 0000000000..842fd04697 --- /dev/null +++ b/lib/reline/kill_ring.rb @@ -0,0 +1,113 @@ +class Reline::KillRing + module State + FRESH = :fresh + CONTINUED = :continued + PROCESSED = :processed + YANK = :yank + end + + RingPoint = Struct.new(:backward, :forward, :str) do + def initialize(str) + super(nil, nil, str) + end + + def ==(other) + object_id == other.object_id + end + end + + class RingBuffer + attr_reader :size + attr_reader :head + + def initialize(max = 1024) + @max = max + @size = 0 + @head = nil # reading head of ring-shaped tape + end + + def <<(point) + if @size.zero? + @head = point + @head.backward = @head + @head.forward = @head + @size = 1 + elsif @size >= @max + tail = @head.forward + new_tail = tail.forward + @head.forward = point + point.backward = @head + new_tail.backward = point + point.forward = new_tail + @head = point + else + tail = @head.forward + @head.forward = point + point.backward = @head + tail.backward = point + point.forward = tail + @head = point + @size += 1 + end + end + + def empty? + @size.zero? + end + end + + def initialize(max = 1024) + @ring = RingBuffer.new(max) + @ring_pointer = nil + @buffer = nil + @state = State::FRESH + end + + def append(string, before_p = false) + case @state + when State::FRESH, State::YANK + @ring << RingPoint.new(string) + @state = State::CONTINUED + when State::CONTINUED, State::PROCESSED + if before_p + @ring.head.str.prepend(string) + else + @ring.head.str.concat(string) + end + @state = State::CONTINUED + end + end + + def process + case @state + when State::FRESH + # nothing to do + when State::CONTINUED + @state = State::PROCESSED + when State::PROCESSED + @state = State::FRESH + when State::YANK + # nothing to do + end + end + + def yank + unless @ring.empty? + @state = State::YANK + @ring_pointer = @ring.head + @ring_pointer.str + else + nil + end + end + + def yank_pop + if @state == State::YANK + prev_yank = @ring_pointer.str + @ring_pointer = @ring_pointer.backward + [@ring_pointer.str, prev_yank] + else + nil + end + end +end |