From 12ef20b8690451b1b66dc72f18b309bcb777a322 Mon Sep 17 00:00:00 2001 From: u9g Date: Wed, 2 Mar 2022 18:31:50 -0500 Subject: [PATCH] Spreadsheet: Improve R function to support all of the examples --- Base/res/js/Spreadsheet/runtime.js | 34 +++++------ .../Applications/Spreadsheet/Tests/basic.js | 59 +++++++++++++++++++ 2 files changed, 76 insertions(+), 17 deletions(-) diff --git a/Base/res/js/Spreadsheet/runtime.js b/Base/res/js/Spreadsheet/runtime.js index 58af22548b..dc1b7fda01 100644 --- a/Base/res/js/Spreadsheet/runtime.js +++ b/Base/res/js/Spreadsheet/runtime.js @@ -277,10 +277,13 @@ class Range { toString() { const endingRow = this.endingRow ?? ""; - return `R\`${this.startingColumnName}${this.startingRow}:${this.endingColumnName}${endingRow}:${this.columnStep}:${this.rowStep}\``; + const showSteps = this.rowStep !== 1 || this.columnStep !== 1; + const steps = showSteps ? `:${this.columnStep}:${this.rowStep}` : ""; + return `R\`${this.startingColumnName}${this.startingRow}:${this.endingColumnName}${endingRow}${steps}\``; } } +const R_FORMAT = /^([a-zA-Z_]+)(?:(\d+):([a-zA-Z_]+)(\d+)?(?::(\d+):(\d+))?)?$/; function R(fmt, ...args) { if (args.length !== 0) throw new TypeError("R`` format must be a literal"); // done because: @@ -288,23 +291,20 @@ function R(fmt, ...args) { // myFunc("ABC") => ""ABC"" // myFunc`ABC` => "["ABC"]" if (Array.isArray(fmt)) fmt = fmt[0]; - const parts = fmt.split(":"); - if (parts.length !== 2 && parts.length !== 4) - throw new Error("Invalid Format. Expected Format: R`A0:A1` or R`A0:A2:1:2`"); - // ColRow:Col(Row)?(:ColStep:RowStep)? - const start = thisSheet.parse_cell_name(parts[0]); - let end = parts[1]; - if (/^[a-zA-Z_]+$/.test(end)) end = { column: end, row: undefined }; - else end = thisSheet.parse_cell_name(parts[1]); - parts[2] ??= 1; - parts[3] ??= 1; + if (!R_FORMAT.test(fmt)) + throw new Error("Invalid Format. Expected Format: R`A` or R`A0:A1` or R`A0:A2:1:2`"); + // Format: Col(Row:Col(Row)?(:ColStep:RowStep)?)? + // Ignore the first element of the match array as that will be the whole match. + const [, ...matches] = fmt.match(R_FORMAT); + const [startCol, startRow, endCol, endRow, colStep, rowStep] = matches; return new Range( - start.column, - end.column, - start.row, - end.row, - integer(parts[2]), - integer(parts[3]) + startCol, + endCol ?? startCol, + integer(startRow ?? 0), + // Don't make undefined an integer, because then it becomes 0. + !!endRow ? integer(endRow) : endRow, + integer(colStep ?? 1), + integer(rowStep ?? 1) ); } diff --git a/Userland/Applications/Spreadsheet/Tests/basic.js b/Userland/Applications/Spreadsheet/Tests/basic.js index 85b6fb3100..93011cb41d 100644 --- a/Userland/Applications/Spreadsheet/Tests/basic.js +++ b/Userland/Applications/Spreadsheet/Tests/basic.js @@ -145,3 +145,62 @@ describe("Range", () => { ).toEqual(",,,,,"); }); }); + +describe("R function", () => { + const workbook = createWorkbook(); + const sheet = createSheet(workbook, "Sheet 1"); + sheet.makeCurrent(); + /* + A B + +++ + 0+1 1 + 1+2 4 + 2+3 9 + */ + for (const row of ["A", "B"]) { + for (let col of [0, 1, 2]) { + sheet.setCell(row, col++, row === "A" ? col : Math.pow(col, 2)); + } + } + sheet.focusCell("A", 0); + + test("Check for correctness: R`A0:A`", () => { + const range = R`A0:A`; + expect(range.toString()).toEqual("R`A0:A`"); + expect(count(range)).toEqual(3); + expect(sum(range)).toEqual(6); + }); + test("Check for correctness: R`A`", () => { + const range = R`A`; + expect(range.toString()).toEqual("R`A0:A`"); + expect(count(range)).toEqual(3); + expect(sum(range)).toEqual(6); + }); + test("Check for correctness: R`A0:B`", () => { + const range = R`A0:B`; + expect(range.toString()).toEqual("R`A0:B`"); + expect(count(range)).toEqual(6); + expect(sum(range)).toEqual(20); + }); + test("Check for correctness: R`A0:B0`", () => { + const range = R`A0:B0`; + expect(range.toString()).toEqual("R`A0:B0`"); + expect(count(range)).toEqual(2); + expect(sum(range)).toEqual(2); + }); + test("Check for correctness: R`A0:B:2:1`", () => { + const range = R`A0:B2:1:2`; + expect(range.toString()).toEqual("R`A0:B2:1:2`"); + expect(range.toArray().toString()).toEqual( + ",,," + ); + expect(count(range)).toEqual(4); + expect(sum(range)).toEqual(14); + }); + test("R`B`", () => { + const range = R`B`; + expect(range.toString()).toEqual("R`B0:B`"); + expect(count(range)).toEqual(3); + expect(sum(range)).toEqual(14); + }); +});