Skip to content

Commit

Permalink
fix(formula): offset formula move range,column,row
Browse files Browse the repository at this point in the history
  • Loading branch information
Dushusir committed Apr 2, 2024
1 parent 1cb52d4 commit cb1bf92
Show file tree
Hide file tree
Showing 8 changed files with 380 additions and 204 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,23 @@
*/

import type { IMutation } from '@univerjs/core';
import { CommandType, Tools } from '@univerjs/core';
import { CommandType } from '@univerjs/core';
import type { IAccessor } from '@wendellhu/redi';

import type { IArrayFormulaRangeType, IArrayFormulaUnitCellType } from '../../basics/common';
import { FormulaDataModel } from '../../models/formula-data.model';

export interface ISetArrayFormulaDataMutationParams {
arrayFormulaRange: IArrayFormulaRangeType;
arrayFormulaCellData: IArrayFormulaUnitCellType;
}

export const SetArrayFormulaDataUndoMutationFactory = (accessor: IAccessor): ISetArrayFormulaDataMutationParams => {
const formulaDataModel = accessor.get(FormulaDataModel);
const arrayFormulaRange = Tools.deepClone(formulaDataModel.getArrayFormulaRange());
const arrayFormulaCellData = Tools.deepClone(formulaDataModel.getArrayFormulaCellData());
return {
arrayFormulaRange,
arrayFormulaCellData,
};
};

