Skip to main content
Termy provides powerful search capabilities to find text in your terminal output, including scrollback history, with support for both literal and regex patterns.

Overview

The search feature allows you to:
  • Search through terminal output and scrollback history
  • Use literal text or regular expressions
  • Toggle case sensitivity
  • Navigate between matches with keyboard shortcuts
  • See match count and position
  • Visual highlighting of all matches
Via Command:
OpenSearch
Via Menu: Edit > Find Via Keyboard: Default keybinding opens the search bar (typically Cmd+F on macOS) The search bar appears at the top-right of the terminal window.

Search Interface

Location: Top-right overlay (width: 320px, height: 36px) Components:
  • Input field - Enter search query
  • Match counter - Shows “X of Y” or “No matches”
  • Navigation buttons - Up/down arrows to move between matches
  • Close button - X to close search

Search Modes

Literal Search (Default)

Searches for exact text matches:
Query: "error"
Matches: "error", "ERROR" (if case-insensitive)
Special regex characters are escaped automatically:
Query: "foo.*bar"
Matches: Literal "foo.*bar" (not regex pattern)
Enable regex mode to use regular expressions:
Query: "\d+"
Matches: Any sequence of digits
Query: "error|warning"
Matches: Either "error" or "warning"
Toggle regex mode:
ToggleSearchRegex
Regex patterns use Rust’s regex crate syntax, which is similar to PCRE but without lookahead/lookbehind.

Case Sensitivity

Default: Case-insensitive
Query: "Error"
Matches: "error", "Error", "ERROR"
Toggle case sensitivity:
ToggleSearchCaseSensitive
When enabled:
Query: "Error"
Matches: Only "Error" (exact case)

Keyboard Navigation

  • Enter - Next match (toward older content)
  • Shift+Enter - Previous match (toward newer content)
  • Escape - Close search

Button Navigation

  • Up arrow (↑) - Next match (scrolls up toward history)
  • Down arrow (↓) - Previous match (scrolls down toward recent output)
  • Edit > Find Next (SearchNext)
  • Edit > Find Previous (SearchPrevious)

Match Ordering

Matches are ordered newest-first, with index 0 being the bottommost (most recent) match.
This matches terminal convention where:
  • Bottom of screen = newest output
  • Top of screen = older output
  • Scrollback history extends upward

Search Scope

Termy searches:
  1. Visible terminal - Current viewport
  2. Scrollback history - All scrollback up to the configured limit
// From config_core
DEFAULT_SCROLLBACK_HISTORY  // Configurable scrollback size
The search engine processes:
  • Grid cells converted to text
  • Control characters replaced with spaces
  • Wide characters handled correctly (CJK, emoji)

Search Debouncing

Searches are debounced to improve performance:
// From src/terminal_view/search.rs
const SEARCH_DEBOUNCE_MS: u64 = 150;  // Typical value
This means:
  • Search doesn’t trigger on every keystroke
  • 150ms delay after you stop typing
  • Reduces CPU usage during fast typing

Implementation Details

Search Engine

Location: crates/search/src/ Core Components:
// engine.rs
pub struct SearchEngine {
    config: SearchConfig,
    compiled_regex: Option<Regex>,
    pattern: String,
}

pub enum SearchMode {
    Literal,
    Regex,
}

pub struct SearchConfig {
    pub case_sensitive: bool,
    pub mode: SearchMode,
}
Search Process:
// Search a single line
pub fn search_line(&self, line_idx: i32, text: &str) -> Vec<SearchMatch>

// Search multiple lines
pub fn search<'a, F>(&self, start_line: i32, end_line: i32, line_provider: F) -> SearchResults

Match Results

Location: crates/search/src/matcher.rs
pub struct SearchMatch {
    pub line: i32,        // Terminal line index
    pub start_col: usize, // Match start column
    pub end_col: usize,   // Match end column
}

pub struct SearchResults {
    matches: Vec<SearchMatch>,
    current_index: Option<usize>,
}

State Management

Location: crates/search/src/state.rs
pub struct SearchState {
    engine: SearchEngine,
    results: SearchResults,
    results_revision: u64,  // Increments on each search
    query: String,
    is_active: bool,
    error: Option<String>,  // Regex parse errors
}

