perf(selection): formula and selection (#6197)

This commit is contained in:
Univer
2025-11-24 21:54:51 +08:00
committed by GitHub
parent 57c1d68143
commit 344f773f52
10 changed files with 48 additions and 33 deletions
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import type { IDefinedNameMap } from '../../services/defined-names.service';
import { Disposable, Inject } from '@univerjs/core';
import { IFormulaCurrentConfigService } from '../../services/current-data.service';
import { IDefinedNamesService } from '../../services/defined-names.service';
@@ -29,6 +30,11 @@ export class Lexer extends Disposable {
}
treeBuilder(formulaString: string, transformSuffix = true) {
const definedNames = this._definedNamesService.getAllDefinedNames();
if (this._isDeepDefinedNameMapEmpty(definedNames)) {
return this._lexerTreeBuilder.treeBuilder(formulaString, transformSuffix);
}
return this._lexerTreeBuilder.treeBuilder(formulaString, transformSuffix, {
unitId: this._formulaCurrentConfigService.getExecuteUnitId(),
getValueByName: this._definedNamesService.getValueByName.bind(this._definedNamesService),
@@ -36,4 +42,13 @@ export class Lexer extends Disposable {
getSheetName: this._formulaCurrentConfigService.getSheetName.bind(this._formulaCurrentConfigService),
});
}
private _isDeepDefinedNameMapEmpty(map: IDefinedNameMap): boolean {
for (const unitId in map) {
if (Object.keys(map[unitId]).length > 0) {
return false;
}
}
return true;
}
}
@@ -184,33 +184,31 @@ export class ReferenceNodeFactory extends BaseAstNodeFactory {
const currentConfigService = this._currentConfigService;
const runtimeService = this._formulaRuntimeService;
const tableMap = this._getTableMap();
const isSuperTableDirect = tableMap?.has(tokenTrim) ?? false;
const isCellRange = regexTestSingeRange(tokenTrim);
const parentIsUnion = isLexerNode && this._checkParentIsUnionOperator(param as LexerNode);
const isRowRef = parentIsUnion && regexTestSingleRow(tokenTrim);
const isColRef = parentIsUnion && regexTestSingleColumn(tokenTrim);
const makeRef = (type: ReferenceObjectType) =>
new ReferenceNode(currentConfigService, runtimeService, tokenTrim, type, isPrepareMerge);
if (isSuperTableDirect) {
return this._getTableReferenceNode(tokenTrim, isLexerNode, isPrepareMerge, true);
}
const isCellRange = regexTestSingeRange(tokenTrim);
if (isCellRange) {
return makeRef(ReferenceObjectType.CELL);
}
const parentIsUnion = isLexerNode && this._checkParentIsUnionOperator(param as LexerNode);
const isRowRef = parentIsUnion && regexTestSingleRow(tokenTrim);
if (isRowRef) {
return makeRef(ReferenceObjectType.ROW);
}
const isColRef = parentIsUnion && regexTestSingleColumn(tokenTrim);
if (isColRef) {
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);
}
@@ -570,6 +570,7 @@ export class FormulaDependencyGenerator extends Disposable {
const treeId = this._dependencyManagerService.getFormulaDependency(unitId, sheetId, row, column);
if (treeId != null) {
FDtree.treeId = treeId;
FDtree.isCache = true;
} else {
this._dependencyManagerService.addFormulaDependency(unitId, sheetId, row, column, FDtree);
this._dependencyManagerService.addFormulaDependencyByDefinedName(FDtree);
@@ -604,6 +605,7 @@ export class FormulaDependencyGenerator extends Disposable {
const treeId = this._dependencyManagerService.getFormulaDependency(unitId, sheetId, row, column);
if (treeId != null) {
FDtree.treeId = treeId;
FDtree.isCache = true;
} else {
this._dependencyManagerService.addFormulaDependency(unitId, sheetId, row, column, FDtree);
this._dependencyManagerService.addFormulaDependencyByDefinedName(FDtree);
@@ -124,7 +124,8 @@ export class BaseReferenceObject extends ObjectClassType {
getRangePosition() {
const { x, y } = this.getRefOffset();
let { startRow, startColumn, endRow, endColumn } = moveRangeByOffset(this.getRangeData(), x, y);
const rangeData = this.getRangeData();
let { startRow, startColumn, endRow, endColumn } = moveRangeByOffset(rangeData, x, y);
if (Number.isNaN(startRow)) {
startRow = 0;
@@ -143,7 +144,6 @@ export class BaseReferenceObject extends ObjectClassType {
}
return {
...this.getRangeData(),
startRow,
endRow,
startColumn,
@@ -21,22 +21,20 @@ import { prefixToken } from '../../basics/token';
import { FUNCTION_NAMES_META } from '../../functions/meta/function-names';
import { PrefixNode } from '../ast-node/prefix-node';
const minusRegExp = new RegExp(prefixToken.MINUS, 'g');
const atRegExp = new RegExp(prefixToken.AT, 'g');
// const minusRegExp = new RegExp(prefixToken.MINUS, 'g');
// const atRegExp = new RegExp(prefixToken.AT, 'g');
export function prefixHandler(tokenTrimParam: string, functionService: IFunctionService, runtimeService: IFormulaRuntimeService) {
let minusPrefixNode: Nullable<PrefixNode>;
let atPrefixNode: Nullable<PrefixNode>;
let tokenTrim = tokenTrimParam;
const prefix = tokenTrim.slice(0, 2);
const prefix = tokenTrim[0];
let sliceLength = 0;
if (prefix[0] === prefixToken.MINUS) {
if (prefix === prefixToken.MINUS) {
const functionExecutor = functionService.getExecutor(FUNCTION_NAMES_META.MINUS);
minusPrefixNode = new PrefixNode(runtimeService, prefixToken.MINUS, functionExecutor);
sliceLength++;
}
if (prefix[0] === prefixToken.AT) {
} else if (prefix === prefixToken.AT) {
atPrefixNode = new PrefixNode(runtimeService, prefixToken.AT);
if (minusPrefixNode) {
// minusPrefixNode.addChildren(atPrefixNode);
@@ -258,6 +258,10 @@ export class Transformer extends Disposable implements ITransformerConfig {
}
clearSelectedObjects() {
if (this._selectedObjectMap.size === 0) {
return;
}
this._selectedObjectMap.clear();
this._cancelFocusSubscription?.unsubscribe();
this._cancelFocusSubscription = null;
@@ -38,8 +38,8 @@ export class DataValidationCacheService extends Disposable {
}
private _initDirtyRanges() {
this.disposeWithMe(this._commandService.onCommandExecuted((commandInfo) => {
if (commandInfo.id === SetRangeValuesMutation.id) {
this.disposeWithMe(this._commandService.onCommandExecuted((commandInfo, options) => {
if (commandInfo.id === SetRangeValuesMutation.id && !options?.onlyLocal) {
const { cellValue, unitId, subUnitId } = commandInfo.params as ISetRangeValuesMutationParams;
if (cellValue) {
const range = new ObjectMatrix(cellValue).getDataRange();
@@ -174,7 +174,7 @@ export class FormulaEditorShowController extends Disposable implements IRenderMo
}
private _displayArrayFormulaRangeShape(matrixRange: IObjectMatrixPrimitiveType<IRange>, row: number, col: number, unitId: string, subUnitId: string, worksheet: Worksheet, cellInfo: Nullable<ICellDataForSheetInterceptor>): Nullable<ICellDataForSheetInterceptor> {
const sheetFormulaData = this._formulaDataModel.getSheetFormulaData(unitId, subUnitId);
// const sheetFormulaData = this._formulaDataModel.getSheetFormulaData(unitId, subUnitId);
new ObjectMatrix(matrixRange).forValue((rowIndex, columnIndex, range) => {
if (range == null) {
@@ -188,19 +188,13 @@ export class FormulaEditorShowController extends Disposable implements IRenderMo
if (row >= startRow && row <= endRow && col >= startColumn && col <= endColumn) {
const mainCellValue = worksheet.getCell(startRow, startColumn);
if (mainCellValue?.v === ErrorType.SPILL) {
if (mainCellValue?.v === ErrorType.SPILL || mainCellValue?.f == null) {
return;
}
const formulaDataItem = sheetFormulaData?.[rowIndex]?.[columnIndex];
if (formulaDataItem == null || formulaDataItem.f == null) {
return true;
}
if (cellInfo == null) {
cellInfo = {
f: formulaDataItem.f,
f: mainCellValue.f,
isInArrayFormulaRange: true,
};
}
@@ -449,6 +449,10 @@ export class SheetRenderController extends RxDisposable implements IRenderModule
}
private _markUnitDirty(command: ICommandInfo) {
if (command.id.substring(0, 3) === 'doc') {
return;
}
const { mainComponent: spreadsheet, scene } = this._context;
if (command.id === SetFormulaCalculationNotificationMutation.id) {
@@ -395,7 +395,7 @@ export class EditorBridgeService extends Disposable implements IEditorBridgeServ
ed: body.dataStream.length - 2,
ts: {
cl: {
rgb: this._themeService.getColorFromTheme('gray.600'),
rgb: this._themeService.getColorFromTheme('gray.300'),
},
},
},