Use idiomatic names for CaseSensitivity and ContainType

This commit is contained in:
Mahmoud Al-Qudsi 2024-11-22 15:23:27 -06:00
parent b570c7f6a6
commit b949497bc1
2 changed files with 109 additions and 56 deletions

View File

@ -102,59 +102,67 @@ pub fn ifind(haystack: &wstr, needle: &wstr, fuzzy: bool /* = false */) -> Optio
}
// The ways one string can contain another.
//
// Note that the order of entries below affects the sort order of completions.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ContainType {
/// exact match: foobar matches foo
exact,
/// prefix match: foo matches foobar
prefix,
/// substring match: ooba matches foobar
substr,
/// subsequence match: fbr matches foobar
subseq,
/// Exact match: `foobar` matches `foo`
Exact,
/// Prefix match: `foo` matches `foobar`
Prefix,
/// Substring match: `ooba` matches `foobar`
Substr,
/// Subsequence match: `fbr` matches `foobar`
Subseq,
}
// The case-folding required for the match.
//
// Note that the order of entries below affects the sort order of completions.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum CaseFold {
/// exact match: foobar matches foobar
samecase,
/// case insensitive match with lowercase input. foobar matches FoBar.
smartcase,
/// case insensitive: FoBaR matches foobAr
icase,
pub enum CaseSensitivity {
/// Exact match: `foobar` only matches `foobar`
Sensitive,
/// Case insensitive match if lowercase input: `foobar` matches `FooBar`.
Smart,
/// Case insensitive: `FooBaR` matches `foobAr`
Insensitive,
}
/// A lightweight value-type describing how closely a string fuzzy-matches another string.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct StringFuzzyMatch {
pub typ: ContainType,
pub case_fold: CaseFold,
pub case_fold: CaseSensitivity,
}
impl StringFuzzyMatch {
pub fn new(typ: ContainType, case_fold: CaseFold) -> Self {
pub fn new(typ: ContainType, case_fold: CaseSensitivity) -> Self {
Self { typ, case_fold }
}
// Helper to return an exact match.
#[inline(always)]
pub fn exact_match() -> Self {
Self::new(ContainType::exact, CaseFold::samecase)
Self::new(ContainType::Exact, CaseSensitivity::Sensitive)
}
/// Return whether this is a samecase exact match.
#[inline(always)]
pub fn is_samecase_exact(&self) -> bool {
self.typ == ContainType::exact && self.case_fold == CaseFold::samecase
self.typ == ContainType::Exact && self.case_fold == CaseSensitivity::Sensitive
}
/// Return if we are exact or prefix match.
#[inline(always)]
pub fn is_exact_or_prefix(&self) -> bool {
matches!(self.typ, ContainType::exact | ContainType::prefix)
matches!(self.typ, ContainType::Exact | ContainType::Prefix)
}
// Return if our match requires a full replacement, i.e. is not a strict extension of our
// existing string. This is false only if our case matches, and our type is prefix or exact.
// existing string. This is false only if our case matches and our type is prefix or exact.
#[inline(always)]
pub fn requires_full_replacement(&self) -> bool {
if self.case_fold != CaseFold::samecase {
if self.case_fold != CaseSensitivity::Sensitive {
return true;
}
matches!(self.typ, ContainType::substr | ContainType::subseq)
matches!(self.typ, ContainType::Substr | ContainType::Subseq)
}
/// Try creating a fuzzy match for `string` against `match_against`.
@ -170,10 +178,10 @@ impl StringFuzzyMatch {
let get_case_fold = || {
for c in string.chars() {
if c.to_lowercase().next().unwrap() != c {
return CaseFold::icase;
return CaseSensitivity::Insensitive;
}
}
CaseFold::smartcase
CaseSensitivity::Smart
};
// A string cannot fuzzy match against a shorter string.
@ -184,27 +192,27 @@ impl StringFuzzyMatch {
// exact samecase
if string == match_against {
return Some(StringFuzzyMatch::new(
ContainType::exact,
CaseFold::samecase,
ContainType::Exact,
CaseSensitivity::Sensitive,
));
}
// prefix samecase
if match_against.starts_with(string) {
return Some(StringFuzzyMatch::new(
ContainType::prefix,
CaseFold::samecase,
ContainType::Prefix,
CaseSensitivity::Sensitive,
));
}
// exact icase
if wcscasecmp(string, match_against).is_eq() {
return Some(StringFuzzyMatch::new(ContainType::exact, get_case_fold()));
return Some(StringFuzzyMatch::new(ContainType::Exact, get_case_fold()));
}
// prefix icase
if string_prefixes_string_case_insensitive(string, match_against) {
return Some(StringFuzzyMatch::new(ContainType::prefix, get_case_fold()));
return Some(StringFuzzyMatch::new(ContainType::Prefix, get_case_fold()));
}
// If anchor_start is set, this is as far as we go.
@ -219,21 +227,21 @@ impl StringFuzzyMatch {
.any(|window| wstr::from_char_slice(window) == string)
{
return Some(StringFuzzyMatch::new(
ContainType::substr,
CaseFold::samecase,
ContainType::Substr,
CaseSensitivity::Sensitive,
));
}
// substr icase
if ifind(match_against, string, true /* fuzzy */).is_some() {
return Some(StringFuzzyMatch::new(ContainType::substr, get_case_fold()));
return Some(StringFuzzyMatch::new(ContainType::Substr, get_case_fold()));
}
// subseq samecase
if subsequence_in_string(string, match_against) {
return Some(StringFuzzyMatch::new(
ContainType::subseq,
CaseFold::samecase,
ContainType::Subseq,
CaseSensitivity::Sensitive,
));
}
@ -246,13 +254,13 @@ impl StringFuzzyMatch {
// smaller. Treat 'exact' types the same as 'prefix' types; this is because we do not
// prefer exact matches to prefix matches when presenting completions to the user.
// Treat smartcase the same as samecase; see #3978.
let effective_type = if self.typ == ContainType::exact {
ContainType::prefix
let effective_type = if self.typ == ContainType::Exact {
ContainType::Prefix
} else {
self.typ
};
let effective_case = if self.case_fold == CaseFold::smartcase {
CaseFold::samecase
let effective_case = if self.case_fold == CaseSensitivity::Smart {
CaseSensitivity::Sensitive
} else {
self.case_fold
};
@ -587,18 +595,63 @@ fn test_fuzzy_match() {
);
};
}
validate!("", "", ContainType::exact, CaseFold::samecase);
validate!("alpha", "alpha", ContainType::exact, CaseFold::samecase);
validate!("alp", "alpha", ContainType::prefix, CaseFold::samecase);
validate!("alpha", "AlPhA", ContainType::exact, CaseFold::smartcase);
validate!("alpha", "AlPhA!", ContainType::prefix, CaseFold::smartcase);
validate!("ALPHA", "alpha!", ContainType::prefix, CaseFold::icase);
validate!("ALPHA!", "alPhA!", ContainType::exact, CaseFold::icase);
validate!("alPh", "ALPHA!", ContainType::prefix, CaseFold::icase);
validate!("LPH", "ALPHA!", ContainType::substr, CaseFold::samecase);
validate!("lph", "AlPhA!", ContainType::substr, CaseFold::smartcase);
validate!("lPh", "ALPHA!", ContainType::substr, CaseFold::icase);
validate!("AA", "ALPHA!", ContainType::subseq, CaseFold::samecase);
validate!("", "", ContainType::Exact, CaseSensitivity::Sensitive);
validate!(
"alpha",
"alpha",
ContainType::Exact,
CaseSensitivity::Sensitive
);
validate!(
"alp",
"alpha",
ContainType::Prefix,
CaseSensitivity::Sensitive
);
validate!("alpha", "AlPhA", ContainType::Exact, CaseSensitivity::Smart);
validate!(
"alpha",
"AlPhA!",
ContainType::Prefix,
CaseSensitivity::Smart
);
validate!(
"ALPHA",
"alpha!",
ContainType::Prefix,
CaseSensitivity::Insensitive
);
validate!(
"ALPHA!",
"alPhA!",
ContainType::Exact,
CaseSensitivity::Insensitive
);
validate!(
"alPh",
"ALPHA!",
ContainType::Prefix,
CaseSensitivity::Insensitive
);
validate!(
"LPH",
"ALPHA!",
ContainType::Substr,
CaseSensitivity::Sensitive
);
validate!("lph", "AlPhA!", ContainType::Substr, CaseSensitivity::Smart);
validate!(
"lPh",
"ALPHA!",
ContainType::Substr,
CaseSensitivity::Insensitive
);
validate!(
"AA",
"ALPHA!",
ContainType::Subseq,
CaseSensitivity::Sensitive
);
// no subseq icase
validate!("lh", "ALPHA!", None);
validate!("BB", "ALPHA!", None);

View File

@ -16,7 +16,7 @@ use crate::future_feature_flags::feature_test;
use crate::future_feature_flags::FeatureFlag;
use crate::wchar::prelude::*;
use crate::wcstringutil::{
string_fuzzy_match_string, string_suffixes_string_case_insensitive, CaseFold,
string_fuzzy_match_string, string_suffixes_string_case_insensitive, CaseSensitivity,
};
use crate::wutil::dir_iter::DirEntryType;
use crate::wutil::{dir_iter::DirEntry, lwstat, waccess};
@ -84,9 +84,9 @@ struct WcCompletePack<'orig, 'f> {
// Weirdly specific and non-reusable helper function that makes its one call site much clearer.
fn has_prefix_match(comps: &CompletionReceiver, first: usize) -> bool {
comps[first..]
.iter()
.any(|c| c.r#match.is_exact_or_prefix() && c.r#match.case_fold == CaseFold::samecase)
comps[first..].iter().any(|c| {
c.r#match.is_exact_or_prefix() && c.r#match.case_fold == CaseSensitivity::Sensitive
})
}
/// Matches the string against the wildcard, and if the wildcard is a possible completion of the