Skip to content

Commit 87ede30

Browse files
committed
Merge branch 'develop' of github.com:handsontable/hyperformula into feature/issue-1116
2 parents 7840290 + 8de58f9 commit 87ede30

File tree

7 files changed

+114
-15
lines changed

7 files changed

+114
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1010
### Fixed
1111

1212
- Fixed an issue where cells were not recalculated after adding new sheet. [#1116](https://github.com/handsontable/hyperformula/issues/1116)
13+
- Fixed an issue where overwriting a non-computed cell caused the `Value of the formula cell is not computed` error. [#1194](https://github.com/handsontable/hyperformula/issues/1194)
1314

1415
## [3.1.0] - 2025-10-14
1516

src/Operations.ts

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ import {
2222
ParsingErrorVertex,
2323
SheetMapping,
2424
SparseStrategy,
25-
ValueCellVertex
25+
ValueCellVertex,
2626
} from './DependencyGraph'
2727
import { FormulaVertex } from './DependencyGraph/FormulaCellVertex'
28-
import { RawAndParsedValue } from './DependencyGraph/ValueCellVertex'
28+
import { RawAndParsedValue, ValueCellVertexValue } from './DependencyGraph/ValueCellVertex'
2929
import { AddColumnsTransformer } from './dependencyTransformers/AddColumnsTransformer'
3030
import { AddRowsTransformer } from './dependencyTransformers/AddRowsTransformer'
3131
import { CleanOutOfScopeDependenciesTransformer } from './dependencyTransformers/CleanOutOfScopeDependenciesTransformer'
@@ -480,8 +480,6 @@ export class Operations {
480480

481481
/**
482482
* Restores a single cell.
483-
* @param {SimpleCellAddress} address
484-
* @param {ClipboardCell} clipboardCell
485483
*/
486484
public restoreCell(address: SimpleCellAddress, clipboardCell: ClipboardCell): void {
487485
switch (clipboardCell.type) {
@@ -591,7 +589,6 @@ export class Operations {
591589

592590
this.setFormulaToCell(address, size, parserResult)
593591
} catch (error) {
594-
595592
if (!(error as Error).message) {
596593
throw error
597594
}
@@ -619,45 +616,66 @@ export class Operations {
619616
}
620617
}
621618

619+
/**
620+
* Sets cell content to an instance of parsing error.
621+
* Creates a ParsingErrorVertex and updates the dependency graph and column search index.
622+
*/
622623
public setParsingErrorToCell(rawInput: string, errors: ParsingError[], address: SimpleCellAddress) {
623-
const oldValue = this.dependencyGraph.getCellValue(address)
624+
this.removeCellValueFromColumnSearch(address)
625+
624626
const vertex = new ParsingErrorVertex(errors, rawInput)
625627
const arrayChanges = this.dependencyGraph.setParsingErrorToCell(address, vertex)
626-
this.columnSearch.remove(getRawValue(oldValue), address)
628+
627629
this.columnSearch.applyChanges(arrayChanges.getChanges())
628630
this.changes.addAll(arrayChanges)
629631
this.changes.addChange(vertex.getCellValue(), address)
630632
}
631633

634+
/**
635+
* Sets cell content to a formula.
636+
* Creates a FormulaCellVertex and updates the dependency graph and column search index.
637+
*/
632638
public setFormulaToCell(address: SimpleCellAddress, size: ArraySize, {
633639
ast,
634640
hasVolatileFunction,
635641
hasStructuralChangeFunction,
636642
dependencies
637643
}: ParsingResult) {
638-
const oldValue = this.dependencyGraph.getCellValue(address)
644+
this.removeCellValueFromColumnSearch(address)
645+
639646
const arrayChanges = this.dependencyGraph.setFormulaToCell(address, ast, absolutizeDependencies(dependencies, address), size, hasVolatileFunction, hasStructuralChangeFunction)
640-
this.columnSearch.remove(getRawValue(oldValue), address)
647+
641648
this.columnSearch.applyChanges(arrayChanges.getChanges())
642649
this.changes.addAll(arrayChanges)
643650
}
644651

652+
/**
653+
* Sets cell content to a value.
654+
* Creates a ValueCellVertex and updates the dependency graph and column search index.
655+
*/
645656
public setValueToCell(value: RawAndParsedValue, address: SimpleCellAddress) {
646-
const oldValue = this.dependencyGraph.getCellValue(address)
657+
this.changeCellValueInColumnSearch(address, value.parsedValue)
658+
647659
const arrayChanges = this.dependencyGraph.setValueToCell(address, value)
648-
this.columnSearch.change(getRawValue(oldValue), getRawValue(value.parsedValue), address)
660+
649661
this.columnSearch.applyChanges(arrayChanges.getChanges().filter(change => !equalSimpleCellAddress(change.address, address)))
650662
this.changes.addAll(arrayChanges)
651663
this.changes.addChange(value.parsedValue, address)
652664
}
653665

666+
/**
667+
* Sets cell content to an empty value.
668+
* Creates an EmptyCellVertex and updates the dependency graph and column search index.
669+
*/
654670
public setCellEmpty(address: SimpleCellAddress) {
655671
if (this.dependencyGraph.isArrayInternalCell(address)) {
656672
return
657673
}
658-
const oldValue = this.dependencyGraph.getCellValue(address)
674+
675+
this.removeCellValueFromColumnSearch(address)
676+
659677
const arrayChanges = this.dependencyGraph.setCellEmpty(address)
660-
this.columnSearch.remove(getRawValue(oldValue), address)
678+
661679
this.columnSearch.applyChanges(arrayChanges.getChanges())
662680
this.changes.addAll(arrayChanges)
663681
this.changes.addChange(EmptyValue, address)
@@ -944,6 +962,45 @@ export class Operations {
944962
}
945963
return this.dependencyGraph.fetchCellOrCreateEmpty(expression.address).vertex
946964
}
965+
966+
/**
967+
* Removes a cell value from the columnSearch index.
968+
* Ignores the non-computed formula vertices.
969+
*/
970+
private removeCellValueFromColumnSearch(address: SimpleCellAddress): void {
971+
if (this.isNotComputed(address)) {
972+
return
973+
}
974+
975+
const oldValue = this.dependencyGraph.getCellValue(address)
976+
this.columnSearch.remove(getRawValue(oldValue), address)
977+
}
978+
979+
/**
980+
* Changes a cell value in the columnSearch index.
981+
* Ignores the non-computed formula vertices.
982+
*/
983+
private changeCellValueInColumnSearch(address: SimpleCellAddress, newValue: ValueCellVertexValue): void {
984+
if (this.isNotComputed(address)) {
985+
return
986+
}
987+
988+
const oldValue = this.dependencyGraph.getCellValue(address)
989+
this.columnSearch.change(getRawValue(oldValue), getRawValue(newValue), address)
990+
}
991+
992+
/**
993+
* Checks if the FormulaCellVertex or ArrayVertex at the given address is not computed.
994+
*/
995+
private isNotComputed(address: SimpleCellAddress): boolean {
996+
const vertex = this.dependencyGraph.getCell(address)
997+
998+
if (!vertex) {
999+
return false
1000+
}
1001+
1002+
return 'isComputed' in vertex && !vertex.isComputed()
1003+
}
9471004
}
9481005

9491006
export function normalizeRemovedIndexes(indexes: ColumnRowIndex[]): ColumnRowIndex[] {

test/unit/column-index.spec.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import {ColumnIndex} from '../../src/Lookup/ColumnIndex'
1414
import {NamedExpressions} from '../../src/NamedExpressions'
1515
import {ColumnsSpan, RowsSpan} from '../../src/Span'
1616
import {Statistics} from '../../src/statistics'
17-
import {adr, expectColumnIndexToMatchSheet} from './testUtils'
17+
import {adr, detailedError, expectColumnIndexToMatchSheet} from './testUtils'
18+
import { ErrorMessage } from '../../src/error-message'
1819

1920
function buildEmptyIndex(transformingService: LazilyTransformingAstService, config: Config, statistics: Statistics): ColumnIndex {
2021
const functionRegistry = new FunctionRegistry(config)
@@ -600,6 +601,8 @@ describe('Arrays', () => {
600601

601602
engine.setCellContents(adr('D1'), [['foo']])
602603

604+
expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.SPILL, ErrorMessage.NoSpaceForArrayResult))
605+
603606
expectColumnIndexToMatchSheet([
604607
[1, 2, null, 'foo']
605608
], engine)

test/unit/computation-suspension.spec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,4 +206,22 @@ describe('Evaluation suspension', () => {
206206
expect(changes).toContainEqual(new ExportedCellChange(adr('B1'), 4))
207207
expect(changes).toContainEqual(new ExportedCellChange(adr('C1'), 5))
208208
})
209+
210+
it('allows to update cell content of a not computed formula cell (#1194)', () => {
211+
const hf = HyperFormula.buildFromArray([], {
212+
licenseKey: 'gpl-v3'
213+
})
214+
215+
hf.suspendEvaluation()
216+
hf.setCellContents(adr('A1'), '=42')
217+
hf.setCellContents(adr('A1'), 42)
218+
hf.setCellContents(adr('A1'), '=42')
219+
hf.setCellContents(adr('A1'), null)
220+
hf.setCellContents(adr('A1'), '=42')
221+
hf.setCellContents(adr('A1'), '==')
222+
hf.setCellContents(adr('A1'), '=42')
223+
hf.setCellContents(adr('A1'), '=42')
224+
hf.resumeEvaluation()
225+
expect(hf.getCellValue(adr('A1'))).toBe(42)
226+
})
209227
})

test/unit/rebuild.spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,14 @@ describe('Rebuilding engine', () => {
5353

5454
expect(rebuildEngineSpy).toHaveBeenCalled()
5555
})
56+
57+
it('doesn\'t throw after adding named expression (#1194)', () => {
58+
const hf = HyperFormula.buildFromArray([['=42']], {
59+
licenseKey: 'gpl-v3'
60+
})
61+
62+
63+
hf.addNamedExpression('ABC', '=Sheet1!$A$1')
64+
expect(() => hf.rebuildAndRecalculate()).not.toThrow()
65+
})
5666
})

test/unit/testUtils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ export function columnIndexToSheet(columnIndex: ColumnIndex, width: number, heig
213213
for (const row of index) {
214214
result[row] = result[row] ?? []
215215
if (result[row][col] !== undefined) {
216-
throw new Error('ColumnIndex ambiguity.')
216+
throw new Error(`ColumnIndex ambiguity. Expected ${JSON.stringify(result[row][col])}, but found ${JSON.stringify(value)} at row ${JSON.stringify(row)}, column ${JSON.stringify(col)}`)
217217
}
218218
result[row][col] = value
219219
}

test/unit/update-config.spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,14 @@ describe('update config', () => {
112112
engine.updateConfig({ useColumnIndex: false, functionArgSeparator: ',', undoLimit: 20 })
113113
expect(rebuildEngineSpy).not.toHaveBeenCalled()
114114
})
115+
116+
it('doesn\'t throw after adding named expression (#1194)', () => {
117+
const hf = HyperFormula.buildFromArray([['=42']], {
118+
licenseKey: 'gpl-v3'
119+
})
120+
121+
122+
hf.addNamedExpression('ABC', '=Sheet1!$A$1')
123+
expect(() => hf.updateConfig({ maxRows: 101 })).not.toThrow()
124+
})
115125
})

0 commit comments

Comments
 (0)