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 scroll_ops.ex
Raw

lib/raxol/terminal/screen_buffer/scroll_ops.ex

defmodule Raxol.Terminal.ScreenBuffer.ScrollOps do
@moduledoc """
Scroll operations for ScreenBuffer: region management and scroll up/down.
"""
alias Raxol.Terminal.Cell
alias Raxol.Terminal.ScreenBuffer.Operations
# ========================================
# Region management
# ========================================
def scroll_to(buffer, top, bottom, line) do
Operations.scroll_to(buffer, top, bottom, line)
end
def reset_scroll_region(buffer), do: clear_scroll_region(buffer)
def get_scroll_top(buffer) do
case buffer.scroll_region do
nil -> 0
{top, _} -> top
end
end
def get_scroll_bottom(buffer) do
case buffer.scroll_region do
nil -> buffer.height - 1
{_, bottom} -> bottom
end
end
def set_scroll_region(buffer, {top, bottom}) do
Operations.set_region(buffer, top, bottom)
end
def set_scroll_region(buffer, top, bottom)
when is_integer(top) and is_integer(bottom) do
Operations.set_region(buffer, top, bottom)
end
def clear_scroll_region(buffer), do: %{buffer | scroll_region: nil}
def get_scroll_region_boundaries(buffer) do
case buffer.scroll_region do
nil -> {0, buffer.height - 1}
{top, bottom} -> {top, bottom}
end
end
def get_scroll_position(buffer), do: buffer.scroll_position || 0
def get_scroll_region(buffer), do: Operations.get_region(buffer)
def shift_region_to_line(buffer, region, target_line) do
Operations.shift_region_to_line(buffer, region, target_line)
end
def scroll_down_with_count(buffer, lines, count)
when is_integer(lines) and is_integer(count) do
Raxol.Terminal.Commands.Scrolling.scroll_down(
buffer,
lines,
buffer.scroll_region,
%{}
)
end
def scroll_down_with_count(buffer, _lines, count) when is_integer(count) do
scroll_down(buffer, count)
end
# ========================================
# Scroll content (formerly in Scrolling)
# ========================================
@doc """
Scrolls the buffer content up by the specified number of lines within the scroll region.
Returns {buffer, scrolled_lines}.
"""
def scroll_up(buffer, lines) when lines > 0 do
{top, bottom} = get_effective_scroll_region(buffer)
if top < bottom do
cells = buffer.cells || []
{before_region, region_and_after} = Enum.split(cells, top)
{region, after_region} = Enum.split(region_and_after, bottom - top + 1)
lines_to_scroll = min(lines, length(region))
scrolled_out = Enum.take(region, lines_to_scroll)
remaining_region = Enum.drop(region, lines_to_scroll)
empty_lines = List.duplicate(create_empty_line(buffer.width), lines_to_scroll)
scrolled_region = remaining_region ++ empty_lines
new_cells = before_region ++ scrolled_region ++ after_region
{%{buffer | cells: new_cells}, scrolled_out}
else
{buffer, []}
end
end
def scroll_up(buffer, _), do: {buffer, []}
@doc """
Scrolls the buffer content down by the specified number of lines within the scroll region.
"""
def scroll_down(buffer, lines) when lines > 0 do
{top, bottom} = get_effective_scroll_region(buffer)
if top < bottom do
cells = buffer.cells || []
{before_region, region_and_after} = Enum.split(cells, top)
{region, after_region} = Enum.split(region_and_after, bottom - top + 1)
lines_to_scroll = min(lines, length(region))
empty_lines = List.duplicate(create_empty_line(buffer.width), lines_to_scroll)
kept_region = Enum.take(region, length(region) - lines_to_scroll)
scrolled_region = empty_lines ++ kept_region
new_cells = before_region ++ scrolled_region ++ after_region
%{buffer | cells: new_cells}
else
buffer
end
end
def scroll_down(buffer, _), do: buffer
@doc """
Scrolls up within an explicit top/bottom region.
"""
def scroll_up(buffer, top, bottom, lines) do
{effective_top, effective_bottom} = normalize_scroll_region(buffer, top, bottom)
cells = buffer.cells || []
if effective_top < effective_bottom and lines > 0 do
{before_region, region_and_after} = Enum.split(cells, effective_top)
{region, after_region} = Enum.split(region_and_after, effective_bottom - effective_top + 1)
lines_to_scroll = min(lines, length(region))
kept_region = Enum.drop(region, lines_to_scroll)
empty_lines = List.duplicate(create_empty_line(buffer.width), lines_to_scroll)
scrolled_region = kept_region ++ empty_lines
new_cells = before_region ++ scrolled_region ++ after_region
%{buffer | cells: new_cells}
else
buffer
end
end
@doc """
Scrolls down within an explicit top/bottom region.
"""
def scroll_down(buffer, top, bottom, lines) do
{effective_top, effective_bottom} = normalize_scroll_region(buffer, top, bottom)
cells = buffer.cells || []
if effective_top < effective_bottom and lines > 0 do
{before_region, region_and_after} = Enum.split(cells, effective_top)
{region, after_region} = Enum.split(region_and_after, effective_bottom - effective_top + 1)
lines_to_scroll = min(lines, length(region))
empty_lines = List.duplicate(create_empty_line(buffer.width), lines_to_scroll)
kept_region = Enum.take(region, length(region) - lines_to_scroll)
scrolled_region = empty_lines ++ kept_region
new_cells = before_region ++ scrolled_region ++ after_region
%{buffer | cells: new_cells}
else
buffer
end
end
# Private helpers
defp get_effective_scroll_region(buffer) do
case buffer.scroll_region do
nil -> {0, buffer.height - 1}
{top, bottom} -> {top, min(bottom, buffer.height - 1)}
end
end
defp normalize_scroll_region(buffer, top, bottom) do
{max(0, top), min(buffer.height - 1, bottom)}
end
defp create_empty_line(width) when is_integer(width) and width > 0 do
List.duplicate(Cell.new(), width)
end
defp create_empty_line(_width), do: []
end