/**
* There is no need to process data here, it is used as the main thread to send data to the worker. The main thread has already updated the data in advance, and there is no need to update it again here.
*/
export const SetArrayFormulaDataMutation: IMutation<ISetArrayFormulaDataMutationParams> = {
id: 'formula.mutation.set-array-formula-data',
type: CommandType.MUTATION,
handler: (accessor: IAccessor, params: ISetArrayFormulaDataMutationParams) => {
const formulaDataModel = accessor.get(FormulaDataModel);
formulaDataModel.setArrayFormulaRange(params.arrayFormulaRange);
formulaDataModel.setArrayFormulaCellData(params.arrayFormulaCellData);
return true;
},
};
3 changes: 1 addition & 2 deletions packages/engine-formula/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export { RegisterFunctionMutation } from './commands/mutations/register-function
export {
type ISetArrayFormulaDataMutationParams,
SetArrayFormulaDataMutation,
SetArrayFormulaDataUndoMutationFactory,
} from './commands/mutations/set-array-formula-data.mutation';
export { RemoveDefinedNameMutation, SetDefinedNameMutation } from './commands/mutations/set-defined-name.mutation';
export {
Expand Down Expand Up @@ -145,4 +144,4 @@ export { IFormulaCurrentConfigService, FormulaCurrentConfigService } from './ser

export { IActiveDirtyManagerService } from './services/active-dirty-manager.service';

export type { IExchangePosition } from './models/formula-data.model';
export type { IRangeChange } from './models/formula-data.model';
153 changes: 54 additions & 99 deletions packages/engine-formula/src/models/formula-data.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,12 @@ import type {
IUnitSheetNameMap,
} from '../basics/common';
import { LexerTreeBuilder } from '../engine/analysis/lexer-tree-builder';
import type { IFormulaIdMap } from './utils/formula-data-util';
import { clearArrayFormulaCellDataByCell, updateFormulaDataByCellValue } from './utils/formula-data-util';

export interface IFormulaIdMap {
f: string;
r: number;
c: number;
}

export interface IExchangePosition {
[key: string]: IRange;
export interface IRangeChange {
oldCell: IRange;
newCell: IRange;
}

export class FormulaDataModel extends Disposable {
Expand Down Expand Up @@ -351,7 +348,7 @@ export class FormulaDataModel extends Disposable {
};
}

updateFormulaData(unitId: string, sheetId: string, cellValue: IObjectMatrixPrimitiveType<Nullable<ICellData>>, exchangePosition?: IExchangePosition) {
updateFormulaData(unitId: string, sheetId: string, cellValue: IObjectMatrixPrimitiveType<Nullable<ICellData>>, rangeList?: IRangeChange[], isReverse?: boolean) {
const cellMatrix = new ObjectMatrix(cellValue);

const formulaIdMap = this.getFormulaIdMap(unitId, sheetId); // Connect the formula and ID
Expand All @@ -370,67 +367,25 @@ export class FormulaDataModel extends Disposable {
const sheetFormulaDataMatrix = new ObjectMatrix<IFormulaDataItem>(workbookFormulaData[sheetId]);
const newSheetFormulaDataMatrix = new ObjectMatrix<IFormulaDataItem | null>();

cellMatrix.forValue((row, column, cell) => {
let r = row;
let c = column;

// Exchange the position of the cell
if (exchangePosition) {
const key = `${row}_${column}`;
if (exchangePosition[key]) {
const { startRow, startColumn } = exchangePosition[key];
r = startRow;
c = startColumn;
}
if (rangeList) {
if (isReverse) {
rangeList.reverse();
}

const formulaString = cell?.f || '';
const formulaId = cell?.si || '';
rangeList.forEach(({ oldCell, newCell }) => {
const { startRow: r, startColumn: c } = oldCell;
const { startRow: newStartRow, startColumn: newStartColumn } = newCell;

const checkFormulaString = isFormulaString(formulaString);
const checkFormulaId = isFormulaId(formulaId);
const cell = cellMatrix.getValue(newStartRow, newStartColumn);

if (checkFormulaString && checkFormulaId) {
sheetFormulaDataMatrix.setValue(r, c, {
f: formulaString,
si: formulaId,
});

formulaIdMap.set(formulaId, { f: formulaString, r, c });

newSheetFormulaDataMatrix.setValue(r, c, {
f: formulaString,
si: formulaId,
});
} else if (checkFormulaString && !checkFormulaId) {
sheetFormulaDataMatrix.setValue(r, c, {
f: formulaString,
});
newSheetFormulaDataMatrix.setValue(r, c, {
f: formulaString,
});
} else if (!checkFormulaString && checkFormulaId) {
sheetFormulaDataMatrix.setValue(r, c, {
f: '',
si: formulaId,
});
}
// When cell is null or cell.f cell.si is null, delete formulaDataItem
else if (((cell?.f === null && cell?.si === null) || cell === null) && sheetFormulaDataMatrix.getValue(r, c)) {
const currentFormulaInfo = sheetFormulaDataMatrix.getValue(r, c);
const f = currentFormulaInfo?.f || '';
const si = currentFormulaInfo?.si || '';

// The id that needs to be offset
// When the cell containing the formulas f and si is deleted, f and si lose their association, and f needs to be moved to the next cell containing the same si.
if (isFormulaString(f) && isFormulaId(si)) {
deleteFormulaIdMap.set(si, f);
}

sheetFormulaDataMatrix.realDeleteValue(r, c);
newSheetFormulaDataMatrix.setValue(r, c, null);
}
});
updateFormulaDataByCellValue(sheetFormulaDataMatrix, newSheetFormulaDataMatrix, formulaIdMap, deleteFormulaIdMap, r, c, cell);
updateFormulaDataByCellValue(sheetFormulaDataMatrix, newSheetFormulaDataMatrix, formulaIdMap, deleteFormulaIdMap, newStartRow, newStartColumn, null);
});
} else {
cellMatrix.forValue((r, c, cell) => {
updateFormulaDataByCellValue(sheetFormulaDataMatrix, newSheetFormulaDataMatrix, formulaIdMap, deleteFormulaIdMap, r, c, cell);
});
}

// Convert the formula ID to formula string
sheetFormulaDataMatrix.forValue((r, c, cell) => {
Expand Down Expand Up @@ -487,7 +442,8 @@ export class FormulaDataModel extends Disposable {
updateArrayFormulaRange(
unitId: string,
sheetId: string,
cellValue: IObjectMatrixPrimitiveType<Nullable<ICellData>>
cellValue: IObjectMatrixPrimitiveType<Nullable<ICellData>>,
rangeList?: IRangeChange[], isReverse?: boolean
) {
// remove the array formula range when cell value is null

Expand All @@ -496,30 +452,32 @@ export class FormulaDataModel extends Disposable {
if (!arrayFormulaRange) return;

const arrayFormulaRangeMatrix = new ObjectMatrix(arrayFormulaRange);

const cellMatrix = new ObjectMatrix(cellValue);
cellMatrix.forValue((r, c, cell) => {
const arrayFormulaRangeValue = arrayFormulaRangeMatrix?.getValue(r, c);
if (arrayFormulaRangeValue == null) {
return true;
}

const formulaString = cell?.f || '';
const formulaId = cell?.si || '';
if (rangeList) {
if (isReverse) {
rangeList.reverse();
}

const checkFormulaString = isFormulaString(formulaString);
const checkFormulaId = isFormulaId(formulaId);
rangeList.forEach(({ oldCell, newCell }) => {
const { startRow: r, startColumn: c } = oldCell;
const { startRow: newStartRow, startColumn: newStartColumn } = newCell;

if (!checkFormulaString && !checkFormulaId) {
arrayFormulaRangeMatrix.realDeleteValue(r, c);
}
});
arrayFormulaRangeMatrix.realDeleteValue(newStartRow, newStartColumn);
});
} else {
cellMatrix.forValue((r, c, cell) => {
arrayFormulaRangeMatrix.realDeleteValue(r, c);
});
}
}

updateArrayFormulaCellData(
unitId: string,
sheetId: string,
cellValue: IObjectMatrixPrimitiveType<Nullable<ICellData>>
cellValue: IObjectMatrixPrimitiveType<Nullable<ICellData>>,
rangeList?: IRangeChange[], isReverse?: boolean
) {
// remove the array formula range when cell value is null

Expand All @@ -536,27 +494,24 @@ export class FormulaDataModel extends Disposable {
const arrayFormulaCellDataMatrix = new ObjectMatrix(arrayFormulaCellData);

const cellMatrix = new ObjectMatrix(cellValue);
cellMatrix.forValue((r, c, cell) => {
const arrayFormulaRangeValue = arrayFormulaRangeMatrix?.getValue(r, c);
if (arrayFormulaRangeValue == null) {
return true;

if (rangeList) {
if (isReverse) {
rangeList.reverse();
}

const formulaString = cell?.f || '';
const formulaId = cell?.si || '';
rangeList.forEach(({ oldCell, newCell }) => {
const { startRow: r, startColumn: c } = oldCell;
const { startRow: newStartRow, startColumn: newStartColumn } = newCell;

const checkFormulaString = isFormulaString(formulaString);
const checkFormulaId = isFormulaId(formulaId);

if (!checkFormulaString && !checkFormulaId) {
const { startRow, startColumn, endRow, endColumn } = arrayFormulaRangeValue;
for (let r = startRow; r <= endRow; r++) {
for (let c = startColumn; c <= endColumn; c++) {
arrayFormulaCellDataMatrix.realDeleteValue(r, c);
}
}
}
});
clearArrayFormulaCellDataByCell(arrayFormulaRangeMatrix, arrayFormulaCellDataMatrix, r, c);
clearArrayFormulaCellDataByCell(arrayFormulaRangeMatrix, arrayFormulaCellDataMatrix, newStartRow, newStartColumn);
});
} else {
cellMatrix.forValue((r, c, cell) => {
clearArrayFormulaCellDataByCell(arrayFormulaRangeMatrix, arrayFormulaCellDataMatrix, r, c);
});
}
}

updateNumfmtData(
Expand Down
86 changes: 86 additions & 0 deletions packages/engine-formula/src/models/utils/formula-data-util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import type { ICellData, IRange, Nullable, ObjectMatrix } from '@univerjs/core';
import { isFormulaId, isFormulaString } from '@univerjs/core';
import type { IFormulaDataItem } from '../../basics/common';

export interface IFormulaIdMap {
f: string;
r: number;
c: number;
}

export function updateFormulaDataByCellValue(sheetFormulaDataMatrix: ObjectMatrix<IFormulaDataItem>, newSheetFormulaDataMatrix: ObjectMatrix<IFormulaDataItem | null>, formulaIdMap: Map<string, IFormulaIdMap>, deleteFormulaIdMap: Map<string, string | IFormulaIdMap>, r: number, c: number, cell: Nullable<ICellData>) {
const formulaString = cell?.f || '';
const formulaId = cell?.si || '';

const checkFormulaString = isFormulaString(formulaString);
const checkFormulaId = isFormulaId(formulaId);

if (checkFormulaString && checkFormulaId) {
sheetFormulaDataMatrix.setValue(r, c, {
f: formulaString,
si: formulaId,
});

formulaIdMap.set(formulaId, { f: formulaString, r, c });

newSheetFormulaDataMatrix.setValue(r, c, {
f: formulaString,
si: formulaId,
});
} else if (checkFormulaString && !checkFormulaId) {
sheetFormulaDataMatrix.setValue(r, c, {
f: formulaString,
});
newSheetFormulaDataMatrix.setValue(r, c, {
f: formulaString,
});
} else if (!checkFormulaString && checkFormulaId) {
sheetFormulaDataMatrix.setValue(r, c, {
f: '',
si: formulaId,
});
} else if (!checkFormulaString && !checkFormulaId && sheetFormulaDataMatrix.getValue(r, c)) {
const currentFormulaInfo = sheetFormulaDataMatrix.getValue(r, c);
const f = currentFormulaInfo?.f || '';
const si = currentFormulaInfo?.si || '';

// The id that needs to be offset
// When the cell containing the formulas f and si is deleted, f and si lose their association, and f needs to be moved to the next cell containing the same si.
if (isFormulaString(f) && isFormulaId(si)) {
deleteFormulaIdMap.set(si, f);
}

sheetFormulaDataMatrix.realDeleteValue(r, c);
newSheetFormulaDataMatrix.setValue(r, c, null);
}
}

export function clearArrayFormulaCellDataByCell(arrayFormulaRangeMatrix: ObjectMatrix<IRange>, arrayFormulaCellDataMatrix: ObjectMatrix<Nullable<ICellData>>, r: number, c: number) {
const arrayFormulaRangeValue = arrayFormulaRangeMatrix?.getValue(r, c);
if (arrayFormulaRangeValue == null) {
return true;
}

const { startRow, startColumn, endRow, endColumn } = arrayFormulaRangeValue;
for (let r = startRow; r <= endRow; r++) {
for (let c = startColumn; c <= endColumn; c++) {
arrayFormulaCellDataMatrix.realDeleteValue(r, c);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -253,21 +253,21 @@ export class UpdateFormulaController extends Disposable {

private _handleSetRangeValuesMutation(params: ISetRangeValuesMutationParams) {
const { subUnitId: sheetId, unitId, cellValue, options } = params;
const { exchangePosition } = options || {};
const { rangeList, isReverse } = options || {};

if (cellValue == null) {
return;
}

const newSheetFormulaData = this._formulaDataModel.updateFormulaData(unitId, sheetId, cellValue, exchangePosition);
const newSheetFormulaData = this._formulaDataModel.updateFormulaData(unitId, sheetId, cellValue, rangeList, isReverse);
const newFormulaData = {
[unitId]: {
[sheetId]: newSheetFormulaData,
},
};

this._formulaDataModel.updateArrayFormulaCellData(unitId, sheetId, cellValue);
this._formulaDataModel.updateArrayFormulaRange(unitId, sheetId, cellValue);
this._formulaDataModel.updateArrayFormulaCellData(unitId, sheetId, cellValue, rangeList, isReverse);
this._formulaDataModel.updateArrayFormulaRange(unitId, sheetId, cellValue, rangeList, isReverse);
this._formulaDataModel.updateNumfmtData(unitId, sheetId, cellValue); // TODO: move model to snapshot

this._commandService.executeCommand(
Expand Down Expand Up @@ -413,7 +413,7 @@ export class UpdateFormulaController extends Disposable {
result
);

const { redoFormulaData, undoFormulaData, exchangePosition } = refRangeFormula(oldFormulaData, newFormulaData, result);
const { redoFormulaData, undoFormulaData, rangeList, isReverse } = refRangeFormula(oldFormulaData, newFormulaData, result);

// console.info('redoFormulaData==', redoFormulaData);
// console.info('undoFormulaData==', undoFormulaData);
Expand All @@ -435,7 +435,8 @@ export class UpdateFormulaController extends Disposable {
unitId,
cellValue: undoFormulaData,
options: {
exchangePosition,
rangeList,
isReverse,
},
};

Expand Down
Loading

0 comments on commit cb1bf92

Please sign in to comment.