From 2c04102aadd6f5eeb8f057374f158574894b91ec Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Wed, 20 May 2026 09:20:24 -0400 Subject: [PATCH] session 1 commit 2/4: cell wire types moved to pmacs-protocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moves Cell, Glyph, Style, Color, UnderlineStyle, CellCoord, CellSize, DiffSpan, Attachment to pmacs-protocol::cell. CellGrid (borrowed-slice render surface) and fn diff() (rendering helper) stay in src/cell.rs since they're instance-side rendering machinery, not wire shapes. src/cell.rs gains 'pub use pmacs_protocol::{Cell, Glyph, Style, ...};' at the top so every existing internal import (crate::cell::Cell, etc.) keeps resolving. The cell-module tests live alongside CellGrid + diff and reference the re-exported types via 'use super::*' — same as before; no test changes needed. Lib gate: still green (no regressions, 1314 passing). Co-Authored-By: Claude Opus 4.7 (1M context) --- pmacs-protocol/src/cell.rs | 177 +++++++++++++++++++++++++++++++++++ pmacs-protocol/src/lib.rs | 9 +- src/cell.rs | 185 +++---------------------------------- 3 files changed, 197 insertions(+), 174 deletions(-) create mode 100644 pmacs-protocol/src/cell.rs diff --git a/pmacs-protocol/src/cell.rs b/pmacs-protocol/src/cell.rs new file mode 100644 index 0000000..a79c233 --- /dev/null +++ b/pmacs-protocol/src/cell.rs @@ -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, +} + +// --------------------------------------------------------------------------- +// 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, +} diff --git a/pmacs-protocol/src/lib.rs b/pmacs-protocol/src/lib.rs index 68eb20b..521526a 100644 --- a/pmacs-protocol/src/lib.rs +++ b/pmacs-protocol/src/lib.rs @@ -34,10 +34,13 @@ //! (`pmacs-gpu`, debug tools, future ports) depend on this crate //! directly. +pub mod cell; pub mod ids; +pub use cell::{ + Attachment, Cell, CellCoord, CellSize, Color, DiffSpan, Glyph, Style, UnderlineStyle, +}; pub use ids::{BufferId, ByteRange, FrontendId, Position}; -// Cell wire types, the SemanticFrame family, top-level message -// envelopes, and the feature-gated `CrdtOp` follow in subsequent -// commits within this PR. +// The `SemanticFrame` family, top-level message envelopes, and the +// feature-gated `CrdtOp` follow in subsequent commits within this PR. diff --git a/src/cell.rs b/src/cell.rs index fc05d9e..1ea1c5e 100644 --- a/src/cell.rs +++ b/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, -} - -// --------------------------------------------------------------------------- -// 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, -} - /// Compute the diff between two cell buffers of identical layout. /// /// `prev` and `next` are row-major slices, each of length at least