Packages
Multi-surface application runtime for Elixir. One TEA module renders to terminal, browser (LiveView), SSH, and MCP (agents). 30+ widgets, flexbox + CSS grid, AI agent runtime, distributed swarm with CRDTs, time-travel debugging, session recording, sandboxed REPL, and agentic commerce.
Current section
Files
Jump to
Current section
Files
lib/raxol/terminal/buffer/line_editor.ex
defmodule Raxol.Terminal.Buffer.LineEditor do
@moduledoc """
Provides functionality for line editing operations in the terminal buffer.
"""
alias Raxol.Terminal.ANSI.TextFormatting
alias Raxol.Terminal.Cell
alias Raxol.Terminal.ScreenBuffer
@doc """
Inserts a specified number of blank lines at the given row index using the provided default_style.
Existing lines from the insertion point downwards are shifted down.
Lines shifted off the bottom of the buffer are discarded.
Uses the buffer's default style for new lines.
"""
@spec insert_lines(
ScreenBuffer.t(),
integer(),
integer(),
TextFormatting.text_style()
) :: ScreenBuffer.t()
def insert_lines(%{__struct__: _} = buffer, row, count, default_style)
when row >= 0 and count > 0 do
# Ensure row is within bounds
eff_row = min(row, buffer.height - 1)
# Create blank lines with the provided default style
blank_cell = %Cell{style: default_style, char: " "}
blank_line = List.duplicate(blank_cell, buffer.width)
blank_lines_to_insert = List.duplicate(blank_line, count)
# Split the buffer cells at the insertion row
{top_part, bottom_part} = Enum.split(buffer.cells, eff_row)
# Take lines from the bottom part that will fit after insertion
# We want to keep as many lines as possible, but ensure we don't exceed buffer height
max_lines_after_insertion = buffer.height - eff_row - count
kept_bottom_part = Enum.take(bottom_part, max_lines_after_insertion)
# Combine the parts
new_cells = top_part ++ blank_lines_to_insert ++ kept_bottom_part
# Ensure we don't exceed the buffer height
final_cells = Enum.take(new_cells, buffer.height)
%{buffer | cells: final_cells}
end
def insert_lines(buffer, _row, _count, _blank_cell) when is_tuple(buffer) do
raise ArgumentError,
"Expected buffer struct, got tuple (did you pass result of get_dimensions/1?)"
end
@doc """
Deletes a specified number of lines starting from the given row index.
Lines below the deleted lines are shifted up.
Blank lines are added at the bottom of the buffer to fill the space using the provided default_style.
Uses the buffer's default style for new lines.
"""
@spec delete_lines(
ScreenBuffer.t(),
integer(),
integer(),
TextFormatting.text_style()
) :: ScreenBuffer.t()
def delete_lines(%ScreenBuffer{} = buffer, row, count, default_style)
when row >= 0 and count > 0 do
# Ensure row is within bounds
eff_row = min(row, buffer.height - 1)
# Calculate actual number of lines to delete
eff_count = min(count, buffer.height - eff_row)
# Create blank lines with the provided default style to add at the bottom
blank_cell = %Cell{style: default_style, char: " "}
blank_line = List.duplicate(blank_cell, buffer.width)
blank_lines_to_add = List.duplicate(blank_line, eff_count)
# Split the buffer cells at the deletion row
{top_part, part_to_modify} = Enum.split(buffer.cells, eff_row)
# Skip the deleted lines and take the rest
bottom_part_kept = Enum.drop(part_to_modify, eff_count)
# Combine the parts
new_cells = top_part ++ bottom_part_kept ++ blank_lines_to_add
%{buffer | cells: new_cells}
end
# No-op for invalid input
def delete_lines(buffer, _row, _count, _default_style), do: buffer
end