refactor(search): polish search results rendering with semantic Theme styles
Phase 5 of the UX overhaul. Migrates search result display from raw console styling to the centralized Theme system with semantic methods, improving visual consistency and readability. Search result changes: - Type badges now use semantic styles (issue_ref, mr_ref) with fixed-width alignment for clean columnar layout - Snippet rendering uses Theme::highlight() for matched terms and Theme::muted() for surrounding context, replacing bold+underline - Metadata line uses Theme::username() for authors and per-part styling with middle-dot separators instead of a single dim line - Result numbering uses muted style with right-aligned width - Consistent 8-space indent for metadata, snippets, and explain lines - Header line uses muted style for search mode instead of dim+parens - Trailing blank line moved after the result loop instead of per-result Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
committed by
teernisse
parent
d710403567
commit
96b288ccdd
@@ -309,20 +309,20 @@ fn parse_json_array(json: &str) -> Vec<String> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render FTS snippet with `<mark>` tags as terminal bold+underline.
|
/// Render FTS snippet with `<mark>` tags as terminal highlight style.
|
||||||
fn render_snippet(snippet: &str) -> String {
|
fn render_snippet(snippet: &str) -> String {
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
let mut remaining = snippet;
|
let mut remaining = snippet;
|
||||||
while let Some(start) = remaining.find("<mark>") {
|
while let Some(start) = remaining.find("<mark>") {
|
||||||
result.push_str(&remaining[..start]);
|
result.push_str(&Theme::muted().render(&remaining[..start]));
|
||||||
remaining = &remaining[start + 6..];
|
remaining = &remaining[start + 6..];
|
||||||
if let Some(end) = remaining.find("</mark>") {
|
if let Some(end) = remaining.find("</mark>") {
|
||||||
let highlighted = &remaining[..end];
|
let highlighted = &remaining[..end];
|
||||||
result.push_str(&Theme::bold().underline().render(highlighted));
|
result.push_str(&Theme::highlight().render(highlighted));
|
||||||
remaining = &remaining[end + 7..];
|
remaining = &remaining[end + 7..];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.push_str(remaining);
|
result.push_str(&Theme::muted().render(remaining));
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,35 +342,37 @@ pub fn print_search_results(response: &SearchResponse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"\n {} results for '{}' {}",
|
"\n {} results for '{}' {}",
|
||||||
Theme::bold().render(&response.total_results.to_string()),
|
Theme::bold().render(&response.total_results.to_string()),
|
||||||
Theme::bold().render(&response.query),
|
Theme::bold().render(&response.query),
|
||||||
Theme::dim().render(&format!("({})", response.mode))
|
Theme::muted().render(&response.mode)
|
||||||
);
|
);
|
||||||
println!();
|
|
||||||
|
|
||||||
for (i, result) in response.results.iter().enumerate() {
|
for (i, result) in response.results.iter().enumerate() {
|
||||||
|
println!();
|
||||||
|
|
||||||
let type_badge = match result.source_type.as_str() {
|
let type_badge = match result.source_type.as_str() {
|
||||||
"issue" => Theme::info().render("issue"),
|
"issue" => Theme::issue_ref().render("issue"),
|
||||||
"merge_request" => Theme::accent().render("mr"),
|
"merge_request" => Theme::mr_ref().render(" mr "),
|
||||||
"discussion" => Theme::info().render("disc"),
|
"discussion" => Theme::info().render(" disc"),
|
||||||
"note" => Theme::info().render("note"),
|
"note" => Theme::muted().render(" note"),
|
||||||
_ => Theme::dim().render(&result.source_type),
|
_ => Theme::muted().render(&format!("{:>5}", &result.source_type)),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Title line: rank, type badge, title
|
// Title line: rank, type badge, title
|
||||||
println!(
|
println!(
|
||||||
" {} {} {}",
|
" {:>3}. {} {}",
|
||||||
Theme::dim().render(&format!("{:>2}.", i + 1)),
|
Theme::muted().render(&(i + 1).to_string()),
|
||||||
type_badge,
|
type_badge,
|
||||||
Theme::bold().render(&result.title)
|
Theme::bold().render(&result.title)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Metadata: project, author, labels — compact middle-dot line
|
// Metadata: project, author, labels — compact middle-dot line
|
||||||
|
let sep = Theme::muted().render(" \u{b7} ");
|
||||||
let mut meta_parts: Vec<String> = Vec::new();
|
let mut meta_parts: Vec<String> = Vec::new();
|
||||||
meta_parts.push(result.project_path.clone());
|
meta_parts.push(Theme::muted().render(&result.project_path));
|
||||||
if let Some(ref author) = result.author {
|
if let Some(ref author) = result.author {
|
||||||
meta_parts.push(format!("@{author}"));
|
meta_parts.push(Theme::username().render(&format!("@{author}")));
|
||||||
}
|
}
|
||||||
if !result.labels.is_empty() {
|
if !result.labels.is_empty() {
|
||||||
let label_str = if result.labels.len() <= 3 {
|
let label_str = if result.labels.len() <= 3 {
|
||||||
@@ -382,20 +384,17 @@ pub fn print_search_results(response: &SearchResponse) {
|
|||||||
result.labels.len() - 2
|
result.labels.len() - 2
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
meta_parts.push(label_str);
|
meta_parts.push(Theme::muted().render(&label_str));
|
||||||
}
|
}
|
||||||
println!(
|
println!(" {}", meta_parts.join(&sep));
|
||||||
" {}",
|
|
||||||
Theme::dim().render(&meta_parts.join(" \u{b7} "))
|
|
||||||
);
|
|
||||||
|
|
||||||
// Snippet with proper highlighting
|
// Snippet with highlight styling
|
||||||
let rendered = render_snippet(&result.snippet);
|
let rendered = render_snippet(&result.snippet);
|
||||||
println!(" {}", Theme::dim().render(&rendered));
|
println!(" {rendered}");
|
||||||
|
|
||||||
if let Some(ref explain) = result.explain {
|
if let Some(ref explain) = result.explain {
|
||||||
println!(
|
println!(
|
||||||
" {} vec={} fts={} rrf={:.4}",
|
" {} vec={} fts={} rrf={:.4}",
|
||||||
Theme::accent().render("explain"),
|
Theme::accent().render("explain"),
|
||||||
explain
|
explain
|
||||||
.vector_rank
|
.vector_rank
|
||||||
@@ -408,9 +407,9 @@ pub fn print_search_results(response: &SearchResponse) {
|
|||||||
explain.rrf_score
|
explain.rrf_score
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
|||||||
Reference in New Issue
Block a user