From a8a4c5dab3a0684bb7d5326e17c33bf950e5abcc Mon Sep 17 00:00:00 2001 From: nihilist-narwhal <240798405+nihilist-narwhal@users.noreply.github.com> Date: Sat, 10 Oct 2026 03:56:35 -0500 Subject: [PATCH] Conditional formatting: accept Excel's operator names, refuse unknown ones home.conditionalFormat took any operator it didn't know as "greater", so a Cell Value rule given Excel's own names (lessThan, greaterThan, lessThanOrEqual, greaterThanOrEqual, as in the file format and VBA) silently became "greater than". Those names are now accepted, as data validation already does, and anything else is an error. --- crates/engine/src/cf.rs | 14 ++++++++++++++ crates/engine/src/cmd/insert.rs | 10 ++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/crates/engine/src/cf.rs b/crates/engine/src/cf.rs index 6640c7a..9979bff 100644 --- a/crates/engine/src/cf.rs +++ b/crates/engine/src/cf.rs @@ -392,4 +392,18 @@ mod tests { // No blanks: neither the empty cell nor the one holding a space. assert_eq!([look("A1"), look("A2"), look("A4")].map(border), [true, false, false]); } + + #[test] + fn cell_value_operator_names() { + let mut s = crate::Session::new(); + s.new_workbook(); + s.execute("range.setValues", json!({"range": "A1", "values": [[3], [7]]})).unwrap(); + let rule = |op: &str| json!({"range": "A1:A2", "rule": {"type": "cellIs", "operator": op, "value": "5", "preset": "lightRedFill"}}); + s.execute("home.conditionalFormat", rule("lessThan")).unwrap(); + assert!(s.execute("home.conditionalFormat", rule("smaller")).is_err()); + let d = s.doc().unwrap(); + let mut cache = CfCache::new(); + let mut look = |a: &str| cache.look(&d.wb, 0, CellRef::parse(a).unwrap()).and_then(|l| l.style).is_some(); + assert_eq!([look("A1"), look("A2")], [true, false]); + } } diff --git a/crates/engine/src/cmd/insert.rs b/crates/engine/src/cmd/insert.rs index 4c96005..3fc6cff 100644 --- a/crates/engine/src/cmd/insert.rs +++ b/crates/engine/src/cmd/insert.rs @@ -1157,15 +1157,17 @@ fn add_cf(s: &mut Session, p: &Json) -> Result { }; let rule = match kind.as_str() { "cellIs" => { + // Excel's file and VBA names (`lessThan`, …) are accepted too. let op = match g("operator").as_deref().unwrap_or("greater") { "between" => CfOperator::Between, "notBetween" => CfOperator::NotBetween, "equal" => CfOperator::Equal, "notEqual" => CfOperator::NotEqual, - "less" => CfOperator::Less, - "greaterOrEqual" => CfOperator::GreaterOrEqual, - "lessOrEqual" => CfOperator::LessOrEqual, - _ => CfOperator::Greater, + "less" | "lessThan" => CfOperator::Less, + "greaterOrEqual" | "greaterThanOrEqual" => CfOperator::GreaterOrEqual, + "lessOrEqual" | "lessThanOrEqual" => CfOperator::LessOrEqual, + "greater" | "greaterThan" => CfOperator::Greater, + other => return Err(bad("home.conditionalFormat", format!("unknown operator `{other}`"))), }; let a = rule.get("value").map(super::edit::json_to_input).unwrap_or_default(); let b = rule.get("value2").map(super::edit::json_to_input);