feat: Improve info table rendering by truncating wide columns
Co-authored-by: aider (openai/andrew/openrouter/deepseek/deepseek-chat-v3.1) <aider@aider.chat>
This commit is contained in:
@@ -221,3 +221,57 @@ pub fn get_terminal_width() -> usize {
|
|||||||
// Default to 80 if all else fails
|
// Default to 80 if all else fails
|
||||||
80
|
80
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculate the maximum width for the last column in a table
|
||||||
|
/// Takes into account the widths of other columns and table borders
|
||||||
|
pub fn calculate_last_column_width(column_widths: &[usize], terminal_width: usize) -> usize {
|
||||||
|
if terminal_width == 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each column has 1 character padding on each side, and there are borders between columns
|
||||||
|
// The table format uses '│' characters between columns, which take 1 character each
|
||||||
|
let total_other_columns_width: usize = column_widths.iter().map(|&w| w + 2).sum();
|
||||||
|
let border_characters = column_widths.len().saturating_sub(1);
|
||||||
|
|
||||||
|
// Total width used by other columns and their formatting
|
||||||
|
let used_width = total_other_columns_width + border_characters;
|
||||||
|
|
||||||
|
if used_width >= terminal_width {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The last column also has 2 characters for padding
|
||||||
|
terminal_width - used_width - 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Truncate a string to fit within a maximum width, adding an ellipsis if truncated
|
||||||
|
pub fn truncate_with_ellipsis(s: &str, max_width: usize) -> String {
|
||||||
|
if max_width == 0 {
|
||||||
|
return String::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.chars().count() <= max_width {
|
||||||
|
return s.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to truncate and add an ellipsis
|
||||||
|
// The ellipsis takes 1 character, so we can show max_width-1 characters
|
||||||
|
if max_width == 1 {
|
||||||
|
return "…".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = String::new();
|
||||||
|
let mut current_width = 0;
|
||||||
|
|
||||||
|
for c in s.chars() {
|
||||||
|
if current_width + 1 > max_width - 1 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result.push(c);
|
||||||
|
current_width += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push('…');
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|||||||
@@ -75,66 +75,104 @@ fn show_item(
|
|||||||
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
|
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
table.add_row(Row::new(vec![
|
// Collect all rows to calculate column widths
|
||||||
|
let mut rows = Vec::new();
|
||||||
|
|
||||||
|
// Add all the rows
|
||||||
|
rows.push(Row::new(vec![
|
||||||
Cell::new("ID").with_style(Attr::Bold),
|
Cell::new("ID").with_style(Attr::Bold),
|
||||||
Cell::new(&item_id.to_string()),
|
Cell::new(&item_id.to_string()),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
let ts_cell = Cell::new(&item.ts.with_timezone(&Local).format("%F %T %Z").to_string());
|
let timestamp_str = item.ts.with_timezone(&Local).format("%F %T %Z").to_string();
|
||||||
table.add_row(Row::new(vec![
|
rows.push(Row::new(vec![
|
||||||
Cell::new("Timestamp").with_style(Attr::Bold),
|
Cell::new("Timestamp").with_style(Attr::Bold),
|
||||||
ts_cell,
|
Cell::new(×tamp_str),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
let mut item_path_buf = data_path.clone(); // Renamed to avoid conflict if item_path is used later
|
let mut item_path_buf = data_path.clone();
|
||||||
item_path_buf.push(item.id.unwrap().to_string()); // Again, consider safer unwrap
|
item_path_buf.push(item.id.unwrap().to_string());
|
||||||
|
let path_str = item_path_buf.to_str().expect("Unable to get item path").to_string();
|
||||||
table.add_row(Row::new(vec![
|
rows.push(Row::new(vec![
|
||||||
Cell::new("Path").with_style(Attr::Bold),
|
Cell::new("Path").with_style(Attr::Bold),
|
||||||
Cell::new(item_path_buf.to_str().expect("Unable to get item path")),
|
Cell::new(&path_str),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
let size_cell = match item.size {
|
let size_str = match item.size {
|
||||||
Some(size) => Cell::new(format_size(size as u64, settings.human_readable).as_str()),
|
Some(size) => format_size(size as u64, settings.human_readable),
|
||||||
None => Cell::new("Missing")
|
None => "Missing".to_string(),
|
||||||
.with_style(Attr::ForegroundColor(prettytable::color::RED))
|
|
||||||
.with_style(Attr::Bold),
|
|
||||||
};
|
};
|
||||||
table.add_row(Row::new(vec![
|
rows.push(Row::new(vec![
|
||||||
Cell::new("Stream Size").with_style(Attr::Bold),
|
Cell::new("Stream Size").with_style(Attr::Bold),
|
||||||
size_cell,
|
Cell::new(&size_str),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
table.add_row(Row::new(vec![
|
rows.push(Row::new(vec![
|
||||||
Cell::new("Compression").with_style(Attr::Bold),
|
Cell::new("Compression").with_style(Attr::Bold),
|
||||||
Cell::new(&item.compression),
|
Cell::new(&item.compression),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
let file_size_cell = match item_path_buf.metadata() {
|
let file_size_str = match item_path_buf.metadata() {
|
||||||
Ok(metadata) => {
|
Ok(metadata) => format_size(metadata.len(), settings.human_readable),
|
||||||
Cell::new(format_size(metadata.len(), settings.human_readable).as_str())
|
Err(_) => "Missing".to_string(),
|
||||||
}
|
|
||||||
Err(_) => Cell::new("Missing")
|
|
||||||
.with_style(Attr::ForegroundColor(prettytable::color::RED))
|
|
||||||
.with_style(Attr::Bold),
|
|
||||||
};
|
};
|
||||||
table.add_row(Row::new(vec![
|
rows.push(Row::new(vec![
|
||||||
Cell::new("File Size").with_style(Attr::Bold),
|
Cell::new("File Size").with_style(Attr::Bold),
|
||||||
file_size_cell,
|
Cell::new(&file_size_str),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
table.add_row(Row::new(vec![
|
let tags_str = item_tags.join(" ");
|
||||||
|
rows.push(Row::new(vec![
|
||||||
Cell::new("Tags").with_style(Attr::Bold),
|
Cell::new("Tags").with_style(Attr::Bold),
|
||||||
Cell::new(&item_tags.join(" ")),
|
Cell::new(&tags_str),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
|
// Collect meta rows to potentially truncate the last one
|
||||||
|
let mut meta_rows = Vec::new();
|
||||||
for meta in item_with_meta.meta {
|
for meta in item_with_meta.meta {
|
||||||
let meta_name = format!("Meta: {}", &meta.name);
|
let meta_name = format!("Meta: {}", &meta.name);
|
||||||
table.add_row(Row::new(vec![
|
meta_rows.push((meta_name, meta.value));
|
||||||
Cell::new(meta_name.as_str()).with_style(Attr::Bold),
|
}
|
||||||
Cell::new(&meta.value),
|
|
||||||
|
// Calculate the maximum width for the last column if we're in a terminal
|
||||||
|
let terminal_width = crate::modes::common::get_terminal_width();
|
||||||
|
if std::io::stdout().is_terminal() && terminal_width > 0 {
|
||||||
|
// Estimate widths of other columns (assuming first column is the longest label)
|
||||||
|
// This is a simplification, but should work for most cases
|
||||||
|
let max_label_width = rows.iter()
|
||||||
|
.map(|row| row.get_cell(0).map(|c| c.get_content().chars().count()).unwrap_or(0))
|
||||||
|
.max()
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
// Calculate the maximum width for the value column
|
||||||
|
let max_value_width = crate::modes::common::calculate_last_column_width(&[max_label_width], terminal_width);
|
||||||
|
|
||||||
|
// Process meta rows to truncate values if necessary
|
||||||
|
for (meta_name, meta_value) in meta_rows {
|
||||||
|
let truncated_value = if max_value_width > 0 {
|
||||||
|
crate::modes::common::truncate_with_ellipsis(&meta_value, max_value_width)
|
||||||
|
} else {
|
||||||
|
meta_value
|
||||||
|
};
|
||||||
|
rows.push(Row::new(vec![
|
||||||
|
Cell::new(&meta_name).with_style(Attr::Bold),
|
||||||
|
Cell::new(&truncated_value),
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Not a terminal or no terminal width, add all meta rows without truncation
|
||||||
|
for (meta_name, meta_value) in meta_rows {
|
||||||
|
rows.push(Row::new(vec![
|
||||||
|
Cell::new(&meta_name).with_style(Attr::Bold),
|
||||||
|
Cell::new(&meta_value),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all rows to the table
|
||||||
|
for row in rows {
|
||||||
|
table.add_row(row);
|
||||||
|
}
|
||||||
|
|
||||||
table.printstd();
|
table.printstd();
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in New Issue
Block a user