use std::path::Path;
use super::file_finder::format_file_size;
use super::{CompletionItem, CompletionKind};
pub fn file_type_indicator(path: &str) -> (&'static str, &'static str) {
let ext = Path::new(path)
.extension()
.and_then(|e| e.to_str())
.unwrap_or("");
let name = Path::new(path)
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("");
match ext.to_lowercase().as_str() {
"py" => ("py", "Cyan"),
"js" => ("js", "Yellow"),
"jsx" => ("jsx", "Yellow"),
"ts" => ("ts", "Blue"),
"tsx" => ("tsx", "Blue"),
"rs" => ("rs", "Red"),
"go" => ("go", "Cyan"),
"java" => ("java", "Red"),
"c" | "h" => ("c", "Blue"),
"cpp" | "cc" | "hpp" => ("cpp", "Blue"),
"cs" => ("cs", "Green"),
"rb" => ("rb", "Red"),
"php" => ("php", "Magenta"),
"swift" => ("swift", "Yellow"),
"kt" | "kts" => ("kt", "Magenta"),
"html" | "htm" => ("html", "Yellow"),
"css" => ("css", "Blue"),
"scss" | "sass" => ("scss", "Magenta"),
"json" => ("json", "Yellow"),
"xml" => ("xml", "Green"),
"yaml" | "yml" => ("yaml", "Magenta"),
"toml" => ("toml", "Magenta"),
"md" | "markdown" => ("md", "Blue"),
"txt" => ("txt", "Gray"),
"sh" | "bash" | "zsh" => ("sh", "Green"),
"sql" => ("sql", "Yellow"),
"csv" => ("csv", "Green"),
"env" => ("env", "Yellow"),
"lock" => ("lock", "Gray"),
_ => {
match name {
"Makefile" => ("make", "Red"),
"Dockerfile" => ("dock", "Blue"),
_ => {
if ext.is_empty() {
("file", "Gray")
} else {
("file", "Gray")
}
}
}
}
}
}
pub fn shorten_path(path: &str, max_len: usize) -> String {
if path.len() <= max_len {
return path.to_string();
}
let parts: Vec<&str> = path.split('/').collect();
let mut result = String::new();
for (i, part) in parts.iter().enumerate().rev() {
let candidate = if result.is_empty() {
part.to_string()
} else {
format!("{}/{}", part, result)
};
if candidate.len() + 4 > max_len && i > 0 {
return format!(".../{}", result);
}
result = candidate;
}
result
}
pub struct CompletionFormatter;
impl CompletionFormatter {
pub fn format(item: &CompletionItem) -> (String, String) {
match item.kind {
CompletionKind::Command => {
let label = format!("{:<18}", item.label);
(label, item.description.clone())
}
CompletionKind::File => {
let (type_tag, _color) = file_type_indicator(&item.label);
let shortened = shorten_path(&item.label, 46);
let label = format!("{} {:<46}", type_tag, shortened);
let meta = if item.description.is_empty() {
String::new()
} else {
format!("{:>10}", item.description)
};
(label, meta)
}
CompletionKind::Symbol => {
let label = format!("{:<30}", item.label);
(label, item.description.clone())
}
}
}
pub fn file_size_string(path: &Path) -> String {
match std::fs::metadata(path) {
Ok(meta) => format_file_size(meta.len()),
Err(_) => String::new(),
}
}
}
#[cfg(test)]
#[path = "formatters_tests.rs"]
mod tests;