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. // 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)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ContainType { pub enum ContainType {
/// exact match: foobar matches foo /// Exact match: `foobar` matches `foo`
exact, Exact,
/// prefix match: foo matches foobar /// Prefix match: `foo` matches `foobar`
prefix, Prefix,
/// substring match: ooba matches foobar /// Substring match: `ooba` matches `foobar`
substr, Substr,
/// subsequence match: fbr matches foobar /// Subsequence match: `fbr` matches `foobar`
subseq, Subseq,
} }
// The case-folding required for the match. // 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)] #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum CaseFold { pub enum CaseSensitivity {
/// exact match: foobar matches foobar /// Exact match: `foobar` only matches `foobar`
samecase, Sensitive,
/// case insensitive match with lowercase input. foobar matches FoBar. /// Case insensitive match if lowercase input: `foobar` matches `FooBar`.
smartcase, Smart,
/// case insensitive: FoBaR matches foobAr /// Case insensitive: `FooBaR` matches `foobAr`
icase, Insensitive,
} }
/// A lightweight value-type describing how closely a string fuzzy-matches another string. /// A lightweight value-type describing how closely a string fuzzy-matches another string.
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct StringFuzzyMatch { pub struct StringFuzzyMatch {
pub typ: ContainType, pub typ: ContainType,
pub case_fold: CaseFold, pub case_fold: CaseSensitivity,
} }
impl StringFuzzyMatch { impl StringFuzzyMatch {
pub fn new(typ: ContainType, case_fold: CaseFold) -> Self { pub fn new(typ: ContainType, case_fold: CaseSensitivity) -> Self {
Self { typ, case_fold } Self { typ, case_fold }
} }
// Helper to return an exact match. // Helper to return an exact match.
#[inline(always)]
pub fn exact_match() -> Self { 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. /// Return whether this is a samecase exact match.
#[inline(always)]
pub fn is_samecase_exact(&self) -> bool { 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. /// Return if we are exact or prefix match.
#[inline(always)]
pub fn is_exact_or_prefix(&self) -> bool { 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 // 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 { pub fn requires_full_replacement(&self) -> bool {
if self.case_fold != CaseFold::samecase { if self.case_fold != CaseSensitivity::Sensitive {
return true; 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`. /// Try creating a fuzzy match for `string` against `match_against`.
@ -170,10 +178,10 @@ impl StringFuzzyMatch {
let get_case_fold = || { let get_case_fold = || {
for c in string.chars() { for c in string.chars() {
if c.to_lowercase().next().unwrap() != c { 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. // A string cannot fuzzy match against a shorter string.
@ -184,27 +192,27 @@ impl StringFuzzyMatch {
// exact samecase // exact samecase
if string == match_against { if string == match_against {
return Some(StringFuzzyMatch::new( return Some(StringFuzzyMatch::new(
ContainType::exact, ContainType::Exact,
CaseFold::samecase, CaseSensitivity::Sensitive,
)); ));
} }
// prefix samecase // prefix samecase
if match_against.starts_with(string) { if match_against.starts_with(string) {
return Some(StringFuzzyMatch::new( return Some(StringFuzzyMatch::new(
ContainType::prefix, ContainType::Prefix,
CaseFold::samecase, CaseSensitivity::Sensitive,
)); ));
} }
// exact icase // exact icase
if wcscasecmp(string, match_against).is_eq() { 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 // prefix icase
if string_prefixes_string_case_insensitive(string, match_against) { 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. // 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) .any(|window| wstr::from_char_slice(window) == string)
{ {
return Some(StringFuzzyMatch::new( return Some(StringFuzzyMatch::new(
ContainType::substr, ContainType::Substr,
CaseFold::samecase, CaseSensitivity::Sensitive,
)); ));
} }
// substr icase // substr icase
if ifind(match_against, string, true /* fuzzy */).is_some() { 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 // subseq samecase
if subsequence_in_string(string, match_against) { if subsequence_in_string(string, match_against) {
return Some(StringFuzzyMatch::new( return Some(StringFuzzyMatch::new(
ContainType::subseq, ContainType::Subseq,
CaseFold::samecase, CaseSensitivity::Sensitive,
)); ));
} }
@ -246,13 +254,13 @@ impl StringFuzzyMatch {
// smaller. Treat 'exact' types the same as 'prefix' types; this is because we do not // 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. // prefer exact matches to prefix matches when presenting completions to the user.
// Treat smartcase the same as samecase; see #3978. // Treat smartcase the same as samecase; see #3978.
let effective_type = if self.typ == ContainType::exact { let effective_type = if self.typ == ContainType::Exact {
ContainType::prefix ContainType::Prefix
} else { } else {
self.typ self.typ
}; };
let effective_case = if self.case_fold == CaseFold::smartcase { let effective_case = if self.case_fold == CaseSensitivity::Smart {
CaseFold::samecase CaseSensitivity::Sensitive
} else { } else {
self.case_fold self.case_fold
}; };
@ -587,18 +595,63 @@ fn test_fuzzy_match() {
); );
}; };
} }
validate!("", "", ContainType::exact, CaseFold::samecase); validate!("", "", ContainType::Exact, CaseSensitivity::Sensitive);
validate!("alpha", "alpha", ContainType::exact, CaseFold::samecase); validate!(
validate!("alp", "alpha", ContainType::prefix, CaseFold::samecase); "alpha",
validate!("alpha", "AlPhA", ContainType::exact, CaseFold::smartcase); "alpha",
validate!("alpha", "AlPhA!", ContainType::prefix, CaseFold::smartcase); ContainType::Exact,
validate!("ALPHA", "alpha!", ContainType::prefix, CaseFold::icase); CaseSensitivity::Sensitive
validate!("ALPHA!", "alPhA!", ContainType::exact, CaseFold::icase); );
validate!("alPh", "ALPHA!", ContainType::prefix, CaseFold::icase); validate!(
validate!("LPH", "ALPHA!", ContainType::substr, CaseFold::samecase); "alp",
validate!("lph", "AlPhA!", ContainType::substr, CaseFold::smartcase); "alpha",
validate!("lPh", "ALPHA!", ContainType::substr, CaseFold::icase); ContainType::Prefix,
validate!("AA", "ALPHA!", ContainType::subseq, CaseFold::samecase); 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 // no subseq icase
validate!("lh", "ALPHA!", None); validate!("lh", "ALPHA!", None);
validate!("BB", "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::future_feature_flags::FeatureFlag;
use crate::wchar::prelude::*; use crate::wchar::prelude::*;
use crate::wcstringutil::{ 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::DirEntryType;
use crate::wutil::{dir_iter::DirEntry, lwstat, waccess}; 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. // Weirdly specific and non-reusable helper function that makes its one call site much clearer.
fn has_prefix_match(comps: &CompletionReceiver, first: usize) -> bool { fn has_prefix_match(comps: &CompletionReceiver, first: usize) -> bool {
comps[first..] comps[first..].iter().any(|c| {
.iter() c.r#match.is_exact_or_prefix() && c.r#match.case_fold == CaseSensitivity::Sensitive
.any(|c| c.r#match.is_exact_or_prefix() && c.r#match.case_fold == CaseFold::samecase) })
} }
/// Matches the string against the wildcard, and if the wildcard is a possible completion of the /// Matches the string against the wildcard, and if the wildcard is a possible completion of the