diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 6a3e198c1..e11cc31b1 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -422,6 +422,47 @@ pub mod completers { Accept, } + fn best_match(opt1: &str, opt2: &str, input: &str) -> std::cmp::Ordering { + let min_len = std::cmp::min(std::cmp::min(opt1.len(), opt2.len()), input.len()); + + let mut opt1vals = (0, false); + let mut opt2vals = (0, false); + + for i in 0..min_len { + if opt1vals.1 && opt2vals.1 { + break; + } + + if !opt1vals.1 { + if opt1[i..=i] == input[i..=i] { + opt1vals.0 += 1; + } else { + opt1vals.1 = true; + } + } + + if !opt2vals.1 { + if opt2[i..=i] == input[i..=i] { + opt2vals.0 += 1; + } else { + opt2vals.1 = true; + } + } + } + + let ord = opt2vals.0.cmp(&opt1vals.0); + + if ord == std::cmp::Ordering::Equal { + if opt1.len() < opt2.len() { + return std::cmp::Ordering::Less; + } else if opt1.len() > opt2.len() { + return std::cmp::Ordering::Greater; + } + } + + ord + } + // TODO: we could return an iter/lazy thing so it can fetch as many as it needs. fn filename_impl( _editor: &Editor, @@ -437,6 +478,10 @@ pub mod completers { use ignore::WalkBuilder; use std::path::Path; + let input_filename = input + .split(std::path::MAIN_SEPARATOR) + .last() + .unwrap_or_default(); let is_tilde = input == "~"; let path = helix_stdx::path::expand_tilde(Path::new(input)); @@ -509,12 +554,15 @@ pub mod completers { // if empty, return a list of dirs and files in current dir if let Some(file_name) = file_name { let range = (input.len().saturating_sub(file_name.len()))..; - fuzzy_match(&file_name, files, true) + let mut files: Vec<_> = fuzzy_match(&file_name, files, true) .into_iter() .map(|(name, _)| (range.clone(), name)) - .collect() + .collect(); - // TODO: complete to longest common match + files.sort_unstable_by(|(_, path1), (_, path2)| { + best_match(path1, path2, input_filename) + }); + files } else { let mut files: Vec<_> = files.map(|file| (end.clone(), file)).collect(); files.sort_unstable_by(|(_, path1), (_, path2)| path1.cmp(path2));