removed: FormulaDependenciesMap, fixed: undo-redo bugs with formula cache

This commit is contained in:
Shashank Agarwal 2024-12-16 11:43:01 +00:00
parent 02df819ca1
commit 20f3b66619
5 changed files with 161 additions and 154 deletions

View File

@ -19,6 +19,7 @@ import {
functionHTMLGenerate,
getcellrange,
iscelldata,
isFormula,
} from "./formula";
import {
attrToCssName,
@ -678,10 +679,6 @@ export function cancelNormalSelected(ctx: Context) {
ctx.formulaCache.rangedrag_row_start = false;
}
function isFormula(value: any) {
return _.isString(value) && value.slice(0, 1) === "=" && value.length > 1;
}
// formula.updatecell
export function updateCell(
ctx: Context,

View File

@ -5,7 +5,6 @@ import type {
Cell,
CellMatrix,
FormulaDependency,
FormulaDependenciesMap,
FormulaCell,
FormulaCellInfoMap,
History,
@ -59,6 +58,10 @@ const LABEL_EXTRACT_REGEXP = new RegExp(
`^${rowColumnWithSheetName}(?:[:]${rowColumnWithSheetName})?$`
);
export function isFormula(value: any) {
return _.isString(value) && value.slice(0, 1) === "=" && value.length > 1;
}
// FormulaCache is defined as class to avoid being frozen by immer
export class FormulaCache {
parser: any;
@ -107,9 +110,6 @@ export class FormulaCache {
execFunctionGlobalData: any;
// useful in cut-paste operation where several cells may be affected but the formulas remains the same
formulaDependenciesMap: FormulaDependenciesMap;
formulaCellInfoMap: FormulaCellInfoMap | null;
constructor() {
@ -118,7 +118,6 @@ export class FormulaCache {
this.selectingRangeIndex = -1;
this.functionlistMap = {};
this.execFunctionGlobalData = {};
this.formulaDependenciesMap = {};
this.formulaCellInfoMap = null;
this.cellTextToIndexList = {};
this.parser = new Parser();
@ -197,7 +196,12 @@ export class FormulaCache {
return cell?.v;
}
updateFormulaCache(ctx: Context, history: History, data?: CellMatrix) {
updateFormulaCache(
ctx: Context,
history: History,
type: "undo" | "redo",
data?: CellMatrix
) {
function requestUpdate(value: any) {
if (value instanceof Object) {
if (!_.isNil(value.r) && !_.isNil(value.c)) {
@ -213,8 +217,14 @@ export class FormulaCache {
}
}
}
history.patches.forEach((patch) => {
if (patch.path[5] === "f") {
const changesHistory =
type === "undo" ? history.inversePatches : history.patches;
changesHistory.forEach((patch) => {
if (
isFormula(patch.value?.f) ||
patch.value === null ||
patch.path[5] === "f"
) {
requestUpdate({ r: patch.path[3], c: patch.path[4] });
} else if (Array.isArray(patch.value)) {
patch.value.forEach((value) => {

View File

@ -5,6 +5,7 @@ import {
execfunction,
FormulaCell,
FormulaCellInfo,
FormulaDependency,
getcellFormula,
getcellrange,
iscelldata,
@ -18,7 +19,7 @@ export function setFormulaCellInfo(
data?: CellMatrix
) {
const key = `r${formulaCell.r}c${formulaCell.c}i${formulaCell.id}`;
const calc_funcStr = getcellFormula(
const calc_funcStr: string | undefined = getcellFormula(
ctx,
formulaCell.r,
formulaCell.c,
@ -35,150 +36,145 @@ export function setFormulaCellInfo(
txt1.indexOf("OFFSET(") > -1 ||
txt1.indexOf("INDEX(") > -1;
const formulaDependency =
ctx.formulaCache.formulaDependenciesMap[calc_funcStr] || [];
if (formulaDependency.length === 0) {
if (isOffsetFunc) {
isFunctionRange(
ctx,
calc_funcStr,
null,
null,
formulaCell.id,
null,
(str_nb: string) => {
const range = getcellrange(ctx, _.trim(str_nb), formulaCell.id, data);
if (!_.isNil(range)) {
formulaDependency.push(range);
}
const formulaDependency: FormulaDependency[] = [];
if (isOffsetFunc) {
isFunctionRange(
ctx,
calc_funcStr,
null,
null,
formulaCell.id,
null,
(str_nb: string) => {
const range = getcellrange(ctx, _.trim(str_nb), formulaCell.id, data);
if (!_.isNil(range)) {
formulaDependency.push(range);
}
);
} else if (
!(
calc_funcStr.substring(0, 2) === '="' &&
calc_funcStr.substring(calc_funcStr.length - 1, 1) === '"'
)
) {
// let formulaTextArray = calc_funcStr.split(/==|!=|<>|<=|>=|[,()=+-\/*%&^><]/g);//无法正确分割单引号或双引号之间有==、!=、-等运算符的情况。导致如='1-2'!A1公式中表名1-2的A1单元格内容更新后公式的值不更新的bug
// 解决='1-2'!A1+5会被calc_funcStr.split(/==|!=|<>|<=|>=|[,()=+-\/*%&^><]/g)分割成["","'1","2'!A1",5]的错误情况
let point = 0; // pointer
let squote = -1; // single quote
let dquote = -1; // double quotes
const formulaTextArray = [];
const sq_end_array = []; // Saves the paired single quotes in the index of formulaTextArray.
const calc_funcStr_length = calc_funcStr.length;
for (let j = 0; j < calc_funcStr_length; j += 1) {
const char = calc_funcStr.charAt(j);
if (char === "'" && dquote === -1) {
// If it starts with a single quote
if (squote === -1) {
if (point !== j) {
formulaTextArray.push(
...calc_funcStr
.substring(point, j)
.split(/==|!=|<>|<=|>=|[,()=+-/*%&^><]/)
);
}
squote = j;
point = j;
} // end single quote
else {
// if (squote === i - 1)//配对的单引号后第一个字符不能是单引号
// {
// ;//到此处说明公式错误
// }
// 如果是''代表着输出'
if (
j < calc_funcStr_length - 1 &&
calc_funcStr.charAt(j + 1) === "'"
) {
j += 1;
} else {
// If the next character is not ', it means the end of a single quote
// if (calc_funcStr.charAt(i - 1) === "'") {//The last character after the paired single quote cannot be a single quote
// ;//Go here to explain the formula error
point = j + 1;
formulaTextArray.push(calc_funcStr.substring(squote, point));
sq_end_array.push(formulaTextArray.length - 1);
squote = -1;
// } else {
// point = i + 1;
// formulaTextArray.push(calc_funcStr.substring(squote, point));
// sq_end_array.push(formulaTextArray.length - 1);
// squote = -1;
// }
}
}
);
} else if (
!(
calc_funcStr.substring(0, 2) === '="' &&
calc_funcStr.substring(calc_funcStr.length - 1, 1) === '"'
)
) {
// let formulaTextArray = calc_funcStr.split(/==|!=|<>|<=|>=|[,()=+-\/*%&^><]/g);//无法正确分割单引号或双引号之间有==、!=、-等运算符的情况。导致如='1-2'!A1公式中表名1-2的A1单元格内容更新后公式的值不更新的bug
// 解决='1-2'!A1+5会被calc_funcStr.split(/==|!=|<>|<=|>=|[,()=+-\/*%&^><]/g)分割成["","'1","2'!A1",5]的错误情况
let point = 0; // pointer
let squote = -1; // single quote
let dquote = -1; // double quotes
const formulaTextArray = [];
const sq_end_array = []; // Saves the paired single quotes in the index of formulaTextArray.
const calc_funcStr_length = calc_funcStr.length;
for (let j = 0; j < calc_funcStr_length; j += 1) {
const char = calc_funcStr.charAt(j);
if (char === "'" && dquote === -1) {
// If it starts with a single quote
if (squote === -1) {
if (point !== j) {
formulaTextArray.push(
...calc_funcStr
.substring(point, j)
.split(/==|!=|<>|<=|>=|[,()=+-/*%&^><]/)
);
}
} else if (char === '"' && squote === -1) {
// If it starts with double quotes
if (dquote === -1) {
if (point !== j) {
formulaTextArray.push(
...calc_funcStr
.substring(point, j)
.split(/==|!=|<>|<=|>=|[,()=+-/*%&^><]/)
);
}
dquote = j;
point = j;
squote = j;
point = j;
} // end single quote
else {
// if (squote === i - 1)//配对的单引号后第一个字符不能是单引号
// {
// ;//到此处说明公式错误
// }
// 如果是''代表着输出'
if (
j < calc_funcStr_length - 1 &&
calc_funcStr.charAt(j + 1) === "'"
) {
j += 1;
} else {
// If "" represents output"
if (
j < calc_funcStr_length - 1 &&
calc_funcStr.charAt(j + 1) === '"'
) {
j += 1;
} else {
// end with double quotes
point = j + 1;
formulaTextArray.push(calc_funcStr.substring(dquote, point));
dquote = -1;
}
// If the next character is not ', it means the end of a single quote
// if (calc_funcStr.charAt(i - 1) === "'") {//The last character after the paired single quote cannot be a single quote
// ;//Go here to explain the formula error
point = j + 1;
formulaTextArray.push(calc_funcStr.substring(squote, point));
sq_end_array.push(formulaTextArray.length - 1);
squote = -1;
// } else {
// point = i + 1;
// formulaTextArray.push(calc_funcStr.substring(squote, point));
// sq_end_array.push(formulaTextArray.length - 1);
// squote = -1;
// }
}
}
}
if (point !== calc_funcStr_length) {
formulaTextArray.push(
...calc_funcStr
.substring(point, calc_funcStr_length)
.split(/==|!=|<>|<=|>=|[,()=+-/*%&^><]/)
);
}
// 拼接所有配对单引号及之后一个单元格内容,例如["'1-2'","!A1"]拼接为["'1-2'!A1"]
for (let j = sq_end_array.length - 1; j >= 0; j -= 1) {
if (sq_end_array[j] !== formulaTextArray.length - 1) {
formulaTextArray[sq_end_array[j]] +=
formulaTextArray[sq_end_array[j] + 1];
formulaTextArray.splice(sq_end_array[j] + 1, 1);
} else if (char === '"' && squote === -1) {
// If it starts with double quotes
if (dquote === -1) {
if (point !== j) {
formulaTextArray.push(
...calc_funcStr
.substring(point, j)
.split(/==|!=|<>|<=|>=|[,()=+-/*%&^><]/)
);
}
dquote = j;
point = j;
} else {
// If "" represents output"
if (
j < calc_funcStr_length - 1 &&
calc_funcStr.charAt(j + 1) === '"'
) {
j += 1;
} else {
// end with double quotes
point = j + 1;
formulaTextArray.push(calc_funcStr.substring(dquote, point));
dquote = -1;
}
}
}
// 至此=SUM('1-2'!A1:A2&"'1-2'!A2")由原来的["","SUM","'1","2'!A1:A2","",""'1","2'!A2""]更正为["","SUM","","'1-2'!A1:A2","","",""'1-2'!A2""]
for (let j = 0; j < formulaTextArray.length; j += 1) {
const t = formulaTextArray[j];
if (t.length <= 1) {
continue;
}
if (
(t.substring(0, 1) === '"' && t.substring(t.length - 1, 1) === '"') ||
!iscelldata(t)
) {
continue;
}
const range = getcellrange(ctx, _.trim(t), formulaCell.id, data);
if (_.isNil(range)) {
continue;
}
formulaDependency.push(range);
}
}
if (point !== calc_funcStr_length) {
formulaTextArray.push(
...calc_funcStr
.substring(point, calc_funcStr_length)
.split(/==|!=|<>|<=|>=|[,()=+-/*%&^><]/)
);
}
// 拼接所有配对单引号及之后一个单元格内容,例如["'1-2'","!A1"]拼接为["'1-2'!A1"]
for (let j = sq_end_array.length - 1; j >= 0; j -= 1) {
if (sq_end_array[j] !== formulaTextArray.length - 1) {
formulaTextArray[sq_end_array[j]] +=
formulaTextArray[sq_end_array[j] + 1];
formulaTextArray.splice(sq_end_array[j] + 1, 1);
}
}
// 至此=SUM('1-2'!A1:A2&"'1-2'!A2")由原来的["","SUM","'1","2'!A1:A2","",""'1","2'!A2""]更正为["","SUM","","'1-2'!A1:A2","","",""'1-2'!A2""]
for (let j = 0; j < formulaTextArray.length; j += 1) {
const t = formulaTextArray[j];
if (t.length <= 1) {
continue;
}
if (
(t.substring(0, 1) === '"' && t.substring(t.length - 1, 1) === '"') ||
!iscelldata(t)
) {
continue;
}
const range = getcellrange(ctx, _.trim(t), formulaCell.id, data);
if (_.isNil(range)) {
continue;
}
formulaDependency.push(range);
}
}
if (!ctx.formulaCache.formulaDependenciesMap[calc_funcStr])
ctx.formulaCache.formulaDependenciesMap[calc_funcStr] = formulaDependency;
const item: FormulaCellInfo = {
formulaDependency,

View File

@ -322,10 +322,6 @@ export type FormulaDependency = {
sheetId: string | undefined;
};
export type FormulaDependenciesMap = {
[formula: string]: FormulaDependency[];
};
type AncestorFormulaCell = {
[rxcxix: string]: number;
};

View File

@ -370,7 +370,11 @@ const Workbook = React.forwardRef<WorkbookInstance, Settings & AdditionalProps>(
delete inversedOptions!.addSheet!.value!.data;
}
emitOp(newContext, history.inversePatches, inversedOptions, true);
newContext.formulaCache.updateFormulaCache(newContext, history);
newContext.formulaCache.updateFormulaCache(
newContext,
history,
"undo"
);
return newContext;
});
}
@ -383,7 +387,11 @@ const Workbook = React.forwardRef<WorkbookInstance, Settings & AdditionalProps>(
const newContext = applyPatches(ctx_, history.patches);
globalCache.current.undoList.push(history);
emitOp(newContext, history.patches, history.options);
newContext.formulaCache.updateFormulaCache(newContext, history);
newContext.formulaCache.updateFormulaCache(
newContext,
history,
"redo"
);
return newContext;
});
}