Fix SEARCH position when text contains a char with multi-char lowercase (#571)

This commit is contained in:
MarekSpodBiedry 2026-10-12 08:27:39 +02:00
parent 3926d21aa9
commit f8983e25eb
2 changed files with 16 additions and 3 deletions

View File

@ -168,19 +168,25 @@ fn tokenize(pattern: &str) -> Vec<Tok> {
while let Some(c) = it.next() {
match c {
'~' => match it.next() {
Some(n) => out.extend(n.to_lowercase().map(Tok::Char)),
Some(n) => out.extend(std::iter::once(lower_char(n)).map(Tok::Char)),
None => out.push(Tok::Char('~')),
},
'*' => out.push(Tok::Star),
'?' => out.push(Tok::Any),
c => out.extend(c.to_lowercase().map(Tok::Char)),
c => out.extend(std::iter::once(lower_char(c)).map(Tok::Char)),
}
}
out
}
/// Lowercase one char to exactly one char, so positions in the lowered text match the original
/// (U+0130 `İ` folds to `i`, like `fold` in text.rs).
fn lower_char(c: char) -> char {
c.to_lowercase().next().unwrap_or(c)
}
fn lower_chars(s: &str) -> Vec<char> {
s.chars().flat_map(char::to_lowercase).collect()
s.chars().map(lower_char).collect()
}
/// Whether the pattern contains an unescaped wildcard.
@ -317,6 +323,8 @@ mod tests {
assert_eq!(wildcard_find("b?d", "abxxd bcd", 0), Some(6));
assert_eq!(wildcard_find("c*", "abcd", 0), Some(2));
assert_eq!(wildcard_find("z", "abcd", 0), None);
assert_eq!(wildcard_find("b", "a\u{130}bc", 0), Some(2));
assert!(wildcard_match("i*", "\u{130}stanbul"));
assert!(has_wildcards("a*"));
assert!(!has_wildcards("abc"));
}

View File

@ -1032,6 +1032,11 @@ mod tests {
close(ev("SEARCH", vec![t("~*"), t("a*b")]), 2.0);
is_err(ev("SEARCH", vec![t("z"), t("abc")]), CellError::Value);
is_err(ev("SEARCH", vec![t("a"), t("abc"), n(5.0)]), CellError::Value);
close(ev("SEARCH", vec![t("b"), t("\u{130}b")]), 2.0);
close(ev("SEARCH", vec![t("B"), t("\u{130}b")]), 2.0);
close(ev("SEARCH", vec![t("b?"), t("a\u{130}bc")]), 3.0);
close(ev("SEARCH", vec![t("i"), t("a\u{130}bc")]), 2.0);
is_err(ev("SEARCH", vec![t("b"), t("a\u{130}bc"), n(4.0)]), CellError::Value);
assert_eq!(ev("EXACT", vec![t("a"), t("A")]), Value::Bool(false));
assert_eq!(ev("EXACT", vec![t("a"), t("a")]), Value::Bool(true));
}