(core) Improve suggestions for formula autocomplete

Summary:
- Make suggestions less case-sensitive (not entirely case-insensitive, but
  allow top-level suggestions to match in all-lowercase)
- Add function signatures to suggestions for Grist functions.
- Excel-like functions that are present but not implemented are no longer
  offered as suggestions.

Test Plan:
Added a test case on python side, and a browser test case for how suggestions
are rendered and inserted.

Reviewers: paulfitz

Reviewed By: paulfitz

Differential Revision: https://phab.getgrist.com/D2608
This commit is contained in:
Dmitry S
2020-09-11 14:18:03 -04:00
parent 2fbd3f1706
commit 003029bf8a
9 changed files with 259 additions and 16 deletions

View File

@@ -9,9 +9,11 @@ import re
import column
from functions import date # pylint: disable=import-error
from functions.unimplemented import unimplemented
from usertypes import AltText # pylint: disable=import-error
from records import Record, RecordSet
@unimplemented
def ISBLANK(value):
"""
Returns whether a value refers to an empty cell. It isn't implemented in Grist. To check for an
@@ -486,6 +488,7 @@ def NA():
return float('nan')
@unimplemented
def TYPE(value):
"""
Returns a number associated with the type of data passed into the function. This is not
@@ -493,6 +496,7 @@ def TYPE(value):
"""
raise NotImplementedError()
@unimplemented
def CELL(info_type, reference):
"""
Returns the requested information about the specified cell. This is not implemented in Grist

View File

@@ -1,57 +1,72 @@
# pylint: disable=redefined-builtin, line-too-long
from unimplemented import unimplemented
@unimplemented
def ADDRESS(row, column, absolute_relative_mode, use_a1_notation, sheet):
"""Returns a cell reference as a string."""
raise NotImplementedError()
@unimplemented
def CHOOSE(index, choice1, choice2):
"""Returns an element from a list of choices based on index."""
raise NotImplementedError()
@unimplemented
def COLUMN(cell_reference=None):
"""Returns the column number of a specified cell, with `A=1`."""
raise NotImplementedError()
@unimplemented
def COLUMNS(range):
"""Returns the number of columns in a specified array or range."""
raise NotImplementedError()
@unimplemented
def GETPIVOTDATA(value_name, any_pivot_table_cell, original_column_1, pivot_item_1=None, *args):
"""Extracts an aggregated value from a pivot table that corresponds to the specified row and column headings."""
raise NotImplementedError()
@unimplemented
def HLOOKUP(search_key, range, index, is_sorted):
"""Horizontal lookup. Searches across the first row of a range for a key and returns the value of a specified cell in the column found."""
raise NotImplementedError()
@unimplemented
def HYPERLINK(url, link_label):
"""Creates a hyperlink inside a cell."""
raise NotImplementedError()
@unimplemented
def INDEX(reference, row, column):
"""Returns the content of a cell, specified by row and column offset."""
raise NotImplementedError()
@unimplemented
def INDIRECT(cell_reference_as_string):
"""Returns a cell reference specified by a string."""
raise NotImplementedError()
@unimplemented
def LOOKUP(search_key, search_range_or_search_result_array, result_range=None):
"""Looks through a row or column for a key and returns the value of the cell in a result range located in the same position as the search row or column."""
raise NotImplementedError()
@unimplemented
def MATCH(search_key, range, search_type):
"""Returns the relative position of an item in a range that matches a specified value."""
raise NotImplementedError()
@unimplemented
def OFFSET(cell_reference, offset_rows, offset_columns, height, width):
"""Returns a range reference shifted a specified number of rows and columns from a starting cell reference."""
raise NotImplementedError()
@unimplemented
def ROW(cell_reference):
"""Returns the row number of a specified cell."""
raise NotImplementedError()
@unimplemented
def ROWS(range):
"""Returns the number of rows in a specified array or range."""
raise NotImplementedError()

View File

