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 parser states csi_intermediate_state.ex
Raw

lib/raxol/terminal/parser/states/csi_intermediate_state.ex

defmodule Raxol.Terminal.Parser.States.CSIIntermediateState do
@moduledoc """
Handles the :csi_intermediate state of the terminal parser.
"""
alias Raxol.Core.Runtime.Log
alias Raxol.Terminal.Commands.Executor
alias Raxol.Terminal.Emulator
alias Raxol.Terminal.Parser.ParserState, as: State
@doc """
Processes input when the parser is in the :csi_intermediate state.
Collects intermediate bytes (0x20-0x2F).
"""
@spec handle(Emulator.t(), State.t(), binary()) ::
{:continue, Emulator.t(), State.t(), binary()}
| {:finished, Emulator.t(), State.t()}
| {:incomplete, Emulator.t(), State.t()}
def handle(emulator, %State{state: :csi_intermediate} = parser_state, input) do
Log.debug(
"CSIIntermediateState.handle: input=#{inspect(input)}, params_buffer=#{inspect(parser_state.params_buffer)}, intermediates_buffer=#{inspect(parser_state.intermediates_buffer)}"
)
dispatch_input(input, emulator, parser_state)
end
defp dispatch_input(<<>>, emulator, parser_state),
do: handle_empty_input(emulator, parser_state)
defp dispatch_input(
<<intermediate_byte, rest::binary>>,
emulator,
parser_state
)
when intermediate_byte >= 0x20 and intermediate_byte <= 0x2F,
do:
handle_intermediate_byte(
emulator,
parser_state,
intermediate_byte,
rest
)
defp dispatch_input(<<param_byte, rest::binary>>, emulator, parser_state)
when param_byte >= ?0 and param_byte <= ?;,
do: handle_param_byte(emulator, parser_state, param_byte, rest)
defp dispatch_input(<<final_byte, rest::binary>>, emulator, parser_state)
when final_byte >= ?@ and final_byte <= ?~,
do: handle_final_byte(emulator, parser_state, final_byte, rest)
defp dispatch_input(<<ignored_byte, rest::binary>>, emulator, parser_state)
when ignored_byte == 0x18 or ignored_byte == 0x1A,
do: handle_can_sub(emulator, parser_state, rest)
defp dispatch_input(<<ignored_byte, rest::binary>>, emulator, parser_state)
when (ignored_byte >= 0 and ignored_byte <= 23 and ignored_byte != 0x18 and
ignored_byte != 0x1A) or
(ignored_byte >= 27 and ignored_byte <= 31) or ignored_byte == 127,
do: handle_ignored_byte(emulator, parser_state, ignored_byte, rest)
defp dispatch_input(<<unhandled_byte, rest::binary>>, emulator, parser_state),
do: handle_unhandled_byte(emulator, parser_state, unhandled_byte, rest)
# Private helper functions
defp handle_empty_input(emulator, parser_state),
do: {:incomplete, emulator, parser_state}
defp handle_intermediate_byte(emulator, parser_state, intermediate_byte, rest) do
Log.debug(
"CSIIntermediateState.handle_intermediate_byte: byte=#{inspect(<<intermediate_byte>>)}, current_intermediates=#{inspect(parser_state.intermediates_buffer)}"
)
next_parser_state = %{
parser_state
| intermediates_buffer: parser_state.intermediates_buffer <> <<intermediate_byte>>
}
Log.debug(
"CSIIntermediateState.handle_intermediate_byte: new_intermediates=#{inspect(next_parser_state.intermediates_buffer)}"
)
{:continue, emulator, next_parser_state, rest}
end
defp handle_param_byte(emulator, parser_state, param_byte, rest) do
Log.debug(
"CSIIntermediateState.handle_param_byte: byte=#{inspect(<<param_byte>>)}, current_params=#{inspect(parser_state.params_buffer)}"
)
next_parser_state = %{
parser_state
| params_buffer: parser_state.params_buffer <> <<param_byte>>
}
Log.debug(
"CSIIntermediateState.handle_param_byte: new_params=#{inspect(next_parser_state.params_buffer)}, transitioning to :csi_param"
)
{:continue, emulator, %{next_parser_state | state: :csi_param}, rest}
end
defp handle_final_byte(emulator, parser_state, final_byte, rest) do
Log.debug(
"CSIIntermediateState.handle_final_byte: final_byte=#{inspect(<<final_byte>>)}, params_buffer=#{inspect(parser_state.params_buffer)}, intermediates_buffer=#{inspect(parser_state.intermediates_buffer)}"
)
final_emulator =
Executor.execute_csi_command(
emulator,
parser_state.params_buffer,
parser_state.intermediates_buffer,
final_byte
)
Raxol.Core.Runtime.Log.debug(
"CSIIntermediate: After execute, emulator.scroll_region=#{inspect(final_emulator.scroll_region)}"
)
next_parser_state = %{
parser_state
| state: :ground,
params_buffer: "",
intermediates_buffer: "",
final_byte: nil
}
Log.debug(
"CSIIntermediateState.handle_final_byte: executed command, returning to ground state"
)
{:continue, final_emulator, next_parser_state, rest}
end
defp handle_can_sub(emulator, parser_state, rest) do
Raxol.Core.Runtime.Log.debug("Ignoring CAN/SUB byte in CSI Intermediate")
next_parser_state = %{parser_state | state: :ground}
{:continue, emulator, next_parser_state, rest}
end
defp handle_ignored_byte(emulator, parser_state, ignored_byte, rest) do
Raxol.Core.Runtime.Log.debug("Ignoring C0/DEL byte #{ignored_byte} in CSI Intermediate")
{:continue, emulator, parser_state, rest}
end
defp handle_unhandled_byte(emulator, parser_state, unhandled_byte, rest) do
Raxol.Core.Runtime.Log.warning_with_context(
"Unhandled byte #{unhandled_byte} in CSI Intermediate state, returning to ground.",
%{}
)
next_parser_state = %{parser_state | state: :ground}
{:continue, emulator, next_parser_state, rest}
end
end