Merge pull request #39 from levineuwirth/worktree-protocol-crate-extraction
Session 1: pmacs-protocol crate extraction
This commit is contained in:
commit
dab2a48bb0
|
|
@ -1249,6 +1249,7 @@ dependencies = [
|
|||
"loro",
|
||||
"mlua",
|
||||
"nix 0.29.0",
|
||||
"pmacs-protocol",
|
||||
"portable-pty",
|
||||
"postcard",
|
||||
"proptest",
|
||||
|
|
@ -1272,6 +1273,15 @@ dependencies = [
|
|||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pmacs-protocol"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"postcard",
|
||||
"serde",
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "portable-pty"
|
||||
version = "0.9.0"
|
||||
|
|
|
|||
31
Cargo.toml
31
Cargo.toml
|
|
@ -1,3 +1,19 @@
|
|||
[workspace]
|
||||
# Session 1 of the pmacs-gpu arc: `pmacs-protocol` is the wire-types
|
||||
# crate the post-v1.0 frontend (`pmacs-gpu`) will consume directly. The
|
||||
# root `pmacs` package stays a workspace member (no file moves); the
|
||||
# new crate lives under `pmacs-protocol/`. See
|
||||
# `docs/pmacs-gpu-design.md`.
|
||||
members = [".", "pmacs-protocol"]
|
||||
|
||||
[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"
|
||||
|
||||
[package]
|
||||
name = "pmacs"
|
||||
version = "1.0.0"
|
||||
|
|
@ -56,11 +72,11 @@ lua54 = ["mlua/lua54", "mlua/vendored"]
|
|||
# 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"]
|
||||
crdt = ["dep:loro", "pmacs-protocol/crdt"]
|
||||
|
||||
[dependencies]
|
||||
crossterm = "0.28"
|
||||
thiserror = "2"
|
||||
thiserror = { workspace = true }
|
||||
unicode-width = "0.2"
|
||||
# Work-stealing deque, MPMC channels, and parking primitives for the
|
||||
# M3 worker pool (spec §6.3). The umbrella crate re-exports
|
||||
|
|
@ -70,8 +86,15 @@ crossbeam = "0.8"
|
|||
# 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 = { version = "1", features = ["derive"] }
|
||||
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
|
||||
|
|
@ -81,7 +104,7 @@ rmp-serde = "1"
|
|||
# `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 = { version = "1", features = ["use-std"] }
|
||||
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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
[package]
|
||||
name = "pmacs-protocol"
|
||||
version = "1.0.0"
|
||||
edition = "2024"
|
||||
rust-version = "1.95"
|
||||
description = "Wire types for the pmacs daemon ↔ frontend protocol (the SemanticFrame family, capabilities, attach handshake)"
|
||||
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"
|
||||
keywords = ["editor", "emacs", "protocol", "ipc"]
|
||||
categories = ["text-editors", "data-structures"]
|
||||
|
||||
[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: same set as the root `pmacs` crate, mirrored so wire types
|
||||
# moved here don't trip lints the originals didn't.
|
||||
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]
|
||||
# Mirrors the parent `pmacs` crate's `crdt` feature. The wire type
|
||||
# set is unconditional (`CrdtOp` is always compiled — see
|
||||
# `crdt.rs`); the feature exists so `cfg!(feature = "crdt")` checks
|
||||
# inside capability-default helpers (`InstanceCapabilities::default`,
|
||||
# `FrontendCapabilities::default`) evaluate to the same value here
|
||||
# as in the parent crate. The parent activates it via
|
||||
# `pmacs-protocol/crdt` from its own `crdt` feature.
|
||||
crdt = []
|
||||
|
||||
[dependencies]
|
||||
serde = { workspace = true }
|
||||
postcard = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
//! Cell wire types — moved from `pmacs::cell` in session 1 of the
|
||||
//! `pmacs-gpu` arc. The original `pmacs::cell` module keeps
|
||||
//! `CellGrid` (borrowed-slice render surface) and `fn diff()`
|
||||
//! (rendering helper) since those are instance-side rendering
|
||||
//! machinery, not wire shapes; the data types below all travel on
|
||||
//! the `InstanceMessage::CellDelta` wire and on the
|
||||
//! `SemanticFrame` family's `StyleSpan` / `Decoration` shapes.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Coordinates
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Coordinate in the cell grid (row, col), measured in cells.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CellCoord {
|
||||
/// 0-based row.
|
||||
pub row: u32,
|
||||
/// 0-based column.
|
||||
pub col: u32,
|
||||
}
|
||||
|
||||
impl CellCoord {
|
||||
/// Construct a cell coordinate.
|
||||
#[must_use]
|
||||
pub const fn new(row: u32, col: u32) -> Self {
|
||||
Self { row, col }
|
||||
}
|
||||
}
|
||||
|
||||
/// Dimensions of a cell grid, measured in cells.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CellSize {
|
||||
/// Number of rows.
|
||||
pub rows: u32,
|
||||
/// Number of columns.
|
||||
pub cols: u32,
|
||||
}
|
||||
|
||||
impl CellSize {
|
||||
/// Construct a cell size.
|
||||
#[must_use]
|
||||
pub const fn new(rows: u32, cols: u32) -> Self {
|
||||
Self { rows, cols }
|
||||
}
|
||||
|
||||
/// Number of cells in the grid (`rows * cols`).
|
||||
#[must_use]
|
||||
pub const fn area(self) -> u32 {
|
||||
self.rows * self.cols
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Cell content
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// A glyph in a cell.
|
||||
///
|
||||
/// `Char` is the common case (single Unicode codepoint, single column).
|
||||
/// `Cluster` carries a UTF-8 grapheme cluster spanning multiple codepoints
|
||||
/// (e.g. emoji with modifiers, combining characters). `Continuation` is the
|
||||
/// trailing column of a wide character: it has no glyph of its own; the
|
||||
/// preceding cell's glyph occupies both columns.
|
||||
#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum Glyph {
|
||||
/// A single Unicode codepoint occupying one column.
|
||||
Char(char),
|
||||
/// A grapheme cluster (one or more codepoints, encoded as UTF-8).
|
||||
Cluster(Box<[u8]>),
|
||||
/// The trailing column of a wide character. The preceding cell's glyph
|
||||
/// renders into both columns; this cell's `glyph` and `style` are
|
||||
/// ignored by frontends.
|
||||
Continuation,
|
||||
}
|
||||
|
||||
impl Default for Glyph {
|
||||
fn default() -> Self {
|
||||
Self::Char(' ')
|
||||
}
|
||||
}
|
||||
|
||||
/// A 24-bit RGB color, plus a `Default` sentinel meaning "use terminal
|
||||
/// foreground/background".
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub enum Color {
|
||||
/// Use the terminal's default foreground or background.
|
||||
#[default]
|
||||
Default,
|
||||
/// Truecolor RGB.
|
||||
Rgb(u8, u8, u8),
|
||||
/// 8-bit indexed terminal color (0..=255).
|
||||
Indexed(u8),
|
||||
}
|
||||
|
||||
/// Underline style.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub enum UnderlineStyle {
|
||||
/// No underline.
|
||||
#[default]
|
||||
None,
|
||||
/// Single straight underline.
|
||||
Single,
|
||||
/// Double underline.
|
||||
Double,
|
||||
/// Curly (wavy) underline, typical for diagnostics.
|
||||
Curly,
|
||||
/// Dotted underline.
|
||||
Dotted,
|
||||
/// Dashed underline.
|
||||
Dashed,
|
||||
}
|
||||
|
||||
/// Visual style applied to a cell.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Style {
|
||||
/// Foreground color.
|
||||
pub fg: Color,
|
||||
/// Background color.
|
||||
pub bg: Color,
|
||||
/// Bold.
|
||||
pub bold: bool,
|
||||
/// Italic.
|
||||
pub italic: bool,
|
||||
/// Underline.
|
||||
pub underline: UnderlineStyle,
|
||||
/// Reverse video.
|
||||
pub reverse: bool,
|
||||
}
|
||||
|
||||
/// A non-text attachment carried in a cell (TUI ignores this).
|
||||
///
|
||||
/// The TUI backend never inspects `Attachment`; a GUI backend interprets it
|
||||
/// to render images, embedded widgets, and the like.
|
||||
#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum Attachment {
|
||||
/// One cell of an image. The image is identified by `image_id` and the
|
||||
/// cell's location within the image is `(sub_x, sub_y)`.
|
||||
ImageCell {
|
||||
/// Identifier into the frontend's image registry.
|
||||
image_id: u32,
|
||||
/// Sub-cell X offset.
|
||||
sub_x: u16,
|
||||
/// Sub-cell Y offset.
|
||||
sub_y: u16,
|
||||
},
|
||||
}
|
||||
|
||||
/// One cell in the grid.
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Cell {
|
||||
/// What is drawn in the cell.
|
||||
pub glyph: Glyph,
|
||||
/// How it is drawn.
|
||||
pub style: Style,
|
||||
/// Frontend-specific attachment (ignored by the TUI).
|
||||
pub attachment: Option<Attachment>,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Diff span (wire shape for `InstanceMessage::CellDelta`)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// A run of changed cells starting at one position.
|
||||
///
|
||||
/// Frontend translation: emit one cursor-move escape and then write the
|
||||
/// cells in order. Wide characters appear as a leading `Char(_)` followed
|
||||
/// by a [`Glyph::Continuation`] in the same span; the frontend consumes
|
||||
/// both cells but only emits the leading glyph (the terminal handles the
|
||||
/// width).
|
||||
#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct DiffSpan {
|
||||
/// First cell of the span.
|
||||
pub start: CellCoord,
|
||||
/// New contents of the cells in the span, in row-major order. The
|
||||
/// span occupies a contiguous run on `start.row`.
|
||||
pub cells: Vec<Cell>,
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
//! `CrdtOp` — moved from `pmacs::rope` in session 1 of the `pmacs-gpu`
|
||||
//! arc. Carried on the `InstanceMessage::CrdtOp` /
|
||||
//! `FrontendEvent::CrdtOp` wire variants when an attached session
|
||||
//! negotiated `crdt_replica: true`.
|
||||
//!
|
||||
//! The type is unconditional (not `#[cfg]`-gated) — the comment on
|
||||
//! the original `pmacs::rope::CrdtOp` explained why: "Always present
|
||||
//! (not `#[cfg]`-gated) to avoid feature-flag proliferation through
|
||||
//! every Edit consumer." Keeping the same shape here. The `crdt`
|
||||
//! feature on the parent `pmacs` crate gates loro and the actual
|
||||
//! application of CRDT ops; the wire-type definition stays compiled
|
||||
//! unconditionally so consumers (`pmacs-gpu`, debug tools) don't have
|
||||
//! to mirror the feature flag to handle a wire-level variant they
|
||||
//! may never see.
|
||||
|
||||
/// T M10.2 Day 3: CRDT-op metadata carried by `Edit` in CRDT mode.
|
||||
///
|
||||
/// Two fields:
|
||||
///
|
||||
/// * `peer_id` — the producing-frontend identity. M10.4's per-frontend
|
||||
/// undo reads this as the "is this op mine?" filter; saves the
|
||||
/// consumer from parsing the op bytes to extract identity.
|
||||
/// * `bytes` — wire-format serialization of the CRDT ops produced by
|
||||
/// the originating edit, as returned by loro's
|
||||
/// `ExportMode::updates_owned(pre_version)`. M10.5+ sends these
|
||||
/// over the wire; receiving frontends import them via loro's
|
||||
/// `import` to apply on their local CRDT.
|
||||
///
|
||||
/// Constructed by `Buffer::apply_edit` (and `undo` / `redo`) in CRDT
|
||||
/// mode; rope's edit constructors set `Edit::crdt_op` to `None` and
|
||||
/// the Buffer wraps after the rope returns.
|
||||
///
|
||||
/// T M10.5: serde derives added so this type can be the payload of
|
||||
/// `InstanceMessage::CrdtOp` and `FrontendEvent::CrdtOp` on the wire.
|
||||
/// `bytes` is opaque to the protocol layer — it's loro's incremental-
|
||||
/// update format; the receiving end's `CrdtState::import_updates`
|
||||
/// decodes it.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CrdtOp {
|
||||
/// Producing frontend's identity (loro `PeerID`).
|
||||
pub peer_id: u64,
|
||||
/// Wire-format op bytes (loro `ExportMode::updates_owned` output).
|
||||
pub bytes: Vec<u8>,
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
//! Identity and range types — moved from `pmacs::buffer`,
|
||||
//! `pmacs::protocol`, and `pmacs::rope` in session 1 of the
|
||||
//! `pmacs-gpu` arc. The originals re-export these names so internal
|
||||
//! `pmacs` imports (`crate::buffer::BufferId`, `crate::rope::Position`,
|
||||
//! etc.) keep working unchanged.
|
||||
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
/// Opaque, per-process identifier for a buffer.
|
||||
///
|
||||
/// The internal representation is private (R22): callers cannot reach
|
||||
/// for `.0`; construction goes through [`BufferId::next`].
|
||||
///
|
||||
/// T M10.5: `Serialize` / `Deserialize` derived so `BufferId` can be
|
||||
/// the routing key on `InstanceMessage::CrdtOp` / `FrontendEvent::CrdtOp`.
|
||||
/// The serialized form is the bare `u64` (transparent newtype).
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct BufferId(u64);
|
||||
|
||||
impl BufferId {
|
||||
/// Allocate a fresh [`BufferId`] from the process-wide counter.
|
||||
///
|
||||
/// Threading: any thread.
|
||||
#[must_use]
|
||||
pub fn next() -> Self {
|
||||
static COUNTER: AtomicU64 = AtomicU64::new(1);
|
||||
Self(COUNTER.fetch_add(1, Ordering::Relaxed))
|
||||
}
|
||||
|
||||
/// Inspect the raw value. Useful for logging and FFI.
|
||||
#[must_use]
|
||||
pub const fn raw(self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Rebuild an ID from a raw value for crate-internal references that
|
||||
/// persist an already-issued buffer identity in generated text.
|
||||
///
|
||||
/// Was `pub(crate)` before the session-1 crate split; promoted to
|
||||
/// `pub` to remain reachable from `pmacs` after the move. Not
|
||||
/// stable API for external consumers — external callers should
|
||||
/// either round-trip via `serde` or accept that the constructor
|
||||
/// may change.
|
||||
#[must_use]
|
||||
pub const fn from_raw(raw: u64) -> Self {
|
||||
Self(raw)
|
||||
}
|
||||
}
|
||||
|
||||
/// Opaque identifier for a frontend attached to an instance.
|
||||
///
|
||||
/// Every input event carries a `FrontendId`. v0.1 uses one ID per
|
||||
/// instance ([`FrontendId::LOCAL`]); v0.3 generalizes to multi-frontend
|
||||
/// (multi-window, multi-user) without a protocol break.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct FrontendId(pub u64);
|
||||
|
||||
impl FrontendId {
|
||||
/// The single frontend used in v0.1's local-attach mode.
|
||||
///
|
||||
/// Future multi-frontend deployments allocate IDs from a counter
|
||||
/// starting after this value; the constant is reserved.
|
||||
pub const LOCAL: FrontendId = FrontendId(1);
|
||||
}
|
||||
|
||||
/// Byte offset into a rope. Buffer-wide; cursor / selection / span
|
||||
/// anchors all use this type. Type alias rather than newtype so
|
||||
/// arithmetic on offsets (slice ranges, byte deltas) doesn't need
|
||||
/// conversions.
|
||||
pub type Position = u64;
|
||||
|
||||
/// Half-open byte range `[start, end)` into a buffer's rope, matching
|
||||
/// the rope's own range convention.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ByteRange {
|
||||
/// Inclusive start byte offset.
|
||||
pub start: u64,
|
||||
/// Exclusive end byte offset.
|
||||
pub end: u64,
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
//! Wire types for the pmacs daemon ↔ frontend protocol.
|
||||
//!
|
||||
//! Session 1 of the pmacs-gpu arc — see `docs/pmacs-gpu-design.md` in
|
||||
//! the workspace root. This crate owns every type that appears on the
|
||||
//! `InstanceMessage` / `FrontendEvent` wire so a future `pmacs-gpu`
|
||||
//! frontend can depend on it directly without pulling in the `pmacs`
|
||||
//! main crate (and its Lua / tree-sitter / process-supervisor surface).
|
||||
//!
|
||||
//! What lives here:
|
||||
//! - The `SemanticFrame` family: `StyleSpans`, `Decorations`,
|
||||
//! `InlineAdornments`, `BlockAdornments`, `FoldState`,
|
||||
//! `ResourceOffer`, `FileStyleSummary`.
|
||||
//! - The grid-rendering family: `CellDelta`, plus `Cell`, `Glyph`,
|
||||
//! `Style`, `Color`, `UnderlineStyle`, `CellCoord`, `CellSize`,
|
||||
//! `DiffSpan`, `Attachment`.
|
||||
//! - Identity types: `BufferId`, `FrontendId`, `Position`, `ByteRange`.
|
||||
//! - The full message envelopes: `InstanceMessage`, `FrontendEvent`,
|
||||
//! `GoodbyeReason`, capability structs, `PresenceUpdate`, etc.
|
||||
//! - The optional `CrdtOp` wire variant (feature-gated on `crdt`).
|
||||
//!
|
||||
//! What does NOT live here:
|
||||
//! - `crate::cell::CellGrid` and `crate::cell::diff()` (rendering
|
||||
//! helpers, not wire types — stay in the `pmacs` crate).
|
||||
//! - `Buffer` / `BufferRegistry` / `Rope` / `Edit` / `Range`
|
||||
//! (instance-side editor machinery).
|
||||
//! - `AttachTarget` and the attach-CLI binding error types
|
||||
//! (`pmacs`-binary-only logic; `pmacs-gpu` builds its own attach
|
||||
//! client).
|
||||
//! - Lua / tree-sitter / process-supervisor everything.
|
||||
//!
|
||||
//! The `pmacs` crate re-exports back through its existing module paths
|
||||
//! (`crate::cell::Style`, `crate::buffer::BufferId`, etc.) so internal
|
||||
//! pmacs code doesn't churn its imports. New consumers
|
||||
//! (`pmacs-gpu`, debug tools, future ports) depend on this crate
|
||||
//! directly.
|
||||
|
||||
pub mod cell;
|
||||
pub mod crdt;
|
||||
pub mod ids;
|
||||
pub mod message;
|
||||
|
||||
pub use cell::{
|
||||
Attachment, Cell, CellCoord, CellSize, Color, DiffSpan, Glyph, Style, UnderlineStyle,
|
||||
};
|
||||
pub use crdt::CrdtOp;
|
||||
pub use ids::{BufferId, ByteRange, FrontendId, Position};
|
||||
pub use message::{
|
||||
AdornmentContent, AdornmentPlacement, AttachRequest, BlockAdornment, CursorState, Decoration,
|
||||
DecorationKind, DecorationSegment, FrontendCapabilities, FrontendEvent, GoodbyeReason, Hello,
|
||||
InlineAdornment, InstanceCapabilities, InstanceIdentity, InstanceMessage, InstanceSignal, Key,
|
||||
KeyEvent, Modifiers, MouseButton, MouseEvent, MouseKind, NegotiatedCapabilities,
|
||||
PROTOCOL_VERSION, ResourceBody, SUPPORTED_PROTOCOL_VERSIONS, SelectionSnapshot, StyleSegment,
|
||||
StyleSpan, is_supported_protocol_version, negotiate_capabilities,
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -27,7 +27,6 @@
|
|||
//! observe `&Buffer` while the buffer's own `&mut self` is held.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use crate::file_io::FileMeta;
|
||||
use crate::rope::{Edit, Position, Range, Rope, RopeError};
|
||||
|
|
@ -37,40 +36,12 @@ use crate::view::{InterceptContext, View};
|
|||
// Identifiers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Opaque, per-process identifier for a buffer.
|
||||
///
|
||||
/// The internal representation is private (R22): callers cannot reach for
|
||||
/// `.0`; construction goes through [`BufferId::next`].
|
||||
///
|
||||
/// T M10.5: `Serialize` / `Deserialize` derived so `BufferId` can be the
|
||||
/// routing key on `InstanceMessage::CrdtOp` / `FrontendEvent::CrdtOp`.
|
||||
/// The serialized form is the bare `u64` (transparent newtype).
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct BufferId(u64);
|
||||
|
||||
impl BufferId {
|
||||
/// Allocate a fresh [`BufferId`] from the process-wide counter.
|
||||
///
|
||||
/// Threading: any thread.
|
||||
#[must_use]
|
||||
pub fn next() -> Self {
|
||||
static COUNTER: AtomicU64 = AtomicU64::new(1);
|
||||
Self(COUNTER.fetch_add(1, Ordering::Relaxed))
|
||||
}
|
||||
|
||||
/// Inspect the raw value. Useful for logging and FFI.
|
||||
#[must_use]
|
||||
pub const fn raw(self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Rebuild an ID from a raw value for crate-internal references that
|
||||
/// persist an already-issued buffer identity in generated text.
|
||||
#[must_use]
|
||||
pub(crate) const fn from_raw(raw: u64) -> Self {
|
||||
Self(raw)
|
||||
}
|
||||
}
|
||||
/// Re-export of `pmacs_protocol::BufferId` (moved there in session 1
|
||||
/// of the `pmacs-gpu` arc — see `docs/pmacs-gpu-design.md`). Existing
|
||||
/// `crate::buffer::BufferId` import paths continue to resolve through
|
||||
/// this re-export; new consumers (`pmacs-gpu`, debug tools) should
|
||||
/// depend on `pmacs-protocol` directly.
|
||||
pub use pmacs_protocol::BufferId;
|
||||
|
||||
/// Opaque, per-buffer identifier for an attached view.
|
||||
///
|
||||
|
|
|
|||
185
src/cell.rs
185
src/cell.rs
|
|
@ -7,162 +7,21 @@
|
|||
//! [`Style`], and an optional [`Attachment`]. The TUI ignores `Attachment`;
|
||||
//! a future GUI backend interprets it.
|
||||
//!
|
||||
//! The full layout and helpers (composition, diffing) land in T M1.6. T M1.4
|
||||
//! pulls in the public types so the [`crate::view::View`] trait can reference
|
||||
//! them.
|
||||
//! ## Module split (session 1 of the `pmacs-gpu` arc)
|
||||
//!
|
||||
//! The data types — `Cell`, `Glyph`, `Style`, `Color`, `UnderlineStyle`,
|
||||
//! `CellCoord`, `CellSize`, `Attachment`, `DiffSpan` — moved to
|
||||
//! `pmacs-protocol::cell` and are re-exported here so existing
|
||||
//! `crate::cell::Cell` import paths keep resolving. [`CellGrid`] and
|
||||
//! [`diff`] stay in this module — they are instance-side rendering
|
||||
//! machinery, not wire shapes. See `docs/pmacs-gpu-design.md`.
|
||||
|
||||
pub use pmacs_protocol::{
|
||||
Attachment, Cell, CellCoord, CellSize, Color, DiffSpan, Glyph, Style, UnderlineStyle,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Coordinates
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Coordinate in the cell grid (row, col), measured in cells.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CellCoord {
|
||||
/// 0-based row.
|
||||
pub row: u32,
|
||||
/// 0-based column.
|
||||
pub col: u32,
|
||||
}
|
||||
|
||||
impl CellCoord {
|
||||
/// Construct a cell coordinate.
|
||||
#[must_use]
|
||||
pub const fn new(row: u32, col: u32) -> Self {
|
||||
Self { row, col }
|
||||
}
|
||||
}
|
||||
|
||||
/// Dimensions of a cell grid, measured in cells.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CellSize {
|
||||
/// Number of rows.
|
||||
pub rows: u32,
|
||||
/// Number of columns.
|
||||
pub cols: u32,
|
||||
}
|
||||
|
||||
impl CellSize {
|
||||
/// Construct a cell size.
|
||||
#[must_use]
|
||||
pub const fn new(rows: u32, cols: u32) -> Self {
|
||||
Self { rows, cols }
|
||||
}
|
||||
|
||||
/// Number of cells in the grid (`rows * cols`).
|
||||
#[must_use]
|
||||
pub const fn area(self) -> u32 {
|
||||
self.rows * self.cols
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Cell content
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// A glyph in a cell.
|
||||
///
|
||||
/// `Char` is the common case (single Unicode codepoint, single column).
|
||||
/// `Cluster` carries a UTF-8 grapheme cluster spanning multiple codepoints
|
||||
/// (e.g. emoji with modifiers, combining characters). `Continuation` is the
|
||||
/// trailing column of a wide character: it has no glyph of its own; the
|
||||
/// preceding cell's glyph occupies both columns.
|
||||
#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum Glyph {
|
||||
/// A single Unicode codepoint occupying one column.
|
||||
Char(char),
|
||||
/// A grapheme cluster (one or more codepoints, encoded as UTF-8).
|
||||
Cluster(Box<[u8]>),
|
||||
/// The trailing column of a wide character. The preceding cell's glyph
|
||||
/// renders into both columns; this cell's `glyph` and `style` are
|
||||
/// ignored by frontends.
|
||||
Continuation,
|
||||
}
|
||||
|
||||
impl Default for Glyph {
|
||||
fn default() -> Self {
|
||||
Self::Char(' ')
|
||||
}
|
||||
}
|
||||
|
||||
/// A 24-bit RGB color, plus a `Default` sentinel meaning "use terminal
|
||||
/// foreground/background".
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub enum Color {
|
||||
/// Use the terminal's default foreground or background.
|
||||
#[default]
|
||||
Default,
|
||||
/// Truecolor RGB.
|
||||
Rgb(u8, u8, u8),
|
||||
/// 8-bit indexed terminal color (0..=255).
|
||||
Indexed(u8),
|
||||
}
|
||||
|
||||
/// Underline style.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub enum UnderlineStyle {
|
||||
/// No underline.
|
||||
#[default]
|
||||
None,
|
||||
/// Single straight underline.
|
||||
Single,
|
||||
/// Double underline.
|
||||
Double,
|
||||
/// Curly (wavy) underline, typical for diagnostics.
|
||||
Curly,
|
||||
/// Dotted underline.
|
||||
Dotted,
|
||||
/// Dashed underline.
|
||||
Dashed,
|
||||
}
|
||||
|
||||
/// Visual style applied to a cell.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Style {
|
||||
/// Foreground color.
|
||||
pub fg: Color,
|
||||
/// Background color.
|
||||
pub bg: Color,
|
||||
/// Bold.
|
||||
pub bold: bool,
|
||||
/// Italic.
|
||||
pub italic: bool,
|
||||
/// Underline.
|
||||
pub underline: UnderlineStyle,
|
||||
/// Reverse video.
|
||||
pub reverse: bool,
|
||||
}
|
||||
|
||||
/// A non-text attachment carried in a cell (TUI ignores this).
|
||||
///
|
||||
/// The TUI backend never inspects `Attachment`; a GUI backend interprets it
|
||||
/// to render images, embedded widgets, and the like.
|
||||
#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum Attachment {
|
||||
/// One cell of an image. The image is identified by `image_id` and the
|
||||
/// cell's location within the image is `(sub_x, sub_y)`.
|
||||
ImageCell {
|
||||
/// Identifier into the frontend's image registry.
|
||||
image_id: u32,
|
||||
/// Sub-cell X offset.
|
||||
sub_x: u16,
|
||||
/// Sub-cell Y offset.
|
||||
sub_y: u16,
|
||||
},
|
||||
}
|
||||
|
||||
/// One cell in the grid.
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Cell {
|
||||
/// What is drawn in the cell.
|
||||
pub glyph: Glyph,
|
||||
/// How it is drawn.
|
||||
pub style: Style,
|
||||
/// Frontend-specific attachment (ignored by the TUI).
|
||||
pub attachment: Option<Attachment>,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Grid
|
||||
// Grid (instance-side render surface; borrowed slice; does not move)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// A mutable view onto a row-major cell buffer.
|
||||
|
|
@ -213,25 +72,9 @@ impl CellGrid<'_> {
|
|||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Diff
|
||||
// Diff (instance-side renderer helper; does not move)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// A run of changed cells starting at one position.
|
||||
///
|
||||
/// Frontend translation: emit one cursor-move escape and then write the
|
||||
/// cells in order. Wide characters appear as a leading `Char(_)` followed
|
||||
/// by a [`Glyph::Continuation`] in the same span; the frontend consumes
|
||||
/// both cells but only emits the leading glyph (the terminal handles the
|
||||
/// width).
|
||||
#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct DiffSpan {
|
||||
/// First cell of the span.
|
||||
pub start: CellCoord,
|
||||
/// New contents of the cells in the span, in row-major order. The
|
||||
/// span occupies a contiguous run on `start.row`.
|
||||
pub cells: Vec<Cell>,
|
||||
}
|
||||
|
||||
/// Compute the diff between two cell buffers of identical layout.
|
||||
///
|
||||
/// `prev` and `next` are row-major slices, each of length at least
|
||||
|
|
|
|||
1370
src/protocol.rs
1370
src/protocol.rs
File diff suppressed because it is too large
Load Diff
43
src/rope.rs
43
src/rope.rs
|
|
@ -43,11 +43,11 @@ const MAX_CHILDREN: usize = 8;
|
|||
// Public types
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Byte offset into a [`Rope`].
|
||||
///
|
||||
/// Not a codepoint index, not a grapheme index. Grapheme awareness is a
|
||||
/// view-layer concern.
|
||||
pub type Position = u64;
|
||||
// `Position` is re-exported from `pmacs-protocol` (session 1 of the
|
||||
// `pmacs-gpu` arc — see `docs/pmacs-gpu-design.md`). The type alias
|
||||
// is a `u64` byte offset into a [`Rope`]: not a codepoint index, not
|
||||
// a grapheme index. Grapheme awareness is a view-layer concern.
|
||||
pub use pmacs_protocol::Position;
|
||||
|
||||
/// A persistent rope of bytes.
|
||||
///
|
||||
|
|
@ -330,35 +330,10 @@ pub struct Edit {
|
|||
pub crdt_op: Option<Box<CrdtOp>>,
|
||||
}
|
||||
|
||||
/// T M10.2 Day 3: CRDT-op metadata carried by [`Edit`] in CRDT mode.
|
||||
///
|
||||
/// Two fields:
|
||||
///
|
||||
/// * `peer_id` — the producing-frontend identity. M10.4's per-frontend
|
||||
/// undo reads this as the "is this op mine?" filter; saves the
|
||||
/// consumer from parsing the op bytes to extract identity.
|
||||
/// * `bytes` — wire-format serialization of the CRDT ops produced by
|
||||
/// the originating edit, as returned by loro's
|
||||
/// `ExportMode::updates_owned(pre_version)`. M10.5+ sends these
|
||||
/// over the wire; receiving frontends import them via loro's
|
||||
/// `import` to apply on their local CRDT.
|
||||
///
|
||||
/// Constructed by `Buffer::apply_edit` (and `undo` / `redo`) in CRDT
|
||||
/// mode; rope's edit constructors set `Edit::crdt_op` to `None` and
|
||||
/// the Buffer wraps after the rope returns.
|
||||
///
|
||||
/// T M10.5: serde derives added so this type can be the payload of
|
||||
/// `InstanceMessage::CrdtOp` and `FrontendEvent::CrdtOp` on the wire.
|
||||
/// `bytes` is opaque to the protocol layer — it's loro's incremental-
|
||||
/// update format; the receiving end's `CrdtState::import_updates`
|
||||
/// decodes it.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CrdtOp {
|
||||
/// Producing frontend's identity (loro `PeerID`).
|
||||
pub peer_id: u64,
|
||||
/// Wire-format op bytes (loro `ExportMode::updates_owned` output).
|
||||
pub bytes: Vec<u8>,
|
||||
}
|
||||
// `CrdtOp` moved to `pmacs-protocol::crdt` (session 1 of the
|
||||
// `pmacs-gpu` arc — see `docs/pmacs-gpu-design.md`). Re-exported here
|
||||
// so existing `crate::rope::CrdtOp` import paths continue to resolve.
|
||||
pub use pmacs_protocol::CrdtOp;
|
||||
|
||||
/// A half-open byte range `[start, end)` into a rope.
|
||||
///
|
||||
|
|
|
|||
Loading…
Reference in New Issue