Handbook
A text editor for macOS, drawn on the GPU straight from Metal and CoreText, with a terminal, local Git and Claude Code built in.
Alpha. It is used daily by its author and is now going to a small group of testers. Passing tests are not a stability guarantee; what is known to be broken is in docs/issues.md. Why the project exists is in docs/vision.md, and where it is going in docs/roadmap.md.
The number
Section titled “The number”Measured on 2026-09-24 for v0.2.0-alpha.1, with a 100 MiB file and the
caret halfway through 2.7 million lines:
| Scope | Keystroke to GPU completion, p99 | Budget |
|---|---|---|
| Editor viewport | 0.803 ms | 8.333 ms |
| Editor with native toolbar, sidebar, tabs and status | 1.009 ms | 8.333 ms |
Reproduce both:
cargo run --release --offline --example frame_latencycargo run --release --offline --example frame_latency -- --chromeThe harness measures buffer mutation, layout, draw encoding and GPU completion. It excludes event delivery before the app and display scanout, so it is not a camera-measured input-to-photon result. Results vary by machine.
Requirements
Section titled “Requirements”- An Apple Silicon Mac on macOS 14 or later. There is no Intel build in the alpha and no plan for Linux or Windows; see Design decisions.
- Nothing else. Language servers are optional and found if installed.
Install
Section titled “Install”Download the DMG from the latest release, open it, drag crc to Applications.
The app is signed with a Developer ID and notarized, so it opens without a
Gatekeeper warning. Check the download against SHA256SUMS from the same
release if you like.
To build it yourself instead, with Rust 1.98 or newer:
git clone https://github.com/caioricciuti/crccd crcscripts/bundle.sh --installThat puts an ad-hoc signed crc.app in /Applications. Either way:
open -a crc path/to/file # open a fileopen -a crc . # open the current folder as a projectSettings
Section titled “Settings”crc > Settings (Cmd-,) opens ~/.config/crc/config.toml, created from a
commented template the first time. Five keys: font, font_size, theme
(system, dark or light), caret_blink and update_check (true or
false). Saving the file applies it. Cmd-=, Cmd-- and Cmd-0 change
the size and write it back.
What works
Section titled “What works”- Syntax highlighting via tree-sitter, compiled from vendored C: Rust, Python, C, C++, Go, HTML, CSS, JavaScript and JSX, TypeScript and TSX, JSON, TOML, YAML and shell. Languages embedded in one another are parsed as themselves. Indent guides, bracket-pair highlight, and Git marks in the gutter for lines that differ from HEAD.
- Language servers: completion (as you type, or
Ctrl-Space), diagnostics underlined with counts in the status line, go to definition (F12orCmd-click) and hover (F1). One server per language per project, started when the first file of that language opens: rust-analyzer, gopls, pyright, typescript-language-server and clangd, found in the usual install locations without reading your shell profile. No rename, formatting or code actions yet. - Claude Code inside crc: type
claudein any crc terminal and it connects to the window by itself, or pressCmd-Shift-Cfor a Claude tab. Claude sees the file and selection you are on and the language server diagnostics, and every edit it proposes opens as a diff tab: Accept (Cmd-Return) or Reject (Esc). Files Claude writes to disk show up in their tabs at once. The bridge listens on 127.0.0.1 only, behind a random token in a lock file readable by you alone;CRC_NO_CLAUDE=1turns it off. - Terminal panel (
Ctrl-`orCmd-J): sessions under the editor running your login shell in the project folder, on an xterm-compatible emulator of our own with 24-bit colour, scroll regions, alternate screen and bracketed paste.Cmd-click a path such assrc/main.rs:42:7in the output to open it at that line. - Local Git (
Cmd-Option-G): branch and status, changed files, staged and working-tree diffs, whole-file and hunk stage/unstage, commit. Git runs on workers and reads saved disk state; hooks and signing stay as Git has them. No remote operations. - Files you can trust: atomic saves that keep mode, links and symlink targets; a file changed by another program reloads if the tab is clean, and Save asks Overwrite, Cancel or Reload if it is not; a deleted file marks its tab unsaved; unsaved-changes prompts on close and quit; crash recovery of unsaved text; UTF-8 and UTF-16 BOMs, Windows-1252 and every line ending preserved. Files over 512 MB open read-only and files over 2 GB are refused, both saying why.
- Split panes (
Cmd-\), up to four, each with its own tabs. - Project search (
Cmd-Shift-F) in the background,Cmd-Pfuzzy file open, and>in the same box to run any menu command. - HTTP requests from
.httpfiles (Cmd-Return) in the JetBrains and VS Code REST Client format, with environments and a response tab. - Markdown renders in place as you edit; images, PDFs, audio, video and office files open as Quick Look previews.
- A native, themed UI: system-font chrome, light and dark following the system or your setting, a home screen with recent projects, overlay scrollbars, font zoom, and the right pointer for every control.
- Everything an editor needs: multiple cursors (
Cmd-D, Option-click), find and replace with regex, go to line, comment toggle, line move and duplicate, auto-indent and auto-close, word-wise and Emacs motions, full Unicode with emoji and CJK, session restore, Dock and Finder integration.
What does not
Section titled “What does not”- Language coverage is the list above. A grammar is vendored generated C
pinned in
third_party/CHECKSUMS; SQL and Svelte are not in the alpha. - Git is local. No branch switching, remotes, conflict UI, blame or history. Hunk staging covers tracked text changes; other change types use whole-file actions. Gutter marks compare with HEAD, not the index.
- Language servers are read-only helpers. No rename, formatting, code actions, signature help or references.
- No word wrap, no code folding, no minimap.
- Panes are a way of looking. A file is open in one pane at a time, and a session restores every pane’s files into one pane.
- Bounded by design. Project search skips files over 2 MiB and shows the first 500 matches; gutter marks stop at 2 MiB; the bracket matcher counts its own pair only and does not skip strings or comments.
- Text input is new. Dead keys and IME go through macOS text input and need wider testing across layouts.
- Updates are announced, not installed. Once a day crc asks GitHub for
the list of releases and says in the status line if a newer one exists;
Help > Check for Updates opens its page.
update_check = falseturns the daily check off. Nothing is downloaded. - macOS only, and deliberately so.
| arrows, Home/End, PageUp/PageDown | move |
| shift + any of those | extend the selection |
| click, drag, shift-click | position and select |
Cmd-A |
select all |
Cmd-C / Cmd-X / Cmd-V |
copy, cut, paste |
Cmd-Z / Cmd-Shift-Z |
undo, redo |
Cmd-F |
find; Tab switches to replace; Enter cycles or replaces |
Cmd-/ |
toggle comment |
Cmd-Shift-D |
duplicate line |
Cmd-Shift-Up/Down |
move line |
| Tab / Shift-Tab | indent / outdent selection |
Cmd-P |
fuzzy file open |
Cmd-Option-G |
local Source Control |
Cmd-Return in Source Control |
commit staged changes |
Cmd-Return in a .http file |
send the request under the caret |
Cmd-\ |
split the editor to the right |
Cmd-Option-[ / Cmd-Option-] |
focus the previous / next pane |
Cmd-Option-W |
close the pane |
F12, Cmd-click |
go to definition |
F1 |
hover information for the symbol at the caret |
Ctrl-Space |
completion list; Up/Down, Return or Tab, Escape |
| Escape in a picker/panel | dismiss |
Cmd-F / Cmd-Shift-F |
find in file / find in project |
Cmd-L |
go to line |
Cmd-D |
select word, then next occurrence as another cursor |
| Option-click | add a cursor |
| Escape | back to one cursor |
Cmd-O / Cmd-Shift-O |
open file / open folder |
Cmd-S / Cmd-Shift-S |
save / save as; File > Revert to Saved goes back to disk |
Cmd-, |
settings file |
Cmd-= / Cmd-- / Cmd-0 |
zoom the code font in, out, back to 13 |
Cmd-Shift-C |
Claude Code tab |
Ctrl-` / Cmd-J |
terminal panel |
> in Cmd-P |
run a menu command |
Cmd-N |
new file: a name field in the sidebar |
Cmd-B |
toggle sidebar |
Cmd-1..9, Cmd-[, Cmd-] |
switch tabs |
| drag a tab, or right-click it | reorder or manage tabs |
| scroll over the tab strip | reveal overflowed tabs |
| double-click empty tab strip | new file |
Cmd-W / Cmd-Q |
close tab, quit |
| Option-arrows, Option-Backspace | word-wise motion and deletion |
Ctrl-A/E/K/D/B/F/P/N |
the macOS Emacs bindings |
Design decisions
Section titled “Design decisions”macOS only, no wgpu. Going straight to Metal via objc2 costs ~10 crates
instead of ~200, and one build script instead of ~40. The price is that a
Linux or Windows port means writing a second backend from scratch. That was a
deliberate trade, not an oversight.
CoreText, not a Rust font stack. It ships with the OS, handles hinting and complex-script shaping better than anything we would write, and costs zero dependencies.
Our own rope. src/text/rope.rs is a persistent B-tree. Not
[Not Invented Here]: the rope’s API shape dictates undo, multi-cursor,
syntax-tree sync and eventual CRDT merging, so it is core to the product
rather than plumbing. Because it is persistent, Rope::clone is O(1) and
shares structure, which is what makes whole-buffer undo snapshots cheap and
will let a background thread parse a consistent snapshot while you keep
typing.
One draw call per frame. Every glyph is an instance of one quad. Drawing a
screen of text is a single drawPrimitives with no per-frame geometry work.
Supply chain
Section titled “Supply chain”The dependency tree is 14 crates, one vendored build script, zero proc macros, and that build script has been read line by line.
tree-sitter is compiled from C checked into third_party/ with our own
build.rs, rather than taken as a crate. The crate would have cost 29 crates,
10 build scripts and a proc macro, because serde_json is one of its build
dependencies.
This is not incidental. Cargo runs arbitrary code at build time via build.rs
and at compile time via proc macros, with no allowlist, no --ignore-scripts,
and no minimum release age. Every dependency is pinned exactly (=x.y.z), and
CI fails if a new build script or any proc-macro crate enters the tree.
Details, including the review of every build script, are in
docs/dependency-review.md. Read it before
proposing a dependency.
vendor/ is not committed (16MB against 132KB of source). Run
scripts/vendor.sh for a fully offline, auditable build.
Layout
Section titled “Layout”src/text/rope.rs persistent B-tree ropesrc/text/buffer.rs cursor, selection, undo, files, edit trackingsrc/text/documents.rs open tabssrc/syntax/ tree-sitter parsing and highlight queriessrc/project/tree.rs sidebar file treesrc/project/finder.rs Cmd-P fuzzy matchingsrc/project/git.rs local Git commands, status parsing and bounded diffssrc/platform/git_panel.rs native Source Control panel and workerssrc/http/ .http request files, environments, curl runnersrc/json.rs JSON reader and printer, shared by HTTP and LSPsrc/lsp/ language servers: registry, stdio transport, clientsrc/project/watch.rs FSEvents watcher for the project rootsrc/platform/dispatch.rs main-thread wake-ups from other threadssrc/render/font.rs CoreText shaping and paged glyph rasterizationsrc/render/layout.rs visible lines to glyph quads, hit testingsrc/render/metal.rs pipeline, instance buffer, one draw callsrc/render/shader.metal vertex + fragmentsrc/platform/window.rs NSWindow, input, event loopsrc/platform/latency.rs keystroke-to-present timingsrc/platform/session.rs what was open last timeexamples/ benchmarks and headless render dumpsReporting a problem
Section titled “Reporting a problem”Help > Report a Problem opens a new GitHub issue in your browser with the
version, your macOS version and the last crash (if any) filled in; crc
sends nothing itself. Crash logs are in ~/Library/Logs/crc (Help > Show
Crash Logs). Or open an issue by hand with what you did, what you
expected, what happened, and the version from crc > About crc or
crc --version. A screenshot helps more than a long description.
Contributing
Section titled “Contributing”See CONTRIBUTING.md. The short version: a dependency needs a real argument, and performance claims need a measurement. For a new development chat, start with AGENTS.md and the session handoff.
Licence
Section titled “Licence”GPL-3.0-or-later. See LICENSE.