mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-24 10:46:50 -06:00
Keep '!' in structured-reference columns intact on XLSX save
Found while testing crates/xlsx/src/fmla.rs:101 (with crates/formula/src/printer.rs:181) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
06920de8da
commit
c39d2ad197
@@ -85,6 +85,28 @@ pub fn struct_ref_file(s: &StructRef) -> String {
|
||||
write_struct_ref(s, false)
|
||||
}
|
||||
|
||||
/// Splits a sheet-qualified name at its last `!` outside brackets. A `!` inside brackets
|
||||
/// belongs to a structured reference's column (`Table1[Done!]`, `Table1[#REF!]`).
|
||||
fn sheet_split(n: &str) -> Option<(&str, &str)> {
|
||||
let mut depth = 0usize;
|
||||
let mut escaped = false;
|
||||
let mut at = None;
|
||||
for (i, c) in n.char_indices() {
|
||||
if escaped {
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
match c {
|
||||
'\'' if depth > 0 => escaped = true,
|
||||
'[' => depth = depth.saturating_add(1),
|
||||
']' => depth = depth.saturating_sub(1),
|
||||
'!' if depth == 0 => at = Some(i),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
at.and_then(|i| Some((n.get(..i)?, n.get(i + 1..)?)))
|
||||
}
|
||||
|
||||
fn write_struct_ref(s: &StructRef, at_shorthand: bool) -> String {
|
||||
let esc = |c: &str| c.replace('\'', "''").replace('[', "'[").replace(']', "']").replace('#', "'#");
|
||||
let item = |i: &StructItem| match i {
|
||||
@@ -178,7 +200,7 @@ fn write_expr(s: &mut String, e: &Expr, r1c1: Option<CellRef>) {
|
||||
Some(at) => s.push_str(&reference_r1c1(r, at)),
|
||||
None => s.push_str(&reference_a1(r)),
|
||||
},
|
||||
Expr::Name(n) => match n.rsplit_once('!') {
|
||||
Expr::Name(n) => match sheet_split(n) {
|
||||
// A sheet-qualified name is stored with the bare sheet name (`My Sheet!Total`): quote it so it parses back.
|
||||
Some((sheet, name)) => {
|
||||
s.push_str("e_sheet(sheet));
|
||||
|
||||
@@ -183,6 +183,18 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn struct_ref_column_with_bang() {
|
||||
// A `!` inside the brackets is part of the column name, not a sheet separator.
|
||||
assert_eq!(to_file(&Formula::new("SUM(Table1[Done!])")), "SUM(Table1[Done!])");
|
||||
assert_eq!(text_to_file("=Table1[[#This Row],[a!b]]"), "Table1[[#This Row],[a!b]]");
|
||||
// `Table1[#REF!]` was found by the command-sequence fuzzer (seed 40).
|
||||
for text in ["SUM(Table1[Done!])", "Table1[#REF!]", "Table1[@[x!]]", "Sheet2!A1+Table1[[#All],[a!b]]"] {
|
||||
let f = Formula::new(text);
|
||||
assert_eq!(from_file(&to_file(&f)), f.text);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prefixes() {
|
||||
assert_eq!(to_file(&Formula::new("XLOOKUP(1,A:A,B:B)+SUM(A1)")), "_xlfn.XLOOKUP(1,A:A,B:B)+SUM(A1)");
|
||||
|
||||
Reference in New Issue
Block a user