|
| 1 | +// FIXME: Figure out a way to document non-function entities too. |
| 2 | +class Position { |
| 3 | + constructor(column, row, sheet) { |
| 4 | + this.column = column; |
| 5 | + this.row = row; |
| 6 | + this.sheet = sheet ?? thisSheet; |
| 7 | + this.name = `${column}${row}`; |
| 8 | + } |
| 9 | + |
| 10 | + static from_name(name) { |
| 11 | + let sheet = thisSheet; |
| 12 | + let obj = sheet.parse_cell_name(name); |
| 13 | + return new Position(obj.column, obj.row, sheet); |
| 14 | + } |
| 15 | + |
| 16 | + up(how_many) { |
| 17 | + how_many = how_many ?? 1; |
| 18 | + const row = Math.max(0, this.row - how_many); |
| 19 | + return new Position(this.column, row, this.sheet); |
| 20 | + } |
| 21 | + |
| 22 | + down(how_many) { |
| 23 | + how_many = how_many ?? 1; |
| 24 | + const row = Math.max(0, this.row + how_many); |
| 25 | + return new Position(this.column, row, this.sheet); |
| 26 | + } |
| 27 | + |
| 28 | + left(how_many) { |
| 29 | + how_many = how_many ?? 1; |
| 30 | + const column = Math.min( |
| 31 | + "Z".charCodeAt(0), |
| 32 | + Math.max("A".charCodeAt(0), this.column.charCodeAt(0) - how_many) |
| 33 | + ); |
| 34 | + return new Position(String.fromCharCode(column), this.row, this.sheet); |
| 35 | + } |
| 36 | + |
| 37 | + right(how_many) { |
| 38 | + how_many = how_many ?? 1; |
| 39 | + const column = Math.min( |
| 40 | + "Z".charCodeAt(0), |
| 41 | + Math.max("A".charCodeAt(0), this.column.charCodeAt(0) + how_many) |
| 42 | + ); |
| 43 | + return new Position(String.fromCharCode(column), this.row, this.sheet); |
| 44 | + } |
| 45 | + |
| 46 | + with_column(value) { |
| 47 | + return new Position(value, this.row, this.sheet); |
| 48 | + } |
| 49 | + |
| 50 | + with_row(value) { |
| 51 | + return new Position(this.column, value, this.sheet); |
| 52 | + } |
| 53 | + |
| 54 | + in_sheet(the_sheet) { |
| 55 | + return new Position(this.column, this.row, sheet(the_sheet)); |
| 56 | + } |
| 57 | + |
| 58 | + value() { |
| 59 | + return this.sheet[this.name]; |
| 60 | + } |
| 61 | + |
| 62 | + valueOf() { |
| 63 | + return value(); |
| 64 | + } |
| 65 | + |
| 66 | + toString() { |
| 67 | + return `<Cell at ${this.name}>`; |
| 68 | + } |
| 69 | +} |
| 70 | + |
1 | 71 | function range(start, end, columnStep, rowStep) {
|
2 | 72 | columnStep = integer(columnStep ?? 1);
|
3 | 73 | rowStep = integer(rowStep ?? 1);
|
4 |
| - start = parse_cell_name(start) ?? { column: "A", row: 0 }; |
5 |
| - end = parse_cell_name(end) ?? start; |
| 74 | + if (!(start instanceof Position)) { |
| 75 | + start = parse_cell_name(start) ?? { column: "A", row: 0 }; |
| 76 | + } |
| 77 | + if (!(end instanceof Position)) { |
| 78 | + end = parse_cell_name(end) ?? start; |
| 79 | + } |
6 | 80 |
|
7 | 81 | if (end.column.length > 1 || start.column.length > 1)
|
8 | 82 | throw new TypeError("Only single-letter column names are allowed (TODO)");
|
@@ -172,6 +246,21 @@ function stddev(cells) {
|
172 | 246 | return Math.sqrt(variance(cells));
|
173 | 247 | }
|
174 | 248 |
|
| 249 | +// Lookup |
| 250 | + |
| 251 | +function row() { |
| 252 | + return thisSheet.current_cell_position().row; |
| 253 | +} |
| 254 | + |
| 255 | +function column() { |
| 256 | + return thisSheet.current_cell_position().column; |
| 257 | +} |
| 258 | + |
| 259 | +function here() { |
| 260 | + const position = current_cell_position(); |
| 261 | + return new Position(position.column, position.row, thisSheet); |
| 262 | +} |
| 263 | + |
175 | 264 | // Cheat the system and add documentation
|
176 | 265 | range.__documentation = JSON.stringify({
|
177 | 266 | name: "range",
|
@@ -382,3 +471,42 @@ stddev.__documentation = JSON.stringify({
|
382 | 471 | 'stddev(range("A0", "C4"))': "Calculate the standard deviation of the values in A0:C4",
|
383 | 472 | },
|
384 | 473 | });
|
| 474 | + |
| 475 | +row.__documentation = JSON.stringify({ |
| 476 | + name: "row", |
| 477 | + argc: 0, |
| 478 | + argnames: [], |
| 479 | + doc: "Returns the row number of the current cell", |
| 480 | + examples: {}, |
| 481 | +}); |
| 482 | + |
| 483 | +column.__documentation = JSON.stringify({ |
| 484 | + name: "column", |
| 485 | + argc: 0, |
| 486 | + argnames: [], |
| 487 | + doc: "Returns the column name of the current cell", |
| 488 | + examples: {}, |
| 489 | +}); |
| 490 | + |
| 491 | +here.__documentation = JSON.stringify({ |
| 492 | + name: "here", |
| 493 | + argc: 0, |
| 494 | + argnames: [], |
| 495 | + doc: |
| 496 | + "Returns an object representing the current cell's position, see `Position` below.\n\n" + |
| 497 | + "## Position\na `Position` is an object representing a given cell position in a given sheet.\n" + |
| 498 | + "### Methods:\n- `up(count = 1)`: goes up count cells, or returns the top position if at the top\n" + |
| 499 | + "- `down(count = 1)`: goes down count cells\n" + |
| 500 | + "- `left(count = 1)`: Goes left count cells, or returns the leftmost position if the edge\n" + |
| 501 | + "- `right(count = 1)`: Goes right count cells.\n" + |
| 502 | + "- `with_row(row)`: Returns a Position with its column being this object's, and its row being the provided the value.\n" + |
| 503 | + "- `with_column(column)`: Similar to `with_row()`, but changes the column instead.\n" + |
| 504 | + "- `in_sheet(the_sheet)`: Returns a Position with the same column and row as this one, but with its sheet being `the_sheet`.\n" + |
| 505 | + "- `value()`: Returns the value at the position which it represents, in the object's sheet (current sheet by default).\n\n" + |
| 506 | + "**NOTE**: Currently only supports single-letter column names", |
| 507 | + examples: { |
| 508 | + "here().up().value()": "Get the value of the cell above this one", |
| 509 | + "here().up().with_column('A')": |
| 510 | + "Get a Position above this one in column A, for instance, evaluates to A2 if run in B3.", |
| 511 | + }, |
| 512 | +}); |
0 commit comments