Rollup merge of #89956 - JohnTitor:suggest-case-insensitive-match-names, r=estebank
Suggest a case insensitive match name regardless of levenshtein distance Fixes #86170 Currently, `find_best_match_for_name` only returns a case insensitive match name depending on a Levenshtein distance. It's a bit unfortunate that that hides some suggestions for typos like `Bar` -> `BAR`. That idea is from https://github.com/rust-lang/rust/pull/46347#discussion_r153701834, but I think it still makes some sense to show a candidate when we find a case insensitive match name as it's more like a typo. Skipped the `candidate != lookup` check because the current (i.e, `levenshtein_match`) returns the exact same `Symbol` anyway but it doesn't seem to confuse anything on UI tests. r? ``@estebank``
This commit is contained in:
commit
8c8835d277
4 changed files with 38 additions and 31 deletions
|
@ -58,34 +58,28 @@ pub fn find_best_match_for_name(
|
||||||
let lookup = &lookup.as_str();
|
let lookup = &lookup.as_str();
|
||||||
let max_dist = dist.unwrap_or_else(|| cmp::max(lookup.len(), 3) / 3);
|
let max_dist = dist.unwrap_or_else(|| cmp::max(lookup.len(), 3) / 3);
|
||||||
|
|
||||||
let (case_insensitive_match, levenshtein_match) = name_vec
|
// Priority of matches:
|
||||||
|
// 1. Exact case insensitive match
|
||||||
|
// 2. Levenshtein distance match
|
||||||
|
// 3. Sorted word match
|
||||||
|
if let Some(case_insensitive_match) =
|
||||||
|
name_vec.iter().find(|candidate| candidate.as_str().to_uppercase() == lookup.to_uppercase())
|
||||||
|
{
|
||||||
|
return Some(*case_insensitive_match);
|
||||||
|
}
|
||||||
|
let levenshtein_match = name_vec
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|&name| {
|
.filter_map(|&name| {
|
||||||
let dist = lev_distance(lookup, &name.as_str());
|
let dist = lev_distance(lookup, &name.as_str());
|
||||||
if dist <= max_dist { Some((name, dist)) } else { None }
|
if dist <= max_dist { Some((name, dist)) } else { None }
|
||||||
})
|
})
|
||||||
// Here we are collecting the next structure:
|
// Here we are collecting the next structure:
|
||||||
// (case_insensitive_match, (levenshtein_match, levenshtein_distance))
|
// (levenshtein_match, levenshtein_distance)
|
||||||
.fold((None, None), |result, (candidate, dist)| {
|
.fold(None, |result, (candidate, dist)| match result {
|
||||||
(
|
|
||||||
if candidate.as_str().to_uppercase() == lookup.to_uppercase() {
|
|
||||||
Some(candidate)
|
|
||||||
} else {
|
|
||||||
result.0
|
|
||||||
},
|
|
||||||
match result.1 {
|
|
||||||
None => Some((candidate, dist)),
|
None => Some((candidate, dist)),
|
||||||
Some((c, d)) => Some(if dist < d { (candidate, dist) } else { (c, d) }),
|
Some((c, d)) => Some(if dist < d { (candidate, dist) } else { (c, d) }),
|
||||||
},
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
// Priority of matches:
|
if levenshtein_match.is_some() {
|
||||||
// 1. Exact case insensitive match
|
|
||||||
// 2. Levenshtein distance match
|
|
||||||
// 3. Sorted word match
|
|
||||||
if let Some(candidate) = case_insensitive_match {
|
|
||||||
Some(candidate)
|
|
||||||
} else if levenshtein_match.is_some() {
|
|
||||||
levenshtein_match.map(|(candidate, _)| candidate)
|
levenshtein_match.map(|(candidate, _)| candidate)
|
||||||
} else {
|
} else {
|
||||||
find_match_by_sorted_words(name_vec, lookup)
|
find_match_by_sorted_words(name_vec, lookup)
|
||||||
|
|
|
@ -31,15 +31,11 @@ fn test_find_best_match_for_name() {
|
||||||
|
|
||||||
assert_eq!(find_best_match_for_name(&input, Symbol::intern("1111111111"), None), None);
|
assert_eq!(find_best_match_for_name(&input, Symbol::intern("1111111111"), None), None);
|
||||||
|
|
||||||
let input = vec![Symbol::intern("aAAA")];
|
|
||||||
assert_eq!(
|
|
||||||
find_best_match_for_name(&input, Symbol::intern("AAAA"), None),
|
|
||||||
Some(Symbol::intern("aAAA"))
|
|
||||||
);
|
|
||||||
|
|
||||||
let input = vec![Symbol::intern("AAAA")];
|
let input = vec![Symbol::intern("AAAA")];
|
||||||
// Returns None because `lev_distance > max_dist / 3`
|
assert_eq!(
|
||||||
assert_eq!(find_best_match_for_name(&input, Symbol::intern("aaaa"), None), None);
|
find_best_match_for_name(&input, Symbol::intern("aaaa"), None),
|
||||||
|
Some(Symbol::intern("AAAA"))
|
||||||
|
);
|
||||||
|
|
||||||
let input = vec![Symbol::intern("AAAA")];
|
let input = vec![Symbol::intern("AAAA")];
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -2,11 +2,14 @@ error[E0425]: cannot find value `Opaque` in this scope
|
||||||
--> $DIR/rustc-macro-transparency.rs:26:5
|
--> $DIR/rustc-macro-transparency.rs:26:5
|
||||||
|
|
|
|
||||||
LL | Opaque;
|
LL | Opaque;
|
||||||
| ^^^^^^ help: a local variable with a similar name exists (notice the capitalization): `opaque`
|
| ^^^^^^ not found in this scope
|
||||||
|
|
||||||
error[E0423]: expected value, found macro `semitransparent`
|
error[E0423]: expected value, found macro `semitransparent`
|
||||||
--> $DIR/rustc-macro-transparency.rs:29:5
|
--> $DIR/rustc-macro-transparency.rs:29:5
|
||||||
|
|
|
|
||||||
|
LL | struct SemiTransparent;
|
||||||
|
| ----------------------- similarly named unit struct `SemiTransparent` defined here
|
||||||
|
...
|
||||||
LL | semitransparent;
|
LL | semitransparent;
|
||||||
| ^^^^^^^^^^^^^^^ not a value
|
| ^^^^^^^^^^^^^^^ not a value
|
||||||
|
|
|
|
||||||
|
@ -14,10 +17,17 @@ help: use `!` to invoke the macro
|
||||||
|
|
|
|
||||||
LL | semitransparent!;
|
LL | semitransparent!;
|
||||||
| +
|
| +
|
||||||
|
help: a unit struct with a similar name exists
|
||||||
|
|
|
||||||
|
LL | SemiTransparent;
|
||||||
|
| ~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
error[E0423]: expected value, found macro `opaque`
|
error[E0423]: expected value, found macro `opaque`
|
||||||
--> $DIR/rustc-macro-transparency.rs:30:5
|
--> $DIR/rustc-macro-transparency.rs:30:5
|
||||||
|
|
|
|
||||||
|
LL | struct Opaque;
|
||||||
|
| -------------- similarly named unit struct `Opaque` defined here
|
||||||
|
...
|
||||||
LL | opaque;
|
LL | opaque;
|
||||||
| ^^^^^^ not a value
|
| ^^^^^^ not a value
|
||||||
|
|
|
|
||||||
|
@ -25,6 +35,10 @@ help: use `!` to invoke the macro
|
||||||
|
|
|
|
||||||
LL | opaque!;
|
LL | opaque!;
|
||||||
| +
|
| +
|
||||||
|
help: a unit struct with a similar name exists
|
||||||
|
|
|
||||||
|
LL | Opaque;
|
||||||
|
| ~~~~~~
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,10 @@ LL | enum Delicious {
|
||||||
| -------------- variant or associated item `PIE` not found here
|
| -------------- variant or associated item `PIE` not found here
|
||||||
...
|
...
|
||||||
LL | ApplePie = Delicious::Apple as isize | Delicious::PIE as isize,
|
LL | ApplePie = Delicious::Apple as isize | Delicious::PIE as isize,
|
||||||
| ^^^ variant or associated item not found in `Delicious`
|
| ^^^
|
||||||
|
| |
|
||||||
|
| variant or associated item not found in `Delicious`
|
||||||
|
| help: there is a variant with a similar name: `Pie`
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue