Packages

Core behaviours, utilities, events, config, accessibility, and plugin infrastructure for Raxol. Zero external runtime dependencies. Provides BaseManager, event system, plugin lifecycle, keyboard/focus management, and accessibility primitives.

Current section

Files

Jump to
raxol_core lib raxol core buffer.ex
Raw

lib/raxol/core/buffer.ex

defmodule Raxol.Core.Buffer do
@moduledoc """
Lightweight terminal buffer primitives for Raxol v2.0.
This module provides pure functional buffer operations without framework dependencies.
Designed to be used standalone or as the foundation for higher-level abstractions.
## Buffer Structure
%{
lines: [
%{cells: [
%{char: " ", style: %{bold: false, fg_color: nil, bg_color: nil}}
]}
],
width: 80,
height: 24
}
## Performance Targets
- Operations complete in < 1ms for 80x24 buffer
- Zero external dependencies
- Memory efficient
## Examples
# Create a blank buffer
buffer = Raxol.Core.Buffer.create_blank_buffer(80, 24)
# Write text at coordinates
buffer = Raxol.Core.Buffer.write_at(buffer, 5, 3, "Hello, Raxol!")
# Get a specific cell
cell = Raxol.Core.Buffer.get_cell(buffer, 5, 3)
# Render to string
output = Raxol.Core.Buffer.to_string(buffer)
"""
@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()
}
@doc """
Creates a blank buffer with the specified dimensions.
## Parameters
- `width` - Width of the buffer in characters
- `height` - Height of the buffer in lines
## Examples
iex> buffer = Raxol.Core.Buffer.create_blank_buffer(80, 24)
iex> buffer.width
80
iex> buffer.height
24
"""
@spec create_blank_buffer(non_neg_integer(), non_neg_integer()) :: t()
def create_blank_buffer(width, height) do
blank_cell = %{char: " ", style: %{}}
blank_line = %{cells: List.duplicate(blank_cell, width)}
lines = List.duplicate(blank_line, height)
%{
lines: lines,
width: width,
height: height
}
end
@doc """
Writes text at the specified coordinates with optional styling.
## Parameters
- `buffer` - The buffer to write to
- `x` - X coordinate (column)
- `y` - Y coordinate (row)
- `content` - Text to write
- `style` - Optional style map (default: %{})
## Examples
iex> buffer = Raxol.Core.Buffer.create_blank_buffer(80, 24)
iex> buffer = Raxol.Core.Buffer.write_at(buffer, 0, 0, "Hello")
iex> cell = Raxol.Core.Buffer.get_cell(buffer, 0, 0)
iex> cell.char
"H"
"""
@spec write_at(t(), non_neg_integer(), non_neg_integer(), String.t(), map()) ::
t()
def write_at(buffer, x, y, content, style \\ %{}) do
content
|> String.graphemes()
|> Enum.with_index()
|> Enum.reduce(buffer, fn {char, index}, acc_buffer ->
set_cell(acc_buffer, x + index, y, char, style)
end)
end
@doc """
Retrieves the cell at the specified coordinates.
## Parameters
- `buffer` - The buffer to read from
- `x` - X coordinate (column)
- `y` - Y coordinate (row)
## Returns
The cell at the specified position, or `nil` if out of bounds.
"""
@spec get_cell(t(), non_neg_integer(), non_neg_integer()) :: cell() | nil
def get_cell(%{lines: lines, width: width, height: height}, x, y) do
cond do
y >= height or y < 0 ->
nil
x >= width or x < 0 ->
nil
true ->
lines
|> Enum.at(y)
|> Map.get(:cells)
|> Enum.at(x)
end
end
@doc """
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
- `style` - Style to apply
"""
@spec set_cell(t(), non_neg_integer(), non_neg_integer(), String.t(), map()) ::
t()
def set_cell(
%{lines: lines, width: width, height: height} = buffer,
x,
y,
char,
style
) do
cond do
y >= height or y < 0 ->
buffer
x >= width or x < 0 ->
buffer
true ->
new_cell = %{char: char, style: style}
updated_lines =
List.update_at(lines, y, fn line ->
updated_cells =
List.update_at(line.cells, x, fn _old_cell -> new_cell end)
%{cells: updated_cells}
end)
%{buffer | lines: updated_lines}
end
end
@doc """
Clears the buffer, resetting all cells to blank.
## Parameters
- `buffer` - The buffer to clear
"""
@spec clear(t()) :: t()
def clear(%{width: width, height: height}) do
create_blank_buffer(width, height)
end
@doc """
Resizes the buffer to new dimensions.
## Parameters
- `buffer` - The buffer to resize
- `width` - New width
- `height` - New height
"""
@spec resize(t(), non_neg_integer(), non_neg_integer()) :: t()
def resize(%{lines: old_lines, width: old_width}, new_width, new_height) do
blank_cell = %{char: " ", style: %{}}
resized_lines =
old_lines
|> Enum.take(new_height)
|> Enum.map(fn line ->
cells = line.cells
cond do
new_width > old_width ->
%{cells: cells ++ List.duplicate(blank_cell, new_width - old_width)}
new_width < old_width ->
%{cells: Enum.take(cells, new_width)}
true ->
line
end
end)
lines_to_add = new_height - length(resized_lines)
new_lines =
if lines_to_add > 0 do
blank_line = %{cells: List.duplicate(blank_cell, new_width)}
resized_lines ++ List.duplicate(blank_line, lines_to_add)
else
resized_lines
end
%{
lines: new_lines,
width: new_width,
height: new_height
}
end
@doc """
Converts the buffer to a string representation for debugging.
## Parameters
- `buffer` - The buffer to convert
"""
@spec to_string(t()) :: String.t()
def to_string(%{lines: lines}) do
lines
|> Enum.map(fn line ->
line.cells
|> Enum.map(fn cell -> cell.char end)
|> Enum.join("")
end)
|> Enum.join("\n")
end
end