| <!--* freshness: { reviewed: '2026-10-12' } *--> | |
| # Output Formatting (`zxdb/format`) | |
| ## Overview | |
| `//src/developer/debug/zxdb/format` provides synchronous and asynchronous text | |
| formatting, syntax highlighting, identifier/function formatting, and | |
| `FormatNode` tree rendering (Layer 4). It decouples styled output buffers | |
| (`OutputBuffer` and `AsyncOutputBuffer`) from interactive `zxdb/console` session | |
| state. | |
| ## Interface | |
| Public headers expose these buffer and formatting primitives: | |
| - `zxdb::OutputBuffer`, `zxdb::OutputBuffer::Span`: Styled text buffer of spans | |
| annotated with `Syntax`, `TextForegroundColor`, and `TextBackgroundColor` | |
| (`Append`, `FormatHelp`, `WriteToStdout`, `AsString`, `UnicodeCharWidth`, | |
| `TrimTrailingNewlines`, `Clear`, `GetDebugString`) (`output_buffer.h`). | |
| - `zxdb::AsyncOutputBuffer`: Ref-counted hierarchical output buffer | |
| (`fxl::RefCountedThreadSafe<AsyncOutputBuffer>`) that coordinates asynchronous | |
| formatting (`Append`, `Complete`, `SetCompletionCallback`, `is_complete`, | |
| `DestructiveFlatten`) (`async_output_buffer.h`). | |
| - `zxdb::FormatBufferOptions`, `zxdb::DescribeFormatTreeNode`, | |
| `zxdb::FormatTreeNode`, `zxdb::FormatValue`, `zxdb::FormatVariable`, | |
| `zxdb::FormatExpressions`: Value and expression formatters that render | |
| `FormatNode` trees into `OutputBuffer` or `AsyncOutputBuffer` instances | |
| (`format.h`). | |
| - `zxdb::FormatIdentifierOptions`, `zxdb::FormatIdentifier`, | |
| `zxdb::FormatFunctionNameOptions`, `zxdb::FormatFunctionName`: Formatters for | |
| `Identifier`, `ParsedIdentifier`, and `Function` symbols (`format_name.h`). | |
| - `zxdb::Syntax`, `zxdb::TextForegroundColor`, `zxdb::TextBackgroundColor`, | |
| `zxdb::SyntaxToString`, `zxdb::TextForegroundColorToString`, | |
| `zxdb::TextBackgroundColorToString`: Syntax and terminal color enums and | |
| string converters (`output_buffer.h`). | |
| - `zxdb::GetCurrentRowMarker`, `zxdb::GetBreakpointMarker`, | |
| `zxdb::GetDisabledBreakpointMarker`, `zxdb::GetBullet`, | |
| `zxdb::GetExclamation`, `zxdb::GetRightArrow`, `zxdb::StringToBool`, | |
| `zxdb::BoolToString`, `zxdb::UnicodeCharWidth`: Console glyph helpers, boolean | |
| parsers, and UTF-8 display width calculators (`string_util.h`). | |
| ## Invariants | |
| Callers and implementations must preserve these invariants: | |
| - In `OutputBuffer::Span`, explicit `TextForegroundColor` and | |
| `TextBackgroundColor` values apply only when `syntax == Syntax::kNormal` and | |
| do not override `Syntax` styling. | |
| - `OutputBuffer::operator==` and `OutputBuffer::GetDebugString` normalize spans | |
| by merging adjacent spans that share identical syntax, foreground, and | |
| background attributes. | |
| - `AsyncOutputBuffer::is_complete()` returns `true` only after the caller calls | |
| `Complete()` (`marked_complete_ == true`) and all child buffers complete | |
| (`pending_resolution_ == 0`). | |
| - `AsyncOutputBuffer` enforces `FX_DCHECK(!marked_complete_)` on every | |
| `Append()` and `Complete()` call. Callers must not append or complete again | |
| after calling `Complete()`. | |
| - `AsyncOutputBuffer::SetCompletionCallback` enforces | |
| `FX_DCHECK(!is_complete())` and `FX_DCHECK(!cb || !completion_callback_)`. | |
| Callers can register a non-empty callback at most once, and the buffer clears | |
| `completion_callback_` before invoking it. | |
| - `AsyncOutputBuffer` owns child buffers via `fxl::RefPtr` in a top-down tree, | |
| while child callbacks hold raw parent pointers to avoid cycles. Callers must | |
| keep the root `AsyncOutputBuffer` alive until completion. | |
| - `AsyncOutputBuffer::DestructiveFlatten()` requires `FX_DCHECK(is_complete())`. | |
| Call it at most once because it moves spans out of the buffer tree. | |
| - `DescribeFormatTreeNode` and `FormatTreeNode` cap tree depth at | |
| `FormatBufferOptions::max_depth` (default `16`, eliding deeper nodes with an | |
| ellipsis). They also cap pointer dereferences along any path at | |
| `FormatBufferOptions::pointer_expand_depth` (default `1`) to break pointer | |
| cycles. | |
| - `FormatTreeNode`, `FormatValue`, `FormatVariable`, and `FormatExpressions` | |
| return formatted output without a trailing newline. | |
| - `FormatIdentifier` wraps non-standard identifier components (those failing | |
| `ExprTokenizer::IsNameToken` for C and not starting with `operator`) in | |
| `$(...)`. It always escapes `$` and `\\`, and escapes `(` and `)` when | |
| parentheses inside the component do not balance. | |
| ## Side Effects | |
| These formatting functions produce the following I/O and tree side effects: | |
| - `OutputBuffer::WriteToStdout` writes ANSI escape sequences and span text to | |
| `stdout` via `fwrite` and flushes `stdout` via `std::fflush`. | |
| - `DescribeFormatTreeNode`, `FormatValue`, `FormatVariable`, and | |
| `FormatExpressions` start asynchronous symbol and expression evaluation via | |
| `FillFormatNodeDescription` and `EvalContext::GetVariableValue`. They update | |
| `FormatNode` trees in place and complete the returned `AsyncOutputBuffer` when | |
| evaluation finishes. | |
| - `AsyncOutputBuffer::DestructiveFlatten` moves `Span` elements out of the root | |
| `AsyncOutputBuffer` and all descendant buffers into the returned | |
| `OutputBuffer`. | |
| ## Verification | |
| Run these commands to build and test `zxdb/format`: | |
| - Build: `fx build //src/developer/debug/zxdb/format` | |
| - Test: `fx test zxdb_tests` |