192 lines
8.9 KiB
Markdown
192 lines
8.9 KiB
Markdown
# Pmacs
|
|
|
|
Parallel Emacs --- a Rust-cored, Lua-scripted editor in the Emacs tradition.
|
|
|
|
Pmacs runs the editor's hot path (rope, buffers, views, async runtime,
|
|
process supervision) in Rust, and exposes the rest --- commands, keymaps,
|
|
hooks, packages --- through an embedded Lua VM. The design follows Emacs
|
|
in shape (configurable, introspectable, programmable from inside) but
|
|
discards the single-threaded substrate; workers, message bus, and a
|
|
coroutine-based async surface are core primitives, not bolt-ons.
|
|
|
|
The editor is partitioned into a long-lived **instance** (the daemon
|
|
that owns buffers, processes, and language services) and a thin
|
|
**frontend** that attaches over a typed protocol. Frontends can run
|
|
locally over a Unix socket or remotely over SSH; reconnect-on-drop
|
|
modeled on `mosh` keeps remote sessions alive across laptop suspends.
|
|
|
|
The first-class package is a **REPL package** written entirely against
|
|
the public Lua API: PTY-spawned shells (bash, zsh, fish, lua), an
|
|
ECMA-48 ANSI parser, multi-REPL coexistence, and scrollback management
|
|
with line/byte retention. Successful completion of an audit verifying
|
|
the package uses zero direct Rust core access was the v0.1 ship gate.
|
|
|
|
## Status
|
|
|
|
**v1.0.0 --- stable.** The design described above is implemented and
|
|
working. Solo development carried the project to 1.0; public
|
|
contributions are open from this release. Use, evaluate, file issues,
|
|
and send pull requests.
|
|
|
|
## Build
|
|
|
|
Builds on the toolchain pinned in `rust-toolchain.toml` (Rust
|
|
`1.95.0`, edition 2024); rustup selects it automatically.
|
|
|
|
```sh
|
|
cargo build --release # produce target/release/pmacs (LuaJIT)
|
|
cargo run --release -- <file> # build and run on a file
|
|
cargo test --workspace # unit + integration tests (all crates)
|
|
cargo fmt --check
|
|
cargo clippy --workspace --all-targets -- -D warnings # incl. pmacs-gpu
|
|
```
|
|
|
|
### Feature matrix
|
|
|
|
Cargo features fall into two independent axes. **Do not use
|
|
`--all-features`** — it enables both Lua flavors at once, which cannot
|
|
build (see below).
|
|
|
|
| Feature | Axis | Notes |
|
|
| -------- | ---------- | ---------------------------------------------------------- |
|
|
| `luajit` | Lua flavor | **Default.** LuaJIT backend via `mlua` (vendored). |
|
|
| `lua54` | Lua flavor | Lua 5.4 fallback for hosts without LuaJIT (big-endian, …). |
|
|
| `crdt` | Buffer | Opt-in CRDT-backed buffer mode (adds the `loro` dep). v1.0 builds enable it; orthogonal to the flavor. |
|
|
|
|
**Exactly one Lua flavor must be enabled** — `luajit` *or* `lua54`, never
|
|
both (and never neither). They map to `mlua`'s mutually-exclusive Lua
|
|
backends, so `--all-features` (or `--features luajit,lua54`, or
|
|
`--no-default-features` with no flavor) fails in the `mlua-sys` build
|
|
script with *"You can enable only one of the features: …"*. That check
|
|
lives in a dependency cargo builds first, so pmacs can't replace it with a
|
|
friendlier error — the fix is to build a specific flavor. Supported build
|
|
lines:
|
|
|
|
```sh
|
|
cargo build --release # luajit (default)
|
|
cargo build --release --no-default-features --features lua54
|
|
cargo build --release --features crdt # luajit + crdt
|
|
cargo build --release --no-default-features --features lua54,crdt
|
|
```
|
|
|
|
CI, `cargo hack`, and distro tooling should iterate the flavors
|
|
explicitly (`--no-default-features --features <flavor>[,crdt]`) rather
|
|
than reaching for `--all-features`. Both flavors pass the full test suite;
|
|
CI runs the matrix on every push.
|
|
|
|
Release-only perf gates (M5 keystroke-to-render, M6 ingest/RSS/cancel
|
|
and scrollback navigation/search) are `#[ignore]`'d during normal
|
|
test runs and exercised in CI under dedicated jobs.
|
|
|
|
## Runtime requirements
|
|
|
|
The pmacs binary depends on a small set of POSIX command-line tools
|
|
at runtime. The dependency exists because the project enforces
|
|
`#![forbid(unsafe_code)]` everywhere, including in tests; calls that
|
|
would otherwise need `unsafe` (PTY raw-mode setup, signal name
|
|
translation) are routed through trampolines that exec these tools.
|
|
|
|
- **`/bin/sh`** (POSIX shell). Used for the PTY raw-mode trampoline:
|
|
`/bin/sh -c 'stty raw -echo </dev/tty 2>/dev/null; exec "$@"' --`
|
|
configures the controlling TTY's line discipline before exec'ing
|
|
the actual subprocess. Required by the REPL package and any other
|
|
caller that spawns a process in raw PTY mode.
|
|
- **`stty`** (coreutils). The line-discipline configurator invoked
|
|
by the trampoline above.
|
|
- **`coreutils`** more broadly. The M6 process-supervisor tests
|
|
spawn `cat`, `yes`, and `which`; absent these the test suite (not
|
|
the editor itself) degrades. `which` is also used by the M6.5
|
|
shell-locator helper to find `bash` / `zsh` / `fish` for
|
|
per-shell integration tests. The M7.2 fetcher's timeout test
|
|
uses `sleep`.
|
|
- **`git`** (added in M7.2). Required for any package operation:
|
|
the package fetcher shells out to `git` to clone, fetch, and
|
|
resolve refs, with a deterministic environment
|
|
(`GIT_TERMINAL_PROMPT=0`, `GIT_CONFIG_NOSYSTEM=1`, `LC_ALL=C`,
|
|
inherited `GIT_*` variables stripped). Authentication for
|
|
private repositories rides the user's existing git configuration
|
|
(credential helpers, SSH agent), so packagers do not need a
|
|
separate auth story. Pre-M7 builds without package operations
|
|
do not need git.
|
|
- **`tar`** (added in M7.3). Required for `pmacs.packages.install`:
|
|
the installer materializes a snapshot via `git archive --format=tar`
|
|
piped into `tar -x -C <dest>`, which keeps the on-disk install
|
|
directory self-contained (no `.git` linkage back to the bare
|
|
cache, no working-tree state). GNU tar and bsdtar both work.
|
|
Pre-M7 builds and any path that doesn't call
|
|
`pmacs.packages.install{...}` do not need tar.
|
|
|
|
Distribution packagers should ensure these are runtime dependencies
|
|
of the pmacs package. On a typical Linux distribution, busybox or
|
|
GNU coreutils plus a shell of any kind satisfies the requirement; on
|
|
macOS the system shell and `/usr/bin/stty` are both standard.
|
|
|
|
The Lua VM (LuaJIT or Lua 5.4) is statically vendored via `mlua`'s
|
|
`vendored` feature, so there is no external Lua dependency at
|
|
runtime.
|
|
|
|
## What v0.1 ships with
|
|
|
|
- **Editor core.** Persistent rope with O(log N) edits and snapshots;
|
|
buffers with chained intercept-views; undo/redo; atomic file I/O;
|
|
crossterm-driven TUI.
|
|
- **Lua surface.** Embedded LuaJIT (or Lua 5.4) with `pmacs.command`,
|
|
`pmacs.keymap` (global / mode / buffer scopes), `pmacs.hook`
|
|
(typed kinds: all-must-succeed, first-non-nil, last-write-wins),
|
|
`pmacs.buffer`, `pmacs.window`, `pmacs.editor`. Minibuffer is itself
|
|
a buffer. `describe-key` and `describe-command` for self-introspection.
|
|
- **Async runtime.** Worker pool + message bus + coroutine-based Lua
|
|
async surface (`pmacs.async`). Cancellation is provably correct
|
|
under load.
|
|
- **Language services.** Tree-sitter highlighting and LSP integration
|
|
ride the worker/message infrastructure. Project indexing as a third
|
|
service. Symbol search across 1M+ symbols completes under a second.
|
|
- **Frontend partition.** Daemon mode with local Unix-socket transport;
|
|
cell-delta diffing on the instance side; SSH transport variant for
|
|
remote attach; reconnect-on-drop preserves session state across
|
|
laptop suspend / network drop.
|
|
- **REPL package.** A 691-line Lua package that wires the M6 ANSI
|
|
parser to PTY-spawned shells with raw-mode line discipline. Three-
|
|
region buffer (history / prompt / input) with read-only enforcement;
|
|
RET / C-c / C-d bindings; multi-REPL coexistence; scrollback
|
|
retention with line- and byte-bounded truncation. Published
|
|
alongside an audit verifying zero direct Rust core access.
|
|
|
|
## Layout
|
|
|
|
```
|
|
src/ Rust core
|
|
rope.rs persistent byte-sequence backing every buffer
|
|
buffer.rs buffer + view chain + undo/redo
|
|
editor_core.rs cursor + commands + edit dispatch
|
|
async_runtime.rs worker pool + message bus
|
|
process.rs PTY-aware process supervisor
|
|
ansi.rs ECMA-48 parser
|
|
daemon.rs instance side of the frontend partition
|
|
attach.rs frontend side; protocol + reconnect
|
|
lsp.rs language-server client
|
|
syntax.rs tree-sitter integration
|
|
project_index.rs symbol / file indexing
|
|
text_view.rs cell-grid renderer
|
|
frontend.rs crossterm TUI
|
|
lua_bindings.rs pmacs.* Lua surface installers
|
|
main.rs entry point (TUI + daemon modes)
|
|
|
|
builtin/ Lua runtime shipped with the binary
|
|
commands/default.lua named commands for every editor primitive
|
|
keymaps/default.lua default key bindings
|
|
hooks/default.lua built-in hook definitions
|
|
runtime/ packages (async, lsp, repl, syntax)
|
|
|
|
tests/ integration tests (acceptance gates per milestone)
|
|
```
|
|
|
|
## License
|
|
|
|
Dual-licensed under either of:
|
|
|
|
- MIT License ([LICENSE-MIT](LICENSE-MIT))
|
|
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))
|
|
|
|
at your option.
|