Raxol.Core Buffer API Reference

View Source

Complete API documentation for the Raxol.Core buffer primitives.

Overview

The Raxol.Core package provides lightweight, zero-dependency terminal buffer operations designed for standalone use or as a foundation for higher-level abstractions.

Modules

Raxol.Core.Buffer

Pure functional buffer operations for terminal rendering.

Types

@type cell :: %{
  char: String.t(),
  style: map()
}

@type line :: %{cells: list(cell())}

@type t :: %{
  lines: list(line()),
  width: non_neg_integer(),
  height: non_neg_integer()
}

Functions

create_blank_buffer/2
@spec create_blank_buffer(non_neg_integer(), non_neg_integer()) :: t()

Creates a blank buffer with the specified dimensions.

Parameters:

  • width - Width of the buffer in characters
  • height - Height of the buffer in lines

Returns: A new buffer with all cells initialized to blank spaces

Example:

buffer = Raxol.Core.Buffer.create_blank_buffer(80, 24)
# => %{lines: [...], width: 80, height: 24}

Performance: < 1ms for standard 80x24 buffer


write_at/5
@spec write_at(t(), non_neg_integer(), non_neg_integer(), String.t(), map()) :: t()

Writes text at the specified coordinates with optional styling.

Parameters:

  • buffer - The buffer to write to
  • x - X coordinate (column, 0-indexed)
  • y - Y coordinate (row, 0-indexed)
  • content - Text to write (will be split into graphemes)
  • style - Optional style map (default: %{})

Returns: Updated buffer with text written

Example:

buffer = Raxol.Core.Buffer.create_blank_buffer(80, 24)
buffer = Raxol.Core.Buffer.write_at(buffer, 5, 3, "Hello, World!")
buffer = Raxol.Core.Buffer.write_at(buffer, 5, 4, "Styled text", %{bold: true, fg_color: :blue})

Notes:

  • Text wraps character-by-character, no automatic line breaks
  • Out-of-bounds writes are silently ignored
  • Unicode graphemes are supported

Performance: < 1ms for typical strings


get_cell/3
@spec get_cell(t(), non_neg_integer(), non_neg_integer()) :: cell() | nil

Retrieves the cell at the specified coordinates.

Parameters:

  • buffer - The buffer to read from
  • x - X coordinate (column)
  • y - Y coordinate (row)

Returns: Cell at position, or nil if out of bounds

Example:

buffer = Raxol.Core.Buffer.write_at(buffer, 5, 3, "A")
cell = Raxol.Core.Buffer.get_cell(buffer, 5, 3)
# => %{char: "A", style: %{}}

cell = Raxol.Core.Buffer.get_cell(buffer, 1000, 1000)
# => nil

Performance: O(1) access time


set_cell/5
@spec set_cell(t(), non_neg_integer(), non_neg_integer(), String.t(), map()) :: t()

Updates a single cell at the specified coordinates.

Parameters:

  • buffer - The buffer to update
  • x - X coordinate (column)
  • y - Y coordinate (row)
  • char - Character to set (single grapheme)
  • style - Style to apply

Returns: Updated buffer

Example:

buffer = Raxol.Core.Buffer.set_cell(buffer, 10, 5, "█", %{bg_color: :red})

Notes:

  • Out-of-bounds updates are silently ignored
  • More efficient than write_at for single characters

Performance: < 100μs


clear/1
@spec clear(t()) :: t()

Clears the buffer, resetting all cells to blank.

Parameters:

  • buffer - The buffer to clear

Returns: New buffer with same dimensions, all cells blank

Example:

buffer = Raxol.Core.Buffer.clear(buffer)

Performance: Same as create_blank_buffer/2


resize/3
@spec resize(t(), non_neg_integer(), non_neg_integer()) :: t()

Resizes the buffer to new dimensions.

Parameters:

  • buffer - The buffer to resize
  • width - New width
  • height - New height

Returns: Resized buffer

Example:

# Expand buffer
buffer = Raxol.Core.Buffer.resize(buffer, 120, 40)

# Shrink buffer (content is cropped)
buffer = Raxol.Core.Buffer.resize(buffer, 40, 20)

