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 scroll optimizer.ex
Raw

lib/raxol/terminal/scroll/optimizer.ex

defmodule Raxol.Terminal.Scroll.Optimizer do
@moduledoc """
Handles scroll optimization for better performance.
Dynamically adjusts batch size based on recent scroll patterns and (optionally) performance metrics.
"""
@history_size 10
@type scroll_event :: %{
direction: :up | :down,
lines: non_neg_integer(),
timestamp: integer()
}
@type t :: %__MODULE__{
batch_size: non_neg_integer(),
last_optimization: non_neg_integer(),
history: [scroll_event()]
}
defstruct batch_size: 10,
last_optimization: 0,
history: []
@doc """
Creates a new optimizer instance.
"""
@spec new() :: t()
def new do
%__MODULE__{
batch_size: 10,
last_optimization: System.monotonic_time(),
history: []
}
end
@doc """
Optimizes scroll operations for better performance.
- Increases batch size for large/rapid scrolls.
- Decreases batch size for small/precise or alternating scrolls.
- Uses recent scroll history to adapt.
"""
@spec optimize(t(), :up | :down, non_neg_integer()) :: t()
def optimize(%__MODULE__{} = optimizer, direction, lines) do
now = System.monotonic_time()
event = %{direction: direction, lines: lines, timestamp: now}
history = [event | Enum.take(optimizer.history, @history_size - 1)]
new_batch_size = calculate_batch_size(optimizer.batch_size, history)
%{
optimizer
| batch_size: new_batch_size,
last_optimization: now,
history: history
}
end
defp calculate_batch_size(current_batch_size, history) do
avg_lines = calculate_average_lines(history)
alternation_ratio = calculate_alternation_ratio(history)
calculate_batch_size_by_conditions(
current_batch_size,
avg_lines,
alternation_ratio
)
end
# High alternation pattern - reduce batch size to handle direction changes
defp calculate_batch_size_by_conditions(
current_batch_size,
_avg_lines,
alternation_ratio
)
when alternation_ratio > 0.5 do
max(current_batch_size - 2, 1)
end
# Large scroll operations - increase batch size significantly
defp calculate_batch_size_by_conditions(
current_batch_size,
avg_lines,
_alternation_ratio
)
when avg_lines >= 50 do
min(current_batch_size + 10, 100)
end
# Medium scroll operations - moderate increase
defp calculate_batch_size_by_conditions(
current_batch_size,
avg_lines,
_alternation_ratio
)
when avg_lines >= 20 do
min(current_batch_size + 5, 50)
end
# Very small scrolls - reduce batch size for precision
defp calculate_batch_size_by_conditions(
current_batch_size,
avg_lines,
_alternation_ratio
)
when avg_lines <= 2 do
max(current_batch_size - 2, 1)
end
# Small scrolls - slight reduction
defp calculate_batch_size_by_conditions(
current_batch_size,
avg_lines,
_alternation_ratio
)
when avg_lines <= 5 do
max(current_batch_size - 1, 1)
end
# Default case - no changes needed
defp calculate_batch_size_by_conditions(
current_batch_size,
_avg_lines,
_alternation_ratio
) do
current_batch_size
end
# history is always non-empty when called from calculate_batch_size
# (optimize/3 prepends an event before calling)
defp calculate_average_lines(history) do
Enum.sum(Enum.map(history, & &1.lines)) / length(history)
end
defp calculate_alternation_ratio(history) when length(history) <= 1, do: 0
defp calculate_alternation_ratio(history) do
alternations =
history
|> Enum.chunk_every(2, 1, :discard)
|> Enum.count(fn [a, b] -> a.direction != b.direction end)
alternations / (length(history) - 1)
end
end