mirror of
https://github.com/dream-num/univer.git
synced 2026-08-29 07:13:59 +08:00
fix(formula): add graph api (#6313)
This commit is contained in:
@@ -26,6 +26,7 @@ import type {
|
||||
ObjectMatrix,
|
||||
Styles,
|
||||
} from '@univerjs/core';
|
||||
import type { sequenceNodeType } from '../engine/utils/sequence';
|
||||
import type { IImageFormulaInfo } from '../engine/value-object/primitive-object';
|
||||
|
||||
export const ERROR_VALUE_OBJECT_CLASS_TYPE = 'errorValueObject';
|
||||
@@ -134,7 +135,7 @@ export interface IUnitImageFormulaDataType {
|
||||
[unitId: string]: Nullable<{ [sheetId: string]: ObjectMatrix<Nullable<IImageFormulaInfo>> }>;
|
||||
}
|
||||
|
||||
export interface IArrayFormulaUnitCellType extends IRuntimeUnitDataPrimitiveType {}
|
||||
export interface IArrayFormulaUnitCellType extends IRuntimeUnitDataPrimitiveType { }
|
||||
|
||||
export interface IFormulaData {
|
||||
[unitId: string]: Nullable<{ [sheetId: string]: Nullable<IObjectMatrixPrimitiveType<Nullable<IFormulaDataItem>>> }>;
|
||||
@@ -223,6 +224,7 @@ export interface IFormulaDatasetConfig {
|
||||
unitStylesData?: IUnitStylesData;
|
||||
unitSheetNameMap?: IUnitSheetNameMap;
|
||||
maxIteration?: number;
|
||||
isCalculateTreeModel?: boolean;
|
||||
rowData?: IUnitRowData; // Include rows hidden by filters
|
||||
}
|
||||
|
||||
@@ -230,3 +232,9 @@ export enum ConcatenateType {
|
||||
FRONT,
|
||||
BACK,
|
||||
}
|
||||
|
||||
export interface IExprTreeNode {
|
||||
value: string;
|
||||
children: IExprTreeNode[];
|
||||
type?: sequenceNodeType;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ export class CalculateController extends Disposable {
|
||||
private async _calculate(
|
||||
formulaDirtyData: Partial<IFormulaDirtyData>
|
||||
) {
|
||||
const { forceCalculation: forceCalculate = false, dirtyRanges = [], dirtyNameMap = {}, dirtyDefinedNameMap = {}, dirtyUnitFeatureMap = {}, dirtyUnitOtherFormulaMap = {}, clearDependencyTreeCache = {}, maxIteration = DEFAULT_CYCLE_REFERENCE_COUNT, rowData } = formulaDirtyData;
|
||||
const { forceCalculation: forceCalculate = false, dirtyRanges = [], dirtyNameMap = {}, dirtyDefinedNameMap = {}, dirtyUnitFeatureMap = {}, dirtyUnitOtherFormulaMap = {}, clearDependencyTreeCache = {}, maxIteration = DEFAULT_CYCLE_REFERENCE_COUNT, rowData, isCalculateTreeModel = false } = formulaDirtyData;
|
||||
|
||||
const formulaData = this._formulaDataModel.getFormulaData();
|
||||
const arrayFormulaCellData = this._formulaDataModel.getArrayFormulaCellData();
|
||||
@@ -112,6 +112,7 @@ export class CalculateController extends Disposable {
|
||||
dirtyUnitOtherFormulaMap,
|
||||
clearDependencyTreeCache,
|
||||
maxIteration,
|
||||
isCalculateTreeModel,
|
||||
rowData,
|
||||
});
|
||||
}
|
||||
@@ -229,7 +230,7 @@ export class CalculateController extends Disposable {
|
||||
}
|
||||
|
||||
private async _applyResult(data: IAllRuntimeData) {
|
||||
const { unitData, unitOtherData, arrayFormulaRange, arrayFormulaCellData, clearArrayFormulaCellData, arrayFormulaEmbedded, imageFormulaData } = data;
|
||||
const { unitData, unitOtherData, arrayFormulaRange, arrayFormulaCellData, clearArrayFormulaCellData, arrayFormulaEmbedded, imageFormulaData, dependencyTreeModelData } = data;
|
||||
|
||||
if (!unitData) {
|
||||
console.error('No sheetData from Formula Engine!');
|
||||
@@ -267,6 +268,18 @@ export class CalculateController extends Disposable {
|
||||
);
|
||||
}
|
||||
|
||||
if (dependencyTreeModelData.length > 0) {
|
||||
this._commandService.executeCommand(
|
||||
SetFormulaDependencyCalculationResultMutation.id,
|
||||
{
|
||||
result: dependencyTreeModelData,
|
||||
},
|
||||
{
|
||||
onlyLocal: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
this._commandService.executeCommand(
|
||||
SetFormulaCalculationResultMutation.id,
|
||||
{
|
||||
|
||||
@@ -24,6 +24,8 @@ export const DEFAULT_CYCLE_REFERENCE_COUNT = 1;
|
||||
|
||||
export const ENGINE_FORMULA_CYCLE_REFERENCE_COUNT = 'CYCLE_REFERENCE_COUNT';
|
||||
|
||||
export const ENGINE_FORMULA_RETURN_DEPENDENCY_TREE = 'RETURN_DEPENDENCY_TREE';
|
||||
|
||||
export const configSymbol = Symbol(ENGINE_FORMULA_PLUGIN_CONFIG_KEY);
|
||||
|
||||
export interface IUniverEngineFormulaConfig {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import type { IRange, Nullable } from '@univerjs/core';
|
||||
import type { IDirtyUnitSheetDefinedNameMap } from '../../basics/common';
|
||||
import type { IDirtyUnitSheetDefinedNameMap, IExprTreeNode, ISuperTable } from '../../basics/common';
|
||||
|
||||
import type { IFunctionNames } from '../../basics/function';
|
||||
import type { IDefinedNamesServiceParam } from '../../services/defined-names.service';
|
||||
@@ -1951,4 +1951,14 @@ export class LexerTreeBuilder extends Disposable {
|
||||
getNewFormulaWithPrefix(formulaString: string, hasFunction: (functionToken: IFunctionNames) => boolean): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
getFormulaExprTree(
|
||||
formulaString: string,
|
||||
unitId: string,
|
||||
hasFunction: (functionToken: IFunctionNames) => boolean,
|
||||
getDefinedNameName: (unitId: string, name: string) => Nullable<IDefinedNamesServiceParam>,
|
||||
getTable: (unitId: string, tableName: string) => Nullable<ISuperTable>
|
||||
): IExprTreeNode | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import { ErrorValueObject } from '../value-object/base-value-object';
|
||||
import { BaseAstNode } from './base-ast-node';
|
||||
import { BaseAstNodeFactory, DEFAULT_AST_NODE_FACTORY_Z_INDEX } from './base-ast-node-factory';
|
||||
import { NODE_ORDER_MAP, NodeType } from './node-type';
|
||||
import { splitTableStructuredRef } from '../utils/reference';
|
||||
|
||||
export class ReferenceNode extends BaseAstNode {
|
||||
private _refOffsetX = 0;
|
||||
@@ -187,6 +188,12 @@ export class ReferenceNodeFactory extends BaseAstNodeFactory {
|
||||
const makeRef = (type: ReferenceObjectType) =>
|
||||
new ReferenceNode(currentConfigService, runtimeService, tokenTrim, type, isPrepareMerge);
|
||||
|
||||
const tableMap = this._getTableMap();
|
||||
const isSuperTableDirect = tableMap?.has(tokenTrim) ?? false;
|
||||
if (isSuperTableDirect) {
|
||||
return this._getTableReferenceNode(tokenTrim, isLexerNode, isPrepareMerge, true);
|
||||
}
|
||||
|
||||
const isCellRange = regexTestSingeRange(tokenTrim);
|
||||
if (isCellRange) {
|
||||
return makeRef(ReferenceObjectType.CELL);
|
||||
@@ -203,12 +210,6 @@ export class ReferenceNodeFactory extends BaseAstNodeFactory {
|
||||
return makeRef(ReferenceObjectType.COLUMN);
|
||||
}
|
||||
|
||||
const tableMap = this._getTableMap();
|
||||
const isSuperTableDirect = tableMap?.has(tokenTrim) ?? false;
|
||||
if (isSuperTableDirect) {
|
||||
return this._getTableReferenceNode(tokenTrim, isLexerNode, isPrepareMerge, true);
|
||||
}
|
||||
|
||||
return this._getTableReferenceNode(tokenTrim, isLexerNode, isPrepareMerge, false);
|
||||
}
|
||||
|
||||
@@ -216,7 +217,7 @@ export class ReferenceNodeFactory extends BaseAstNodeFactory {
|
||||
if (!this._checkTokenIsTableReference(tokenTrim) && !isSuperTableDirectly) {
|
||||
return;
|
||||
}
|
||||
const { tableName, columnStruct } = this._splitTableStructuredRef(tokenTrim);
|
||||
const { tableName, columnStruct } = splitTableStructuredRef(tokenTrim);
|
||||
const tableMap = this._getTableMap();
|
||||
if (!isLexerNode && tableMap?.has(tableName)) {
|
||||
const columnDataString = columnStruct;
|
||||
@@ -233,17 +234,6 @@ export class ReferenceNodeFactory extends BaseAstNodeFactory {
|
||||
}
|
||||
}
|
||||
|
||||
private _splitTableStructuredRef(ref: string) {
|
||||
const idx = ref.indexOf('[');
|
||||
if (idx === -1) {
|
||||
return { tableName: ref, struct: '' };
|
||||
}
|
||||
return {
|
||||
tableName: ref.slice(0, idx),
|
||||
columnStruct: ref.slice(idx), // 包含外层 [[...]]
|
||||
};
|
||||
}
|
||||
|
||||
private _checkTokenIsTableReference(token: string): boolean {
|
||||
return regexTestReferenceTableAllColumn(token) || regexTestReferenceTableSingleColumn(token) || regexTestReferenceTableMultipleColumn(token) || regexTestReferenceTableTitleOnlyAnyHash(token);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ export function generateRandomDependencyTreeId(dependencyManagerService: IDepend
|
||||
}
|
||||
|
||||
export interface IFormulaDependencyGenerator {
|
||||
generate(): Promise<IFormulaDependencyTree[]>;
|
||||
generate(isCalculateTreeModel?: boolean): Promise<IFormulaDependencyTree[]>;
|
||||
getAllDependencyJson(): Promise<IFormulaDependencyTreeJson[]>;
|
||||
getCellDependencyJson(unitId: string, sheetId: string, row: number, column: number): Promise<IFormulaDependencyTreeFullJson | undefined>;
|
||||
getRangeDependents(unitRanges: IUnitRange[]): Promise<IFormulaDependencyTreeJson[]>;
|
||||
@@ -91,7 +91,7 @@ export class FormulaDependencyGenerator extends Disposable {
|
||||
FORMULA_AST_CACHE.clear();
|
||||
}
|
||||
|
||||
async generate() {
|
||||
async generate(isCalculateTreeModel = false) {
|
||||
this._updateRangeFlatten();
|
||||
// const formulaInterpreter = Interpreter.create(interpreterDatasetConfig);
|
||||
|
||||
@@ -122,6 +122,12 @@ export class FormulaDependencyGenerator extends Disposable {
|
||||
|
||||
const treeList = await this._generateTreeList(formulaData, otherFormulaData, unitData);
|
||||
|
||||
if (isCalculateTreeModel) {
|
||||
this._runtimeService.setDependencyTreeModelData(
|
||||
this._getAllDependencyJson(treeList)
|
||||
);
|
||||
}
|
||||
|
||||
const updateTreeList = this._getUpdateTreeListAndMakeDependency(treeList);
|
||||
|
||||
let finalTreeList = this._calculateRunList(updateTreeList);
|
||||
@@ -1376,9 +1382,7 @@ export class FormulaDependencyGenerator extends Disposable {
|
||||
|
||||
}
|
||||
|
||||
async getAllDependencyJson(): Promise<IFormulaDependencyTreeJson[]> {
|
||||
const treeList = await this._getAllTreeList();
|
||||
|
||||
protected _getAllDependencyJson(treeList: IFormulaDependencyTree[]): IFormulaDependencyTreeJson[] {
|
||||
this._startFormulaDependencyTreeModel();
|
||||
|
||||
const results: FormulaDependencyTreeModel[] = [];
|
||||
@@ -1399,6 +1403,14 @@ export class FormulaDependencyGenerator extends Disposable {
|
||||
return resultsJson;
|
||||
}
|
||||
|
||||
async getAllDependencyJson(): Promise<IFormulaDependencyTreeJson[]> {
|
||||
const treeList = await this._getAllTreeList();
|
||||
|
||||
const resultsJson = this._getAllDependencyJson(treeList);
|
||||
|
||||
return resultsJson;
|
||||
}
|
||||
|
||||
protected _setRealFormulaString(treeModel: FormulaDependencyTreeModel) {
|
||||
if (!treeModel.refTreeId) {
|
||||
return;
|
||||
|
||||
@@ -464,3 +464,14 @@ function startsWithNonAlphabetic(name: string) {
|
||||
// Check if the first character is not a letter (including non-English characters)
|
||||
return !/^\p{Letter}/u.test(name.charAt(0));
|
||||
}
|
||||
|
||||
export function splitTableStructuredRef(ref: string) {
|
||||
const idx = ref.indexOf('[');
|
||||
if (idx === -1) {
|
||||
return { tableName: ref, struct: '' };
|
||||
}
|
||||
return {
|
||||
tableName: ref.slice(0, idx),
|
||||
columnStruct: ref.slice(idx), // include [[...]]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ export enum sequenceNodeType {
|
||||
REFERENCE,
|
||||
ARRAY,
|
||||
DEFINED_NAME,
|
||||
TABLE,
|
||||
}
|
||||
|
||||
export interface ISequenceNode {
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
*/
|
||||
|
||||
import type { ICommandInfo, IDisposable, IUnitRange } from '@univerjs/core';
|
||||
import type { FormulaExecutedStateType, IExecutionInProgressParams, IFormulaDependencyTreeFullJson, IFormulaDependencyTreeJson, IFormulaExecuteResultMap, IFormulaStringMap, ISequenceNode, ISetCellFormulaDependencyCalculationResultMutation, ISetFormulaCalculationNotificationMutation, ISetFormulaCalculationResultMutation, ISetFormulaCalculationStartMutation, ISetFormulaDependencyCalculationResultMutation, ISetFormulaStringBatchCalculationResultMutation } from '@univerjs/engine-formula';
|
||||
import type { FormulaExecutedStateType, IExecutionInProgressParams, IExprTreeNode, IFormulaDependencyTreeFullJson, IFormulaDependencyTreeJson, IFormulaExecuteResultMap, IFormulaStringMap, ISequenceNode, ISetCellFormulaDependencyCalculationResultMutation, ISetFormulaCalculationNotificationMutation, ISetFormulaCalculationResultMutation, ISetFormulaCalculationStartMutation, ISetFormulaDependencyCalculationResultMutation, ISetFormulaStringBatchCalculationResultMutation } from '@univerjs/engine-formula';
|
||||
import { ICommandService, IConfigService, Inject, Injector } from '@univerjs/core';
|
||||
import { FBase } from '@univerjs/core/facade';
|
||||
import { ENGINE_FORMULA_CYCLE_REFERENCE_COUNT, GlobalComputingStatusService, LexerTreeBuilder, SetCellFormulaDependencyCalculationMutation, SetCellFormulaDependencyCalculationResultMutation, SetFormulaCalculationNotificationMutation, SetFormulaCalculationResultMutation, SetFormulaCalculationStartMutation, SetFormulaCalculationStopMutation, SetFormulaDependencyCalculationMutation, SetFormulaDependencyCalculationResultMutation, SetFormulaStringBatchCalculationMutation, SetFormulaStringBatchCalculationResultMutation, SetQueryFormulaDependencyMutation, SetQueryFormulaDependencyResultMutation } from '@univerjs/engine-formula';
|
||||
import { ENGINE_FORMULA_CYCLE_REFERENCE_COUNT, ENGINE_FORMULA_RETURN_DEPENDENCY_TREE, GlobalComputingStatusService, IDefinedNamesService, IFunctionService, ISuperTableService, LexerTreeBuilder, SetCellFormulaDependencyCalculationMutation, SetCellFormulaDependencyCalculationResultMutation, SetFormulaCalculationNotificationMutation, SetFormulaCalculationResultMutation, SetFormulaCalculationStartMutation, SetFormulaCalculationStopMutation, SetFormulaDependencyCalculationMutation, SetFormulaDependencyCalculationResultMutation, SetFormulaStringBatchCalculationMutation, SetFormulaStringBatchCalculationResultMutation, SetQueryFormulaDependencyMutation, SetQueryFormulaDependencyResultMutation } from '@univerjs/engine-formula';
|
||||
import { filter, firstValueFrom, map, race, timer } from 'rxjs';
|
||||
|
||||
/**
|
||||
@@ -30,7 +30,11 @@ export class FFormula extends FBase {
|
||||
@Inject(ICommandService) protected readonly _commandService: ICommandService,
|
||||
@Inject(Injector) protected readonly _injector: Injector,
|
||||
@Inject(LexerTreeBuilder) private _lexerTreeBuilder: LexerTreeBuilder,
|
||||
@IConfigService protected readonly _configService: IConfigService
|
||||
@IConfigService protected readonly _configService: IConfigService,
|
||||
@IFunctionService private readonly _functionService: IFunctionService,
|
||||
@IDefinedNamesService private readonly _definedNamesService: IDefinedNamesService,
|
||||
@ISuperTableService private readonly _superTableService: ISuperTableService
|
||||
|
||||
) {
|
||||
super();
|
||||
this._initialize();
|
||||
@@ -721,4 +725,123 @@ export class FFormula extends FBase {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable emitting formula dependency trees after each formula calculation.
|
||||
*
|
||||
* When enabled, the formula engine will emit the dependency trees produced by
|
||||
* each completed formula calculation through the internal command system.
|
||||
* Consumers can obtain the result by listening for the corresponding
|
||||
* calculation-result command.
|
||||
*
|
||||
* When disabled, dependency trees will not be emitted.
|
||||
*
|
||||
* This option only controls whether dependency trees are exposed.
|
||||
* It does not affect formula calculation behavior.
|
||||
*
|
||||
* @param {boolean} value
|
||||
* Whether to emit formula dependency trees after calculation.
|
||||
* - `true`: Emit dependency trees after each calculation.
|
||||
* - `false`: Do not emit dependency trees (default behavior).
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const formulaEngine = univerAPI.getFormula();
|
||||
*
|
||||
* // Enable dependency tree emission
|
||||
* formulaEngine.setFormulaReturnDependencyTree(true);
|
||||
*
|
||||
* // Listen for dependency trees produced by formula calculation
|
||||
* const trees = await new Promise<IFormulaDependencyTreeJson[]>((resolve, reject) => {
|
||||
* const timer = setTimeout(() => {
|
||||
* disposable.dispose();
|
||||
* reject(new Error('Timeout waiting for formula dependency trees'));
|
||||
* }, 30_000);
|
||||
*
|
||||
* const disposable = commandService.onCommandExecuted((command) => {
|
||||
* if (command.id !== SetFormulaDependencyCalculationResultMutation.id) {
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* clearTimeout(timer);
|
||||
* disposable.dispose();
|
||||
*
|
||||
* const params = command.params as ISetFormulaDependencyCalculationResultMutation;
|
||||
* resolve(params.result ?? []);
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* console.log('Dependency trees:', trees);
|
||||
* ```
|
||||
*/
|
||||
setFormulaReturnDependencyTree(value: boolean): void {
|
||||
this._configService.setConfig(ENGINE_FORMULA_RETURN_DEPENDENCY_TREE, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a formula string and return its **formula expression tree**.
|
||||
*
|
||||
* This API analyzes the syntactic structure of a formula and builds an
|
||||
* expression tree that reflects how the formula is composed (functions,
|
||||
* operators, ranges, and nested expressions), without performing calculation
|
||||
* or dependency evaluation.
|
||||
*
|
||||
* The returned tree is suitable for:
|
||||
* - Formula structure visualization
|
||||
* - Explaining complex formulas (e.g. LET / LAMBDA)
|
||||
* - Debugging or inspecting formula composition
|
||||
* - Building advanced formula tooling
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const formulaEngine = univerAPI.getFormula();
|
||||
*
|
||||
* const formula = '=LET(x,SUM(A1,B1,A1:B10),y,OFFSET(A1:B10,0,1),SUM(x,y)+x)+1';
|
||||
*
|
||||
* const exprTree = formulaEngine.getFormulaExpressTree(formula);
|
||||
*
|
||||
* console.log(exprTree);
|
||||
* ```
|
||||
*
|
||||
* Example output (simplified):
|
||||
*
|
||||
* ```json
|
||||
* {
|
||||
* "value": "let(x,sum(A1,B1,A1:B10),y,offset(A1:B10,0,1),sum(x,y)+x)+1",
|
||||
* "children": [
|
||||
* {
|
||||
* "value": "let(x,sum(A1,B1,A1:B10),y,offset(A1:B10,0,1),sum(x,y)+x)",
|
||||
* "children": [
|
||||
* {
|
||||
* "value": "sum(A1,B1,A1:B10)",
|
||||
* "children": [
|
||||
* {
|
||||
* "value": "A1:B10",
|
||||
* "children": []
|
||||
* }
|
||||
* ]
|
||||
* },
|
||||
* {
|
||||
* "value": "offset(A1:B10,0,1)",
|
||||
* "children": [
|
||||
* {
|
||||
* "value": "A1:B10",
|
||||
* "children": []
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param formulaString The formula string to parse (with or without leading `=`)
|
||||
* @returns A formula expression tree describing the hierarchical structure of the formula
|
||||
*/
|
||||
getFormulaExpressTree(formulaString: string, unitId: string): IExprTreeNode | null {
|
||||
return this._lexerTreeBuilder.getFormulaExprTree(formulaString, unitId, this._functionService.hasExecutor.bind(this._functionService), this._definedNamesService.getValueByName.bind(this._definedNamesService), this._superTableService.getTable.bind(this._superTableService));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ export type {
|
||||
} from './basics/common';
|
||||
export { BooleanValue } from './basics/common';
|
||||
export { type IOtherFormulaData } from './basics/common';
|
||||
export { type IUnitRowData } from './basics/common';
|
||||
export type { IExprTreeNode, ISuperTable, IUnitRowData } from './basics/common';
|
||||
export { isInDirtyRange } from './basics/dirty';
|
||||
export { ERROR_TYPE_SET, ErrorType } from './basics/error-type';
|
||||
export { type ISheetFormulaError } from './basics/error-type';
|
||||
@@ -82,7 +82,7 @@ export { type IRemoveOtherFormulaMutationParams, type ISetOtherFormulaMutationPa
|
||||
export { RemoveSuperTableMutation, SetSuperTableMutation, SetSuperTableOptionMutation } from './commands/mutations/set-super-table.mutation';
|
||||
export type { ISetSuperTableMutationParam, ISetSuperTableMutationSearchParam } from './commands/mutations/set-super-table.mutation';
|
||||
export { CalculateController } from './controller/calculate.controller';
|
||||
export { ENGINE_FORMULA_CYCLE_REFERENCE_COUNT, ENGINE_FORMULA_PLUGIN_CONFIG_KEY, type IUniverEngineFormulaConfig } from './controller/config.schema';
|
||||
export { ENGINE_FORMULA_CYCLE_REFERENCE_COUNT, ENGINE_FORMULA_PLUGIN_CONFIG_KEY, ENGINE_FORMULA_RETURN_DEPENDENCY_TREE, type IUniverEngineFormulaConfig } from './controller/config.schema';
|
||||
export { Lexer } from './engine/analysis/lexer';
|
||||
export { LexerNode } from './engine/analysis/lexer-node';
|
||||
export { LexerTreeBuilder } from './engine/analysis/lexer-tree-builder';
|
||||
@@ -128,6 +128,7 @@ export {
|
||||
serializeRangeWithSheet,
|
||||
serializeRangeWithSpreadsheet,
|
||||
singleReferenceToGrid,
|
||||
splitTableStructuredRef,
|
||||
unquoteSheetName,
|
||||
} from './engine/utils/reference';
|
||||
export { handleRefStringInfo } from './engine/utils/reference';
|
||||
|
||||
@@ -90,6 +90,8 @@ export class CalculateFormulaService extends Disposable implements ICalculateFor
|
||||
|
||||
private _executeLock = new AsyncLock();
|
||||
|
||||
protected _isCalculateTreeModel: boolean = false;
|
||||
|
||||
constructor(
|
||||
@IConfigService protected readonly _configService: IConfigService,
|
||||
@Inject(Lexer) protected readonly _lexer: Lexer,
|
||||
@@ -145,6 +147,8 @@ export class CalculateFormulaService extends Disposable implements ICalculateFor
|
||||
|
||||
const cycleReferenceCount = (formulaDatasetConfig.maxIteration || DEFAULT_CYCLE_REFERENCE_COUNT) as number;
|
||||
|
||||
this._isCalculateTreeModel = formulaDatasetConfig.isCalculateTreeModel || false;
|
||||
|
||||
this._executeLock.acquire('FORMULA_EXECUTION_LOCK', async () => {
|
||||
for (let i = 0; i < cycleReferenceCount; i++) {
|
||||
this._runtimeService.setFormulaCycleIndex(i);
|
||||
@@ -265,7 +269,7 @@ export class CalculateFormulaService extends Disposable implements ICalculateFor
|
||||
|
||||
this._executionInProgressListener$.next(this._runtimeService.getRuntimeState());
|
||||
|
||||
const treeList = (await this._formulaDependencyGenerator.generate()).reverse();
|
||||
const treeList = (await this._formulaDependencyGenerator.generate(this._isCalculateTreeModel)).reverse();
|
||||
|
||||
const interpreter = this._interpreter;
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ export interface IFormulaDirtyData {
|
||||
dirtyUnitOtherFormulaMap: IDirtyUnitOtherFormulaMap;
|
||||
clearDependencyTreeCache: IDirtyUnitSheetNameMap; // unitId -> sheetId
|
||||
maxIteration?: number;
|
||||
isCalculateTreeModel?: boolean; // whether to calculate the dependency tree model
|
||||
rowData?: IUnitRowData; // Include rows hidden by filters
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import type {
|
||||
IRuntimeUnitDataType,
|
||||
} from '../basics/common';
|
||||
import type { BaseAstNode } from '../engine/ast-node/base-ast-node';
|
||||
import type { IFormulaDependencyTreeJson } from '../engine/dependency/dependency-tree';
|
||||
import type { BaseReferenceObject, FunctionVariantType } from '../engine/reference-object/base-reference-object';
|
||||
import type { ArrayValueObject } from '../engine/value-object/array-value-object';
|
||||
import type { BaseValueObject } from '../engine/value-object/base-value-object';
|
||||
@@ -77,6 +78,8 @@ export interface IAllRuntimeData {
|
||||
|
||||
runtimeFeatureRange: { [featureId: string]: IFeatureDirtyRangeType };
|
||||
runtimeFeatureCellData: { [featureId: string]: IRuntimeUnitDataType };
|
||||
|
||||
dependencyTreeModelData: IFormulaDependencyTreeJson[];
|
||||
}
|
||||
|
||||
export interface IExecutionInProgressParams {
|
||||
@@ -190,6 +193,10 @@ export interface IFormulaRuntimeService {
|
||||
clearArrayObjectCache(): void;
|
||||
|
||||
getRuntimeImageFormulaData(): IRuntimeImageFormulaDataType[];
|
||||
|
||||
setDependencyTreeModelData(data: IFormulaDependencyTreeJson[]): void;
|
||||
|
||||
getDependencyTreeModelData(): IFormulaDependencyTreeJson[];
|
||||
}
|
||||
|
||||
export class FormulaRuntimeService extends Disposable implements IFormulaRuntimeService {
|
||||
@@ -241,6 +248,8 @@ export class FormulaRuntimeService extends Disposable implements IFormulaRuntime
|
||||
|
||||
private _isCycleDependency: boolean = false;
|
||||
|
||||
private _dependencyTreeModelData: IFormulaDependencyTreeJson[] = [];
|
||||
|
||||
constructor(
|
||||
@IFormulaCurrentConfigService private readonly _currentConfigService: IFormulaCurrentConfigService,
|
||||
@IHyperlinkEngineFormulaService private readonly _hyperlinkEngineFormulaService: IHyperlinkEngineFormulaService
|
||||
@@ -752,6 +761,14 @@ export class FormulaRuntimeService extends Disposable implements IFormulaRuntime
|
||||
this._runtimeFeatureCellData[featureId] = featureData;
|
||||
}
|
||||
|
||||
setDependencyTreeModelData(data: IFormulaDependencyTreeJson[]) {
|
||||
this._dependencyTreeModelData = data;
|
||||
}
|
||||
|
||||
getDependencyTreeModelData() {
|
||||
return this._dependencyTreeModelData;
|
||||
}
|
||||
|
||||
getRuntimeImageFormulaData() {
|
||||
return this._runtimeImageFormulaData;
|
||||
}
|
||||
@@ -769,6 +786,8 @@ export class FormulaRuntimeService extends Disposable implements IFormulaRuntime
|
||||
|
||||
runtimeFeatureRange: this.getRuntimeFeatureRange(),
|
||||
runtimeFeatureCellData: this.getRuntimeFeatureCellData(),
|
||||
|
||||
dependencyTreeModelData: this.getDependencyTreeModelData(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ export interface ISuperTableService {
|
||||
remove(unitId: string, tableName: string): void;
|
||||
|
||||
update$: Observable<unknown>;
|
||||
|
||||
getTable(unitId: string, tableName: string): Nullable<ISuperTable>;
|
||||
}
|
||||
|
||||
export class SuperTableService extends Disposable implements ISuperTableService {
|
||||
@@ -92,6 +94,10 @@ export class SuperTableService extends Disposable implements ISuperTableService
|
||||
this._tableOptionMap.set(tableOption, tableOptionType);
|
||||
}
|
||||
|
||||
getTable(unitId: string, tableName: string): Nullable<ISuperTable> {
|
||||
return this._tableMap.get(unitId)?.get(tableName);
|
||||
}
|
||||
|
||||
private _update() {
|
||||
this._update$.next(null);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import type { IUniverSheetsFormulaBaseConfig } from './config.schema';
|
||||
import { Disposable, ICommandService, IConfigService, ILogService, Inject, LocaleService } from '@univerjs/core';
|
||||
import {
|
||||
ENGINE_FORMULA_CYCLE_REFERENCE_COUNT,
|
||||
ENGINE_FORMULA_RETURN_DEPENDENCY_TREE,
|
||||
FormulaDataModel,
|
||||
FormulaExecutedStateType,
|
||||
FormulaExecuteStageType,
|
||||
@@ -36,6 +37,7 @@ import {
|
||||
SetFormulaCalculationNotificationMutation,
|
||||
SetFormulaCalculationStartMutation,
|
||||
SetFormulaCalculationStopMutation,
|
||||
SetFormulaStringBatchCalculationMutation,
|
||||
} from '@univerjs/engine-formula';
|
||||
import {
|
||||
ClearSelectionFormatCommand,
|
||||
@@ -170,9 +172,14 @@ export class TriggerCalculationController extends Disposable {
|
||||
// The filtering information is not synchronized to the worker and must be passed in from the main thread each time
|
||||
this.disposeWithMe(
|
||||
this._commandService.beforeCommandExecuted((command: ICommandInfo) => {
|
||||
if (command.id === SetFormulaCalculationStartMutation.id) {
|
||||
if (command.id === SetFormulaCalculationStartMutation.id || command.id === SetFormulaStringBatchCalculationMutation.id) {
|
||||
const params = command.params as ISetFormulaCalculationStartMutation;
|
||||
if (command.id === SetFormulaCalculationStartMutation.id) {
|
||||
const isCalculateTreeModel = this._configService.getConfig<boolean>(ENGINE_FORMULA_RETURN_DEPENDENCY_TREE) || false;
|
||||
params.isCalculateTreeModel = isCalculateTreeModel;
|
||||
}
|
||||
|
||||
params.maxIteration = this._configService.getConfig(ENGINE_FORMULA_CYCLE_REFERENCE_COUNT) as number | undefined;
|
||||
params.rowData = this._formulaDataModel.getHiddenRowsFiltered();
|
||||
}
|
||||
})
|
||||
@@ -272,7 +279,6 @@ export class TriggerCalculationController extends Disposable {
|
||||
dirtyUnitOtherFormulaMap: allDirtyUnitOtherFormulaMap,
|
||||
forceCalculation: false,
|
||||
clearDependencyTreeCache: allClearDependencyTreeCache,
|
||||
maxIteration: (this._configService.getConfig(ENGINE_FORMULA_CYCLE_REFERENCE_COUNT)) as number | undefined,
|
||||
// numfmtItemMap,
|
||||
};
|
||||
}
|
||||
@@ -291,8 +297,6 @@ export class TriggerCalculationController extends Disposable {
|
||||
this._mergeDirtyUnitFeatureOrOtherFormulaMap(allDirtyUnitOtherFormulaMap, dirtyData2.dirtyUnitOtherFormulaMap);
|
||||
this._mergeDirtyNameMap(allClearDependencyTreeCache, dirtyData2.clearDependencyTreeCache);
|
||||
|
||||
const maxIteration = dirtyData1.maxIteration || dirtyData2.maxIteration;
|
||||
|
||||
return {
|
||||
dirtyRanges: allDirtyRanges,
|
||||
dirtyNameMap: allDirtyNameMap,
|
||||
@@ -301,7 +305,6 @@ export class TriggerCalculationController extends Disposable {
|
||||
dirtyUnitOtherFormulaMap: allDirtyUnitOtherFormulaMap,
|
||||
forceCalculation: !!this._forceCalculating,
|
||||
clearDependencyTreeCache: allClearDependencyTreeCache,
|
||||
maxIteration,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -552,7 +555,6 @@ export class TriggerCalculationController extends Disposable {
|
||||
dirtyUnitFeatureMap,
|
||||
dirtyUnitOtherFormulaMap,
|
||||
clearDependencyTreeCache,
|
||||
maxIteration: this._configService.getConfig(ENGINE_FORMULA_CYCLE_REFERENCE_COUNT) as number | undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user