Behavior:

  • Expanding: New cells are filled with blank spaces
  • Shrinking: Content is cropped from bottom and right
  • Existing content is preserved where it fits

Performance: < 2ms for standard sizes


to_string/1
@spec to_string(t()) :: String.t()

Converts the buffer to a string representation for debugging.

Parameters:

  • buffer - The buffer to convert

Returns: Multi-line string showing buffer contents

Example:

buffer = Raxol.Core.Buffer.create_blank_buffer(10, 3)
buffer = Raxol.Core.Buffer.write_at(buffer, 0, 0, "Hello")
buffer = Raxol.Core.Buffer.write_at(buffer, 0, 1, "World")

IO.puts(Raxol.Core.Buffer.to_string(buffer))
# Output:
# Hello
# World
#

Notes:

  • Styles are not rendered (use Raxol.Core.Renderer for styled output)
  • Useful for testing and debugging
  • Each line ends with a newline

Performance: < 1ms for standard buffers


Raxol.Core.Renderer

Pure functional rendering and diffing operations.

Functions

render_to_string/1
@spec render_to_string(Buffer.t()) :: String.t()

Renders buffer to plain ASCII string (no ANSI codes).

Parameters:

  • buffer - The buffer to render

Returns: String representation

Example:

output = Raxol.Core.Renderer.render_to_string(buffer)
IO.puts(output)

Performance: < 1ms for 80x24 buffer


render_diff/2
@spec render_diff(Buffer.t(), Buffer.t()) :: list(String.t())

Calculates minimal updates between two buffers.

Parameters:

  • old_buffer - Previous buffer state
  • new_buffer - New buffer state

Returns: List of ANSI cursor positioning and update sequences

Example:

old_buffer = Raxol.Core.Buffer.create_blank_buffer(80, 24)
new_buffer = Raxol.Core.Buffer.write_at(old_buffer, 5, 3, "Changed!")

diff = Raxol.Core.Renderer.render_diff(old_buffer, new_buffer)
# => ["\e[4;6HChanged!"]  # Only updates changed cells

Optimization:

  • Only generates updates for changed lines
  • Uses efficient Enum.zip for line comparison
  • Minimal ANSI escape sequences

Performance: < 2ms for 80x24 buffer (target met in benchmarks)


Raxol.Core.Style

Style management and ANSI escape code generation.

Functions

new/1
@spec new(keyword()) :: map()

Creates a new style map with validation.

Parameters:

  • opts - Keyword list of style options

Returns: Validated style map

Options:

  • :bold - Boolean
  • :italic - Boolean
  • :underline - Boolean
  • :fg_color - Foreground color (atom, RGB tuple, or integer)
  • :bg_color - Background color (atom, RGB tuple, or integer)

Example:

style = Raxol.Core.Style.new(bold: true, fg_color: :blue)
# => %{bold: true, fg_color: :blue}

merge/2
@spec merge(map(), map()) :: map()

Merges two style maps (second overrides first).

Example:

base = Raxol.Core.Style.new(bold: true, fg_color: :blue)
override = Raxol.Core.Style.new(fg_color: :red)
result = Raxol.Core.Style.merge(base, override)
# => %{bold: true, fg_color: :red}

rgb/3
@spec rgb(0..255, 0..255, 0..255) :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}

Creates an RGB color tuple.

Example:

color = Raxol.Core.Style.rgb(255, 100, 50)
style = Raxol.Core.Style.new(fg_color: color)

color_256/1
@spec color_256(0..255) :: non_neg_integer()

Creates a 256-color palette index.

Example:

color = Raxol.Core.Style.color_256(196)  # Bright red
style = Raxol.Core.Style.new(bg_color: color)

named_color/1
@spec named_color(atom()) :: atom()

Validates named colors.

Supported Colors: :black, :red, :green, :yellow, :blue, :magenta, :cyan, :white

Example:

color = Raxol.Core.Style.named_color(:blue)

to_ansi/1
@spec to_ansi(map()) :: String.t()

Converts style to ANSI escape codes.

Example:

style = Raxol.Core.Style.new(bold: true, fg_color: :blue)
ansi = Raxol.Core.Style.to_ansi(style)
# => "\e[1;34m"

