tastty-driver 0.1.0

Terminal automation driver built on tastty
//! Snapshot, scrollback, and pattern search.

#[cfg(feature = "async")]
use std::sync::Arc;

use super::Session;
use crate::snapshot::{
    inspect_snapshot_from_screen, scrollback_snapshot_from_screen, snapshot_from_screen,
};
use crate::{InspectSnapshot, ScrollbackSnapshot, Snapshot};

impl Session {
    /// Return a rich snapshot of the current terminal state.
    #[must_use]
    pub fn snapshot(&self) -> Snapshot {
        self.terminal.with_screen(snapshot_from_screen)
    }

    /// Search the viewport (and optionally scrollback) for matches of
    /// `pattern`.
    ///
    /// Soft-wrapped physical rows are joined into one logical line
    /// before matching, so a regex like `WARN.*failed` finds a hit
    /// even if the renderer overflowed the right edge in the middle
    /// of `WARN`. Regex anchors operate on logical lines: `^` matches
    /// the first column, `$` matches the last, and `\n` in the
    /// pattern crosses a logical-line boundary.
    ///
    /// Match positions count cells, not bytes: a wide character
    /// (CJK, emoji, ...) sits in one cell whose column is the
    /// lead-half cell's column, so the
    /// [`AbsolutePosition`](tastty::AbsolutePosition) in a returned
    /// [`SearchMatch`](crate::SearchMatch) is directly comparable
    /// with [`Screen::visible_to_absolute`](tastty::Screen::visible_to_absolute)
    /// output and with [`WaitMatch::position`](crate::WaitMatch).
    ///
    /// Holds the parser read lock for the duration of the walk; for
    /// long-running searches over deep scrollback consider capping
    /// with [`SearchOptions::max_results`](crate::SearchOptions::max_results)
    /// so write-side updates (input echo, child output) are not
    /// blocked unnecessarily.
    ///
    /// # Errors
    ///
    /// Returns [`SearchError::EmptyPattern`](crate::SearchError::EmptyPattern)
    /// when called with an empty
    /// [`SearchPattern::Literal`](crate::SearchPattern::Literal);
    /// regex emptiness is rejected at construction time by
    /// [`SearchPattern::regex`](crate::SearchPattern::regex).
    pub fn find(
        &self,
        pattern: &crate::SearchPattern,
        opts: crate::SearchOptions,
    ) -> std::result::Result<Vec<crate::SearchMatch>, crate::SearchError> {
        self.terminal
            .with_screen(|screen| crate::search::find(screen, pattern, opts))
    }

    /// Asynchronous variant of [`Session::find`].
    ///
    /// Cancellation-safe: dropping the returned future before it
    /// resolves leaves no parser-state poisoning, and reissuing on the
    /// same [`Session`] is always safe. Returns a `Send + 'static`
    /// future so it can be spawned on any executor; pattern ownership
    /// is taken by value (the compiled regex is reference-counted, so
    /// callers reusing a pattern just clone it).
    ///
    /// See [`Session::find`] for pattern semantics, anchor and column
    /// rules, scrollback and direction options, and the error path.
    ///
    /// # Errors
    ///
    /// Same as [`Session::find`].
    #[cfg(feature = "async")]
    pub fn find_async(
        &self,
        pattern: crate::SearchPattern,
        opts: crate::SearchOptions,
    ) -> impl std::future::Future<
        Output = std::result::Result<Vec<crate::SearchMatch>, crate::SearchError>,
    > + Send
    + 'static {
        let terminal = Arc::clone(&self.terminal);
        async move { terminal.with_screen(|screen| crate::search::find(screen, &pattern, opts)) }
    }

    /// Return retained scrollback rows followed by visible screen rows.
    #[must_use]
    pub fn snapshot_with_scrollback(&self, limit: Option<usize>) -> ScrollbackSnapshot {
        self.terminal
            .with_screen(|screen| scrollback_snapshot_from_screen(screen, limit))
    }

    /// Return the number of retained scrollback rows available.
    #[must_use]
    pub fn scrollback_count(&self) -> usize {
        self.terminal
            .with_screen(|screen| screen.scrollback_available())
    }

    /// Return terminal mode and metadata state.
    #[must_use]
    pub fn inspect(&self) -> InspectSnapshot {
        self.terminal.with_screen(inspect_snapshot_from_screen)
    }
}