Packages

Terminal emulation and driver infrastructure for Raxol. ANSI parsing, screen buffers, command processing, cursor management, input handling, session management, and termbox2 NIF integration.

Current section

Files

Jump to
raxol_terminal lib raxol terminal screen_buffer operations erasing.ex
Raw

lib/raxol/terminal/screen_buffer/operations/erasing.ex

defmodule Raxol.Terminal.ScreenBuffer.Operations.Erasing do
@moduledoc """
Erasing operations for the screen buffer.
This module handles various erasing operations including
erase in display and erase in line with different modes.
"""
alias Raxol.Terminal.Cell
alias Raxol.Terminal.ScreenBuffer
@doc """
Erases in display based on mode.
Mode values:
- 0: From cursor to end of display
- 1: From start to cursor
- 2: Entire display
- 3: Entire display including scrollback
"""
@spec erase_in_display(ScreenBuffer.t(), non_neg_integer(), map()) ::
ScreenBuffer.t()
def erase_in_display(buffer, mode, cursor_pos \\ %{x: 0, y: 0})
def erase_in_display(buffer, 0, cursor_pos) do
# Erase from cursor to end of display
erase_from_cursor_to_end(buffer, cursor_pos)
end
def erase_in_display(buffer, 1, cursor_pos) do
# Erase from start to cursor
erase_from_start_to_cursor(buffer, cursor_pos)
end
def erase_in_display(buffer, 2, _cursor_pos) do
# Erase entire display
clear_display(buffer)
end
def erase_in_display(buffer, 3, _cursor_pos) do
# Erase entire display including scrollback
clear_display_with_scrollback(buffer)
end
def erase_in_display(buffer, _, _), do: buffer
@doc """
Erases in line based on mode.
Mode values:
- 0: From cursor to end of line
- 1: From start of line to cursor
- 2: Entire line
"""
@spec erase_in_line(ScreenBuffer.t(), non_neg_integer(), map()) ::
ScreenBuffer.t()
def erase_in_line(buffer, mode, cursor_pos \\ %{x: 0, y: 0})
def erase_in_line(buffer, 0, cursor_pos) do
# Erase from cursor to end of line
erase_line_from_cursor_to_end(buffer, cursor_pos)
end
def erase_in_line(buffer, 1, cursor_pos) do
# Erase from start of line to cursor
erase_line_from_start_to_cursor(buffer, cursor_pos)
end
def erase_in_line(buffer, 2, cursor_pos) do
# Erase entire line
clear_line(buffer, cursor_pos.y)
end
def erase_in_line(buffer, _, _), do: buffer
# Private helper functions
defp erase_from_cursor_to_end(buffer, %{x: x, y: y}) do
cells = buffer.cells || []
new_cells =
cells
|> Enum.with_index()
|> Enum.map(fn {row, row_idx} ->
cond do
row_idx < y -> row
row_idx == y -> erase_row_from(row, x)
true -> create_empty_row(buffer.width)
end
end)
%{buffer | cells: new_cells}
end
defp erase_from_start_to_cursor(buffer, %{x: x, y: y}) do
cells = buffer.cells || []
new_cells =
cells
|> Enum.with_index()
|> Enum.map(fn {row, row_idx} ->
cond do
row_idx < y -> create_empty_row(buffer.width)
row_idx == y -> erase_row_to(row, x)
true -> row
end
end)
%{buffer | cells: new_cells}
end
defp erase_line_from_cursor_to_end(buffer, %{x: x, y: y}) do
cells = buffer.cells || []
new_cells =
cells
|> Enum.with_index()
|> Enum.map(fn {row, row_idx} ->
if row_idx == y do
erase_row_from(row, x)
else
row
end
end)
%{buffer | cells: new_cells}
end
defp erase_line_from_start_to_cursor(buffer, %{x: x, y: y}) do
cells = buffer.cells || []
new_cells =
cells
|> Enum.with_index()
|> Enum.map(fn {row, row_idx} ->
if row_idx == y do
erase_row_to(row, x)
else
row
end
end)
%{buffer | cells: new_cells}
end
defp clear_display(buffer) do
empty_cells =
for _ <- 0..(buffer.height - 1) do
create_empty_row(buffer.width)
end
%{buffer | cells: empty_cells}
end
defp clear_display_with_scrollback(buffer) do
buffer
|> clear_display()
|> Map.put(:scrollback, [])
end
defp clear_line(buffer, y) when y >= 0 and y < buffer.height do
cells = buffer.cells || []
new_cells =
cells
|> Enum.with_index()
|> Enum.map(fn {row, row_idx} ->
if row_idx == y do
create_empty_row(buffer.width)
else
row
end
end)
%{buffer | cells: new_cells}
end
defp clear_line(buffer, _), do: buffer
defp create_empty_row(width) do
List.duplicate(Cell.new(), width)
end
defp erase_row_from(row, x) do
row
|> Enum.with_index()
|> Enum.map(fn {cell, idx} ->
if idx >= x, do: Cell.new(), else: cell
end)
end
defp erase_row_to(row, x) do
row
|> Enum.with_index()
|> Enum.map(fn {cell, idx} ->
if idx <= x, do: Cell.new(), else: cell
end)
end
end