# `Drafter.Terminal.InputBuffer`
[🔗](https://github.com/jaman/drafter/blob/main/lib/drafter/terminal/input_buffer.ex#L1)

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.

The buffer also remembers which mouse buttons are down, from the presses and
releases it decodes. A motion report that names a button the terminal never
reported pressed, or reported released since, is delivered as a `:move`, not a
`:drag`: a terminal in any-motion mode may encode a free pointer's motion with a
button number rather than the "no button" code.

It also remembers the last `{:cell_size, size}` and `{:pixel_mouse, on?}` reports it
decoded, and passes both to every parse, so SGR mouse reports are read as pixels
while the terminal has confirmed pixel mode and the cell size is known. See "Pixel
mouse reports" in `Drafter.Terminal.ANSI`.

    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}]

# `t`

```elixir
@type t() :: %Drafter.Terminal.InputBuffer{
  cell_size: {pos_integer(), pos_integer()} | nil,
  held: MapSet.t(atom()),
  key_release: boolean(),
  pending: binary(),
  pixel_mouse: boolean(),
  timer: reference() | nil
}
```

# `feed`

```elixir
@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`

```elixir
@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> {events, %Drafter.Terminal.InputBuffer{pending: "", timer: nil}} = Drafter.Terminal.InputBuffer.flush(buffer)
    iex> events
    [{:key, :escape}]

An empty buffer yields no events.

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

# `flush_after_ms`

```elixir
@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`

```elixir
@spec new(keyword()) :: t()
```

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

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

## Options

  * `:key_release` - `boolean()`, passed to every parse as
    `Drafter.Terminal.ANSI.parse_sequence/2` describes. Default `false`.

# `reset`

```elixir
@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{pending: "", timer: nil} = Drafter.Terminal.InputBuffer.reset(buffer)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
