Change:
On identifiers that have more than one word, it is relatively common to write them in the wrong order ( → ). These are normally not found by Levenshtein distance checks, but we could do a basic "split on ``, sort and join before comparison" so that we could suggest the right identifier. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --This issue has been assigned to via . <!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOT_END --
I'd like to try to work on this. As is short enough to be caught by the Levenstein distance, I wrote for testing: claim
I'm thinking about the priority of the different possible matches. I tend to think that a match found by levenstein distance should have a greater priority, but what do you think?
it's tricky. I feel that your instinct might be right but we'd only know when looking at real world usage. The nice thing is that the lev distance check by its very nature it is limited, so doing the swapped words check as a fallback should work out ok in practice.
Releasing my assignment, as I am not sure to be able to commit. In case someone want to take on this, here is the function that should be modified. release-assignment
(I think this is appropriate, please tell me if it isn't) modify labels to +E-Easy
I can take a stab at this, seems reasonable with previous comments. claim

Discussion:
T: Iterator<Item = &'a Symbol>, { let max_dist = dist.map_or_else(|| cmp::max(lookup.len(), 3) / 3, |d| d); <ins> let name_vec: Vec<&Symbol> = iter_names.collect(); </ins> <del> let (case_insensitive_match, levenstein_match) = iter_names </del> <ins> let (case_insensitive_match, levenshtein_match) = name_vec .iter() </ins> .filter_map(|&name| { let dist = lev_distance(lookup, &name.as_str()); if dist <= max_dist { Some((name, dist)) } else { None } }) // Here we are collecting the next structure: <del> // (case_insensitive_match, (levenstein_match, levenstein_distance)) </del> <ins> // (case_insensitive_match, (levenshtein_match, levenshtein_distance)) </ins> .fold((None, None), |result, (candidate, dist)| { ( if candidate.as_str().to_uppercase() == lookup.to_uppercase() {

}, ) }); <ins> // Priority of matches: // 1. Exact case insensitive match // 2. Levenshtein distance match // 3. Sorted word match </ins> if let Some(candidate) = case_insensitive_match { <del> Some(candidate) // exact case insensitive match has a higher priority </del> <ins> Some(*candidate) } else if levenshtein_match.is_some() { levenshtein_match.map(|(candidate, _)| *candidate) </ins> } else { <del> levenstein_match.map(|(candidate, _)| candidate) </del> <ins> find_match_by_sorted_words(name_vec, lookup) </ins> } } <ins> fn find_match_by_sorted_words<'a>(iter_names: Vec<&'a Symbol>, lookup: &str) -> Option<Symbol> { iter_names.iter().fold(None, |result, candidate| { if sort_by_words(&candidate.as_str()) == sort_by_words(lookup) { Some(**candidate) } else { result } }) } fn sort_by_words(name: &str) -> String { let mut split_words: Vec<&str> = name.split('_').collect(); split_words.sort(); split_words.join("_") } </ins>

find_best_match_for_name(input.iter(), "aaaa", Some(4)), Some(Symbol::intern("AAAA")) ); <ins> let input = vec![Symbol::intern("a_longer_variable_name")]; assert_eq!( find_best_match_for_name(input.iter(), "a_variable_longer_name", None), Some(Symbol::intern("a_longer_variable_name")) ); </ins> }) }

<ins> fn main() { let a_longer_variable_name = 1; println!("{}", a_variable_longer_name); //~ ERROR E0425 } </ins>

<ins> error[E0425]: cannot find value `a_variable_longer_name` in this scope --> $DIR/issue-66968-suggest-sorted-words.rs:3:20 | LL |     println!("{}", a_variable_longer_name); |                    ^^^^^^^^^^^^^^^^^^^^^^ help: a local variable with a similar name exists: `a_longer_variable_name` error: aborting due to previous error For more information about this error, try `rustc --explain E0425`. </ins>