Raxol.Core Buffer API Reference
View SourceComplete 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 charactersheight- 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 tox- 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() | nilRetrieves the cell at the specified coordinates.
Parameters:
buffer- The buffer to read fromx- 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)
# => nilPerformance: 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 updatex- 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_atfor 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 resizewidth- New widthheight- 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 statenew_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 cellsOptimization:
- 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 | :dashedFunctions
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 onx- X coordinate (left edge)y- Y coordinate (top edge)width- Width of the boxheight- Height of the boxstyle- 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 onx- Starting X coordinatey- Y coordinate (row)length- Length of the linechar- 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 onx- X coordinate (column)y- Starting Y coordinatelength- Length of the linechar- 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 onx- X coordinate (left edge)y- Y coordinate (top edge)width- Width of the areaheight- Height of the areachar- Character to fill withstyle- 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:
| Operation | Target | Actual (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 | < 1ms | 0.04-0.6ms | ✅ |
| draw_line | < 1ms | 0.01ms | ✅ |
| fill_area (small) | < 1ms | 0.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)