Skip to content

Commit

Permalink
feat(sheet): indirect support defined name (#1899)
Browse files Browse the repository at this point in the history
  • Loading branch information
DR-Univer committed Apr 15, 2024
1 parent 761a372 commit 6c0833a
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 3 deletions.
29 changes: 27 additions & 2 deletions packages/engine-formula/src/engine/ast-node/function-node.ts
Expand Up @@ -33,6 +33,7 @@ import type {
import { ArrayValueObject, transformToValueObject, ValueObjectFactory } from '../value-object/array-value-object';
import type { BaseValueObject } from '../value-object/base-value-object';
import { prefixHandler } from '../utils/prefixHandler';
import { IDefinedNamesService } from '../../services/defined-names.service';
import { BaseAstNode, ErrorNode } 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';
Expand All @@ -42,7 +43,8 @@ export class FunctionNode extends BaseAstNode {
token: string,
private _functionExecutor: BaseFunction,
private _currentConfigService: IFormulaCurrentConfigService,
private _runtimeService: IFormulaRuntimeService
private _runtimeService: IFormulaRuntimeService,
private _definedNamesService: IDefinedNamesService
) {
super(token);

Expand Down Expand Up @@ -206,12 +208,34 @@ export class FunctionNode extends BaseAstNode {
});
}
} else {
/**
* In Excel, to inject a defined name into a function that has positioning capabilities,
* such as using the INDIRECT function to reference a named range,
* you can write it as follows:
* =INDIRECT("DefinedName1")
*/
if (this._functionExecutor.isAddress()) {
this._setDefinedNamesForFunction();
}
resultVariant = this._functionExecutor.calculate(...variants);
}

return resultVariant;
}

private _setDefinedNamesForFunction() {
const editorUnitId = this._currentConfigService.getExecuteUnitId();
if (editorUnitId == null) {
return;
}
const definedNames = this._definedNamesService.getDefinedNameMap(editorUnitId);
if (definedNames == null) {
return;
}

this._functionExecutor.setDefinedNames(definedNames);
}

private _setRefInfo() {
const { currentUnitId, currentSubUnitId, currentRow, currentColumn } = this._runtimeService;

Expand Down Expand Up @@ -243,6 +267,7 @@ export class FunctionNodeFactory extends BaseAstNodeFactory {
@IFunctionService private readonly _functionService: IFunctionService,
@IFormulaCurrentConfigService private readonly _currentConfigService: IFormulaCurrentConfigService,
@IFormulaRuntimeService private readonly _runtimeService: IFormulaRuntimeService,
@IDefinedNamesService private readonly _definedNamesService: IDefinedNamesService,
@Inject(Injector) private readonly _injector: Injector
) {
super();
Expand All @@ -259,7 +284,7 @@ export class FunctionNodeFactory extends BaseAstNodeFactory {
return ErrorNode.create(ErrorType.NAME);
}

return new FunctionNode(token, functionExecutor, this._currentConfigService, this._runtimeService);
return new FunctionNode(token, functionExecutor, this._currentConfigService, this._runtimeService, this._definedNamesService);
}

override checkAndCreateNodeType(param: LexerNode | string) {
Expand Down
22 changes: 22 additions & 0 deletions packages/engine-formula/src/functions/base-function.ts
Expand Up @@ -34,12 +34,14 @@ import { CellReferenceObject } from '../engine/reference-object/cell-reference-o
import { RowReferenceObject } from '../engine/reference-object/row-reference-object';
import { ColumnReferenceObject } from '../engine/reference-object/column-reference-object';
import { RangeReferenceObject } from '../engine/reference-object/range-reference-object';
import type { IDefinedNameMapItem } from '../services/defined-names.service';

export class BaseFunction extends Disposable {
private _unitId: Nullable<string>;
private _subUnitId: Nullable<string>;
private _row: number = -1;
private _column: number = -1;
private _definedNames: Nullable<IDefinedNameMapItem>;

/**
* Whether the function needs to expand the parameters
Expand Down Expand Up @@ -75,6 +77,26 @@ export class BaseFunction extends Disposable {
return this._column;
}

/**
* In Excel, to inject a defined name into a function that has positioning capabilities,
* such as using the INDIRECT function to reference a named range,
* you can write it as follows:
* =INDIRECT("DefinedName1")
*/
getDefinedName(name: string) {
const nameMap = this._definedNames;
if (nameMap == null) {
return null;
}
return Array.from(Object.values(nameMap)).filter((value) => {
return value.name === name;
})?.[0];
}

setDefinedNames(definedNames: IDefinedNameMapItem) {
this._definedNames = definedNames;
}

isAsync() {
return false;
}
Expand Down
32 changes: 31 additions & 1 deletion packages/engine-formula/src/functions/lookup/indirect/index.ts
Expand Up @@ -16,6 +16,7 @@

import { ErrorType } from '../../../basics/error-type';
import { REFERENCE_REGEX_COLUMN, REFERENCE_REGEX_ROW, REFERENCE_SINGLE_RANGE_REGEX } from '../../../basics/regex';
import { operatorToken } from '../../../basics/token';
import type { BaseReferenceObject } from '../../../engine/reference-object/base-reference-object';
import { CellReferenceObject } from '../../../engine/reference-object/cell-reference-object';
import { ColumnReferenceObject } from '../../../engine/reference-object/column-reference-object';
Expand All @@ -28,6 +29,10 @@ import { type BaseValueObject, ErrorValueObject } from '../../../engine/value-ob
import { BaseFunction } from '../../base-function';

export class Indirect extends BaseFunction {
override isAddress() {
return true;
}

override calculate(refText: BaseValueObject, a1?: BaseValueObject) {
if (refText == null) {
return ErrorValueObject.create(ErrorType.NA);
Expand Down Expand Up @@ -58,7 +63,7 @@ export class Indirect extends BaseFunction {
return ErrorValueObject.create(ErrorType.REF);
}

const refTextV = refText.getValue() as string;
const refTextV = this._convertToDefinedName(refText.getValue() as string);

if (a1Value === 0) {
const gridRange = deserializeRangeForR1C1(refTextV);
Expand Down Expand Up @@ -104,4 +109,29 @@ export class Indirect extends BaseFunction {
object.setDefaultSheetId(this.subUnitId);
return object;
}

/**
* In Excel, to inject a defined name into a function that has positioning capabilities,
* such as using the INDIRECT function to reference a named range,
* you can write it as follows:
* =INDIRECT("DefinedName1")
*/
private _convertToDefinedName(refText: string) {
const definedName = this.getDefinedName(refText);
if (definedName == null) {
return refText;
}

const formulaOrRefString = definedName.formulaOrRefString;

if (formulaOrRefString == null) {
return refText;
}

if (formulaOrRefString.startsWith(operatorToken.EQUALS)) {
return formulaOrRefString.slice(1);
}

return formulaOrRefString;
}
}

0 comments on commit 6c0833a

Please sign in to comment.