![]() |
libghostty
|
Search a terminal for a string, covering the active area and scrollback of both the primary and alternate screens.
A GhosttySearch searches the terminal it was created with for a needle set with GHOSTTY_SEARCH_OPT_NEEDLE. It handles the hard parts of terminal search internally: results stay in sync with the live screens, survive primary/alternate screen switches (entering and leaving a fullscreen app such as vim does not restart a scrollback search), and recover from resize, reflow, resets, and scrollback pruning.
A search starts idle. Setting the needle starts the search, changing it restarts the search from scratch, and clearing it returns the search to idle. Matching is byte-exact except ASCII letters, which compare case-insensitively.
Searching a large scrollback takes time, so the work is split into small steps the caller drives so that the caller can control performance more directly:
GHOSTTY_SEARCH_STATUS_COMPLETE means the search is caught up with the terminal as of the last feed. It never means finished forever, since later terminal writes require another feed to be seen.
Every match is returned as a GhosttySelection snapshot with rectangle set to false, so the existing selection APIs all work on matches: ghostty_terminal_selection_format_buf() to copy the matched text, ghostty_terminal_point_from_grid_ref() with GHOSTTY_POINT_TAG_VIEWPORT to position highlight rectangles, ghostty_terminal_selection_contains() for hit testing, and ghostty_terminal_set() with GHOSTTY_TERMINAL_OPT_SELECTION to make a match the terminal's selection.
Returned matches follow the usual snapshot lifetime rules: they are only valid until the next operation that modifies the terminal, including ghostty_terminal_vt_write(), resize, reset, and free. Read matches after a feed, use them before the terminal changes again, and re-read them rather than caching them. The selected match is kept accurate internally across terminal changes, so the safe way to follow a match is to re-read GHOSTTY_SEARCH_DATA_SELECTED_MATCH after each feed.
The search borrows the terminal it was created with and never frees it. Any number of searches, alongside other terminal readers such as formatters and render states, may share one terminal.
The search and its terminal can be freed in either order. Freeing the search first releases tracked state it holds within the terminal. If the terminal is freed first, the search detects this: calls that need the terminal return GHOSTTY_INVALID_VALUE, reads return whatever the search last saw, and ghostty_search_free() releases only search-owned memory. A search cannot be rebound, so searching another terminal means creating a new search.
The library creates no threads. Calls on one GhosttySearch are not safe to make concurrently with each other, so the caller must serialize them.
Functions that touch the terminal (ghostty_search_new(), ghostty_search_feed(), ghostty_search_run(), ghostty_search_set() with the needle and select options, and ghostty_search_free()) must also be serialized with all other access to the same terminal.
Everything else (ghostty_search_tick(), ghostty_search_get(), and ghostty_search_get_multi()) only touches memory owned by the search and is safe to call while another thread modifies the terminal. This split is how Ghostty runs search on a background thread: tick freely, and take the terminal lock only to feed. Reading returned match values is always safe, but passing them to APIs that take the terminal follows the terminal serialization rule above.
Typedefs | |
| typedef struct GhosttySearchImpl * | GhosttySearch |
Enumerations | |
| enum | GhosttySearchStatus { GHOSTTY_SEARCH_STATUS_RUNNING = 0 , GHOSTTY_SEARCH_STATUS_FEED_REQUIRED = 1 , GHOSTTY_SEARCH_STATUS_COMPLETE = 2 , GHOSTTY_SEARCH_STATUS_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE } |
| enum | GhosttySearchScroll { GHOSTTY_SEARCH_SCROLL_IF_NEEDED = 0 , GHOSTTY_SEARCH_SCROLL_NONE = 1 , GHOSTTY_SEARCH_SCROLL_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE } |
| enum | GhosttySearchData { GHOSTTY_SEARCH_DATA_STATUS = 0 , GHOSTTY_SEARCH_DATA_NEEDLE = 1 , GHOSTTY_SEARCH_DATA_TOTAL_MATCHES = 2 , GHOSTTY_SEARCH_DATA_SELECTED_INDEX = 3 , GHOSTTY_SEARCH_DATA_SELECTED_MATCH = 4 , GHOSTTY_SEARCH_DATA_MATCHES = 5 , GHOSTTY_SEARCH_DATA_VIEWPORT_MATCHES = 6 , GHOSTTY_SEARCH_DATA_SELECT_SCROLL = 7 , GHOSTTY_SEARCH_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE } |
| enum | GhosttySearchOption { GHOSTTY_SEARCH_OPT_NEEDLE = 0 , GHOSTTY_SEARCH_OPT_SELECT_NEXT = 1 , GHOSTTY_SEARCH_OPT_SELECT_PREV = 2 , GHOSTTY_SEARCH_OPT_SELECT_SCROLL = 3 , GHOSTTY_SEARCH_OPT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE } |
Functions | |
| GHOSTTY_API GhosttyResult | ghostty_search_new (const GhosttyAllocator *allocator, GhosttySearch *out_search, GhosttyTerminal terminal) |
| GHOSTTY_API void | ghostty_search_free (GhosttySearch search) |
| GHOSTTY_API GhosttyResult | ghostty_search_tick (GhosttySearch search, GhosttySearchStatus *out_status) |
| GHOSTTY_API GhosttyResult | ghostty_search_feed (GhosttySearch search) |
| GHOSTTY_API GhosttyResult | ghostty_search_run (GhosttySearch search) |
| GHOSTTY_API GhosttyResult | ghostty_search_set (GhosttySearch search, GhosttySearchOption option, const void *value) |
| GHOSTTY_API GhosttyResult | ghostty_search_get (GhosttySearch search, GhosttySearchData data, void *value) |
| GHOSTTY_API GhosttyResult | ghostty_search_get_multi (GhosttySearch search, size_t count, const GhosttySearchData *keys, void **values, size_t *out_written) |
| typedef struct GhosttySearchImpl* GhosttySearch |
Opaque handle to a terminal search.
A search is bound to the terminal it was created with. It borrows the terminal, so it never frees it, and the search must be freed with ghostty_search_free(). If the terminal is freed first, the search detects this: calls that need the terminal fail cleanly and the search can still be freed.
| enum GhosttySearchData |
Data fields readable with ghostty_search_get(). The output value type is documented per field.
All reads reflect the terminal's active screen as of the last feed. When the running application switches to the alternate screen, the next feed switches counts, matches, and selection to that screen's results. Primary screen results, including completed scrollback searches, are retained and restored on the way back.
| Enumerator | |
|---|---|
| GHOSTTY_SEARCH_DATA_STATUS | Current search status: GhosttySearchStatus*. |
| GHOSTTY_SEARCH_DATA_NEEDLE | The needle this search is looking for: GhosttyString*. The bytes are borrowed from the search and remain valid until the needle is changed or the search is freed. Returns GHOSTTY_NO_VALUE when no needle is set. |
| GHOSTTY_SEARCH_DATA_TOTAL_MATCHES | Total matches found so far on the active screen: size_t*. Zero until the first feed. |
| GHOSTTY_SEARCH_DATA_SELECTED_INDEX | Index of the selected match: size_t*. This indexes the newest to oldest ordering of GHOSTTY_SEARCH_DATA_MATCHES, where 0 is the newest match, so a "k of n" find bar renders index + 1 of GHOSTTY_SEARCH_DATA_TOTAL_MATCHES. Returns GHOSTTY_NO_VALUE when nothing is selected. |
| GHOSTTY_SEARCH_DATA_SELECTED_MATCH | The selected match: GhosttySelection*. This is an untracked snapshot with standard GhosttySelection lifetime rules. Returns GHOSTTY_NO_VALUE when nothing is selected. |
| GHOSTTY_SEARCH_DATA_MATCHES | All matches on the active screen, ordered newest to oldest, from the bottom of the active area up through scrollback: GhosttySelectionBuffer*. Set ptr to NULL with cap 0 to query the required capacity. An undersized buffer returns GHOSTTY_OUT_OF_SPACE with the required capacity in len. |
| GHOSTTY_SEARCH_DATA_VIEWPORT_MATCHES | Matches on the pages covering the viewport, for drawing highlight rectangles: GhosttySelectionBuffer*. The list is computed during feeds and cached, so it reflects the viewport as of the last feed. Matches are found a page at a time, so the list can include matches slightly outside the visible viewport when they share a page with it. Ghostty's own renderer behaves the same way. Converting each match to viewport coordinates with ghostty_terminal_point_from_grid_ref() clips this naturally: skip matches that fail the conversion or whose row is beyond the visible row count. |
| GHOSTTY_SEARCH_DATA_SELECT_SCROLL | Current scroll policy: GhosttySearchScroll*. |
| enum GhosttySearchOption |
Options writable with ghostty_search_set(). The value type, and what a NULL value means, is documented per option.
| enum GhosttySearchScroll |
Scroll policy applied when a match becomes selected via GHOSTTY_SEARCH_OPT_SELECT_NEXT or GHOSTTY_SEARCH_OPT_SELECT_PREV.
| Enumerator | |
|---|---|
| GHOSTTY_SEARCH_SCROLL_IF_NEEDED | Scroll the viewport so the match is visible, only if it is not already visible. This is the default. |
| GHOSTTY_SEARCH_SCROLL_NONE | Never scroll the viewport. |
| enum GhosttySearchStatus |
Progress state of a search.
| Enumerator | |
|---|---|
| GHOSTTY_SEARCH_STATUS_RUNNING | ghostty_search_tick() can make progress without terminal access. |
| GHOSTTY_SEARCH_STATUS_FEED_REQUIRED | Blocked until ghostty_search_feed(). This is also the state right after a needle is set, since the search has not yet seen the terminal. |
| GHOSTTY_SEARCH_STATUS_COMPLETE | Caught up with the terminal state as of the last feed. This never means finished forever, since later terminal writes require another feed to be seen. A search with no needle set also reports complete, since there is nothing to look for. |
| GHOSTTY_API GhosttyResult ghostty_search_feed | ( | GhosttySearch | search | ) |
Read the terminal to update the search.
Each feed catches the search up with the terminal: it reconciles the tracked screens against the live ones, re-scans the active area, refreshes the viewport match list, gives the scrollback searcher its next chunk of data, and prunes results that scrollback eviction invalidated. Feeding is also the only way the search learns about terminal changes, so keep feeding periodically while the search is in use, even after it reports complete.
This reads the terminal, so the caller must serialize it with all other access to the same terminal. Each call does a bounded amount of work so that any caller-held terminal lock is held only briefly.
| search | Search handle (NULL returns GHOSTTY_INVALID_VALUE) |
References ghostty_search_feed().
Referenced by ghostty_search_feed().
| GHOSTTY_API void ghostty_search_free | ( | GhosttySearch | search | ) |
Free a search.
If the bound terminal is still alive, this releases tracked state the search holds within it, so the caller must serialize this call with all other access to the same terminal. If the terminal was already freed, the search has been detached and this releases only search-owned memory. Passing NULL is allowed and is a no-op.
| search | Search handle to free |
References ghostty_search_free().
Referenced by ghostty_search_free().
| GHOSTTY_API GhosttyResult ghostty_search_get | ( | GhosttySearch | search, |
| GhosttySearchData | data, | ||
| void * | value ) |
Read a data field from a search.
The output value type depends on data and is documented by GhosttySearchData. This never reads the terminal, so it is safe to call while another thread modifies the terminal. Returned selections are untracked snapshots with standard GhosttySelection lifetime rules.
| search | Search handle (NULL returns GHOSTTY_INVALID_VALUE) |
| data | Data field to read |
| value | Output pointer whose type depends on data |
References ghostty_search_get().
Referenced by ghostty_search_get().
| GHOSTTY_API GhosttyResult ghostty_search_get_multi | ( | GhosttySearch | search, |
| size_t | count, | ||
| const GhosttySearchData * | keys, | ||
| void ** | values, | ||
| size_t * | out_written ) |
Read multiple data fields from a search in a single call.
This is an optimization over calling ghostty_search_get() multiple times. Each entry in values must point to storage of the type documented by the corresponding GhosttySearchData key.
If any individual read fails, the function returns that error and writes the index of the failing key to out_written when out_written is non-NULL. Earlier keys have already been written. On success, out_written receives count when non-NULL. A too-small GhosttySelectionBuffer stops the batch with GHOSTTY_OUT_OF_SPACE at that key's index with the required capacity in its len, so order buffer-valued keys after scalar keys.
| search | Search handle (NULL returns GHOSTTY_INVALID_VALUE) |
| count | Number of data fields to read |
| keys | Data fields to read (must not be NULL) |
| values | Output pointers corresponding to keys (must not be NULL) |
| out_written | Optional number of fields read, or failing index on error |
References ghostty_search_get_multi().
Referenced by ghostty_search_get_multi().
| GHOSTTY_API GhosttyResult ghostty_search_new | ( | const GhosttyAllocator * | allocator, |
| GhosttySearch * | out_search, | ||
| GhosttyTerminal | terminal ) |
Create a search bound to a terminal.
The search borrows the terminal and never frees it. The search and the terminal can be freed in either order; see ghostty_search_free().
The search starts idle with no needle: it reports GHOSTTY_SEARCH_STATUS_COMPLETE and finds nothing. Set GHOSTTY_SEARCH_OPT_NEEDLE to start searching.
Creation is cheap and does not read terminal contents, but it registers the search with the terminal so the two can be freed in any order. The caller must serialize this call with all other access to the same terminal.
| allocator | Allocator, or NULL for the default allocator |
| out_search | Receives the created search handle |
| terminal | Terminal to bind the search to |
References ghostty_search_new().
Referenced by ghostty_search_new().
| GHOSTTY_API GhosttyResult ghostty_search_run | ( | GhosttySearch | search | ) |
Feed and tick until the search is caught up with the terminal.
This is a blocking convenience for one-shot and single-threaded embedders. It always performs at least one feed, so it also picks up any terminal changes since the last feed, then loops until the status is GHOSTTY_SEARCH_STATUS_COMPLETE. Searching a large scrollback can take a while, so interactive embedders should drive ghostty_search_tick() and ghostty_search_feed() themselves.
This reads the terminal for the entire call, so the caller must serialize it with all other access to the same terminal.
| search | Search handle (NULL returns GHOSTTY_INVALID_VALUE) |
References ghostty_search_run().
Referenced by ghostty_search_run().
| GHOSTTY_API GhosttyResult ghostty_search_set | ( | GhosttySearch | search, |
| GhosttySearchOption | option, | ||
| const void * | value ) |
Write an option to a search.
The value type, and what a NULL value means, depends on the option and is documented by GhosttySearchOption. The needle and select options touch the terminal, so the caller must serialize those calls with all other access to the same terminal. GHOSTTY_SEARCH_OPT_SELECT_SCROLL only modifies search-owned state.
| search | Search handle (NULL returns GHOSTTY_INVALID_VALUE) |
| option | Option to write |
| value | Pointer to the input value for the option. The meaning of NULL is documented per option. |
References ghostty_search_set().
Referenced by ghostty_search_set().
| GHOSTTY_API GhosttyResult ghostty_search_tick | ( | GhosttySearch | search, |
| GhosttySearchStatus * | out_status ) |
Make a bounded amount of search progress.
This only works on data the search has already copied and never reads the terminal, so it is safe to call while another thread modifies the terminal. Call it in a loop while the status is GHOSTTY_SEARCH_STATUS_RUNNING. When the status becomes GHOSTTY_SEARCH_STATUS_FEED_REQUIRED, call ghostty_search_feed() to unblock it.
| search | Search handle (NULL returns GHOSTTY_INVALID_VALUE) | |
| [out] | out_status | Receives the status after the tick (may be NULL) |
References ghostty_search_tick().
Referenced by ghostty_search_tick().