mirror of
https://github.com/ruilisi/fortune-sheet.git
synced 2025-01-07 03:16:50 +08:00
removed: FormulaDependenciesMap, fixed: undo-redo bugs with formula cache
This commit is contained in:
parent
02df819ca1
commit
20f3b66619
@ -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,
|
||||
|
@ -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) => {
|
||||
|
@ -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,
|
||||
|
@ -322,10 +322,6 @@ export type FormulaDependency = {
|
||||
sheetId: string | undefined;
|
||||
};
|
||||
|
||||
export type FormulaDependenciesMap = {
|
||||
[formula: string]: FormulaDependency[];
|
||||
};
|
||||
|
||||
type AncestorFormulaCell = {
|
||||
[rxcxix: string]: number;
|
||||
};
|
||||
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user