Raxol.Core.Box

Box drawing and area fill utilities.

Types

@type box_style :: :single | :double | :rounded | :heavy | :dashed

Functions

draw_box/6
@spec draw_box(Buffer.t(), non_neg_integer(), non_neg_integer(),
               non_neg_integer(), non_neg_integer(), box_style()) :: Buffer.t()

Draws a box at the specified coordinates.

Parameters:

  • buffer - The buffer to draw on
  • x - X coordinate (left edge)
  • y - Y coordinate (top edge)
  • width - Width of the box
  • height - Height of the box
  • style - Box style (default: :single)

Box Styles:

  • :single - Single line (─│┌┐└┘)
  • :double - Double line (═║╔╗╚╝)
  • :rounded - Rounded corners (─│╭╮╰╯)
  • :heavy - Heavy/bold lines (━┃┏┓┗┛)
  • :dashed - Dashed lines (╌╎┌┐└┘)

Example:

buffer = Raxol.Core.Box.draw_box(buffer, 5, 3, 30, 10, :double)

Performance: 38-588μs depending on size and style


draw_horizontal_line/5
@spec draw_horizontal_line(Buffer.t(), non_neg_integer(), non_neg_integer(),
                           non_neg_integer(), String.t()) :: Buffer.t()

Draws a horizontal line.

Parameters:

  • buffer - The buffer to draw on
  • x - Starting X coordinate
  • y - Y coordinate (row)
  • length - Length of the line
  • char - Character to use (default: "-")

Example:

buffer = Raxol.Core.Box.draw_horizontal_line(buffer, 0, 0, 80, "=")

Performance: ~10μs for 20 characters


draw_vertical_line/5
@spec draw_vertical_line(Buffer.t(), non_neg_integer(), non_neg_integer(),
                         non_neg_integer(), String.t()) :: Buffer.t()

Draws a vertical line.

Parameters:

  • buffer - The buffer to draw on
  • x - X coordinate (column)
  • y - Starting Y coordinate
  • length - Length of the line
  • char - Character to use (default: "|")

Example:

buffer = Raxol.Core.Box.draw_vertical_line(buffer, 40, 0, 24, "║")

Performance: ~8μs for 10 characters


fill_area/7
@spec fill_area(Buffer.t(), non_neg_integer(), non_neg_integer(),
                non_neg_integer(), non_neg_integer(), String.t(), map()) :: Buffer.t()

Fills a rectangular area with a character and style.

Parameters:

  • buffer - The buffer to draw on
  • x - X coordinate (left edge)
  • y - Y coordinate (top edge)
  • width - Width of the area
  • height - Height of the area
  • char - Character to fill with
  • style - Style to apply (default: %{})

Example:

# Fill with background color
buffer = Raxol.Core.Box.fill_area(buffer, 10, 5, 20, 10, " ", %{bg_color: :blue})

# Fill with pattern
buffer = Raxol.Core.Box.fill_area(buffer, 10, 5, 20, 10, "░", %{})

Performance: ~44μs for 10x10 area, ~1.3ms for full 80x24 buffer


Performance Targets

All operations designed to complete in < 1ms for standard 80x24 buffers:

OperationTargetActual (avg)Status
create_blank_buffer< 1ms~0.5ms
write_at (short string)< 1ms~0.1ms
get_cell< 1ms~0.001ms
set_cell< 1ms~0.1ms
clear< 1ms~0.5ms
resize< 2ms~1ms
to_string< 1ms~0.5ms
render_diff< 2ms~2ms
draw_box< 1ms0.04-0.6ms
draw_line< 1ms0.01ms
fill_area (small)< 1ms0.04ms

See bench/core/ for detailed benchmarks.

Error Handling

All functions use defensive programming:

  • Out-of-bounds coordinates are silently ignored
  • Invalid dimensions default to minimum viable values
  • No exceptions thrown for normal usage
  • Pattern matching validates input types at compile time

Thread Safety

All modules are pure functional - no shared state:

  • Safe for concurrent use
  • No GenServers or processes
  • Immutable data structures throughout
  • Can be used in any context (LiveView, Phoenix, CLI, scripts)

See Also