Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
Presentation Formatting

Uni20 common presentation formatting lives in uni20::presentation:

The layer is intentionally independent of trace, async, tensor, AD, and scheduler semantics. It renders styled text spans and semantic glyph tokens into terminal, plain, or strict ASCII output. Trace uses this layer for diagnostic line assembly, styled/color output, charset fallback, and display-cell container alignment, but presentation does not depend on trace.

When UNI20_HAS_STACKTRACE is enabled, presentation_stacktrace.hpp provides format_stacktrace(...). It converts a std::stacktrace into policy-aware styled_text with semantic tree connectors. Terminal, plain, strict-ASCII, and future adapters therefore render the same document model. If output_policy::wrap_width is set, long frames wrap by display width with continuation lines aligned beneath the frame description. stacktrace_format_options controls indentation and span styles without choosing the final glyph or color policy.

Output Policy

output_policy controls rendering:

Field Purpose
color never, automatic, or always ANSI style emission.
glyphs emoji by default; select unicode or ascii for semantic glyph rendering when needed.
charset Preserve UTF-8, escape non-ASCII text, or replace it.
width Measure by bytes or display cells.
ambiguous Treat ambiguous-width code points as narrow or wide.
invalid Escape or replace invalid UTF-8 bytes.
tab_width Display-cell tab expansion.
wrap_width Optional wrapping width.
ornaments Disable, minimize, or enrich decorative glyphs.

terminal_policy() reads the global presentation environment:

Variable Default Values Effect
UNI20_GLYPHS emoji emoji, unicode, ascii Select semantic glyph spelling.
UNI20_CHARSET utf8 utf8, escape, replace Preserve UTF-8, escape non-ASCII text, or replace it. utf-8, ascii_escape, and ascii_replace aliases are accepted.
UNI20_COLOR auto auto, yes, always, no, never, plus boolean aliases Control ANSI style emission globally.

Automatic color follows terminal detection and honors NO_COLOR. Explicit color_mode::always, including UNI20_COLOR=yes or UNI20_COLOR=always, still forces color. Terminal and display sinks determine width from their actual FILE* destination. Width selection uses a positive COLUMNS value first, then terminal-size detection on that destination, then the configured UNI20_FALLBACK_TERMINAL_WIDTH value (132 by default). Consequently, output redirected to a regular file uses the configured fallback rather than inheriting an unrelated stdout terminal width. Configure a different non-terminal default with -DUNI20_FALLBACK_TERMINAL_WIDTH=N.

Callers may deliberately override fields on a policy for a forced demonstration or a known output destination. For ordinary terminal output, prefer terminal_policy(...) as the starting point so user environment choices win over program defaults.

Semantic Glyphs

Call sites should use semantic_glyph tokens instead of hard-coding Unicode and ASCII spellings:

text.append(presentation::semantic_glyph::success)
.append(" operation ")
.append(presentation::semantic_glyph::arrow_right)
.append(" complete");
auto policy = presentation::terminal_policy(stderr);
auto rendered = presentation::render(text, policy);
A sequence of styled text spans and semantic glyph tokens.
Definition presentation.hpp:216
styled_text & append(std::string_view text, terminal::TerminalStyle style={})
Append a text span.
Definition presentation.cpp:1164
presentation::styled_text text
Definition display.cpp:141
Definition data_table.cpp:8
output_policy terminal_policy(std::FILE *stream)
Build a policy for terminal rendering.
Definition presentation.cpp:1186
std::string render(styled_text const &text, output_policy const &policy)
Render styled text with color/style sequences only when policy allows.
Definition presentation.cpp:1289

The default terminal and plain policies prefer emoji for semantic status glyphs. Use glyph_set::unicode for symbol-only output, or glyph_set::ascii when fixed-width terminal behavior matters more than rich status symbols. Central mappings cover status symbols, arrows, ellipsis, square and rounded box/table drawing, diagonal connector glyphs, and tree drawing. ASCII output uses these mappings automatically.

Diagnostic and display code should choose semantic severity or disposition, not literal symbols:

Semantic glyph Intended use Emoji policy ASCII policy
success Completed, passed, or converged status. check mark button [OK]
warning Non-fatal warning or advisory diagnostic. warning sign [WARN]
info Neutral information or progress status. information sign [INFO]
failure Recoverable error or exception-boundary diagnostic. cross mark [FAIL]
fatal Abort-path diagnostics such as PANIC, CHECK, and PRECONDITION. siren [FATAL]
partial Partly completed report status. yellow circle [PARTIAL]
deferred Intentionally postponed report status. pause button [DEFER]
skipped Not applicable, out of scope, or unavailable report status. prohibited sign [SKIP]

