feat(search): overhaul search output formatting (GIT-5)

Phase 1: Add source_entity_iid to search results via CASE subquery on
hydrate_results() for all 4 source types (issue, MR, discussion, note).
Phase 2: Fix visual alignment - compute indent from prefix visible width.
Phase 3: Show compact relative time on title line.
Phase 4: Add drill-down hint footer (lore issues <iid>).
Phase 5: Move labels to --explain mode, limit snippets to 2 terminal lines.
Phase 6: Use section_divider() for results header.

Also: promote strip_ansi/visible_width to public render utils, update
robot mode --fields minimal search preset with source_entity_iid.
This commit is contained in:
teernisse
2026-03-11 10:37:38 -04:00
parent 60075cd400
commit 44431667e8
4 changed files with 170 additions and 60 deletions

View File

@@ -569,6 +569,32 @@ pub fn terminal_width() -> usize {
80
}
/// Strip ANSI escape codes (SGR sequences) from a string.
pub fn strip_ansi(s: &str) -> String {
let mut out = String::with_capacity(s.len());
let mut chars = s.chars();
while let Some(c) = chars.next() {
if c == '\x1b' {
// Consume `[`, then digits/semicolons, then the final letter
if chars.next() == Some('[') {
for c in chars.by_ref() {
if c.is_ascii_alphabetic() {
break;
}
}
}
} else {
out.push(c);
}
}
out
}
/// Compute the visible width of a string that may contain ANSI escape sequences.
pub fn visible_width(s: &str) -> usize {
strip_ansi(s).chars().count()
}
/// Truncate a string to `max` characters, appending "..." if truncated.
pub fn truncate(s: &str, max: usize) -> String {
if max < 4 {
@@ -1459,24 +1485,19 @@ mod tests {
// ── helpers ──
/// Strip ANSI escape codes (SGR sequences) for content assertions.
/// Delegate to the public `strip_ansi` for test assertions.
fn strip_ansi(s: &str) -> String {
let mut out = String::with_capacity(s.len());
let mut chars = s.chars();
while let Some(c) = chars.next() {
if c == '\x1b' {
// Consume `[`, then digits/semicolons, then the final letter
if chars.next() == Some('[') {
for c in chars.by_ref() {
if c.is_ascii_alphabetic() {
break;
}
}
}
} else {
out.push(c);
}
}
out
super::strip_ansi(s)
}
#[test]
fn visible_width_strips_ansi() {
let styled = "\x1b[1mhello\x1b[0m".to_string();
assert_eq!(super::visible_width(&styled), 5);
}
#[test]
fn visible_width_plain_string() {
assert_eq!(super::visible_width("hello"), 5);
}
}