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/search_buffer.ex
defmodule Raxol.Terminal.SearchBuffer do
@moduledoc """
Manages search state, options, matches, and history for terminal search operations.
"""
@type match :: %{
line: integer(),
start: integer(),
length: integer(),
text: String.t()
}
@type options :: %{case_sensitive: boolean(), regex: boolean()}
@type t :: %__MODULE__{
pattern: String.t() | nil,
options: options(),
matches: [match()],
current_index: integer(),
history: [String.t()]
}
defstruct pattern: nil,
options: %{case_sensitive: false, regex: false},
matches: [],
current_index: -1,
history: []
@doc """
Starts a new search with the given pattern.
"""
@spec start_search(t(), String.t()) :: {:ok, t()} | {:error, term()}
def start_search(buffer, pattern) when is_binary(pattern) do
handle_search_pattern(pattern == "", buffer, pattern)
end
defp handle_search_pattern(true, _buffer, _pattern) do
{:error, :empty_pattern}
end
defp handle_search_pattern(false, buffer, pattern) do
# For now, just clear matches; real impl would search buffer content
new_buffer = %{
buffer
| pattern: pattern,
matches: [],
current_index: -1,
history: add_pattern_to_history(buffer.history, pattern)
}
{:ok, new_buffer}
end
@doc """
Finds the next match in the search.
"""
@spec find_next(t()) :: {:ok, t(), match()} | {:error, term()}
def find_next(%__MODULE__{matches: [], pattern: nil} = _buffer),
do: {:error, :no_search}
def find_next(%__MODULE__{matches: []} = _buffer), do: {:error, :no_matches}
def find_next(%__MODULE__{matches: matches, current_index: idx} = buffer) do
new_idx = rem(idx + 1, length(matches))
{:ok, %{buffer | current_index: new_idx}, Enum.at(matches, new_idx)}
end
@doc """
Finds the previous match in the search.
"""
@spec find_previous(t()) :: {:ok, t(), match()} | {:error, term()}
def find_previous(%__MODULE__{matches: [], pattern: nil} = _buffer),
do: {:error, :no_search}
def find_previous(%__MODULE__{matches: []} = _buffer),
do: {:error, :no_matches}
def find_previous(%__MODULE__{matches: matches, current_index: idx} = buffer) do
new_idx = rem(idx - 1 + length(matches), length(matches))
{:ok, %{buffer | current_index: new_idx}, Enum.at(matches, new_idx)}
end
@doc """
Gets the current search pattern.
"""
@spec get_pattern(t()) :: String.t() | nil
def get_pattern(%__MODULE__{pattern: pattern}), do: pattern
@doc """
Sets the search options.
"""
@spec set_options(t(), map()) :: t()
def set_options(buffer, opts) when is_map(opts) do
%{buffer | options: Map.merge(buffer.options, opts)}
end
@doc """
Gets the current search options.
"""
@spec get_options(t()) :: map()
def get_options(%__MODULE__{options: opts}), do: opts
@doc """
Gets all matches in the current search.
"""
@spec get_all_matches(t()) :: [match()]
def get_all_matches(%__MODULE__{matches: matches}), do: matches
@doc """
Gets the current match index.
"""
@spec get_current_index(t()) :: integer()
def get_current_index(%__MODULE__{current_index: idx}), do: idx
@doc """
Gets the total number of matches.
"""
@spec get_match_count(t()) :: non_neg_integer()
def get_match_count(%__MODULE__{matches: matches}), do: length(matches)
@doc """
Highlights all matches in the current view (no-op placeholder).
"""
@spec highlight_matches(t()) :: t()
def highlight_matches(buffer), do: buffer
@doc """
Clears the current search.
"""
@spec clear(t()) :: t()
def clear(buffer) do
%{buffer | pattern: nil, matches: [], current_index: -1}
end
@doc """
Gets the search history.
"""
@spec get_search_history(t()) :: [String.t()]
def get_search_history(%__MODULE__{history: history}), do: history
@doc """
Adds a pattern to the search history.
"""
@spec add_to_history(t(), String.t()) :: t()
def add_to_history(buffer, pattern) when is_binary(pattern) do
%{buffer | history: add_pattern_to_history(buffer.history, pattern)}
end
@doc """
Clears the search history.
"""
@spec clear_history(t()) :: t()
def clear_history(buffer), do: %{buffer | history: []}
# Helper
defp add_pattern_to_history(history, pattern) do
[pattern | Enum.reject(history, &(&1 == pattern))]
# Limit history size
|> Enum.take(20)
end
end