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 commands commands_parser.ex
Raw

lib/raxol/terminal/commands/commands_parser.ex

defmodule Raxol.Terminal.Commands.CommandsParser do
@moduledoc """
Handles parsing of command parameters in terminal sequences.
This module is part of the terminal command execution system. It provides
utilities for parsing and extracting parameters from CSI, OSC, and DCS
sequence parameter strings.
"""
@doc """
Parses a raw parameter string buffer into a list of integers or nil values.
Handles empty or malformed parameters by converting them to nil.
Handles parameters with sub-parameters (separated by ":")
## Examples
iex> Parser.parse_params("5;10;15")
[5, 10, 15]
iex> Parser.parse_params("5;10;;15")
[5, 10, nil, 15]
iex> Parser.parse_params("5:1;10:2;15:3")
[[5, 1], [10, 2], [15, 3]]
"""
@spec parse_params(String.t()) ::
list(integer() | nil | list(integer() | nil))
def parse_params(""), do: []
def parse_params(nil), do: []
def parse_params(params_string) do
params_string
|> String.split(";")
|> Enum.map(&parse_single_param/1)
end
defp parse_single_param(""), do: nil
defp parse_single_param(param) when is_binary(param) do
case String.contains?(param, ":") do
true ->
param
|> String.split(":")
|> Enum.map(&parse_subparam/1)
false ->
parse_int(param)
end
end
defp parse_single_param(param), do: parse_int(param)
defp parse_subparam(""), do: nil
defp parse_subparam(subparam), do: parse_int(subparam)
@doc """
Gets a parameter at a specific index from the params list.
If the parameter is not available, returns the provided default value.
## Examples
iex> Parser.get_param([5, 10, 15], 2)
10
iex> Parser.get_param([5, 10], 3)
1
iex> Parser.get_param([5, 10], 3, 0)
0
"""
@spec get_param(list(integer() | nil), non_neg_integer(), integer()) ::
integer()
def get_param(params, index, default \\ 1) do
# Get the parameter at 0-based index, with default value
case Enum.at(params, index) do
nil -> default
val -> val
end
end
@doc """
Safely parses a string into an integer.
Returns the parsed integer, or nil on failure.
## Examples
iex> Parser.parse_int("123")
123
iex> Parser.parse_int("abc")
nil
"""
@spec parse_int(String.t()) :: integer() | nil
def parse_int(str) do
case Integer.parse(str) do
# Only return the value if the remainder is empty
{val, ""} -> val
# Return nil for incomplete parses or errors
_ -> nil
end
end
@doc """
Parses a command string into a structured command.
Returns {:ok, parsed_command} or {:error, reason}.
## Examples
iex> Parser.parse("\\e[5;10H")
{:ok, %{type: :csi, params: [5, 10], final_byte: ?H}}
iex> Parser.parse("\\e]0;title\\a")
{:ok, %{type: :osc, command: 0, params: ["title"]}}
"""
@spec parse(String.t()) :: {:ok, map()} | {:error, String.t()}
def parse(command) when is_binary(command) do
# For now, return a basic structure
# This is a simplified implementation
case command do
<<"\e[", rest::binary>> ->
# CSI command
case parse_csi_command(rest) do
{:ok, params, final_byte} ->
{:ok, %{type: :csi, params_buffer: params, final_byte: final_byte}}
{:error, reason} ->
{:error, reason}
end
<<"\e]", rest::binary>> ->
# OSC command
case parse_osc_command(rest) do
{:ok, command_num, params} ->
{:ok, %{type: :osc, command: command_num, params: params}}
{:error, reason} ->
{:error, reason}
end
_ ->
{:error, "Unknown command format"}
end
end
defp parse_csi_command(rest) do
# Simplified CSI parsing
case String.last(rest) do
nil ->
{:error, "No final byte"}
<<final_byte>> when final_byte in ?A..?Z or final_byte in ?a..?z ->
params_string = String.slice(rest, 0..-2//1)
params = parse_params(params_string)
{:ok, params, final_byte}
_ ->
{:error, "Invalid final byte"}
end
end
defp parse_osc_command(rest) do
# Simplified OSC parsing
case String.split(rest, ";", parts: 2) do
[command_str, params_str] ->
case parse_int(command_str) do
nil ->
{:error, "Invalid OSC command number"}
command_num ->
params = [params_str]
{:ok, command_num, params}
end
_ ->
{:error, "Invalid OSC format"}
end
end
end