#1408: match a Greek final sigma in node.find filters

Lowercasing a name ending in Σ gives ς, while a query Σ on its own lowercases to σ, so the
substring check missed it (CodeRabbit). Both sides now fold ς to σ.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Mert Dede
2026-10-27 08:33:28 +03:00
co-authored by Claude Opus 5.5
parent 57c1133a34
commit 9d7c435f6b
2 changed files with 22 additions and 6 deletions
+12 -6
View File
@@ -46,10 +46,16 @@ const LAYER_TYPES: [&str; 16] = [
"SLICE",
];
/// Lowercase for `node.find`'s substring filters. Greek final sigma becomes σ on both sides, so a
/// query lowercased on its own (`Σ` to `σ`) still matches a name ending in it (`ΟΣ` to `ος`).
fn fold(s: &str) -> String {
s.to_lowercase().replace('ς', "σ")
}
fn node_find(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "node.find";
let filter = |key| -> Result<Option<String>> {
get(p, key).map(|v| v.as_str().map(str::to_lowercase).ok_or_else(|| bad(cmd, format!("`{key}` must be a string")))).transpose()
get(p, key).map(|v| v.as_str().map(fold).ok_or_else(|| bad(cmd, format!("`{key}` must be a string")))).transpose()
};
let name = filter("name")?;
let text = filter("text")?;
@@ -77,12 +83,12 @@ fn node_find(s: &mut Session, p: &Value) -> Result<Value> {
};
let n = doc.get(id)?;
stack.push(n.children.iter());
if name.as_ref().is_some_and(|q| !n.name.to_lowercase().contains(q))
if name.as_ref().is_some_and(|q| !fold(&n.name).contains(q))
|| kind.as_ref().is_some_and(|q| !n.kind.type_name().eq_ignore_ascii_case(q))
|| text.as_ref().is_some_and(|q| !matches!(&n.kind, NodeKind::Text(t) if t.characters.to_lowercase().contains(q)))
|| component.as_ref().is_some_and(|q| {
!matches!(&n.kind, NodeKind::Instance { component, .. } if doc.node(*component).is_some_and(|c| c.name.to_lowercase().contains(q)))
})
|| text.as_ref().is_some_and(|q| !matches!(&n.kind, NodeKind::Text(t) if fold(&t.characters).contains(q)))
|| component.as_ref().is_some_and(
|q| !matches!(&n.kind, NodeKind::Instance { component, .. } if doc.node(*component).is_some_and(|c| fold(&c.name).contains(q))),
)
{
continue;
}
+10
View File
@@ -35,6 +35,16 @@ fn node_find_filters_are_literal_case_insensitive_and_anded() {
found(&mut s, json!({"name": ".*"}), &[]);
}
#[test]
fn node_find_matches_a_greek_final_sigma_both_ways() {
let mut s = Session::new();
let page = s.page().unwrap();
let os = add(&mut s, page, NodeKind::Text(TextData::new("ΛΟΓΟΣ")), "ΟΣ");
found(&mut s, json!({"name": "Σ"}), &[os]);
found(&mut s, json!({"name": "ς"}), &[os]);
found(&mut s, json!({"text": "λογοσ"}), &[os]);
}
#[test]
fn node_find_scope_is_preorder_strict_descendants_in_stored_order() {
let mut s = Session::new();