https://github.com/mlang/caret
Streaming pager which uses the terminal cursor as reading position
https://github.com/mlang/caret
Last synced: 4 days ago
JSON representation
Streaming pager which uses the terminal cursor as reading position
- Host: GitHub
- URL: https://github.com/mlang/caret
- Owner: mlang
- Created: 2026-06-01T19:06:07.000Z (about 2 months ago)
- Default Branch: master
- Last Pushed: 2026-07-14T06:49:12.000Z (14 days ago)
- Last Synced: 2026-07-14T08:26:14.058Z (14 days ago)
- Language: C
- Size: 89.7 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# caret
`caret` is a cursor-based syntax-aware streaming terminal pager
with keyboard-driven navigation, search, selection and copy.
`tmux` users can think of it as streaming copy-mode.
You can move through output while it arrives, and use the terminal
cursor to set a mark and copy the selection.
## Build
Requirements:
- POSIX-like system
- C++23 compiler
- `ncursesw`
Install:
```sh
cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local .
make install
```
## Usage
With streaming input:
```sh
llm ... | caret
```
With files:
```sh
caret file.txt another-file.txt
```
With syntax-aware commands:
```sh
caret --language cpp file.cc
```
Language options apply to the files that follow them, until the next language
option:
```sh
caret -l c foo.def -l markdown README
```
List available languages:
```sh
caret -L
```
`caret` reads data from standard input or files, but takes keyboard input from
`/dev/tty`, so pipelines remain interactive.
For file input, caret enables syntax-aware commands automatically when it
recognizes the filename or extension. Use `--language LANG` or `-l LANG` to
override automatic detection for the following files. Input from stdin defaults
to Markdown.
Run `caret -L` to print the complete language list.
## Keys
Movement:
- `Up` / `Down`, `k` / `j`: move by one wrapped screen row
- `Left` / `Right`, `h` / `l`: move horizontally, crossing row boundaries
- `M-b` / `M-f`: move to the previous/next word, crossing logical lines
- `C-M-b` / `C-M-f`: move to the previous/next matching bracket
- `%`: move to the matching bracket, or the next bracket on the current line
- `C-a` / `C-e`, `Home` / `end`, `0` / `$`: beginning/end of current logical line
- `^` / `M-m`: move to the first non-whitespace character of the current logical line
- `PageUp` / `PageDown`, `Ctrl-B` / `Ctrl-F`: move by nearly one screen
- `zt` / `zz` / `zb`: place the cursor row at the top/middle/bottom of the viewport
- `Ctrl-L`: recenter the viewport on the cursor (equivalent to `zz`)
- `M-<` / `M->`, `g g` / `G`: start/end of document
- `:N`: move to the start of logical line `N` (1-based, clamped to the document)
- `:n` / `:p`: switch to the next/previous file
- `Space`: page down
Selection and copy:
- `C-Space`: set mark at cursor
- `x`: exchange cursor and mark
- `M-w`: copy region between mark and cursor
- `C-k`: copy from cursor to end of current logical line
Syntax selection, when `--language` / `-l` is set:
- `Enter`: select syntax node at cursor
- `u` / `p`: previous/next sibling syntax range
- `i` / `o`: shrink/expand syntax selection
Search:
- `C-s` / `C-r`: incremental literal search forward/backward
- `/` / `?`: forward/backward regex search
- `*` / `#`: search forward/backward for the keyword at or after the cursor
- `n` / `N`: repeat last search in the same/opposite direction
- `f` / `F` then character: next/previous matching character in current line
Quit:
- `q`, `Q`, `C-c`, `C-d`
## Prompts
The `/`, `?`, and `:` prompts share these editing keys:
- `Left` / `Right`, `Ctrl-B` / `Ctrl-F`: move the prompt cursor
- `Home` / `End`, `Ctrl-A` / `Ctrl-E`: move to the start/end of the prompt
- `Backspace`: delete before the cursor, or close an empty prompt
- `Delete`: delete the character under the cursor
- `C-u`: delete from the cursor to the start of the prompt
- `C-k`: delete from the cursor to the end of the prompt
- `Esc`, `Ctrl-G`: cancel
In the `:` prompt, a number moves to the start of that logical line. Line
numbers are 1-based and clamp to the first or last line. `:n` and `:p` switch
between files without wrapping. Empty input does nothing; other input reports
`Unknown command`. `Up` / `Down` browse its session-local command history, which
includes every non-empty submitted command and is separate from search history.
## Regex search
Searches are line-local and use ECMAScript regex syntax.
In the search prompt:
- `Enter`: submit search
- `Up` / `Down`: browse session-local search history
Non-empty submitted searches and keyword searches are saved in history. `/`,
`?`, `*`, and `#` share the same history.
When a match is found, the cursor moves to the start of the match and the mark
is set to the end, highlighting the match. `M-w` then copies the match.
## Incremental search
Incremental searches are line-local literal searches. The search string is shown
in the status line while matches are found as you type. `Ctrl-S` repeats forward
and `Ctrl-R` repeats backward. `Backspace` edits the search string.
`C-g` cancels incremental search and restores the cursor and mark. Movement
and other non-text command keys end incremental search at the current match,
then run normally.
## Copy output
The last region copied with `M-w` is printed to standard output after `caret`
leaves the alternate screen.
Inside tmux, `M-w` also sends the region to:
```sh
tmux load-buffer -
```
If `brltty-clip` is found, the region is also sent to it.
## Notes
- Regular files are loaded directly as UTF-8. Streaming input from stdin and
FIFOs is decoded using the current locale’s multibyte encoding; all active
streams are polled concurrently.
- The program links against `ncursesw`.
- ANSI CSI and OSC escape sequences in the input stream are ignored.