mirror of
https://github.com/openai/codex.git
synced 2026-10-25 18:28:38 +00:00
docs(tui): clarify markdown table streaming contracts
This commit is contained in:
committed by
Felipe Coury
parent
38664bc2fb
commit
d9035a61b0
@@ -1,5 +1,8 @@
|
||||
//! Markdown rendering for the TUI transcript.
|
||||
//! Low-level markdown event renderer for the TUI transcript.
|
||||
//!
|
||||
//! This module consumes `pulldown-cmark` events and emits styled `ratatui`
|
||||
//! lines, including table layout, width-aware wrapping, and local file-link
|
||||
//! display.
|
||||
//! This renderer intentionally treats local file links differently from normal web links. For
|
||||
//! local paths, the displayed text comes from the destination, not the markdown label, so
|
||||
//! transcripts show the real file target (including normalized location suffixes) and can shorten
|
||||
@@ -198,11 +201,23 @@ struct TableColumnMetrics {
|
||||
kind: TableColumnKind,
|
||||
}
|
||||
|
||||
/// Render markdown with default wrapping behavior.
|
||||
///
|
||||
/// Use this when the caller does not have a concrete render width yet (for
|
||||
/// example, snapshot tests or contexts that intentionally defer wrapping). If
|
||||
/// a viewport width is known, prefer [`render_markdown_text_with_width`] so
|
||||
/// table fallback and line wrapping decisions match the visible terminal.
|
||||
pub fn render_markdown_text(input: &str) -> Text<'static> {
|
||||
render_markdown_text_with_width(input, /*width*/ None)
|
||||
}
|
||||
|
||||
/// Render markdown using the current process working directory for local file-link display.
|
||||
/// Render markdown constrained to a known terminal width.
|
||||
///
|
||||
/// The renderer preserves table structure when possible and falls back to
|
||||
/// pipe-table output when a box table cannot fit the available width. Passing
|
||||
/// `None` keeps intrinsic line widths and disables width-driven wrapping in the
|
||||
/// markdown writer. Local file links render relative to the current process
|
||||
/// working directory.
|
||||
pub(crate) fn render_markdown_text_with_width(input: &str, width: Option<usize>) -> Text<'static> {
|
||||
let cwd = std::env::current_dir().ok();
|
||||
render_markdown_text_with_width_and_cwd(input, width, cwd.as_deref())
|
||||
@@ -1240,6 +1255,8 @@ where
|
||||
out.push('|');
|
||||
for cell in row {
|
||||
out.push(' ');
|
||||
// Preserve literal `|` inside cell text in markdown fallback mode so
|
||||
// downstream markdown parsers keep the cell content intact.
|
||||
out.push_str(&cell.plain_text().replace('|', "\\|"));
|
||||
out.push(' ');
|
||||
out.push('|');
|
||||
@@ -1374,6 +1391,9 @@ where
|
||||
fn pop_link(&mut self) {
|
||||
if let Some(link) = self.link.take() {
|
||||
if link.show_destination {
|
||||
// Link destinations are rendered as " (url)" suffixes. When parsing table cells,
|
||||
// append the suffix into the active cell buffer rather than the outer paragraph
|
||||
// line to avoid detached url lines.
|
||||
if self.in_table_cell() {
|
||||
self.push_span_to_table_cell(" (".into());
|
||||
self.push_span_to_table_cell(Span::styled(link.destination, self.styles.link));
|
||||
|
||||
@@ -35,12 +35,11 @@ pub(crate) struct MarkdownStreamCollector {
|
||||
}
|
||||
|
||||
impl MarkdownStreamCollector {
|
||||
/// Create a collector that renders markdown using `cwd` for local file-link display.
|
||||
/// Create a collector that accumulates raw markdown deltas.
|
||||
///
|
||||
/// The collector snapshots `cwd` into owned state because stream commits can happen long after
|
||||
/// construction. The same `cwd` should be reused for the entire stream lifecycle; mixing
|
||||
/// different working directories within one stream would make the same link render with
|
||||
/// different path prefixes across incremental commits.
|
||||
/// `width` and `cwd` are only used by test-only rendering helpers; production stream commits
|
||||
/// operate on raw source boundaries. The collector snapshots `cwd` so test rendering keeps
|
||||
/// local file-link display stable across incremental commits.
|
||||
pub fn new(width: Option<usize>, cwd: &Path) -> Self {
|
||||
#[cfg(not(test))]
|
||||
let _ = cwd;
|
||||
@@ -56,10 +55,12 @@ impl MarkdownStreamCollector {
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the rendering width used by test-only line-commit helpers.
|
||||
pub fn set_width(&mut self, width: Option<usize>) {
|
||||
self.width = width;
|
||||
}
|
||||
|
||||
/// Reset all buffered source and commit bookkeeping.
|
||||
pub fn clear(&mut self) {
|
||||
self.buffer.clear();
|
||||
self.committed_source_len = 0;
|
||||
@@ -69,6 +70,7 @@ impl MarkdownStreamCollector {
|
||||
}
|
||||
}
|
||||
|
||||
/// Append a raw streaming delta to the internal source buffer.
|
||||
pub fn push_delta(&mut self, delta: &str) {
|
||||
tracing::trace!("push_delta: {delta:?}");
|
||||
self.buffer.push_str(delta);
|
||||
|
||||
@@ -532,6 +532,7 @@ fn source_bytes_for_rendered_count(
|
||||
// Table holdback infrastructure
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Return fence marker character and run length for a potential fence line.
|
||||
fn parse_fence_marker(line: &str) -> Option<(char, usize)> {
|
||||
let mut chars = line.chars();
|
||||
let first = chars.next()?;
|
||||
@@ -582,6 +583,11 @@ fn is_markdown_fence(trimmed_line: &str, marker_len: usize) -> bool {
|
||||
info.eq_ignore_ascii_case("md") || info.eq_ignore_ascii_case("markdown")
|
||||
}
|
||||
|
||||
/// Parse source into lines tagged with fenced-code context for table scanning.
|
||||
///
|
||||
/// Fence close markers must use the same marker character and at least the
|
||||
/// opening marker length, with no trailing content. This avoids accidentally
|
||||
/// closing on shorter markers that appear inside longer fences.
|
||||
fn parse_lines_with_fence_state(source: &str) -> Vec<ParsedLine<'_>> {
|
||||
let mut in_fence = false;
|
||||
let mut fence_char = '\0';
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
//! Shared width guards for transcript rendering.
|
||||
//!
|
||||
//! Callers use these helpers when they reserve fixed prefix columns (for
|
||||
//! bullets, gutters, or labels) and need to know whether any content width
|
||||
//! remains.
|
||||
|
||||
/// Returns usable content width after reserving fixed columns.
|
||||
///
|
||||
/// Guarantees a strict positive width (`Some(n)` where `n > 0`) or `None` when
|
||||
/// the reserved columns consume the full width.
|
||||
///
|
||||
/// Treat `None` as "render prefix-only fallback". Coercing it to `0` and still
|
||||
/// attempting wrapped rendering often produces empty or unstable output at very
|
||||
/// narrow terminal widths.
|
||||
pub(crate) fn usable_content_width(total_width: usize, reserved_cols: usize) -> Option<usize> {
|
||||
total_width
|
||||
.checked_sub(reserved_cols)
|
||||
@@ -9,6 +19,9 @@ pub(crate) fn usable_content_width(total_width: usize, reserved_cols: usize) ->
|
||||
}
|
||||
|
||||
/// `u16` convenience wrapper around [`usable_content_width`].
|
||||
///
|
||||
/// This keeps width math at callsites that receive terminal dimensions as
|
||||
/// `u16` while preserving the same `None` contract for exhausted width.
|
||||
pub(crate) fn usable_content_width_u16(total_width: u16, reserved_cols: u16) -> Option<usize> {
|
||||
usable_content_width(usize::from(total_width), usize::from(reserved_cols))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user