diff --git a/crates/engine/src/cmd/formulas.rs b/crates/engine/src/cmd/formulas.rs index 7718279b2..15216492b 100644 --- a/crates/engine/src/cmd/formulas.rs +++ b/crates/engine/src/cmd/formulas.rs @@ -664,8 +664,12 @@ fn error_checking(s: &mut Session, _: &Json) -> Result { } } } - let circ: Vec = - d.calc.circular.iter().map(|(si, c)| format!("{}!{}", d.wb.sheet(*si).map(|s| s.name.clone()).unwrap_or_default(), c.a1())).collect(); + let circ: Vec = d + .calc + .circular + .iter() + .map(|(si, c)| format!("{}!{}", d.wb.sheet(*si).map(|s| gridcraft_formula::quote_sheet(&s.name)).unwrap_or_default(), c.a1())) + .collect(); Ok(json!({"errors": out, "circular": circ})) } diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index f4c47bb62..70552b21a 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -2784,6 +2784,23 @@ fn calculation_options_enable_iterative_calculation() { near(&s, "A1", 10.0); } +#[test] +fn error_checking_quotes_sheet_names_in_circular_references() { + let mut s = s(); + s.execute("home.insertSheet", json!({"name": "Bob's Q1"})).unwrap(); + s.execute("cell.set", json!({"cell": "B2", "input": "=B2+1"})).unwrap(); + let r = s.execute("formulas.errorChecking", json!({})).unwrap(); + assert_eq!(r["circular"], json!(["'Bob''s Q1'!B2"])); + // The reference reads back in a formula and in Go To. + let reference = r["circular"][0].as_str().unwrap().to_string(); + s.execute("sheet.activate", json!({"sheet": 0})).unwrap(); + s.execute("cell.set", json!({"cell": "A1", "input": format!("=ROW({reference})")})).unwrap(); + assert_eq!(v(&s, "A1"), Value::Number(2.0)); + s.execute("edit.goTo", json!({"reference": reference})).unwrap(); + let d = s.doc().unwrap(); + assert_eq!((d.wb.active_sheet, d.selection.active.a1()), (1, "B2".to_string())); +} + fn chart_data(s: &mut Session) { s.execute( "range.setValues", diff --git a/crates/ui-egui/src/tabs.rs b/crates/ui-egui/src/tabs.rs index bc6fcc1d8..10b0c3bc0 100644 --- a/crates/ui-egui/src/tabs.rs +++ b/crates/ui-egui/src/tabs.rs @@ -282,7 +282,7 @@ pub fn status_bar(app: &mut SheetApp, ui: &mut Ui) { if !d.calc.circular.is_empty() { ui.add_space(12.0); let (si, c) = d.calc.circular[0]; - let name = d.wb.sheet(si).map(|s| s.name.clone()).unwrap_or_default(); + let name = d.wb.sheet(si).map(|s| gridcraft_engine::formula::quote_sheet(&s.name)).unwrap_or_default(); ui.label(egui::RichText::new(format!("Circular References: {name}!{}", c.a1())).font(theme::ui_font(12.5)).color(t.danger)); } if app.session.clipboard.is_some() && app.editor.is_none() { diff --git a/crates/ui-egui/tests/it/interaction.rs b/crates/ui-egui/tests/it/interaction.rs index 10841c7bd..fbe7089e6 100644 --- a/crates/ui-egui/tests/it/interaction.rs +++ b/crates/ui-egui/tests/it/interaction.rs @@ -922,6 +922,15 @@ fn status_bar_shows_macro_badge() { h.run_steps(2); } +#[test] +fn status_bar_quotes_the_sheet_name_of_a_circular_reference() { + let mut s = blank(); + s.execute("home.insertSheet", json!({"name": "Bob's Q1"})).unwrap(); + s.execute("cell.set", json!({"cell": "B2", "input": "=B2+1"})).unwrap(); + let h = harness(s); + assert!(h.query_by_label("Circular References: 'Bob''s Q1'!B2").is_some(), "the status bar names the cell as a reference"); +} + #[test] fn alt_f8_opens_the_macros_dialog() { let mut h = harness(blank());