mirror of
https://github.com/dream-num/univer.git
synced 2026-09-24 15:32:18 +08:00
feat(formula): adapt formula format for SSC service compatibility (#6109)
This commit is contained in:
@@ -265,6 +265,12 @@ export interface ICellData {
|
||||
*/
|
||||
ref?: Nullable<string>;
|
||||
|
||||
/**
|
||||
* New Excel formulas need to add prefixes for differentiation, e.g., _xlfn._xlws. _xlfn. _xlws. _xludf.
|
||||
* @ignore
|
||||
*/
|
||||
xf?: Nullable<string>;
|
||||
|
||||
/**
|
||||
* Id of the formula.
|
||||
*/
|
||||
|
||||
@@ -115,6 +115,10 @@ export interface IArrayFormulaRangeType {
|
||||
[unitId: string]: Nullable<{ [sheetId: string]: IObjectMatrixPrimitiveType<IRange> }>;
|
||||
}
|
||||
|
||||
export interface IArrayFormulaEmbeddedMap {
|
||||
[unitId: string]: Nullable<{ [sheetId: string]: IObjectMatrixPrimitiveType<boolean> }>;
|
||||
}
|
||||
|
||||
export interface IFeatureDirtyRangeType {
|
||||
[unitId: string]: Nullable<{ [sheetId: string]: IRange[] }>;
|
||||
}
|
||||
|
||||
@@ -15,13 +15,14 @@
|
||||
*/
|
||||
|
||||
import type { IAccessor, IMutation } from '@univerjs/core';
|
||||
import { CommandType } from '@univerjs/core';
|
||||
import type { IArrayFormulaEmbeddedMap, IArrayFormulaRangeType, IArrayFormulaUnitCellType } from '../../basics/common';
|
||||
|
||||
import type { IArrayFormulaRangeType, IArrayFormulaUnitCellType } from '../../basics/common';
|
||||
import { CommandType } from '@univerjs/core';
|
||||
|
||||
export interface ISetArrayFormulaDataMutationParams {
|
||||
arrayFormulaRange: IArrayFormulaRangeType;
|
||||
arrayFormulaCellData: IArrayFormulaUnitCellType;
|
||||
arrayFormulaEmbedded: IArrayFormulaEmbeddedMap;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,7 @@ export interface ISetDefinedNameMutationParam extends ISetDefinedNameMutationSea
|
||||
comment?: string;
|
||||
localSheetId?: string;
|
||||
hidden?: boolean;
|
||||
formulaOrRefStringWithPrefix?: string; // for excel
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,7 +66,7 @@ export const SetDefinedNameMutation: IMutation<ISetDefinedNameMutationParam> = {
|
||||
}
|
||||
|
||||
const definedNamesService = accessor.get(IDefinedNamesService);
|
||||
const { id, unitId, name, formulaOrRefString, comment, hidden, localSheetId } = params as ISetDefinedNameMutationParam;
|
||||
const { id, unitId, name, formulaOrRefString, comment, hidden, localSheetId, formulaOrRefStringWithPrefix } = params as ISetDefinedNameMutationParam;
|
||||
definedNamesService.registerDefinedName(unitId, {
|
||||
id,
|
||||
name: name.trim(),
|
||||
@@ -73,6 +74,7 @@ export const SetDefinedNameMutation: IMutation<ISetDefinedNameMutationParam> = {
|
||||
comment: comment?.trim(),
|
||||
hidden,
|
||||
localSheetId,
|
||||
formulaOrRefStringWithPrefix,
|
||||
});
|
||||
|
||||
return true;
|
||||
|
||||
@@ -145,14 +145,14 @@ export class CalculateController extends Disposable {
|
||||
}
|
||||
|
||||
private async _applyResult(data: IAllRuntimeData) {
|
||||
const { unitData, unitOtherData, arrayFormulaRange, arrayFormulaCellData, clearArrayFormulaCellData } = data;
|
||||
const { unitData, unitOtherData, arrayFormulaRange, arrayFormulaCellData, clearArrayFormulaCellData, arrayFormulaEmbedded } = data;
|
||||
|
||||
if (!unitData) {
|
||||
console.error('No sheetData from Formula Engine!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (arrayFormulaRange) {
|
||||
if (arrayFormulaRange || arrayFormulaEmbedded) {
|
||||
this._formulaDataModel.clearPreviousArrayFormulaCellData(clearArrayFormulaCellData);
|
||||
this._formulaDataModel.mergeArrayFormulaCellData(arrayFormulaCellData);
|
||||
this._formulaDataModel.mergeArrayFormulaRange(arrayFormulaRange);
|
||||
@@ -162,6 +162,7 @@ export class CalculateController extends Disposable {
|
||||
{
|
||||
arrayFormulaRange: this._formulaDataModel.getArrayFormulaRange(),
|
||||
arrayFormulaCellData: this._formulaDataModel.getArrayFormulaCellData(),
|
||||
arrayFormulaEmbedded,
|
||||
},
|
||||
{
|
||||
onlyLocal: true,
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
import type { LexerNode } from '../lexer-node';
|
||||
|
||||
import { AbsoluteRefType } from '@univerjs/core';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { ErrorType } from '../../../basics/error-type';
|
||||
|
||||
@@ -17,11 +17,12 @@
|
||||
import type { IRange, Nullable } from '@univerjs/core';
|
||||
import type { IDirtyUnitSheetDefinedNameMap } from '../../basics/common';
|
||||
|
||||
import type { IFunctionNames } from '../../basics/function';
|
||||
import type { IDefinedNamesServiceParam } from '../../services/defined-names.service';
|
||||
import type { ISequenceArray, ISequenceNode } from '../utils/sequence';
|
||||
import { AbsoluteRefType, Disposable, isValidRange, moveRangeByOffset, Tools } from '@univerjs/core';
|
||||
import { FormulaAstLRU } from '../../basics/cache-lru';
|
||||
|
||||
import { FormulaAstLRU } from '../../basics/cache-lru';
|
||||
import { ERROR_TYPE_COUNT_ARRAY, ERROR_TYPE_SET, ErrorType } from '../../basics/error-type';
|
||||
import { isFormulaLexerToken, isTokenCannotBeAtEnd, isTokenCannotPrecedeSuffixToken } from '../../basics/match-token';
|
||||
import { regexTestSingeRange } from '../../basics/regex';
|
||||
@@ -1878,4 +1879,8 @@ export class LexerTreeBuilder extends Disposable {
|
||||
currentLexerNode: this._currentLexerNode,
|
||||
});
|
||||
}
|
||||
|
||||
getNewFormulaWithPrefix(formulaString: string, hasFunction: (functionToken: IFunctionNames) => boolean): string | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,12 +100,15 @@ export class FunctionNode extends BaseAstNode {
|
||||
|
||||
const resultVariant = await this._calculateAsync(variants);
|
||||
let result: FunctionVariantType;
|
||||
|
||||
if (resultVariant.isAsyncObject() || resultVariant.isAsyncArrayObject()) {
|
||||
result = await (resultVariant as AsyncObject | AsyncArrayObject).getValue();
|
||||
} else {
|
||||
result = resultVariant as FunctionVariantType;
|
||||
}
|
||||
|
||||
this._setEmbeddedArrayFormulaToResult(result);
|
||||
|
||||
this._setRefData(result);
|
||||
|
||||
this.setValue(result);
|
||||
@@ -143,6 +146,8 @@ export class FunctionNode extends BaseAstNode {
|
||||
|
||||
const resultVariant = this._calculate(variants) as FunctionVariantType;
|
||||
|
||||
this._setEmbeddedArrayFormulaToResult(resultVariant);
|
||||
|
||||
this._setRefData(resultVariant);
|
||||
|
||||
this.setValue(resultVariant as FunctionVariantType);
|
||||
@@ -152,6 +157,17 @@ export class FunctionNode extends BaseAstNode {
|
||||
return this._functionExecutor.isArgumentsIgnoreNumberPattern();
|
||||
}
|
||||
|
||||
/**
|
||||
* If it contains an array formula, set the current cell to the cache and send itself as a ref outward
|
||||
*/
|
||||
private _setEmbeddedArrayFormulaToResult(result: FunctionVariantType) {
|
||||
if (!result.isArray()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._runtimeService.setUnitArrayFormulaEmbeddedMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility handling for special functions.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { IFunctionNames } from '../basics/function';
|
||||
import { FUNCTION_NAMES_DATE } from './date/function-names';
|
||||
import { FUNCTION_NAMES_ENGINEERING } from './engineering/function-names';
|
||||
import { FUNCTION_NAMES_FINANCIAL } from './financial/function-names';
|
||||
import { FUNCTION_NAMES_INFORMATION } from './information/function-names';
|
||||
import { FUNCTION_NAMES_LOGICAL } from './logical/function-names';
|
||||
import { FUNCTION_NAMES_LOOKUP } from './lookup/function-names';
|
||||
import { FUNCTION_NAMES_MATH } from './math/function-names';
|
||||
import { FUNCTION_NAMES_STATISTICAL } from './statistical/function-names';
|
||||
import { FUNCTION_NAMES_TEXT } from './text/function-names';
|
||||
import { FUNCTION_NAMES_WEB } from './web/function-names';
|
||||
|
||||
export const NEW_EXCEL_FUNCTIONS = new Set<IFunctionNames>([
|
||||
// Trigonometric & Math extensions
|
||||
FUNCTION_NAMES_MATH.ACOT,
|
||||
FUNCTION_NAMES_MATH.ACOTH,
|
||||
FUNCTION_NAMES_MATH.ARABIC,
|
||||
FUNCTION_NAMES_MATH.BASE,
|
||||
FUNCTION_NAMES_MATH.CEILING_MATH,
|
||||
FUNCTION_NAMES_MATH.CEILING_PRECISE,
|
||||
FUNCTION_NAMES_MATH.COMBINA,
|
||||
FUNCTION_NAMES_MATH.COT,
|
||||
FUNCTION_NAMES_MATH.COTH,
|
||||
FUNCTION_NAMES_MATH.CSC,
|
||||
FUNCTION_NAMES_MATH.CSCH,
|
||||
FUNCTION_NAMES_MATH.DECIMAL,
|
||||
FUNCTION_NAMES_MATH.FLOOR_MATH,
|
||||
FUNCTION_NAMES_MATH.FLOOR_PRECISE,
|
||||
FUNCTION_NAMES_MATH.MUNIT,
|
||||
FUNCTION_NAMES_MATH.RANDARRAY,
|
||||
FUNCTION_NAMES_MATH.SEC,
|
||||
FUNCTION_NAMES_MATH.SECH,
|
||||
FUNCTION_NAMES_MATH.SEQUENCE,
|
||||
|
||||
// Dynamic array functions
|
||||
FUNCTION_NAMES_LOOKUP.CHOOSECOLS,
|
||||
FUNCTION_NAMES_LOOKUP.CHOOSEROWS,
|
||||
FUNCTION_NAMES_LOOKUP.DROP,
|
||||
FUNCTION_NAMES_LOOKUP.EXPAND,
|
||||
FUNCTION_NAMES_LOOKUP.FILTER,
|
||||
FUNCTION_NAMES_LOOKUP.FORMULATEXT,
|
||||
FUNCTION_NAMES_LOOKUP.HSTACK,
|
||||
FUNCTION_NAMES_LOOKUP.SORT,
|
||||
FUNCTION_NAMES_LOOKUP.SORTBY,
|
||||
FUNCTION_NAMES_LOOKUP.TAKE,
|
||||
FUNCTION_NAMES_LOOKUP.TOCOL,
|
||||
FUNCTION_NAMES_LOOKUP.TOROW,
|
||||
FUNCTION_NAMES_LOOKUP.UNIQUE,
|
||||
FUNCTION_NAMES_LOOKUP.VSTACK,
|
||||
FUNCTION_NAMES_LOOKUP.WRAPCOLS,
|
||||
FUNCTION_NAMES_LOOKUP.WRAPROWS,
|
||||
FUNCTION_NAMES_LOOKUP.XLOOKUP,
|
||||
FUNCTION_NAMES_LOOKUP.XMATCH,
|
||||
|
||||
// Bitwise and complex math
|
||||
FUNCTION_NAMES_ENGINEERING.BITAND,
|
||||
FUNCTION_NAMES_ENGINEERING.BITLSHIFT,
|
||||
FUNCTION_NAMES_ENGINEERING.BITOR,
|
||||
FUNCTION_NAMES_ENGINEERING.BITRSHIFT,
|
||||
FUNCTION_NAMES_ENGINEERING.BITXOR,
|
||||
FUNCTION_NAMES_ENGINEERING.ERF_PRECISE,
|
||||
FUNCTION_NAMES_ENGINEERING.ERFC_PRECISE,
|
||||
FUNCTION_NAMES_ENGINEERING.IMCOSH,
|
||||
FUNCTION_NAMES_ENGINEERING.IMCOT,
|
||||
FUNCTION_NAMES_ENGINEERING.IMCSC,
|
||||
FUNCTION_NAMES_ENGINEERING.IMCSCH,
|
||||
FUNCTION_NAMES_ENGINEERING.IMSEC,
|
||||
FUNCTION_NAMES_ENGINEERING.IMSECH,
|
||||
FUNCTION_NAMES_ENGINEERING.IMSINH,
|
||||
FUNCTION_NAMES_ENGINEERING.IMTAN,
|
||||
|
||||
// Information functions
|
||||
FUNCTION_NAMES_INFORMATION.ISFORMULA,
|
||||
FUNCTION_NAMES_INFORMATION.SHEET,
|
||||
FUNCTION_NAMES_INFORMATION.SHEETS,
|
||||
|
||||
// Logical extensions
|
||||
FUNCTION_NAMES_LOGICAL.IFNA,
|
||||
FUNCTION_NAMES_LOGICAL.IFS,
|
||||
FUNCTION_NAMES_LOGICAL.SWITCH,
|
||||
FUNCTION_NAMES_LOGICAL.XOR,
|
||||
|
||||
// Statistical and text extensions
|
||||
FUNCTION_NAMES_STATISTICAL.BETA_DIST,
|
||||
FUNCTION_NAMES_STATISTICAL.BETA_INV,
|
||||
FUNCTION_NAMES_STATISTICAL.BINOM_DIST,
|
||||
FUNCTION_NAMES_STATISTICAL.BINOM_DIST_RANGE,
|
||||
FUNCTION_NAMES_STATISTICAL.BINOM_INV,
|
||||
FUNCTION_NAMES_STATISTICAL.CHISQ_DIST,
|
||||
FUNCTION_NAMES_STATISTICAL.CHISQ_DIST_RT,
|
||||
FUNCTION_NAMES_STATISTICAL.CHISQ_INV,
|
||||
FUNCTION_NAMES_STATISTICAL.CHISQ_INV_RT,
|
||||
FUNCTION_NAMES_STATISTICAL.CHISQ_TEST,
|
||||
FUNCTION_NAMES_STATISTICAL.CONFIDENCE_NORM,
|
||||
FUNCTION_NAMES_STATISTICAL.CONFIDENCE_T,
|
||||
FUNCTION_NAMES_STATISTICAL.COVARIANCE_P,
|
||||
FUNCTION_NAMES_STATISTICAL.COVARIANCE_S,
|
||||
FUNCTION_NAMES_STATISTICAL.EXPON_DIST,
|
||||
FUNCTION_NAMES_STATISTICAL.F_DIST,
|
||||
FUNCTION_NAMES_STATISTICAL.F_DIST_RT,
|
||||
FUNCTION_NAMES_STATISTICAL.F_INV,
|
||||
FUNCTION_NAMES_STATISTICAL.F_INV_RT,
|
||||
FUNCTION_NAMES_STATISTICAL.F_TEST,
|
||||
FUNCTION_NAMES_STATISTICAL.FORECAST_LINEAR,
|
||||
FUNCTION_NAMES_STATISTICAL.GAMMA,
|
||||
FUNCTION_NAMES_STATISTICAL.GAMMA_DIST,
|
||||
FUNCTION_NAMES_STATISTICAL.GAMMA_INV,
|
||||
FUNCTION_NAMES_STATISTICAL.GAMMALN_PRECISE,
|
||||
FUNCTION_NAMES_STATISTICAL.GAUSS,
|
||||
FUNCTION_NAMES_STATISTICAL.HYPGEOM_DIST,
|
||||
FUNCTION_NAMES_STATISTICAL.LOGNORM_DIST,
|
||||
FUNCTION_NAMES_STATISTICAL.LOGNORM_INV,
|
||||
FUNCTION_NAMES_STATISTICAL.MAXIFS,
|
||||
FUNCTION_NAMES_STATISTICAL.MINIFS,
|
||||
FUNCTION_NAMES_STATISTICAL.MODE_MULT,
|
||||
FUNCTION_NAMES_STATISTICAL.MODE_SNGL,
|
||||
FUNCTION_NAMES_STATISTICAL.NEGBINOM_DIST,
|
||||
FUNCTION_NAMES_STATISTICAL.NORM_DIST,
|
||||
FUNCTION_NAMES_STATISTICAL.NORM_INV,
|
||||
FUNCTION_NAMES_STATISTICAL.NORM_S_DIST,
|
||||
FUNCTION_NAMES_STATISTICAL.NORM_S_INV,
|
||||
FUNCTION_NAMES_STATISTICAL.PERCENTILE_EXC,
|
||||
FUNCTION_NAMES_STATISTICAL.PERCENTILE_INC,
|
||||
FUNCTION_NAMES_STATISTICAL.PERCENTRANK_EXC,
|
||||
FUNCTION_NAMES_STATISTICAL.PERCENTRANK_INC,
|
||||
FUNCTION_NAMES_STATISTICAL.PERMUTATIONA,
|
||||
FUNCTION_NAMES_STATISTICAL.PHI,
|
||||
FUNCTION_NAMES_STATISTICAL.POISSON_DIST,
|
||||
FUNCTION_NAMES_STATISTICAL.QUARTILE_EXC,
|
||||
FUNCTION_NAMES_STATISTICAL.QUARTILE_INC,
|
||||
FUNCTION_NAMES_STATISTICAL.RANK_AVG,
|
||||
FUNCTION_NAMES_STATISTICAL.RANK_EQ,
|
||||
FUNCTION_NAMES_STATISTICAL.SKEW_P,
|
||||
FUNCTION_NAMES_STATISTICAL.STDEV_P,
|
||||
FUNCTION_NAMES_STATISTICAL.STDEV_S,
|
||||
FUNCTION_NAMES_STATISTICAL.T_DIST,
|
||||
FUNCTION_NAMES_STATISTICAL.T_DIST_2T,
|
||||
FUNCTION_NAMES_STATISTICAL.T_DIST_RT,
|
||||
FUNCTION_NAMES_STATISTICAL.T_INV,
|
||||
FUNCTION_NAMES_STATISTICAL.T_INV_2T,
|
||||
FUNCTION_NAMES_STATISTICAL.T_TEST,
|
||||
FUNCTION_NAMES_STATISTICAL.VAR_P,
|
||||
FUNCTION_NAMES_STATISTICAL.VAR_S,
|
||||
FUNCTION_NAMES_STATISTICAL.WEIBULL_DIST,
|
||||
FUNCTION_NAMES_STATISTICAL.Z_TEST,
|
||||
|
||||
// Text and encoding
|
||||
FUNCTION_NAMES_TEXT.ARRAYTOTEXT,
|
||||
FUNCTION_NAMES_WEB.ENCODEURL,
|
||||
FUNCTION_NAMES_TEXT.NUMBERVALUE,
|
||||
FUNCTION_NAMES_TEXT.TEXTAFTER,
|
||||
FUNCTION_NAMES_TEXT.TEXTBEFORE,
|
||||
FUNCTION_NAMES_TEXT.TEXTJOIN,
|
||||
FUNCTION_NAMES_TEXT.TEXTSPLIT,
|
||||
FUNCTION_NAMES_TEXT.UNICHAR,
|
||||
FUNCTION_NAMES_TEXT.UNICODE,
|
||||
FUNCTION_NAMES_TEXT.VALUETOTEXT,
|
||||
|
||||
// Date & time
|
||||
FUNCTION_NAMES_DATE.DAYS,
|
||||
FUNCTION_NAMES_DATE.ISOWEEKNUM,
|
||||
FUNCTION_NAMES_FINANCIAL.PDURATION,
|
||||
FUNCTION_NAMES_FINANCIAL.RRI,
|
||||
|
||||
FUNCTION_NAMES_LOGICAL.BYCOL,
|
||||
FUNCTION_NAMES_LOGICAL.BYROW,
|
||||
FUNCTION_NAMES_LOGICAL.MAKEARRAY,
|
||||
FUNCTION_NAMES_LOGICAL.MAP,
|
||||
FUNCTION_NAMES_LOGICAL.REDUCE,
|
||||
FUNCTION_NAMES_LOGICAL.SCAN,
|
||||
]);
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
export type {
|
||||
IArrayFormulaEmbeddedMap,
|
||||
IArrayFormulaRangeType,
|
||||
IArrayFormulaUnitCellType,
|
||||
IDirtyUnitFeatureMap,
|
||||
@@ -42,7 +43,8 @@ export { includeFormulaLexerToken, isFormulaLexerToken, normalizeSheetName } fro
|
||||
export { matchRefDrawToken } from './basics/match-token';
|
||||
export { isReferenceString } from './basics/regex';
|
||||
export { convertUnitDataToRuntime } from './basics/runtime';
|
||||
export { compareToken, matchToken, operatorToken } from './basics/token';
|
||||
export { compareToken, matchToken, OPERATOR_TOKEN_SET, operatorToken, prefixToken, SUFFIX_TOKEN_SET } from './basics/token';
|
||||
export { DEFAULT_TOKEN_LAMBDA_FUNCTION_NAME, DEFAULT_TOKEN_LET_FUNCTION_NAME, DEFAULT_TOKEN_TYPE_LAMBDA_PARAMETER, DEFAULT_TOKEN_TYPE_PARAMETER, DEFAULT_TOKEN_TYPE_ROOT } from './basics/token-type';
|
||||
export { RegisterFunctionMutation } from './commands/mutations/register-function.mutation';
|
||||
export { type ISetArrayFormulaDataMutationParams, SetArrayFormulaDataMutation } from './commands/mutations/set-array-formula-data.mutation';
|
||||
export { type ISetDefinedNameMutationParam, type ISetDefinedNameMutationSearchParam, RemoveDefinedNameMutation, SetDefinedNameMutation } from './commands/mutations/set-defined-name.mutation';
|
||||
@@ -142,6 +144,7 @@ export { FUNCTION_NAMES_LOOKUP } from './functions/lookup/function-names';
|
||||
export { functionMath } from './functions/math/function-map';
|
||||
export { FUNCTION_NAMES_MATH } from './functions/math/function-names';
|
||||
export { functionMeta } from './functions/meta/function-map';
|
||||
export { NEW_EXCEL_FUNCTIONS } from './functions/new-excel-functions';
|
||||
export { functionStatistical } from './functions/statistical/function-map';
|
||||
export { FUNCTION_NAMES_STATISTICAL } from './functions/statistical/function-names';
|
||||
export { functionText } from './functions/text/function-map';
|
||||
|
||||
@@ -123,8 +123,6 @@ export class UniverFormulaEnginePlugin extends Plugin {
|
||||
[GlobalComputingStatusService],
|
||||
// Models
|
||||
[FormulaDataModel],
|
||||
// Engine
|
||||
[LexerTreeBuilder],
|
||||
//Controllers
|
||||
[FormulaController],
|
||||
[SetSuperTableController],
|
||||
@@ -165,6 +163,7 @@ export class UniverFormulaEnginePlugin extends Plugin {
|
||||
}
|
||||
|
||||
protected _initializeWithOverride() {
|
||||
this._injector.add([LexerTreeBuilder, { useClass: LexerTreeBuilder }]);
|
||||
if (!this._config?.notExecuteFormula) {
|
||||
// only worker
|
||||
const dependencies: Dependency[] = [
|
||||
|
||||
@@ -27,7 +27,7 @@ export interface IDefinedNamesServiceParam {
|
||||
comment?: string;
|
||||
localSheetId?: string;
|
||||
hidden?: boolean;
|
||||
|
||||
formulaOrRefStringWithPrefix?: string; // for excel
|
||||
}
|
||||
|
||||
export interface IDefinedNamesServiceFocusParam extends IDefinedNamesServiceParam {
|
||||
@@ -75,6 +75,8 @@ export interface IDefinedNamesService {
|
||||
|
||||
getWorksheetByRef(unitId: string, ref: string): Nullable<Worksheet>;
|
||||
|
||||
getAllDefinedNames(): IDefinedNameMap;
|
||||
|
||||
}
|
||||
export class DefinedNamesService extends Disposable implements IDefinedNamesService {
|
||||
// 18.2.6 definedNames (Defined Names)
|
||||
@@ -217,6 +219,10 @@ export class DefinedNamesService extends Disposable implements IDefinedNamesServ
|
||||
return size !== 0;
|
||||
}
|
||||
|
||||
getAllDefinedNames() {
|
||||
return this._definedNameMap;
|
||||
}
|
||||
|
||||
private _update() {
|
||||
this._update$.next(null);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import type { ICellData, IRange, Nullable } from '@univerjs/core';
|
||||
import type {
|
||||
IArrayFormulaEmbeddedMap,
|
||||
IArrayFormulaRangeType,
|
||||
IFeatureDirtyRangeType,
|
||||
IRuntimeOtherUnitDataType,
|
||||
@@ -65,6 +66,7 @@ export enum FormulaExecutedStateType {
|
||||
export interface IAllRuntimeData {
|
||||
unitData: IRuntimeUnitDataType;
|
||||
arrayFormulaRange: IArrayFormulaRangeType;
|
||||
arrayFormulaEmbedded: IArrayFormulaEmbeddedMap;
|
||||
unitOtherData: IRuntimeOtherUnitDataType;
|
||||
functionsExecutedState: FormulaExecutedStateType;
|
||||
arrayFormulaCellData: IRuntimeUnitDataType;
|
||||
@@ -177,6 +179,10 @@ export interface IFormulaRuntimeService {
|
||||
setRuntimeFeatureRange(featureId: string, featureRange: IFeatureDirtyRangeType): void;
|
||||
|
||||
clearReferenceAndNumberformatCache(): void;
|
||||
|
||||
getUnitArrayFormulaEmbeddedMap(): IArrayFormulaEmbeddedMap;
|
||||
|
||||
setUnitArrayFormulaEmbeddedMap(): void;
|
||||
}
|
||||
|
||||
export class FormulaRuntimeService extends Disposable implements IFormulaRuntimeService {
|
||||
@@ -199,6 +205,8 @@ export class FormulaRuntimeService extends Disposable implements IFormulaRuntime
|
||||
|
||||
private _unitArrayFormulaRange: IArrayFormulaRangeType = {};
|
||||
|
||||
private _unitArrayFormulaEmbeddedMap: IArrayFormulaEmbeddedMap = {};
|
||||
|
||||
private _runtimeArrayFormulaCellData: IRuntimeUnitDataType = {};
|
||||
|
||||
private _runtimeClearArrayFormulaCellData: IRuntimeUnitDataType = {};
|
||||
@@ -355,6 +363,7 @@ export class FormulaRuntimeService extends Disposable implements IFormulaRuntime
|
||||
this._runtimeData = {};
|
||||
this._runtimeOtherData = {};
|
||||
this._unitArrayFormulaRange = {};
|
||||
this._unitArrayFormulaEmbeddedMap = {};
|
||||
this._runtimeArrayFormulaCellData = {};
|
||||
this._runtimeClearArrayFormulaCellData = {};
|
||||
|
||||
@@ -661,6 +670,32 @@ export class FormulaRuntimeService extends Disposable implements IFormulaRuntime
|
||||
return this._unitArrayFormulaRange;
|
||||
}
|
||||
|
||||
getUnitArrayFormulaEmbeddedMap() {
|
||||
return this._unitArrayFormulaEmbeddedMap;
|
||||
}
|
||||
|
||||
setUnitArrayFormulaEmbeddedMap() {
|
||||
const unitId = this._currentUnitId;
|
||||
const sheetId = this._currentSubUnitId;
|
||||
const rowIndex = this._currentRow;
|
||||
const columnIndex = this._currentColumn;
|
||||
|
||||
const arrayFormulaEmbeddedMap = this._unitArrayFormulaEmbeddedMap;
|
||||
if (arrayFormulaEmbeddedMap[unitId] == null) {
|
||||
arrayFormulaEmbeddedMap[unitId] = {};
|
||||
}
|
||||
|
||||
if (arrayFormulaEmbeddedMap[unitId][sheetId] == null) {
|
||||
arrayFormulaEmbeddedMap[unitId][sheetId] = {};
|
||||
}
|
||||
|
||||
if (arrayFormulaEmbeddedMap[unitId][sheetId][rowIndex] == null) {
|
||||
arrayFormulaEmbeddedMap[unitId][sheetId][rowIndex] = {};
|
||||
}
|
||||
|
||||
arrayFormulaEmbeddedMap[unitId][sheetId][rowIndex][columnIndex] = true;
|
||||
}
|
||||
|
||||
getRuntimeOtherData() {
|
||||
return this._runtimeOtherData;
|
||||
}
|
||||
@@ -693,6 +728,7 @@ export class FormulaRuntimeService extends Disposable implements IFormulaRuntime
|
||||
return {
|
||||
unitData: this.getUnitData(),
|
||||
arrayFormulaRange: this.getUnitArrayFormula(),
|
||||
arrayFormulaEmbedded: this.getUnitArrayFormulaEmbeddedMap(),
|
||||
unitOtherData: this.getRuntimeOtherData(),
|
||||
functionsExecutedState: this._functionsExecutedState,
|
||||
arrayFormulaCellData: this.getRuntimeArrayFormulaCellData(),
|
||||
|
||||
+100
-6
@@ -15,11 +15,11 @@
|
||||
*/
|
||||
|
||||
import type { ICellData, ICommandInfo } from '@univerjs/core';
|
||||
import type { IArrayFormulaRangeType, IArrayFormulaUnitCellType, ISetArrayFormulaDataMutationParams } from '@univerjs/engine-formula';
|
||||
import type { IArrayFormulaEmbeddedMap, IArrayFormulaRangeType, IArrayFormulaUnitCellType, ISetArrayFormulaDataMutationParams, ISetDefinedNameMutationParam } from '@univerjs/engine-formula';
|
||||
import type { ISetRangeValuesMutationParams } from '@univerjs/sheets';
|
||||
import type { IUniverSheetsFormulaBaseConfig } from './config.schema';
|
||||
import { CellValueType, Disposable, ICommandService, IConfigService, Inject, InterceptorEffectEnum, isRealNum, ObjectMatrix } from '@univerjs/core';
|
||||
import { FormulaDataModel, serializeRange, SetArrayFormulaDataMutation, stripErrorMargin } from '@univerjs/engine-formula';
|
||||
import { FormulaDataModel, IDefinedNamesService, IFunctionService, LexerTreeBuilder, serializeRange, SetArrayFormulaDataMutation, SetDefinedNameMutation, stripErrorMargin } from '@univerjs/engine-formula';
|
||||
import { INTERCEPTOR_POINT, SetRangeValuesMutation, SheetInterceptorService } from '@univerjs/sheets';
|
||||
import { PLUGIN_CONFIG_KEY_BASE } from './config.schema';
|
||||
|
||||
@@ -28,7 +28,10 @@ export class ArrayFormulaCellInterceptorController extends Disposable {
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
@IConfigService private readonly _configService: IConfigService,
|
||||
@Inject(SheetInterceptorService) private _sheetInterceptorService: SheetInterceptorService,
|
||||
@Inject(FormulaDataModel) private readonly _formulaDataModel: FormulaDataModel
|
||||
@Inject(FormulaDataModel) private readonly _formulaDataModel: FormulaDataModel,
|
||||
@Inject(LexerTreeBuilder) private readonly _lexerTreeBuilder: LexerTreeBuilder,
|
||||
@IFunctionService private readonly _functionService: IFunctionService,
|
||||
@IDefinedNamesService private readonly _definedNamesService: IDefinedNamesService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -55,19 +58,78 @@ export class ArrayFormulaCellInterceptorController extends Disposable {
|
||||
return;
|
||||
}
|
||||
|
||||
const { arrayFormulaRange, arrayFormulaCellData } = params;
|
||||
const { arrayFormulaRange, arrayFormulaCellData, arrayFormulaEmbedded } = params;
|
||||
this._formulaDataModel.setArrayFormulaRange(arrayFormulaRange);
|
||||
this._formulaDataModel.setArrayFormulaCellData(arrayFormulaCellData);
|
||||
|
||||
// Note the logic should only be executed in exporting and hence shall be set once.
|
||||
if (this._configService.getConfig<IUniverSheetsFormulaBaseConfig>(PLUGIN_CONFIG_KEY_BASE)?.writeArrayFormulaToSnapshot) {
|
||||
this._writeArrayFormulaToSnapshot(arrayFormulaRange, arrayFormulaCellData);
|
||||
this._writeArrayFormulaToSnapshot(arrayFormulaRange, arrayFormulaCellData, arrayFormulaEmbedded);
|
||||
this._addPrefixToFunctionSnapshot();
|
||||
this._addPreixToDefinedNamesFunctionSnapshot();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private _writeArrayFormulaToSnapshot(arrayFormulaRange: IArrayFormulaRangeType, arrayFormulaCellData: IArrayFormulaUnitCellType) {
|
||||
private _addPreixToDefinedNamesFunctionSnapshot() {
|
||||
const allDefinedNames = this._definedNamesService.getAllDefinedNames();
|
||||
Object.entries(allDefinedNames).forEach(([unitId, definedNames]) => {
|
||||
definedNames && Array.from(Object.entries(definedNames)).forEach(([_, definedName]) => {
|
||||
const { formulaOrRefString } = definedName;
|
||||
if (formulaOrRefString.substring(0, 1) === '=') {
|
||||
const newFormula = this._lexerTreeBuilder.getNewFormulaWithPrefix(formulaOrRefString, this._functionService.hasExecutor.bind(this._functionService));
|
||||
if (newFormula) {
|
||||
this._commandService.executeCommand<ISetDefinedNameMutationParam>(SetDefinedNameMutation.id, {
|
||||
...definedName,
|
||||
unitId,
|
||||
formulaOrRefStringWithPrefix: newFormula,
|
||||
}, {
|
||||
onlyLocal: true,
|
||||
fromFormula: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private _addPrefixToFunctionSnapshot() {
|
||||
const dataModel = this._formulaDataModel.getFormulaData();
|
||||
Object.entries(dataModel).forEach(([unitId, subUnitData]) => {
|
||||
subUnitData && Array.from(Object.entries(subUnitData)).forEach(([subUnitId, formulaDataItem]) => {
|
||||
// Convert from IObjectMatrixPrimitiveType<IRange> to IObjectMatrixPrimitiveType<ICellData>
|
||||
if (!formulaDataItem) {
|
||||
return;
|
||||
}
|
||||
const cellValue = new ObjectMatrix<ICellData>();
|
||||
const matrix = new ObjectMatrix(formulaDataItem);
|
||||
matrix.forValue((row, col, value) => {
|
||||
const functionText = value?.f;
|
||||
if (!functionText || functionText.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newFormula = this._lexerTreeBuilder.getNewFormulaWithPrefix(functionText, this._functionService.hasExecutor.bind(this._functionService));
|
||||
if (newFormula) {
|
||||
cellValue.setValue(row, col, { xf: newFormula });
|
||||
}
|
||||
});
|
||||
|
||||
// Keep this local to avoid triggering re-calculate in the worker.
|
||||
this._commandService.executeCommand<ISetRangeValuesMutationParams>(SetRangeValuesMutation.id, {
|
||||
unitId,
|
||||
subUnitId,
|
||||
cellValue: cellValue.getMatrix(),
|
||||
}, {
|
||||
onlyLocal: true,
|
||||
fromFormula: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private _writeArrayFormulaToSnapshot(arrayFormulaRange: IArrayFormulaRangeType, arrayFormulaCellData: IArrayFormulaUnitCellType, arrayFormulaEmbedded: IArrayFormulaEmbeddedMap) {
|
||||
// Write values to the `ref` property of the array formula range.
|
||||
Object.entries(arrayFormulaRange).forEach(([unitId, subUnitData]) => {
|
||||
subUnitData && Array.from(Object.entries(subUnitData)).forEach(([subUnitId, rangeData]) => {
|
||||
@@ -90,6 +152,38 @@ export class ArrayFormulaCellInterceptorController extends Disposable {
|
||||
});
|
||||
});
|
||||
|
||||
// <f t="array" ref="E7">SUM(A1:A7*B1:B7)</f> , write ref for embedded array formula cells
|
||||
Array.from(Object.entries(arrayFormulaEmbedded)).forEach(([unitId, subUnitData]) => {
|
||||
subUnitData && Array.from(Object.entries(subUnitData)).forEach(([subUnitId, rangeData]) => {
|
||||
const cellValue = new ObjectMatrix<ICellData>();
|
||||
const matrix = new ObjectMatrix(rangeData);
|
||||
matrix.forValue((row, col) => {
|
||||
const existingArrayRange = arrayFormulaRange?.[unitId]?.[subUnitId]?.[row]?.[col];
|
||||
if (existingArrayRange) {
|
||||
return; // Skip if there is already an array formula range at this position
|
||||
}
|
||||
cellValue.setValue(row, col, {
|
||||
ref: serializeRange({
|
||||
startRow: row,
|
||||
endRow: row,
|
||||
startColumn: col,
|
||||
endColumn: col,
|
||||
}),
|
||||
}); // convert to ref string
|
||||
});
|
||||
|
||||
// Keep this local to avoid triggering re-calculate in the worker.
|
||||
this._commandService.executeCommand<ISetRangeValuesMutationParams>(SetRangeValuesMutation.id, {
|
||||
unitId,
|
||||
subUnitId,
|
||||
cellValue: cellValue.getMatrix(),
|
||||
}, {
|
||||
onlyLocal: true,
|
||||
fromFormula: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Write values to the 'v' property of the cell data.
|
||||
Array.from(Object.entries(arrayFormulaCellData)).forEach(([unitId, subUnitData]) => {
|
||||
subUnitData && Array.from(Object.entries(subUnitData)).forEach(([subUnitId, rowData]) => {
|
||||
|
||||
Reference in New Issue
Block a user