mirror of
https://github.com/dream-num/univer.git
synced 2026-10-02 04:24:34 +08:00
feat(formula): dependency tree and calculation api (#6258)
This commit is contained in:
@@ -146,6 +146,19 @@ export interface IFormulaIdMap {
|
|||||||
c: number;
|
c: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IFormulaStringMap {
|
||||||
|
[unitId: string]: Nullable<{ [sheetId: string]: IObjectMatrixPrimitiveType<string[]> }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IFormulaExecuteResultItem {
|
||||||
|
value: Nullable<number | string | boolean | Array<Array<number | string | boolean | null>>>;
|
||||||
|
formula: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IFormulaExecuteResultMap {
|
||||||
|
[unitId: string]: Nullable<{ [sheetId: string]: IObjectMatrixPrimitiveType<IFormulaExecuteResultItem[]> }>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IFormulaIdMapData {
|
export interface IFormulaIdMapData {
|
||||||
[unitId: string]: Nullable<{ [subUnitId: string]: Nullable<{ [formulaId: string]: IFormulaIdMap }> }>;
|
[unitId: string]: Nullable<{ [subUnitId: string]: Nullable<{ [formulaId: string]: IFormulaIdMap }> }>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,13 @@
|
|||||||
|
|
||||||
import type { IExecutionOptions, IMutation, Nullable } from '@univerjs/core';
|
import type { IExecutionOptions, IMutation, Nullable } from '@univerjs/core';
|
||||||
import type {
|
import type {
|
||||||
|
IFormulaExecuteResultMap,
|
||||||
|
IFormulaStringMap,
|
||||||
IRuntimeOtherUnitDataType,
|
IRuntimeOtherUnitDataType,
|
||||||
IRuntimeUnitDataPrimitiveType,
|
IRuntimeUnitDataPrimitiveType,
|
||||||
} from '../../basics/common';
|
} from '../../basics/common';
|
||||||
|
|
||||||
|
import type { IFormulaDependencyTreeFullJson, IFormulaDependencyTreeJson } from '../../engine/dependency/dependency-tree';
|
||||||
import type { IFormulaDirtyData } from '../../services/current-data.service';
|
import type { IFormulaDirtyData } from '../../services/current-data.service';
|
||||||
import type { FormulaExecutedStateType, IExecutionInProgressParams } from '../../services/runtime.service';
|
import type { FormulaExecutedStateType, IExecutionInProgressParams } from '../../services/runtime.service';
|
||||||
import { CommandType } from '@univerjs/core';
|
import { CommandType } from '@univerjs/core';
|
||||||
@@ -27,6 +30,34 @@ import { CommandType } from '@univerjs/core';
|
|||||||
export interface ISetFormulaCalculationStartMutation extends IFormulaDirtyData {
|
export interface ISetFormulaCalculationStartMutation extends IFormulaDirtyData {
|
||||||
options: Nullable<IExecutionOptions>;
|
options: Nullable<IExecutionOptions>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ISetFormulaStringBatchCalculationMutation {
|
||||||
|
formulas: IFormulaStringMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISetFormulaStringBatchCalculationResultMutation {
|
||||||
|
result: IFormulaExecuteResultMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISetFormulaStringBatchCalculationResultMutation {
|
||||||
|
result: IFormulaExecuteResultMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISetFormulaDependencyCalculationMutation {
|
||||||
|
unitId: string;
|
||||||
|
sheetId: string;
|
||||||
|
row: number;
|
||||||
|
column: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISetFormulaDependencyCalculationResultMutation {
|
||||||
|
result: IFormulaDependencyTreeJson[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISetCellFormulaDependencyCalculationResultMutation {
|
||||||
|
result: IFormulaDependencyTreeFullJson | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: @DR-Univer
|
* TODO: @DR-Univer
|
||||||
* Trigger the calculation of the formula and stop the formula
|
* Trigger the calculation of the formula and stop the formula
|
||||||
@@ -37,7 +68,19 @@ export const SetFormulaCalculationStartMutation: IMutation<ISetFormulaCalculatio
|
|||||||
handler: () => true,
|
handler: () => true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ISetFormulaCalculationStopMutation {}
|
export const SetFormulaStringBatchCalculationMutation: IMutation<ISetFormulaStringBatchCalculationMutation> = {
|
||||||
|
id: 'formula.mutation.set-formula-string-batch-calculation',
|
||||||
|
type: CommandType.MUTATION,
|
||||||
|
handler: () => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SetFormulaStringBatchCalculationResultMutation: IMutation<ISetFormulaStringBatchCalculationResultMutation> = {
|
||||||
|
id: 'formula.mutation.set-formula-string-batch-calculation-result',
|
||||||
|
type: CommandType.MUTATION,
|
||||||
|
handler: () => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface ISetFormulaCalculationStopMutation { }
|
||||||
|
|
||||||
export const SetFormulaCalculationStopMutation: IMutation<ISetFormulaCalculationStopMutation> = {
|
export const SetFormulaCalculationStopMutation: IMutation<ISetFormulaCalculationStopMutation> = {
|
||||||
id: 'formula.mutation.set-formula-calculation-stop',
|
id: 'formula.mutation.set-formula-calculation-stop',
|
||||||
@@ -67,3 +110,27 @@ export const SetFormulaCalculationResultMutation: IMutation<ISetFormulaCalculati
|
|||||||
type: CommandType.MUTATION,
|
type: CommandType.MUTATION,
|
||||||
handler: () => true,
|
handler: () => true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const SetFormulaDependencyCalculationMutation: IMutation<{}> = {
|
||||||
|
id: 'formula.mutation.set-formula-dependency-calculation',
|
||||||
|
type: CommandType.MUTATION,
|
||||||
|
handler: () => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SetFormulaDependencyCalculationResultMutation: IMutation<ISetFormulaDependencyCalculationResultMutation> = {
|
||||||
|
id: 'formula.mutation.set-formula-dependency-calculation-result',
|
||||||
|
type: CommandType.MUTATION,
|
||||||
|
handler: () => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SetCellFormulaDependencyCalculationMutation: IMutation<ISetFormulaDependencyCalculationMutation> = {
|
||||||
|
id: 'formula.mutation.set-cell-formula-dependency-calculation',
|
||||||
|
type: CommandType.MUTATION,
|
||||||
|
handler: () => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SetCellFormulaDependencyCalculationResultMutation: IMutation<ISetCellFormulaDependencyCalculationResultMutation> = {
|
||||||
|
id: 'formula.mutation.set-cell-formula-dependency-calculation-result',
|
||||||
|
type: CommandType.MUTATION,
|
||||||
|
handler: () => true,
|
||||||
|
};
|
||||||
|
|||||||
@@ -16,17 +16,23 @@
|
|||||||
|
|
||||||
import type { ICommandInfo } from '@univerjs/core';
|
import type { ICommandInfo } from '@univerjs/core';
|
||||||
import type { ISetArrayFormulaDataMutationParams } from '../commands/mutations/set-array-formula-data.mutation';
|
import type { ISetArrayFormulaDataMutationParams } from '../commands/mutations/set-array-formula-data.mutation';
|
||||||
import type { ISetFormulaCalculationStartMutation } from '../commands/mutations/set-formula-calculation.mutation';
|
import type { ISetFormulaCalculationStartMutation, ISetFormulaDependencyCalculationMutation, ISetFormulaStringBatchCalculationMutation } from '../commands/mutations/set-formula-calculation.mutation';
|
||||||
import type { IFormulaDirtyData } from '../services/current-data.service';
|
import type { IFormulaDirtyData } from '../services/current-data.service';
|
||||||
import type { IAllRuntimeData } from '../services/runtime.service';
|
import type { IAllRuntimeData } from '../services/runtime.service';
|
||||||
import { Disposable, ICommandService, Inject } from '@univerjs/core';
|
import { Disposable, ICommandService, Inject } from '@univerjs/core';
|
||||||
import { convertRuntimeToUnitData } from '../basics/runtime';
|
import { convertRuntimeToUnitData } from '../basics/runtime';
|
||||||
import { SetArrayFormulaDataMutation } from '../commands/mutations/set-array-formula-data.mutation';
|
import { SetArrayFormulaDataMutation } from '../commands/mutations/set-array-formula-data.mutation';
|
||||||
import {
|
import {
|
||||||
|
SetCellFormulaDependencyCalculationMutation,
|
||||||
|
SetCellFormulaDependencyCalculationResultMutation,
|
||||||
SetFormulaCalculationNotificationMutation,
|
SetFormulaCalculationNotificationMutation,
|
||||||
SetFormulaCalculationResultMutation,
|
SetFormulaCalculationResultMutation,
|
||||||
SetFormulaCalculationStartMutation,
|
SetFormulaCalculationStartMutation,
|
||||||
SetFormulaCalculationStopMutation,
|
SetFormulaCalculationStopMutation,
|
||||||
|
SetFormulaDependencyCalculationMutation,
|
||||||
|
SetFormulaDependencyCalculationResultMutation,
|
||||||
|
SetFormulaStringBatchCalculationMutation,
|
||||||
|
SetFormulaStringBatchCalculationResultMutation,
|
||||||
} from '../commands/mutations/set-formula-calculation.mutation';
|
} from '../commands/mutations/set-formula-calculation.mutation';
|
||||||
import { SetImageFormulaDataMutation } from '../commands/mutations/set-image-formula-data.mutation';
|
import { SetImageFormulaDataMutation } from '../commands/mutations/set-image-formula-data.mutation';
|
||||||
import { FormulaDataModel } from '../models/formula-data.model';
|
import { FormulaDataModel } from '../models/formula-data.model';
|
||||||
@@ -67,6 +73,12 @@ export class CalculateController extends Disposable {
|
|||||||
// TODO@Dushusir: Merge the array formula data into the formulaDataModel
|
// TODO@Dushusir: Merge the array formula data into the formulaDataModel
|
||||||
this._formulaDataModel.setArrayFormulaRange(arrayFormulaRange);
|
this._formulaDataModel.setArrayFormulaRange(arrayFormulaRange);
|
||||||
this._formulaDataModel.setArrayFormulaCellData(arrayFormulaCellData);
|
this._formulaDataModel.setArrayFormulaCellData(arrayFormulaCellData);
|
||||||
|
} else if (command.id === SetFormulaStringBatchCalculationMutation.id) {
|
||||||
|
this._calculateFormulaString(command.params as ISetFormulaStringBatchCalculationMutation);
|
||||||
|
} else if (command.id === SetFormulaDependencyCalculationMutation.id) {
|
||||||
|
this._generateAllDependencyTreeJson();
|
||||||
|
} else if (command.id === SetCellFormulaDependencyCalculationMutation.id) {
|
||||||
|
this._generateCellDependencyTreeJson(command.params as ISetFormulaDependencyCalculationMutation);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -98,6 +110,62 @@ export class CalculateController extends Disposable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _generateAllDependencyTreeJson() {
|
||||||
|
const result = await this._calculateFormulaService.getAllDependencyJson();
|
||||||
|
|
||||||
|
this._commandService.executeCommand(
|
||||||
|
SetFormulaDependencyCalculationResultMutation.id,
|
||||||
|
{
|
||||||
|
result,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onlyLocal: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _generateCellDependencyTreeJson(param: ISetFormulaDependencyCalculationMutation) {
|
||||||
|
const { unitId, sheetId, row, column } = param;
|
||||||
|
const result = await this._calculateFormulaService.getCellDependencyJson(unitId, sheetId, row, column);
|
||||||
|
|
||||||
|
this._commandService.executeCommand(
|
||||||
|
SetCellFormulaDependencyCalculationResultMutation.id,
|
||||||
|
{
|
||||||
|
result,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onlyLocal: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _calculateFormulaString(params: ISetFormulaStringBatchCalculationMutation) {
|
||||||
|
const formulaData = this._formulaDataModel.getFormulaData();
|
||||||
|
const arrayFormulaCellData = this._formulaDataModel.getArrayFormulaCellData();
|
||||||
|
// array formula range is used to check whether the newly added array formula conflicts with the existing array formula
|
||||||
|
const arrayFormulaRange = this._formulaDataModel.getArrayFormulaRange();
|
||||||
|
|
||||||
|
const rowData = this._formulaDataModel.getHiddenRowsFiltered();
|
||||||
|
|
||||||
|
const result = await this._calculateFormulaService.executeFormulas(
|
||||||
|
params.formulas,
|
||||||
|
formulaData,
|
||||||
|
arrayFormulaCellData,
|
||||||
|
arrayFormulaRange,
|
||||||
|
rowData
|
||||||
|
);
|
||||||
|
|
||||||
|
this._commandService.executeCommand(
|
||||||
|
SetFormulaStringBatchCalculationResultMutation.id,
|
||||||
|
{
|
||||||
|
result,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onlyLocal: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Notification
|
// Notification
|
||||||
private _initialExecuteFormulaListener() {
|
private _initialExecuteFormulaListener() {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -25,10 +25,16 @@ import { SetArrayFormulaDataMutation } from '../commands/mutations/set-array-for
|
|||||||
import { RemoveDefinedNameMutation, SetDefinedNameMutation } from '../commands/mutations/set-defined-name.mutation';
|
import { RemoveDefinedNameMutation, SetDefinedNameMutation } from '../commands/mutations/set-defined-name.mutation';
|
||||||
import { RemoveFeatureCalculationMutation, SetFeatureCalculationMutation } from '../commands/mutations/set-feature-calculation.mutation';
|
import { RemoveFeatureCalculationMutation, SetFeatureCalculationMutation } from '../commands/mutations/set-feature-calculation.mutation';
|
||||||
import {
|
import {
|
||||||
|
SetCellFormulaDependencyCalculationMutation,
|
||||||
|
SetCellFormulaDependencyCalculationResultMutation,
|
||||||
SetFormulaCalculationNotificationMutation,
|
SetFormulaCalculationNotificationMutation,
|
||||||
SetFormulaCalculationResultMutation,
|
SetFormulaCalculationResultMutation,
|
||||||
SetFormulaCalculationStartMutation,
|
SetFormulaCalculationStartMutation,
|
||||||
SetFormulaCalculationStopMutation,
|
SetFormulaCalculationStopMutation,
|
||||||
|
SetFormulaDependencyCalculationMutation,
|
||||||
|
SetFormulaDependencyCalculationResultMutation,
|
||||||
|
SetFormulaStringBatchCalculationMutation,
|
||||||
|
SetFormulaStringBatchCalculationResultMutation,
|
||||||
} from '../commands/mutations/set-formula-calculation.mutation';
|
} from '../commands/mutations/set-formula-calculation.mutation';
|
||||||
import { SetFormulaDataMutation } from '../commands/mutations/set-formula-data.mutation';
|
import { SetFormulaDataMutation } from '../commands/mutations/set-formula-data.mutation';
|
||||||
import { SetImageFormulaDataMutation } from '../commands/mutations/set-image-formula-data.mutation';
|
import { SetImageFormulaDataMutation } from '../commands/mutations/set-image-formula-data.mutation';
|
||||||
@@ -76,10 +82,17 @@ export class FormulaController extends Disposable {
|
|||||||
SetArrayFormulaDataMutation,
|
SetArrayFormulaDataMutation,
|
||||||
SetImageFormulaDataMutation,
|
SetImageFormulaDataMutation,
|
||||||
SetFormulaCalculationStartMutation,
|
SetFormulaCalculationStartMutation,
|
||||||
|
SetFormulaStringBatchCalculationMutation,
|
||||||
|
SetFormulaStringBatchCalculationResultMutation,
|
||||||
SetFormulaCalculationStopMutation,
|
SetFormulaCalculationStopMutation,
|
||||||
SetFormulaCalculationNotificationMutation,
|
SetFormulaCalculationNotificationMutation,
|
||||||
SetFormulaCalculationResultMutation,
|
SetFormulaCalculationResultMutation,
|
||||||
|
|
||||||
|
SetFormulaDependencyCalculationMutation,
|
||||||
|
SetFormulaDependencyCalculationResultMutation,
|
||||||
|
SetCellFormulaDependencyCalculationMutation,
|
||||||
|
SetCellFormulaDependencyCalculationResultMutation,
|
||||||
|
|
||||||
SetDefinedNameMutation,
|
SetDefinedNameMutation,
|
||||||
RemoveDefinedNameMutation,
|
RemoveDefinedNameMutation,
|
||||||
SetFeatureCalculationMutation,
|
SetFeatureCalculationMutation,
|
||||||
|
|||||||
@@ -511,3 +511,111 @@ export class FormulaDependencyTree extends FormulaDependencyTreeCalculator {
|
|||||||
// this.parents.add(tree.treeId);
|
// this.parents.add(tree.treeId);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IFormulaDependencyTreeJsonBase {
|
||||||
|
treeId: number;
|
||||||
|
formula: string;
|
||||||
|
row: number;
|
||||||
|
column: number;
|
||||||
|
unitId: string;
|
||||||
|
subUnitId: string;
|
||||||
|
refOffsetX: number;
|
||||||
|
refOffsetY: number;
|
||||||
|
rangeList: IUnitRange[];
|
||||||
|
refTreeId: number | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IFormulaDependencyTreeJson extends IFormulaDependencyTreeJsonBase {
|
||||||
|
children: number[];
|
||||||
|
parents: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IFormulaDependencyTreeFullJson extends IFormulaDependencyTreeJsonBase {
|
||||||
|
children: IFormulaDependencyTreeJson[];
|
||||||
|
parents: IFormulaDependencyTreeJson[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FormulaDependencyTreeModel {
|
||||||
|
children: Set<FormulaDependencyTreeModel> = new Set();
|
||||||
|
|
||||||
|
parents: Set<FormulaDependencyTreeModel> = new Set();
|
||||||
|
|
||||||
|
treeId: number = -1;
|
||||||
|
|
||||||
|
formula: string = '';
|
||||||
|
|
||||||
|
refOffsetX: number = 0;
|
||||||
|
|
||||||
|
refOffsetY: number = 0;
|
||||||
|
|
||||||
|
row: number = -1;
|
||||||
|
|
||||||
|
column: number = -1;
|
||||||
|
|
||||||
|
unitId: string = '';
|
||||||
|
|
||||||
|
subUnitId: string = '';
|
||||||
|
|
||||||
|
rangeList: IUnitRange[] = [];
|
||||||
|
|
||||||
|
refTreeId: number | undefined;
|
||||||
|
|
||||||
|
constructor(tree: IFormulaDependencyTree) {
|
||||||
|
this.treeId = tree.treeId;
|
||||||
|
this.row = tree.row;
|
||||||
|
this.column = tree.column;
|
||||||
|
this.unitId = tree.unitId;
|
||||||
|
this.subUnitId = tree.subUnitId;
|
||||||
|
this.refOffsetX = tree.refOffsetX;
|
||||||
|
this.refOffsetY = tree.refOffsetY;
|
||||||
|
this.rangeList = tree.rangeList;
|
||||||
|
|
||||||
|
if (tree.isVirtual) {
|
||||||
|
this.refTreeId = (tree as FormulaDependencyTreeVirtual).refTree?.treeId ?? -1;
|
||||||
|
} else {
|
||||||
|
this.formula = tree.formula;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toJson(): IFormulaDependencyTreeJson {
|
||||||
|
return {
|
||||||
|
children: Array.from(this.children).map((child) => child.treeId),
|
||||||
|
parents: Array.from(this.parents).map((parent) => parent.treeId),
|
||||||
|
treeId: this.treeId,
|
||||||
|
formula: this.formula,
|
||||||
|
row: this.row,
|
||||||
|
column: this.column,
|
||||||
|
unitId: this.unitId,
|
||||||
|
subUnitId: this.subUnitId,
|
||||||
|
refOffsetX: this.refOffsetX,
|
||||||
|
refOffsetY: this.refOffsetY,
|
||||||
|
rangeList: this.rangeList,
|
||||||
|
refTreeId: this.refTreeId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
toFullJson(): IFormulaDependencyTreeFullJson {
|
||||||
|
return {
|
||||||
|
children: Array.from(this.children).map((child) => child.toJson()),
|
||||||
|
parents: Array.from(this.parents).map((parent) => parent.toJson()),
|
||||||
|
treeId: this.treeId,
|
||||||
|
formula: this.formula,
|
||||||
|
row: this.row,
|
||||||
|
column: this.column,
|
||||||
|
unitId: this.unitId,
|
||||||
|
subUnitId: this.subUnitId,
|
||||||
|
refOffsetX: this.refOffsetX,
|
||||||
|
refOffsetY: this.refOffsetY,
|
||||||
|
rangeList: this.rangeList,
|
||||||
|
refTreeId: this.refTreeId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
addParent(parent: FormulaDependencyTreeModel) {
|
||||||
|
this.parents.add(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
addChild(child: FormulaDependencyTreeModel) {
|
||||||
|
this.children.add(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,21 +25,22 @@ import type { BaseAstNode } from '../ast-node/base-ast-node';
|
|||||||
import type { BaseReferenceObject, FunctionVariantType } from '../reference-object/base-reference-object';
|
import type { BaseReferenceObject, FunctionVariantType } from '../reference-object/base-reference-object';
|
||||||
import type { IExecuteAstNodeData } from '../utils/ast-node-tool';
|
import type { IExecuteAstNodeData } from '../utils/ast-node-tool';
|
||||||
import type { PreCalculateNodeType } from '../utils/node-type';
|
import type { PreCalculateNodeType } from '../utils/node-type';
|
||||||
import type { IFormulaDependencyTree } from './dependency-tree';
|
import type { IFormulaDependencyTree, IFormulaDependencyTreeFullJson, IFormulaDependencyTreeJson } from './dependency-tree';
|
||||||
import { createIdentifier, Disposable, Inject, ObjectMatrix, RTree } from '@univerjs/core';
|
import { createIdentifier, Disposable, Inject, ObjectMatrix, RTree } from '@univerjs/core';
|
||||||
|
|
||||||
import { prefixToken, suffixToken } from '../../basics/token';
|
import { prefixToken, suffixToken } from '../../basics/token';
|
||||||
|
|
||||||
import { IFormulaCurrentConfigService } from '../../services/current-data.service';
|
import { IFormulaCurrentConfigService } from '../../services/current-data.service';
|
||||||
import { IDependencyManagerService } from '../../services/dependency-manager.service';
|
import { IDependencyManagerService } from '../../services/dependency-manager.service';
|
||||||
import { IFeatureCalculationManagerService } from '../../services/feature-calculation-manager.service';
|
import { IFeatureCalculationManagerService } from '../../services/feature-calculation-manager.service';
|
||||||
import { IOtherFormulaManagerService } from '../../services/other-formula-manager.service';
|
import { IOtherFormulaManagerService } from '../../services/other-formula-manager.service';
|
||||||
import { IFormulaRuntimeService } from '../../services/runtime.service';
|
import { IFormulaRuntimeService } from '../../services/runtime.service';
|
||||||
import { Lexer } from '../analysis/lexer';
|
import { Lexer } from '../analysis/lexer';
|
||||||
|
import { LexerTreeBuilder } from '../analysis/lexer-tree-builder';
|
||||||
import { AstTreeBuilder } from '../analysis/parser';
|
import { AstTreeBuilder } from '../analysis/parser';
|
||||||
import { NodeType } from '../ast-node/node-type';
|
import { NodeType } from '../ast-node/node-type';
|
||||||
import { Interpreter } from '../interpreter/interpreter';
|
import { Interpreter } from '../interpreter/interpreter';
|
||||||
import { FORMULA_AST_CACHE, generateAstNode, includeDefinedName } from '../utils/generate-ast-node';
|
import { FORMULA_AST_CACHE, generateAstNode, includeDefinedName } from '../utils/generate-ast-node';
|
||||||
import { FormulaDependencyTree, FormulaDependencyTreeType, FormulaDependencyTreeVirtual } from './dependency-tree';
|
import { FormulaDependencyTree, FormulaDependencyTreeModel, FormulaDependencyTreeType, FormulaDependencyTreeVirtual } from './dependency-tree';
|
||||||
|
|
||||||
const FORMULA_CACHE_LRU_COUNT = 5000;
|
const FORMULA_CACHE_LRU_COUNT = 5000;
|
||||||
|
|
||||||
@@ -56,6 +57,8 @@ export function generateRandomDependencyTreeId(dependencyManagerService: IDepend
|
|||||||
|
|
||||||
export interface IFormulaDependencyGenerator {
|
export interface IFormulaDependencyGenerator {
|
||||||
generate(): Promise<IFormulaDependencyTree[]>;
|
generate(): Promise<IFormulaDependencyTree[]>;
|
||||||
|
getAllDependencyJson(): Promise<IFormulaDependencyTreeJson[]>;
|
||||||
|
getCellDependencyJson(unitId: string, sheetId: string, row: number, column: number): Promise<IFormulaDependencyTreeFullJson | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IFormulaDependencyGenerator = createIdentifier<IFormulaDependencyGenerator>('engine-formula.dependency-generator');
|
export const IFormulaDependencyGenerator = createIdentifier<IFormulaDependencyGenerator>('engine-formula.dependency-generator');
|
||||||
@@ -74,7 +77,8 @@ export class FormulaDependencyGenerator extends Disposable {
|
|||||||
@Inject(Interpreter) private readonly _interpreter: Interpreter,
|
@Inject(Interpreter) private readonly _interpreter: Interpreter,
|
||||||
@Inject(AstTreeBuilder) protected readonly _astTreeBuilder: AstTreeBuilder,
|
@Inject(AstTreeBuilder) protected readonly _astTreeBuilder: AstTreeBuilder,
|
||||||
@Inject(Lexer) protected readonly _lexer: Lexer,
|
@Inject(Lexer) protected readonly _lexer: Lexer,
|
||||||
@IDependencyManagerService protected readonly _dependencyManagerService: IDependencyManagerService
|
@IDependencyManagerService protected readonly _dependencyManagerService: IDependencyManagerService,
|
||||||
|
@Inject(LexerTreeBuilder) protected readonly _lexerTreeBuilder: LexerTreeBuilder
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@@ -1298,4 +1302,140 @@ export class FormulaDependencyGenerator extends Disposable {
|
|||||||
|
|
||||||
return formulaRunList;
|
return formulaRunList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async _initializeGenerateTreeList() {
|
||||||
|
const formulaData = this._currentConfigService.getFormulaData();
|
||||||
|
|
||||||
|
const otherFormulaData = this._otherFormulaManagerService.getOtherFormulaData();
|
||||||
|
|
||||||
|
const unitData = this._currentConfigService.getUnitData();
|
||||||
|
|
||||||
|
const treeList = await this._generateTreeList(formulaData, otherFormulaData, unitData);
|
||||||
|
|
||||||
|
return treeList;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async _getAllTreeList() {
|
||||||
|
const treeList: IFormulaDependencyTree[] = await this._initializeGenerateTreeList();
|
||||||
|
|
||||||
|
const allTree: IFormulaDependencyTree[] = this._dependencyManagerService.buildDependencyTree(treeList);
|
||||||
|
|
||||||
|
return allTree;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _formulaDependencyTreeModel = new Map<number, FormulaDependencyTreeModel>();
|
||||||
|
|
||||||
|
protected _getTreeModel(treeId: number): FormulaDependencyTreeModel {
|
||||||
|
let treeModel = this._formulaDependencyTreeModel.get(treeId);
|
||||||
|
if (!treeModel) {
|
||||||
|
const tree = this._getTreeById(treeId);
|
||||||
|
if (!tree) {
|
||||||
|
throw new Error('FormulaDependencyTree is null');
|
||||||
|
}
|
||||||
|
|
||||||
|
treeModel = new FormulaDependencyTreeModel(tree);
|
||||||
|
// if (tree.isVirtual) {
|
||||||
|
// const formula = this._lexerTreeBuilder.moveFormulaRefOffset(
|
||||||
|
// tree.formula,
|
||||||
|
// tree.refOffsetX,
|
||||||
|
// tree.refOffsetY
|
||||||
|
// );
|
||||||
|
// treeModel.formula = formula;
|
||||||
|
// }
|
||||||
|
this._formulaDependencyTreeModel.set(treeId, treeModel);
|
||||||
|
}
|
||||||
|
return treeModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _getDependencyTreeParenIds(tree: IFormulaDependencyTree): Set<number> {
|
||||||
|
return tree.parents;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _getDependencyTreeChildrenIds(tree: IFormulaDependencyTree): Set<number> {
|
||||||
|
return tree.children;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _getFormulaDependencyTreeModel(tree: IFormulaDependencyTree): FormulaDependencyTreeModel {
|
||||||
|
const treeModel = this._getTreeModel(tree.treeId);
|
||||||
|
const parentIds = this._getDependencyTreeParenIds(tree);
|
||||||
|
for (const parentId of parentIds) {
|
||||||
|
const parentTreeModel = this._getTreeModel(parentId);
|
||||||
|
treeModel.addParent(parentTreeModel);
|
||||||
|
parentTreeModel.addChild(treeModel);
|
||||||
|
}
|
||||||
|
return treeModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _endFormulaDependencyTreeModel() {
|
||||||
|
this._formulaDependencyTreeModel.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _startFormulaDependencyTreeModel() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllDependencyJson(): Promise<IFormulaDependencyTreeJson[]> {
|
||||||
|
const treeList = await this._getAllTreeList();
|
||||||
|
|
||||||
|
this._startFormulaDependencyTreeModel();
|
||||||
|
|
||||||
|
const results: FormulaDependencyTreeModel[] = [];
|
||||||
|
for (const tree of treeList) {
|
||||||
|
const treeModel = this._getFormulaDependencyTreeModel(tree);
|
||||||
|
results[tree.treeId] = treeModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resultsJson: IFormulaDependencyTreeJson[] = [];
|
||||||
|
for (const result of results) {
|
||||||
|
if (result) {
|
||||||
|
resultsJson.push(result.toJson());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._endFormulaDependencyTreeModel();
|
||||||
|
|
||||||
|
return resultsJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCellDependencyJson(unitId: string, sheetId: string, row: number, column: number): Promise<IFormulaDependencyTreeFullJson | undefined> {
|
||||||
|
await this._initializeGenerateTreeList();
|
||||||
|
const treeId = this._dependencyManagerService.getFormulaDependency(unitId, sheetId, row, column);
|
||||||
|
if (treeId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const tree = this._getTreeById(treeId);
|
||||||
|
if (!tree) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._startFormulaDependencyTreeModel();
|
||||||
|
|
||||||
|
const treeModel = this._getFormulaDependencyTreeModel(tree);
|
||||||
|
const formula = this._lexerTreeBuilder.moveFormulaRefOffset(
|
||||||
|
tree.formula,
|
||||||
|
tree.refOffsetX,
|
||||||
|
tree.refOffsetY
|
||||||
|
);
|
||||||
|
treeModel.formula = formula;
|
||||||
|
|
||||||
|
const childIds = this._getDependencyTreeChildrenIds(tree);
|
||||||
|
for (const childId of childIds) {
|
||||||
|
const childTreeModel = this._getTreeModel(childId);
|
||||||
|
const tree = this._getTreeById(treeId);
|
||||||
|
if (!tree) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const formula = this._lexerTreeBuilder.moveFormulaRefOffset(
|
||||||
|
tree.formula,
|
||||||
|
tree.refOffsetX,
|
||||||
|
tree.refOffsetY
|
||||||
|
);
|
||||||
|
childTreeModel.formula = formula;
|
||||||
|
treeModel.addChild(childTreeModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._endFormulaDependencyTreeModel();
|
||||||
|
|
||||||
|
return treeModel.toFullJson();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ICommandInfo, IDisposable } from '@univerjs/core';
|
import type { ICommandInfo, IDisposable } from '@univerjs/core';
|
||||||
import type { FormulaExecutedStateType, IExecutionInProgressParams, ISequenceNode, ISetFormulaCalculationNotificationMutation, ISetFormulaCalculationStartMutation } from '@univerjs/engine-formula';
|
import type { FormulaExecutedStateType, IExecutionInProgressParams, IFormulaDependencyTreeFullJson, IFormulaDependencyTreeJson, IFormulaExecuteResultMap, IFormulaStringMap, ISequenceNode, ISetCellFormulaDependencyCalculationResultMutation, ISetFormulaCalculationNotificationMutation, ISetFormulaCalculationStartMutation, ISetFormulaDependencyCalculationResultMutation, ISetFormulaStringBatchCalculationResultMutation } from '@univerjs/engine-formula';
|
||||||
import { ICommandService, IConfigService, Inject, Injector } from '@univerjs/core';
|
import { ICommandService, IConfigService, Inject, Injector } from '@univerjs/core';
|
||||||
import { FBase } from '@univerjs/core/facade';
|
import { FBase } from '@univerjs/core/facade';
|
||||||
import { ENGINE_FORMULA_CYCLE_REFERENCE_COUNT, GlobalComputingStatusService, LexerTreeBuilder, SetFormulaCalculationNotificationMutation, SetFormulaCalculationStartMutation, SetFormulaCalculationStopMutation } from '@univerjs/engine-formula';
|
import { ENGINE_FORMULA_CYCLE_REFERENCE_COUNT, GlobalComputingStatusService, LexerTreeBuilder, SetCellFormulaDependencyCalculationMutation, SetCellFormulaDependencyCalculationResultMutation, SetFormulaCalculationNotificationMutation, SetFormulaCalculationStartMutation, SetFormulaCalculationStopMutation, SetFormulaDependencyCalculationMutation, SetFormulaDependencyCalculationResultMutation, SetFormulaStringBatchCalculationMutation, SetFormulaStringBatchCalculationResultMutation } from '@univerjs/engine-formula';
|
||||||
import { filter, firstValueFrom, map, race, timer } from 'rxjs';
|
import { filter, firstValueFrom, map, race, timer } from 'rxjs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -248,4 +248,173 @@ export class FFormula extends FBase {
|
|||||||
setMaxIteration(maxIteration: number): void {
|
setMaxIteration(maxIteration: number): void {
|
||||||
this._configService.setConfig(ENGINE_FORMULA_CYCLE_REFERENCE_COUNT, maxIteration);
|
this._configService.setConfig(ENGINE_FORMULA_CYCLE_REFERENCE_COUNT, maxIteration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a batch of formulas asynchronously and receive computed results.
|
||||||
|
*
|
||||||
|
* Each formula cell is represented as a string array:
|
||||||
|
* [fullFormula, ...subFormulas]
|
||||||
|
*
|
||||||
|
* Where:
|
||||||
|
* - fullFormula (index 0) is the complete formula expression written in the cell.
|
||||||
|
* Example: "=SUM(A1:A10) + SQRT(D7)".
|
||||||
|
*
|
||||||
|
* - subFormulas (index 1+) are **optional decomposed expressions** extracted from
|
||||||
|
* the full formula. Each of them can be independently computed by the formula engine.
|
||||||
|
*
|
||||||
|
* These sub-expressions can include:
|
||||||
|
* - Single-cell references: "A2", "B2", "C5"
|
||||||
|
* - Range references: "A1:A10"
|
||||||
|
* - Function calls: "SQRT(D7)", "ABS(A2-B2)"
|
||||||
|
* - Any sub-formula that was parsed out of the original formula and can be
|
||||||
|
* evaluated on its own.
|
||||||
|
*
|
||||||
|
* The batch execution engine may use these sub-formulas for dependency resolution,
|
||||||
|
* incremental computation, or performance optimizations.
|
||||||
|
*
|
||||||
|
* @param {IFormulaStringMap} formulas
|
||||||
|
* Nested structure (unit → sheet → row → column) describing formulas and
|
||||||
|
* their decomposed sub-expressions.
|
||||||
|
*
|
||||||
|
* @param {(result: IFormulaExecuteResultMap) => void} callback
|
||||||
|
* Receives the computed value map mirroring the input structure.
|
||||||
|
*
|
||||||
|
* @returns {IDisposable}
|
||||||
|
* A disposer to stop listening for batch results.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const formulaEngine = univerAPI.getFormula();
|
||||||
|
* const formulas = {
|
||||||
|
* Book1: {
|
||||||
|
* Sheet1: {
|
||||||
|
* 2: {
|
||||||
|
* 3: [
|
||||||
|
* // Full formula:
|
||||||
|
* "=SUM(A1:A10) + SQRT(D7)",
|
||||||
|
*
|
||||||
|
* // Decomposed sub-formulas (each one can be evaluated independently):
|
||||||
|
* "SUM(A1:A10)", // sub-formula 1
|
||||||
|
* "SQRT(D7)", // sub-formula 2
|
||||||
|
* "A1:A10", // range reference
|
||||||
|
* "D7", // single-cell reference
|
||||||
|
* ],
|
||||||
|
* },
|
||||||
|
* 4: {
|
||||||
|
* 5: [
|
||||||
|
* "=A2 + B2 + SQRT(C5)",
|
||||||
|
* "A2",
|
||||||
|
* "B2",
|
||||||
|
* "SQRT(C5)",
|
||||||
|
* ],
|
||||||
|
* }
|
||||||
|
* },
|
||||||
|
* },
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* const disposer = formulaEngine.executeFormulas(formulas, (result) => {
|
||||||
|
* console.log(result);
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
executeFormulas(formulas: IFormulaStringMap, callback: (result: IFormulaExecuteResultMap) => void): void {
|
||||||
|
this._commandService.executeCommand(SetFormulaStringBatchCalculationMutation.id, { formulas }, { onlyLocal: true });
|
||||||
|
const disposable = this._commandService.onCommandExecuted((command: ICommandInfo) => {
|
||||||
|
if (command.id !== SetFormulaStringBatchCalculationResultMutation.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = command.params as ISetFormulaStringBatchCalculationResultMutation;
|
||||||
|
|
||||||
|
if (params.result != null) {
|
||||||
|
callback(params.result);
|
||||||
|
}
|
||||||
|
disposable.dispose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all formula dependency trees that were produced during the latest
|
||||||
|
* dependency-analysis run. This triggers a local dependency-calculation command
|
||||||
|
* and returns the complete set of dependency trees once the calculation finishes.
|
||||||
|
*
|
||||||
|
* @param callback A function invoked with the resulting array of dependency trees.
|
||||||
|
*
|
||||||
|
* @returns {IDisposable} An object that disposes the internal event listener.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const formulaEngine = univerAPI.getFormula();
|
||||||
|
*
|
||||||
|
* // Fetch all dependency trees generated for the current workbook.
|
||||||
|
* const disposable = formulaEngine.getAllDependencyTrees((trees) => {
|
||||||
|
* console.log('All dependency trees:', trees);
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
getAllDependencyTrees(callback: (result: IFormulaDependencyTreeJson[]) => void): void {
|
||||||
|
this._commandService.executeCommand(SetFormulaDependencyCalculationMutation.id, undefined, { onlyLocal: true });
|
||||||
|
const disposable = this._commandService.onCommandExecuted((command: ICommandInfo) => {
|
||||||
|
if (command.id !== SetFormulaDependencyCalculationResultMutation.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = command.params as ISetFormulaDependencyCalculationResultMutation;
|
||||||
|
|
||||||
|
if (params.result != null) {
|
||||||
|
callback(params.result);
|
||||||
|
}
|
||||||
|
|
||||||
|
disposable.dispose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the dependency tree of a specific cell. This triggers a local
|
||||||
|
* dependency-calculation command for the given unit, sheet, and cell location,
|
||||||
|
* and returns the computed dependency tree when the calculation is completed.
|
||||||
|
*
|
||||||
|
* @param param The target cell location:
|
||||||
|
* - `unitId` The workbook ID.
|
||||||
|
* - `sheetId` The sheet ID.
|
||||||
|
* - `row` The zero-based row index.
|
||||||
|
* - `column` The zero-based column index.
|
||||||
|
*
|
||||||
|
* @param callback A function invoked with the resulting dependency tree or
|
||||||
|
* `undefined` if no dependency tree exists for that cell.
|
||||||
|
*
|
||||||
|
* @returns {IDisposable} An object that disposes the internal event listener.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const formulaEngine = univerAPI.getFormula();
|
||||||
|
*
|
||||||
|
* // Query the dependency tree for cell B2 in a specific sheet.
|
||||||
|
* const disposable = formulaEngine.getCellDependencyTree(
|
||||||
|
* { unitId: 'workbook1', sheetId: 'sheet1', row: 1, column: 1 },
|
||||||
|
* (tree) => {
|
||||||
|
* console.log('Cell dependency tree:', tree);
|
||||||
|
* }
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
getCellDependencyTree(param: { unitId: string; sheetId: string; row: number; column: number }, callback: (result: IFormulaDependencyTreeFullJson | undefined) => void): void {
|
||||||
|
this._commandService.executeCommand(SetCellFormulaDependencyCalculationMutation.id, param, { onlyLocal: true });
|
||||||
|
const disposable = this._commandService.onCommandExecuted((command: ICommandInfo) => {
|
||||||
|
if (command.id !== SetCellFormulaDependencyCalculationResultMutation.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = command.params as ISetCellFormulaDependencyCalculationResultMutation;
|
||||||
|
|
||||||
|
if (params.result !== undefined) {
|
||||||
|
callback(params.result);
|
||||||
|
}
|
||||||
|
|
||||||
|
disposable.dispose();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ export type {
|
|||||||
IFormulaData,
|
IFormulaData,
|
||||||
IFormulaDataItem,
|
IFormulaDataItem,
|
||||||
IFormulaDatasetConfig,
|
IFormulaDatasetConfig,
|
||||||
|
IFormulaExecuteResultMap,
|
||||||
|
IFormulaStringMap,
|
||||||
IRuntimeImageFormulaDataType,
|
IRuntimeImageFormulaDataType,
|
||||||
IRuntimeUnitDataType,
|
IRuntimeUnitDataType,
|
||||||
ISheetData,
|
ISheetData,
|
||||||
@@ -53,13 +55,23 @@ export { type ISetDefinedNameMutationParam, type ISetDefinedNameMutationSearchPa
|
|||||||
export { SetDefinedNameMutationFactory } from './commands/mutations/set-defined-name.mutation';
|
export { SetDefinedNameMutationFactory } from './commands/mutations/set-defined-name.mutation';
|
||||||
export { RemoveFeatureCalculationMutation, SetFeatureCalculationMutation } from './commands/mutations/set-feature-calculation.mutation';
|
export { RemoveFeatureCalculationMutation, SetFeatureCalculationMutation } from './commands/mutations/set-feature-calculation.mutation';
|
||||||
export {
|
export {
|
||||||
|
type ISetCellFormulaDependencyCalculationResultMutation,
|
||||||
type ISetFormulaCalculationNotificationMutation,
|
type ISetFormulaCalculationNotificationMutation,
|
||||||
type ISetFormulaCalculationResultMutation,
|
type ISetFormulaCalculationResultMutation,
|
||||||
type ISetFormulaCalculationStartMutation,
|
type ISetFormulaCalculationStartMutation,
|
||||||
|
type ISetFormulaDependencyCalculationMutation,
|
||||||
|
type ISetFormulaDependencyCalculationResultMutation,
|
||||||
|
type ISetFormulaStringBatchCalculationResultMutation,
|
||||||
|
SetCellFormulaDependencyCalculationMutation,
|
||||||
|
SetCellFormulaDependencyCalculationResultMutation,
|
||||||
SetFormulaCalculationNotificationMutation,
|
SetFormulaCalculationNotificationMutation,
|
||||||
SetFormulaCalculationResultMutation,
|
SetFormulaCalculationResultMutation,
|
||||||
SetFormulaCalculationStartMutation,
|
SetFormulaCalculationStartMutation,
|
||||||
SetFormulaCalculationStopMutation,
|
SetFormulaCalculationStopMutation,
|
||||||
|
SetFormulaDependencyCalculationMutation,
|
||||||
|
SetFormulaDependencyCalculationResultMutation,
|
||||||
|
SetFormulaStringBatchCalculationMutation,
|
||||||
|
SetFormulaStringBatchCalculationResultMutation,
|
||||||
} from './commands/mutations/set-formula-calculation.mutation';
|
} from './commands/mutations/set-formula-calculation.mutation';
|
||||||
export { type ISetFormulaDataMutationParams, SetFormulaDataMutation } from './commands/mutations/set-formula-data.mutation';
|
export { type ISetFormulaDataMutationParams, SetFormulaDataMutation } from './commands/mutations/set-formula-data.mutation';
|
||||||
export { type ISetImageFormulaDataMutationParams, SetImageFormulaDataMutation } from './commands/mutations/set-image-formula-data.mutation';
|
export { type ISetImageFormulaDataMutationParams, SetImageFormulaDataMutation } from './commands/mutations/set-image-formula-data.mutation';
|
||||||
@@ -83,7 +95,7 @@ export { ReferenceNodeFactory } from './engine/ast-node/reference-node';
|
|||||||
export { SuffixNodeFactory } from './engine/ast-node/suffix-node';
|
export { SuffixNodeFactory } from './engine/ast-node/suffix-node';
|
||||||
export { UnionNodeFactory } from './engine/ast-node/union-node';
|
export { UnionNodeFactory } from './engine/ast-node/union-node';
|
||||||
export { ValueNodeFactory } from './engine/ast-node/value-node';
|
export { ValueNodeFactory } from './engine/ast-node/value-node';
|
||||||
export { FormulaDependencyTree, type IFormulaDependencyTree } from './engine/dependency/dependency-tree';
|
export { FormulaDependencyTree, FormulaDependencyTreeModel, type IFormulaDependencyTree, type IFormulaDependencyTreeFullJson, type IFormulaDependencyTreeJson } from './engine/dependency/dependency-tree';
|
||||||
export { FormulaDependencyTreeType } from './engine/dependency/dependency-tree';
|
export { FormulaDependencyTreeType } from './engine/dependency/dependency-tree';
|
||||||
export { FormulaDependencyTreeVirtual } from './engine/dependency/dependency-tree';
|
export { FormulaDependencyTreeVirtual } from './engine/dependency/dependency-tree';
|
||||||
export { FormulaDependencyGenerator, IFormulaDependencyGenerator } from './engine/dependency/formula-dependency';
|
export { FormulaDependencyGenerator, IFormulaDependencyGenerator } from './engine/dependency/formula-dependency';
|
||||||
|
|||||||
@@ -18,15 +18,24 @@ import type { IUnitRange } from '@univerjs/core';
|
|||||||
import type { Observable } from 'rxjs';
|
import type { Observable } from 'rxjs';
|
||||||
import type {
|
import type {
|
||||||
IArrayFormulaRangeType,
|
IArrayFormulaRangeType,
|
||||||
|
IArrayFormulaUnitCellType,
|
||||||
IFeatureDirtyRangeType,
|
IFeatureDirtyRangeType,
|
||||||
|
IFormulaData,
|
||||||
IFormulaDatasetConfig,
|
IFormulaDatasetConfig,
|
||||||
|
IFormulaExecuteResultItem,
|
||||||
|
IFormulaExecuteResultMap,
|
||||||
|
IFormulaStringMap,
|
||||||
IRuntimeUnitDataType,
|
IRuntimeUnitDataType,
|
||||||
IUnitExcludedCell,
|
IUnitExcludedCell,
|
||||||
|
IUnitRowData,
|
||||||
} from '../basics/common';
|
} from '../basics/common';
|
||||||
|
|
||||||
import type { IUniverEngineFormulaConfig } from '../controller/config.schema';
|
import type { IUniverEngineFormulaConfig } from '../controller/config.schema';
|
||||||
import type { LexerNode } from '../engine/analysis/lexer-node';
|
import type { LexerNode } from '../engine/analysis/lexer-node';
|
||||||
import type { FunctionVariantType } from '../engine/reference-object/base-reference-object';
|
import type { IFormulaDependencyTreeFullJson, 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';
|
||||||
import type { IAllRuntimeData, IExecutionInProgressParams } from './runtime.service';
|
import type { IAllRuntimeData, IExecutionInProgressParams } from './runtime.service';
|
||||||
import {
|
import {
|
||||||
AsyncLock,
|
AsyncLock,
|
||||||
@@ -43,7 +52,6 @@ import { CELL_INVERTED_INDEX_CACHE } from '../basics/inverted-index-cache';
|
|||||||
import { DEFAULT_CYCLE_REFERENCE_COUNT, ENGINE_FORMULA_PLUGIN_CONFIG_KEY } from '../controller/config.schema';
|
import { DEFAULT_CYCLE_REFERENCE_COUNT, ENGINE_FORMULA_PLUGIN_CONFIG_KEY } from '../controller/config.schema';
|
||||||
import { Lexer } from '../engine/analysis/lexer';
|
import { Lexer } from '../engine/analysis/lexer';
|
||||||
import { AstTreeBuilder } from '../engine/analysis/parser';
|
import { AstTreeBuilder } from '../engine/analysis/parser';
|
||||||
import { ErrorNode } from '../engine/ast-node/base-ast-node';
|
|
||||||
import { IFormulaDependencyGenerator } from '../engine/dependency/formula-dependency';
|
import { IFormulaDependencyGenerator } from '../engine/dependency/formula-dependency';
|
||||||
import { Interpreter } from '../engine/interpreter/interpreter';
|
import { Interpreter } from '../engine/interpreter/interpreter';
|
||||||
import { FORMULA_REF_TO_ARRAY_CACHE } from '../engine/reference-object/base-reference-object';
|
import { FORMULA_REF_TO_ARRAY_CACHE } from '../engine/reference-object/base-reference-object';
|
||||||
@@ -66,6 +74,9 @@ export interface ICalculateFormulaService {
|
|||||||
execute(formulaDatasetConfig: IFormulaDatasetConfig): Promise<void>;
|
execute(formulaDatasetConfig: IFormulaDatasetConfig): Promise<void>;
|
||||||
stopFormulaExecution(): void;
|
stopFormulaExecution(): void;
|
||||||
calculate(formulaString: string, transformSuffix?: boolean): void;
|
calculate(formulaString: string, transformSuffix?: boolean): void;
|
||||||
|
executeFormulas(formulas: IFormulaStringMap, formulaData: IFormulaData, arrayFormulaCellData: IArrayFormulaUnitCellType, arrayFormulaRange: IArrayFormulaRangeType, rowData: IUnitRowData): Promise<IFormulaExecuteResultMap>;
|
||||||
|
getAllDependencyJson(): Promise<IFormulaDependencyTreeJson[]>;
|
||||||
|
getCellDependencyJson(unitId: string, sheetId: string, row: number, column: number): Promise<IFormulaDependencyTreeFullJson | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ICalculateFormulaService = createIdentifier<ICalculateFormulaService>('engine-formula.calculate-formula.service');
|
export const ICalculateFormulaService = createIdentifier<ICalculateFormulaService>('engine-formula.calculate-formula.service');
|
||||||
@@ -367,42 +378,147 @@ export class CalculateFormulaService extends Disposable implements ICalculateFor
|
|||||||
return this._runtimeService.getAllRuntimeData();
|
return this._runtimeService.getAllRuntimeData();
|
||||||
}
|
}
|
||||||
|
|
||||||
calculate(formulaString: string, transformSuffix: boolean = true) {
|
async executeFormulas(formulas: IFormulaStringMap, formulaData: IFormulaData, arrayFormulaCellData: IArrayFormulaUnitCellType, arrayFormulaRange: IArrayFormulaRangeType, rowData?: IUnitRowData) {
|
||||||
// TODO how to observe @alex
|
this._currentConfigService.loadDataLite(
|
||||||
// this.getObserver('onBeforeFormulaCalculateObservable')?.notifyObservers(formulaString);
|
formulaData,
|
||||||
const lexerNode = this._lexer.treeBuilder(formulaString, transformSuffix);
|
arrayFormulaCellData,
|
||||||
|
arrayFormulaRange,
|
||||||
|
rowData
|
||||||
|
);
|
||||||
|
|
||||||
if (Object.values(ErrorType).includes(lexerNode as ErrorType)) {
|
const unitData = this._currentConfigService.getUnitData();
|
||||||
return ErrorNode.create(lexerNode as ErrorType);
|
|
||||||
|
this._runtimeService.reset();
|
||||||
|
|
||||||
|
const result: IFormulaExecuteResultMap = {};
|
||||||
|
|
||||||
|
for (const unitId of Object.keys(formulas)) {
|
||||||
|
const sheetFormulas = formulas[unitId];
|
||||||
|
if (sheetFormulas == null) continue;
|
||||||
|
|
||||||
|
result[unitId] = {};
|
||||||
|
|
||||||
|
for (const sheetId of Object.keys(sheetFormulas)) {
|
||||||
|
const rowFormulas = sheetFormulas[sheetId];
|
||||||
|
if (rowFormulas == null) continue;
|
||||||
|
|
||||||
|
const sheetItem = unitData[unitId]?.[sheetId];
|
||||||
|
|
||||||
|
if (sheetItem == null) continue;
|
||||||
|
|
||||||
|
result[unitId]![sheetId] = {};
|
||||||
|
|
||||||
|
for (const rowString of Object.keys(rowFormulas)) {
|
||||||
|
const row = Number.parseInt(rowString);
|
||||||
|
result[unitId]![sheetId]![row] = {};
|
||||||
|
|
||||||
|
const cellData = rowFormulas[row];
|
||||||
|
if (!cellData) continue;
|
||||||
|
|
||||||
|
for (const columnString of Object.keys(cellData)) {
|
||||||
|
const column = Number.parseInt(columnString);
|
||||||
|
const formulaStrings = cellData[column];
|
||||||
|
if (!formulaStrings) continue;
|
||||||
|
|
||||||
|
const rowCount = sheetItem.rowCount || 0;
|
||||||
|
const columnCount = sheetItem.columnCount || 0;
|
||||||
|
|
||||||
|
this._runtimeService.setCurrent(
|
||||||
|
row,
|
||||||
|
column,
|
||||||
|
rowCount,
|
||||||
|
columnCount,
|
||||||
|
sheetId,
|
||||||
|
unitId
|
||||||
|
);
|
||||||
|
|
||||||
|
const item: IFormulaExecuteResultItem[] = [];
|
||||||
|
|
||||||
|
for (const formulaString of formulaStrings) {
|
||||||
|
let resultCell = await this.calculate(formulaString);
|
||||||
|
if (!resultCell) {
|
||||||
|
item.push({
|
||||||
|
value: null,
|
||||||
|
formula: formulaString,
|
||||||
|
});
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultCell.isReferenceObject()) {
|
||||||
|
resultCell = (resultCell as BaseReferenceObject).toArrayValueObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
const resultRefObject = resultCell as BaseValueObject;
|
||||||
|
|
||||||
|
if (resultRefObject.isArray()) {
|
||||||
|
const resultRefArrayObject = resultRefObject as ArrayValueObject;
|
||||||
|
if (resultRefArrayObject.getRowCount() === 1 && resultRefArrayObject.getColumnCount() === 1) {
|
||||||
|
item.push({
|
||||||
|
value: resultRefArrayObject.getFirstCell().getValue(),
|
||||||
|
formula: formulaString,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.push({
|
||||||
|
value: resultRefArrayObject.toValue(),
|
||||||
|
formula: formulaString,
|
||||||
|
});
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.push({
|
||||||
|
value: resultRefObject.getValue(),
|
||||||
|
formula: formulaString,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
result[unitId]![sheetId]![row]![column] = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this.lexerTreeBuilder.suffixExpressionHandler(lexerNode); // suffix Express, 1+(3*4=4)*5+1 convert to 134*4=5*1++
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// console.log('sequence', this.lexerTreeBuilder.sequenceNodesBuilder(formulaString));
|
async calculate(formulaString: string) {
|
||||||
|
// TODO how to observe @alex
|
||||||
|
// this.getObserver('onBeforeFormulaCalculateObservable')?.notifyObservers(formulaString);
|
||||||
|
const lexerNode = this._lexer.treeBuilder(formulaString);
|
||||||
|
|
||||||
// this.getObserver('onAfterFormulaLexerObservable')?.notifyObservers(lexerNode);
|
if (Object.values(ErrorType).includes(lexerNode as ErrorType)) {
|
||||||
|
return;
|
||||||
// const astTreeBuilder = new AstTreeBuilder();
|
}
|
||||||
|
|
||||||
const astNode = this._astTreeBuilder.parse(lexerNode as LexerNode);
|
const astNode = this._astTreeBuilder.parse(lexerNode as LexerNode);
|
||||||
|
|
||||||
// console.log('astNode', astNode?.serialize());
|
const interpreter = this._interpreter;
|
||||||
astNode?.serialize();
|
|
||||||
|
|
||||||
// const interpreter = Interpreter.create();
|
if (astNode == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// if (astNode == null) {
|
const nodeData = {
|
||||||
// return;
|
node: astNode,
|
||||||
// }
|
refOffsetX: 0,
|
||||||
|
refOffsetY: 0,
|
||||||
|
};
|
||||||
|
|
||||||
// if (interpreter.checkAsyncNode(astNode)) {
|
if (interpreter.checkAsyncNode(astNode)) {
|
||||||
// const resultPromise = interpreter.executeAsync(astNode);
|
return await interpreter.executeAsync(nodeData);
|
||||||
|
} else {
|
||||||
|
return interpreter.execute(nodeData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// resultPromise.then((value) => {
|
async getAllDependencyJson(): Promise<IFormulaDependencyTreeJson[]> {
|
||||||
// console.log('formulaResult', value);
|
return this._formulaDependencyGenerator.getAllDependencyJson();
|
||||||
// });
|
}
|
||||||
// } else {
|
|
||||||
// console.log(interpreter.execute(astNode));
|
async getCellDependencyJson(unitId: string, sheetId: string, row: number, column: number): Promise<IFormulaDependencyTreeFullJson | undefined> {
|
||||||
// }
|
return this._formulaDependencyGenerator.getCellDependencyJson(unitId, sheetId, row, column);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
import type { IUnitRange, LocaleType, Nullable, Workbook } from '@univerjs/core';
|
import type { IUnitRange, LocaleType, Nullable, Workbook } from '@univerjs/core';
|
||||||
import type {
|
import type {
|
||||||
IArrayFormulaRangeType,
|
IArrayFormulaRangeType,
|
||||||
|
IArrayFormulaUnitCellType,
|
||||||
IDirtyUnitFeatureMap,
|
IDirtyUnitFeatureMap,
|
||||||
IDirtyUnitOtherFormulaMap,
|
IDirtyUnitOtherFormulaMap,
|
||||||
IDirtyUnitSheetDefinedNameMap,
|
IDirtyUnitSheetDefinedNameMap,
|
||||||
@@ -113,6 +114,8 @@ export interface IFormulaCurrentConfigService {
|
|||||||
getFilteredOutRows(unitId: string, sheetId: string, startRow: number, endRow: number): number[];
|
getFilteredOutRows(unitId: string, sheetId: string, startRow: number, endRow: number): number[];
|
||||||
|
|
||||||
setSheetNameMap(sheetIdToNameMap: IUnitSheetIdToNameMap): void;
|
setSheetNameMap(sheetIdToNameMap: IUnitSheetIdToNameMap): void;
|
||||||
|
|
||||||
|
loadDataLite(formulaData: IFormulaData, arrayFormulaCellData: IArrayFormulaUnitCellType, arrayFormulaRange: IArrayFormulaRangeType, rowData?: IUnitRowData): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FormulaCurrentConfigService extends Disposable implements IFormulaCurrentConfigService {
|
export class FormulaCurrentConfigService extends Disposable implements IFormulaCurrentConfigService {
|
||||||
@@ -345,6 +348,25 @@ export class FormulaCurrentConfigService extends Disposable implements IFormulaC
|
|||||||
this._mergeNameMap(this._sheetNameMap, this._dirtyNameMap);
|
this._mergeNameMap(this._sheetNameMap, this._dirtyNameMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadDataLite(formulaData: IFormulaData, arrayFormulaCellData: IArrayFormulaUnitCellType, arrayFormulaRange: IArrayFormulaRangeType, rowData?: IUnitRowData) {
|
||||||
|
const { allUnitData, unitSheetNameMap, unitStylesData } = this._loadSheetData();
|
||||||
|
|
||||||
|
this._unitData = allUnitData;
|
||||||
|
|
||||||
|
this._unitStylesData = unitStylesData;
|
||||||
|
|
||||||
|
this._sheetNameMap = unitSheetNameMap;
|
||||||
|
|
||||||
|
this._formulaData = formulaData;
|
||||||
|
|
||||||
|
this._arrayFormulaCellData = convertUnitDataToRuntime(arrayFormulaCellData);
|
||||||
|
|
||||||
|
this._arrayFormulaRange = arrayFormulaRange;
|
||||||
|
|
||||||
|
// apply row data, including rows hidden by filters
|
||||||
|
rowData && this._applyUnitRowData(rowData);
|
||||||
|
}
|
||||||
|
|
||||||
getDirtyData(): IFormulaDirtyData {
|
getDirtyData(): IFormulaDirtyData {
|
||||||
return {
|
return {
|
||||||
forceCalculation: this._forceCalculate,
|
forceCalculation: this._forceCalculate,
|
||||||
|
|||||||
Reference in New Issue
Block a user