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/state_manager.ex
defmodule Raxol.Terminal.StateManager do
@moduledoc """
Manages terminal state transitions and state stack operations.
This module is responsible for maintaining and manipulating the terminal's state.
This module implements the StateManager behavior for consistent state management
patterns across the codebase while maintaining its specific terminal functionality.
"""
# Raxol.Core.StateManager lives in main raxol; guarded at runtime
@compile {:no_warn_undefined, Raxol.Core.StateManager}
def init, do: {:ok, %{}}
def init(opts), do: {:ok, Keyword.get(opts, :initial_state, %{})}
alias Raxol.Terminal.Emulator
require Raxol.Core.Runtime.Log
@doc """
Saves the current terminal state.
Returns {:ok, updated_emulator}.
"""
@spec save_state(Emulator.t()) :: {:ok, Emulator.t()}
def save_state(emulator) do
current_state = %{
cursor: get_cursor_state(emulator),
screen: get_screen_state(emulator),
modes: get_mode_state(emulator)
}
new_stack = [current_state | emulator.state_stack]
{:ok, %{emulator | state_stack: new_stack}}
end
@doc """
Restores the most recently saved terminal state.
Returns {:ok, updated_emulator}.
"""
@spec restore_state(Emulator.t()) :: {:ok, Emulator.t()}
def restore_state(emulator) do
case emulator.state_stack do
[state | new_stack] ->
emulator = apply_saved_state(emulator, state)
{:ok, %{emulator | state_stack: new_stack}}
[] ->
{:error, "No saved state to restore"}
end
end
@doc """
Clears all saved states.
Returns {:ok, updated_emulator}.
"""
@spec clear_states(Emulator.t()) :: {:ok, Emulator.t()}
def clear_states(emulator) do
{:ok, %{emulator | state_stack: []}}
end
@doc """
Gets the current terminal state.
Returns the current state map.
"""
@spec get_current_state(Emulator.t()) :: map()
def get_current_state(emulator) do
%{
cursor: get_cursor_state(emulator),
screen: get_screen_state(emulator),
modes: get_mode_state(emulator)
}
end
@doc """
Updates the current terminal state.
Returns {:ok, updated_emulator}.
"""
@spec update_current_state(Emulator.t(), map()) :: {:ok, Emulator.t()}
def update_current_state(emulator, new_state) do
emulator = apply_saved_state(emulator, new_state)
{:ok, emulator}
end
# Private helper functions
defp get_cursor_state(emulator) do
%{
position: emulator.cursor.position,
visible: emulator.cursor.visible,
style: emulator.cursor.style,
blink_state: emulator.cursor.blink_state
}
end
defp get_screen_state(emulator) do
%{
buffer: emulator.active_buffer,
scroll_region: emulator.scroll_region,
charset_state: emulator.charset_state
}
end
defp get_mode_state(emulator) do
%{
insert_mode: emulator.mode_manager.insert_mode,
origin_mode: emulator.mode_manager.origin_mode,
auto_wrap: emulator.mode_manager.auto_wrap,
cursor_visible: emulator.mode_manager.cursor_visible
}
end
defp apply_saved_state(emulator, state) do
emulator
|> apply_cursor_state(state.cursor)
|> apply_screen_state(state.screen)
|> apply_mode_state(state.modes)
end
defp apply_cursor_state(emulator, cursor_state) do
%{emulator | cursor: Map.merge(emulator.cursor, cursor_state)}
end
defp apply_screen_state(emulator, screen_state) do
emulator
|> Map.put(:active_buffer, screen_state.buffer)
|> Map.put(:scroll_region, screen_state.scroll_region)
|> Map.put(:charset_state, screen_state.charset_state)
end
defp apply_mode_state(emulator, mode_state) do
%{emulator | mode_manager: Map.merge(emulator.mode_manager, mode_state)}
end
end