Drafter.Terminal.InputBuffer (drafter v0.3.2)

Copy Markdown View Source

Carries partially-received terminal input across reads.

Terminal input arrives in arbitrary chunks, and a control sequence can be split across two of them. feed/2 decodes whatever is complete and holds the undecided tail until the rest arrives.

A trailing lone ESC is held too, since it is both a complete keypress and the start of every escape sequence. flush/1 resolves it as the escape key. Whenever bytes are held, an :input_flush message is scheduled to the calling process after flush_after_ms/0; drivers resolve the tail by calling flush/1 on receipt.

iex> buffer = Drafter.Terminal.InputBuffer.new()
iex> {events, buffer} = Drafter.Terminal.InputBuffer.feed(buffer, "ab\e[")
iex> events
[{:key, :a}, {:key, :b}]
iex> {events, _buffer} = Drafter.Terminal.InputBuffer.feed(buffer, "A")
iex> events
[{:key, :up}]

Summary

Functions

Append a chunk and decode whatever is now complete.

Resolve any held bytes without waiting for more input.

Milliseconds of silence after which a held sequence is resolved.

An empty buffer, holding no bytes and with no flush scheduled.

Discard held bytes and any pending flush, decoding nothing.

Types

t()

@type t() :: %Drafter.Terminal.InputBuffer{
  pending: binary(),
  timer: reference() | nil
}

Functions

feed(buffer, data)

@spec feed(t(), binary()) :: {[Drafter.Terminal.ANSI.event()], t()}

Append a chunk and decode whatever is now complete.

Returns the decoded events and the updated buffer. When bytes remain undecided an :input_flush message is scheduled to the calling process, replacing any previously scheduled one.

iex> {events, buffer} = Drafter.Terminal.InputBuffer.feed(Drafter.Terminal.InputBuffer.new(), "hi\e")
iex> events
[{:key, :h}, {:key, :i}]
iex> buffer.pending
"\e"

flush(buffer)

@spec flush(t()) :: {[Drafter.Terminal.ANSI.event()], t()}

Resolve any held bytes without waiting for more input.

Call on :input_flush, or when the input stream closes. Any scheduled flush is cancelled.

iex> {_events, buffer} = Drafter.Terminal.InputBuffer.feed(Drafter.Terminal.InputBuffer.new(), "\e")
iex> Drafter.Terminal.InputBuffer.flush(buffer)
{[{:key, :escape}], %Drafter.Terminal.InputBuffer{pending: "", timer: nil}}

An empty buffer yields no events.

iex> Drafter.Terminal.InputBuffer.flush(Drafter.Terminal.InputBuffer.new())
{[], %Drafter.Terminal.InputBuffer{pending: "", timer: nil}}

flush_after_ms()

@spec flush_after_ms() :: pos_integer()

Milliseconds of silence after which a held sequence is resolved.

iex> Drafter.Terminal.InputBuffer.flush_after_ms()
40

new()

@spec new() :: t()

An empty buffer, holding no bytes and with no flush scheduled.

iex> Drafter.Terminal.InputBuffer.new()
%Drafter.Terminal.InputBuffer{pending: "", timer: nil}

reset(buffer)

@spec reset(t()) :: t()

Discard held bytes and any pending flush, decoding nothing.

iex> {_events, buffer} = Drafter.Terminal.InputBuffer.feed(Drafter.Terminal.InputBuffer.new(), "\e[")
iex> Drafter.Terminal.InputBuffer.reset(buffer)
%Drafter.Terminal.InputBuffer{pending: "", timer: nil}