···44 * When typing inside a function call, shows a tooltip with the full
55 * function signature, highlighting the current parameter based on
66 * cursor position (counting commas and parens).
77+ *
88+ * Function metadata lives in ./function-metadata.ts (pure data).
99+ * This module provides detection logic and tooltip rendering.
710 */
81199-import type { FunctionMetadataEntry, DetectedFunction, FunctionParam } from './types.js';
1010-1111-/**
1212- * Complete function metadata for all 57 supported functions.
1313- * Each entry has a description and per-parameter info.
1414- */
1515-export const FUNCTION_METADATA: Record<string, FunctionMetadataEntry> = {
1616- // --- Math & Stats ---
1717- SUM: {
1818- desc: 'Adds all numbers in a range',
1919- params: [
2020- { name: 'range1', desc: 'First range to sum', required: true },
2121- { name: 'range2', desc: 'Additional ranges to sum', required: false },
2222- ],
2323- },
2424- AVERAGE: {
2525- desc: 'Returns the arithmetic mean of the arguments',
2626- params: [
2727- { name: 'range1', desc: 'First range to average', required: true },
2828- { name: 'range2', desc: 'Additional ranges', required: false },
2929- ],
3030- },
3131- COUNT: {
3232- desc: 'Counts the number of cells that contain numbers',
3333- params: [
3434- { name: 'range1', desc: 'First range to count', required: true },
3535- { name: 'range2', desc: 'Additional ranges', required: false },
3636- ],
3737- },
3838- COUNTA: {
3939- desc: 'Counts the number of non-empty cells',
4040- params: [
4141- { name: 'range1', desc: 'First range to count', required: true },
4242- { name: 'range2', desc: 'Additional ranges', required: false },
4343- ],
4444- },
4545- MIN: {
4646- desc: 'Returns the smallest value in a set of numbers',
4747- params: [
4848- { name: 'range1', desc: 'First range to evaluate', required: true },
4949- { name: 'range2', desc: 'Additional ranges', required: false },
5050- ],
5151- },
5252- MAX: {
5353- desc: 'Returns the largest value in a set of numbers',
5454- params: [
5555- { name: 'range1', desc: 'First range to evaluate', required: true },
5656- { name: 'range2', desc: 'Additional ranges', required: false },
5757- ],
5858- },
5959- MEDIAN: {
6060- desc: 'Returns the median of the given numbers',
6161- params: [
6262- { name: 'range1', desc: 'First range of values', required: true },
6363- { name: 'range2', desc: 'Additional ranges', required: false },
6464- ],
6565- },
6666- STDEV: {
6767- desc: 'Estimates standard deviation based on a sample',
6868- params: [
6969- { name: 'range1', desc: 'First range of values', required: true },
7070- { name: 'range2', desc: 'Additional ranges', required: false },
7171- ],
7272- },
7373- ADD: {
7474- desc: 'Returns the sum of two values (equivalent to value1 + value2)',
7575- params: [
7676- { name: 'value1', desc: 'The first addend', required: true },
7777- { name: 'value2', desc: 'The second addend', required: true },
7878- ],
7979- },
8080- MINUS: {
8181- desc: 'Returns the difference of two values (equivalent to value1 - value2)',
8282- params: [
8383- { name: 'value1', desc: 'The value to subtract from', required: true },
8484- { name: 'value2', desc: 'The value to subtract', required: true },
8585- ],
8686- },
8787- MULTIPLY: {
8888- desc: 'Returns the product of two values (equivalent to factor1 * factor2)',
8989- params: [
9090- { name: 'factor1', desc: 'The first factor', required: true },
9191- { name: 'factor2', desc: 'The second factor', required: true },
9292- ],
9393- },
9494- DIVIDE: {
9595- desc: 'Returns the result of dividing one value by another (equivalent to dividend / divisor)',
9696- params: [
9797- { name: 'dividend', desc: 'The number to be divided', required: true },
9898- { name: 'divisor', desc: 'The number to divide by', required: true },
9999- ],
100100- },
101101- ABS: {
102102- desc: 'Returns the absolute value of a number',
103103- params: [
104104- { name: 'number', desc: 'The number to get the absolute value of', required: true },
105105- ],
106106- },
107107- ROUND: {
108108- desc: 'Rounds a number to a specified number of digits',
109109- params: [
110110- { name: 'number', desc: 'The number to round', required: true },
111111- { name: 'num_digits', desc: 'Number of decimal places (default 0)', required: false },
112112- ],
113113- },
114114- ROUNDUP: {
115115- desc: 'Rounds a number up, away from zero',
116116- params: [
117117- { name: 'number', desc: 'The number to round up', required: true },
118118- { name: 'num_digits', desc: 'Number of decimal places (default 0)', required: false },
119119- ],
120120- },
121121- ROUNDDOWN: {
122122- desc: 'Rounds a number down, toward zero',
123123- params: [
124124- { name: 'number', desc: 'The number to round down', required: true },
125125- { name: 'num_digits', desc: 'Number of decimal places (default 0)', required: false },
126126- ],
127127- },
128128- INT: {
129129- desc: 'Rounds a number down to the nearest integer',
130130- params: [
131131- { name: 'number', desc: 'The number to round down', required: true },
132132- ],
133133- },
134134- MOD: {
135135- desc: 'Returns the remainder after dividing a number by a divisor',
136136- params: [
137137- { name: 'number', desc: 'The number to divide', required: true },
138138- { name: 'divisor', desc: 'The divisor', required: true },
139139- ],
140140- },
141141- POWER: {
142142- desc: 'Returns a number raised to a power',
143143- params: [
144144- { name: 'number', desc: 'The base number', required: true },
145145- { name: 'power', desc: 'The exponent', required: true },
146146- ],
147147- },
148148- SQRT: {
149149- desc: 'Returns the positive square root of a number',
150150- params: [
151151- { name: 'number', desc: 'The number to take the square root of', required: true },
152152- ],
153153- },
154154- LOG: {
155155- desc: 'Returns the logarithm of a number to a specified base',
156156- params: [
157157- { name: 'number', desc: 'The positive number to take the log of', required: true },
158158- { name: 'base', desc: 'The base of the logarithm (default 10)', required: false },
159159- ],
160160- },
161161- LN: {
162162- desc: 'Returns the natural logarithm of a number',
163163- params: [
164164- { name: 'number', desc: 'The positive number to take the natural log of', required: true },
165165- ],
166166- },
167167- EXP: {
168168- desc: 'Returns e raised to the power of a number',
169169- params: [
170170- { name: 'number', desc: 'The exponent applied to the base e', required: true },
171171- ],
172172- },
173173- PI: {
174174- desc: 'Returns the value of pi (3.14159...)',
175175- params: [],
176176- },
177177- RAND: {
178178- desc: 'Returns a random number between 0 and 1',
179179- params: [],
180180- },
1212+import type { DetectedFunction } from './types.js';
1313+import { FUNCTION_METADATA } from './function-metadata.js';
18114182182- // --- Logical ---
183183- IF: {
184184- desc: 'Returns one value if a condition is true and another if false',
185185- params: [
186186- { name: 'condition', desc: 'The logical test to evaluate', required: true },
187187- { name: 'value_if_true', desc: 'Value returned when condition is true', required: true },
188188- { name: 'value_if_false', desc: 'Value returned when condition is false', required: false },
189189- ],
190190- },
191191- AND: {
192192- desc: 'Returns TRUE if all arguments are true',
193193- params: [
194194- { name: 'logical1', desc: 'First condition to evaluate', required: true },
195195- { name: 'logical2', desc: 'Additional conditions', required: false },
196196- ],
197197- },
198198- OR: {
199199- desc: 'Returns TRUE if any argument is true',
200200- params: [
201201- { name: 'logical1', desc: 'First condition to evaluate', required: true },
202202- { name: 'logical2', desc: 'Additional conditions', required: false },
203203- ],
204204- },
205205- NOT: {
206206- desc: 'Reverses the logic of its argument',
207207- params: [
208208- { name: 'logical', desc: 'The value or expression to negate', required: true },
209209- ],
210210- },
211211- IFERROR: {
212212- desc: 'Returns a value if no error, otherwise returns an alternate value',
213213- params: [
214214- { name: 'value', desc: 'The value to check for an error', required: true },
215215- { name: 'value_if_error', desc: 'Value to return if an error is found', required: true },
216216- ],
217217- },
218218-219219- // --- Text ---
220220- CONCATENATE: {
221221- desc: 'Joins several text strings into one',
222222- params: [
223223- { name: 'text1', desc: 'First text string', required: true },
224224- { name: 'text2', desc: 'Additional text strings to join', required: false },
225225- ],
226226- },
227227- LEN: {
228228- desc: 'Returns the number of characters in a text string',
229229- params: [
230230- { name: 'text', desc: 'The text string to measure', required: true },
231231- ],
232232- },
233233- LEFT: {
234234- desc: 'Returns the leftmost characters from a text string',
235235- params: [
236236- { name: 'text', desc: 'The source text string', required: true },
237237- { name: 'num_chars', desc: 'Number of characters to extract (default 1)', required: false },
238238- ],
239239- },
240240- RIGHT: {
241241- desc: 'Returns the rightmost characters from a text string',
242242- params: [
243243- { name: 'text', desc: 'The source text string', required: true },
244244- { name: 'num_chars', desc: 'Number of characters to extract (default 1)', required: false },
245245- ],
246246- },
247247- MID: {
248248- desc: 'Returns a specific number of characters from a text string',
249249- params: [
250250- { name: 'text', desc: 'The source text string', required: true },
251251- { name: 'start_num', desc: 'Position of the first character (1-based)', required: true },
252252- { name: 'num_chars', desc: 'Number of characters to extract', required: true },
253253- ],
254254- },
255255- UPPER: {
256256- desc: 'Converts text to uppercase',
257257- params: [
258258- { name: 'text', desc: 'The text to convert', required: true },
259259- ],
260260- },
261261- LOWER: {
262262- desc: 'Converts text to lowercase',
263263- params: [
264264- { name: 'text', desc: 'The text to convert', required: true },
265265- ],
266266- },
267267- TRIM: {
268268- desc: 'Removes leading and trailing spaces from text',
269269- params: [
270270- { name: 'text', desc: 'The text to trim', required: true },
271271- ],
272272- },
273273- SUBSTITUTE: {
274274- desc: 'Replaces occurrences of old text with new text in a string',
275275- params: [
276276- { name: 'text', desc: 'The text containing characters to replace', required: true },
277277- { name: 'old_text', desc: 'The text to find and replace', required: true },
278278- { name: 'new_text', desc: 'The replacement text', required: true },
279279- { name: 'instance', desc: 'Which occurrence to replace (default: all)', required: false },
280280- ],
281281- },
282282- FIND: {
283283- desc: 'Finds the position of a text string within another (case-sensitive)',
284284- params: [
285285- { name: 'find_text', desc: 'The text to find', required: true },
286286- { name: 'within_text', desc: 'The text to search within', required: true },
287287- { name: 'start_num', desc: 'Position to start searching from (default 1)', required: false },
288288- ],
289289- },
290290- SEARCH: {
291291- desc: 'Finds the position of a text string within another (case-insensitive)',
292292- params: [
293293- { name: 'find_text', desc: 'The text to find', required: true },
294294- { name: 'within_text', desc: 'The text to search within', required: true },
295295- { name: 'start_num', desc: 'Position to start searching from (default 1)', required: false },
296296- ],
297297- },
298298- TEXT: {
299299- desc: 'Formats a number as text with a specified format',
300300- params: [
301301- { name: 'value', desc: 'The number to format', required: true },
302302- { name: 'format_text', desc: 'Format pattern (e.g. "0.00", "#,##0")', required: true },
303303- ],
304304- },
305305- VALUE: {
306306- desc: 'Converts a text string that represents a number to a number',
307307- params: [
308308- { name: 'text', desc: 'The text to convert to a number', required: true },
309309- ],
310310- },
311311-312312- // --- Date ---
313313- NOW: {
314314- desc: 'Returns the current date and time',
315315- params: [],
316316- },
317317- TODAY: {
318318- desc: 'Returns the current date',
319319- params: [],
320320- },
321321- DATE: {
322322- desc: 'Creates a date from year, month, and day components',
323323- params: [
324324- { name: 'year', desc: 'The year (e.g. 2024)', required: true },
325325- { name: 'month', desc: 'The month (1-12)', required: true },
326326- { name: 'day', desc: 'The day of the month (1-31)', required: true },
327327- ],
328328- },
329329- YEAR: {
330330- desc: 'Returns the year of a date',
331331- params: [
332332- { name: 'date', desc: 'The date to extract the year from', required: true },
333333- ],
334334- },
335335- MONTH: {
336336- desc: 'Returns the month of a date (1-12)',
337337- params: [
338338- { name: 'date', desc: 'The date to extract the month from', required: true },
339339- ],
340340- },
341341- DAY: {
342342- desc: 'Returns the day of the month of a date (1-31)',
343343- params: [
344344- { name: 'date', desc: 'The date to extract the day from', required: true },
345345- ],
346346- },
347347-348348- // --- Lookup ---
349349- VLOOKUP: {
350350- desc: 'Searches first column of range for key and returns value from specified column',
351351- params: [
352352- { name: 'lookup_value', desc: 'The value to search for', required: true },
353353- { name: 'table_array', desc: 'Range containing the data', required: true },
354354- { name: 'col_index', desc: 'Column number to return (1-based)', required: true },
355355- { name: 'range_lookup', desc: 'FALSE for exact match, TRUE for approximate', required: false },
356356- ],
357357- },
358358- HLOOKUP: {
359359- desc: 'Searches first row of range for key and returns value from specified row',
360360- params: [
361361- { name: 'lookup_value', desc: 'The value to search for', required: true },
362362- { name: 'table_array', desc: 'Range containing the data', required: true },
363363- { name: 'row_index', desc: 'Row number to return (1-based)', required: true },
364364- { name: 'range_lookup', desc: 'FALSE for exact match, TRUE for approximate', required: false },
365365- ],
366366- },
367367- INDEX: {
368368- desc: 'Returns the value of a cell in a range at a given row and column',
369369- params: [
370370- { name: 'array', desc: 'The range of cells', required: true },
371371- { name: 'row_num', desc: 'Row number in the range (1-based)', required: true },
372372- { name: 'col_num', desc: 'Column number in the range (1-based)', required: false },
373373- ],
374374- },
375375- MATCH: {
376376- desc: 'Returns the relative position of a value in a range',
377377- params: [
378378- { name: 'lookup_value', desc: 'The value to search for', required: true },
379379- { name: 'lookup_array', desc: 'The range to search', required: true },
380380- { name: 'match_type', desc: '1 for less than, 0 for exact, -1 for greater than', required: false },
381381- ],
382382- },
383383-384384- // --- Conditional ---
385385- SUMIF: {
386386- desc: 'Sums cells that meet a specified condition',
387387- params: [
388388- { name: 'range', desc: 'The range to evaluate against the criteria', required: true },
389389- { name: 'criteria', desc: 'The condition to match (e.g. ">100")', required: true },
390390- { name: 'sum_range', desc: 'The range to sum (default: same as range)', required: false },
391391- ],
392392- },
393393- COUNTIF: {
394394- desc: 'Counts cells that meet a specified condition',
395395- params: [
396396- { name: 'range', desc: 'The range to evaluate', required: true },
397397- { name: 'criteria', desc: 'The condition to match', required: true },
398398- ],
399399- },
400400- AVERAGEIF: {
401401- desc: 'Averages cells that meet a specified condition',
402402- params: [
403403- { name: 'range', desc: 'The range to evaluate against the criteria', required: true },
404404- { name: 'criteria', desc: 'The condition to match', required: true },
405405- { name: 'average_range', desc: 'The range to average (default: same as range)', required: false },
406406- ],
407407- },
408408-409409- // --- Reference ---
410410- INDIRECT: {
411411- desc: 'Returns the value of a cell specified by a text string reference',
412412- params: [
413413- { name: 'ref_text', desc: 'A cell reference as a text string (e.g. "A1", "Sheet2!B3")', required: true },
414414- ],
415415- },
416416- ADDRESS: {
417417- desc: 'Returns a cell address as text given row and column numbers',
418418- params: [
419419- { name: 'row_num', desc: 'The row number of the cell reference', required: true },
420420- { name: 'col_num', desc: 'The column number of the cell reference', required: true },
421421- { name: 'abs_num', desc: '1=absolute (default), 2=abs row, 3=abs col, 4=relative', required: false },
422422- ],
423423- },
424424- ROW: {
425425- desc: 'Returns the row number of a cell reference',
426426- params: [
427427- { name: 'reference', desc: 'The cell reference to get the row number from', required: true },
428428- ],
429429- },
430430- COLUMN: {
431431- desc: 'Returns the column number of a cell reference',
432432- params: [
433433- { name: 'reference', desc: 'The cell reference to get the column number from', required: true },
434434- ],
435435- },
436436-437437- // --- Information ---
438438- ISNUMBER: {
439439- desc: 'Returns TRUE if the value is a number',
440440- params: [
441441- { name: 'value', desc: 'The value to check', required: true },
442442- ],
443443- },
444444- ISTEXT: {
445445- desc: 'Returns TRUE if the value is text',
446446- params: [
447447- { name: 'value', desc: 'The value to check', required: true },
448448- ],
449449- },
450450- ISBLANK: {
451451- desc: 'Returns TRUE if the value is blank',
452452- params: [
453453- { name: 'value', desc: 'The value to check', required: true },
454454- ],
455455- },
456456- ISERROR: {
457457- desc: 'Returns TRUE if the value is any error value',
458458- params: [
459459- { name: 'value', desc: 'The value to check for an error', required: true },
460460- ],
461461- },
462462- ISNA: {
463463- desc: 'Returns TRUE if the value is the #N/A error',
464464- params: [
465465- { name: 'value', desc: 'The value to check', required: true },
466466- ],
467467- },
468468- ISLOGICAL: {
469469- desc: 'Returns TRUE if the value is a logical value (TRUE or FALSE)',
470470- params: [
471471- { name: 'value', desc: 'The value to check', required: true },
472472- ],
473473- },
474474- TYPE: {
475475- desc: 'Returns the type of a value (1=number, 2=text, 4=logical, 16=error, 64=array)',
476476- params: [
477477- { name: 'value', desc: 'The value whose type to determine', required: true },
478478- ],
479479- },
480480- N: {
481481- desc: 'Converts a value to a number',
482482- params: [
483483- { name: 'value', desc: 'The value to convert (TRUE=1, FALSE=0, text=0)', required: true },
484484- ],
485485- },
486486- T: {
487487- desc: 'Returns the value if it is text, otherwise returns empty string',
488488- params: [
489489- { name: 'value', desc: 'The value to test and return if text', required: true },
490490- ],
491491- },
492492-493493- // --- Math (additional) ---
494494- SUMPRODUCT: {
495495- desc: 'Returns the sum of the products of corresponding array elements',
496496- params: [
497497- { name: 'array1', desc: 'First array of values to multiply', required: true },
498498- { name: 'array2', desc: 'Additional arrays to multiply element-wise', required: false },
499499- ],
500500- },
501501- PRODUCT: {
502502- desc: 'Multiplies all the numbers given as arguments',
503503- params: [
504504- { name: 'number1', desc: 'First number to multiply', required: true },
505505- { name: 'number2', desc: 'Additional numbers to multiply', required: false },
506506- ],
507507- },
508508- SIGN: {
509509- desc: 'Returns the sign of a number: 1 if positive, -1 if negative, 0 if zero',
510510- params: [
511511- { name: 'number', desc: 'The number to get the sign of', required: true },
512512- ],
513513- },
514514- EVEN: {
515515- desc: 'Rounds a number up to the nearest even integer',
516516- params: [
517517- { name: 'number', desc: 'The number to round up', required: true },
518518- ],
519519- },
520520- ODD: {
521521- desc: 'Rounds a number up to the nearest odd integer',
522522- params: [
523523- { name: 'number', desc: 'The number to round up', required: true },
524524- ],
525525- },
526526- CEILING: {
527527- desc: 'Rounds a number up to the nearest multiple of significance',
528528- params: [
529529- { name: 'number', desc: 'The number to round', required: true },
530530- { name: 'significance', desc: 'The multiple to round up to', required: true },
531531- ],
532532- },
533533- FLOOR: {
534534- desc: 'Rounds a number down to the nearest multiple of significance',
535535- params: [
536536- { name: 'number', desc: 'The number to round', required: true },
537537- { name: 'significance', desc: 'The multiple to round down to', required: true },
538538- ],
539539- },
540540- FACT: {
541541- desc: 'Returns the factorial of a number',
542542- params: [
543543- { name: 'number', desc: 'The non-negative integer to compute the factorial of', required: true },
544544- ],
545545- },
546546- COMBIN: {
547547- desc: 'Returns the number of combinations for a given number of items',
548548- params: [
549549- { name: 'n', desc: 'The total number of items', required: true },
550550- { name: 'k', desc: 'The number of items to choose', required: true },
551551- ],
552552- },
553553- GCD: {
554554- desc: 'Returns the greatest common divisor of two numbers',
555555- params: [
556556- { name: 'a', desc: 'First integer', required: true },
557557- { name: 'b', desc: 'Second integer', required: true },
558558- ],
559559- },
560560- LCM: {
561561- desc: 'Returns the least common multiple of two numbers',
562562- params: [
563563- { name: 'a', desc: 'First integer', required: true },
564564- { name: 'b', desc: 'Second integer', required: true },
565565- ],
566566- },
567567- QUOTIENT: {
568568- desc: 'Returns the integer portion of a division',
569569- params: [
570570- { name: 'numerator', desc: 'The dividend', required: true },
571571- { name: 'denominator', desc: 'The divisor', required: true },
572572- ],
573573- },
574574-575575- // --- Trigonometric ---
576576- SIN: {
577577- desc: 'Returns the sine of an angle (in radians)',
578578- params: [
579579- { name: 'angle', desc: 'The angle in radians', required: true },
580580- ],
581581- },
582582- COS: {
583583- desc: 'Returns the cosine of an angle (in radians)',
584584- params: [
585585- { name: 'angle', desc: 'The angle in radians', required: true },
586586- ],
587587- },
588588- TAN: {
589589- desc: 'Returns the tangent of an angle (in radians)',
590590- params: [
591591- { name: 'angle', desc: 'The angle in radians', required: true },
592592- ],
593593- },
594594- ASIN: {
595595- desc: 'Returns the arcsine (inverse sine) of a number',
596596- params: [
597597- { name: 'value', desc: 'A value between -1 and 1', required: true },
598598- ],
599599- },
600600- ACOS: {
601601- desc: 'Returns the arccosine (inverse cosine) of a number',
602602- params: [
603603- { name: 'value', desc: 'A value between -1 and 1', required: true },
604604- ],
605605- },
606606- ATAN: {
607607- desc: 'Returns the arctangent (inverse tangent) of a number',
608608- params: [
609609- { name: 'value', desc: 'The number to get the arctangent of', required: true },
610610- ],
611611- },
612612- ATAN2: {
613613- desc: 'Returns the arctangent from x and y coordinates',
614614- params: [
615615- { name: 'x_num', desc: 'The x-coordinate', required: true },
616616- { name: 'y_num', desc: 'The y-coordinate', required: true },
617617- ],
618618- },
619619- DEGREES: {
620620- desc: 'Converts radians to degrees',
621621- params: [
622622- { name: 'radians', desc: 'The angle in radians to convert', required: true },
623623- ],
624624- },
625625- RADIANS: {
626626- desc: 'Converts degrees to radians',
627627- params: [
628628- { name: 'degrees', desc: 'The angle in degrees to convert', required: true },
629629- ],
630630- },
631631-632632- // --- Text (additional) ---
633633- PROPER: {
634634- desc: 'Capitalizes the first letter of each word in a text string',
635635- params: [
636636- { name: 'text', desc: 'The text to capitalize', required: true },
637637- ],
638638- },
639639- REPT: {
640640- desc: 'Repeats text a given number of times',
641641- params: [
642642- { name: 'text', desc: 'The text to repeat', required: true },
643643- { name: 'number_times', desc: 'Number of times to repeat', required: true },
644644- ],
645645- },
646646- EXACT: {
647647- desc: 'Checks whether two text strings are exactly the same (case-sensitive)',
648648- params: [
649649- { name: 'text1', desc: 'First text string', required: true },
650650- { name: 'text2', desc: 'Second text string', required: true },
651651- ],
652652- },
653653- REPLACE: {
654654- desc: 'Replaces part of a text string with a different text string by position',
655655- params: [
656656- { name: 'old_text', desc: 'The original text', required: true },
657657- { name: 'start_num', desc: 'Position of first character to replace (1-based)', required: true },
658658- { name: 'num_chars', desc: 'Number of characters to replace', required: true },
659659- { name: 'new_text', desc: 'The replacement text', required: true },
660660- ],
661661- },
662662- CLEAN: {
663663- desc: 'Removes all non-printable characters from text',
664664- params: [
665665- { name: 'text', desc: 'The text to clean', required: true },
666666- ],
667667- },
668668- CHAR: {
669669- desc: 'Returns the character specified by a number (character code)',
670670- params: [
671671- { name: 'number', desc: 'The character code (1-255)', required: true },
672672- ],
673673- },
674674- CODE: {
675675- desc: 'Returns the numeric code for the first character in a text string',
676676- params: [
677677- { name: 'text', desc: 'The text to get the code from', required: true },
678678- ],
679679- },
680680-681681- // --- Date/Time (additional) ---
682682- HOUR: {
683683- desc: 'Returns the hour of a time value (0-23)',
684684- params: [
685685- { name: 'serial_number', desc: 'The time to extract the hour from', required: true },
686686- ],
687687- },
688688- MINUTE: {
689689- desc: 'Returns the minutes of a time value (0-59)',
690690- params: [
691691- { name: 'serial_number', desc: 'The time to extract minutes from', required: true },
692692- ],
693693- },
694694- SECOND: {
695695- desc: 'Returns the seconds of a time value (0-59)',
696696- params: [
697697- { name: 'serial_number', desc: 'The time to extract seconds from', required: true },
698698- ],
699699- },
700700- WEEKDAY: {
701701- desc: 'Returns the day of the week for a date',
702702- params: [
703703- { name: 'serial_number', desc: 'The date to find the day of week for', required: true },
704704- { name: 'return_type', desc: '1=Sun-Sat (1-7), 2=Mon-Sun (1-7), 3=Mon-Sun (0-6)', required: false },
705705- ],
706706- },
707707- EDATE: {
708708- desc: 'Returns the date that is a given number of months before or after a start date',
709709- params: [
710710- { name: 'start_date', desc: 'The starting date', required: true },
711711- { name: 'months', desc: 'Number of months to add (negative for before)', required: true },
712712- ],
713713- },
714714- EOMONTH: {
715715- desc: 'Returns the last day of the month a given number of months before or after a start date',
716716- params: [
717717- { name: 'start_date', desc: 'The starting date', required: true },
718718- { name: 'months', desc: 'Number of months to offset', required: true },
719719- ],
720720- },
721721- DAYS: {
722722- desc: 'Returns the number of days between two dates',
723723- params: [
724724- { name: 'end_date', desc: 'The end date', required: true },
725725- { name: 'start_date', desc: 'The start date', required: true },
726726- ],
727727- },
728728- NETWORKDAYS: {
729729- desc: 'Returns the number of working days between two dates (excluding weekends)',
730730- params: [
731731- { name: 'start_date', desc: 'The start date', required: true },
732732- { name: 'end_date', desc: 'The end date', required: true },
733733- ],
734734- },
735735-736736- // --- Statistical (additional) ---
737737- LARGE: {
738738- desc: 'Returns the k-th largest value in a data set',
739739- params: [
740740- { name: 'array', desc: 'The range of data', required: true },
741741- { name: 'k', desc: 'The position (from the largest) to return', required: true },
742742- ],
743743- },
744744- SMALL: {
745745- desc: 'Returns the k-th smallest value in a data set',
746746- params: [
747747- { name: 'array', desc: 'The range of data', required: true },
748748- { name: 'k', desc: 'The position (from the smallest) to return', required: true },
749749- ],
750750- },
751751- RANK: {
752752- desc: 'Returns the rank of a number in a list of numbers',
753753- params: [
754754- { name: 'number', desc: 'The number to rank', required: true },
755755- { name: 'ref', desc: 'The list of numbers to rank against', required: true },
756756- { name: 'order', desc: '0 or omitted for descending, non-zero for ascending', required: false },
757757- ],
758758- },
759759- PERCENTILE: {
760760- desc: 'Returns the k-th percentile of values in a range',
761761- params: [
762762- { name: 'array', desc: 'The range of data', required: true },
763763- { name: 'k', desc: 'Percentile value between 0 and 1 inclusive', required: true },
764764- ],
765765- },
766766- VAR: {
767767- desc: 'Estimates variance based on a sample',
768768- params: [
769769- { name: 'number1', desc: 'First number or range', required: true },
770770- { name: 'number2', desc: 'Additional numbers or ranges', required: false },
771771- ],
772772- },
773773- VARP: {
774774- desc: 'Calculates variance based on the entire population',
775775- params: [
776776- { name: 'number1', desc: 'First number or range', required: true },
777777- { name: 'number2', desc: 'Additional numbers or ranges', required: false },
778778- ],
779779- },
780780- STDEVP: {
781781- desc: 'Calculates standard deviation based on the entire population',
782782- params: [
783783- { name: 'number1', desc: 'First number or range', required: true },
784784- { name: 'number2', desc: 'Additional numbers or ranges', required: false },
785785- ],
786786- },
787787-788788- // --- Financial ---
789789- PMT: {
790790- desc: 'Calculates the payment for a loan based on constant payments and a constant interest rate',
791791- params: [
792792- { name: 'rate', desc: 'The interest rate per period', required: true },
793793- { name: 'nper', desc: 'The total number of payment periods', required: true },
794794- { name: 'pv', desc: 'The present value (loan amount)', required: true },
795795- { name: 'fv', desc: 'Future value (default 0)', required: false },
796796- { name: 'type', desc: '0=end of period (default), 1=beginning', required: false },
797797- ],
798798- },
799799- FV: {
800800- desc: 'Returns the future value of an investment',
801801- params: [
802802- { name: 'rate', desc: 'The interest rate per period', required: true },
803803- { name: 'nper', desc: 'The total number of payment periods', required: true },
804804- { name: 'pmt', desc: 'The payment made each period', required: true },
805805- { name: 'pv', desc: 'Present value (default 0)', required: false },
806806- { name: 'type', desc: '0=end of period (default), 1=beginning', required: false },
807807- ],
808808- },
809809- PV: {
810810- desc: 'Returns the present value of an investment',
811811- params: [
812812- { name: 'rate', desc: 'The interest rate per period', required: true },
813813- { name: 'nper', desc: 'The total number of payment periods', required: true },
814814- { name: 'pmt', desc: 'The payment made each period', required: true },
815815- { name: 'fv', desc: 'Future value (default 0)', required: false },
816816- { name: 'type', desc: '0=end of period (default), 1=beginning', required: false },
817817- ],
818818- },
819819- NPV: {
820820- desc: 'Returns the net present value of an investment based on periodic cash flows and a discount rate',
821821- params: [
822822- { name: 'rate', desc: 'The discount rate per period', required: true },
823823- { name: 'value1', desc: 'First cash flow', required: true },
824824- { name: 'value2', desc: 'Additional cash flows', required: false },
825825- ],
826826- },
827827- IRR: {
828828- desc: 'Returns the internal rate of return for a series of cash flows',
829829- params: [
830830- { name: 'values', desc: 'Array of cash flows (must contain at least one positive and one negative)', required: true },
831831- { name: 'guess', desc: 'Initial guess for the rate (default 0.1)', required: false },
832832- ],
833833- },
834834-835835- // --- Lookup (additional) ---
836836- CHOOSE: {
837837- desc: 'Returns a value from a list based on an index number',
838838- params: [
839839- { name: 'index_num', desc: 'The index number (1-based) of the value to return', required: true },
840840- { name: 'value1', desc: 'First value to choose from', required: true },
841841- { name: 'value2', desc: 'Additional values', required: false },
842842- ],
843843- },
844844-845845- // --- Dynamic Array Functions ---
846846- FILTER: {
847847- desc: 'Returns a filtered version of the source range, returning only rows or columns that meet the specified conditions',
848848- params: [
849849- { name: 'range', desc: 'The data to filter', required: true },
850850- { name: 'include', desc: 'Boolean array indicating rows to include', required: true },
851851- { name: 'if_empty', desc: 'Value to return if no results match', required: false },
852852- ],
853853- },
854854- SORT: {
855855- desc: 'Sorts the rows of a given array based on the values in one or more columns',
856856- params: [
857857- { name: 'range', desc: 'The data to sort', required: true },
858858- { name: 'sort_column', desc: 'Column index to sort by (default 1)', required: false },
859859- { name: 'is_ascending', desc: 'TRUE for ascending, FALSE for descending', required: false },
860860- { name: 'by_col', desc: 'TRUE to sort columns instead of rows', required: false },
861861- ],
862862- },
863863- UNIQUE: {
864864- desc: 'Returns unique rows from a range, removing duplicates',
865865- params: [
866866- { name: 'range', desc: 'The data to filter for unique entries', required: true },
867867- { name: 'by_col', desc: 'TRUE to compare columns instead of rows', required: false },
868868- { name: 'exactly_once', desc: 'TRUE to return only entries that appear exactly once', required: false },
869869- ],
870870- },
871871- SEQUENCE: {
872872- desc: 'Generates a list of sequential numbers in an array',
873873- params: [
874874- { name: 'rows', desc: 'Number of rows to return', required: true },
875875- { name: 'columns', desc: 'Number of columns to return (default 1)', required: false },
876876- { name: 'start', desc: 'First number in the sequence (default 1)', required: false },
877877- { name: 'step', desc: 'Amount to increment each value (default 1)', required: false },
878878- ],
879879- },
880880-881881- // --- Lookup Power Functions ---
882882- XLOOKUP: {
883883- desc: 'Searches a range for a match and returns the corresponding item from a second range',
884884- params: [
885885- { name: 'search_key', desc: 'The value to search for', required: true },
886886- { name: 'lookup_range', desc: 'The range to search in', required: true },
887887- { name: 'return_range', desc: 'The range from which to return a value', required: true },
888888- { name: 'if_not_found', desc: 'Value to return if no match is found', required: false },
889889- { name: 'match_mode', desc: '0=exact (default), -1=next smaller, 1=next larger', required: false },
890890- { name: 'search_mode', desc: '1=first-to-last (default), -1=last-to-first', required: false },
891891- ],
892892- },
893893- SUMIFS: {
894894- desc: 'Returns the sum of a range based on multiple criteria',
895895- params: [
896896- { name: 'sum_range', desc: 'The range to sum', required: true },
897897- { name: 'criteria_range1', desc: 'First range to check against criteria', required: true },
898898- { name: 'criteria1', desc: 'First condition to match', required: true },
899899- { name: 'criteria_range2', desc: 'Additional range to check', required: false },
900900- { name: 'criteria2', desc: 'Additional condition', required: false },
901901- ],
902902- },
903903- COUNTIFS: {
904904- desc: 'Returns the count of a range based on multiple criteria',
905905- params: [
906906- { name: 'criteria_range1', desc: 'First range to check against criteria', required: true },
907907- { name: 'criteria1', desc: 'First condition to match', required: true },
908908- { name: 'criteria_range2', desc: 'Additional range to check', required: false },
909909- { name: 'criteria2', desc: 'Additional condition', required: false },
910910- ],
911911- },
912912- AVERAGEIFS: {
913913- desc: 'Returns the average of a range based on multiple criteria',
914914- params: [
915915- { name: 'average_range', desc: 'The range to average', required: true },
916916- { name: 'criteria_range1', desc: 'First range to check against criteria', required: true },
917917- { name: 'criteria1', desc: 'First condition to match', required: true },
918918- { name: 'criteria_range2', desc: 'Additional range to check', required: false },
919919- { name: 'criteria2', desc: 'Additional condition', required: false },
920920- ],
921921- },
922922- QUERY: {
923923- desc: 'Runs a SQL-like query against a data range',
924924- params: [
925925- { name: 'data', desc: 'The range of cells to query', required: true },
926926- { name: 'query', desc: 'SQL-like query string (SELECT, WHERE, ORDER BY, LIMIT)', required: true },
927927- { name: 'headers', desc: 'Whether the first row contains headers (default TRUE)', required: false },
928928- ],
929929- },
930930-931931- // --- Visualization ---
932932- SPARKLINE: {
933933- desc: 'Creates a miniature chart within a cell (line, bar, or win/loss)',
934934- params: [
935935- { name: 'data', desc: 'Range of numeric values to chart', required: true },
936936- { name: 'options', desc: 'Chart type string ("line", "bar", "winloss") or options object', required: false },
937937- ],
938938- },
939939-};
1515+// Re-export so existing consumers don't need to change their imports
1616+export { FUNCTION_METADATA } from './function-metadata.js';
9401794118/**
94219 * Detect which function the cursor is currently inside, and which
···9906799168 // We found the matching open paren at depth 0.
99269 // Look backwards from here to find the function name.
993993- let nameEnd = i;
99470 // Skip whitespace before the paren
99571 let k = i - 1;
99672 while (k >= 0 && text[k] === ' ') k--;
···1045121 tooltip.id = 'formula-tooltip';
10461221047123 // Build signature line with highlighted current param
10481048- const sigParts = [];
124124+ const sigParts: string[] = [];
1049125 sigParts.push(`<span class="formula-tooltip-fn">${functionName}</span>(`);
1050126 meta.params.forEach((p, idx) => {
1051127 if (idx > 0) sigParts.push(', ');
1052128 const isActive = idx === paramIndex;
1053129 const cls = isActive ? 'formula-tooltip-param-active' : 'formula-tooltip-param';
10541054- const bracket = p.required ? '' : ['[', ']'];
1055130 if (!p.required) sigParts.push('<span class="formula-tooltip-optional">[</span>');
1056131 sigParts.push(`<span class="${cls}">${p.name}</span>`);
1057132 if (!p.required) sigParts.push('<span class="formula-tooltip-optional">]</span>');
+938
src/sheets/function-metadata.ts
···11+/**
22+ * Function metadata for all supported spreadsheet functions.
33+ *
44+ * Pure data module — each entry has a description and per-parameter info
55+ * used by formula tooltips, autocomplete, and documentation.
66+ */
77+88+import type { FunctionMetadataEntry } from './types.js';
99+1010+/**
1111+ * Complete function metadata for all supported functions.
1212+ * Each entry has a description and per-parameter info.
1313+ */
1414+export const FUNCTION_METADATA: Record<string, FunctionMetadataEntry> = {
1515+ // --- Math & Stats ---
1616+ SUM: {
1717+ desc: 'Adds all numbers in a range',
1818+ params: [
1919+ { name: 'range1', desc: 'First range to sum', required: true },
2020+ { name: 'range2', desc: 'Additional ranges to sum', required: false },
2121+ ],
2222+ },
2323+ AVERAGE: {
2424+ desc: 'Returns the arithmetic mean of the arguments',
2525+ params: [
2626+ { name: 'range1', desc: 'First range to average', required: true },
2727+ { name: 'range2', desc: 'Additional ranges', required: false },
2828+ ],
2929+ },
3030+ COUNT: {
3131+ desc: 'Counts the number of cells that contain numbers',
3232+ params: [
3333+ { name: 'range1', desc: 'First range to count', required: true },
3434+ { name: 'range2', desc: 'Additional ranges', required: false },
3535+ ],
3636+ },
3737+ COUNTA: {
3838+ desc: 'Counts the number of non-empty cells',
3939+ params: [
4040+ { name: 'range1', desc: 'First range to count', required: true },
4141+ { name: 'range2', desc: 'Additional ranges', required: false },
4242+ ],
4343+ },
4444+ MIN: {
4545+ desc: 'Returns the smallest value in a set of numbers',
4646+ params: [
4747+ { name: 'range1', desc: 'First range to evaluate', required: true },
4848+ { name: 'range2', desc: 'Additional ranges', required: false },
4949+ ],
5050+ },
5151+ MAX: {
5252+ desc: 'Returns the largest value in a set of numbers',
5353+ params: [
5454+ { name: 'range1', desc: 'First range to evaluate', required: true },
5555+ { name: 'range2', desc: 'Additional ranges', required: false },
5656+ ],
5757+ },
5858+ MEDIAN: {
5959+ desc: 'Returns the median of the given numbers',
6060+ params: [
6161+ { name: 'range1', desc: 'First range of values', required: true },
6262+ { name: 'range2', desc: 'Additional ranges', required: false },
6363+ ],
6464+ },
6565+ STDEV: {
6666+ desc: 'Estimates standard deviation based on a sample',
6767+ params: [
6868+ { name: 'range1', desc: 'First range of values', required: true },
6969+ { name: 'range2', desc: 'Additional ranges', required: false },
7070+ ],
7171+ },
7272+ ADD: {
7373+ desc: 'Returns the sum of two values (equivalent to value1 + value2)',
7474+ params: [
7575+ { name: 'value1', desc: 'The first addend', required: true },
7676+ { name: 'value2', desc: 'The second addend', required: true },
7777+ ],
7878+ },
7979+ MINUS: {
8080+ desc: 'Returns the difference of two values (equivalent to value1 - value2)',
8181+ params: [
8282+ { name: 'value1', desc: 'The value to subtract from', required: true },
8383+ { name: 'value2', desc: 'The value to subtract', required: true },
8484+ ],
8585+ },
8686+ MULTIPLY: {
8787+ desc: 'Returns the product of two values (equivalent to factor1 * factor2)',
8888+ params: [
8989+ { name: 'factor1', desc: 'The first factor', required: true },
9090+ { name: 'factor2', desc: 'The second factor', required: true },
9191+ ],
9292+ },
9393+ DIVIDE: {
9494+ desc: 'Returns the result of dividing one value by another (equivalent to dividend / divisor)',
9595+ params: [
9696+ { name: 'dividend', desc: 'The number to be divided', required: true },
9797+ { name: 'divisor', desc: 'The number to divide by', required: true },
9898+ ],
9999+ },
100100+ ABS: {
101101+ desc: 'Returns the absolute value of a number',
102102+ params: [
103103+ { name: 'number', desc: 'The number to get the absolute value of', required: true },
104104+ ],
105105+ },
106106+ ROUND: {
107107+ desc: 'Rounds a number to a specified number of digits',
108108+ params: [
109109+ { name: 'number', desc: 'The number to round', required: true },
110110+ { name: 'num_digits', desc: 'Number of decimal places (default 0)', required: false },
111111+ ],
112112+ },
113113+ ROUNDUP: {
114114+ desc: 'Rounds a number up, away from zero',
115115+ params: [
116116+ { name: 'number', desc: 'The number to round up', required: true },
117117+ { name: 'num_digits', desc: 'Number of decimal places (default 0)', required: false },
118118+ ],
119119+ },
120120+ ROUNDDOWN: {
121121+ desc: 'Rounds a number down, toward zero',
122122+ params: [
123123+ { name: 'number', desc: 'The number to round down', required: true },
124124+ { name: 'num_digits', desc: 'Number of decimal places (default 0)', required: false },
125125+ ],
126126+ },
127127+ INT: {
128128+ desc: 'Rounds a number down to the nearest integer',
129129+ params: [
130130+ { name: 'number', desc: 'The number to round down', required: true },
131131+ ],
132132+ },
133133+ MOD: {
134134+ desc: 'Returns the remainder after dividing a number by a divisor',
135135+ params: [
136136+ { name: 'number', desc: 'The number to divide', required: true },
137137+ { name: 'divisor', desc: 'The divisor', required: true },
138138+ ],
139139+ },
140140+ POWER: {
141141+ desc: 'Returns a number raised to a power',
142142+ params: [
143143+ { name: 'number', desc: 'The base number', required: true },
144144+ { name: 'power', desc: 'The exponent', required: true },
145145+ ],
146146+ },
147147+ SQRT: {
148148+ desc: 'Returns the positive square root of a number',
149149+ params: [
150150+ { name: 'number', desc: 'The number to take the square root of', required: true },
151151+ ],
152152+ },
153153+ LOG: {
154154+ desc: 'Returns the logarithm of a number to a specified base',
155155+ params: [
156156+ { name: 'number', desc: 'The positive number to take the log of', required: true },
157157+ { name: 'base', desc: 'The base of the logarithm (default 10)', required: false },
158158+ ],
159159+ },
160160+ LN: {
161161+ desc: 'Returns the natural logarithm of a number',
162162+ params: [
163163+ { name: 'number', desc: 'The positive number to take the natural log of', required: true },
164164+ ],
165165+ },
166166+ EXP: {
167167+ desc: 'Returns e raised to the power of a number',
168168+ params: [
169169+ { name: 'number', desc: 'The exponent applied to the base e', required: true },
170170+ ],
171171+ },
172172+ PI: {
173173+ desc: 'Returns the value of pi (3.14159...)',
174174+ params: [],
175175+ },
176176+ RAND: {
177177+ desc: 'Returns a random number between 0 and 1',
178178+ params: [],
179179+ },
180180+181181+ // --- Logical ---
182182+ IF: {
183183+ desc: 'Returns one value if a condition is true and another if false',
184184+ params: [
185185+ { name: 'condition', desc: 'The logical test to evaluate', required: true },
186186+ { name: 'value_if_true', desc: 'Value returned when condition is true', required: true },
187187+ { name: 'value_if_false', desc: 'Value returned when condition is false', required: false },
188188+ ],
189189+ },
190190+ AND: {
191191+ desc: 'Returns TRUE if all arguments are true',
192192+ params: [
193193+ { name: 'logical1', desc: 'First condition to evaluate', required: true },
194194+ { name: 'logical2', desc: 'Additional conditions', required: false },
195195+ ],
196196+ },
197197+ OR: {
198198+ desc: 'Returns TRUE if any argument is true',
199199+ params: [
200200+ { name: 'logical1', desc: 'First condition to evaluate', required: true },
201201+ { name: 'logical2', desc: 'Additional conditions', required: false },
202202+ ],
203203+ },
204204+ NOT: {
205205+ desc: 'Reverses the logic of its argument',
206206+ params: [
207207+ { name: 'logical', desc: 'The value or expression to negate', required: true },
208208+ ],
209209+ },
210210+ IFERROR: {
211211+ desc: 'Returns a value if no error, otherwise returns an alternate value',
212212+ params: [
213213+ { name: 'value', desc: 'The value to check for an error', required: true },
214214+ { name: 'value_if_error', desc: 'Value to return if an error is found', required: true },
215215+ ],
216216+ },
217217+218218+ // --- Text ---
219219+ CONCATENATE: {
220220+ desc: 'Joins several text strings into one',
221221+ params: [
222222+ { name: 'text1', desc: 'First text string', required: true },
223223+ { name: 'text2', desc: 'Additional text strings to join', required: false },
224224+ ],
225225+ },
226226+ LEN: {
227227+ desc: 'Returns the number of characters in a text string',
228228+ params: [
229229+ { name: 'text', desc: 'The text string to measure', required: true },
230230+ ],
231231+ },
232232+ LEFT: {
233233+ desc: 'Returns the leftmost characters from a text string',
234234+ params: [
235235+ { name: 'text', desc: 'The source text string', required: true },
236236+ { name: 'num_chars', desc: 'Number of characters to extract (default 1)', required: false },
237237+ ],
238238+ },
239239+ RIGHT: {
240240+ desc: 'Returns the rightmost characters from a text string',
241241+ params: [
242242+ { name: 'text', desc: 'The source text string', required: true },
243243+ { name: 'num_chars', desc: 'Number of characters to extract (default 1)', required: false },
244244+ ],
245245+ },
246246+ MID: {
247247+ desc: 'Returns a specific number of characters from a text string',
248248+ params: [
249249+ { name: 'text', desc: 'The source text string', required: true },
250250+ { name: 'start_num', desc: 'Position of the first character (1-based)', required: true },
251251+ { name: 'num_chars', desc: 'Number of characters to extract', required: true },
252252+ ],
253253+ },
254254+ UPPER: {
255255+ desc: 'Converts text to uppercase',
256256+ params: [
257257+ { name: 'text', desc: 'The text to convert', required: true },
258258+ ],
259259+ },
260260+ LOWER: {
261261+ desc: 'Converts text to lowercase',
262262+ params: [
263263+ { name: 'text', desc: 'The text to convert', required: true },
264264+ ],
265265+ },
266266+ TRIM: {
267267+ desc: 'Removes leading and trailing spaces from text',
268268+ params: [
269269+ { name: 'text', desc: 'The text to trim', required: true },
270270+ ],
271271+ },
272272+ SUBSTITUTE: {
273273+ desc: 'Replaces occurrences of old text with new text in a string',
274274+ params: [
275275+ { name: 'text', desc: 'The text containing characters to replace', required: true },
276276+ { name: 'old_text', desc: 'The text to find and replace', required: true },
277277+ { name: 'new_text', desc: 'The replacement text', required: true },
278278+ { name: 'instance', desc: 'Which occurrence to replace (default: all)', required: false },
279279+ ],
280280+ },
281281+ FIND: {
282282+ desc: 'Finds the position of a text string within another (case-sensitive)',
283283+ params: [
284284+ { name: 'find_text', desc: 'The text to find', required: true },
285285+ { name: 'within_text', desc: 'The text to search within', required: true },
286286+ { name: 'start_num', desc: 'Position to start searching from (default 1)', required: false },
287287+ ],
288288+ },
289289+ SEARCH: {
290290+ desc: 'Finds the position of a text string within another (case-insensitive)',
291291+ params: [
292292+ { name: 'find_text', desc: 'The text to find', required: true },
293293+ { name: 'within_text', desc: 'The text to search within', required: true },
294294+ { name: 'start_num', desc: 'Position to start searching from (default 1)', required: false },
295295+ ],
296296+ },
297297+ TEXT: {
298298+ desc: 'Formats a number as text with a specified format',
299299+ params: [
300300+ { name: 'value', desc: 'The number to format', required: true },
301301+ { name: 'format_text', desc: 'Format pattern (e.g. "0.00", "#,##0")', required: true },
302302+ ],
303303+ },
304304+ VALUE: {
305305+ desc: 'Converts a text string that represents a number to a number',
306306+ params: [
307307+ { name: 'text', desc: 'The text to convert to a number', required: true },
308308+ ],
309309+ },
310310+311311+ // --- Date ---
312312+ NOW: {
313313+ desc: 'Returns the current date and time',
314314+ params: [],
315315+ },
316316+ TODAY: {
317317+ desc: 'Returns the current date',
318318+ params: [],
319319+ },
320320+ DATE: {
321321+ desc: 'Creates a date from year, month, and day components',
322322+ params: [
323323+ { name: 'year', desc: 'The year (e.g. 2024)', required: true },
324324+ { name: 'month', desc: 'The month (1-12)', required: true },
325325+ { name: 'day', desc: 'The day of the month (1-31)', required: true },
326326+ ],
327327+ },
328328+ YEAR: {
329329+ desc: 'Returns the year of a date',
330330+ params: [
331331+ { name: 'date', desc: 'The date to extract the year from', required: true },
332332+ ],
333333+ },
334334+ MONTH: {
335335+ desc: 'Returns the month of a date (1-12)',
336336+ params: [
337337+ { name: 'date', desc: 'The date to extract the month from', required: true },
338338+ ],
339339+ },
340340+ DAY: {
341341+ desc: 'Returns the day of the month of a date (1-31)',
342342+ params: [
343343+ { name: 'date', desc: 'The date to extract the day from', required: true },
344344+ ],
345345+ },
346346+347347+ // --- Lookup ---
348348+ VLOOKUP: {
349349+ desc: 'Searches first column of range for key and returns value from specified column',
350350+ params: [
351351+ { name: 'lookup_value', desc: 'The value to search for', required: true },
352352+ { name: 'table_array', desc: 'Range containing the data', required: true },
353353+ { name: 'col_index', desc: 'Column number to return (1-based)', required: true },
354354+ { name: 'range_lookup', desc: 'FALSE for exact match, TRUE for approximate', required: false },
355355+ ],
356356+ },
357357+ HLOOKUP: {
358358+ desc: 'Searches first row of range for key and returns value from specified row',
359359+ params: [
360360+ { name: 'lookup_value', desc: 'The value to search for', required: true },
361361+ { name: 'table_array', desc: 'Range containing the data', required: true },
362362+ { name: 'row_index', desc: 'Row number to return (1-based)', required: true },
363363+ { name: 'range_lookup', desc: 'FALSE for exact match, TRUE for approximate', required: false },
364364+ ],
365365+ },
366366+ INDEX: {
367367+ desc: 'Returns the value of a cell in a range at a given row and column',
368368+ params: [
369369+ { name: 'array', desc: 'The range of cells', required: true },
370370+ { name: 'row_num', desc: 'Row number in the range (1-based)', required: true },
371371+ { name: 'col_num', desc: 'Column number in the range (1-based)', required: false },
372372+ ],
373373+ },
374374+ MATCH: {
375375+ desc: 'Returns the relative position of a value in a range',
376376+ params: [
377377+ { name: 'lookup_value', desc: 'The value to search for', required: true },
378378+ { name: 'lookup_array', desc: 'The range to search', required: true },
379379+ { name: 'match_type', desc: '1 for less than, 0 for exact, -1 for greater than', required: false },
380380+ ],
381381+ },
382382+383383+ // --- Conditional ---
384384+ SUMIF: {
385385+ desc: 'Sums cells that meet a specified condition',
386386+ params: [
387387+ { name: 'range', desc: 'The range to evaluate against the criteria', required: true },
388388+ { name: 'criteria', desc: 'The condition to match (e.g. ">100")', required: true },
389389+ { name: 'sum_range', desc: 'The range to sum (default: same as range)', required: false },
390390+ ],
391391+ },
392392+ COUNTIF: {
393393+ desc: 'Counts cells that meet a specified condition',
394394+ params: [
395395+ { name: 'range', desc: 'The range to evaluate', required: true },
396396+ { name: 'criteria', desc: 'The condition to match', required: true },
397397+ ],
398398+ },
399399+ AVERAGEIF: {
400400+ desc: 'Averages cells that meet a specified condition',
401401+ params: [
402402+ { name: 'range', desc: 'The range to evaluate against the criteria', required: true },
403403+ { name: 'criteria', desc: 'The condition to match', required: true },
404404+ { name: 'average_range', desc: 'The range to average (default: same as range)', required: false },
405405+ ],
406406+ },
407407+408408+ // --- Reference ---
409409+ INDIRECT: {
410410+ desc: 'Returns the value of a cell specified by a text string reference',
411411+ params: [
412412+ { name: 'ref_text', desc: 'A cell reference as a text string (e.g. "A1", "Sheet2!B3")', required: true },
413413+ ],
414414+ },
415415+ ADDRESS: {
416416+ desc: 'Returns a cell address as text given row and column numbers',
417417+ params: [
418418+ { name: 'row_num', desc: 'The row number of the cell reference', required: true },
419419+ { name: 'col_num', desc: 'The column number of the cell reference', required: true },
420420+ { name: 'abs_num', desc: '1=absolute (default), 2=abs row, 3=abs col, 4=relative', required: false },
421421+ ],
422422+ },
423423+ ROW: {
424424+ desc: 'Returns the row number of a cell reference',
425425+ params: [
426426+ { name: 'reference', desc: 'The cell reference to get the row number from', required: true },
427427+ ],
428428+ },
429429+ COLUMN: {
430430+ desc: 'Returns the column number of a cell reference',
431431+ params: [
432432+ { name: 'reference', desc: 'The cell reference to get the column number from', required: true },
433433+ ],
434434+ },
435435+436436+ // --- Information ---
437437+ ISNUMBER: {
438438+ desc: 'Returns TRUE if the value is a number',
439439+ params: [
440440+ { name: 'value', desc: 'The value to check', required: true },
441441+ ],
442442+ },
443443+ ISTEXT: {
444444+ desc: 'Returns TRUE if the value is text',
445445+ params: [
446446+ { name: 'value', desc: 'The value to check', required: true },
447447+ ],
448448+ },
449449+ ISBLANK: {
450450+ desc: 'Returns TRUE if the value is blank',
451451+ params: [
452452+ { name: 'value', desc: 'The value to check', required: true },
453453+ ],
454454+ },
455455+ ISERROR: {
456456+ desc: 'Returns TRUE if the value is any error value',
457457+ params: [
458458+ { name: 'value', desc: 'The value to check for an error', required: true },
459459+ ],
460460+ },
461461+ ISNA: {
462462+ desc: 'Returns TRUE if the value is the #N/A error',
463463+ params: [
464464+ { name: 'value', desc: 'The value to check', required: true },
465465+ ],
466466+ },
467467+ ISLOGICAL: {
468468+ desc: 'Returns TRUE if the value is a logical value (TRUE or FALSE)',
469469+ params: [
470470+ { name: 'value', desc: 'The value to check', required: true },
471471+ ],
472472+ },
473473+ TYPE: {
474474+ desc: 'Returns the type of a value (1=number, 2=text, 4=logical, 16=error, 64=array)',
475475+ params: [
476476+ { name: 'value', desc: 'The value whose type to determine', required: true },
477477+ ],
478478+ },
479479+ N: {
480480+ desc: 'Converts a value to a number',
481481+ params: [
482482+ { name: 'value', desc: 'The value to convert (TRUE=1, FALSE=0, text=0)', required: true },
483483+ ],
484484+ },
485485+ T: {
486486+ desc: 'Returns the value if it is text, otherwise returns empty string',
487487+ params: [
488488+ { name: 'value', desc: 'The value to test and return if text', required: true },
489489+ ],
490490+ },
491491+492492+ // --- Math (additional) ---
493493+ SUMPRODUCT: {
494494+ desc: 'Returns the sum of the products of corresponding array elements',
495495+ params: [
496496+ { name: 'array1', desc: 'First array of values to multiply', required: true },
497497+ { name: 'array2', desc: 'Additional arrays to multiply element-wise', required: false },
498498+ ],
499499+ },
500500+ PRODUCT: {
501501+ desc: 'Multiplies all the numbers given as arguments',
502502+ params: [
503503+ { name: 'number1', desc: 'First number to multiply', required: true },
504504+ { name: 'number2', desc: 'Additional numbers to multiply', required: false },
505505+ ],
506506+ },
507507+ SIGN: {
508508+ desc: 'Returns the sign of a number: 1 if positive, -1 if negative, 0 if zero',
509509+ params: [
510510+ { name: 'number', desc: 'The number to get the sign of', required: true },
511511+ ],
512512+ },
513513+ EVEN: {
514514+ desc: 'Rounds a number up to the nearest even integer',
515515+ params: [
516516+ { name: 'number', desc: 'The number to round up', required: true },
517517+ ],
518518+ },
519519+ ODD: {
520520+ desc: 'Rounds a number up to the nearest odd integer',
521521+ params: [
522522+ { name: 'number', desc: 'The number to round up', required: true },
523523+ ],
524524+ },
525525+ CEILING: {
526526+ desc: 'Rounds a number up to the nearest multiple of significance',
527527+ params: [
528528+ { name: 'number', desc: 'The number to round', required: true },
529529+ { name: 'significance', desc: 'The multiple to round up to', required: true },
530530+ ],
531531+ },
532532+ FLOOR: {
533533+ desc: 'Rounds a number down to the nearest multiple of significance',
534534+ params: [
535535+ { name: 'number', desc: 'The number to round', required: true },
536536+ { name: 'significance', desc: 'The multiple to round down to', required: true },
537537+ ],
538538+ },
539539+ FACT: {
540540+ desc: 'Returns the factorial of a number',
541541+ params: [
542542+ { name: 'number', desc: 'The non-negative integer to compute the factorial of', required: true },
543543+ ],
544544+ },
545545+ COMBIN: {
546546+ desc: 'Returns the number of combinations for a given number of items',
547547+ params: [
548548+ { name: 'n', desc: 'The total number of items', required: true },
549549+ { name: 'k', desc: 'The number of items to choose', required: true },
550550+ ],
551551+ },
552552+ GCD: {
553553+ desc: 'Returns the greatest common divisor of two numbers',
554554+ params: [
555555+ { name: 'a', desc: 'First integer', required: true },
556556+ { name: 'b', desc: 'Second integer', required: true },
557557+ ],
558558+ },
559559+ LCM: {
560560+ desc: 'Returns the least common multiple of two numbers',
561561+ params: [
562562+ { name: 'a', desc: 'First integer', required: true },
563563+ { name: 'b', desc: 'Second integer', required: true },
564564+ ],
565565+ },
566566+ QUOTIENT: {
567567+ desc: 'Returns the integer portion of a division',
568568+ params: [
569569+ { name: 'numerator', desc: 'The dividend', required: true },
570570+ { name: 'denominator', desc: 'The divisor', required: true },
571571+ ],
572572+ },
573573+574574+ // --- Trigonometric ---
575575+ SIN: {
576576+ desc: 'Returns the sine of an angle (in radians)',
577577+ params: [
578578+ { name: 'angle', desc: 'The angle in radians', required: true },
579579+ ],
580580+ },
581581+ COS: {
582582+ desc: 'Returns the cosine of an angle (in radians)',
583583+ params: [
584584+ { name: 'angle', desc: 'The angle in radians', required: true },
585585+ ],
586586+ },
587587+ TAN: {
588588+ desc: 'Returns the tangent of an angle (in radians)',
589589+ params: [
590590+ { name: 'angle', desc: 'The angle in radians', required: true },
591591+ ],
592592+ },
593593+ ASIN: {
594594+ desc: 'Returns the arcsine (inverse sine) of a number',
595595+ params: [
596596+ { name: 'value', desc: 'A value between -1 and 1', required: true },
597597+ ],
598598+ },
599599+ ACOS: {
600600+ desc: 'Returns the arccosine (inverse cosine) of a number',
601601+ params: [
602602+ { name: 'value', desc: 'A value between -1 and 1', required: true },
603603+ ],
604604+ },
605605+ ATAN: {
606606+ desc: 'Returns the arctangent (inverse tangent) of a number',
607607+ params: [
608608+ { name: 'value', desc: 'The number to get the arctangent of', required: true },
609609+ ],
610610+ },
611611+ ATAN2: {
612612+ desc: 'Returns the arctangent from x and y coordinates',
613613+ params: [
614614+ { name: 'x_num', desc: 'The x-coordinate', required: true },
615615+ { name: 'y_num', desc: 'The y-coordinate', required: true },
616616+ ],
617617+ },
618618+ DEGREES: {
619619+ desc: 'Converts radians to degrees',
620620+ params: [
621621+ { name: 'radians', desc: 'The angle in radians to convert', required: true },
622622+ ],
623623+ },
624624+ RADIANS: {
625625+ desc: 'Converts degrees to radians',
626626+ params: [
627627+ { name: 'degrees', desc: 'The angle in degrees to convert', required: true },
628628+ ],
629629+ },
630630+631631+ // --- Text (additional) ---
632632+ PROPER: {
633633+ desc: 'Capitalizes the first letter of each word in a text string',
634634+ params: [
635635+ { name: 'text', desc: 'The text to capitalize', required: true },
636636+ ],
637637+ },
638638+ REPT: {
639639+ desc: 'Repeats text a given number of times',
640640+ params: [
641641+ { name: 'text', desc: 'The text to repeat', required: true },
642642+ { name: 'number_times', desc: 'Number of times to repeat', required: true },
643643+ ],
644644+ },
645645+ EXACT: {
646646+ desc: 'Checks whether two text strings are exactly the same (case-sensitive)',
647647+ params: [
648648+ { name: 'text1', desc: 'First text string', required: true },
649649+ { name: 'text2', desc: 'Second text string', required: true },
650650+ ],
651651+ },
652652+ REPLACE: {
653653+ desc: 'Replaces part of a text string with a different text string by position',
654654+ params: [
655655+ { name: 'old_text', desc: 'The original text', required: true },
656656+ { name: 'start_num', desc: 'Position of first character to replace (1-based)', required: true },
657657+ { name: 'num_chars', desc: 'Number of characters to replace', required: true },
658658+ { name: 'new_text', desc: 'The replacement text', required: true },
659659+ ],
660660+ },
661661+ CLEAN: {
662662+ desc: 'Removes all non-printable characters from text',
663663+ params: [
664664+ { name: 'text', desc: 'The text to clean', required: true },
665665+ ],
666666+ },
667667+ CHAR: {
668668+ desc: 'Returns the character specified by a number (character code)',
669669+ params: [
670670+ { name: 'number', desc: 'The character code (1-255)', required: true },
671671+ ],
672672+ },
673673+ CODE: {
674674+ desc: 'Returns the numeric code for the first character in a text string',
675675+ params: [
676676+ { name: 'text', desc: 'The text to get the code from', required: true },
677677+ ],
678678+ },
679679+680680+ // --- Date/Time (additional) ---
681681+ HOUR: {
682682+ desc: 'Returns the hour of a time value (0-23)',
683683+ params: [
684684+ { name: 'serial_number', desc: 'The time to extract the hour from', required: true },
685685+ ],
686686+ },
687687+ MINUTE: {
688688+ desc: 'Returns the minutes of a time value (0-59)',
689689+ params: [
690690+ { name: 'serial_number', desc: 'The time to extract minutes from', required: true },
691691+ ],
692692+ },
693693+ SECOND: {
694694+ desc: 'Returns the seconds of a time value (0-59)',
695695+ params: [
696696+ { name: 'serial_number', desc: 'The time to extract seconds from', required: true },
697697+ ],
698698+ },
699699+ WEEKDAY: {
700700+ desc: 'Returns the day of the week for a date',
701701+ params: [
702702+ { name: 'serial_number', desc: 'The date to find the day of week for', required: true },
703703+ { name: 'return_type', desc: '1=Sun-Sat (1-7), 2=Mon-Sun (1-7), 3=Mon-Sun (0-6)', required: false },
704704+ ],
705705+ },
706706+ EDATE: {
707707+ desc: 'Returns the date that is a given number of months before or after a start date',
708708+ params: [
709709+ { name: 'start_date', desc: 'The starting date', required: true },
710710+ { name: 'months', desc: 'Number of months to add (negative for before)', required: true },
711711+ ],
712712+ },
713713+ EOMONTH: {
714714+ desc: 'Returns the last day of the month a given number of months before or after a start date',
715715+ params: [
716716+ { name: 'start_date', desc: 'The starting date', required: true },
717717+ { name: 'months', desc: 'Number of months to offset', required: true },
718718+ ],
719719+ },
720720+ DAYS: {
721721+ desc: 'Returns the number of days between two dates',
722722+ params: [
723723+ { name: 'end_date', desc: 'The end date', required: true },
724724+ { name: 'start_date', desc: 'The start date', required: true },
725725+ ],
726726+ },
727727+ NETWORKDAYS: {
728728+ desc: 'Returns the number of working days between two dates (excluding weekends)',
729729+ params: [
730730+ { name: 'start_date', desc: 'The start date', required: true },
731731+ { name: 'end_date', desc: 'The end date', required: true },
732732+ ],
733733+ },
734734+735735+ // --- Statistical (additional) ---
736736+ LARGE: {
737737+ desc: 'Returns the k-th largest value in a data set',
738738+ params: [
739739+ { name: 'array', desc: 'The range of data', required: true },
740740+ { name: 'k', desc: 'The position (from the largest) to return', required: true },
741741+ ],
742742+ },
743743+ SMALL: {
744744+ desc: 'Returns the k-th smallest value in a data set',
745745+ params: [
746746+ { name: 'array', desc: 'The range of data', required: true },
747747+ { name: 'k', desc: 'The position (from the smallest) to return', required: true },
748748+ ],
749749+ },
750750+ RANK: {
751751+ desc: 'Returns the rank of a number in a list of numbers',
752752+ params: [
753753+ { name: 'number', desc: 'The number to rank', required: true },
754754+ { name: 'ref', desc: 'The list of numbers to rank against', required: true },
755755+ { name: 'order', desc: '0 or omitted for descending, non-zero for ascending', required: false },
756756+ ],
757757+ },
758758+ PERCENTILE: {
759759+ desc: 'Returns the k-th percentile of values in a range',
760760+ params: [
761761+ { name: 'array', desc: 'The range of data', required: true },
762762+ { name: 'k', desc: 'Percentile value between 0 and 1 inclusive', required: true },
763763+ ],
764764+ },
765765+ VAR: {
766766+ desc: 'Estimates variance based on a sample',
767767+ params: [
768768+ { name: 'number1', desc: 'First number or range', required: true },
769769+ { name: 'number2', desc: 'Additional numbers or ranges', required: false },
770770+ ],
771771+ },
772772+ VARP: {
773773+ desc: 'Calculates variance based on the entire population',
774774+ params: [
775775+ { name: 'number1', desc: 'First number or range', required: true },
776776+ { name: 'number2', desc: 'Additional numbers or ranges', required: false },
777777+ ],
778778+ },
779779+ STDEVP: {
780780+ desc: 'Calculates standard deviation based on the entire population',
781781+ params: [
782782+ { name: 'number1', desc: 'First number or range', required: true },
783783+ { name: 'number2', desc: 'Additional numbers or ranges', required: false },
784784+ ],
785785+ },
786786+787787+ // --- Financial ---
788788+ PMT: {
789789+ desc: 'Calculates the payment for a loan based on constant payments and a constant interest rate',
790790+ params: [
791791+ { name: 'rate', desc: 'The interest rate per period', required: true },
792792+ { name: 'nper', desc: 'The total number of payment periods', required: true },
793793+ { name: 'pv', desc: 'The present value (loan amount)', required: true },
794794+ { name: 'fv', desc: 'Future value (default 0)', required: false },
795795+ { name: 'type', desc: '0=end of period (default), 1=beginning', required: false },
796796+ ],
797797+ },
798798+ FV: {
799799+ desc: 'Returns the future value of an investment',
800800+ params: [
801801+ { name: 'rate', desc: 'The interest rate per period', required: true },
802802+ { name: 'nper', desc: 'The total number of payment periods', required: true },
803803+ { name: 'pmt', desc: 'The payment made each period', required: true },
804804+ { name: 'pv', desc: 'Present value (default 0)', required: false },
805805+ { name: 'type', desc: '0=end of period (default), 1=beginning', required: false },
806806+ ],
807807+ },
808808+ PV: {
809809+ desc: 'Returns the present value of an investment',
810810+ params: [
811811+ { name: 'rate', desc: 'The interest rate per period', required: true },
812812+ { name: 'nper', desc: 'The total number of payment periods', required: true },
813813+ { name: 'pmt', desc: 'The payment made each period', required: true },
814814+ { name: 'fv', desc: 'Future value (default 0)', required: false },
815815+ { name: 'type', desc: '0=end of period (default), 1=beginning', required: false },
816816+ ],
817817+ },
818818+ NPV: {
819819+ desc: 'Returns the net present value of an investment based on periodic cash flows and a discount rate',
820820+ params: [
821821+ { name: 'rate', desc: 'The discount rate per period', required: true },
822822+ { name: 'value1', desc: 'First cash flow', required: true },
823823+ { name: 'value2', desc: 'Additional cash flows', required: false },
824824+ ],
825825+ },
826826+ IRR: {
827827+ desc: 'Returns the internal rate of return for a series of cash flows',
828828+ params: [
829829+ { name: 'values', desc: 'Array of cash flows (must contain at least one positive and one negative)', required: true },
830830+ { name: 'guess', desc: 'Initial guess for the rate (default 0.1)', required: false },
831831+ ],
832832+ },
833833+834834+ // --- Lookup (additional) ---
835835+ CHOOSE: {
836836+ desc: 'Returns a value from a list based on an index number',
837837+ params: [
838838+ { name: 'index_num', desc: 'The index number (1-based) of the value to return', required: true },
839839+ { name: 'value1', desc: 'First value to choose from', required: true },
840840+ { name: 'value2', desc: 'Additional values', required: false },
841841+ ],
842842+ },
843843+844844+ // --- Dynamic Array Functions ---
845845+ FILTER: {
846846+ desc: 'Returns a filtered version of the source range, returning only rows or columns that meet the specified conditions',
847847+ params: [
848848+ { name: 'range', desc: 'The data to filter', required: true },
849849+ { name: 'include', desc: 'Boolean array indicating rows to include', required: true },
850850+ { name: 'if_empty', desc: 'Value to return if no results match', required: false },
851851+ ],
852852+ },
853853+ SORT: {
854854+ desc: 'Sorts the rows of a given array based on the values in one or more columns',
855855+ params: [
856856+ { name: 'range', desc: 'The data to sort', required: true },
857857+ { name: 'sort_column', desc: 'Column index to sort by (default 1)', required: false },
858858+ { name: 'is_ascending', desc: 'TRUE for ascending, FALSE for descending', required: false },
859859+ { name: 'by_col', desc: 'TRUE to sort columns instead of rows', required: false },
860860+ ],
861861+ },
862862+ UNIQUE: {
863863+ desc: 'Returns unique rows from a range, removing duplicates',
864864+ params: [
865865+ { name: 'range', desc: 'The data to filter for unique entries', required: true },
866866+ { name: 'by_col', desc: 'TRUE to compare columns instead of rows', required: false },
867867+ { name: 'exactly_once', desc: 'TRUE to return only entries that appear exactly once', required: false },
868868+ ],
869869+ },
870870+ SEQUENCE: {
871871+ desc: 'Generates a list of sequential numbers in an array',
872872+ params: [
873873+ { name: 'rows', desc: 'Number of rows to return', required: true },
874874+ { name: 'columns', desc: 'Number of columns to return (default 1)', required: false },
875875+ { name: 'start', desc: 'First number in the sequence (default 1)', required: false },
876876+ { name: 'step', desc: 'Amount to increment each value (default 1)', required: false },
877877+ ],
878878+ },
879879+880880+ // --- Lookup Power Functions ---
881881+ XLOOKUP: {
882882+ desc: 'Searches a range for a match and returns the corresponding item from a second range',
883883+ params: [
884884+ { name: 'search_key', desc: 'The value to search for', required: true },
885885+ { name: 'lookup_range', desc: 'The range to search in', required: true },
886886+ { name: 'return_range', desc: 'The range from which to return a value', required: true },
887887+ { name: 'if_not_found', desc: 'Value to return if no match is found', required: false },
888888+ { name: 'match_mode', desc: '0=exact (default), -1=next smaller, 1=next larger', required: false },
889889+ { name: 'search_mode', desc: '1=first-to-last (default), -1=last-to-first', required: false },
890890+ ],
891891+ },
892892+ SUMIFS: {
893893+ desc: 'Returns the sum of a range based on multiple criteria',
894894+ params: [
895895+ { name: 'sum_range', desc: 'The range to sum', required: true },
896896+ { name: 'criteria_range1', desc: 'First range to check against criteria', required: true },
897897+ { name: 'criteria1', desc: 'First condition to match', required: true },
898898+ { name: 'criteria_range2', desc: 'Additional range to check', required: false },
899899+ { name: 'criteria2', desc: 'Additional condition', required: false },
900900+ ],
901901+ },
902902+ COUNTIFS: {
903903+ desc: 'Returns the count of a range based on multiple criteria',
904904+ params: [
905905+ { name: 'criteria_range1', desc: 'First range to check against criteria', required: true },
906906+ { name: 'criteria1', desc: 'First condition to match', required: true },
907907+ { name: 'criteria_range2', desc: 'Additional range to check', required: false },
908908+ { name: 'criteria2', desc: 'Additional condition', required: false },
909909+ ],
910910+ },
911911+ AVERAGEIFS: {
912912+ desc: 'Returns the average of a range based on multiple criteria',
913913+ params: [
914914+ { name: 'average_range', desc: 'The range to average', required: true },
915915+ { name: 'criteria_range1', desc: 'First range to check against criteria', required: true },
916916+ { name: 'criteria1', desc: 'First condition to match', required: true },
917917+ { name: 'criteria_range2', desc: 'Additional range to check', required: false },
918918+ { name: 'criteria2', desc: 'Additional condition', required: false },
919919+ ],
920920+ },
921921+ QUERY: {
922922+ desc: 'Runs a SQL-like query against a data range',
923923+ params: [
924924+ { name: 'data', desc: 'The range of cells to query', required: true },
925925+ { name: 'query', desc: 'SQL-like query string (SELECT, WHERE, ORDER BY, LIMIT)', required: true },
926926+ { name: 'headers', desc: 'Whether the first row contains headers (default TRUE)', required: false },
927927+ ],
928928+ },
929929+930930+ // --- Visualization ---
931931+ SPARKLINE: {
932932+ desc: 'Creates a miniature chart within a cell (line, bar, or win/loss)',
933933+ params: [
934934+ { name: 'data', desc: 'Range of numeric values to chart', required: true },
935935+ { name: 'options', desc: 'Chart type string ("line", "bar", "winloss") or options object', required: false },
936936+ ],
937937+ },
938938+};