[workspace]
# Session 1 of the pmacs-gpu arc landed `pmacs-protocol` as a workspace
# member (the wire-types crate). Session 2 (this) adds `pmacs-gpu` as
# the GPU/GUI frontend binary — wgpu/winit/cosmic-text/glyphon — with
# hello-world rendering. Protocol consumption arrives in session 3.
# See `docs/pmacs-gpu-design.md`.
members = [".", "pmacs-protocol", "pmacs-gpu"]

[workspace.dependencies]
# Shared between `pmacs` and `pmacs-protocol`. Pinned here so both
# crates use byte-identical postcard / serde versions — the wire
# format depends on it.
serde = { version = "1", features = ["derive"] }
postcard = { version = "1", features = ["use-std"] }
thiserror = "2"
# Shared between `pmacs` (terminal screen) and `pmacs-protocol`
# (`TerminalFrame::validate`). Pinned here so the producer and the
# validator agree on every glyph's column width.
unicode-width = "0.2"

[package]
name = "pmacs"
default-run = "pmacs"
version = "1.0.0"
edition = "2024"
rust-version = "1.95"
description = "Parallel Emacs --- a Rust-cored, Lua-scripted editor in the Emacs tradition"
license = "MIT OR Apache-2.0"
authors = ["Pmacs contributors"]
readme = "README.md"
repository = "https://git.levineuwirth.org/neuwirth/pmacs"
homepage = "https://levineuwirth.org/essays/pmacs"
documentation = "https://docs.rs/pmacs"
keywords = ["editor", "emacs", "tui"]
categories = ["text-editors"]

[lib]
name = "pmacs"
path = "src/lib.rs"

[[bin]]
name = "pmacs"
path = "src/main.rs"

[[bin]]
name = "pmacs-audit"
path = "src/bin/pmacs_audit.rs"

[lints.rust]
unsafe_code = "forbid"
missing_docs = "warn"

[lints.clippy]
all = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
cargo = { level = "warn", priority = -1 }
# Allows: pedantic lints that fight readable code.
module_name_repetitions = "allow"
must_use_candidate = "allow"
missing_errors_doc = "allow"
missing_panics_doc = "allow"
cast_possible_truncation = "allow"
cast_sign_loss = "allow"
cast_precision_loss = "allow"
similar_names = "allow"
multiple_crate_versions = "allow"

[features]
# Pick exactly ONE Lua flavor at build time. v0.1 default is LuaJIT
# (spec §3 Checkpoint 6); lua54 is a buildable fallback for environments
# without LuaJIT support (e.g. some CI hosts, big-endian machines).
#
# `luajit` and `lua54` are MUTUALLY EXCLUSIVE — they select mlua's
# mutually-exclusive Lua backends. `--all-features` enables both and CANNOT
# build; the `mlua-sys` build script errors ("You can enable only one of
# the features: …") before this crate compiles, so pmacs can't preempt it
# with a friendlier message — the fix is to build one flavor. See the
# feature matrix in README.md; build a non-default flavor with
# `--no-default-features --features lua54` (audit F-002).
default = ["luajit"]
luajit = ["mlua/luajit", "mlua/vendored"]
lua54 = ["mlua/lua54", "mlua/vendored"]
# T M10.2: gate the CRDT-backed buffer mode behind an opt-in feature
# so v0.1 builds carry zero CRDT overhead (no loro dependency, no
# field on the Buffer struct layout, no branch on apply_edit). v1.0
# builds enable `crdt`; the rope-projection redirect from M10.1 means
# the feature flip is invisible to v0.1 frontends and to workers.
crdt = ["dep:loro", "pmacs-protocol/crdt"]

