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
Current section
Files
lib/raxol/terminal/buffer/line_operations/insertion.ex
defmodule Raxol.Terminal.Buffer.LineOperations.Insertion do
@moduledoc """
Line insertion operations for terminal buffers.
Handles insertion of single and multiple lines with style support.
"""
@doc """
Insert empty lines at the current cursor position.
"""
def insert_lines(buffer, count) do
{_x, y} = buffer.cursor_position
insert_lines(buffer, y, count)
end
def insert_lines(buffer, y, count) do
do_insert_lines(buffer, y, count, %{})
end
def insert_lines(buffer, y, count, style) do
do_insert_lines(buffer, y, count, style)
end
def insert_lines(buffer, y, count, scroll_top, scroll_bottom) do
do_insert_lines_with_style(buffer, y, count, scroll_top, scroll_bottom)
end
def insert_lines(buffer, y, count, scroll_top, scroll_bottom, _style) do
# do_insert_lines_in_region already creates blank lines with style
do_insert_lines_in_region(buffer, y, count, scroll_top, scroll_bottom)
end
@doc """
Internal insertion with default style.
"""
def do_insert_lines(buffer, y, count, style) do
alias Raxol.Terminal.ScreenBuffer.DataAdapter
DataAdapter.with_lines_format(buffer, fn buffer_with_lines ->
lines = Map.get(buffer_with_lines, :lines, %{})
height = Map.get(buffer_with_lines, :height, 24)
width = Map.get(buffer_with_lines, :width, 80)
# Build new lines using pattern matching
new_lines =
0..(height - 1)
|> Enum.map(fn line_y ->
{line_y, build_line_at_position(lines, line_y, y, count, width, style, height)}
end)
|> Enum.reject(fn {_line_y, line} -> is_nil(line) end)
|> Enum.into(%{})
%{buffer_with_lines | lines: new_lines}
end)
end
@doc """
Insert lines with style in a scroll region.
"""
def do_insert_lines_with_style(buffer, y, count, scroll_top, scroll_bottom) do
do_insert_lines_in_region(buffer, y, count, scroll_top, scroll_bottom)
end
# Helper functions
defp do_insert_lines_in_region(buffer, y, count, top, bottom) do
alias Raxol.Terminal.ScreenBuffer.DataAdapter
DataAdapter.with_lines_format(buffer, fn buffer_with_lines ->
lines = Map.get(buffer_with_lines, :lines, %{})
height = Map.get(buffer_with_lines, :height, 24)
width = Map.get(buffer_with_lines, :width, 80)
new_lines =
Enum.reduce(0..(height - 1), %{}, fn line_y, acc ->
cond do
# Outside scroll region - keep unchanged
line_y < top or line_y > bottom ->
original_line = Map.get(lines, line_y)
Map.put(acc, line_y, original_line)
# Before insertion point - keep unchanged
line_y < y ->
Map.put(acc, line_y, Map.get(lines, line_y))
# New inserted lines
line_y < y + count ->
Map.put(acc, line_y, create_empty_line(width, %{}))
# Shifted lines within region
line_y <= bottom ->
source_y = line_y - count
if source_y <= bottom - count do
Map.put(acc, line_y, Map.get(lines, source_y))
else
acc
end
true ->
acc
end
end)
%{buffer_with_lines | lines: new_lines}
end)
end
defp create_empty_line(width, style) do
Enum.map(0..(width - 1), fn _ -> %{char: " ", style: style} end)
end
# Pattern match on line position relative to insertion point
defp build_line_at_position(
lines,
line_y,
insert_y,
_count,
_width,
_style,
_height
)
when line_y < insert_y do
# Lines before insertion point stay the same
Map.get(lines, line_y)
end
defp build_line_at_position(
_lines,
line_y,
insert_y,
count,
width,
style,
_height
)
when line_y >= insert_y and line_y < insert_y + count do
# Insert new empty lines
create_empty_line(width, style)
end
defp build_line_at_position(
lines,
line_y,
_insert_y,
count,
_width,
_style,
height
)
when line_y < height do
# Shift remaining lines down if they fit
source_y = line_y - count
build_shifted_line(lines, source_y, height - count)
end
defp build_line_at_position(
_lines,
_line_y,
_insert_y,
_count,
_width,
_style,
_height
) do
nil
end
defp build_shifted_line(lines, source_y, max_source)
when source_y < max_source do
Map.get(lines, source_y)
end
defp build_shifted_line(_lines, _source_y, _max_source), do: nil
end