@@ -7,6 +7,7 @@ import operator
import random
from functions.info import ISNUMBER, ISLOGICAL
from functions.unimplemented import unimplemented
import roman
# Iterates through elements of iterable arguments, or through individual args when not iterable.
@@ -727,6 +728,7 @@ def SQRTPI(value):
"""
return _math.sqrt(_math.pi * value)
@unimplemented
def SUBTOTAL(function_code, range1, range2):
"""
Returns a subtotal for a vertical range of cells using a specified aggregation function.
@@ -755,12 +757,14 @@ def SUM(value1, *more_values):
return sum(_chain_numeric_a(value1, *more_values))
@unimplemented
def SUMIF(records, criterion, sum_range):
"""
Returns a conditional sum across a range.
"""
raise NotImplementedError()
@unimplemented
def SUMIFS(sum_range, criteria_range1, criterion1, *args):
"""
Returns the sum of a range depending on multiple criteria.
@@ -782,6 +786,7 @@ def SUMPRODUCT(array1, *more_arrays):
"""
return sum(reduce(operator.mul, values) for values in itertools.izip(array1, *more_arrays))
@unimplemented
def SUMSQ(value1, value2):
"""
Returns the sum of the squares of a series of numbers and/or cells.

View File

@@ -3,7 +3,7 @@
from math import _chain, _chain_numeric, _chain_numeric_a
from info import ISNUMBER, ISLOGICAL
from date import DATE # pylint: disable=unused-import
from unimplemented import unimplemented
def _average(iterable):
total, count = 0.0, 0
@@ -24,6 +24,7 @@ def _default_if_empty(iterable, default):
yield default
@unimplemented
def AVEDEV(value1, value2):
"""Calculates the average of the magnitudes of deviations of data from a dataset's mean."""
raise NotImplementedError()
@@ -95,14 +96,17 @@ def AVERAGE_WEIGHTED(pairs):
return sum_value / sum_weight
@unimplemented
def AVERAGEIF(criteria_range, criterion, average_range=None):
"""Returns the average of a range depending on criteria."""
raise NotImplementedError()
@unimplemented
def AVERAGEIFS(average_range, criteria_range1, criterion1, *args):
"""Returns the average of a range depending on multiple criteria."""
raise NotImplementedError()
@unimplemented
def BINOMDIST(num_successes, num_trials, prob_success, cumulative):
"""
Calculates the probability of drawing a certain number of successes (or a maximum number of
@@ -111,10 +115,12 @@ def BINOMDIST(num_successes, num_trials, prob_success, cumulative):
"""
raise NotImplementedError()
@unimplemented
def CONFIDENCE(alpha, standard_deviation, pop_size):
"""Calculates the width of half the confidence interval for a normal distribution."""
raise NotImplementedError()
@unimplemented
def CORREL(data_y, data_x):
"""Calculates r, the Pearson product-moment correlation coefficient of a dataset."""
raise NotImplementedError()
@@ -156,22 +162,27 @@ def COUNTA(value, *more_values):
return sum(1 for v in _chain(value, *more_values))
@unimplemented
def COVAR(data_y, data_x):
"""Calculates the covariance of a dataset."""
raise NotImplementedError()
@unimplemented
def CRITBINOM(num_trials, prob_success, target_prob):
"""Calculates the smallest value for which the cumulative binomial distribution is greater than or equal to a specified criteria."""
raise NotImplementedError()
@unimplemented
def DEVSQ(value1, value2):
"""Calculates the sum of squares of deviations based on a sample."""
raise NotImplementedError()
@unimplemented
def EXPONDIST(x, lambda_, cumulative):
"""Returns the value of the exponential distribution function with a specified lambda at a specified value."""
raise NotImplementedError()
@unimplemented
def F_DIST(x, degrees_freedom1, degrees_freedom2, cumulative):
"""
Calculates the left-tailed F probability distribution (degree of diversity) for two data sets
@@ -180,6 +191,7 @@ def F_DIST(x, degrees_freedom1, degrees_freedom2, cumulative):
"""
raise NotImplementedError()
@unimplemented
def F_DIST_RT(x, degrees_freedom1, degrees_freedom2):
"""
Calculates the right-tailed F probability distribution (degree of diversity) for two data sets
@@ -188,6 +200,7 @@ def F_DIST_RT(x, degrees_freedom1, degrees_freedom2):
"""
raise NotImplementedError()
@unimplemented
def FDIST(x, degrees_freedom1, degrees_freedom2):
"""
Calculates the right-tailed F probability distribution (degree of diversity) for two data sets
@@ -196,46 +209,57 @@ def FDIST(x, degrees_freedom1, degrees_freedom2):
"""
raise NotImplementedError()
@unimplemented
def FISHER(value):
"""Returns the Fisher transformation of a specified value."""
raise NotImplementedError()
@unimplemented
def FISHERINV(value):
"""Returns the inverse Fisher transformation of a specified value."""
raise NotImplementedError()
@unimplemented
def FORECAST(x, data_y, data_x):
"""Calculates the expected y-value for a specified x based on a linear regression of a dataset."""
raise NotImplementedError()
@unimplemented
def GEOMEAN(value1, value2):
"""Calculates the geometric mean of a dataset."""
raise NotImplementedError()
@unimplemented
def HARMEAN(value1, value2):
"""Calculates the harmonic mean of a dataset."""
raise NotImplementedError()
@unimplemented
def HYPGEOMDIST(num_successes, num_draws, successes_in_pop, pop_size):
"""Calculates the probability of drawing a certain number of successes in a certain number of tries given a population of a certain size containing a certain number of successes, without replacement of draws."""
raise NotImplementedError()
@unimplemented
def INTERCEPT(data_y, data_x):
"""Calculates the y-value at which the line resulting from linear regression of a dataset will intersect the y-axis (x=0)."""
raise NotImplementedError()
@unimplemented
def KURT(value1, value2):
"""Calculates the kurtosis of a dataset, which describes the shape, and in particular the "peakedness" of that dataset."""
raise NotImplementedError()
@unimplemented
def LARGE(data, n):
"""Returns the nth largest element from a data set, where n is user-defined."""
raise NotImplementedError()
@unimplemented
def LOGINV(x, mean, standard_deviation):
"""Returns the value of the inverse log-normal cumulative distribution with given mean and standard deviation at a specified value."""
raise NotImplementedError()
@unimplemented
def LOGNORMDIST(x, mean, standard_deviation):
"""Returns the value of the log-normal cumulative distribution with given mean and standard deviation at a specified value."""
raise NotImplementedError()
@@ -364,14 +388,17 @@ def MINA(value, *more_values):
return min(_default_if_empty(_chain_numeric_a(value, *more_values), 0))
@unimplemented
def MODE(value1, value2):
"""Returns the most commonly occurring value in a dataset."""
raise NotImplementedError()
@unimplemented
def NEGBINOMDIST(num_failures, num_successes, prob_success):
"""Calculates the probability of drawing a certain number of failures before a certain number of successes given a probability of success in independent trials."""
raise NotImplementedError()
@unimplemented
def NORMDIST(x, mean, standard_deviation, cumulative):
"""
Returns the value of the normal distribution function (or normal cumulative distribution
@@ -379,42 +406,52 @@ def NORMDIST(x, mean, standard_deviation, cumulative):
"""
raise NotImplementedError()
@unimplemented
def NORMINV(x, mean, standard_deviation):
"""Returns the value of the inverse normal distribution function for a specified value, mean, and standard deviation."""
raise NotImplementedError()
@unimplemented
def NORMSDIST(x):
"""Returns the value of the standard normal cumulative distribution function for a specified value."""
raise NotImplementedError()
@unimplemented
def NORMSINV(x):
"""Returns the value of the inverse standard normal distribution function for a specified value."""
raise NotImplementedError()
@unimplemented
def PEARSON(data_y, data_x):
"""Calculates r, the Pearson product-moment correlation coefficient of a dataset."""
raise NotImplementedError()
@unimplemented
def PERCENTILE(data, percentile):
"""Returns the value at a given percentile of a dataset."""
raise NotImplementedError()
@unimplemented
def PERCENTRANK(data, value, significant_digits=None):
"""Returns the percentage rank (percentile) of a specified value in a dataset."""
raise NotImplementedError()
@unimplemented
def PERCENTRANK_EXC(data, value, significant_digits=None):
"""Returns the percentage rank (percentile) from 0 to 1 exclusive of a specified value in a dataset."""
raise NotImplementedError()
@unimplemented
def PERCENTRANK_INC(data, value, significant_digits=None):
"""Returns the percentage rank (percentile) from 0 to 1 inclusive of a specified value in a dataset."""
raise NotImplementedError()
@unimplemented
def PERMUT(n, k):
"""Returns the number of ways to choose some number of objects from a pool of a given size of objects, considering order."""
raise NotImplementedError()
@unimplemented
def POISSON(x, mean, cumulative):
"""
Returns the value of the Poisson distribution function (or Poisson cumulative distribution
@@ -422,42 +459,52 @@ def POISSON(x, mean, cumulative):
"""
raise NotImplementedError()
@unimplemented
def PROB(data, probabilities, low_limit, high_limit=None):
"""Given a set of values and corresponding probabilities, calculates the probability that a value chosen at random falls between two limits."""
raise NotImplementedError()
@unimplemented
def QUARTILE(data, quartile_number):
"""Returns a value nearest to a specified quartile of a dataset."""
raise NotImplementedError()
@unimplemented
def RANK(value, data, is_ascending=None):
"""Returns the rank of a specified value in a dataset."""
raise NotImplementedError()
@unimplemented
def RANK_AVG(value, data, is_ascending=None):
"""Returns the rank of a specified value in a dataset. If there is more than one entry of the same value in the dataset, the average rank of the entries will be returned."""
raise NotImplementedError()
@unimplemented
def RANK_EQ(value, data, is_ascending=None):
"""Returns the rank of a specified value in a dataset. If there is more than one entry of the same value in the dataset, the top rank of the entries will be returned."""
raise NotImplementedError()
@unimplemented
def RSQ(data_y, data_x):
"""Calculates the square of r, the Pearson product-moment correlation coefficient of a dataset."""
raise NotImplementedError()
@unimplemented
def SKEW(value1, value2):
"""Calculates the skewness of a dataset, which describes the symmetry of that dataset about the mean."""
raise NotImplementedError()
@unimplemented
def SLOPE(data_y, data_x):
"""Calculates the slope of the line resulting from linear regression of a dataset."""
raise NotImplementedError()
@unimplemented
def SMALL(data, n):
"""Returns the nth smallest element from a data set, where n is user-defined."""
raise NotImplementedError()
@unimplemented
def STANDARDIZE(value, mean, standard_deviation):
"""Calculates the normalized equivalent of a random variable given mean and standard deviation of the distribution."""
raise NotImplementedError()
@@ -559,50 +606,62 @@ def STDEVPA(value, *more_values):
"""
return _stddev(list(_chain_numeric_a(value, *more_values)), 0)
@unimplemented
def STEYX(data_y, data_x):
"""Calculates the standard error of the predicted y-value for each x in the regression of a dataset."""
raise NotImplementedError()
@unimplemented
def T_INV(probability, degrees_freedom):
"""Calculates the negative inverse of the one-tailed TDIST function."""
raise NotImplementedError()
@unimplemented
def T_INV_2T(probability, degrees_freedom):
"""Calculates the inverse of the two-tailed TDIST function."""
raise NotImplementedError()
@unimplemented
def TDIST(x, degrees_freedom, tails):
"""Calculates the probability for Student's t-distribution with a given input (x)."""
raise NotImplementedError()
@unimplemented
def TINV(probability, degrees_freedom):
"""Calculates the inverse of the two-tailed TDIST function."""
raise NotImplementedError()
@unimplemented
def TRIMMEAN(data, exclude_proportion):
"""Calculates the mean of a dataset excluding some proportion of data from the high and low ends of the dataset."""
raise NotImplementedError()
@unimplemented
def TTEST(range1, range2, tails, type):
"""Returns the probability associated with t-test. Determines whether two samples are likely to have come from the same two underlying populations that have the same mean."""
raise NotImplementedError()
@unimplemented
def VAR(value1, value2):
"""Calculates the variance based on a sample."""
raise NotImplementedError()
@unimplemented
def VARA(value1, value2):
"""Calculates an estimate of variance based on a sample, setting text to the value `0`."""
raise NotImplementedError()
@unimplemented
def VARP(value1, value2):
"""Calculates the variance based on an entire population."""
raise NotImplementedError()
@unimplemented
def VARPA(value1, value2):
"""Calculates the variance based on an entire population, setting text to the value `0`."""
raise NotImplementedError()
@unimplemented
def WEIBULL(x, shape, scale, cumulative):
"""
Returns the value of the Weibull distribution function (or Weibull cumulative distribution
@@ -610,6 +669,7 @@ def WEIBULL(x, shape, scale, cumulative):
"""
raise NotImplementedError()
@unimplemented
def ZTEST(data, value, standard_deviation):
"""Returns the two-tailed P-value of a Z-test with standard distribution."""
raise NotImplementedError()

View File

@@ -5,6 +5,7 @@ import dateutil.parser
import numbers
import re
from unimplemented import unimplemented
from usertypes import AltText # pylint: disable=import-error
def CHAR(table_number):
@@ -499,6 +500,7 @@ def T(value):
str(value) if isinstance(value, AltText) else "")
@unimplemented
def TEXT(number, format_type):
"""
Converts a number into text according to a specified format. It is not yet implemented in

View File

@@ -0,0 +1,11 @@
"""
Decorator that marks functions as not implemented. It sets func.unimplemented=True.
Usage:
@unimplemented
def func(...):
raise NotImplemented
"""
def unimplemented(func):
func.unimplemented = True
return func