libghostty
Loading...
Searching...
No Matches
c-vt-search/src/main.c

This example demonstrates how to search terminal contents for a string, navigate between the matches like a find bar, and read the viewport matches used to draw highlights.

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <ghostty/vt.h>
int main() {
// Create a terminal and fill it with some content to search.
GhosttyTerminal terminal;
GhosttyResult result = ghostty_terminal_new(NULL, &terminal, 80, 24);
assert(result == GHOSTTY_SUCCESS);
const char *lines[] = {
"$ make test\r\n",
"compiling module A... ok\r\n",
"compiling module B... error: missing semicolon\r\n",
"linking... error: undefined symbol\r\n",
"$ grep -n ERROR build.log\r\n",
};
for (size_t i = 0; i < sizeof(lines) / sizeof(lines[0]); i++) {
ghostty_terminal_vt_write(terminal, (const uint8_t *)lines[i],
strlen(lines[i]));
}
// The user opened the find bar, so create a search bound to the
// terminal. It starts idle until it has a needle.
GhosttySearch search;
result = ghostty_search_new(NULL, &search, terminal);
assert(result == GHOSTTY_SUCCESS);
// The user typed a query. Matching is byte-exact except ASCII
// letters, which compare case-insensitively, so "error" also finds
// "ERROR". Retyping just sets the needle again: a changed needle
// restarts the search and an unchanged one keeps its results.
GhosttyString needle = { (const uint8_t *)"error", 5 };
result = ghostty_search_set(search, GHOSTTY_SEARCH_OPT_NEEDLE, &needle);
assert(result == GHOSTTY_SUCCESS);
// Drive the search. Interactive embedders interleave
// ghostty_search_tick() and ghostty_search_feed() with their event
// loop, but for a one-shot search we can just run it to completion.
result = ghostty_search_run(search);
assert(result == GHOSTTY_SUCCESS);
// The total match count, for find bar text like "1 of 3".
size_t total = 0;
&total);
assert(result == GHOSTTY_SUCCESS);
printf("%zu matches for \"error\"\n", total);
// The user pressed Enter, so select the next match. Selection starts
// at the newest match, moves toward older content, and wraps around.
// This scrolls the viewport to the match if it isn't visible, per
// the GHOSTTY_SEARCH_OPT_SELECT_SCROLL policy.
while (true) {
if (result != GHOSTTY_SUCCESS) break;
// Read the selection state in one call. Index 0 is the newest
// match, so a "k of n" find bar renders index + 1.
size_t idx = 0;
const GhosttySearchData keys[] = {
};
void *values[] = { &idx, &match };
search, sizeof(keys) / sizeof(keys[0]), keys, values, NULL);
assert(result == GHOSTTY_SUCCESS);
printf("selected %zu of %zu\n", idx + 1, total);
// Wrapped back around to the first match: stop.
if (idx + 1 == total) break;
}
// Each frame while the find bar is open, feed to catch up with any
// terminal changes and then read the viewport matches to draw
// highlights. The list can include matches just past the viewport
// when they share a page with it, so convert each endpoint to
// viewport coordinates and skip matches outside the visible rows.
result = ghostty_search_feed(search);
assert(result == GHOSTTY_SUCCESS);
GhosttySelection viewport_storage[64];
.ptr = viewport_storage,
.cap = sizeof(viewport_storage) / sizeof(viewport_storage[0]),
};
&viewport);
assert(result == GHOSTTY_SUCCESS);
for (size_t i = 0; i < viewport.len; i++) {
terminal, &viewport_storage[i].start, GHOSTTY_POINT_TAG_VIEWPORT,
&start) != GHOSTTY_SUCCESS) continue;
terminal, &viewport_storage[i].end, GHOSTTY_POINT_TAG_VIEWPORT,
&end) != GHOSTTY_SUCCESS) continue;
if (start.y >= 24 || end.y >= 24) continue;
// A real embedder draws a highlight rect from start to end here.
printf("highlight rows %u-%u, cols %u-%u\n",
(unsigned)start.y, (unsigned)end.y,
(unsigned)start.x, (unsigned)end.x);
}
// Closing the find bar. The search borrows the terminal, but the
// two can be freed in either order.
return 0;
}
@ GHOSTTY_POINT_TAG_VIEWPORT
Definition point.h:52
GHOSTTY_API GhosttyResult ghostty_search_new(const GhosttyAllocator *allocator, GhosttySearch *out_search, GhosttyTerminal terminal)
struct GhosttySearchImpl * GhosttySearch
Definition types.h:202
GHOSTTY_API GhosttyResult ghostty_search_run(GhosttySearch search)
GHOSTTY_API GhosttyResult ghostty_search_get_multi(GhosttySearch search, size_t count, const GhosttySearchData *keys, void **values, size_t *out_written)
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 void ghostty_search_free(GhosttySearch search)
GHOSTTY_API GhosttyResult ghostty_search_feed(GhosttySearch search)
GhosttySearchData
Definition search.h:175
@ GHOSTTY_SEARCH_OPT_NEEDLE
Definition search.h:264
@ GHOSTTY_SEARCH_OPT_SELECT_NEXT
Definition search.h:281
@ GHOSTTY_SEARCH_DATA_VIEWPORT_MATCHES
Definition search.h:232
@ GHOSTTY_SEARCH_DATA_TOTAL_MATCHES
Definition search.h:191
@ GHOSTTY_SEARCH_DATA_SELECTED_MATCH
Definition search.h:207
@ GHOSTTY_SEARCH_DATA_SELECTED_INDEX
Definition search.h:200
GHOSTTY_API GhosttyResult ghostty_terminal_point_from_grid_ref(GhosttyTerminal terminal, const GhosttyGridRef *ref, GhosttyPointTag tag, GhosttyPointCoordinate *out)
struct GhosttyTerminalImpl * GhosttyTerminal
Definition types.h:119
GHOSTTY_API void ghostty_terminal_free(GhosttyTerminal terminal)
GHOSTTY_API void ghostty_terminal_vt_write(GhosttyTerminal terminal, const uint8_t *data, size_t len)
GHOSTTY_API GhosttyResult ghostty_terminal_new(const GhosttyAllocator *allocator, GhosttyTerminal *terminal, uint16_t cols, uint16_t rows)
GhosttyResult
Definition types.h:88
@ GHOSTTY_SUCCESS
Definition types.h:90
#define GHOSTTY_INIT_SIZED(type)
Definition types.h:349