From 5154c40e2c438083a25fee1a84698e7c9e3266e6 Mon Sep 17 00:00:00 2001 From: Sami Fouad Date: Fri, 11 Sep 2026 23:10:23 -0600 Subject: [PATCH] fix(registry): restore edit costs for typo suggestions (#19) -codex --- src/registry.rs | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/registry.rs b/src/registry.rs index fa3d1b5..bbba752 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -522,7 +522,9 @@ fn levenshtein(a: &str, b: &str) -> usize { current[0] = i + 1; for (j, b_char) in b.chars().enumerate() { let cost = usize::from(a_char != b_char); - current[j + 1] = current[j].min(previous[j + 1]).min(previous[j] + cost); + current[j + 1] = (current[j] + 1) + .min(previous[j + 1] + 1) + .min(previous[j] + cost); } previous.clone_from_slice(¤t); } @@ -536,6 +538,42 @@ mod tests { static HANDLER_RAN: AtomicBool = AtomicBool::new(false); + #[test] + fn levenshtein_counts_each_edit() { + for (a, b, expected) in [ + ("", "", 0), + ("", "abc", 3), + ("same", "same", 0), + ("cat", "cut", 1), + ("ab", "abcd", 2), + ("kitten", "sitting", 3), + ("é", "é猫", 1), + ("--hep", "--help", 1), + ("--hep", "--version", 6), + ("--hep", "--verbose", 6), + ] { + assert_eq!(levenshtein(a, b), expected, "{a:?} -> {b:?}"); + assert_eq!(levenshtein(b, a), expected, "{b:?} -> {a:?}"); + } + } + + #[test] + fn misspelled_help_suggests_only_help() { + let registry = RegistryBuilder::new() + .flags(["--help", "--version", "--verbose"].map(|name| FlagSpec { + name, + aliases: &[], + description: "test flag", + })) + .build() + .expect("registry builds"); + + let parsed = Args::collect(vec!["--hep".to_string()], ®istry); + + assert_eq!(parsed.errors.len(), 1); + assert_eq!(parsed.errors[0].suggestions, ["--help"]); + } + fn handler(_context: &Context) { HANDLER_RAN.store(true, Ordering::SeqCst); }