Skip to content

dhruvasagar/lattice

Repository files navigation

Lattice - a modal, GPU-accelerated, plugin-first text editor in Rust

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 Editor escape 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. See docs/dev/operations/implementation.md for the per-feature ledger.


Why another editor?

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).


Paramount goals

In priority order when they conflict:

  1. 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).
  2. 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, …).
  3. 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.
  4. Asynchronicity. Three-layer architecture (UI / Core / Plugins) communicating via typed message passing. Multi-threaded by construction. Each plugin instance owns its own wasmtime::Store and 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 one CommandRegistry with one dispatcher. The : line stays vim DSL (no function-call / palette syntax — explicit non-goal); plugins / init.rs / Rust callers construct CommandInvocation directly 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 BufferRegistry already holds documents and file trees today; :bn / :bp / :ls / :bd work across kinds. No fixed sidebar or bottom-panel concept. (§5.9.)
  • One extension substrate, two config layers. options.toml for static data; init.rs (compiled to WASM, auto-built on first boot, cached) for programmable config. init.rs is a plugin with boot capability — 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.

Architecture

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> &nbsp;(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/>&nbsp;&nbsp;ex-commands, plugin contributions — peers, not<br/>&nbsp;&nbsp;separated worlds"]

	Plugin["<b>Plugin Layer</b> &nbsp;<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&lt;Effect&gt;<br/>• apply_edit() — Pending&lt;AppliedEdit&gt;"| 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
Loading

Crate map

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

Quick start

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.md

The CLI opens the file in the TUI. Editing is full vim modal.

Build & run (GPUI — GPU renderer)

cargo run --release --features gui -- --gui README.md

Pass --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.app

The 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 --workspace

Numbers are tracked in docs/dev/operations/benchmarks.md.

Editor sanity tour

In the running editor:

  • i a o O — Insert / append / open below / open above
  • h j k l w b e gg G — motions
  • dd 2dd dw daw diw — delete with operators / counts / text objects
  • yy 2yy p P — yank / paste
  • u <C-r> — undo / redo (every operator lands as one undo unit)
  • Ctrl-V then Ij<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>v then <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 / zc to 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 buffers for deep-dive docs (<Tab> completes)

What distinguishes Lattice

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: RenderState for 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.


Performance commitments

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 ⚠️ ~17 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).


Roadmap

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

Detailed feature checklist

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-swap snapshot 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-LatencyClass deadline 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 CommandInvocation sequences, 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 CommandRegistry for ex-commands, motions, operators, text objects
  • : line is a parser front-end producing typed CommandInvocations
  • 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/body and :v/pat/body parse body up front (no per-match re-parse)
  • Range::Selection resolves 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 Event catalog in lattice-protocol
  • EventBus: subscribe(filter, target), unsubscribe, publish (kind-indexed dispatch)
  • SubscriptionTarget::Channel (mpsc) and SubscriptionTarget::Invocation
  • Actor publishes events on edit / save / mode-change
  • Before*-event mutation / veto seam (formatters can rewrite content; BeforeQuit can abort)
  • :autocmd and add-hook parser front-ends desugar to subscribe

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 via include_str! from docs/user/*.md; Dynamic body 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): OptionType trait with primitive impls (bool, i64, String); Option<T> with ArcSwap<T> value cell for wait-free reads; OptionHandle<T> for zero-overhead typed access; ErasedOption + ConfigRegistry for by-name lookups; :set parser; gen:options completion source — every consumer (App, plugins, future renderers) registers options through the same API
  • register_core_options(&registry) → CoreOptions for 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(&registry) → TuiOptions covers ui.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 (drives ConfigRegistry::parse_and_set_command)
  • :describe-option <name> (reads the erased view: name, aliases, type label, default, current value, enumerated values, doc)
  • options.toml deserializer (static settings layer)
  • init.rs plugin 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 build diagnostic CLI subcommand
  • Project-local .lattice/options.toml (project-local init.rs deferred 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 Style variants 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 + Wake channel; idle CPU ≈ 0
  • Decoration retention: inactive panes keep full syntax + inlay hints + diagnostics; focus change = opacity flip only, zero recompute
  • Per-pane DisplayMatrix (keyed by PaneId): 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, $/progress modeline, :lsp-restart, dynamic registerCapability / unregisterCapability
  • Cancellation tokens plumbed through every wrapper; per-server compatibility shims
  • 15 sub-modes toggle individually or via the lsp-mode umbrella
  • linkedEditingRange (needs shadow-edit machinery), inlineValue (needs DAP), inlineCompletion (lsp-types proposed) — 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 folder defers 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-multibuffer crate, 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)

Documentation

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.


Contributing

The project is open to contributions, but please note the development model:

  1. The design doc is load-bearing. Significant features need to land in docs/dev/architecture/design.md first (or be a refinement of an existing section). Open an issue describing the design rationale before sending a PR for non-trivial work.
  2. The four paramount goals override stylistic preferences when they conflict. Performance, extensibility, vim semantics, asynchronicity — in that order.
  3. 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.
  4. No backwards-compatibility shims for vim or emacs configs. Explicit non-goal.
  5. 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.

Good first issues

  • 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_object API.
  • Adding a tree-sitter grammar (look at how lattice-syntax wires Rust / Python / JS).
  • Closing a §15 open question with a small design proposal.

Development workflow

# Format + lint + test before pushing.
cargo fmt --all
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace

The 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.


License

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.


Acknowledgements

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.

About

Yet another text editor that tries to bring together the best of vim & emacs

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors