diff --git a/crates/formula/src/lexer.rs b/crates/formula/src/lexer.rs index f85fcde93..83ea78a86 100644 --- a/crates/formula/src/lexer.rs +++ b/crates/formula/src/lexer.rs @@ -338,12 +338,16 @@ pub fn tokenize_locale(src: &str, locale: crate::FormulaLocale) -> Result 0 && c == locale.array_column_separator())) { k += 1; } - k > j + 1 && ch(k) == Some('!') + // `Sheet1!A1:Sheet1!B2` is a range whose second end repeats the sheet, not a span. + let repeats_sheet = is_range_end(&w) + && matches!(out.last(), Some(Token { tok: Tok::Sheet(p, None), .. }) + if chars.get(j + 1..k).is_some_and(|s| s.iter().map(|c| c.1).eq(p.chars()))); + k > j + 1 && ch(k) == Some('!') && !repeats_sheet } { let mut k = j + 1; while ch(k).is_some_and(|c| is_word_char(c) && !(array_depth > 0 && c == locale.array_column_separator())) { diff --git a/crates/formula/src/tests.rs b/crates/formula/src/tests.rs index 822554923..2318a8b29 100644 --- a/crates/formula/src/tests.rs +++ b/crates/formula/src/tests.rs @@ -5,6 +5,13 @@ fn rt(src: &str) -> String { print(&parse(src).unwrap_or_else(|e| panic!("{src}: {e}"))) } +#[test] +fn range_end_repeating_sheet_name_round_trips() { + assert_eq!(rt("'a'!A1:'a'!B2"), "a!A1:a!B2"); + assert_eq!(rt("Sheet1!A1:Sheet1!B2"), "Sheet1!A1:Sheet1!B2"); + assert_eq!(rt("SUM(a!A1:a!B2)"), "SUM(a!A1:a!B2)"); +} + #[test] fn quoted_sheet_qualified_name_keeps_quotes() { assert_eq!(rt("'My Sheet'!Total"), "'My Sheet'!Total"); @@ -503,3 +510,9 @@ fn number_literal_too_large_for_f64_is_a_parse_error() { } assert!(parse("1E+308").is_ok()); } + +#[test] +fn three_d_span_and_cell_start_still_lex_as_span() { + assert_eq!(rt("SUM(Sheet1:Sheet3!A1)"), "SUM(Sheet1:Sheet3!A1)"); + assert_eq!(rt("SUM(Q1:Sheet3!A1)"), "SUM('Q1:Sheet3'!A1)"); +}