From b9c7b8a54e4a0b256ee91df83c8ed0199808d4a4 Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Sun, 16 Aug 2026 17:33:04 +0800 Subject: [PATCH] fix(cli): exempt the last column from the cell bound MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bounding every cell cost `doctor` the thing it exists to report: the config directory came out with its middle replaced by an ellipsis, and a path a reader cannot copy is not a diagnostic. Caught by running `tty7 doctor` against the isolated daemon right after making the change. Nothing is padded against the last column — `render_row` trims the end of every line — so its width cannot push anything out of line, and cutting it buys nothing. `ws ls` still bounds NAME, which has four columns to its right; `doctor` and the agent listing, whose findings sit last, print whole again. --- crates/tty7-cli/src/output.rs | 37 ++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/crates/tty7-cli/src/output.rs b/crates/tty7-cli/src/output.rs index 9438a79b..53abae34 100644 --- a/crates/tty7-cli/src/output.rs +++ b/crates/tty7-cli/src/output.rs @@ -129,12 +129,27 @@ pub(crate) fn printable(s: &str) -> std::borrow::Cow<'_, str> { /// /// A full classic terminal width, so every realistic name and path still /// prints whole. `--json` is the output for anything that must not be cut. +/// +/// The last column is exempt. Nothing is padded against it — `render_row` +/// trims the end of every line — so its width cannot push anything out of +/// line, and cutting it is pure loss: `doctor` puts its findings there, and a +/// config path with its middle replaced by an ellipsis is the one thing the +/// reader of a diagnostic needed whole. const MAX_CELL: usize = 80; pub fn table(header: &[&str], rows: &[Vec]) -> String { + let last = header.len().saturating_sub(1); let rows: Vec> = rows .iter() - .map(|row| row.iter().map(|c| clamp(&printable(c), MAX_CELL)).collect()) + .map(|row| { + row.iter() + .enumerate() + .map(|(i, c)| match i == last { + true => printable(c).into_owned(), + false => clamp(&printable(c), MAX_CELL), + }) + .collect() + }) .collect(); let rows = &rows; let mut widths: Vec = header.iter().map(|h| width(h)).collect(); @@ -497,6 +512,26 @@ mod tests { assert!(widths.iter().all(|w| *w <= widest)); } + #[test] + fn the_last_column_is_never_cut() { + // `doctor` reports its findings in the last column, and a config path + // with an ellipsis through the middle is the one thing the reader of a + // diagnostic needed whole. Nothing is padded against the last column, + // so its width costs no alignment. + let long = format!("/private/var/folders/{}/config", "x".repeat(200)); + let out = table( + &["CHECK", "RESULT"], + &[ + vec!["config".into(), long.clone()], + vec!["server".into(), "ok".into()], + ], + ); + assert!(out.contains(&long), "the last column was cut"); + // The row that says `ok` is not dragged out to meet it. + let ok_row = out.lines().find(|l| l.contains("server")).unwrap(); + assert!(width(ok_row) < 40, "a short row was padded to the long one"); + } + #[test] fn wide_characters_are_padded_by_display_width_not_byte_length() { // "项目" is 6 bytes but occupies 4 columns. Padding by len() would add