Packages

Multi-surface application runtime for Elixir. One TEA module renders to terminal, browser (LiveView), SSH, and MCP (agents). 30+ widgets, flexbox + CSS grid, AI agent runtime, distributed swarm with CRDTs, time-travel debugging, session recording, sandboxed REPL, and agentic commerce.

Current section

Files

Jump to
raxol lib raxol terminal escape parsers base_parser.ex
Raw

lib/raxol/terminal/escape/parsers/base_parser.ex

defmodule Raxol.Terminal.Escape.Parsers.BaseParser do
@moduledoc """
Base parser utilities for escape sequence parsers.
Provides common functionality for logging and handling unknown sequences.
"""
require Raxol.Core.Runtime.Log
@doc """
Logs an unknown escape sequence for debugging purposes.
## Parameters
- prefix: The escape sequence prefix (e.g., "ESC", "CSI")
- sequence: The unknown sequence
## Returns
:ok
"""
@spec log_unknown_sequence(String.t(), String.t()) :: :ok
def log_unknown_sequence(prefix, sequence) do
Raxol.Core.Runtime.Log.debug(
"Unknown #{prefix} sequence",
sequence: sequence,
bytes: :erlang.binary_to_list(sequence)
)
:ok
end
@doc """
Parses a numeric parameter from a string.
## Parameters
- str: String containing the number
## Returns
The parsed integer or nil if parsing fails
"""
@spec parse_int(String.t()) :: integer() | nil
def parse_int(""), do: nil
def parse_int(str) do
case Integer.parse(str) do
{num, ""} -> num
_ -> nil
end
end
@doc """
Splits parameters by semicolon and parses them as integers.
## Parameters
- params: String containing semicolon-separated parameters
## Returns
List of parsed integers (nils are filtered out)
"""
@spec parse_params(String.t()) :: list(integer())
def parse_params(params) when is_binary(params) do
params
|> String.split(";")
|> Enum.map(&parse_int/1)
|> Enum.reject(&is_nil/1)
end
@doc """
Extracts the final byte from a CSI sequence.
The final byte determines the command type in CSI sequences.
## Parameters
- input: The input string
## Returns
{final_byte, params_string} or nil if no final byte found
"""
@spec extract_final_byte(String.t()) :: {String.t(), String.t()} | nil
def extract_final_byte(input) do
# CSI sequences end with a byte in the range 0x40-0x7E (@ through ~)
case Regex.run(~r/^([\x20-\x3F]*)([\x40-\x7E])(.*)$/s, input) do
[_, params, final, rest] ->
{final, params, rest}
_ ->
nil
end
end
@doc """
Checks if a character is a valid CSI intermediate character.
Intermediate characters are in the range 0x20-0x2F (space through /)
## Parameters
- char: Character code to check
## Returns
Boolean indicating if it's an intermediate character
"""
@spec intermediate?(integer()) :: boolean()
def intermediate?(char) when is_integer(char) do
char >= 0x20 and char <= 0x2F
end
@deprecated "Use intermediate?/1 instead"
def is_intermediate?(char), do: intermediate?(char)
@doc """
Checks if a character is a valid CSI parameter character.
Parameter characters are in the range 0x30-0x3F (0 through ?)
## Parameters
- char: Character code to check
## Returns
Boolean indicating if it's a parameter character
"""
@spec parameter?(integer()) :: boolean()
def parameter?(char) when is_integer(char) do
char >= 0x30 and char <= 0x3F
end
@deprecated "Use parameter?/1 instead"
def is_parameter?(char), do: parameter?(char)
@doc """
Checks if a character is a valid CSI final character.
Final characters are in the range 0x40-0x7E (@ through ~)
## Parameters
- char: Character code to check
## Returns
Boolean indicating if it's a final character
"""
@spec final?(integer()) :: boolean()
def final?(char) when is_integer(char) do
char >= 0x40 and char <= 0x7E
end
@deprecated "Use final?/1 instead"
def is_final?(char), do: final?(char)
end