Pure helpers for editing a (value, cursor) pair.
The cursor is an integer index into String.graphemes(value) — 0 is
before the first grapheme, length(graphemes) is after the last. This
matches what users expect ("the cursor is between graphemes, not codepoints").
Display column position is a separate concern, computed by cursor_column/2
using Harlock.Width.
These functions are owned by the app's model. The text_input element
is a dumb renderer that reads :value and :cursor out of the model
— no internal state. The app's update/2 calls these helpers to react
to key events.
Typical usage:
def update({:key, key, mods}, model) do
case Harlock.Focus.current() do
:search ->
case TextBuffer.apply_key({:key, key, mods}, model.search, model.search_cursor) do
{:edit, v, c} -> %{model | search: v, search_cursor: c}
:submit -> trigger_search(model)
:noop -> model
end
_ ->
model
end
end
Summary
Functions
Map a raw {:key, key, mods} event to an edit. Returns one of
Display column corresponding to a cursor index — the sum of display widths of all graphemes before the cursor. Useful for positioning the terminal cursor in the renderer.
Delete the grapheme before the cursor. No-op at position 0.
Delete the grapheme after the cursor. No-op at end.
Move cursor to the end (one past the last grapheme).
Move cursor to the start.
Insert a string at the cursor. Returns the new value and cursor.
Move cursor one grapheme left. Clamps at 0.
Move cursor one grapheme right. Clamps at the end.
Types
@type cursor() :: non_neg_integer()
Functions
@spec apply_key({:key, any(), [atom()]}, String.t(), cursor()) :: input_event()
Map a raw {:key, key, mods} event to an edit. Returns one of:
{:edit, value, cursor}— model should adopt the new pair:submit— user pressed Enter; app handles submission:noop— key isn't part of the text-input vocabulary, ignore
@spec cursor_column(String.t(), cursor()) :: non_neg_integer()
Display column corresponding to a cursor index — the sum of display widths of all graphemes before the cursor. Useful for positioning the terminal cursor in the renderer.
Delete the grapheme before the cursor. No-op at position 0.
Delete the grapheme after the cursor. No-op at end.
Move cursor to the end (one past the last grapheme).
@spec home() :: cursor()
Move cursor to the start.
Insert a string at the cursor. Returns the new value and cursor.
Move cursor one grapheme left. Clamps at 0.
Move cursor one grapheme right. Clamps at the end.