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/scroller.ex
defmodule Raxol.Terminal.Buffer.Scroller do
@moduledoc """
Handles scrolling operations for the terminal buffer.
"""
alias Raxol.Terminal.ScreenBuffer
@doc """
Scrolls the buffer up by the specified number of lines.
"""
def scroll_up(buffer, count) do
do_scroll_up(buffer, count)
end
@doc """
Scrolls the buffer down by the specified number of lines.
"""
def scroll_down(buffer, count) do
do_scroll_down(buffer, count)
end
@doc """
Gets the scroll top position.
"""
def get_scroll_top(_buffer, scroll_margins) do
scroll_margins.top
end
@doc """
Gets the scroll bottom position.
"""
def get_scroll_bottom(_buffer, scroll_margins) do
scroll_margins.bottom
end
@doc """
Scrolls the entire buffer up by the specified number of lines.
## Parameters
* `buffer` - The screen buffer to scroll
* `count` - The number of lines to scroll up
## Returns
A tuple containing :ok and the updated buffer.
## Examples
iex> buffer = ScreenBuffer.new(80, 24)
iex> {:ok, new_buffer} = Scroller.scroll_entire_buffer_up(buffer, 1)
iex> length(new_buffer.content)
24
"""
@spec scroll_entire_buffer_up(ScreenBuffer.t(), non_neg_integer()) ::
{:ok, ScreenBuffer.t()}
def scroll_entire_buffer_up(buffer, count) do
{new_buffer, _removed_lines} = ScreenBuffer.pop_bottom_lines(buffer, count)
empty_lines = List.duplicate(List.duplicate(%{}, buffer.width), count)
new_cells = empty_lines ++ new_buffer.cells
{:ok, %{new_buffer | cells: new_cells}}
end
@doc """
Scrolls the entire buffer down by the specified number of lines.
## Parameters
* `buffer` - The screen buffer to scroll
* `count` - The number of lines to scroll down
## Returns
A tuple containing :ok and the updated buffer.
## Examples
iex> buffer = ScreenBuffer.new(80, 24)
iex> {:ok, new_buffer} = Scroller.scroll_entire_buffer_down(buffer, 1)
iex> length(new_buffer.content)
24
"""
@spec scroll_entire_buffer_down(ScreenBuffer.t(), non_neg_integer()) ::
{:ok, ScreenBuffer.t()}
def scroll_entire_buffer_down(buffer, count) do
{new_buffer, _removed_lines} = ScreenBuffer.pop_bottom_lines(buffer, count)
empty_lines = List.duplicate(List.duplicate(%{}, buffer.width), count)
new_cells = new_buffer.cells ++ empty_lines
{:ok, %{new_buffer | cells: new_cells}}
end
@doc """
Scrolls a specific region of the buffer up by the specified number of lines.
## Parameters
* `buffer` - The screen buffer to scroll
* `count` - The number of lines to scroll up
* `top` - The top boundary of the scroll region
* `bottom` - The bottom boundary of the scroll region
## Returns
A tuple containing :ok and the updated buffer.
## Examples
iex> buffer = ScreenBuffer.new(80, 24)
iex> {:ok, new_buffer} = Scroller.scroll_region_up(buffer, 1, 5, 15)
iex> length(new_buffer.content)
24
"""
@spec scroll_region_up(
ScreenBuffer.t(),
non_neg_integer(),
non_neg_integer(),
non_neg_integer()
) :: {:ok, ScreenBuffer.t()}
def scroll_region_up(buffer, count, top, bottom) do
region_lines = Enum.slice(buffer.cells, top..bottom)
{_to_scroll, remaining} = Enum.split(region_lines, count)
empty_lines = List.duplicate(List.duplicate(%{}, buffer.width), count)
new_region = remaining ++ empty_lines
new_cells = List.replace_at(buffer.cells, top, new_region)
{:ok, %{buffer | cells: new_cells}}
end
@doc """
Scrolls a specific region of the buffer down by the specified number of lines.
## Parameters
* `buffer` - The screen buffer to scroll
* `count` - The number of lines to scroll down
* `top` - The top boundary of the scroll region
* `bottom` - The bottom boundary of the scroll region
## Returns
A tuple containing :ok and the updated buffer.
## Examples
iex> buffer = ScreenBuffer.new(80, 24)
iex> {:ok, new_buffer} = Scroller.scroll_region_down(buffer, 1, 5, 15)
iex> length(new_buffer.content)
24
"""
@spec scroll_region_down(
ScreenBuffer.t(),
non_neg_integer(),
non_neg_integer(),
non_neg_integer()
) :: {:ok, ScreenBuffer.t()}
def scroll_region_down(buffer, count, top, bottom) do
region_lines = Enum.slice(buffer.cells, top..bottom)
{remaining, _to_scroll} = Enum.split(region_lines, -count)
empty_lines = List.duplicate(List.duplicate(%{}, buffer.width), count)
new_region = empty_lines ++ remaining
new_cells = List.replace_at(buffer.cells, top, new_region)
{:ok, %{buffer | cells: new_cells}}
end
# Private helper functions
defp do_scroll_up(buffer, count) do
case buffer.scroll_region do
nil ->
{:ok, new_buffer} = scroll_entire_buffer_up(buffer, count)
new_buffer
region ->
{:ok, new_buffer} =
scroll_region_up(buffer, count, region.top, region.bottom)
new_buffer
end
end
defp do_scroll_down(buffer, count) do
case buffer.scroll_region do
nil ->
{:ok, new_buffer} = scroll_entire_buffer_down(buffer, count)
new_buffer
region ->
{:ok, new_buffer} =
scroll_region_down(buffer, count, region.top, region.bottom)
new_buffer
end
end
end