Trace diagnostics use the severity split so warnings, recoverable errors, and aborting assertions remain distinct while still rendering through the active glyph and charset policy. Human-facing reports and display output can also use the disposition glyphs when communicating progress or review state rather than diagnostic severity.

Styled Text

Use presentation::style(...) to define a reusable callable style. The helper builds styled_text, not ANSI strings, so final rendering still respects UNI20_COLOR, NO_COLOR, UNI20_CHARSET, and UNI20_GLYPHS:

auto RedBold = presentation::style("Red;Bold");
auto message = RedBold("residual {:.3e}", residual);
auto value = RedBold(residual);
auto warning = RedBold(presentation::semantic_glyph::warning, "stagnated at {:.3e}", residual);
locale_t value
Definition data_table.cpp:49
text_style style(terminal::TerminalStyle style)
Create a callable style wrapper from terminal style metadata.
Definition presentation.hpp:369

terminal::TerminalStyle remains the low-level style carrier. It can be constructed from style strings such as "Red;Bold" or "fg:#ff0000;Bold", but ordinary presentation and display code should prefer presentation::style(...) so color is not rendered too early.

Styled text can be inserted into table cells without pre-rendering it:

auto Warn = presentation::style("Yellow;Bold");
report.table("Solver")
.column("quantity", presentation::table_alignment::left)
.column("value", presentation::table_alignment::left)
.row("residual", Warn(presentation::semantic_glyph::warning, "{:.3e}", residual));

Text Fallback

Strict ASCII modes perform a small deterministic fallback pass for common non-language symbols such as smart quotes, dashes, ellipsis, arrows, check/cross/warning symbols, simple math signs, and box drawing. Human-language text is not transliterated. For example, Chinese text is preserved under text_charset::utf8, escaped under text_charset::ascii_escape, and replaced under text_charset::ascii_replace.

Width And Layout

Layout helpers operate after glyph and charset fallback:

auto cells = presentation::display_width(text, policy);
auto padded = presentation::pad_right("label", 12, policy);
auto clipped = presentation::clip_to_width("value", 8, policy);
auto lines = presentation::wrap_text("long value", 10, policy);
auto indented = presentation::indent_text("line 1\nline 2", 4, policy);
std::string pad_right(std::string_view text, std::size_t target_width, output_policy const &policy)
Right-pad rendered text to a target width.
Definition presentation.cpp:2588
std::string indent_text(std::string_view text, std::size_t spaces, output_policy const &policy, bool indent_first)
Indent each rendered line by a fixed number of spaces.
Definition presentation.cpp:2690
std::string clip_to_width(std::string_view text, std::size_t max_width, output_policy const &policy, std::size_t initial_column)
Clip rendered text to a target width without appending a marker.
Definition presentation.cpp:2629
std::vector< std::string > wrap_text(std::string_view text, std::size_t max_width, output_policy const &policy)
Wrap rendered text by display cells or bytes.
Definition presentation.cpp:2831
std::size_t display_width(std::string_view text, output_policy const &policy, std::size_t initial_column)
Measure rendered text width under the selected policy.
Definition presentation.cpp:2560
std::vector< styled_text > lines
Definition presentation.cpp:2015

Display-cell width is best effort and deterministic. It treats ASCII printable characters as width 1, style spans as width 0, combining marks as width 0, common CJK wide characters as width 2, emoji conservatively as width 2, and tabs relative to the current column.

prefix_lines(...) and indent_text(...) are deliberately simple block-layout helpers. They are intended for diagnostics, nested trace sections, and text-art blocks where callers want fixed indentation after wrapping or clipping has already chosen visible content.

Use truncate_to_width(...) when preserving the beginning of a long value, and truncate_left_to_width(...) when preserving the end. The latter is useful for diagnostics that need to show the tail of a long prefix before a highlighted token:

auto prefix = presentation::truncate_left_to_width(before_error, 24, policy, "…");
auto suffix = presentation::truncate_to_width(error_and_after, 40, policy, "…");
std::string truncate_to_width(std::string_view text, std::size_t max_width, output_policy const &policy, std::string_view marker, std::size_t initial_column)
Truncate rendered text to a target width and optionally append a marker.
Definition presentation.cpp:2635
std::string truncate_left_to_width(std::string_view text, std::size_t max_width, output_policy const &policy, std::string_view marker, std::size_t initial_column)
Truncate rendered text from the left to a target width and optionally prepend a marker.
Definition presentation.cpp:2649