[dependencies]
crossterm = "0.28"
thiserror = { workspace = true }
unicode-width = { workspace = true }
unicode-segmentation = "1"
# Regex engine for in-buffer regex search (Q#RX1). `regex::bytes::Regex`
# matches over rope-snapshot bytes and yields byte offsets directly.
# Already in the lockfile transitively; promoted to a direct dependency.
regex = "1"
# Work-stealing deque, MPMC channels, and parking primitives for the
# M3 worker pool (spec §6.3). The umbrella crate re-exports
# `crossbeam_deque`, `crossbeam_channel`, and `crossbeam_utils`.
crossbeam = "0.8"
# Wire format for the M3 message bus (spec §5.2). `serde` derives the
# trait, `rmp-serde` is the MessagePack codec the spec calls out by
# name; in-process and out-of-process workers must look identical to
# Lua, which means even the in-process bus encodes through MessagePack.
serde = { workspace = true }
rmp-serde = "1"
# Wire-types crate (session 1 of pmacs-gpu arc). Owns the
# `InstanceMessage` / `FrontendEvent` / capability / `SemanticFrame`
# family, plus the cell/buffer/rope wire types they reference. The
# `pmacs` crate re-exports through `crate::protocol`, `crate::cell`,
# `crate::buffer`, and `crate::rope` so existing internal imports keep
# working unchanged.
pmacs-protocol = { version = "1.0.0", path = "pmacs-protocol" }
# Wire format for the M5 frontend ↔ instance protocol (T M5.5b).
# Length-prefix framing wraps postcard-encoded payloads. Chosen for
# compactness on the cell-stream traffic (60 Hz cell deltas dominate
# the wire) and for its no-std future-option (a thin attach client or
# embedded REPL bridge can use the same parser). Schema evolution is
# handled by an explicit version handshake (`PROTOCOL_VERSION`); see
# `src/protocol.rs::Hello`. The worker-protocol encoding (§5.5 spec)
# remains MessagePack via `rmp-serde`; different subsystems with
# different requirements (compactness vs schema evolution).
postcard = { workspace = true }
# Signal handling for the M5.5 daemon (T M5.5e). Provides a safe
# wrapper for installing handlers that set an AtomicBool flag, which
# our accept loop polls between iterations. Used for SIGTERM/SIGINT
# (graceful shutdown) and SIGPIPE/SIGHUP (no-op handlers so writes
# return EPIPE rather than killing the process).
signal-hook = "0.3"
# mlua is feature-gated; the `default-features = false` here lets the
# top-level features above pick the Lua flavor without conflict.
mlua = { version = "0.10", default-features = false }
# Tree-sitter incremental-parser core (T M4.1). The bundled grammars
# below (T M4.2) sit on top of this crate.
tree-sitter = "0.26"
# Bundled grammars (T M4.2). Detection is by file extension; loading
# is lazy --- the C-side language object isn't materialized until the
# first buffer of that language opens. Adding a new grammar requires
# only a new dependency line here plus one entry in
# `crate::syntax::BUILTIN_LANGUAGES`.
tree-sitter-rust = "0.24"
tree-sitter-lua = "0.5"
# T M_B3 — C / C++. Bundled so opening a `.c`/`.cpp`/header file in
# the grid TUI gets lexical highlighting (keywords/strings/operators)
# via tree-sitter, with `LspStyleView` layered on top for semantic
# refinement (functions/types/macros via clangd's semantic tokens).
# `.h` is ambiguous C / C++; the `c` entry below claims it by default,
# matching the LSP filetype map in `builtin/runtime/lsp.lua`.
tree-sitter-c = "0.24"
tree-sitter-cpp = "0.23"
# CUDA (`.cu`/`.cuh`). CUDA is C++ with device extensions, but its own
# grammar recognizes `__global__`/`__device__` kernels, `<<<...>>>`
# launch syntax, and CUDA builtins that the C++ grammar would misparse.
# Loading is lazy like the others; `LspStyleView` layers clangd's CUDA
# semantic tokens on top (clangd serves `.cu`/`.cuh` off the same
# binary as C/C++). The crate exports `LANGUAGE`/`HIGHLIGHTS_QUERY`
# (plural, the rust/lua idiom) and rides `tree-sitter-language 0.1`, so
# it shares the ABI crate with the other grammars — no second
# `tree-sitter` in the graph (its own `tree-sitter` dep is dev-only).
tree-sitter-cuda = "0.21"
# Shell / bash. Lexical highlighting for `.sh`/`.bash` and the wider
# shell family (`.zsh`/`.ksh`/`.ash`/`.bats`) — the LSP half
# (bash-language-server + shellcheck/shfmt) was already wired in
# `builtin/runtime/lsp.lua`; this fills in the missing grammar. bash
# does not inherit another grammar, so `HIGHLIGHT_QUERY` (singular, the
# c/cpp/md idiom) is self-contained — no base-query composition like
# `cuda`. Exports `LANGUAGE` over `tree-sitter-language 0.1` (shared ABI
# crate, no second `tree-sitter` in the graph). Note: because the
# grammar's language name (`bash`) matches the `pmacs.lsp.config.bash`
# key, every extension claimed here also auto-attaches
# bash-language-server (shellcheck refuses zsh, so `.zsh` diagnostics
# may be sparse; highlighting is unaffected).
tree-sitter-bash = "0.25"
# Filename-identified languages (no useful extension): Dockerfile, Make,
# CMake. Highlighting for files detected by BASENAME (`Dockerfile`,
# `Makefile`, `CMakeLists.txt`) via the new filename map, plus their
# extensions (`.dockerfile`/`.mk`/`.cmake`). All three ride
# `tree-sitter-language 0.1` with `tree-sitter` dev-only (no second core
# in the graph) and export `LANGUAGE`/`HIGHLIGHTS_QUERY`.
#   * Dockerfile: `tree-sitter-containerfile` — the maintained,
#     ABI-current grammar (the older `tree-sitter-dockerfile` is pinned to
#     `tree-sitter ^0.20` and would fork the graph). Covers Containerfile.
#   * Make / CMake serve `dockerfile`/`cmake` LSP servers too (see lsp.lua);
#     Make has no language server.
tree-sitter-containerfile = "0.9"
tree-sitter-make = "1.1"
tree-sitter-cmake = "0.7"
# Grammar-gap languages: these already have LSP configs (basedpyright /
# gopls / tsserver / taplo / zls) but shipped no tree-sitter grammar, so
# they rendered with no lexical color. Fill the gap. All ride
# `tree-sitter-language 0.1` (tree-sitter dev-only) and export
# `LANGUAGE`/`HIGHLIGHTS_QUERY`. TypeScript is special: one crate ships
# TWO grammars (`LANGUAGE_TYPESCRIPT`/`LANGUAGE_TSX`) and its highlights
# inherit JavaScript, so the `typescript*` entries compose the JS query
# ahead of the TS one (see `crate::syntax::BUILTIN_LANGUAGES`). JavaScript
# parses both `.js` and `.jsx`.
tree-sitter-python = "0.25"
tree-sitter-go = "0.25"
tree-sitter-javascript = "0.25"
tree-sitter-typescript = "0.23"
tree-sitter-toml-ng = "0.7"
tree-sitter-zig = "1.1"
# JSON + YAML — config formats + the honest gate on the Jupyter path
# (JSON). Both are ABI-current (`LANGUAGE: LanguageFn` via
# `tree-sitter-language`), NOT a `tree-sitter ^0.20` fork. Registering
# yaml also lights up markdown `---` frontmatter through the #122
# injection engine (the block injection query already sets yaml for it).
tree-sitter-json = "0.24"
tree-sitter-yaml = "0.7"
# T M9.7: markdown grammar so prompt result buffers with
# `_meta.format = "markdown"` get structured highlighting through the
# same M4 path as rust/lua — no special-case painter in Lua.
# Pinned to `0.5` (caret-excludes-major for 0.x; see
# `tree-sitter-rust = "0.24"` style above). The crate is the official
# tree-sitter-org grammar (`tree-sitter-md` on crates.io); the
# alternative names `tree-sitter-markdown*` are forks of varying
# maintenance status — pick the upstream one.
tree-sitter-md = "0.5"
# LaTeX / TeX (`.tex`/`.latex`/`.sty`/`.cls`) — the "basic LaTeX mode"
# foothold and the tier-1 detection substrate for the inline-math arc (lane
# framing `docs/latex-grammar-math-substrate-framing.md`). The canonical crate name
# `tree-sitter-latex` is squatted by a broken 0.1.0 repackage that ships no
# `scanner.c` for its declared external tokens and therefore CANNOT LINK
# (undefined `tree_sitter_latex_external_scanner_*`). `codebook-tree-sitter-latex`
# is a linkable republish of the real, maintained `latex-lsp/tree-sitter-latex`
# (MIT, texlab's author) modernized to the `LanguageFn` shim: it ships
# `scanner.c`, exports `LANGUAGE` over `tree-sitter-language 0.1` (tree-sitter
# dev-only, shared ABI crate — no second core in the graph), and its
# `grammar.js`/`scanner.c` are byte-identical to upstream (provenance diff
# discharged in the framing). It exports NO query constants, so highlighting
# uses the in-repo overlay `builtin/queries/latex/highlights.scm` (the first
# such overlay; `include_str!`'d in `crate::syntax::BUILTIN_LANGUAGES`).
codebook-tree-sitter-latex = "0.6"
# Web grammars: HTML + CSS (`.html`/`.htm`/`.xhtml`, `.css`). The official
# tree-sitter-org grammars; both export `LANGUAGE` + query constants over
# `tree-sitter-language 0.1` (shared ABI crate, `tree-sitter` dev-only), so no
# overlay is needed. HTML's `INJECTIONS_QUERY` lights up `<script>` ->
# javascript (already registered) and `<style>` -> css via the #122 injection
# engine (see `crate::syntax::BUILTIN_LANGUAGES`).
tree-sitter-html = "0.23"
tree-sitter-css = "0.25"
# Lean 4 (`.lean`) — Arc 8 Stage 1 (`docs/lean4-mode-framing.md`, Q#LN1).
# `leanprover` ships no tree-sitter grammar (Lean parses with its own
# kernel), so both candidates are third-party. The obvious-looking
# `tree-sitter-lean4` is NOT usable: it depends on `tree-sitter = "0.25"`
# DIRECTLY rather than the shared `tree-sitter-language` ABI crate, which
# `^0.25` makes incompatible with our 0.26 and would fork the graph (the
# same defect that rules out `tree-sitter-dockerfile` above); it exports
# only `pub fn language()` while its README advertises a `LANGUAGE` const
# that does not exist; and its package `include` omits `queries/`, so it
# ships no highlights at all. `arborium-lean` is a republish from the
# arborium grammar collection that does it correctly: `tree-sitter-language
# 0.1` as its sole runtime dep, a pre-generated ABI-15 `parser.c` plus
# `scanner.c` (no CLI at build time), and `HIGHLIGHTS_QUERY` /
# `INJECTIONS_QUERY` / `LOCALS_QUERY` constants. Note the shape: it exports
# `const fn language() -> LanguageFn`, so the entry in
# `crate::syntax::BUILTIN_LANGUAGES` reads `arborium_lean::language().into()`
# rather than the `LANGUAGE.into()` every other entry uses.
arborium-lean = "2.18"
# T M4.4 process supervisor: signal sending without `unsafe`. Keep
# the feature surface tight to keep build time low. `poll` feeds the
# compile-mode group readers (cancellable poll-based reads, Q#CM3).
nix = { version = "0.29", default-features = false, features = ["signal", "user", "fs", "term", "socket", "poll"] }
# T M4.4 PTY mode: portable abstraction over openpty / fork+exec
# with controlling-tty wiring. The crate uses internal `unsafe`
# but exposes a fully safe API; pmacs's own `unsafe_code = "forbid"`
# rule still holds.
portable-pty = "0.9"
# T M4.5 LSP wire format: JSON-RPC 2.0 bodies inside Content-Length
# framing. Used only on LSP and similar protocols that require JSON
# specifically; in-process workers continue to use MessagePack
# (spec §5.2). `preserve_order` is off --- LSP doesn't require it
# and the default `BTreeMap` keeps allocation low.
serde_json = "1"
# T M7.1 package manifest format (spec §sec:packages-future): TOML at
# the package's root (`pmacs.toml`). Read at install time, not at every
# load.
toml = "0.8"
# T M7.1 semantic versioning for `version` and `pmacs_required` fields.
# `serde` feature gives us localized parse errors during deserialization
# (rejected at parse time, not at install time).
semver = { version = "1", features = ["serde"] }
# T M7.6 lockfile content-hashing. SHA-256 over `git archive --format=tar`
# bytes detects upstream tampering even when the host serves a SHA-1
# collision. Pure-Rust implementation; no system dep.
sha2 = "0.10"
# T M10.2 sequence CRDT for the v1.0 multi-frontend promotion. Selected
# in M10.1 (Decision section under spec §sec:m10-crdt-choice) on the
# basis of: per-op throughput dominance under realistic mixed-workload
# (190× faster than yrs at 30s window), ~100× more compact wire
# representation, stable v1.x API, and explicit spec mention. The pin
# is exact (`=1.12.0`) per the M10.1 commitment language: library
# updates are deliberate work (re-run M10.1 benchmarks, run convergence
# proptest, run acceptance suite), not Cargo background activity.
# Optional + feature-gated: zero footprint in v0.1 builds.
loro = { version = "=1.12.0", optional = true }

[dev-dependencies]
proptest = "1"
tempfile = "3"
# T M10.2 Day 7 perf bench (tests/m10_2_perf.rs) — seeded RNG plus
# log-normal op-size distribution matching the M10.1 methodology.
# Dev-only; not in release builds. rand 0.8 + edition 2024 requires
# `r#gen` for the (now-reserved) `gen` method name.
rand = "0.8"
rand_distr = "0.4"

[profile.release]
opt-level = 3
lto = "thin"
codegen-units = 1
debug = "line-tables-only"
strip = "debuginfo"

[profile.bench]
opt-level = 3
lto = "thin"
codegen-units = 1
debug = "line-tables-only"
