lib/pretty/core/src/root.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 //! Pretty is a width-aware document layout and structured output package.
  2 //!
  3 //! ### Design rationale and problem model
  4 //!
  5 //! Pretty printing separates declarative document structure from geometric
  6 //! layout to resolve formatting choices without output backtracking:
  7 //! formatters cannot determine whether an expression fits on a single line
  8 //! until both the expression and its trailing syntactic context are known.
  9 //! An algebraic tree (`Doc`) defers line-break selection to the renderer.
 10 //!
 11 //! The package enforces strict separation between construction and rendering:
 12 //!
 13 //! - Construction: `Builder` allocates AST nodes using any caller-supplied
 14 //!   allocator, while text nodes borrow source slices. Callers bear the
 15 //!   obligation of ensuring both allocated nodes and borrowed slices remain
 16 //!   valid until rendering finishes.
 17 //! - Traversal: The layout pass (`write`, `writeWithState`) performs zero
 18 //!   dynamic allocation, streaming directly to a caller-owned `*std.Io.Writer`.
 19 //!   Callers retain control over output buffering, flush timing, and I/O side
 20 //!   effects, trading automated memory management for zero-allocation
 21 //!   rendering. Where an owned slice is required, `renderAlloc` manages an
 22 //!   allocating writer.
 23 //!
 24 //! ### Continuation-sensitive layout and mode inheritance
 25 //!
 26 //! Line-break decisions require continuation lookahead: evaluating a group
 27 //! in isolation is insufficient because trailing tokens on the same line
 28 //! (such as closing delimiters or commas) could force an immediate overflow.
 29 //! The renderer therefore tests whether the candidate and its inline sibling
 30 //! continuation fit the saturating remaining width `width -| col`.
 31 //!
 32 //! Mode inheritance is deliberately asymmetric:
 33 //!
 34 //! - A flat parent forces all descendant groups into flat mode, because an
 35 //!   inner line break would contradict the parent's single-line commitment.
 36 //! - A broken parent permits nested groups to choose independently, allowing
 37 //!   localized compaction of sub-expressions within an expanded structure.
 38 //!
 39 //! Lookahead revisits subtrees to measure flat width prior to emission, and
 40 //! recursive descent operates without an explicit stack ceiling. Unconditional
 41 //! breaks (`hardline`) emit a newline followed by the current nesting indentation
 42 //! in all modes.
 43 //!
 44 //! ### Layout metrics, control breaks, and styling
 45 //!
 46 //! Column advancement strictly measures raw byte length (`bytes.len`),
 47 //! omitting Unicode scalar, grapheme, and terminal display-width semantics.
 48 //! Raw escape bytes inside text count as bytes. When indivisible text exceeds
 49 //! the margin, it emits across it without error, prioritizing semantic data
 50 //! preservation over rigid column boundaries.
 51 //!
 52 //! Only carriage returns and line feeds reset the column:
 53 //!
 54 //! - In text, `\r` and `\n` reset column tracking, where CR alone produces
 55 //!   zero line feeds. They do not insert structural indentation.
 56 //! - In soft breaks, alternatives containing CR or LF force broken mode to
 57 //!   prevent broken control characters from polluting flat layout.
 58 //!
 59 //! Semantic styling (`styled`) emits ANSI sequences under `ColorMode.ansi`.
 60 //! These escape sequences consume zero layout columns, ensuring monochrome
 61 //! and styled outputs make identical line-break decisions.
 62 //!
 63 //! ### Worked example: Why continuation lookahead matters
 64 //!
 65 //! The trailing-continuation witness from `test.zig` demonstrates why groups
 66 //! cannot evaluate fit in isolation:
 67 //!
 68 //! ```zig
 69 //! const grouped = try builder.group(try builder.concat(&.{
 70 //!     builder.text("abc"),
 71 //!     pretty.softline,
 72 //!     builder.text("de"),
 73 //! }));
 74 //! const doc = try builder.concat(&.{ grouped, builder.text("XY") });
 75 //! ```
 76 //!
 77 //! At width 6, `grouped` alone has a flat width of 6 (`"abc de"`).
 78 //! Evaluated in isolation, it would fit flat. However, trailing sibling
 79 //! `"XY"` shares the same output line without an intervening break,
 80 //! requiring 8 columns total.
 81 //!
 82 //! Continuation lookahead inspects `"XY"`, detects that $6 + 2 > 6$, and
 83 //! forces `grouped` to break, emitting `"abc\ndeXY"` instead of overflowing
 84 //! to `"abc deXY"`.
 85 //!
 86 //! ### Verification boundary
 87 //!
 88 //! Evidence is partitioned across formal models and test suites:
 89 //!
 90 //! - Formal model: The Lean specification in `verification/pretty/` proves
 91 //!   compositionality and layout properties for finite trees over abstract
 92 //!   byte classes (`cell`, `LF`, `CR`) and unbounded natural numbers (`Nat`).
 93 //!   It assumes successful writes and omits continuation lookahead, machine
 94 //!   `usize` saturation, and display width. No formal theorem proves that the
 95 //!   Zig implementation refines the Lean specification.
 96 //! - Empirical and capacity evidence: Automated unit tests in `test.zig` and
 97 //!   Hypothesis property tests in `src/properties/` test ANSI neutrality,
 98 //!   saturating arithmetic, and position composition, while bounded table
 99 //!   capacity contracts are declared in `compact/table.zig`.
100 
101 const types = @import("types.zig");
102 const builder_product = @import("builder.zig");
103 const render = @import("render.zig");
104 
105 pub const json = @import("json/root.zig");
106 pub const compact = @import("compact/root.zig");
107 pub const diagnostic = @import("diagnostic.zig");
108 
109 pub const default_width = types.default_width;
110 pub const LayoutOptions = types.LayoutOptions;
111 pub const ColorMode = types.ColorMode;
112 pub const Style = types.Style;
113 pub const Doc = types.Doc;
114 pub const Nested = types.Nested;
115 pub const Styled = types.Styled;
116 pub const WriteState = types.WriteState;
117 pub const TextWriter = @import("stream.zig").TextWriter;
118 pub const softline = types.softline;
119 pub const softbreak = types.softbreak;
120 pub const hardline = types.hardline;
121 pub const Builder = builder_product.Builder;
122 pub const RenderError = render.Error;
123 pub const FlatFitWriter = render.FlatFitWriter;
124 pub const renderAlloc = render.renderAlloc;
125 pub const write = render.write;
126 pub const writeWithState = render.writeWithState;