See examples/presentation/ for runnable demonstrations of semantic Unicode/emoji output, display-cell table alignment, fixed indentation after wrapping, tensor-network-style connector art, the default rounded tensor-box style, and parser-style range diagnostics that adapt to different terminal widths.

Report Tables

The current report model stores formatted text, including styled cells and spanning layout. The Typed Data Tables API retains original numerical values and supplies to_report_table snapshots plus CSV/TSV/JSON export and typed streaming sinks. Use it when the same calculation results need display formatting and data files.

report_builder and report_table provide a small higher-level API for command-line examples and diagnostics:

presentation::report_builder report("Krylov solve");
report.status(presentation::semantic_glyph::success, "converged").field("dimension", 128).field("tolerance", "1.0e-12");
report.table("Solver Summary")
.grid()
.column("solver", presentation::table_alignment::left)
.column("matvecs")
.column("residual")
.row("native", 185, "1.0e-15")
.row("arpack", 238, "8.0e-13");
Builder for simple terminal reports with headings, status lines, fields, and tables.
Definition presentation.hpp:602
output_policy plain_policy()
Build a policy for plain file/log rendering with no ANSI color.
Definition presentation.cpp:1195
std::string render_plain(styled_text const &text, output_policy policy)
Render styled text without ANSI escape sequences.
Definition presentation.cpp:1325

Tables are compact by default, preserving the existing two-space column layout. Use outer_border(), column_separators(), row_separators(), and header_separator() for individual rules, or grid() for a full outer border with column, header, and row rules. Borders use semantic box glyphs, so Unicode, emoji, and strict ASCII policies all render through the same fallback path.

Rows can contain table_cell values when a cell should span multiple columns:

report.table("Grouped Summary")
.grid(presentation::table_rule_style::double_line)
.column("stage", presentation::table_alignment::left)
.column("note", presentation::table_alignment::left)
.column("error", presentation::table_alignment::decimal)
.row("setup", "read inputs", 4)
.separator()
.row({{"solve", 1}, {"Krylov iterations and restart details", 2, presentation::table_alignment::left}});

presentation::cell(...) builds the same table_cell explicitly and can carry styled content, spans, and per-cell alignment:

auto Note = presentation::style("Cyan;Bold");
report.table("Notes")
.grid()
.column("label", presentation::table_alignment::left)
.column("detail", presentation::table_alignment::left)
.row({presentation::cell("solver"),
presentation::cell(Note("styled note"), 1, presentation::table_alignment::left)});
table_cell cell(styled_text content, std::size_t span=1, std::optional< table_alignment > alignment=std::nullopt)
Build a table cell from styled presentation text.
Definition presentation.hpp:428

separator() inserts an explicit body rule without enabling global row separators; top_separator() inserts an explicit rule before the generated heading row. Automatic borders and separators use table_border_options::rule_style, set directly with border_style(...) or by calling grid(presentation::table_rule_style::double_line).

table_alignment::decimal aligns finite values on .; values without a decimal point align as if the point followed the rendered value. Non-finite spellings such as nan, inf, and -inf are centered in a decimal-aligned cell. A table_cell can also override alignment for one cell, including non-spanning cells.

When output_policy::wrap_width is set, report tables use it as a table-width budget. The renderer first keeps natural column widths if the table fits, then prefers shrinking columns that can still wrap at whitespace before forcing a hard split inside an unbreakable token. Report-specific renderers disable final whole-string wrapping after the table has been formatted, because wrapping a completed table would split borders and column alignment.

Report header fields also use the wrap width: values wrap at whitespace, with continuation lines aligned below the value. Indivisible tokens such as numbers, executable names and URLs may exceed the available width. A long token does not widen the surrounding prose. With no wrap width (or a width of zero), field values retain their original layout.

Use table.preserve_tokens() when a cell contains numbers, command-line arguments, identifiers or links which must stay intact. This makes whitespace the only wrapping boundary and allows the table to exceed wrap_width if necessary. Minimum widths account for headings and spanning cells as well as ordinary cells, including their rendered styles and character-set policy. Ordinary tables retain hard wrapping by default; table.preserve_tokens(false) restores it. Application help and build-information tables enable token preservation automatically.

render_report(report, policy) exposes the styled intermediate representation used by the report renderers. It preserves semantic glyphs and span styles, but the report layout may already have consumed policy.wrap_width. Prefer render_plain(report, policy) or render_terminal(report, policy) for final output. If the intermediate styled document is rendered directly, render it without a smaller final wrap_width; a wider output area is harmless, but generic whole-string wrapping can break a completed table.

Python And Notebook Display

