A modal, GPU-accelerated, plugin-first text editor written in Rust. Combines vim's modal editing power with emacs's extensibility model on a non-blocking, multi-threaded core where the UI thread does no I/O, no parsing, and no shaping.
Status: Pre-1.0 / heavy development. Phases 0–3 are complete; Phase 4 (LSP) is in wind-down (~90% shipped, 3 trigger-UX items deferred); Phase 5 (GPU rendering + architectural asynchrony) is architecturally complete — the Editor runs on its own dedicated thread with
&mut Editorescape compile-time impossible (EditorActorHandle), both TUI and GPUI peers edit against a live rust-analyzer backend, and Phase 5.8 GPUI feature-parity work is the active frontier. Recent cross-cutting improvements: O(viewport) incremental highlight + cell build (flat to 100k lines), soft-wrap on both renderers, event-driven TUI loop (100ms poll retired), decoration retention across focus changes (inactive panes keep full syntax/inlay/diagnostic set), multibuffer excerpt display, and a diff foundation. The WASM plugin host arrives in Phase 7. Seedocs/dev/operations/implementation.mdfor the per-feature ledger.
Three editors dominate today: Vim/Neovim (best modal editing, single-threaded core, vimscript-only first-class config), Emacs (best extensibility, single- threaded core, elisp-only first-class config), and VS Code (best plugin ecosystem, web stack, latency dominated by Electron).
Lattice picks the strongest property from each and rebuilds them on a modern foundation:
- Strict vim grammar. Counts, registers, operators, motions, text objects, ex-ranges, dot-repeat, marks, macros — semantics preserved exactly. The grammar is the public command API; the default keymap is a config file.
- Emacs-class extensibility through WebAssembly. Plugins are sandboxed WASM components: cross-language, capability-gated, fuel-limited, crash-isolated. A misbehaving plugin cannot freeze the editor.
- Imperceptible input latency. Keystroke → glyph indistinguishable from the terminal/compositor echoing the key — within one display frame under any background load, measured against the best-in-class reference and ratcheted by CI (it only gets faster). The UI thread never blocks. Multi-threaded by construction (one tokio task per document, snapshot-based render reads, bounded-mailbox dispatch).
- GPU-accelerated rendering. Sub-pixel-precise text, smooth scroll, layered paint paths optimized per content type (code vs. rich text vs. inline media). TUI is a first-class peer — not a throwaway.
The full design is in docs/dev/architecture/design.md (v0.4, ~2300 lines).
In priority order when they conflict:
- Performance. Imperceptible keystroke→glyph latency — match-or-beat the best-in-class reference, always within one display frame under load, ratcheted by CI (never regress; only gets faster). Per-call WASM overhead budgeted in CI (typed call < 500 ns p99; grammar-extension round-trip < 5 µs p99).
- Extensibility. WebAssembly Component Model plugin host from day one. WIT is the canonical API. Plugins ship in any language with component-model toolchain support (Rust, Zig, Go, AssemblyScript, …).
- Extensible vim modal editing. Strict vim semantics. The grammar (operators, motions, text objects, registers, ranges, counts) IS the public command API. Adding new motions / text objects / operators is first-class — including future tree-sitter-driven variants.
- Asynchronicity. Three-layer architecture (UI / Core / Plugins)
communicating via typed message passing. Multi-threaded by construction.
Each plugin instance owns its own
wasmtime::Storeand runs as a tokio task; many plugins execute in parallel across cores.
Three deliberate deviations from vim and emacs:
- Unified command / grammar dispatch. Vim's
:ex-command world and the functional / plugin world are merged into oneCommandRegistrywith one dispatcher. The:line stays vim DSL (no function-call / palette syntax — explicit non-goal); plugins /init.rs/ Rust callers constructCommandInvocationdirectly via the WIT host. Two input surfaces, one substrate. Every command is reachable from:via the kind-prefix form (:motion goto-first-line,:operator delete word- forward). (DESIGN.md §2.2 + §5.2.1.) - Everything is a buffer. File tree, outline, diagnostics, search
results, terminal, REPL — all are buffers placed by the user into panes
via splits. The unified
BufferRegistryalready holds documents and file trees today;:bn/:bp/:ls/:bdwork across kinds. No fixed sidebar or bottom-panel concept. (§5.9.) - One extension substrate, two config layers.
options.tomlfor static data;init.rs(compiled to WASM, auto-built on first boot, cached) for programmable config.init.rsis a plugin withbootcapability — the same WIT, same toolchain, same host as third-party plugins. No vimscript, no elisp, no Lua, no embedded scripting language. See DESIGN.md §5.12.
Three layers, communicating only via typed messages and wait-free snapshot loads:
flowchart TD
UI["<b>UI Layer</b><br/><code>lattice-ui-tui</code> (future GPU renderer)<br/>• Renders snapshots; never blocks; never holds locks<br/>• Translates input → CommandInvocation"]
Core["<b>Core Layer</b><br/><code>lattice-runtime</code> + <code>lattice-core</code> + <code>lattice-grammar</code><br/>• One DocumentActor per open document (tokio task)<br/>• Owns the writable Document; bounded mpsc mailbox<br/>• Publishes immutable snapshots via arc-swap<br/>• Grammar dispatcher: motions, operators, text objects,<br/> ex-commands, plugin contributions — peers, not<br/> separated worlds"]
Plugin["<b>Plugin Layer</b> <i>(planned)</i><br/><code>lattice-plugin-host</code><br/>• wasmtime + Component Model + WASI<br/>• One Store per plugin instance, runs as a tokio task<br/>• Capability-gated, fuel-limited, crash-isolated"]
UI -->|"<b>DocumentHandle</b> (cheap clone)<br/>• snapshot() — wait-free Arc load<br/>• dispatch_with_cancel() — Pending<Effect><br/>• apply_edit() — Pending<AppliedEdit>"| Core
Core -.->|"WIT-defined ABI<br/><i>(planned)</i>"| Plugin
classDef done fill:#1f4d2c,stroke:#2ea043,color:#e6edf3
classDef planned fill:#3d2a1a,stroke:#bf8700,color:#e6edf3,stroke-dasharray:5 5
class UI,Core done
class Plugin planned
| Crate | Purpose | Status |
|---|---|---|
lattice-protocol |
Bottom-layer types: Position, Range, Edit, Selection, CancellationToken, Event, ID newtypes. |
✅ stable |
lattice-core |
Buffer (ropey-backed), Document with batched undo, file I/O, regex search (fancy-regex w/ backrefs). |
✅ stable |
lattice-grammar |
Vim modal state machine, CommandRegistry, dispatcher, built-in motions/operators/text objects/ex-cmds. |
✅ stable |
lattice-runtime |
DocumentActor + DocumentHandle (tokio task per doc), arc-swap snapshots, Pending<T>, event bus. |
✅ stable |
lattice-syntax |
Tree-sitter integration (Rust / Python / JavaScript / Markdown bundled), incremental parse, highlights. | ✅ stable |
lattice-completion |
Pluggable completion pipeline: generators, matchers, rankers, annotators. | ✅ stable |
lattice-config |
Typed-options registry (OptionType + ArcSwap-backed cells), :set parser, gen:options source. |
✅ stable |
lattice-mode |
Major / minor mode trait surface, lifecycle events, mode registry, mode-async epoch. | ✅ stable |
lattice-help |
HelpContent + HelpBuffer + per-line markdown highlight cache; backing for :help and :describe-*. |
✅ stable |
lattice-picker |
Picker primitive: source registry, candidate batches, live-query subsystem, MRU frecency. | ✅ stable |
lattice-snippet |
Snippet parser + registry; lazy expansion against editor.snippet_registry (ArcSwap). |
✅ stable |
lattice-file-tree |
File-tree buffer kind + per-buffer state types. | ✅ stable |
lattice-oil |
Oil-style directory buffer kind + state types. | ✅ stable |
lattice-lsp |
LSP client: actor pool, capability fingerprinting, diagnostics layer, supervisor, watcher subscriptions. | ✅ stable |
lattice-cells |
Pure data substrate for the cell-grid renderer: CellMatrix, DisplayMatrix, VirtualRow, display-slice iteration. No I/O, no rendering. |
✅ stable |
lattice-multibuffer |
Multibuffer data model, excerpt layout, major mode, motions, header/fold virtual-row providers. | 🚧 active |
lattice-diff |
Two-way and three-way hunk computation over ropey::Rope inputs (Histogram algorithm via imara-diff). Pure data; no I/O. |
🚧 active |
lattice-terminal |
Terminal emulator state machine + cell grid (alacritty_terminal). Backs the terminal buffer kind. |
🚧 active |
lattice-host |
Renderer-agnostic substrate. Owns Editor, dispatch, mode lifecycle, options cascade, RenderState, PerBufferCache, LSP watcher task, cells/virtual-rows workers. |
✅ stable |
lattice-ui-tui |
Terminal UI peer: crossterm + ratatui, modal cursor, gutter, hlsearch, soft-wrap, command line, popups, picker UI. | ✅ stable |
lattice-ui-gpui |
GPU UI peer (feature window): GPUI + blade rendering. Full edit + LSP against rust-analyzer; Phase 5.8 feature-parity in progress. |
🚧 active |
lattice-cli |
Binary entry-point. --tui / --gui flag routes to either peer; tokio multi-thread main. |
✅ stable |
lattice-config-macros |
Proc-macro for typed-option / OptionGroup registration via linkme distributed slices. |
✅ stable |
lattice-plugin-host |
WASM Component Model host. Planned (Phase 7). | ⛔ planned |
Requirements
- Rust 1.94+ (edition 2024)
- A POSIX terminal that handles 256 colors and bracketed paste
Build & run (TUI — default)
cargo build --release
cargo run --release -- README.mdThe CLI opens the file in the TUI. Editing is full vim modal.
Build & run (GPUI — GPU renderer)
cargo run --release --features gui -- --gui README.mdPass --gui to route to the GPUI peer. --tui explicitly forces the terminal
renderer. The two flags are mutually exclusive; the default stays TUI for direct
invocations.
macOS app bundle
# Install once:
cargo install cargo-bundle
# Build the .app (must cd into the binary crate — cargo-bundle has no -p flag):
cd crates/lattice-cli
cargo bundle --release --features gui
# Open:
open ../../target/release/bundle/osx/Lattice.appThe bundle auto-routes to the GPUI renderer (no --gui flag needed) via
bundle-context detection: when the binary runs inside …/Contents/MacOS/, it
detects the Contents path component and selects GUI automatically. Pass
--tui to override. The icon comes from assets/lattice.icns (10 sizes,
16–512 px + @2x), configured in [package.metadata.bundle] in
crates/lattice-cli/Cargo.toml.
Linux desktop entry
sudo cp assets/linux/com.lattice-editor.lattice.desktop \
/usr/share/applications/
sudo cp assets/favicon-64.png \
/usr/share/icons/hicolor/64x64/apps/com.lattice-editor.lattice.png
gtk-update-icon-cache -f /usr/share/icons/hicolor/Run tests
cargo test --workspace # ~3748 tests, sub-second
cargo clippy --workspace # workspace lints (deny unsafe outside opt-in)Run benchmarks
cargo bench --workspaceNumbers are tracked in docs/dev/operations/benchmarks.md.
Editor sanity tour
In the running editor:
i a o O— Insert / append / open below / open aboveh j k l w b e gg G— motionsdd 2dd dw daw diw— delete with operators / counts / text objectsyy 2yy p P— yank / pasteu <C-r>— undo / redo (every operator lands as one undo unit)Ctrl-VthenIj<Esc>— block-visual insert;>indents block lines/foo<CR> n N— incremental search (regex with backrefs):%s/foo/bar/g— substitute ($1,${name}template syntax):describe-command write— every primitive carries help metadata:motion goto-first-line— invoke any motion / operator / text-object by name (chord grammar still preferred for typing):operator delete word-forward— operator + bare target (motion / text-object resolved implicitly)<C-w>vthen<C-w>l— split vertically and focus the right pane:Tree .(or:e some-folder) — open a folder as a file-tree buffer;<CR>toggles directories or opens files:bn/:bp/:ls/:bd— cycle, list, or close any open buffer (document or tree):set foldmethod=indent— auto-fold indented blocks;zo/zcto open / close:set wrap— enable soft-wrap; long lines reflow at the pane width across both TUI and GPUI:set ui.dim_inactive=off— turn off the inactive-pane DIM overlay (inactive panes keep full syntax + inlay hints; only opacity changes):help— open the topic index;:help folding/:help buffersfor deep-dive docs (<Tab>completes)
Among modern editors built on Rust + GPU + tree-sitter + LSP + WASM (the only stack that hits sub-frame latency without compromising extensibility, and one Lattice shares with Zed deliberately), the differentiators are:
- Vim grammar is the public command API, not a key mapping over a non-modal core. One dispatcher; ex-commands, chords, and plugin contributions all flow through
CommandInvocation. Adding a motion is extending the grammar. - Everything is a buffer, enforced. File tree, diagnostics list, terminal,
*messages*, scratch — all are buffers placed by the user into panes via splits. There is no sidebar / bottom-panel concept. Every text operation works on every buffer kind through one code path. - WIT is the canonical plugin API today (not aspirationally). Any Component-Model language speaks the same protocol — Rust, Zig, Go, AssemblyScript. CI-gated overhead budgets (typed-call < 500 ns p99, grammar-extension round-trip < 5 µs p99).
- Asynchrony is architectural, not disciplinary. Other editors keep the UI thread free by convention — contributors know which calls might block. Lattice (DESIGN.md §5.7) chooses primitives that make UI-thread blocking physically impossible in the steady state:
RenderStatefor reads,Arc<ArcSwapOption<T>>/PerBufferCache<T>for writes, dedicated subsystem tasks for everything else. The architecture stays uniform under feature pressure.
The detailed framing — what Lattice converges with Zed on, where it diverges deliberately, what we evaluated (Zed's cx.notify() reactive paint — assessed and deferred; the savings curve doesn't transfer to Lattice's cursor-coupled UI), and what we explicitly don't borrow (imposed layouts, custom rope, single-language extensions) — is in DESIGN.md Appendix C.
Tracked against DESIGN.md §8.2. Latest measured numbers in
docs/dev/operations/benchmarks.md:
| Commitment | Target (p99) | Status |
|---|---|---|
| Keystroke → buffer mutation | < 100 µs | ✅ ~83 µs constant across buffer sizes |
| Reflex motion / operator | < 2 ms | ✅ all under budget on 50k-line buffers |
| Search (literal pattern) | < 2 ms | ✅ all variants under 2 ms on 200k-line buffers |
| Snapshot load (renderer) | < 5 ns | load_full Arc bump — known headroom) |
| WASM typed call (planned) | < 500 ns | ⛔ Phase 7 |
The architectural rule: the UI thread does no I/O, no parsing, no
shaping. Document mutations route through the actor; renderers read
wait-free snapshots; cancellation is cooperative (Reflex commands observe a
flipped CancellationToken within ~100 µs).
11 phases. The detailed status ledger is in
docs/dev/operations/implementation.md; the phase-level summary:
| Phase | Title | Status |
|---|---|---|
| 0 | Foundation | ✅ done |
| 1 | Modal Editing | ✅ done |
| 2 | Terminal UI Bootstrap | ✅ done |
| 3 | Tree-sitter (Rust / Python / JS / MD) | ✅ done |
| 4 | LSP | 🚧 wind-down (~90% shipped; 3 trigger-UX items deferred: linkedEditingRange, inlineValue, inlineCompletion) |
| 5 | GPU Rendering + architectural async | 🚧 architecturally complete (Editor on its own thread, compile-time enforced; both peers edit against live LSP). Phase 5.8 GPUI feature-parity is the active frontier. |
| 6 | Document Renderer + UI Components | ⛔ planned |
| 7 | Plugin Host (WASM Component Model) | ⛔ planned |
| 8 | Major / Minor Modes + Reference Plugins | ⛔ planned |
| 9 | Rich Buffer Rendering | ⛔ planned |
| 10 | Polish + v1.0 | ⛔ planned |
The granular pre-Phase-4 polish plan, plus the upcoming Phase 4 work:
Async runtime + cancellation (DESIGN.md §5.2, §5.6.8, §5.7)
-
DocumentActor+ bounded-mailbox dispatch (one tokio task per doc) -
arc-swapsnapshot publish-before-reply contract -
Pending<T>typed handles for every mutating call - Cooperative
CancellationToken(grammar + actor + search loops) - Actor stress tests (mailbox saturation, concurrent senders, snapshot ordering)
- Per-
LatencyClassdeadline timers (Reflex < 2 ms, Display < 10 ms) - Plugin async-task host primitive (Phase 7)
Vim modal editing (DESIGN.md §5.2)
- Modal state machine: Normal / Insert / Visual / Op-pending / Command / Search / Replace
- Strict vim grammar: counts, registers, operators, motions, text objects, ex-ranges
- Built-in motion / operator / text-object catalog
- Macros (recorded as
CommandInvocationsequences, not keystrokes) - Marks, dot-repeat with insert-replay, position-history ring (§5.1.1)
- Search + hlsearch with
fancy-regex(RE2 + bounded NFA for backrefs) - Substitute (
:s/:%s) with$1/${name}template syntax - Block-visual
d/y/c/I/A/>/<(per-row dispatch + replicate-on-Esc + single undo unit) - Manual folds (
zf/zo/zc/za/zR/zM/zd) - Counts on linewise ops (
2dd,2>>) collapse to one undo unit - Substitute live preview (matches highlighted while typing
:s/pat/repl/...) - Computed folds — indent fallback (
:set foldmethod=indent); tree-sitter folds queued
Unified command / grammar dispatch (DESIGN.md §5.2.1)
- One
CommandRegistryfor ex-commands, motions, operators, text objects -
:line is a parser front-end producing typedCommandInvocations - Every command is reachable from
:via the kind-prefix form (:motion goto-first-line,:operator delete word-forward,:text-object inner-word); ex-commands keep their bare alias surface; chord grammar stays the natural compact-typing path -
:g/pat/bodyand:v/pat/bodyparsebodyup front (no per-match re-parse) -
Range::Selectionresolves to active visual selection - Interactive arg-prompts via
args_schema(any required arg arms a prompt; Chord kind auto-submits on next chord)
Event system + hooks (DESIGN.md §5.10)
- Typed
Eventcatalog inlattice-protocol -
EventBus:subscribe(filter, target),unsubscribe,publish(kind-indexed dispatch) -
SubscriptionTarget::Channel(mpsc) andSubscriptionTarget::Invocation - Actor publishes events on edit / save / mode-change
-
Before*-event mutation / veto seam (formatters can rewrite content;BeforeQuitcan abort) -
:autocmdandadd-hookparser front-ends desugar tosubscribe
Self-documenting help (DESIGN.md §5.11)
- Every command / option / mode / keybinding carries metadata at registration time
-
:describe-command,:describe-buffer,:describe-key,:keymap,:apropos -
:describe-option,:options(typed options registry) -
:help [topic]-- free-form topic surface with<Tab>completion; built-ins embedded viainclude_str!fromdocs/user/*.md;Dynamicbody variant is the seam for LSP / plugin-supplied topics;:describe-*views emit "See also" topic cross-links -
:describe-event,:describe-mode(each lands when its registry does)
Configuration (DESIGN.md §5.12)
- Renderer-agnostic typed-options crate (
lattice-config):OptionTypetrait with primitive impls (bool,i64,String);Option<T>withArcSwap<T>value cell for wait-free reads;OptionHandle<T>for zero-overhead typed access;ErasedOption+ConfigRegistryfor by-name lookups;:setparser;gen:optionscompletion source — every consumer (App, plugins, future renderers) registers options through the same API -
register_core_options(®istry) → CoreOptionsfor the nine renderer-agnostic options (number,relativenumber,wrap,ignorecase,tabstop,foldenable,foldmethod,scrolloff,completion.auto_insert_single) - Renderer-specific options register via the same API:
lattice-ui-tui::register_tui_options(®istry) → TuiOptionscoversui.dim_inactive,ui.separator,ui.separator_color,ui.statusline_active_fg,ui.statusline_inactive_fg. Future GUI / web renderers register their own -
:set name=value,:set name,:set noname,:set name?parser front-end (drivesConfigRegistry::parse_and_set_command) -
:describe-option <name>(reads the erased view: name, aliases, type label, default, current value, enumerated values, doc) -
options.tomldeserializer (static settings layer) -
init.rsplugin loader (Rust → WASM, auto-built on first boot, cached) — depends on Phase 7 plugin host - Customize-as-buffer-view writes back to
options.toml -
lattice config builddiagnostic CLI subcommand - Project-local
.lattice/options.toml(project-localinit.rsdeferred behind a per-directory trust prompt)
Rendering (DESIGN.md §5.6)
- TUI renderer (crossterm + ratatui) — first-class peer for headless / SSH
- Display-width-aware cursor placement (CJK / Latin / emoji)
- Tree-sitter highlight emission (Rust / Python / JS / Markdown bundled)
- Markdown grammar with fenced-code injections (
```rust```blocks highlight as rust) - Markup
Stylevariants for headings (1-6), bold / italic, links, raw — themable from day one - GPU compositor via GPUI — full edit + LSP parity with TUI on both renderers
- O(viewport) incremental highlight + cell build (flat to 100k-line files; H-series)
- Soft-wrap on both renderers: reflow at pane width, wrap-continuation gutter marker, wrapped cursor movement
- Event-driven TUI loop — 100ms poll replaced by reader-thread +
Wakechannel; idle CPU ≈ 0 - Decoration retention: inactive panes keep full syntax + inlay hints + diagnostics; focus change = opacity flip only, zero recompute
- Per-pane
DisplayMatrix(keyed byPaneId): shared produce/consume path for both active and inactive panes - Renderer trait split (
EditorRenderer/DocumentRenderer/TuiRenderer) — Phase 5/6 - Sprite atlas for icons (file-type, severity, gutter, picker, status) — §5.6.7
- Rich-buffer rendering (variable fonts within a single buffer) — Phase 9
LSP (DESIGN.md §5.4) — Phase 4 (wind-down)
- Diagnostics, completion (Insert-mode + LSP source + docs popup + snippets + ghost text), hover (
K), go-to-definition family (gd/gD/gy/gI), references (gr), symbols - Signature help, rename + prepareRename, code actions + execute, formatting (range / on-type / format-on-save)
- Inlay hints, semantic tokens, document highlights, folding ranges, selection ranges
- Call hierarchy, type hierarchy, document links, code lens, document colors
-
window/showMessage,$/progressmodeline,:lsp-restart, dynamicregisterCapability/unregisterCapability - Cancellation tokens plumbed through every wrapper; per-server compatibility shims
- 15 sub-modes toggle individually or via the
lsp-modeumbrella -
linkedEditingRange(needs shadow-edit machinery),inlineValue(needs DAP),inlineCompletion(lsp-typesproposed) — deferred
Plugin host (DESIGN.md §5.5, §9) — Phase 7
-
wasmtime+ Component Model + WIT bindings - AOT module cache; lazy instantiation; capability manifests; fuel limits
- Per-call overhead bench gates in CI (typed call < 500 ns p99; round-trip < 5 µs p99)
- Reference plugin:
fuzzy-finder(validates picker primitive end-to-end)
Multi-buffer + UI components (DESIGN.md §5.9) — Phase 6
- Buffer abstraction + active-buffer routing;
<C-o>/<C-i>walk across buffers - Pane tree +
<C-w>{s,v,c,h,j,k,l,w,W}window-management chord grammar - Multiple Document buffers +
:bn/:bp/:ls/:bd/:b N - File-tree buffer (
:Tree path); multiple roots;:e folderdefers to:Tree folder - Unified
BufferRegistry: documents and trees in one keyspace;BufferFlags { listed, hidden } - Vim-style pane visuals: per-pane status line,
│separator, inactive dim — all:set ui.* - Hover popup (LSP
K); inline completion popup (vertico-style) - Multibuffer / excerpt display:
lattice-multibuffercrate, excerpt layout, virtual-row header providers, composed→source row map, scrolling -
*messages*buffer (synthetic Document, subsystem-owned streaming content) - Pane groups (D.4) — foundation for diff side-by-side,
:set scrollbind,:windo - Virtual rows +
DisplayMatrix— above/below-anchored inlays, fold summaries, header rows - Diff foundation: two-way and three-way hunk computation; gutter diff signs; side-by-side diff layout (partial)
- Picker primitive as standalone buffer (file picker, project grep, diagnostics list — Phase 6)
- Oil buffer (directory editing à la vim-oil) — Phase 6
- Terminal buffer full PTY (T1 grid + snapshot landed; T2 color/input, T3 persistence — Phase 6)
CI / engineering (DESIGN.md §8)
- Workspace lint policy (
unsafe_code = "deny", opt-in per module) - Criterion benches for runtime, motions, operators, search
- Cross-platform CI matrix (Linux / macOS / Windows) + fmt + doc gates
- Bench compile-check (catches bench-code rot per platform)
- Bench baseline artifact recorded on every push to main
- Bench regression gate (needs stable runner; shared CI variance dwarfs signal)
- Allocation-discipline checks on the render hot path (dhat-based)
| Doc | Purpose |
|---|---|
docs/dev/architecture/design.md |
The design spec (v0.4, authoritative for what to build). |
docs/dev/operations/implementation.md |
Per-feature status ledger; updated per session. |
docs/dev/operations/benchmarks.md |
Latest measured numbers vs. §8.2 commitments. |
docs/dev/operations/verify.md |
Manual-verification checklist for recently shipped features. |
docs/dev/architecture/lsp-architecture.md |
LSP developer reference (companion to DESIGN.md §5.4). |
docs/dev/notes/lsp-features.md |
Every LSP 3.17 capability + implementation status. |
docs/user/ |
User-facing reference (the :help-style topic docs). |
CLAUDE.md |
Conventions for AI-assisted contributions. |
When something disagrees, DESIGN.md and IMPLEMENTATION.md are the authoritative sources for what should exist and what currently does.
The project is open to contributions, but please note the development model:
- The design doc is load-bearing. Significant features need to land in
docs/dev/architecture/design.mdfirst (or be a refinement of an existing section). Open an issue describing the design rationale before sending a PR for non-trivial work. - The four paramount goals override stylistic preferences when they conflict. Performance, extensibility, vim semantics, asynchronicity — in that order.
- Commit history is the moving record. Each commit is a complete unit (one feature / one fix / one refactor). Tests for new behavior land in the same commit.
- No backwards-compatibility shims for vim or emacs configs. Explicit non-goal.
- Specific edge cases may be deferred for v1. The semantics aren't altered, but rare register quirks or obscure block-visual behaviors can land post-1.0 with explicit tests documenting the gap.
- Documenting a vim grammar primitive that's missing from
docs/dev/operations/implementation.md's catalog table. - Adding a built-in motion / text object behind the existing
register_motion/register_text_objectAPI. - Adding a tree-sitter grammar (look at how
lattice-syntaxwires Rust / Python / JS). - Closing a §15 open question with a small design proposal.
# Format + lint + test before pushing.
cargo fmt --all
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspaceThe lint policy is workspace-wide: unsafe_code = "deny". The search
module opts in via #![allow(unsafe_code)] for one specific
from_utf8_unchecked call on a streaming window — every other use must
document its invariant.
Licensed under the MIT License.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be licensed as above, without any additional terms or conditions.
The design draws on three editors that got things right:
- Vim / Neovim — the modal grammar (counts × operators × motions × text objects) is one of the great ideas in editor design. We adopt it wholesale.
- Emacs — the everything-is-a-buffer principle, the self-documenting help system, and the customize-as-buffer-view model are all here.
- Zed — the GPUI rendering stack is the same one Lattice uses, and the modern-Rust editor playbook (GPU rendering, tree-sitter, native LSP, WASM extensions) is shared on purpose. Where Lattice diverges deliberately — modal grammar as the public API, everything-is-a-buffer enforced, WIT as the canonical plugin interface, architectural (not disciplinary) async — is laid out in DESIGN.md Appendix C.