From a92c22b58c6dcf197be501dd0d94f693f7ce714b Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Tue, 26 Aug 2025 08:45:48 -0300 Subject: [PATCH] feat: add ellipsis when truncating strings and only apply max_len for terminal output Co-authored-by: aider (openai/andrew/openrouter/deepseek/deepseek-chat-v3.1) --- src/modes/common.rs | 25 ++++++++++++- src/modes/list.rs | 89 ++++++++++++++++++++++++++------------------- 2 files changed, 75 insertions(+), 39 deletions(-) diff --git a/src/modes/common.rs b/src/modes/common.rs index 19949a1..49530bb 100644 --- a/src/modes/common.rs +++ b/src/modes/common.rs @@ -46,7 +46,30 @@ pub fn string_column(s: String, column_width: usize) -> String { if column_width > 0 { match s.char_indices().nth(column_width) { None => s.to_string(), - Some((idx, _)) => s[..idx].to_string(), + Some((idx, _)) => { + if column_width > 1 { + // Find where to cut to fit the ellipsis + let mut cut_idx = idx; + // We need to make room for the ellipsis character + // Find the character boundary before the cut index + for i in (0..idx).rev() { + if s.is_char_boundary(i) { + if idx - i >= 3 { // Make sure we have enough room + cut_idx = i; + break; + } + } + } + // If we can't find a good place, just truncate without ellipsis + if cut_idx >= 3 { + format!("{}…", &s[..cut_idx - 1]) + } else { + s[..idx].to_string() + } + } else { + s[..idx].to_string() + } + } } } else { s.to_string() diff --git a/src/modes/list.rs b/src/modes/list.rs index 759b04f..f402fe6 100644 --- a/src/modes/list.rs +++ b/src/modes/list.rs @@ -62,26 +62,33 @@ pub fn mode_list( return show_list_structured(items_with_meta, data_path, settings, output_format); } + // Check if output is a terminal + let is_terminal = stdout().is_terminal(); + debug!("Output is terminal: {}", is_terminal); + // Get terminal width once, default to 80 if not available - let term_width = if let Ok(columns_env) = env::var("COLUMNS") { - debug!("COLUMNS environment variable: {:?}", columns_env); - columns_env.parse::().unwrap_or(80) - } else if stdout().is_terminal() { - // Try to get terminal size using termsize - match termsize::get() { - Some(size) => { - let width = size.cols as usize; - debug!("Terminal size detected: {} columns", width); - width - } - None => { - debug!("Failed to get terminal size, defaulting to 80"); - 80 + // Only use max_len when output is a terminal + let term_width = if is_terminal { + if let Ok(columns_env) = env::var("COLUMNS") { + debug!("COLUMNS environment variable: {:?}", columns_env); + columns_env.parse::().unwrap_or(80) + } else { + // Try to get terminal size using termsize + match termsize::get() { + Some(size) => { + let width = size.cols as usize; + debug!("Terminal size detected: {} columns", width); + width + } + None => { + debug!("Failed to get terminal size, defaulting to 80"); + 80 + } } } } else { - debug!("Not a terminal, defaulting to 80"); - 80 + debug!("Not a terminal, max_len will be ignored"); + 0 // Use 0 to indicate no truncation }; debug!("Terminal width: {}", term_width); @@ -114,33 +121,39 @@ pub fn mode_list( let mut meta_name: Option<&str> = None; // Parse max_len, handling numbers, percentages, and negative values - let column_width = if let Some(max_len_str) = &column.max_len { - debug!("Processing max_len for column '{}': {}", column.name, max_len_str); - // Check if it's a negative number - if max_len_str.starts_with('-') { - // Parse as negative number - let abs_value = max_len_str[1..].parse::().unwrap_or(0); - if abs_value > term_width { - 0 + // Only apply max_len when output is a terminal + let column_width = if is_terminal { + if let Some(max_len_str) = &column.max_len { + debug!("Processing max_len for column '{}': {}", column.name, max_len_str); + // Check if it's a negative number + if max_len_str.starts_with('-') { + // Parse as negative number + let abs_value = max_len_str[1..].parse::().unwrap_or(0); + if abs_value > term_width { + 0 + } else { + term_width - abs_value + } + } else if max_len_str.ends_with('%') { + // Parse percentage + let percent_str = max_len_str.trim_end_matches('%'); + let percent = percent_str.parse::().unwrap_or(0.0); + debug!("Percentage: {}%", percent); + let computed_width = (term_width as f64 * percent / 100.0) as usize; + debug!("Computed width: {}", computed_width); + computed_width } else { - term_width - abs_value + // Parse absolute number + let absolute_width = max_len_str.parse::().unwrap_or(0); + debug!("Absolute width: {}", absolute_width); + absolute_width } - } else if max_len_str.ends_with('%') { - // Parse percentage - let percent_str = max_len_str.trim_end_matches('%'); - let percent = percent_str.parse::().unwrap_or(0.0); - debug!("Percentage: {}%", percent); - let computed_width = (term_width as f64 * percent / 100.0) as usize; - debug!("Computed width: {}", computed_width); - computed_width } else { - // Parse absolute number - let absolute_width = max_len_str.parse::().unwrap_or(0); - debug!("Absolute width: {}", absolute_width); - absolute_width + debug!("No max_len specified for column '{}'", column.name); + 0 } } else { - debug!("No max_len specified for column '{}'", column.name); + debug!("Output is not a terminal, ignoring max_len"); 0 };