fix(formula): add api and fix ref formula (#6336)

This commit is contained in:
Univer
2025-12-18 13:06:40 +08:00
committed by GitHub
parent 64d075b488
commit 32f8d4570d
8 changed files with 186 additions and 18 deletions
@@ -22,7 +22,7 @@ import type {
IRuntimeUnitDataPrimitiveType,
} from '../../basics/common';
import type { IFormulaDependencyTreeFullJson, IFormulaDependencyTreeJson } from '../../engine/dependency/dependency-tree';
import type { IFormulaDependencyTreeFullJson, IFormulaDependencyTreeJson, IFormulaDependentsAndInRangeResults } from '../../engine/dependency/dependency-tree';
import type { IFormulaDirtyData } from '../../services/current-data.service';
import type { FormulaExecutedStateType, IExecutionInProgressParams } from '../../services/runtime.service';
import { CommandType } from '@univerjs/core';
@@ -67,6 +67,14 @@ export interface ISetQueryFormulaDependencyResultMutation {
result: IFormulaDependencyTreeJson[];
}
export interface ISetQueryFormulaDependencyAllMutation {
unitRanges: IUnitRange[];
}
export interface ISetQueryFormulaDependencyAllResultMutation {
result: IFormulaDependentsAndInRangeResults;
}
/**
* TODO: @DR-Univer
* Trigger the calculation of the formula and stop the formula
@@ -161,3 +169,15 @@ export const SetQueryFormulaDependencyResultMutation: IMutation<ISetQueryFormula
type: CommandType.MUTATION,
handler: () => true,
};
export const SetQueryFormulaDependencyAllMutation: IMutation<ISetQueryFormulaDependencyAllMutation> = {
id: 'formula.mutation.set-query-formula-dependency-all',
type: CommandType.MUTATION,
handler: () => true,
};
export const SetQueryFormulaDependencyAllResultMutation: IMutation<ISetQueryFormulaDependencyAllResultMutation> = {
id: 'formula.mutation.set-query-formula-dependency-all-result',
type: CommandType.MUTATION,
handler: () => true,
};
@@ -16,8 +16,8 @@
import type { ICommandInfo } from '@univerjs/core';
import type { ISetArrayFormulaDataMutationParams } from '../commands/mutations/set-array-formula-data.mutation';
import type { ISetFormulaCalculationStartMutation, ISetFormulaDependencyCalculationMutation, ISetFormulaStringBatchCalculationMutation, ISetQueryFormulaDependencyMutation } from '../commands/mutations/set-formula-calculation.mutation';
import type { IFormulaDependencyTreeJson } from '../engine/dependency/dependency-tree';
import type { ISetFormulaCalculationStartMutation, ISetFormulaDependencyCalculationMutation, ISetFormulaStringBatchCalculationMutation, ISetQueryFormulaDependencyAllMutation, ISetQueryFormulaDependencyMutation } from '../commands/mutations/set-formula-calculation.mutation';
import type { IFormulaDependencyTreeJson, IFormulaDependentsAndInRangeResults } from '../engine/dependency/dependency-tree';
import type { IFormulaDirtyData } from '../services/current-data.service';
import type { IAllRuntimeData } from '../services/runtime.service';
import { Disposable, ICommandService, Inject } from '@univerjs/core';
@@ -34,6 +34,8 @@ import {
SetFormulaDependencyCalculationResultMutation,
SetFormulaStringBatchCalculationMutation,
SetFormulaStringBatchCalculationResultMutation,
SetQueryFormulaDependencyAllMutation,
SetQueryFormulaDependencyAllResultMutation,
SetQueryFormulaDependencyMutation,
SetQueryFormulaDependencyResultMutation,
} from '../commands/mutations/set-formula-calculation.mutation';
@@ -85,6 +87,9 @@ export class CalculateController extends Disposable {
} else if (command.id === SetQueryFormulaDependencyMutation.id) {
const params = command.params as ISetQueryFormulaDependencyMutation;
this._queryFormulaDependencyJson(params);
} else if (command.id === SetQueryFormulaDependencyAllMutation.id) {
const params = command.params as ISetQueryFormulaDependencyAllMutation;
this._queryFormulaDependencyAllJson(params);
}
})
);
@@ -137,6 +142,20 @@ export class CalculateController extends Disposable {
);
}
private async _queryFormulaDependencyAllJson(param: ISetQueryFormulaDependencyAllMutation) {
const { unitRanges } = param;
const result: IFormulaDependentsAndInRangeResults = await this._calculateFormulaService.getDependentsAndInRangeFormulas(unitRanges);
this._commandService.executeCommand(
SetQueryFormulaDependencyAllResultMutation.id,
{
result,
},
{
onlyLocal: true,
}
);
}
private async _generateAllDependencyTreeJson() {
const result = await this._calculateFormulaService.getAllDependencyJson();
@@ -35,6 +35,8 @@ import {
SetFormulaDependencyCalculationResultMutation,
SetFormulaStringBatchCalculationMutation,
SetFormulaStringBatchCalculationResultMutation,
SetQueryFormulaDependencyAllMutation,
SetQueryFormulaDependencyAllResultMutation,
SetQueryFormulaDependencyMutation,
SetQueryFormulaDependencyResultMutation,
SetTriggerFormulaCalculationStartMutation,
@@ -93,6 +95,8 @@ export class FormulaController extends Disposable {
SetFormulaCalculationStopMutation,
SetFormulaCalculationNotificationMutation,
SetFormulaCalculationResultMutation,
SetQueryFormulaDependencyAllMutation,
SetQueryFormulaDependencyAllResultMutation,
SetFormulaDependencyCalculationMutation,
SetFormulaDependencyCalculationResultMutation,
@@ -538,6 +538,11 @@ export interface IFormulaDependencyTreeFullJson extends IFormulaDependencyTreeJs
parents: IFormulaDependencyTreeJson[];
}
export interface IFormulaDependentsAndInRangeResults {
dependents: IFormulaDependencyTreeJson[];
inRanges: IFormulaDependencyTreeJson[];
}
export class FormulaDependencyTreeModel {
children: Set<FormulaDependencyTreeModel> = new Set();
@@ -25,7 +25,7 @@ import type { BaseAstNode } from '../ast-node/base-ast-node';
import type { BaseReferenceObject, FunctionVariantType } from '../reference-object/base-reference-object';
import type { IExecuteAstNodeData } from '../utils/ast-node-tool';
import type { PreCalculateNodeType } from '../utils/node-type';
import type { IFormulaDependencyTree, IFormulaDependencyTreeFullJson, IFormulaDependencyTreeJson } from './dependency-tree';
import type { IFormulaDependencyTree, IFormulaDependencyTreeFullJson, IFormulaDependencyTreeJson, IFormulaDependentsAndInRangeResults } from './dependency-tree';
import { createIdentifier, Disposable, Inject, ObjectMatrix, RTree } from '@univerjs/core';
import { prefixToken, suffixToken } from '../../basics/token';
@@ -61,6 +61,7 @@ export interface IFormulaDependencyGenerator {
getCellDependencyJson(unitId: string, sheetId: string, row: number, column: number): Promise<IFormulaDependencyTreeFullJson | undefined>;
getRangeDependents(unitRanges: IUnitRange[]): Promise<IFormulaDependencyTreeJson[]>;
getInRangeFormulas(unitRanges: IUnitRange[]): Promise<IFormulaDependencyTreeJson[]>;
getRangeDependentsAndInRangeFormulas(unitRanges: IUnitRange[]): Promise<IFormulaDependentsAndInRangeResults>;
}
export const IFormulaDependencyGenerator = createIdentifier<IFormulaDependencyGenerator>('engine-formula.dependency-generator');
@@ -1415,7 +1416,7 @@ export class FormulaDependencyGenerator extends Disposable {
}
protected _setRealFormulaString(treeModel: FormulaDependencyTreeModel) {
if (!treeModel.refTreeId) {
if (treeModel.refTreeId == null) {
return;
}
@@ -1463,11 +1464,7 @@ export class FormulaDependencyGenerator extends Disposable {
return treeModel.toFullJson();
}
async getRangeDependents(unitRanges: IUnitRange[]): Promise<IFormulaDependencyTreeJson[]> {
await this._initializeGenerateTreeList();
this._startFormulaDependencyTreeModel();
protected _getRangeDependents(unitRanges: IUnitRange[]): IFormulaDependencyTreeJson[] {
const treeIds = this._dependencyManagerService.searchDependency(unitRanges);
const treeList: FormulaDependencyTreeModel[] = [];
for (const treeId of treeIds) {
@@ -1487,13 +1484,22 @@ export class FormulaDependencyGenerator extends Disposable {
}
}
return resultsJson;
}
async getRangeDependents(unitRanges: IUnitRange[]): Promise<IFormulaDependencyTreeJson[]> {
await this._initializeGenerateTreeList();
this._startFormulaDependencyTreeModel();
const resultsJson = this._getRangeDependents(unitRanges);
this._endFormulaDependencyTreeModel();
return resultsJson;
}
async getInRangeFormulas(unitRanges: IUnitRange[]): Promise<IFormulaDependencyTreeJson[]> {
const treeList = await this._getAllTreeList();
protected _getInRangeFormulas(unitRanges: IUnitRange[], treeList: IFormulaDependencyTree[]): IFormulaDependencyTreeJson[] {
const matchTreeList: IFormulaDependencyTree[] = [];
for (const dependencyTree of treeList) {
for (const unitRange of unitRanges) {
@@ -1512,8 +1518,6 @@ export class FormulaDependencyGenerator extends Disposable {
}
}
this._startFormulaDependencyTreeModel();
const results: FormulaDependencyTreeModel[] = [];
for (const tree of matchTreeList) {
const treeModel = this._getFormulaDependencyTreeModel(tree);
@@ -1528,8 +1532,35 @@ export class FormulaDependencyGenerator extends Disposable {
}
}
return resultsJson;
}
async getInRangeFormulas(unitRanges: IUnitRange[]): Promise<IFormulaDependencyTreeJson[]> {
const treeList = await this._getAllTreeList();
this._startFormulaDependencyTreeModel();
const resultsJson = this._getInRangeFormulas(unitRanges, treeList);
this._endFormulaDependencyTreeModel();
return resultsJson;
}
async getRangeDependentsAndInRangeFormulas(unitRanges: IUnitRange[]): Promise<IFormulaDependentsAndInRangeResults> {
const treeList = await this._getAllTreeList();
this._startFormulaDependencyTreeModel();
const dependentsJson = this._getRangeDependents(unitRanges);
const inRangeFormulasJson = this._getInRangeFormulas(unitRanges, treeList);
this._endFormulaDependencyTreeModel();
return {
dependents: dependentsJson,
inRanges: inRangeFormulasJson,
};
}
}
@@ -15,10 +15,10 @@
*/
import type { ICommandInfo, IDisposable, IUnitRange } from '@univerjs/core';
import type { FormulaExecutedStateType, IExecutionInProgressParams, IExprTreeNode, IFormulaDependencyTreeFullJson, IFormulaDependencyTreeJson, IFormulaExecuteResultMap, IFormulaStringMap, ISequenceNode, ISetCellFormulaDependencyCalculationResultMutation, ISetFormulaCalculationNotificationMutation, ISetFormulaCalculationResultMutation, ISetFormulaCalculationStartMutation, ISetFormulaDependencyCalculationResultMutation, ISetFormulaStringBatchCalculationResultMutation } from '@univerjs/engine-formula';
import type { FormulaExecutedStateType, IExecutionInProgressParams, IExprTreeNode, IFormulaDependencyTreeFullJson, IFormulaDependencyTreeJson, IFormulaDependentsAndInRangeResults, IFormulaExecuteResultMap, IFormulaStringMap, ISequenceNode, ISetCellFormulaDependencyCalculationResultMutation, ISetFormulaCalculationNotificationMutation, ISetFormulaCalculationResultMutation, ISetFormulaCalculationStartMutation, ISetFormulaDependencyCalculationResultMutation, ISetFormulaStringBatchCalculationResultMutation, ISetQueryFormulaDependencyAllResultMutation } from '@univerjs/engine-formula';
import { ICommandService, IConfigService, Inject, Injector } from '@univerjs/core';
import { FBase } from '@univerjs/core/facade';
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, SetTriggerFormulaCalculationStartMutation } 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, SetQueryFormulaDependencyAllMutation, SetQueryFormulaDependencyAllResultMutation, SetQueryFormulaDependencyMutation, SetQueryFormulaDependencyResultMutation, SetTriggerFormulaCalculationStartMutation } from '@univerjs/engine-formula';
import { filter, firstValueFrom, map, race, timer } from 'rxjs';
/**
@@ -844,4 +844,84 @@ export class FFormula extends FBase {
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));
}
/**
* Retrieve **both**:
* 1) the full dependency trees of all formulas that **depend on** the specified ranges, and
* 2) the dependency trees of all formulas that **physically reside inside** the specified ranges.
*
* This is a convenience API that combines the behaviors of
* `getRangeDependents` and `getInRangeFormulas` into a single call.
*
* Internally, it triggers a local dependency-calculation command once and
* resolves when both result sets are available, avoiding duplicate
* calculations and event listeners.
*
* @param unitRanges An array of workbook/sheet ranges to query. Each range
* includes:
* - `unitId` The workbook ID.
* - `sheetId` The sheet ID.
* - `range` The zero-based row/column boundaries.
*
* @param {number} [timeout]
* Optional timeout in milliseconds. If the dependency calculation does
* not complete within this period, the promise will be rejected.
*
* @returns {Promise<IFormulaDependentsAndInRangeResults>}
* A promise that resolves with an object containing:
* - `dependents`: Dependency trees of all formulas that depend on the
* specified ranges (upstream consumers).
* - `inRanges`: Dependency trees of all formulas whose definitions
* are located inside the specified ranges.
*
* @example
* ```ts
* const formulaEngine = univerAPI.getFormula();
*
* const result = await formulaEngine.getRangeDependentsAndInRangeFormulas([
* {
* unitId: 'workbook1',
* sheetId: 'sheet1',
* range: { startRow: 0, endRow: 9, startColumn: 0, endColumn: 1 },
* },
* ]);
*
* console.log('Dependent formulas:', result.dependents);
* console.log('Formulas inside range:', result.inRanges);
* ```
*/
getRangeDependentsAndInRangeFormulas(
unitRanges: IUnitRange[],
timeout = 30_000
): Promise<IFormulaDependentsAndInRangeResults> {
return new Promise((resolve, reject) => {
const disposable = this._commandService.onCommandExecuted((command: ICommandInfo) => {
if (command.id !== SetQueryFormulaDependencyAllResultMutation.id) {
return;
}
const params = command.params as ISetQueryFormulaDependencyAllResultMutation;
clearTimeout(timer);
disposable.dispose();
if (params.result != null) {
resolve(params.result);
} else {
resolve({ dependents: [], inRanges: [] });
}
});
const timer = setTimeout(() => {
disposable.dispose();
reject(new Error('Range dependents calculation timeout'));
}, timeout);
this._commandService.executeCommand(
SetQueryFormulaDependencyAllMutation.id,
{ unitRanges },
{ onlyLocal: true }
);
});
}
}
+4 -1
View File
@@ -63,6 +63,7 @@ export {
type ISetFormulaDependencyCalculationMutation,
type ISetFormulaDependencyCalculationResultMutation,
type ISetFormulaStringBatchCalculationResultMutation,
type ISetQueryFormulaDependencyAllResultMutation,
type ISetQueryFormulaDependencyResultMutation,
SetCellFormulaDependencyCalculationMutation,
SetCellFormulaDependencyCalculationResultMutation,
@@ -74,6 +75,8 @@ export {
SetFormulaDependencyCalculationResultMutation,
SetFormulaStringBatchCalculationMutation,
SetFormulaStringBatchCalculationResultMutation,
SetQueryFormulaDependencyAllMutation,
SetQueryFormulaDependencyAllResultMutation,
SetQueryFormulaDependencyMutation,
SetQueryFormulaDependencyResultMutation,
SetTriggerFormulaCalculationStartMutation,
@@ -100,7 +103,7 @@ export { ReferenceNodeFactory } from './engine/ast-node/reference-node';
export { SuffixNodeFactory } from './engine/ast-node/suffix-node';
export { UnionNodeFactory } from './engine/ast-node/union-node';
export { ValueNodeFactory } from './engine/ast-node/value-node';
export { FormulaDependencyTree, FormulaDependencyTreeModel, type IFormulaDependencyTree, type IFormulaDependencyTreeFullJson, type IFormulaDependencyTreeJson } from './engine/dependency/dependency-tree';
export { FormulaDependencyTree, FormulaDependencyTreeModel, type IFormulaDependencyTree, type IFormulaDependencyTreeFullJson, type IFormulaDependencyTreeJson, type IFormulaDependentsAndInRangeResults } from './engine/dependency/dependency-tree';
export { FormulaDependencyTreeType } from './engine/dependency/dependency-tree';
export { FormulaDependencyTreeVirtual } from './engine/dependency/dependency-tree';
export { FormulaDependencyGenerator, IFormulaDependencyGenerator } from './engine/dependency/formula-dependency';
@@ -30,7 +30,7 @@ import type {
import type { IUniverEngineFormulaConfig } from '../controller/config.schema';
import type { LexerNode } from '../engine/analysis/lexer-node';
import type { IFormulaDependencyTreeFullJson, IFormulaDependencyTreeJson } from '../engine/dependency/dependency-tree';
import type { IFormulaDependencyTreeFullJson, IFormulaDependencyTreeJson, IFormulaDependentsAndInRangeResults } 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 +77,7 @@ export interface ICalculateFormulaService {
getCellDependencyJson(unitId: string, sheetId: string, row: number, column: number, rowData?: IUnitRowData): Promise<IFormulaDependencyTreeFullJson | undefined>;
getRangeDependents(unitRanges: IUnitRange[]): Promise<IFormulaDependencyTreeJson[]>;
getInRangeFormulas(unitRanges: IUnitRange[]): Promise<IFormulaDependencyTreeJson[]>;
getDependentsAndInRangeFormulas(unitRanges: IUnitRange[]): Promise<IFormulaDependentsAndInRangeResults>;
}
export const ICalculateFormulaService = createIdentifier<ICalculateFormulaService>('engine-formula.calculate-formula.service');
@@ -534,4 +535,9 @@ export class CalculateFormulaService extends Disposable implements ICalculateFor
this._currentConfigService.loadDataLite();
return this._formulaDependencyGenerator.getInRangeFormulas(unitRanges);
}
async getDependentsAndInRangeFormulas(unitRanges: IUnitRange[]): Promise<IFormulaDependentsAndInRangeResults> {
this._currentConfigService.loadDataLite();
return this._formulaDependencyGenerator.getRangeDependentsAndInRangeFormulas(unitRanges);
}
}