The presentation layer is intended to be the shared formatting backend for future Python bindings and Jupyter display, but Python should not expose the C++ terminal model directly. Treat the current renderers as separate adapters over the same semantic presentation data:

  • terminal text: ANSI color, semantic glyphs, and display-cell width handling;
  • plain text: stable output for repr(...), logs, tests, and redirected streams;
  • strict ASCII: deterministic fallback for non-Unicode environments;
  • future HTML/Jupyter: rich display from the same semantic spans, without ANSI escapes or terminal-width assumptions.

Python bindings should keep repr(obj) conservative: plain, stable, and safe to call on large objects. Rich notebook output should use _repr_html_() or _repr_mimebundle_() and may use color, CSS, and richer layout. User-facing helpers such as obj.pretty(...) can expose explicit per-call controls for glyphs, charset, color, width, selected axes, and preview limits.

Do not let notebook display depend on terminal-only facts. A Python policy should derive automatic color and stream behavior from Python streams such as sys.stdout.isatty() or notebook display detection, not only from C FILE* handles. Environment variables such as UNI20_GLYPHS, UNI20_CHARSET, UNI20_COLOR, NO_COLOR, and COLUMNS are useful defaults, but Python APIs should allow explicit overrides.

Mdspan And Tensor Art

Mdspan-like objects can be rendered through the presentation layer:

auto text =
uni20::presentation::format_mdspan(matrix, policy, [](auto const& value) { return fmt::format("{}", value); });
std::string format_mdspan(MDS const &mds, output_policy const &policy, ElementFormatter &&formatter, mdspan_format_options const &options={})
Render every mdspan/tensor element as display-cell-aligned text art.
Definition presentation_mdspan.hpp:585

Rank-1 values render as a row vector, rank-2 values render as aligned matrix art, and higher-rank values render as labeled rank-2 slices over every leading-axis coordinate. format_mdspan(...) is intentionally exhaustive: printing an actual tensor emits every element.

Use format_mdspan_preview(...) for bounded diagnostic output:

preview.full_element_limit = 256;
preview.edge_items = 3;
preview.max_slices = 4;
auto result = uni20::presentation::format_mdspan_preview(tensor, policy, preview);
mdspan_preview_result format_mdspan_preview(MDS const &mds, output_policy const &policy, ElementFormatter &&formatter, mdspan_preview_options const &options={})
Render a bounded mdspan/tensor preview without visiting omitted elements.
Definition presentation_mdspan.hpp:632
Bounded preview controls for mdspan and tensor display.
Definition presentation_mdspan.hpp:51
std::size_t max_slices
Definition presentation_mdspan.hpp:55
std::size_t full_element_limit
Definition presentation_mdspan.hpp:53
std::size_t edge_items
Definition presentation_mdspan.hpp:54

The preview renderer first uses exhaustive output when the element count is small enough and the result fits output_policy::wrap_width. Otherwise it displays edge rows, columns, and slices with the active semantic ellipsis glyph and records mdspan_preview_result::elided = true. Metadata lines are also width-aware; if the terminal is too narrow for even a one-cell preview, the renderer falls back to a shape/element-count summary plus an elision note.

Trace uses bounded preview by default for mdspan-like values and tensor/view-like objects, while still applying trace scalar formatting such as floating-point precision. Python and Jupyter tensor display should also be preview-first rather than exhaustive by default, with explicit controls for selected axes, preview limits, and full=true opt-in behavior.

Real and complex tensor elements use numeric_format_options when no custom element formatter is supplied. The defaults use general notation with 6 significant digits for float, 15 significant digits for double, normalized negative zero, and algebraic complex form such as 1.25-3.5i. Non-finite real values render deterministically as nan, inf, and -inf; complex values use the same component spelling, for example -inf+nani. When color is enabled, mdspan/tensor rendering highlights non-finite real or complex elements with the default red-bold style while preserving display-cell alignment. mdspan_format_options::numeric can switch to fixed or scientific notation and adjust the digit counts.

For rank-2-or-higher tensors, mdspan_format_options::matrix_axes can choose which axes form the displayed row and column dimensions:

auto text = uni20::presentation::format_mdspan(tensor, policy, options);
Formatting controls for mdspan and tensor display art.
Definition presentation_mdspan.hpp:42
std::optional< mdspan_matrix_axes > matrix_axes
Definition presentation_mdspan.hpp:45
Matrix axes used when rendering rank-2-or-higher tensors.
Definition presentation_mdspan.hpp:31

Any remaining axes become exhaustive slice labels. With shape (2, 3, 2) and matrix axes {0, 2}, labels are slice[:, 0, :], slice[:, 1, :], and slice[:, 2, :].