View Integration

Location: src/terminal_view/search.rs
// Search bar rendering
pub(super) fn render_search_bar(&self, cx: &mut Context<Self>) -> AnyElement

// Search execution
pub(super) fn perform_search(&mut self)

// Navigate to match and scroll if needed
fn scroll_to_current_match(&mut self, cx: &mut Context<Self>)

Unicode Support

Termy’s search engine properly handles:

Wide Characters (CJK)

Text: "a界b界"
Search: "界"
Matches: Correctly identifies both occurrences with proper cell column positions

Emoji

Text: "Hello 😀 World 😀"
Search: "😀"
Matches: Both emoji with correct 2-cell width

Combining Characters

Text: "café" (e + combining acute accent)
Search: "é"
Matches: Correctly handles composed character

Error Handling

Invalid Regex

If you enter an invalid regex pattern:
Query: "[invalid"
Display: Red border on search bar
Counter: Shows error instead of match count
The error message appears in the counter area:
"regex parse error: unclosed character class"

No Matches

When no matches are found:
Counter: "No matches"
Empty query shows nothing:
Counter: (empty)

Visual Highlighting

Matches are highlighted in the terminal:
  1. All matches - Highlighted throughout terminal
  2. Current match - Additional emphasis on selected match
  3. Scrollbar markers - Matches shown as markers on scrollbar
Highlighting updates:
  • Immediately when search changes
  • As new terminal output arrives
  • When scrolling through results

Performance

Optimizations

Debounced Search:
  • Prevents excessive searches while typing
  • 150ms delay improves responsiveness
Incremental Results:
  • Only searches when pattern changes
  • Reuses compiled regex across searches
  • Efficient cell-to-text conversion
Smart Scrolling:
  • Only scrolls if match is not visible
  • Minimal scroll distance to reveal match
  • Preserves scroll position when possible

Large Scrollback

With large scrollback buffers (10,000+ lines):
  • Search may take a few hundred milliseconds
  • Debouncing prevents UI lag during typing
  • Results are computed once and reused for navigation
Reduce scrollback size if search performance is an issue:
scrollback_history: 5000

Use Cases

Finding Errors

Search: "error"
Result: Highlights all error messages in output

Tracking IDs

Search (regex): "[0-9a-f]{8}-[0-9a-f]{4}"
Result: Finds UUID patterns

Log Analysis

Search (regex): "\[(ERROR|WARN)\]"
Result: Finds log entries at ERROR or WARN level

File Paths

Search: "/var/log"
Result: Highlights all references to the /var/log directory

Best Practices

Effective Queries

Use specific terms:
Good: "Connection refused"
Less effective: "error"
Leverage regex for patterns:
IP addresses: "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"
Times: "\d{2}:\d{2}:\d{2}"
URLs: "https?://[^\s]+"
Use case sensitivity strategically:
Case-sensitive ON: Find "Error" vs "error" as distinct
Case-sensitive OFF: Find all variations

Regex Tips

Common patterns:
\d+         # Numbers
\w+         # Words
[A-Z]+      # Uppercase words
\s+         # Whitespace
.*          # Any characters
^           # Start of line
$           # End of line
Escaping special characters: In regex mode, escape these with \:
. * + ? [ ] ( ) { } ^ $ | \

Troubleshooting

Search Not Finding Results

Check:
  1. Case sensitivity setting
  2. Search mode (literal vs regex)
  3. Pattern syntax (if using regex)
  4. Scrollback limit (results might be outside history)

Regex Errors

Common regex mistakes:
[invalid      # Unclosed bracket - add ]
(unclosed     # Unclosed paren - add )
*             # Nothing to repeat - add character before *
Use literal mode if you’re not familiar with regex syntax.

Performance Issues

If search is slow:
  1. Reduce scrollback: Lower scrollback_history setting
  2. Simplify regex: Complex patterns take longer
  3. Clear terminal: Less content = faster search

Keyboard Shortcuts Summary

ActionDefault Binding
Open searchCmd+F (macOS) / Ctrl+F
Next matchEnter
Previous matchShift+Enter
Close searchEscape
Toggle case sensitivity(Via command)
Toggle regex mode(Via command)

Build docs developers (and LLMs) love