TermUI.Renderer.CursorOptimizer (TermUI v0.2.0)

View Source

Optimizes cursor movement by selecting the cheapest movement option.

Instead of always using absolute positioning (ESC[{row};{col}H), this module calculates the byte cost of various movement options and selects the minimum. This can reduce cursor movement overhead by 40%+ compared to naive positioning.

Movement Options

  • Absolute positioning: ESC[{r};{c}H (6-10 bytes)
  • Relative up/down/left/right: ESC[{n}A/B/C/D (4-6 bytes)
  • Carriage return: \r (1 byte)
  • Newline: \n (1 byte)
  • Home: ESC[H (3 bytes)
  • Literal spaces for small rightward moves (1 byte each)

Usage

# Create optimizer with initial position
optimizer = CursorOptimizer.new()

# Get optimal movement sequence
{sequence, new_optimizer} = CursorOptimizer.move_to(optimizer, 5, 10)

# After text output, advance cursor
new_optimizer = CursorOptimizer.advance(optimizer, 5)

Summary

Functions

Advances the cursor position after text output.

Returns the total bytes saved through optimization.

Calculates the byte cost of absolute positioning.

Calculates the byte cost of carriage return (move to column 1).

Calculates the byte cost of moving cursor down.

Calculates the byte cost of home (move to 1,1).

Calculates the byte cost of moving cursor left.

Calculates the byte cost of newline (move down one row).

Calculates the byte cost of moving cursor right.

Calculates the byte cost of moving cursor up.

Moves the cursor to the target position using the optimal movement sequence.

Creates a new cursor optimizer with cursor at position (1, 1).

Creates a cursor optimizer with cursor at the specified position.

Finds the optimal movement sequence from current to target position.

Returns the current cursor position as {row, col}.

Resets the cursor position to (1, 1).

Types

t()

@type t() :: %TermUI.Renderer.CursorOptimizer{
  bytes_saved: non_neg_integer(),
  col: pos_integer(),
  row: pos_integer()
}

Functions

advance(optimizer, cols)

@spec advance(t(), non_neg_integer()) :: t()

Advances the cursor position after text output.

Call this after outputting text to keep cursor position synchronized.

bytes_saved(cursor_optimizer)

@spec bytes_saved(t()) :: non_neg_integer()

Returns the total bytes saved through optimization.

cost_absolute(row, col)

@spec cost_absolute(pos_integer(), pos_integer()) :: pos_integer()

Calculates the byte cost of absolute positioning.

ESC[{row};{col}H costs 4 + digits(row) + digits(col) bytes.

cost_cr()

@spec cost_cr() :: pos_integer()

Calculates the byte cost of carriage return (move to column 1).

cost_down(n)

@spec cost_down(pos_integer()) :: pos_integer()

Calculates the byte cost of moving cursor down.

ESC[{n}B costs 3 + digits(n) bytes, or 3 bytes for n=1.

cost_home()

@spec cost_home() :: pos_integer()

Calculates the byte cost of home (move to 1,1).

cost_left(n)

@spec cost_left(pos_integer()) :: pos_integer()

Calculates the byte cost of moving cursor left.

ESC[{n}D costs 3 + digits(n) bytes, or 3 bytes for n=1.

cost_lf()

@spec cost_lf() :: pos_integer()

Calculates the byte cost of newline (move down one row).

cost_right(n)

@spec cost_right(pos_integer()) :: pos_integer()

Calculates the byte cost of moving cursor right.

ESC[{n}C costs 3 + digits(n) bytes, or 3 bytes for n=1.

cost_up(n)

@spec cost_up(pos_integer()) :: pos_integer()

Calculates the byte cost of moving cursor up.

ESC[{n}A costs 3 + digits(n) bytes, or 3 bytes for n=1.

move_to(optimizer, target_row, target_col)

@spec move_to(t(), pos_integer(), pos_integer()) :: {iodata(), t()}

Moves the cursor to the target position using the optimal movement sequence.

Returns {sequence, updated_optimizer} where sequence is iodata containing the escape sequences for the movement.

Examples

iex> optimizer = CursorOptimizer.new()
iex> {seq, _opt} = CursorOptimizer.move_to(optimizer, 1, 5)
iex> IO.iodata_to_binary(seq)
"\e[5C"

new()

@spec new() :: t()

Creates a new cursor optimizer with cursor at position (1, 1).

new(row, col)

@spec new(pos_integer(), pos_integer()) :: t()

Creates a cursor optimizer with cursor at the specified position.

optimal_move(from_row, from_col, to_row, to_col)

@spec optimal_move(pos_integer(), pos_integer(), pos_integer(), pos_integer()) ::
  {iodata(), pos_integer()}

Finds the optimal movement sequence from current to target position.

Returns {sequence, cost} where sequence is iodata.

position(cursor_optimizer)

@spec position(t()) :: {pos_integer(), pos_integer()}

Returns the current cursor position as {row, col}.

reset(optimizer)

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

Resets the cursor position to (1, 1).