Full document, spreadsheet, slideshow, and diagram tooling
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request 'refactor/formula-tooltip-decompose' (#294) from refactor/formula-tooltip-decompose into main

scott 9f2ab36e 948ee2f4

+946 -933
+8 -933
src/sheets/formula-tooltip.ts
··· 4 4 * When typing inside a function call, shows a tooltip with the full 5 5 * function signature, highlighting the current parameter based on 6 6 * cursor position (counting commas and parens). 7 + * 8 + * Function metadata lives in ./function-metadata.ts (pure data). 9 + * This module provides detection logic and tooltip rendering. 7 10 */ 8 11 9 - import type { FunctionMetadataEntry, DetectedFunction, FunctionParam } from './types.js'; 10 - 11 - /** 12 - * Complete function metadata for all 57 supported functions. 13 - * Each entry has a description and per-parameter info. 14 - */ 15 - export const FUNCTION_METADATA: Record<string, FunctionMetadataEntry> = { 16 - // --- Math & Stats --- 17 - SUM: { 18 - desc: 'Adds all numbers in a range', 19 - params: [ 20 - { name: 'range1', desc: 'First range to sum', required: true }, 21 - { name: 'range2', desc: 'Additional ranges to sum', required: false }, 22 - ], 23 - }, 24 - AVERAGE: { 25 - desc: 'Returns the arithmetic mean of the arguments', 26 - params: [ 27 - { name: 'range1', desc: 'First range to average', required: true }, 28 - { name: 'range2', desc: 'Additional ranges', required: false }, 29 - ], 30 - }, 31 - COUNT: { 32 - desc: 'Counts the number of cells that contain numbers', 33 - params: [ 34 - { name: 'range1', desc: 'First range to count', required: true }, 35 - { name: 'range2', desc: 'Additional ranges', required: false }, 36 - ], 37 - }, 38 - COUNTA: { 39 - desc: 'Counts the number of non-empty cells', 40 - params: [ 41 - { name: 'range1', desc: 'First range to count', required: true }, 42 - { name: 'range2', desc: 'Additional ranges', required: false }, 43 - ], 44 - }, 45 - MIN: { 46 - desc: 'Returns the smallest value in a set of numbers', 47 - params: [ 48 - { name: 'range1', desc: 'First range to evaluate', required: true }, 49 - { name: 'range2', desc: 'Additional ranges', required: false }, 50 - ], 51 - }, 52 - MAX: { 53 - desc: 'Returns the largest value in a set of numbers', 54 - params: [ 55 - { name: 'range1', desc: 'First range to evaluate', required: true }, 56 - { name: 'range2', desc: 'Additional ranges', required: false }, 57 - ], 58 - }, 59 - MEDIAN: { 60 - desc: 'Returns the median of the given numbers', 61 - params: [ 62 - { name: 'range1', desc: 'First range of values', required: true }, 63 - { name: 'range2', desc: 'Additional ranges', required: false }, 64 - ], 65 - }, 66 - STDEV: { 67 - desc: 'Estimates standard deviation based on a sample', 68 - params: [ 69 - { name: 'range1', desc: 'First range of values', required: true }, 70 - { name: 'range2', desc: 'Additional ranges', required: false }, 71 - ], 72 - }, 73 - ADD: { 74 - desc: 'Returns the sum of two values (equivalent to value1 + value2)', 75 - params: [ 76 - { name: 'value1', desc: 'The first addend', required: true }, 77 - { name: 'value2', desc: 'The second addend', required: true }, 78 - ], 79 - }, 80 - MINUS: { 81 - desc: 'Returns the difference of two values (equivalent to value1 - value2)', 82 - params: [ 83 - { name: 'value1', desc: 'The value to subtract from', required: true }, 84 - { name: 'value2', desc: 'The value to subtract', required: true }, 85 - ], 86 - }, 87 - MULTIPLY: { 88 - desc: 'Returns the product of two values (equivalent to factor1 * factor2)', 89 - params: [ 90 - { name: 'factor1', desc: 'The first factor', required: true }, 91 - { name: 'factor2', desc: 'The second factor', required: true }, 92 - ], 93 - }, 94 - DIVIDE: { 95 - desc: 'Returns the result of dividing one value by another (equivalent to dividend / divisor)', 96 - params: [ 97 - { name: 'dividend', desc: 'The number to be divided', required: true }, 98 - { name: 'divisor', desc: 'The number to divide by', required: true }, 99 - ], 100 - }, 101 - ABS: { 102 - desc: 'Returns the absolute value of a number', 103 - params: [ 104 - { name: 'number', desc: 'The number to get the absolute value of', required: true }, 105 - ], 106 - }, 107 - ROUND: { 108 - desc: 'Rounds a number to a specified number of digits', 109 - params: [ 110 - { name: 'number', desc: 'The number to round', required: true }, 111 - { name: 'num_digits', desc: 'Number of decimal places (default 0)', required: false }, 112 - ], 113 - }, 114 - ROUNDUP: { 115 - desc: 'Rounds a number up, away from zero', 116 - params: [ 117 - { name: 'number', desc: 'The number to round up', required: true }, 118 - { name: 'num_digits', desc: 'Number of decimal places (default 0)', required: false }, 119 - ], 120 - }, 121 - ROUNDDOWN: { 122 - desc: 'Rounds a number down, toward zero', 123 - params: [ 124 - { name: 'number', desc: 'The number to round down', required: true }, 125 - { name: 'num_digits', desc: 'Number of decimal places (default 0)', required: false }, 126 - ], 127 - }, 128 - INT: { 129 - desc: 'Rounds a number down to the nearest integer', 130 - params: [ 131 - { name: 'number', desc: 'The number to round down', required: true }, 132 - ], 133 - }, 134 - MOD: { 135 - desc: 'Returns the remainder after dividing a number by a divisor', 136 - params: [ 137 - { name: 'number', desc: 'The number to divide', required: true }, 138 - { name: 'divisor', desc: 'The divisor', required: true }, 139 - ], 140 - }, 141 - POWER: { 142 - desc: 'Returns a number raised to a power', 143 - params: [ 144 - { name: 'number', desc: 'The base number', required: true }, 145 - { name: 'power', desc: 'The exponent', required: true }, 146 - ], 147 - }, 148 - SQRT: { 149 - desc: 'Returns the positive square root of a number', 150 - params: [ 151 - { name: 'number', desc: 'The number to take the square root of', required: true }, 152 - ], 153 - }, 154 - LOG: { 155 - desc: 'Returns the logarithm of a number to a specified base', 156 - params: [ 157 - { name: 'number', desc: 'The positive number to take the log of', required: true }, 158 - { name: 'base', desc: 'The base of the logarithm (default 10)', required: false }, 159 - ], 160 - }, 161 - LN: { 162 - desc: 'Returns the natural logarithm of a number', 163 - params: [ 164 - { name: 'number', desc: 'The positive number to take the natural log of', required: true }, 165 - ], 166 - }, 167 - EXP: { 168 - desc: 'Returns e raised to the power of a number', 169 - params: [ 170 - { name: 'number', desc: 'The exponent applied to the base e', required: true }, 171 - ], 172 - }, 173 - PI: { 174 - desc: 'Returns the value of pi (3.14159...)', 175 - params: [], 176 - }, 177 - RAND: { 178 - desc: 'Returns a random number between 0 and 1', 179 - params: [], 180 - }, 12 + import type { DetectedFunction } from './types.js'; 13 + import { FUNCTION_METADATA } from './function-metadata.js'; 181 14 182 - // --- Logical --- 183 - IF: { 184 - desc: 'Returns one value if a condition is true and another if false', 185 - params: [ 186 - { name: 'condition', desc: 'The logical test to evaluate', required: true }, 187 - { name: 'value_if_true', desc: 'Value returned when condition is true', required: true }, 188 - { name: 'value_if_false', desc: 'Value returned when condition is false', required: false }, 189 - ], 190 - }, 191 - AND: { 192 - desc: 'Returns TRUE if all arguments are true', 193 - params: [ 194 - { name: 'logical1', desc: 'First condition to evaluate', required: true }, 195 - { name: 'logical2', desc: 'Additional conditions', required: false }, 196 - ], 197 - }, 198 - OR: { 199 - desc: 'Returns TRUE if any argument is true', 200 - params: [ 201 - { name: 'logical1', desc: 'First condition to evaluate', required: true }, 202 - { name: 'logical2', desc: 'Additional conditions', required: false }, 203 - ], 204 - }, 205 - NOT: { 206 - desc: 'Reverses the logic of its argument', 207 - params: [ 208 - { name: 'logical', desc: 'The value or expression to negate', required: true }, 209 - ], 210 - }, 211 - IFERROR: { 212 - desc: 'Returns a value if no error, otherwise returns an alternate value', 213 - params: [ 214 - { name: 'value', desc: 'The value to check for an error', required: true }, 215 - { name: 'value_if_error', desc: 'Value to return if an error is found', required: true }, 216 - ], 217 - }, 218 - 219 - // --- Text --- 220 - CONCATENATE: { 221 - desc: 'Joins several text strings into one', 222 - params: [ 223 - { name: 'text1', desc: 'First text string', required: true }, 224 - { name: 'text2', desc: 'Additional text strings to join', required: false }, 225 - ], 226 - }, 227 - LEN: { 228 - desc: 'Returns the number of characters in a text string', 229 - params: [ 230 - { name: 'text', desc: 'The text string to measure', required: true }, 231 - ], 232 - }, 233 - LEFT: { 234 - desc: 'Returns the leftmost characters from a text string', 235 - params: [ 236 - { name: 'text', desc: 'The source text string', required: true }, 237 - { name: 'num_chars', desc: 'Number of characters to extract (default 1)', required: false }, 238 - ], 239 - }, 240 - RIGHT: { 241 - desc: 'Returns the rightmost characters from a text string', 242 - params: [ 243 - { name: 'text', desc: 'The source text string', required: true }, 244 - { name: 'num_chars', desc: 'Number of characters to extract (default 1)', required: false }, 245 - ], 246 - }, 247 - MID: { 248 - desc: 'Returns a specific number of characters from a text string', 249 - params: [ 250 - { name: 'text', desc: 'The source text string', required: true }, 251 - { name: 'start_num', desc: 'Position of the first character (1-based)', required: true }, 252 - { name: 'num_chars', desc: 'Number of characters to extract', required: true }, 253 - ], 254 - }, 255 - UPPER: { 256 - desc: 'Converts text to uppercase', 257 - params: [ 258 - { name: 'text', desc: 'The text to convert', required: true }, 259 - ], 260 - }, 261 - LOWER: { 262 - desc: 'Converts text to lowercase', 263 - params: [ 264 - { name: 'text', desc: 'The text to convert', required: true }, 265 - ], 266 - }, 267 - TRIM: { 268 - desc: 'Removes leading and trailing spaces from text', 269 - params: [ 270 - { name: 'text', desc: 'The text to trim', required: true }, 271 - ], 272 - }, 273 - SUBSTITUTE: { 274 - desc: 'Replaces occurrences of old text with new text in a string', 275 - params: [ 276 - { name: 'text', desc: 'The text containing characters to replace', required: true }, 277 - { name: 'old_text', desc: 'The text to find and replace', required: true }, 278 - { name: 'new_text', desc: 'The replacement text', required: true }, 279 - { name: 'instance', desc: 'Which occurrence to replace (default: all)', required: false }, 280 - ], 281 - }, 282 - FIND: { 283 - desc: 'Finds the position of a text string within another (case-sensitive)', 284 - params: [ 285 - { name: 'find_text', desc: 'The text to find', required: true }, 286 - { name: 'within_text', desc: 'The text to search within', required: true }, 287 - { name: 'start_num', desc: 'Position to start searching from (default 1)', required: false }, 288 - ], 289 - }, 290 - SEARCH: { 291 - desc: 'Finds the position of a text string within another (case-insensitive)', 292 - params: [ 293 - { name: 'find_text', desc: 'The text to find', required: true }, 294 - { name: 'within_text', desc: 'The text to search within', required: true }, 295 - { name: 'start_num', desc: 'Position to start searching from (default 1)', required: false }, 296 - ], 297 - }, 298 - TEXT: { 299 - desc: 'Formats a number as text with a specified format', 300 - params: [ 301 - { name: 'value', desc: 'The number to format', required: true }, 302 - { name: 'format_text', desc: 'Format pattern (e.g. "0.00", "#,##0")', required: true }, 303 - ], 304 - }, 305 - VALUE: { 306 - desc: 'Converts a text string that represents a number to a number', 307 - params: [ 308 - { name: 'text', desc: 'The text to convert to a number', required: true }, 309 - ], 310 - }, 311 - 312 - // --- Date --- 313 - NOW: { 314 - desc: 'Returns the current date and time', 315 - params: [], 316 - }, 317 - TODAY: { 318 - desc: 'Returns the current date', 319 - params: [], 320 - }, 321 - DATE: { 322 - desc: 'Creates a date from year, month, and day components', 323 - params: [ 324 - { name: 'year', desc: 'The year (e.g. 2024)', required: true }, 325 - { name: 'month', desc: 'The month (1-12)', required: true }, 326 - { name: 'day', desc: 'The day of the month (1-31)', required: true }, 327 - ], 328 - }, 329 - YEAR: { 330 - desc: 'Returns the year of a date', 331 - params: [ 332 - { name: 'date', desc: 'The date to extract the year from', required: true }, 333 - ], 334 - }, 335 - MONTH: { 336 - desc: 'Returns the month of a date (1-12)', 337 - params: [ 338 - { name: 'date', desc: 'The date to extract the month from', required: true }, 339 - ], 340 - }, 341 - DAY: { 342 - desc: 'Returns the day of the month of a date (1-31)', 343 - params: [ 344 - { name: 'date', desc: 'The date to extract the day from', required: true }, 345 - ], 346 - }, 347 - 348 - // --- Lookup --- 349 - VLOOKUP: { 350 - desc: 'Searches first column of range for key and returns value from specified column', 351 - params: [ 352 - { name: 'lookup_value', desc: 'The value to search for', required: true }, 353 - { name: 'table_array', desc: 'Range containing the data', required: true }, 354 - { name: 'col_index', desc: 'Column number to return (1-based)', required: true }, 355 - { name: 'range_lookup', desc: 'FALSE for exact match, TRUE for approximate', required: false }, 356 - ], 357 - }, 358 - HLOOKUP: { 359 - desc: 'Searches first row of range for key and returns value from specified row', 360 - params: [ 361 - { name: 'lookup_value', desc: 'The value to search for', required: true }, 362 - { name: 'table_array', desc: 'Range containing the data', required: true }, 363 - { name: 'row_index', desc: 'Row number to return (1-based)', required: true }, 364 - { name: 'range_lookup', desc: 'FALSE for exact match, TRUE for approximate', required: false }, 365 - ], 366 - }, 367 - INDEX: { 368 - desc: 'Returns the value of a cell in a range at a given row and column', 369 - params: [ 370 - { name: 'array', desc: 'The range of cells', required: true }, 371 - { name: 'row_num', desc: 'Row number in the range (1-based)', required: true }, 372 - { name: 'col_num', desc: 'Column number in the range (1-based)', required: false }, 373 - ], 374 - }, 375 - MATCH: { 376 - desc: 'Returns the relative position of a value in a range', 377 - params: [ 378 - { name: 'lookup_value', desc: 'The value to search for', required: true }, 379 - { name: 'lookup_array', desc: 'The range to search', required: true }, 380 - { name: 'match_type', desc: '1 for less than, 0 for exact, -1 for greater than', required: false }, 381 - ], 382 - }, 383 - 384 - // --- Conditional --- 385 - SUMIF: { 386 - desc: 'Sums cells that meet a specified condition', 387 - params: [ 388 - { name: 'range', desc: 'The range to evaluate against the criteria', required: true }, 389 - { name: 'criteria', desc: 'The condition to match (e.g. ">100")', required: true }, 390 - { name: 'sum_range', desc: 'The range to sum (default: same as range)', required: false }, 391 - ], 392 - }, 393 - COUNTIF: { 394 - desc: 'Counts cells that meet a specified condition', 395 - params: [ 396 - { name: 'range', desc: 'The range to evaluate', required: true }, 397 - { name: 'criteria', desc: 'The condition to match', required: true }, 398 - ], 399 - }, 400 - AVERAGEIF: { 401 - desc: 'Averages cells that meet a specified condition', 402 - params: [ 403 - { name: 'range', desc: 'The range to evaluate against the criteria', required: true }, 404 - { name: 'criteria', desc: 'The condition to match', required: true }, 405 - { name: 'average_range', desc: 'The range to average (default: same as range)', required: false }, 406 - ], 407 - }, 408 - 409 - // --- Reference --- 410 - INDIRECT: { 411 - desc: 'Returns the value of a cell specified by a text string reference', 412 - params: [ 413 - { name: 'ref_text', desc: 'A cell reference as a text string (e.g. "A1", "Sheet2!B3")', required: true }, 414 - ], 415 - }, 416 - ADDRESS: { 417 - desc: 'Returns a cell address as text given row and column numbers', 418 - params: [ 419 - { name: 'row_num', desc: 'The row number of the cell reference', required: true }, 420 - { name: 'col_num', desc: 'The column number of the cell reference', required: true }, 421 - { name: 'abs_num', desc: '1=absolute (default), 2=abs row, 3=abs col, 4=relative', required: false }, 422 - ], 423 - }, 424 - ROW: { 425 - desc: 'Returns the row number of a cell reference', 426 - params: [ 427 - { name: 'reference', desc: 'The cell reference to get the row number from', required: true }, 428 - ], 429 - }, 430 - COLUMN: { 431 - desc: 'Returns the column number of a cell reference', 432 - params: [ 433 - { name: 'reference', desc: 'The cell reference to get the column number from', required: true }, 434 - ], 435 - }, 436 - 437 - // --- Information --- 438 - ISNUMBER: { 439 - desc: 'Returns TRUE if the value is a number', 440 - params: [ 441 - { name: 'value', desc: 'The value to check', required: true }, 442 - ], 443 - }, 444 - ISTEXT: { 445 - desc: 'Returns TRUE if the value is text', 446 - params: [ 447 - { name: 'value', desc: 'The value to check', required: true }, 448 - ], 449 - }, 450 - ISBLANK: { 451 - desc: 'Returns TRUE if the value is blank', 452 - params: [ 453 - { name: 'value', desc: 'The value to check', required: true }, 454 - ], 455 - }, 456 - ISERROR: { 457 - desc: 'Returns TRUE if the value is any error value', 458 - params: [ 459 - { name: 'value', desc: 'The value to check for an error', required: true }, 460 - ], 461 - }, 462 - ISNA: { 463 - desc: 'Returns TRUE if the value is the #N/A error', 464 - params: [ 465 - { name: 'value', desc: 'The value to check', required: true }, 466 - ], 467 - }, 468 - ISLOGICAL: { 469 - desc: 'Returns TRUE if the value is a logical value (TRUE or FALSE)', 470 - params: [ 471 - { name: 'value', desc: 'The value to check', required: true }, 472 - ], 473 - }, 474 - TYPE: { 475 - desc: 'Returns the type of a value (1=number, 2=text, 4=logical, 16=error, 64=array)', 476 - params: [ 477 - { name: 'value', desc: 'The value whose type to determine', required: true }, 478 - ], 479 - }, 480 - N: { 481 - desc: 'Converts a value to a number', 482 - params: [ 483 - { name: 'value', desc: 'The value to convert (TRUE=1, FALSE=0, text=0)', required: true }, 484 - ], 485 - }, 486 - T: { 487 - desc: 'Returns the value if it is text, otherwise returns empty string', 488 - params: [ 489 - { name: 'value', desc: 'The value to test and return if text', required: true }, 490 - ], 491 - }, 492 - 493 - // --- Math (additional) --- 494 - SUMPRODUCT: { 495 - desc: 'Returns the sum of the products of corresponding array elements', 496 - params: [ 497 - { name: 'array1', desc: 'First array of values to multiply', required: true }, 498 - { name: 'array2', desc: 'Additional arrays to multiply element-wise', required: false }, 499 - ], 500 - }, 501 - PRODUCT: { 502 - desc: 'Multiplies all the numbers given as arguments', 503 - params: [ 504 - { name: 'number1', desc: 'First number to multiply', required: true }, 505 - { name: 'number2', desc: 'Additional numbers to multiply', required: false }, 506 - ], 507 - }, 508 - SIGN: { 509 - desc: 'Returns the sign of a number: 1 if positive, -1 if negative, 0 if zero', 510 - params: [ 511 - { name: 'number', desc: 'The number to get the sign of', required: true }, 512 - ], 513 - }, 514 - EVEN: { 515 - desc: 'Rounds a number up to the nearest even integer', 516 - params: [ 517 - { name: 'number', desc: 'The number to round up', required: true }, 518 - ], 519 - }, 520 - ODD: { 521 - desc: 'Rounds a number up to the nearest odd integer', 522 - params: [ 523 - { name: 'number', desc: 'The number to round up', required: true }, 524 - ], 525 - }, 526 - CEILING: { 527 - desc: 'Rounds a number up to the nearest multiple of significance', 528 - params: [ 529 - { name: 'number', desc: 'The number to round', required: true }, 530 - { name: 'significance', desc: 'The multiple to round up to', required: true }, 531 - ], 532 - }, 533 - FLOOR: { 534 - desc: 'Rounds a number down to the nearest multiple of significance', 535 - params: [ 536 - { name: 'number', desc: 'The number to round', required: true }, 537 - { name: 'significance', desc: 'The multiple to round down to', required: true }, 538 - ], 539 - }, 540 - FACT: { 541 - desc: 'Returns the factorial of a number', 542 - params: [ 543 - { name: 'number', desc: 'The non-negative integer to compute the factorial of', required: true }, 544 - ], 545 - }, 546 - COMBIN: { 547 - desc: 'Returns the number of combinations for a given number of items', 548 - params: [ 549 - { name: 'n', desc: 'The total number of items', required: true }, 550 - { name: 'k', desc: 'The number of items to choose', required: true }, 551 - ], 552 - }, 553 - GCD: { 554 - desc: 'Returns the greatest common divisor of two numbers', 555 - params: [ 556 - { name: 'a', desc: 'First integer', required: true }, 557 - { name: 'b', desc: 'Second integer', required: true }, 558 - ], 559 - }, 560 - LCM: { 561 - desc: 'Returns the least common multiple of two numbers', 562 - params: [ 563 - { name: 'a', desc: 'First integer', required: true }, 564 - { name: 'b', desc: 'Second integer', required: true }, 565 - ], 566 - }, 567 - QUOTIENT: { 568 - desc: 'Returns the integer portion of a division', 569 - params: [ 570 - { name: 'numerator', desc: 'The dividend', required: true }, 571 - { name: 'denominator', desc: 'The divisor', required: true }, 572 - ], 573 - }, 574 - 575 - // --- Trigonometric --- 576 - SIN: { 577 - desc: 'Returns the sine of an angle (in radians)', 578 - params: [ 579 - { name: 'angle', desc: 'The angle in radians', required: true }, 580 - ], 581 - }, 582 - COS: { 583 - desc: 'Returns the cosine of an angle (in radians)', 584 - params: [ 585 - { name: 'angle', desc: 'The angle in radians', required: true }, 586 - ], 587 - }, 588 - TAN: { 589 - desc: 'Returns the tangent of an angle (in radians)', 590 - params: [ 591 - { name: 'angle', desc: 'The angle in radians', required: true }, 592 - ], 593 - }, 594 - ASIN: { 595 - desc: 'Returns the arcsine (inverse sine) of a number', 596 - params: [ 597 - { name: 'value', desc: 'A value between -1 and 1', required: true }, 598 - ], 599 - }, 600 - ACOS: { 601 - desc: 'Returns the arccosine (inverse cosine) of a number', 602 - params: [ 603 - { name: 'value', desc: 'A value between -1 and 1', required: true }, 604 - ], 605 - }, 606 - ATAN: { 607 - desc: 'Returns the arctangent (inverse tangent) of a number', 608 - params: [ 609 - { name: 'value', desc: 'The number to get the arctangent of', required: true }, 610 - ], 611 - }, 612 - ATAN2: { 613 - desc: 'Returns the arctangent from x and y coordinates', 614 - params: [ 615 - { name: 'x_num', desc: 'The x-coordinate', required: true }, 616 - { name: 'y_num', desc: 'The y-coordinate', required: true }, 617 - ], 618 - }, 619 - DEGREES: { 620 - desc: 'Converts radians to degrees', 621 - params: [ 622 - { name: 'radians', desc: 'The angle in radians to convert', required: true }, 623 - ], 624 - }, 625 - RADIANS: { 626 - desc: 'Converts degrees to radians', 627 - params: [ 628 - { name: 'degrees', desc: 'The angle in degrees to convert', required: true }, 629 - ], 630 - }, 631 - 632 - // --- Text (additional) --- 633 - PROPER: { 634 - desc: 'Capitalizes the first letter of each word in a text string', 635 - params: [ 636 - { name: 'text', desc: 'The text to capitalize', required: true }, 637 - ], 638 - }, 639 - REPT: { 640 - desc: 'Repeats text a given number of times', 641 - params: [ 642 - { name: 'text', desc: 'The text to repeat', required: true }, 643 - { name: 'number_times', desc: 'Number of times to repeat', required: true }, 644 - ], 645 - }, 646 - EXACT: { 647 - desc: 'Checks whether two text strings are exactly the same (case-sensitive)', 648 - params: [ 649 - { name: 'text1', desc: 'First text string', required: true }, 650 - { name: 'text2', desc: 'Second text string', required: true }, 651 - ], 652 - }, 653 - REPLACE: { 654 - desc: 'Replaces part of a text string with a different text string by position', 655 - params: [ 656 - { name: 'old_text', desc: 'The original text', required: true }, 657 - { name: 'start_num', desc: 'Position of first character to replace (1-based)', required: true }, 658 - { name: 'num_chars', desc: 'Number of characters to replace', required: true }, 659 - { name: 'new_text', desc: 'The replacement text', required: true }, 660 - ], 661 - }, 662 - CLEAN: { 663 - desc: 'Removes all non-printable characters from text', 664 - params: [ 665 - { name: 'text', desc: 'The text to clean', required: true }, 666 - ], 667 - }, 668 - CHAR: { 669 - desc: 'Returns the character specified by a number (character code)', 670 - params: [ 671 - { name: 'number', desc: 'The character code (1-255)', required: true }, 672 - ], 673 - }, 674 - CODE: { 675 - desc: 'Returns the numeric code for the first character in a text string', 676 - params: [ 677 - { name: 'text', desc: 'The text to get the code from', required: true }, 678 - ], 679 - }, 680 - 681 - // --- Date/Time (additional) --- 682 - HOUR: { 683 - desc: 'Returns the hour of a time value (0-23)', 684 - params: [ 685 - { name: 'serial_number', desc: 'The time to extract the hour from', required: true }, 686 - ], 687 - }, 688 - MINUTE: { 689 - desc: 'Returns the minutes of a time value (0-59)', 690 - params: [ 691 - { name: 'serial_number', desc: 'The time to extract minutes from', required: true }, 692 - ], 693 - }, 694 - SECOND: { 695 - desc: 'Returns the seconds of a time value (0-59)', 696 - params: [ 697 - { name: 'serial_number', desc: 'The time to extract seconds from', required: true }, 698 - ], 699 - }, 700 - WEEKDAY: { 701 - desc: 'Returns the day of the week for a date', 702 - params: [ 703 - { name: 'serial_number', desc: 'The date to find the day of week for', required: true }, 704 - { name: 'return_type', desc: '1=Sun-Sat (1-7), 2=Mon-Sun (1-7), 3=Mon-Sun (0-6)', required: false }, 705 - ], 706 - }, 707 - EDATE: { 708 - desc: 'Returns the date that is a given number of months before or after a start date', 709 - params: [ 710 - { name: 'start_date', desc: 'The starting date', required: true }, 711 - { name: 'months', desc: 'Number of months to add (negative for before)', required: true }, 712 - ], 713 - }, 714 - EOMONTH: { 715 - desc: 'Returns the last day of the month a given number of months before or after a start date', 716 - params: [ 717 - { name: 'start_date', desc: 'The starting date', required: true }, 718 - { name: 'months', desc: 'Number of months to offset', required: true }, 719 - ], 720 - }, 721 - DAYS: { 722 - desc: 'Returns the number of days between two dates', 723 - params: [ 724 - { name: 'end_date', desc: 'The end date', required: true }, 725 - { name: 'start_date', desc: 'The start date', required: true }, 726 - ], 727 - }, 728 - NETWORKDAYS: { 729 - desc: 'Returns the number of working days between two dates (excluding weekends)', 730 - params: [ 731 - { name: 'start_date', desc: 'The start date', required: true }, 732 - { name: 'end_date', desc: 'The end date', required: true }, 733 - ], 734 - }, 735 - 736 - // --- Statistical (additional) --- 737 - LARGE: { 738 - desc: 'Returns the k-th largest value in a data set', 739 - params: [ 740 - { name: 'array', desc: 'The range of data', required: true }, 741 - { name: 'k', desc: 'The position (from the largest) to return', required: true }, 742 - ], 743 - }, 744 - SMALL: { 745 - desc: 'Returns the k-th smallest value in a data set', 746 - params: [ 747 - { name: 'array', desc: 'The range of data', required: true }, 748 - { name: 'k', desc: 'The position (from the smallest) to return', required: true }, 749 - ], 750 - }, 751 - RANK: { 752 - desc: 'Returns the rank of a number in a list of numbers', 753 - params: [ 754 - { name: 'number', desc: 'The number to rank', required: true }, 755 - { name: 'ref', desc: 'The list of numbers to rank against', required: true }, 756 - { name: 'order', desc: '0 or omitted for descending, non-zero for ascending', required: false }, 757 - ], 758 - }, 759 - PERCENTILE: { 760 - desc: 'Returns the k-th percentile of values in a range', 761 - params: [ 762 - { name: 'array', desc: 'The range of data', required: true }, 763 - { name: 'k', desc: 'Percentile value between 0 and 1 inclusive', required: true }, 764 - ], 765 - }, 766 - VAR: { 767 - desc: 'Estimates variance based on a sample', 768 - params: [ 769 - { name: 'number1', desc: 'First number or range', required: true }, 770 - { name: 'number2', desc: 'Additional numbers or ranges', required: false }, 771 - ], 772 - }, 773 - VARP: { 774 - desc: 'Calculates variance based on the entire population', 775 - params: [ 776 - { name: 'number1', desc: 'First number or range', required: true }, 777 - { name: 'number2', desc: 'Additional numbers or ranges', required: false }, 778 - ], 779 - }, 780 - STDEVP: { 781 - desc: 'Calculates standard deviation based on the entire population', 782 - params: [ 783 - { name: 'number1', desc: 'First number or range', required: true }, 784 - { name: 'number2', desc: 'Additional numbers or ranges', required: false }, 785 - ], 786 - }, 787 - 788 - // --- Financial --- 789 - PMT: { 790 - desc: 'Calculates the payment for a loan based on constant payments and a constant interest rate', 791 - params: [ 792 - { name: 'rate', desc: 'The interest rate per period', required: true }, 793 - { name: 'nper', desc: 'The total number of payment periods', required: true }, 794 - { name: 'pv', desc: 'The present value (loan amount)', required: true }, 795 - { name: 'fv', desc: 'Future value (default 0)', required: false }, 796 - { name: 'type', desc: '0=end of period (default), 1=beginning', required: false }, 797 - ], 798 - }, 799 - FV: { 800 - desc: 'Returns the future value of an investment', 801 - params: [ 802 - { name: 'rate', desc: 'The interest rate per period', required: true }, 803 - { name: 'nper', desc: 'The total number of payment periods', required: true }, 804 - { name: 'pmt', desc: 'The payment made each period', required: true }, 805 - { name: 'pv', desc: 'Present value (default 0)', required: false }, 806 - { name: 'type', desc: '0=end of period (default), 1=beginning', required: false }, 807 - ], 808 - }, 809 - PV: { 810 - desc: 'Returns the present value of an investment', 811 - params: [ 812 - { name: 'rate', desc: 'The interest rate per period', required: true }, 813 - { name: 'nper', desc: 'The total number of payment periods', required: true }, 814 - { name: 'pmt', desc: 'The payment made each period', required: true }, 815 - { name: 'fv', desc: 'Future value (default 0)', required: false }, 816 - { name: 'type', desc: '0=end of period (default), 1=beginning', required: false }, 817 - ], 818 - }, 819 - NPV: { 820 - desc: 'Returns the net present value of an investment based on periodic cash flows and a discount rate', 821 - params: [ 822 - { name: 'rate', desc: 'The discount rate per period', required: true }, 823 - { name: 'value1', desc: 'First cash flow', required: true }, 824 - { name: 'value2', desc: 'Additional cash flows', required: false }, 825 - ], 826 - }, 827 - IRR: { 828 - desc: 'Returns the internal rate of return for a series of cash flows', 829 - params: [ 830 - { name: 'values', desc: 'Array of cash flows (must contain at least one positive and one negative)', required: true }, 831 - { name: 'guess', desc: 'Initial guess for the rate (default 0.1)', required: false }, 832 - ], 833 - }, 834 - 835 - // --- Lookup (additional) --- 836 - CHOOSE: { 837 - desc: 'Returns a value from a list based on an index number', 838 - params: [ 839 - { name: 'index_num', desc: 'The index number (1-based) of the value to return', required: true }, 840 - { name: 'value1', desc: 'First value to choose from', required: true }, 841 - { name: 'value2', desc: 'Additional values', required: false }, 842 - ], 843 - }, 844 - 845 - // --- Dynamic Array Functions --- 846 - FILTER: { 847 - desc: 'Returns a filtered version of the source range, returning only rows or columns that meet the specified conditions', 848 - params: [ 849 - { name: 'range', desc: 'The data to filter', required: true }, 850 - { name: 'include', desc: 'Boolean array indicating rows to include', required: true }, 851 - { name: 'if_empty', desc: 'Value to return if no results match', required: false }, 852 - ], 853 - }, 854 - SORT: { 855 - desc: 'Sorts the rows of a given array based on the values in one or more columns', 856 - params: [ 857 - { name: 'range', desc: 'The data to sort', required: true }, 858 - { name: 'sort_column', desc: 'Column index to sort by (default 1)', required: false }, 859 - { name: 'is_ascending', desc: 'TRUE for ascending, FALSE for descending', required: false }, 860 - { name: 'by_col', desc: 'TRUE to sort columns instead of rows', required: false }, 861 - ], 862 - }, 863 - UNIQUE: { 864 - desc: 'Returns unique rows from a range, removing duplicates', 865 - params: [ 866 - { name: 'range', desc: 'The data to filter for unique entries', required: true }, 867 - { name: 'by_col', desc: 'TRUE to compare columns instead of rows', required: false }, 868 - { name: 'exactly_once', desc: 'TRUE to return only entries that appear exactly once', required: false }, 869 - ], 870 - }, 871 - SEQUENCE: { 872 - desc: 'Generates a list of sequential numbers in an array', 873 - params: [ 874 - { name: 'rows', desc: 'Number of rows to return', required: true }, 875 - { name: 'columns', desc: 'Number of columns to return (default 1)', required: false }, 876 - { name: 'start', desc: 'First number in the sequence (default 1)', required: false }, 877 - { name: 'step', desc: 'Amount to increment each value (default 1)', required: false }, 878 - ], 879 - }, 880 - 881 - // --- Lookup Power Functions --- 882 - XLOOKUP: { 883 - desc: 'Searches a range for a match and returns the corresponding item from a second range', 884 - params: [ 885 - { name: 'search_key', desc: 'The value to search for', required: true }, 886 - { name: 'lookup_range', desc: 'The range to search in', required: true }, 887 - { name: 'return_range', desc: 'The range from which to return a value', required: true }, 888 - { name: 'if_not_found', desc: 'Value to return if no match is found', required: false }, 889 - { name: 'match_mode', desc: '0=exact (default), -1=next smaller, 1=next larger', required: false }, 890 - { name: 'search_mode', desc: '1=first-to-last (default), -1=last-to-first', required: false }, 891 - ], 892 - }, 893 - SUMIFS: { 894 - desc: 'Returns the sum of a range based on multiple criteria', 895 - params: [ 896 - { name: 'sum_range', desc: 'The range to sum', required: true }, 897 - { name: 'criteria_range1', desc: 'First range to check against criteria', required: true }, 898 - { name: 'criteria1', desc: 'First condition to match', required: true }, 899 - { name: 'criteria_range2', desc: 'Additional range to check', required: false }, 900 - { name: 'criteria2', desc: 'Additional condition', required: false }, 901 - ], 902 - }, 903 - COUNTIFS: { 904 - desc: 'Returns the count of a range based on multiple criteria', 905 - params: [ 906 - { name: 'criteria_range1', desc: 'First range to check against criteria', required: true }, 907 - { name: 'criteria1', desc: 'First condition to match', required: true }, 908 - { name: 'criteria_range2', desc: 'Additional range to check', required: false }, 909 - { name: 'criteria2', desc: 'Additional condition', required: false }, 910 - ], 911 - }, 912 - AVERAGEIFS: { 913 - desc: 'Returns the average of a range based on multiple criteria', 914 - params: [ 915 - { name: 'average_range', desc: 'The range to average', required: true }, 916 - { name: 'criteria_range1', desc: 'First range to check against criteria', required: true }, 917 - { name: 'criteria1', desc: 'First condition to match', required: true }, 918 - { name: 'criteria_range2', desc: 'Additional range to check', required: false }, 919 - { name: 'criteria2', desc: 'Additional condition', required: false }, 920 - ], 921 - }, 922 - QUERY: { 923 - desc: 'Runs a SQL-like query against a data range', 924 - params: [ 925 - { name: 'data', desc: 'The range of cells to query', required: true }, 926 - { name: 'query', desc: 'SQL-like query string (SELECT, WHERE, ORDER BY, LIMIT)', required: true }, 927 - { name: 'headers', desc: 'Whether the first row contains headers (default TRUE)', required: false }, 928 - ], 929 - }, 930 - 931 - // --- Visualization --- 932 - SPARKLINE: { 933 - desc: 'Creates a miniature chart within a cell (line, bar, or win/loss)', 934 - params: [ 935 - { name: 'data', desc: 'Range of numeric values to chart', required: true }, 936 - { name: 'options', desc: 'Chart type string ("line", "bar", "winloss") or options object', required: false }, 937 - ], 938 - }, 939 - }; 15 + // Re-export so existing consumers don't need to change their imports 16 + export { FUNCTION_METADATA } from './function-metadata.js'; 940 17 941 18 /** 942 19 * Detect which function the cursor is currently inside, and which ··· 990 67 991 68 // We found the matching open paren at depth 0. 992 69 // Look backwards from here to find the function name. 993 - let nameEnd = i; 994 70 // Skip whitespace before the paren 995 71 let k = i - 1; 996 72 while (k >= 0 && text[k] === ' ') k--; ··· 1045 121 tooltip.id = 'formula-tooltip'; 1046 122 1047 123 // Build signature line with highlighted current param 1048 - const sigParts = []; 124 + const sigParts: string[] = []; 1049 125 sigParts.push(`<span class="formula-tooltip-fn">${functionName}</span>(`); 1050 126 meta.params.forEach((p, idx) => { 1051 127 if (idx > 0) sigParts.push(', '); 1052 128 const isActive = idx === paramIndex; 1053 129 const cls = isActive ? 'formula-tooltip-param-active' : 'formula-tooltip-param'; 1054 - const bracket = p.required ? '' : ['[', ']']; 1055 130 if (!p.required) sigParts.push('<span class="formula-tooltip-optional">[</span>'); 1056 131 sigParts.push(`<span class="${cls}">${p.name}</span>`); 1057 132 if (!p.required) sigParts.push('<span class="formula-tooltip-optional">]</span>');
+938
src/sheets/function-metadata.ts
··· 1 + /** 2 + * Function metadata for all supported spreadsheet functions. 3 + * 4 + * Pure data module — each entry has a description and per-parameter info 5 + * used by formula tooltips, autocomplete, and documentation. 6 + */ 7 + 8 + import type { FunctionMetadataEntry } from './types.js'; 9 + 10 + /** 11 + * Complete function metadata for all supported functions. 12 + * Each entry has a description and per-parameter info. 13 + */ 14 + export const FUNCTION_METADATA: Record<string, FunctionMetadataEntry> = { 15 + // --- Math & Stats --- 16 + SUM: { 17 + desc: 'Adds all numbers in a range', 18 + params: [ 19 + { name: 'range1', desc: 'First range to sum', required: true }, 20 + { name: 'range2', desc: 'Additional ranges to sum', required: false }, 21 + ], 22 + }, 23 + AVERAGE: { 24 + desc: 'Returns the arithmetic mean of the arguments', 25 + params: [ 26 + { name: 'range1', desc: 'First range to average', required: true }, 27 + { name: 'range2', desc: 'Additional ranges', required: false }, 28 + ], 29 + }, 30 + COUNT: { 31 + desc: 'Counts the number of cells that contain numbers', 32 + params: [ 33 + { name: 'range1', desc: 'First range to count', required: true }, 34 + { name: 'range2', desc: 'Additional ranges', required: false }, 35 + ], 36 + }, 37 + COUNTA: { 38 + desc: 'Counts the number of non-empty cells', 39 + params: [ 40 + { name: 'range1', desc: 'First range to count', required: true }, 41 + { name: 'range2', desc: 'Additional ranges', required: false }, 42 + ], 43 + }, 44 + MIN: { 45 + desc: 'Returns the smallest value in a set of numbers', 46 + params: [ 47 + { name: 'range1', desc: 'First range to evaluate', required: true }, 48 + { name: 'range2', desc: 'Additional ranges', required: false }, 49 + ], 50 + }, 51 + MAX: { 52 + desc: 'Returns the largest value in a set of numbers', 53 + params: [ 54 + { name: 'range1', desc: 'First range to evaluate', required: true }, 55 + { name: 'range2', desc: 'Additional ranges', required: false }, 56 + ], 57 + }, 58 + MEDIAN: { 59 + desc: 'Returns the median of the given numbers', 60 + params: [ 61 + { name: 'range1', desc: 'First range of values', required: true }, 62 + { name: 'range2', desc: 'Additional ranges', required: false }, 63 + ], 64 + }, 65 + STDEV: { 66 + desc: 'Estimates standard deviation based on a sample', 67 + params: [ 68 + { name: 'range1', desc: 'First range of values', required: true }, 69 + { name: 'range2', desc: 'Additional ranges', required: false }, 70 + ], 71 + }, 72 + ADD: { 73 + desc: 'Returns the sum of two values (equivalent to value1 + value2)', 74 + params: [ 75 + { name: 'value1', desc: 'The first addend', required: true }, 76 + { name: 'value2', desc: 'The second addend', required: true }, 77 + ], 78 + }, 79 + MINUS: { 80 + desc: 'Returns the difference of two values (equivalent to value1 - value2)', 81 + params: [ 82 + { name: 'value1', desc: 'The value to subtract from', required: true }, 83 + { name: 'value2', desc: 'The value to subtract', required: true }, 84 + ], 85 + }, 86 + MULTIPLY: { 87 + desc: 'Returns the product of two values (equivalent to factor1 * factor2)', 88 + params: [ 89 + { name: 'factor1', desc: 'The first factor', required: true }, 90 + { name: 'factor2', desc: 'The second factor', required: true }, 91 + ], 92 + }, 93 + DIVIDE: { 94 + desc: 'Returns the result of dividing one value by another (equivalent to dividend / divisor)', 95 + params: [ 96 + { name: 'dividend', desc: 'The number to be divided', required: true }, 97 + { name: 'divisor', desc: 'The number to divide by', required: true }, 98 + ], 99 + }, 100 + ABS: { 101 + desc: 'Returns the absolute value of a number', 102 + params: [ 103 + { name: 'number', desc: 'The number to get the absolute value of', required: true }, 104 + ], 105 + }, 106 + ROUND: { 107 + desc: 'Rounds a number to a specified number of digits', 108 + params: [ 109 + { name: 'number', desc: 'The number to round', required: true }, 110 + { name: 'num_digits', desc: 'Number of decimal places (default 0)', required: false }, 111 + ], 112 + }, 113 + ROUNDUP: { 114 + desc: 'Rounds a number up, away from zero', 115 + params: [ 116 + { name: 'number', desc: 'The number to round up', required: true }, 117 + { name: 'num_digits', desc: 'Number of decimal places (default 0)', required: false }, 118 + ], 119 + }, 120 + ROUNDDOWN: { 121 + desc: 'Rounds a number down, toward zero', 122 + params: [ 123 + { name: 'number', desc: 'The number to round down', required: true }, 124 + { name: 'num_digits', desc: 'Number of decimal places (default 0)', required: false }, 125 + ], 126 + }, 127 + INT: { 128 + desc: 'Rounds a number down to the nearest integer', 129 + params: [ 130 + { name: 'number', desc: 'The number to round down', required: true }, 131 + ], 132 + }, 133 + MOD: { 134 + desc: 'Returns the remainder after dividing a number by a divisor', 135 + params: [ 136 + { name: 'number', desc: 'The number to divide', required: true }, 137 + { name: 'divisor', desc: 'The divisor', required: true }, 138 + ], 139 + }, 140 + POWER: { 141 + desc: 'Returns a number raised to a power', 142 + params: [ 143 + { name: 'number', desc: 'The base number', required: true }, 144 + { name: 'power', desc: 'The exponent', required: true }, 145 + ], 146 + }, 147 + SQRT: { 148 + desc: 'Returns the positive square root of a number', 149 + params: [ 150 + { name: 'number', desc: 'The number to take the square root of', required: true }, 151 + ], 152 + }, 153 + LOG: { 154 + desc: 'Returns the logarithm of a number to a specified base', 155 + params: [ 156 + { name: 'number', desc: 'The positive number to take the log of', required: true }, 157 + { name: 'base', desc: 'The base of the logarithm (default 10)', required: false }, 158 + ], 159 + }, 160 + LN: { 161 + desc: 'Returns the natural logarithm of a number', 162 + params: [ 163 + { name: 'number', desc: 'The positive number to take the natural log of', required: true }, 164 + ], 165 + }, 166 + EXP: { 167 + desc: 'Returns e raised to the power of a number', 168 + params: [ 169 + { name: 'number', desc: 'The exponent applied to the base e', required: true }, 170 + ], 171 + }, 172 + PI: { 173 + desc: 'Returns the value of pi (3.14159...)', 174 + params: [], 175 + }, 176 + RAND: { 177 + desc: 'Returns a random number between 0 and 1', 178 + params: [], 179 + }, 180 + 181 + // --- Logical --- 182 + IF: { 183 + desc: 'Returns one value if a condition is true and another if false', 184 + params: [ 185 + { name: 'condition', desc: 'The logical test to evaluate', required: true }, 186 + { name: 'value_if_true', desc: 'Value returned when condition is true', required: true }, 187 + { name: 'value_if_false', desc: 'Value returned when condition is false', required: false }, 188 + ], 189 + }, 190 + AND: { 191 + desc: 'Returns TRUE if all arguments are true', 192 + params: [ 193 + { name: 'logical1', desc: 'First condition to evaluate', required: true }, 194 + { name: 'logical2', desc: 'Additional conditions', required: false }, 195 + ], 196 + }, 197 + OR: { 198 + desc: 'Returns TRUE if any argument is true', 199 + params: [ 200 + { name: 'logical1', desc: 'First condition to evaluate', required: true }, 201 + { name: 'logical2', desc: 'Additional conditions', required: false }, 202 + ], 203 + }, 204 + NOT: { 205 + desc: 'Reverses the logic of its argument', 206 + params: [ 207 + { name: 'logical', desc: 'The value or expression to negate', required: true }, 208 + ], 209 + }, 210 + IFERROR: { 211 + desc: 'Returns a value if no error, otherwise returns an alternate value', 212 + params: [ 213 + { name: 'value', desc: 'The value to check for an error', required: true }, 214 + { name: 'value_if_error', desc: 'Value to return if an error is found', required: true }, 215 + ], 216 + }, 217 + 218 + // --- Text --- 219 + CONCATENATE: { 220 + desc: 'Joins several text strings into one', 221 + params: [ 222 + { name: 'text1', desc: 'First text string', required: true }, 223 + { name: 'text2', desc: 'Additional text strings to join', required: false }, 224 + ], 225 + }, 226 + LEN: { 227 + desc: 'Returns the number of characters in a text string', 228 + params: [ 229 + { name: 'text', desc: 'The text string to measure', required: true }, 230 + ], 231 + }, 232 + LEFT: { 233 + desc: 'Returns the leftmost characters from a text string', 234 + params: [ 235 + { name: 'text', desc: 'The source text string', required: true }, 236 + { name: 'num_chars', desc: 'Number of characters to extract (default 1)', required: false }, 237 + ], 238 + }, 239 + RIGHT: { 240 + desc: 'Returns the rightmost characters from a text string', 241 + params: [ 242 + { name: 'text', desc: 'The source text string', required: true }, 243 + { name: 'num_chars', desc: 'Number of characters to extract (default 1)', required: false }, 244 + ], 245 + }, 246 + MID: { 247 + desc: 'Returns a specific number of characters from a text string', 248 + params: [ 249 + { name: 'text', desc: 'The source text string', required: true }, 250 + { name: 'start_num', desc: 'Position of the first character (1-based)', required: true }, 251 + { name: 'num_chars', desc: 'Number of characters to extract', required: true }, 252 + ], 253 + }, 254 + UPPER: { 255 + desc: 'Converts text to uppercase', 256 + params: [ 257 + { name: 'text', desc: 'The text to convert', required: true }, 258 + ], 259 + }, 260 + LOWER: { 261 + desc: 'Converts text to lowercase', 262 + params: [ 263 + { name: 'text', desc: 'The text to convert', required: true }, 264 + ], 265 + }, 266 + TRIM: { 267 + desc: 'Removes leading and trailing spaces from text', 268 + params: [ 269 + { name: 'text', desc: 'The text to trim', required: true }, 270 + ], 271 + }, 272 + SUBSTITUTE: { 273 + desc: 'Replaces occurrences of old text with new text in a string', 274 + params: [ 275 + { name: 'text', desc: 'The text containing characters to replace', required: true }, 276 + { name: 'old_text', desc: 'The text to find and replace', required: true }, 277 + { name: 'new_text', desc: 'The replacement text', required: true }, 278 + { name: 'instance', desc: 'Which occurrence to replace (default: all)', required: false }, 279 + ], 280 + }, 281 + FIND: { 282 + desc: 'Finds the position of a text string within another (case-sensitive)', 283 + params: [ 284 + { name: 'find_text', desc: 'The text to find', required: true }, 285 + { name: 'within_text', desc: 'The text to search within', required: true }, 286 + { name: 'start_num', desc: 'Position to start searching from (default 1)', required: false }, 287 + ], 288 + }, 289 + SEARCH: { 290 + desc: 'Finds the position of a text string within another (case-insensitive)', 291 + params: [ 292 + { name: 'find_text', desc: 'The text to find', required: true }, 293 + { name: 'within_text', desc: 'The text to search within', required: true }, 294 + { name: 'start_num', desc: 'Position to start searching from (default 1)', required: false }, 295 + ], 296 + }, 297 + TEXT: { 298 + desc: 'Formats a number as text with a specified format', 299 + params: [ 300 + { name: 'value', desc: 'The number to format', required: true }, 301 + { name: 'format_text', desc: 'Format pattern (e.g. "0.00", "#,##0")', required: true }, 302 + ], 303 + }, 304 + VALUE: { 305 + desc: 'Converts a text string that represents a number to a number', 306 + params: [ 307 + { name: 'text', desc: 'The text to convert to a number', required: true }, 308 + ], 309 + }, 310 + 311 + // --- Date --- 312 + NOW: { 313 + desc: 'Returns the current date and time', 314 + params: [], 315 + }, 316 + TODAY: { 317 + desc: 'Returns the current date', 318 + params: [], 319 + }, 320 + DATE: { 321 + desc: 'Creates a date from year, month, and day components', 322 + params: [ 323 + { name: 'year', desc: 'The year (e.g. 2024)', required: true }, 324 + { name: 'month', desc: 'The month (1-12)', required: true }, 325 + { name: 'day', desc: 'The day of the month (1-31)', required: true }, 326 + ], 327 + }, 328 + YEAR: { 329 + desc: 'Returns the year of a date', 330 + params: [ 331 + { name: 'date', desc: 'The date to extract the year from', required: true }, 332 + ], 333 + }, 334 + MONTH: { 335 + desc: 'Returns the month of a date (1-12)', 336 + params: [ 337 + { name: 'date', desc: 'The date to extract the month from', required: true }, 338 + ], 339 + }, 340 + DAY: { 341 + desc: 'Returns the day of the month of a date (1-31)', 342 + params: [ 343 + { name: 'date', desc: 'The date to extract the day from', required: true }, 344 + ], 345 + }, 346 + 347 + // --- Lookup --- 348 + VLOOKUP: { 349 + desc: 'Searches first column of range for key and returns value from specified column', 350 + params: [ 351 + { name: 'lookup_value', desc: 'The value to search for', required: true }, 352 + { name: 'table_array', desc: 'Range containing the data', required: true }, 353 + { name: 'col_index', desc: 'Column number to return (1-based)', required: true }, 354 + { name: 'range_lookup', desc: 'FALSE for exact match, TRUE for approximate', required: false }, 355 + ], 356 + }, 357 + HLOOKUP: { 358 + desc: 'Searches first row of range for key and returns value from specified row', 359 + params: [ 360 + { name: 'lookup_value', desc: 'The value to search for', required: true }, 361 + { name: 'table_array', desc: 'Range containing the data', required: true }, 362 + { name: 'row_index', desc: 'Row number to return (1-based)', required: true }, 363 + { name: 'range_lookup', desc: 'FALSE for exact match, TRUE for approximate', required: false }, 364 + ], 365 + }, 366 + INDEX: { 367 + desc: 'Returns the value of a cell in a range at a given row and column', 368 + params: [ 369 + { name: 'array', desc: 'The range of cells', required: true }, 370 + { name: 'row_num', desc: 'Row number in the range (1-based)', required: true }, 371 + { name: 'col_num', desc: 'Column number in the range (1-based)', required: false }, 372 + ], 373 + }, 374 + MATCH: { 375 + desc: 'Returns the relative position of a value in a range', 376 + params: [ 377 + { name: 'lookup_value', desc: 'The value to search for', required: true }, 378 + { name: 'lookup_array', desc: 'The range to search', required: true }, 379 + { name: 'match_type', desc: '1 for less than, 0 for exact, -1 for greater than', required: false }, 380 + ], 381 + }, 382 + 383 + // --- Conditional --- 384 + SUMIF: { 385 + desc: 'Sums cells that meet a specified condition', 386 + params: [ 387 + { name: 'range', desc: 'The range to evaluate against the criteria', required: true }, 388 + { name: 'criteria', desc: 'The condition to match (e.g. ">100")', required: true }, 389 + { name: 'sum_range', desc: 'The range to sum (default: same as range)', required: false }, 390 + ], 391 + }, 392 + COUNTIF: { 393 + desc: 'Counts cells that meet a specified condition', 394 + params: [ 395 + { name: 'range', desc: 'The range to evaluate', required: true }, 396 + { name: 'criteria', desc: 'The condition to match', required: true }, 397 + ], 398 + }, 399 + AVERAGEIF: { 400 + desc: 'Averages cells that meet a specified condition', 401 + params: [ 402 + { name: 'range', desc: 'The range to evaluate against the criteria', required: true }, 403 + { name: 'criteria', desc: 'The condition to match', required: true }, 404 + { name: 'average_range', desc: 'The range to average (default: same as range)', required: false }, 405 + ], 406 + }, 407 + 408 + // --- Reference --- 409 + INDIRECT: { 410 + desc: 'Returns the value of a cell specified by a text string reference', 411 + params: [ 412 + { name: 'ref_text', desc: 'A cell reference as a text string (e.g. "A1", "Sheet2!B3")', required: true }, 413 + ], 414 + }, 415 + ADDRESS: { 416 + desc: 'Returns a cell address as text given row and column numbers', 417 + params: [ 418 + { name: 'row_num', desc: 'The row number of the cell reference', required: true }, 419 + { name: 'col_num', desc: 'The column number of the cell reference', required: true }, 420 + { name: 'abs_num', desc: '1=absolute (default), 2=abs row, 3=abs col, 4=relative', required: false }, 421 + ], 422 + }, 423 + ROW: { 424 + desc: 'Returns the row number of a cell reference', 425 + params: [ 426 + { name: 'reference', desc: 'The cell reference to get the row number from', required: true }, 427 + ], 428 + }, 429 + COLUMN: { 430 + desc: 'Returns the column number of a cell reference', 431 + params: [ 432 + { name: 'reference', desc: 'The cell reference to get the column number from', required: true }, 433 + ], 434 + }, 435 + 436 + // --- Information --- 437 + ISNUMBER: { 438 + desc: 'Returns TRUE if the value is a number', 439 + params: [ 440 + { name: 'value', desc: 'The value to check', required: true }, 441 + ], 442 + }, 443 + ISTEXT: { 444 + desc: 'Returns TRUE if the value is text', 445 + params: [ 446 + { name: 'value', desc: 'The value to check', required: true }, 447 + ], 448 + }, 449 + ISBLANK: { 450 + desc: 'Returns TRUE if the value is blank', 451 + params: [ 452 + { name: 'value', desc: 'The value to check', required: true }, 453 + ], 454 + }, 455 + ISERROR: { 456 + desc: 'Returns TRUE if the value is any error value', 457 + params: [ 458 + { name: 'value', desc: 'The value to check for an error', required: true }, 459 + ], 460 + }, 461 + ISNA: { 462 + desc: 'Returns TRUE if the value is the #N/A error', 463 + params: [ 464 + { name: 'value', desc: 'The value to check', required: true }, 465 + ], 466 + }, 467 + ISLOGICAL: { 468 + desc: 'Returns TRUE if the value is a logical value (TRUE or FALSE)', 469 + params: [ 470 + { name: 'value', desc: 'The value to check', required: true }, 471 + ], 472 + }, 473 + TYPE: { 474 + desc: 'Returns the type of a value (1=number, 2=text, 4=logical, 16=error, 64=array)', 475 + params: [ 476 + { name: 'value', desc: 'The value whose type to determine', required: true }, 477 + ], 478 + }, 479 + N: { 480 + desc: 'Converts a value to a number', 481 + params: [ 482 + { name: 'value', desc: 'The value to convert (TRUE=1, FALSE=0, text=0)', required: true }, 483 + ], 484 + }, 485 + T: { 486 + desc: 'Returns the value if it is text, otherwise returns empty string', 487 + params: [ 488 + { name: 'value', desc: 'The value to test and return if text', required: true }, 489 + ], 490 + }, 491 + 492 + // --- Math (additional) --- 493 + SUMPRODUCT: { 494 + desc: 'Returns the sum of the products of corresponding array elements', 495 + params: [ 496 + { name: 'array1', desc: 'First array of values to multiply', required: true }, 497 + { name: 'array2', desc: 'Additional arrays to multiply element-wise', required: false }, 498 + ], 499 + }, 500 + PRODUCT: { 501 + desc: 'Multiplies all the numbers given as arguments', 502 + params: [ 503 + { name: 'number1', desc: 'First number to multiply', required: true }, 504 + { name: 'number2', desc: 'Additional numbers to multiply', required: false }, 505 + ], 506 + }, 507 + SIGN: { 508 + desc: 'Returns the sign of a number: 1 if positive, -1 if negative, 0 if zero', 509 + params: [ 510 + { name: 'number', desc: 'The number to get the sign of', required: true }, 511 + ], 512 + }, 513 + EVEN: { 514 + desc: 'Rounds a number up to the nearest even integer', 515 + params: [ 516 + { name: 'number', desc: 'The number to round up', required: true }, 517 + ], 518 + }, 519 + ODD: { 520 + desc: 'Rounds a number up to the nearest odd integer', 521 + params: [ 522 + { name: 'number', desc: 'The number to round up', required: true }, 523 + ], 524 + }, 525 + CEILING: { 526 + desc: 'Rounds a number up to the nearest multiple of significance', 527 + params: [ 528 + { name: 'number', desc: 'The number to round', required: true }, 529 + { name: 'significance', desc: 'The multiple to round up to', required: true }, 530 + ], 531 + }, 532 + FLOOR: { 533 + desc: 'Rounds a number down to the nearest multiple of significance', 534 + params: [ 535 + { name: 'number', desc: 'The number to round', required: true }, 536 + { name: 'significance', desc: 'The multiple to round down to', required: true }, 537 + ], 538 + }, 539 + FACT: { 540 + desc: 'Returns the factorial of a number', 541 + params: [ 542 + { name: 'number', desc: 'The non-negative integer to compute the factorial of', required: true }, 543 + ], 544 + }, 545 + COMBIN: { 546 + desc: 'Returns the number of combinations for a given number of items', 547 + params: [ 548 + { name: 'n', desc: 'The total number of items', required: true }, 549 + { name: 'k', desc: 'The number of items to choose', required: true }, 550 + ], 551 + }, 552 + GCD: { 553 + desc: 'Returns the greatest common divisor of two numbers', 554 + params: [ 555 + { name: 'a', desc: 'First integer', required: true }, 556 + { name: 'b', desc: 'Second integer', required: true }, 557 + ], 558 + }, 559 + LCM: { 560 + desc: 'Returns the least common multiple of two numbers', 561 + params: [ 562 + { name: 'a', desc: 'First integer', required: true }, 563 + { name: 'b', desc: 'Second integer', required: true }, 564 + ], 565 + }, 566 + QUOTIENT: { 567 + desc: 'Returns the integer portion of a division', 568 + params: [ 569 + { name: 'numerator', desc: 'The dividend', required: true }, 570 + { name: 'denominator', desc: 'The divisor', required: true }, 571 + ], 572 + }, 573 + 574 + // --- Trigonometric --- 575 + SIN: { 576 + desc: 'Returns the sine of an angle (in radians)', 577 + params: [ 578 + { name: 'angle', desc: 'The angle in radians', required: true }, 579 + ], 580 + }, 581 + COS: { 582 + desc: 'Returns the cosine of an angle (in radians)', 583 + params: [ 584 + { name: 'angle', desc: 'The angle in radians', required: true }, 585 + ], 586 + }, 587 + TAN: { 588 + desc: 'Returns the tangent of an angle (in radians)', 589 + params: [ 590 + { name: 'angle', desc: 'The angle in radians', required: true }, 591 + ], 592 + }, 593 + ASIN: { 594 + desc: 'Returns the arcsine (inverse sine) of a number', 595 + params: [ 596 + { name: 'value', desc: 'A value between -1 and 1', required: true }, 597 + ], 598 + }, 599 + ACOS: { 600 + desc: 'Returns the arccosine (inverse cosine) of a number', 601 + params: [ 602 + { name: 'value', desc: 'A value between -1 and 1', required: true }, 603 + ], 604 + }, 605 + ATAN: { 606 + desc: 'Returns the arctangent (inverse tangent) of a number', 607 + params: [ 608 + { name: 'value', desc: 'The number to get the arctangent of', required: true }, 609 + ], 610 + }, 611 + ATAN2: { 612 + desc: 'Returns the arctangent from x and y coordinates', 613 + params: [ 614 + { name: 'x_num', desc: 'The x-coordinate', required: true }, 615 + { name: 'y_num', desc: 'The y-coordinate', required: true }, 616 + ], 617 + }, 618 + DEGREES: { 619 + desc: 'Converts radians to degrees', 620 + params: [ 621 + { name: 'radians', desc: 'The angle in radians to convert', required: true }, 622 + ], 623 + }, 624 + RADIANS: { 625 + desc: 'Converts degrees to radians', 626 + params: [ 627 + { name: 'degrees', desc: 'The angle in degrees to convert', required: true }, 628 + ], 629 + }, 630 + 631 + // --- Text (additional) --- 632 + PROPER: { 633 + desc: 'Capitalizes the first letter of each word in a text string', 634 + params: [ 635 + { name: 'text', desc: 'The text to capitalize', required: true }, 636 + ], 637 + }, 638 + REPT: { 639 + desc: 'Repeats text a given number of times', 640 + params: [ 641 + { name: 'text', desc: 'The text to repeat', required: true }, 642 + { name: 'number_times', desc: 'Number of times to repeat', required: true }, 643 + ], 644 + }, 645 + EXACT: { 646 + desc: 'Checks whether two text strings are exactly the same (case-sensitive)', 647 + params: [ 648 + { name: 'text1', desc: 'First text string', required: true }, 649 + { name: 'text2', desc: 'Second text string', required: true }, 650 + ], 651 + }, 652 + REPLACE: { 653 + desc: 'Replaces part of a text string with a different text string by position', 654 + params: [ 655 + { name: 'old_text', desc: 'The original text', required: true }, 656 + { name: 'start_num', desc: 'Position of first character to replace (1-based)', required: true }, 657 + { name: 'num_chars', desc: 'Number of characters to replace', required: true }, 658 + { name: 'new_text', desc: 'The replacement text', required: true }, 659 + ], 660 + }, 661 + CLEAN: { 662 + desc: 'Removes all non-printable characters from text', 663 + params: [ 664 + { name: 'text', desc: 'The text to clean', required: true }, 665 + ], 666 + }, 667 + CHAR: { 668 + desc: 'Returns the character specified by a number (character code)', 669 + params: [ 670 + { name: 'number', desc: 'The character code (1-255)', required: true }, 671 + ], 672 + }, 673 + CODE: { 674 + desc: 'Returns the numeric code for the first character in a text string', 675 + params: [ 676 + { name: 'text', desc: 'The text to get the code from', required: true }, 677 + ], 678 + }, 679 + 680 + // --- Date/Time (additional) --- 681 + HOUR: { 682 + desc: 'Returns the hour of a time value (0-23)', 683 + params: [ 684 + { name: 'serial_number', desc: 'The time to extract the hour from', required: true }, 685 + ], 686 + }, 687 + MINUTE: { 688 + desc: 'Returns the minutes of a time value (0-59)', 689 + params: [ 690 + { name: 'serial_number', desc: 'The time to extract minutes from', required: true }, 691 + ], 692 + }, 693 + SECOND: { 694 + desc: 'Returns the seconds of a time value (0-59)', 695 + params: [ 696 + { name: 'serial_number', desc: 'The time to extract seconds from', required: true }, 697 + ], 698 + }, 699 + WEEKDAY: { 700 + desc: 'Returns the day of the week for a date', 701 + params: [ 702 + { name: 'serial_number', desc: 'The date to find the day of week for', required: true }, 703 + { name: 'return_type', desc: '1=Sun-Sat (1-7), 2=Mon-Sun (1-7), 3=Mon-Sun (0-6)', required: false }, 704 + ], 705 + }, 706 + EDATE: { 707 + desc: 'Returns the date that is a given number of months before or after a start date', 708 + params: [ 709 + { name: 'start_date', desc: 'The starting date', required: true }, 710 + { name: 'months', desc: 'Number of months to add (negative for before)', required: true }, 711 + ], 712 + }, 713 + EOMONTH: { 714 + desc: 'Returns the last day of the month a given number of months before or after a start date', 715 + params: [ 716 + { name: 'start_date', desc: 'The starting date', required: true }, 717 + { name: 'months', desc: 'Number of months to offset', required: true }, 718 + ], 719 + }, 720 + DAYS: { 721 + desc: 'Returns the number of days between two dates', 722 + params: [ 723 + { name: 'end_date', desc: 'The end date', required: true }, 724 + { name: 'start_date', desc: 'The start date', required: true }, 725 + ], 726 + }, 727 + NETWORKDAYS: { 728 + desc: 'Returns the number of working days between two dates (excluding weekends)', 729 + params: [ 730 + { name: 'start_date', desc: 'The start date', required: true }, 731 + { name: 'end_date', desc: 'The end date', required: true }, 732 + ], 733 + }, 734 + 735 + // --- Statistical (additional) --- 736 + LARGE: { 737 + desc: 'Returns the k-th largest value in a data set', 738 + params: [ 739 + { name: 'array', desc: 'The range of data', required: true }, 740 + { name: 'k', desc: 'The position (from the largest) to return', required: true }, 741 + ], 742 + }, 743 + SMALL: { 744 + desc: 'Returns the k-th smallest value in a data set', 745 + params: [ 746 + { name: 'array', desc: 'The range of data', required: true }, 747 + { name: 'k', desc: 'The position (from the smallest) to return', required: true }, 748 + ], 749 + }, 750 + RANK: { 751 + desc: 'Returns the rank of a number in a list of numbers', 752 + params: [ 753 + { name: 'number', desc: 'The number to rank', required: true }, 754 + { name: 'ref', desc: 'The list of numbers to rank against', required: true }, 755 + { name: 'order', desc: '0 or omitted for descending, non-zero for ascending', required: false }, 756 + ], 757 + }, 758 + PERCENTILE: { 759 + desc: 'Returns the k-th percentile of values in a range', 760 + params: [ 761 + { name: 'array', desc: 'The range of data', required: true }, 762 + { name: 'k', desc: 'Percentile value between 0 and 1 inclusive', required: true }, 763 + ], 764 + }, 765 + VAR: { 766 + desc: 'Estimates variance based on a sample', 767 + params: [ 768 + { name: 'number1', desc: 'First number or range', required: true }, 769 + { name: 'number2', desc: 'Additional numbers or ranges', required: false }, 770 + ], 771 + }, 772 + VARP: { 773 + desc: 'Calculates variance based on the entire population', 774 + params: [ 775 + { name: 'number1', desc: 'First number or range', required: true }, 776 + { name: 'number2', desc: 'Additional numbers or ranges', required: false }, 777 + ], 778 + }, 779 + STDEVP: { 780 + desc: 'Calculates standard deviation based on the entire population', 781 + params: [ 782 + { name: 'number1', desc: 'First number or range', required: true }, 783 + { name: 'number2', desc: 'Additional numbers or ranges', required: false }, 784 + ], 785 + }, 786 + 787 + // --- Financial --- 788 + PMT: { 789 + desc: 'Calculates the payment for a loan based on constant payments and a constant interest rate', 790 + params: [ 791 + { name: 'rate', desc: 'The interest rate per period', required: true }, 792 + { name: 'nper', desc: 'The total number of payment periods', required: true }, 793 + { name: 'pv', desc: 'The present value (loan amount)', required: true }, 794 + { name: 'fv', desc: 'Future value (default 0)', required: false }, 795 + { name: 'type', desc: '0=end of period (default), 1=beginning', required: false }, 796 + ], 797 + }, 798 + FV: { 799 + desc: 'Returns the future value of an investment', 800 + params: [ 801 + { name: 'rate', desc: 'The interest rate per period', required: true }, 802 + { name: 'nper', desc: 'The total number of payment periods', required: true }, 803 + { name: 'pmt', desc: 'The payment made each period', required: true }, 804 + { name: 'pv', desc: 'Present value (default 0)', required: false }, 805 + { name: 'type', desc: '0=end of period (default), 1=beginning', required: false }, 806 + ], 807 + }, 808 + PV: { 809 + desc: 'Returns the present value of an investment', 810 + params: [ 811 + { name: 'rate', desc: 'The interest rate per period', required: true }, 812 + { name: 'nper', desc: 'The total number of payment periods', required: true }, 813 + { name: 'pmt', desc: 'The payment made each period', required: true }, 814 + { name: 'fv', desc: 'Future value (default 0)', required: false }, 815 + { name: 'type', desc: '0=end of period (default), 1=beginning', required: false }, 816 + ], 817 + }, 818 + NPV: { 819 + desc: 'Returns the net present value of an investment based on periodic cash flows and a discount rate', 820 + params: [ 821 + { name: 'rate', desc: 'The discount rate per period', required: true }, 822 + { name: 'value1', desc: 'First cash flow', required: true }, 823 + { name: 'value2', desc: 'Additional cash flows', required: false }, 824 + ], 825 + }, 826 + IRR: { 827 + desc: 'Returns the internal rate of return for a series of cash flows', 828 + params: [ 829 + { name: 'values', desc: 'Array of cash flows (must contain at least one positive and one negative)', required: true }, 830 + { name: 'guess', desc: 'Initial guess for the rate (default 0.1)', required: false }, 831 + ], 832 + }, 833 + 834 + // --- Lookup (additional) --- 835 + CHOOSE: { 836 + desc: 'Returns a value from a list based on an index number', 837 + params: [ 838 + { name: 'index_num', desc: 'The index number (1-based) of the value to return', required: true }, 839 + { name: 'value1', desc: 'First value to choose from', required: true }, 840 + { name: 'value2', desc: 'Additional values', required: false }, 841 + ], 842 + }, 843 + 844 + // --- Dynamic Array Functions --- 845 + FILTER: { 846 + desc: 'Returns a filtered version of the source range, returning only rows or columns that meet the specified conditions', 847 + params: [ 848 + { name: 'range', desc: 'The data to filter', required: true }, 849 + { name: 'include', desc: 'Boolean array indicating rows to include', required: true }, 850 + { name: 'if_empty', desc: 'Value to return if no results match', required: false }, 851 + ], 852 + }, 853 + SORT: { 854 + desc: 'Sorts the rows of a given array based on the values in one or more columns', 855 + params: [ 856 + { name: 'range', desc: 'The data to sort', required: true }, 857 + { name: 'sort_column', desc: 'Column index to sort by (default 1)', required: false }, 858 + { name: 'is_ascending', desc: 'TRUE for ascending, FALSE for descending', required: false }, 859 + { name: 'by_col', desc: 'TRUE to sort columns instead of rows', required: false }, 860 + ], 861 + }, 862 + UNIQUE: { 863 + desc: 'Returns unique rows from a range, removing duplicates', 864 + params: [ 865 + { name: 'range', desc: 'The data to filter for unique entries', required: true }, 866 + { name: 'by_col', desc: 'TRUE to compare columns instead of rows', required: false }, 867 + { name: 'exactly_once', desc: 'TRUE to return only entries that appear exactly once', required: false }, 868 + ], 869 + }, 870 + SEQUENCE: { 871 + desc: 'Generates a list of sequential numbers in an array', 872 + params: [ 873 + { name: 'rows', desc: 'Number of rows to return', required: true }, 874 + { name: 'columns', desc: 'Number of columns to return (default 1)', required: false }, 875 + { name: 'start', desc: 'First number in the sequence (default 1)', required: false }, 876 + { name: 'step', desc: 'Amount to increment each value (default 1)', required: false }, 877 + ], 878 + }, 879 + 880 + // --- Lookup Power Functions --- 881 + XLOOKUP: { 882 + desc: 'Searches a range for a match and returns the corresponding item from a second range', 883 + params: [ 884 + { name: 'search_key', desc: 'The value to search for', required: true }, 885 + { name: 'lookup_range', desc: 'The range to search in', required: true }, 886 + { name: 'return_range', desc: 'The range from which to return a value', required: true }, 887 + { name: 'if_not_found', desc: 'Value to return if no match is found', required: false }, 888 + { name: 'match_mode', desc: '0=exact (default), -1=next smaller, 1=next larger', required: false }, 889 + { name: 'search_mode', desc: '1=first-to-last (default), -1=last-to-first', required: false }, 890 + ], 891 + }, 892 + SUMIFS: { 893 + desc: 'Returns the sum of a range based on multiple criteria', 894 + params: [ 895 + { name: 'sum_range', desc: 'The range to sum', required: true }, 896 + { name: 'criteria_range1', desc: 'First range to check against criteria', required: true }, 897 + { name: 'criteria1', desc: 'First condition to match', required: true }, 898 + { name: 'criteria_range2', desc: 'Additional range to check', required: false }, 899 + { name: 'criteria2', desc: 'Additional condition', required: false }, 900 + ], 901 + }, 902 + COUNTIFS: { 903 + desc: 'Returns the count of a range based on multiple criteria', 904 + params: [ 905 + { name: 'criteria_range1', desc: 'First range to check against criteria', required: true }, 906 + { name: 'criteria1', desc: 'First condition to match', required: true }, 907 + { name: 'criteria_range2', desc: 'Additional range to check', required: false }, 908 + { name: 'criteria2', desc: 'Additional condition', required: false }, 909 + ], 910 + }, 911 + AVERAGEIFS: { 912 + desc: 'Returns the average of a range based on multiple criteria', 913 + params: [ 914 + { name: 'average_range', desc: 'The range to average', required: true }, 915 + { name: 'criteria_range1', desc: 'First range to check against criteria', required: true }, 916 + { name: 'criteria1', desc: 'First condition to match', required: true }, 917 + { name: 'criteria_range2', desc: 'Additional range to check', required: false }, 918 + { name: 'criteria2', desc: 'Additional condition', required: false }, 919 + ], 920 + }, 921 + QUERY: { 922 + desc: 'Runs a SQL-like query against a data range', 923 + params: [ 924 + { name: 'data', desc: 'The range of cells to query', required: true }, 925 + { name: 'query', desc: 'SQL-like query string (SELECT, WHERE, ORDER BY, LIMIT)', required: true }, 926 + { name: 'headers', desc: 'Whether the first row contains headers (default TRUE)', required: false }, 927 + ], 928 + }, 929 + 930 + // --- Visualization --- 931 + SPARKLINE: { 932 + desc: 'Creates a miniature chart within a cell (line, bar, or win/loss)', 933 + params: [ 934 + { name: 'data', desc: 'Range of numeric values to chart', required: true }, 935 + { name: 'options', desc: 'Chart type string ("line", "bar", "winloss") or options object', required: false }, 936 + ], 937 + }, 938 + };