From 7f80262eeca9239954f940d64f4fe598b521b501 Mon Sep 17 00:00:00 2001 From: izulin Date: Mon, 29 Mar 2021 15:55:30 +0200 Subject: [PATCH 01/14] parsing of escape character --- src/CellContentParser.ts | 2 +- test/CellContentParser.spec.ts | 10 +++++----- test/interpreter/criterion-computations.spec.ts | 6 +++--- test/interpreter/function-date.spec.ts | 2 +- test/interpreter/function-gcd.spec.ts | 2 +- test/interpreter/function-lcm.spec.ts | 2 +- test/interpreter/function-multinomial.spec.ts | 2 +- test/interpreter/operator-plus.spec.ts | 2 +- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/CellContentParser.ts b/src/CellContentParser.ts index c52cd3d898..4b729a31df 100644 --- a/src/CellContentParser.ts +++ b/src/CellContentParser.ts @@ -174,7 +174,7 @@ export class CellContentParser { return new CellContent.Number(parsedDateNumber) } else { return new CellContent.String( - content.startsWith('\'') ? content.slice(1) : content, + content ) } } diff --git a/test/CellContentParser.spec.ts b/test/CellContentParser.spec.ts index ccc9f35914..f0c3f81345 100644 --- a/test/CellContentParser.spec.ts +++ b/test/CellContentParser.spec.ts @@ -125,12 +125,12 @@ describe('CellContentParser', () => { }) it( 'starts with \'', () => { - expect(cellContentParser.parse('\'123')).toEqual(new CellContent.String('123')) - expect(cellContentParser.parse('\'=1+1')).toEqual(new CellContent.String('=1+1')) - expect(cellContentParser.parse('\'\'1')).toEqual(new CellContent.String('\'1')) - expect(cellContentParser.parse('\' 1')).toEqual(new CellContent.String(' 1')) + expect(cellContentParser.parse('\'123')).toEqual(new CellContent.String('\'123')) + expect(cellContentParser.parse('\'=1+1')).toEqual(new CellContent.String('\'=1+1')) + expect(cellContentParser.parse('\'\'1')).toEqual(new CellContent.String('\'\'1')) + expect(cellContentParser.parse('\' 1')).toEqual(new CellContent.String('\' 1')) expect(cellContentParser.parse(' \'1')).toEqual(new CellContent.String(' \'1')) - expect(cellContentParser.parse('\'02-02-2020')).toEqual(new CellContent.String('02-02-2020')) + expect(cellContentParser.parse('\'02-02-2020')).toEqual(new CellContent.String('\'02-02-2020')) }) it('currency parsing', () => { diff --git a/test/interpreter/criterion-computations.spec.ts b/test/interpreter/criterion-computations.spec.ts index d0329a0c1e..4fb192d05c 100644 --- a/test/interpreter/criterion-computations.spec.ts +++ b/test/interpreter/criterion-computations.spec.ts @@ -295,7 +295,7 @@ describe( 'big test', () => { expect(engine.getCellValue(adr('A29'))).toEqual(0) expect(engine.getCellValue(adr('A30'))).toEqual(0) expect(engine.getCellValue(adr('A31'))).toEqual(1) - expect(engine.getCellValue(adr('A32'))).toEqual(1) + expect(engine.getCellValue(adr('A32'))).toEqual(0) expect(engine.getCellValue(adr('A33'))).toEqual(0) expect(engine.getCellValue(adr('A34'))).toEqual(1) // expect(engine.getCellValue(adr('A35'))).toEqual(1) @@ -305,8 +305,8 @@ describe( 'big test', () => { expect(engine.getCellValue(adr('A39'))).toEqual(1) // expect(engine.getCellValue(adr('A40'))).toEqual(0) // expect(engine.getCellValue(adr('A41'))).toEqual(1) - expect(engine.getCellValue(adr('A42'))).toEqual(3) - expect(engine.getCellValue(adr('A43'))).toEqual(3) + expect(engine.getCellValue(adr('A42'))).toEqual(1) + expect(engine.getCellValue(adr('A43'))).toEqual(1) // expect(engine.getCellValue(adr('A44'))).toEqual(3) expect(engine.getCellValue(adr('A45'))).toEqual(0) expect(engine.getCellValue(adr('A46'))).toEqual(1) diff --git a/test/interpreter/function-date.spec.ts b/test/interpreter/function-date.spec.ts index 1f790f0388..a81ef7f0a4 100644 --- a/test/interpreter/function-date.spec.ts +++ b/test/interpreter/function-date.spec.ts @@ -218,7 +218,7 @@ describe( 'Function DATE + leap years', () =>{ it('with blanks', () => { const config = new Config() const engine = HyperFormula.buildFromArray([ - [null, '', 'string', null, '\''], + [null, '', 'string', null, undefined], ['=DATE(A1, 2, 3)'], ['=DATE(B1, 2, 3)'], ['=DATE(C1, 2, 3)'], diff --git a/test/interpreter/function-gcd.spec.ts b/test/interpreter/function-gcd.spec.ts index 87f6cb7fed..0fbf34e954 100644 --- a/test/interpreter/function-gcd.spec.ts +++ b/test/interpreter/function-gcd.spec.ts @@ -50,7 +50,7 @@ describe('Function GCD', () => { it('coerces to number', () => { const engine = HyperFormula.buildFromArray([ ['=GCD("2",4)'], - ['=GCD(B2:C2)', '\'2', 4], + ['=GCD(B2:C2)', '2', 4], ['=GCD(TRUE(),4)'], ['=GCD(B4:C4)', true, 4], ['=GCD(,4)'], diff --git a/test/interpreter/function-lcm.spec.ts b/test/interpreter/function-lcm.spec.ts index c9d1deb5d1..d91901b26d 100644 --- a/test/interpreter/function-lcm.spec.ts +++ b/test/interpreter/function-lcm.spec.ts @@ -59,7 +59,7 @@ describe('Function LCM', () => { it('coerces to number', () => { const engine = HyperFormula.buildFromArray([ ['=LCM("4",2)'], - ['=LCM(B2:C2)', '\'4', 2], + ['=LCM(B2:C2)', '4', 2], ['=LCM(FALSE(),4)'], ['=LCM(B4:C4)', false, 4], ['=LCM(,4)'], diff --git a/test/interpreter/function-multinomial.spec.ts b/test/interpreter/function-multinomial.spec.ts index 25db33069a..2f1e7fa9b0 100644 --- a/test/interpreter/function-multinomial.spec.ts +++ b/test/interpreter/function-multinomial.spec.ts @@ -41,7 +41,7 @@ describe('Function MULTINOMIAL', () => { it('coerces to number', () => { const engine = HyperFormula.buildFromArray([ ['=MULTINOMIAL("2",4)'], - ['=MULTINOMIAL(B2:C2)', '\'2', 4], + ['=MULTINOMIAL(B2:C2)', '2', 4], ['=MULTINOMIAL(TRUE(),4)'], ['=MULTINOMIAL(B4:C4)', true, 4], ['=MULTINOMIAL(,4)'], diff --git a/test/interpreter/operator-plus.spec.ts b/test/interpreter/operator-plus.spec.ts index c66f98cb55..b00d264302 100644 --- a/test/interpreter/operator-plus.spec.ts +++ b/test/interpreter/operator-plus.spec.ts @@ -16,7 +16,7 @@ describe('Operator PLUS', () => { const engine = HyperFormula.buildFromArray([ ['="2"+"3"'], ['="foobar"+1'], - ['\'3'], + ['3'], ['=A3+A3'], ]) From e1b6746a1309fd07823aa955baa38fef2996c43d Mon Sep 17 00:00:00 2001 From: izulin Date: Mon, 29 Mar 2021 17:31:32 +0200 Subject: [PATCH 02/14] test --- test/escaping-support.spec.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 test/escaping-support.spec.ts diff --git a/test/escaping-support.spec.ts b/test/escaping-support.spec.ts new file mode 100644 index 0000000000..592330cc83 --- /dev/null +++ b/test/escaping-support.spec.ts @@ -0,0 +1,10 @@ +import {HyperFormula} from '../src' +import {adr} from './testUtils' + +describe('escaped formulas', () => { + it('should serialize properly', () => { + const engine = HyperFormula.buildFromArray([['\'=SUM(2,2)']], {licenseKey: 'non-commercial-and-evaluation'}) + expect(engine.getCellSerialized(adr('A1'))).toEqual('\'=SUM(2,2)') + expect(engine.getCellValue(adr('A1'))).toEqual('\'=SUM(2,2)') + }) +}) From 2e44288638f797fe7c1b44493d135160cbff320f Mon Sep 17 00:00:00 2001 From: izulin Date: Mon, 29 Mar 2021 17:35:17 +0200 Subject: [PATCH 03/14] doc --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 348d055d3f..708faf23a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - GPU.js constructor needs to be provided directly to engine configuration. (#355) +- A **breaking change**: `'` symbol on the input is preserved in the value. (#617) ### Added - Added support for row and column reordering. (#343) From 766296e539a791140a300c9d9c54d595e509a5b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Uzna=C5=84ski?= <40573492+izulin@users.noreply.github.com> Date: Wed, 31 Mar 2021 14:04:13 +0200 Subject: [PATCH 04/14] Update test/escaping-support.spec.ts --- test/escaping-support.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/escaping-support.spec.ts b/test/escaping-support.spec.ts index 592330cc83..be383248e8 100644 --- a/test/escaping-support.spec.ts +++ b/test/escaping-support.spec.ts @@ -3,7 +3,7 @@ import {adr} from './testUtils' describe('escaped formulas', () => { it('should serialize properly', () => { - const engine = HyperFormula.buildFromArray([['\'=SUM(2,2)']], {licenseKey: 'non-commercial-and-evaluation'}) + const engine = HyperFormula.buildFromArray([['\'=SUM(2,2)']]) expect(engine.getCellSerialized(adr('A1'))).toEqual('\'=SUM(2,2)') expect(engine.getCellValue(adr('A1'))).toEqual('\'=SUM(2,2)') }) From 4f99654c8d9a1120f08615ab4648ca946e72ca09 Mon Sep 17 00:00:00 2001 From: izulin Date: Sat, 3 Apr 2021 14:04:16 +0200 Subject: [PATCH 05/14] proper distinction between serialization and value --- jest.config.js | 2 +- src/CellContentParser.ts | 4 +--- src/DependencyGraph/AddressMapping/AddressMapping.ts | 4 ++-- src/DependencyGraph/DependencyGraph.ts | 8 ++++---- src/DependencyGraph/ValueCellVertex.ts | 5 ++++- src/Serialization.ts | 6 +++--- test/escaping-support.spec.ts | 2 +- test/interpreter/criterion-computations.spec.ts | 6 +++--- test/interpreter/function-date.spec.ts | 2 +- test/interpreter/function-gcd.spec.ts | 2 +- test/interpreter/function-lcm.spec.ts | 2 +- test/interpreter/function-multinomial.spec.ts | 2 +- test/interpreter/operator-plus.spec.ts | 2 +- 13 files changed, 24 insertions(+), 23 deletions(-) diff --git a/jest.config.js b/jest.config.js index 8be944484f..5c63221bc6 100644 --- a/jest.config.js +++ b/jest.config.js @@ -44,7 +44,7 @@ module.exports = { "/test/**/*spec.(ts|js)" ], - silent: true, + silent: false, // A map from regular expressions to paths to transformers transform: { diff --git a/src/CellContentParser.ts b/src/CellContentParser.ts index 4b729a31df..5de048d12d 100644 --- a/src/CellContentParser.ts +++ b/src/CellContentParser.ts @@ -173,9 +173,7 @@ export class CellContentParser { if (parsedDateNumber !== undefined) { return new CellContent.Number(parsedDateNumber) } else { - return new CellContent.String( - content - ) + return new CellContent.String(content) } } } else { diff --git a/src/DependencyGraph/AddressMapping/AddressMapping.ts b/src/DependencyGraph/AddressMapping/AddressMapping.ts index c7db4ecbdb..62ff93d08e 100644 --- a/src/DependencyGraph/AddressMapping/AddressMapping.ts +++ b/src/DependencyGraph/AddressMapping/AddressMapping.ts @@ -65,7 +65,7 @@ export class AddressMapping { this.addSheet(sheetId, new strategyConstructor(width, height)) } - public getCellValue(address: SimpleCellAddress): InterpreterValue { + public getCellValue(address: SimpleCellAddress, literalValue?: boolean): InterpreterValue { const vertex = this.getCell(address) if (vertex === null) { @@ -73,7 +73,7 @@ export class AddressMapping { } else if (vertex instanceof MatrixVertex) { return vertex.getMatrixCellValue(address) } else { - return vertex.getCellValue() + return vertex.getCellValue(literalValue) } } diff --git a/src/DependencyGraph/DependencyGraph.ts b/src/DependencyGraph/DependencyGraph.ts index 3d58aaf4af..3965986049 100644 --- a/src/DependencyGraph/DependencyGraph.ts +++ b/src/DependencyGraph/DependencyGraph.ts @@ -601,12 +601,12 @@ export class DependencyGraph { return this.addressMapping.getCell(address) } - public getCellValue(address: SimpleCellAddress): InterpreterValue { - return this.addressMapping.getCellValue(address) + public getCellValue(address: SimpleCellAddress, literalValue?: boolean): InterpreterValue { + return this.addressMapping.getCellValue(address, literalValue) } - public getScalarValue(address: SimpleCellAddress): InternalScalarValue { - const value = this.addressMapping.getCellValue(address) + public getScalarValue(address: SimpleCellAddress, literalValue?: boolean): InternalScalarValue { + const value = this.addressMapping.getCellValue(address, literalValue) if (value instanceof SimpleRangeValue) { return new CellError(ErrorType.VALUE, ErrorMessage.ScalarExpected) } diff --git a/src/DependencyGraph/ValueCellVertex.ts b/src/DependencyGraph/ValueCellVertex.ts index d002e05f15..b9d6700732 100644 --- a/src/DependencyGraph/ValueCellVertex.ts +++ b/src/DependencyGraph/ValueCellVertex.ts @@ -22,7 +22,10 @@ export class ValueCellVertex { /** * Returns cell value stored in vertex */ - public getCellValue(): ValueCellVertexValue { + public getCellValue(literalValue?: boolean): ValueCellVertexValue { + if(!literalValue && typeof this.cellValue === 'string') { + return this.cellValue.startsWith('\'') ? this.cellValue.slice(1) : this.cellValue + } return this.cellValue } diff --git a/src/Serialization.ts b/src/Serialization.ts index 8e05e99fff..5f984e0273 100644 --- a/src/Serialization.ts +++ b/src/Serialization.ts @@ -49,7 +49,7 @@ export class Serialization { if (formula !== undefined) { return formula } else { - const value: CellValue = this.getCellValue(address) + const value: CellValue = this.getCellValue(address, true) if (value instanceof DetailedCellError) { return this.config.translationPackage.getErrorTranslation(value.type) } else { @@ -58,8 +58,8 @@ export class Serialization { } } - public getCellValue(address: SimpleCellAddress): CellValue { - return this.exporter.exportValue(this.dependencyGraph.getScalarValue(address)) + public getCellValue(address: SimpleCellAddress, literalValue?: boolean): CellValue { + return this.exporter.exportValue(this.dependencyGraph.getScalarValue(address, literalValue)) } public getSheetValues(sheet: number): CellValue[][] { diff --git a/test/escaping-support.spec.ts b/test/escaping-support.spec.ts index be383248e8..79cc803017 100644 --- a/test/escaping-support.spec.ts +++ b/test/escaping-support.spec.ts @@ -5,6 +5,6 @@ describe('escaped formulas', () => { it('should serialize properly', () => { const engine = HyperFormula.buildFromArray([['\'=SUM(2,2)']]) expect(engine.getCellSerialized(adr('A1'))).toEqual('\'=SUM(2,2)') - expect(engine.getCellValue(adr('A1'))).toEqual('\'=SUM(2,2)') + expect(engine.getCellValue(adr('A1'))).toEqual('=SUM(2,2)') }) }) diff --git a/test/interpreter/criterion-computations.spec.ts b/test/interpreter/criterion-computations.spec.ts index 4fb192d05c..d0329a0c1e 100644 --- a/test/interpreter/criterion-computations.spec.ts +++ b/test/interpreter/criterion-computations.spec.ts @@ -295,7 +295,7 @@ describe( 'big test', () => { expect(engine.getCellValue(adr('A29'))).toEqual(0) expect(engine.getCellValue(adr('A30'))).toEqual(0) expect(engine.getCellValue(adr('A31'))).toEqual(1) - expect(engine.getCellValue(adr('A32'))).toEqual(0) + expect(engine.getCellValue(adr('A32'))).toEqual(1) expect(engine.getCellValue(adr('A33'))).toEqual(0) expect(engine.getCellValue(adr('A34'))).toEqual(1) // expect(engine.getCellValue(adr('A35'))).toEqual(1) @@ -305,8 +305,8 @@ describe( 'big test', () => { expect(engine.getCellValue(adr('A39'))).toEqual(1) // expect(engine.getCellValue(adr('A40'))).toEqual(0) // expect(engine.getCellValue(adr('A41'))).toEqual(1) - expect(engine.getCellValue(adr('A42'))).toEqual(1) - expect(engine.getCellValue(adr('A43'))).toEqual(1) + expect(engine.getCellValue(adr('A42'))).toEqual(3) + expect(engine.getCellValue(adr('A43'))).toEqual(3) // expect(engine.getCellValue(adr('A44'))).toEqual(3) expect(engine.getCellValue(adr('A45'))).toEqual(0) expect(engine.getCellValue(adr('A46'))).toEqual(1) diff --git a/test/interpreter/function-date.spec.ts b/test/interpreter/function-date.spec.ts index a81ef7f0a4..1f790f0388 100644 --- a/test/interpreter/function-date.spec.ts +++ b/test/interpreter/function-date.spec.ts @@ -218,7 +218,7 @@ describe( 'Function DATE + leap years', () =>{ it('with blanks', () => { const config = new Config() const engine = HyperFormula.buildFromArray([ - [null, '', 'string', null, undefined], + [null, '', 'string', null, '\''], ['=DATE(A1, 2, 3)'], ['=DATE(B1, 2, 3)'], ['=DATE(C1, 2, 3)'], diff --git a/test/interpreter/function-gcd.spec.ts b/test/interpreter/function-gcd.spec.ts index 0fbf34e954..87f6cb7fed 100644 --- a/test/interpreter/function-gcd.spec.ts +++ b/test/interpreter/function-gcd.spec.ts @@ -50,7 +50,7 @@ describe('Function GCD', () => { it('coerces to number', () => { const engine = HyperFormula.buildFromArray([ ['=GCD("2",4)'], - ['=GCD(B2:C2)', '2', 4], + ['=GCD(B2:C2)', '\'2', 4], ['=GCD(TRUE(),4)'], ['=GCD(B4:C4)', true, 4], ['=GCD(,4)'], diff --git a/test/interpreter/function-lcm.spec.ts b/test/interpreter/function-lcm.spec.ts index d91901b26d..c9d1deb5d1 100644 --- a/test/interpreter/function-lcm.spec.ts +++ b/test/interpreter/function-lcm.spec.ts @@ -59,7 +59,7 @@ describe('Function LCM', () => { it('coerces to number', () => { const engine = HyperFormula.buildFromArray([ ['=LCM("4",2)'], - ['=LCM(B2:C2)', '4', 2], + ['=LCM(B2:C2)', '\'4', 2], ['=LCM(FALSE(),4)'], ['=LCM(B4:C4)', false, 4], ['=LCM(,4)'], diff --git a/test/interpreter/function-multinomial.spec.ts b/test/interpreter/function-multinomial.spec.ts index 2f1e7fa9b0..25db33069a 100644 --- a/test/interpreter/function-multinomial.spec.ts +++ b/test/interpreter/function-multinomial.spec.ts @@ -41,7 +41,7 @@ describe('Function MULTINOMIAL', () => { it('coerces to number', () => { const engine = HyperFormula.buildFromArray([ ['=MULTINOMIAL("2",4)'], - ['=MULTINOMIAL(B2:C2)', '2', 4], + ['=MULTINOMIAL(B2:C2)', '\'2', 4], ['=MULTINOMIAL(TRUE(),4)'], ['=MULTINOMIAL(B4:C4)', true, 4], ['=MULTINOMIAL(,4)'], diff --git a/test/interpreter/operator-plus.spec.ts b/test/interpreter/operator-plus.spec.ts index b00d264302..c66f98cb55 100644 --- a/test/interpreter/operator-plus.spec.ts +++ b/test/interpreter/operator-plus.spec.ts @@ -16,7 +16,7 @@ describe('Operator PLUS', () => { const engine = HyperFormula.buildFromArray([ ['="2"+"3"'], ['="foobar"+1'], - ['3'], + ['\'3'], ['=A3+A3'], ]) From 4ae0ed07da09b687f182a0c93b9d113a5ee8ec37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Uzna=C5=84ski?= <40573492+izulin@users.noreply.github.com> Date: Sat, 3 Apr 2021 14:07:32 +0200 Subject: [PATCH 06/14] Update jest.config.js --- jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index 5c63221bc6..8be944484f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -44,7 +44,7 @@ module.exports = { "/test/**/*spec.(ts|js)" ], - silent: false, + silent: true, // A map from regular expressions to paths to transformers transform: { From 99cb1b5990ddf68054cccb2e60decd39c6dc51ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Uzna=C5=84ski?= <40573492+izulin@users.noreply.github.com> Date: Sat, 3 Apr 2021 14:08:06 +0200 Subject: [PATCH 07/14] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 708faf23a1..74e54dfeec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - GPU.js constructor needs to be provided directly to engine configuration. (#355) -- A **breaking change**: `'` symbol on the input is preserved in the value. (#617) +- A `'` symbol on the input is preserved in the value for serialization. (#617) ### Added - Added support for row and column reordering. (#343) From 45a05c20a59d12664f2c4c6b621a97527612afdb Mon Sep 17 00:00:00 2001 From: izulin Date: Thu, 8 Apr 2021 18:00:14 +0200 Subject: [PATCH 08/14] refactor for preserving original raw value --- src/CellContentParser.ts | 2 +- src/ClipboardOperations.ts | 2 + .../AddressMapping/AddressMapping.ts | 18 ++- src/DependencyGraph/DependencyGraph.ts | 22 ++- src/DependencyGraph/MatrixVertex.ts | 5 + src/DependencyGraph/ValueCellVertex.ts | 21 +-- src/GraphBuilder.ts | 8 +- src/HyperFormula.ts | 8 +- src/Operations.ts | 18 +-- src/Serialization.ts | 25 +-- test/CellContentParser.spec.ts | 10 +- test/address-mapping.spec.ts | 148 +++++++++--------- test/engine.spec.ts | 4 +- test/graph-vertex.spec.ts | 4 +- test/interpreter/precision.spec.ts | 32 ++-- test/serialization.spec.ts | 73 +++++++++ 16 files changed, 251 insertions(+), 149 deletions(-) create mode 100644 test/serialization.spec.ts diff --git a/src/CellContentParser.ts b/src/CellContentParser.ts index 5de048d12d..4d45683588 100644 --- a/src/CellContentParser.ts +++ b/src/CellContentParser.ts @@ -173,7 +173,7 @@ export class CellContentParser { if (parsedDateNumber !== undefined) { return new CellContent.Number(parsedDateNumber) } else { - return new CellContent.String(content) + return new CellContent.String(content.startsWith('\'') ? content.slice(1) : content ) } } } else { diff --git a/src/ClipboardOperations.ts b/src/ClipboardOperations.ts index c70ff59e46..b6f9696e9f 100644 --- a/src/ClipboardOperations.ts +++ b/src/ClipboardOperations.ts @@ -5,6 +5,7 @@ import {AbsoluteCellRange} from './AbsoluteCellRange' import {invalidSimpleCellAddress, simpleCellAddress, SimpleCellAddress} from './Cell' +import {RawCellContent} from './CellContentParser' import {Operations} from './Operations' import {DependencyGraph} from './DependencyGraph' import {ValueCellVertexValue} from './DependencyGraph/ValueCellVertex' @@ -31,6 +32,7 @@ export enum ClipboardCellType { export interface ClipboardCellValue { type: ClipboardCellType.VALUE, value: ValueCellVertexValue, + rawValue: RawCellContent, } export interface ClipboardCellEmpty { diff --git a/src/DependencyGraph/AddressMapping/AddressMapping.ts b/src/DependencyGraph/AddressMapping/AddressMapping.ts index 62ff93d08e..30e70d3801 100644 --- a/src/DependencyGraph/AddressMapping/AddressMapping.ts +++ b/src/DependencyGraph/AddressMapping/AddressMapping.ts @@ -4,9 +4,10 @@ */ import {SimpleCellAddress} from '../../Cell' +import {RawCellContent} from '../../CellContentParser' import {EmptyValue, InterpreterValue} from '../../interpreter/InterpreterValue' import {ColumnsSpan, RowsSpan} from '../../Span' -import {MatrixVertex} from '../index' +import {EmptyCellVertex, MatrixVertex, ValueCellVertex} from '../index' import {CellVertex} from '../Vertex' import {ChooseAddressMapping} from './ChooseAddressMappingPolicy' import {IAddressMappingStrategy} from './IAddressMappingStrategy' @@ -65,7 +66,7 @@ export class AddressMapping { this.addSheet(sheetId, new strategyConstructor(width, height)) } - public getCellValue(address: SimpleCellAddress, literalValue?: boolean): InterpreterValue { + public getCellValue(address: SimpleCellAddress): InterpreterValue { const vertex = this.getCell(address) if (vertex === null) { @@ -73,7 +74,18 @@ export class AddressMapping { } else if (vertex instanceof MatrixVertex) { return vertex.getMatrixCellValue(address) } else { - return vertex.getCellValue(literalValue) + return vertex.getCellValue() + } + } + + public getRawValue(address: SimpleCellAddress): RawCellContent { + const vertex = this.getCell(address) + if(vertex instanceof ValueCellVertex) { + return vertex.getRawValue() + } else if (vertex instanceof MatrixVertex) { + return vertex.getMatrixCellRawValue(address) + } else { + return null } } diff --git a/src/DependencyGraph/DependencyGraph.ts b/src/DependencyGraph/DependencyGraph.ts index 3965986049..b25da304e3 100644 --- a/src/DependencyGraph/DependencyGraph.ts +++ b/src/DependencyGraph/DependencyGraph.ts @@ -12,6 +12,7 @@ import { simpleCellAddress, SimpleCellAddress } from '../Cell' +import {RawCellContent} from '../CellContentParser' import {CellDependency} from '../CellDependency' import {Config} from '../Config' import {ErrorMessage} from '../error-message' @@ -112,7 +113,7 @@ export class DependencyGraph { this.correctInfiniteRangesDependency(address) } - public setValueToCell(address: SimpleCellAddress, newValue: ValueCellVertexValue) { + public setValueToCell(address: SimpleCellAddress, newValue: ValueCellVertexValue, rawValue: RawCellContent) { const vertex = this.addressMapping.getCell(address) this.ensureThatVertexIsNonMatrixCellVertex(vertex) @@ -120,10 +121,11 @@ export class DependencyGraph { const oldValue = vertex.getCellValue() if (oldValue !== newValue) { vertex.setCellValue(newValue) + vertex.setRawValue(rawValue) this.graph.markNodeAsSpecialRecentlyChanged(vertex) } } else { - const newVertex = new ValueCellVertex(newValue) + const newVertex = new ValueCellVertex(newValue, rawValue) this.exchangeOrAddGraphNode(vertex, newVertex) this.addressMapping.setCell(address, newVertex) this.graph.markNodeAsSpecialRecentlyChanged(newVertex) @@ -505,8 +507,8 @@ export class DependencyGraph { const adjacentNodes = this.graph.adjacentNodes(matrixVertex) for (const address of matrixRange.addresses(this)) { - const value = this.getCellValue(address) as ExtendedNumber // We wouldn't need that typecast if we would take values from Matrix - const valueVertex = new ValueCellVertex(value) + // We wouldn't need that typecast if we would take values from Matrix + const valueVertex = new ValueCellVertex(this.getCellValue(address) as ExtendedNumber, this.getRawValue(address)) this.addVertex(address, valueVertex) } @@ -601,12 +603,16 @@ export class DependencyGraph { return this.addressMapping.getCell(address) } - public getCellValue(address: SimpleCellAddress, literalValue?: boolean): InterpreterValue { - return this.addressMapping.getCellValue(address, literalValue) + public getCellValue(address: SimpleCellAddress): InterpreterValue { + return this.addressMapping.getCellValue(address) } - public getScalarValue(address: SimpleCellAddress, literalValue?: boolean): InternalScalarValue { - const value = this.addressMapping.getCellValue(address, literalValue) + public getRawValue(address: SimpleCellAddress): RawCellContent { + return this.addressMapping.getRawValue(address) + } + + public getScalarValue(address: SimpleCellAddress): InternalScalarValue { + const value = this.addressMapping.getCellValue(address) if (value instanceof SimpleRangeValue) { return new CellError(ErrorType.VALUE, ErrorMessage.ScalarExpected) } diff --git a/src/DependencyGraph/MatrixVertex.ts b/src/DependencyGraph/MatrixVertex.ts index 7a0090c028..72770faa1e 100644 --- a/src/DependencyGraph/MatrixVertex.ts +++ b/src/DependencyGraph/MatrixVertex.ts @@ -56,6 +56,11 @@ export class MatrixVertex { return this.matrix.get(col, row) } + public getMatrixCellRawValue(address: SimpleCellAddress): number | undefined { + const val = this.getMatrixCellValue(address) + return val instanceof CellError ? undefined : val + } + public setMatrixCellValue(address: SimpleCellAddress, value: number): void { const col = address.col - this.cellAddress.col const row = address.row - this.cellAddress.row diff --git a/src/DependencyGraph/ValueCellVertex.ts b/src/DependencyGraph/ValueCellVertex.ts index b9d6700732..ebb8142a44 100644 --- a/src/DependencyGraph/ValueCellVertex.ts +++ b/src/DependencyGraph/ValueCellVertex.ts @@ -4,7 +4,8 @@ */ import {CellError} from '../Cell' -import {ExtendedNumber} from '../interpreter/InterpreterValue' +import {RawCellContent} from '../CellContentParser' +import {ExtendedNumber, RawScalarValue} from '../interpreter/InterpreterValue' export type ValueCellVertexValue = ExtendedNumber | boolean | string | CellError @@ -13,19 +14,13 @@ export type ValueCellVertexValue = ExtendedNumber | boolean | string | CellError */ export class ValueCellVertex { /** Static cell value. */ - private cellValue: ValueCellVertexValue - - constructor(cellValue: ValueCellVertexValue) { - this.cellValue = cellValue + constructor(private cellValue: ValueCellVertexValue, private rawValue: RawCellContent) { } /** * Returns cell value stored in vertex */ - public getCellValue(literalValue?: boolean): ValueCellVertexValue { - if(!literalValue && typeof this.cellValue === 'string') { - return this.cellValue.startsWith('\'') ? this.cellValue.slice(1) : this.cellValue - } + public getCellValue(): ValueCellVertexValue { return this.cellValue } @@ -35,4 +30,12 @@ export class ValueCellVertex { public setCellValue(cellValue: ValueCellVertexValue) { this.cellValue = cellValue } + + public getRawValue(): RawCellContent { + return this.rawValue + } + + public setRawValue(rawValue: RawCellContent) { + this.rawValue = rawValue + } } diff --git a/src/GraphBuilder.ts b/src/GraphBuilder.ts index acbf1fb8c2..61747f1108 100644 --- a/src/GraphBuilder.ts +++ b/src/GraphBuilder.ts @@ -133,7 +133,7 @@ export class SimpleStrategy implements GraphBuilderStrategy { } else if (parsedCellContent instanceof CellContent.Empty) { /* we don't care about empty cells here */ } else { - const vertex = new ValueCellVertex(parsedCellContent.value) + const vertex = new ValueCellVertex(parsedCellContent.value, cellContent) this.columnIndex.add(getRawValue(parsedCellContent.value), address) this.dependencyGraph.addVertex(address, vertex) } @@ -204,7 +204,7 @@ export class MatrixDetectionStrategy implements GraphBuilderStrategy { } else if (parsedCellContent instanceof CellContent.Number) { matrixHeuristic.add(address) } else { - const vertex = new ValueCellVertex(parsedCellContent.value) + const vertex = new ValueCellVertex(parsedCellContent.value, cellContent) this.columnSearch.add(parsedCellContent.value, address) this.dependencyGraph.addVertex(address, vertex) } @@ -219,7 +219,7 @@ export class MatrixDetectionStrategy implements GraphBuilderStrategy { const elem = notMatrices[i] for (const address of elem.cells.reverse()) { const value = sheets[this.dependencyGraph.getSheetName(address.sheet)][address.row][address.col] - const vertex = new ValueCellVertex(Number(value)) + const vertex = new ValueCellVertex(Number(value), value) this.columnSearch.add(Number(value), address) this.dependencyGraph.addVertex(address, vertex) } @@ -234,7 +234,7 @@ export class MatrixDetectionStrategy implements GraphBuilderStrategy { export function buildMatrixVertex(ast: ProcedureAst, formulaAddress: SimpleCellAddress): MatrixVertex | ValueCellVertex { const size = checkMatrixSize(ast, formulaAddress) if (size instanceof CellError) { - return new ValueCellVertex(size) + return new ValueCellVertex(size, undefined) } return new MatrixVertex(formulaAddress, size.width, size.height, ast) } diff --git a/src/HyperFormula.ts b/src/HyperFormula.ts index bd258cebd3..110c98668a 100644 --- a/src/HyperFormula.ts +++ b/src/HyperFormula.ts @@ -643,7 +643,7 @@ export class HyperFormula implements TypedEmitter { * * @category Cells */ - public getCellSerialized(cellAddress: SimpleCellAddress): NoErrorCellValue { + public getCellSerialized(cellAddress: SimpleCellAddress): RawCellContent { this.ensureEvaluationIsNotSuspended() return this._serialization.getCellSerialized(cellAddress) } @@ -733,7 +733,7 @@ export class HyperFormula implements TypedEmitter { * * @category Sheets */ - public getSheetSerialized(sheetId: number): NoErrorCellValue[][] { + public getSheetSerialized(sheetId: number): RawCellContent[][] { this.ensureEvaluationIsNotSuspended() return this._serialization.getSheetSerialized(sheetId) } @@ -850,7 +850,7 @@ export class HyperFormula implements TypedEmitter { * * @category Sheets */ - public getAllSheetsSerialized(): Record { + public getAllSheetsSerialized(): Record { this.ensureEvaluationIsNotSuspended() return this._serialization.getAllSheetsSerialized() } @@ -2214,7 +2214,7 @@ export class HyperFormula implements TypedEmitter { * * @category Ranges */ - public getRangeSerialized(leftCorner: SimpleCellAddress, width: number, height: number): CellValue[][] { + public getRangeSerialized(leftCorner: SimpleCellAddress, width: number, height: number): RawCellContent[][] { const cellRange = AbsoluteCellRange.spanFrom(leftCorner, width, height) return cellRange.arrayOfAddressesInRange().map( (subarray) => subarray.map( diff --git a/src/Operations.ts b/src/Operations.ts index 24e6c01d21..2fe8da342b 100644 --- a/src/Operations.ts +++ b/src/Operations.ts @@ -5,7 +5,7 @@ import {EmptyValue, getRawValue} from './interpreter/InterpreterValue' import {ClipboardCell, ClipboardCellType} from './ClipboardOperations' -import {invalidSimpleCellAddress, simpleCellAddress, SimpleCellAddress} from './Cell' +import {CellError, invalidSimpleCellAddress, simpleCellAddress, SimpleCellAddress} from './Cell' import {AbsoluteCellRange} from './AbsoluteCellRange' import {absolutizeDependencies, filterDependenciesOutOfScope} from './absolutizeDependencies' import {CellContent, CellContentParser, RawCellContent} from './CellContentParser' @@ -444,7 +444,7 @@ export class Operations { public restoreCell(address: SimpleCellAddress, clipboardCell: ClipboardCell) { switch (clipboardCell.type) { case ClipboardCellType.VALUE: { - this.setValueToCell(clipboardCell.value, address) + this.setValueToCell(clipboardCell.value, clipboardCell.rawValue, address) break } case ClipboardCellType.FORMULA: { @@ -577,9 +577,9 @@ export class Operations { if (vertex === null || vertex instanceof EmptyCellVertex) { return {type: ClipboardCellType.EMPTY} } else if (vertex instanceof ValueCellVertex) { - return {type: ClipboardCellType.VALUE, value: vertex.getCellValue()} + return {type: ClipboardCellType.VALUE, value: vertex.getCellValue(), rawValue: vertex.getRawValue()} } else if (vertex instanceof MatrixVertex) { - return {type: ClipboardCellType.VALUE, value: vertex.getMatrixCellValue(address)} + return {type: ClipboardCellType.VALUE, value: vertex.getMatrixCellValue(address), rawValue: vertex.getMatrixCellRawValue(address)} } else if (vertex instanceof FormulaCellVertex) { return { type: ClipboardCellType.FORMULA, @@ -659,7 +659,7 @@ export class Operations { } else if (parsedCellContent instanceof CellContent.MatrixFormula) { throw new Error('Cant happen') } else { - this.setValueToCell(parsedCellContent.value, address) + this.setValueToCell(parsedCellContent.value, newCellContent, address) } } else { throw new Error('Illegal operation') @@ -676,9 +676,9 @@ export class Operations { } } - public setValueToCell(value: ValueCellVertexValue, address: SimpleCellAddress) { + public setValueToCell(value: ValueCellVertexValue, rawValue: RawCellContent, address: SimpleCellAddress) { const oldValue = this.dependencyGraph.getCellValue(address) - this.dependencyGraph.setValueToCell(address, value) + this.dependencyGraph.setValueToCell(address, value, rawValue) this.columnSearch.change(getRawValue(oldValue), getRawValue(value), address) this.changes.addChange(value, address) } @@ -776,7 +776,7 @@ export class Operations { if (parsedCellContent instanceof CellContent.Empty) { this.setCellEmpty(address) } else { - this.setValueToCell(parsedCellContent.value, address) + this.setValueToCell(parsedCellContent.value, expression, address) } } } @@ -847,7 +847,7 @@ export class Operations { } else if (sourceVertex instanceof EmptyCellVertex) { this.setCellEmpty(expression.address) } else if (sourceVertex instanceof ValueCellVertex) { - this.setValueToCell(sourceVertex.getCellValue(), expression.address) + this.setValueToCell(sourceVertex.getCellValue(), sourceVertex.getRawValue(), expression.address) } } return this.dependencyGraph.fetchCellOrCreateEmpty(expression.address) diff --git a/src/Serialization.ts b/src/Serialization.ts index 5f984e0273..199c0d4b62 100644 --- a/src/Serialization.ts +++ b/src/Serialization.ts @@ -4,17 +4,19 @@ */ import {simpleCellAddress, SimpleCellAddress} from './Cell' +import {RawCellContent} from './CellContentParser' import {CellValue, DetailedCellError, NoErrorCellValue} from './CellValue' import {Config} from './Config' import {DependencyGraph, FormulaCellVertex, MatrixVertex, ParsingErrorVertex} from './DependencyGraph' import {Exporter} from './Exporter' +import {RawScalarValue} from './interpreter/InterpreterValue' import {Maybe} from './Maybe' import {buildLexerConfig, Unparser} from './parser' import {NamedExpressionOptions, NamedExpressions} from './NamedExpressions' export interface SerializedNamedExpression { name: string, - expression: NoErrorCellValue, + expression: RawCellContent, scope: Maybe, options: Maybe, } @@ -44,22 +46,21 @@ export class Serialization { return undefined } - public getCellSerialized(address: SimpleCellAddress): NoErrorCellValue { + public getCellSerialized(address: SimpleCellAddress): RawCellContent { const formula: Maybe = this.getCellFormula(address) if (formula !== undefined) { return formula } else { - const value: CellValue = this.getCellValue(address, true) - if (value instanceof DetailedCellError) { - return this.config.translationPackage.getErrorTranslation(value.type) - } else { - return value - } + return this.getRawValue(address) } } - public getCellValue(address: SimpleCellAddress, literalValue?: boolean): CellValue { - return this.exporter.exportValue(this.dependencyGraph.getScalarValue(address, literalValue)) + public getCellValue(address: SimpleCellAddress): CellValue { + return this.exporter.exportValue(this.dependencyGraph.getScalarValue(address)) + } + + public getRawValue(address: SimpleCellAddress): RawCellContent { + return this.dependencyGraph.getRawValue(address) } public getSheetValues(sheet: number): CellValue[][] { @@ -110,7 +111,7 @@ export class Serialization { return result } - public getSheetSerialized(sheet: number): NoErrorCellValue[][] { + public getSheetSerialized(sheet: number): RawCellContent[][] { return this.genericSheetGetter(sheet, (arg) => this.getCellSerialized(arg)) } @@ -122,7 +123,7 @@ export class Serialization { return this.genericAllSheetsGetter((arg) => this.getSheetFormulas(arg)) } - public getAllSheetsSerialized(): Record { + public getAllSheetsSerialized(): Record { return this.genericAllSheetsGetter((arg) => this.getSheetSerialized(arg)) } diff --git a/test/CellContentParser.spec.ts b/test/CellContentParser.spec.ts index f0c3f81345..ccc9f35914 100644 --- a/test/CellContentParser.spec.ts +++ b/test/CellContentParser.spec.ts @@ -125,12 +125,12 @@ describe('CellContentParser', () => { }) it( 'starts with \'', () => { - expect(cellContentParser.parse('\'123')).toEqual(new CellContent.String('\'123')) - expect(cellContentParser.parse('\'=1+1')).toEqual(new CellContent.String('\'=1+1')) - expect(cellContentParser.parse('\'\'1')).toEqual(new CellContent.String('\'\'1')) - expect(cellContentParser.parse('\' 1')).toEqual(new CellContent.String('\' 1')) + expect(cellContentParser.parse('\'123')).toEqual(new CellContent.String('123')) + expect(cellContentParser.parse('\'=1+1')).toEqual(new CellContent.String('=1+1')) + expect(cellContentParser.parse('\'\'1')).toEqual(new CellContent.String('\'1')) + expect(cellContentParser.parse('\' 1')).toEqual(new CellContent.String(' 1')) expect(cellContentParser.parse(' \'1')).toEqual(new CellContent.String(' \'1')) - expect(cellContentParser.parse('\'02-02-2020')).toEqual(new CellContent.String('\'02-02-2020')) + expect(cellContentParser.parse('\'02-02-2020')).toEqual(new CellContent.String('02-02-2020')) }) it('currency parsing', () => { diff --git a/test/address-mapping.spec.ts b/test/address-mapping.spec.ts index 5ce896942a..617fa56f70 100644 --- a/test/address-mapping.spec.ts +++ b/test/address-mapping.spec.ts @@ -11,7 +11,7 @@ import {adr} from './testUtils' const sharedExamples = (builder: (width: number, height: number) => AddressMapping) => { it('simple set', () => { const mapping = builder(1, 1) - const vertex = new ValueCellVertex(42) + const vertex = new ValueCellVertex(42, 42) const address = adr('A1') mapping.setCell(address, vertex) @@ -21,7 +21,7 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('set and using different reference when get', () => { const mapping = builder(1, 1) - const vertex = new ValueCellVertex(42) + const vertex = new ValueCellVertex(42, 42) mapping.setCell(adr('A1'), vertex) @@ -37,7 +37,7 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('get when there was already something in that column', () => { const mapping = builder(1, 2) - mapping.setCell(adr('A2'), new ValueCellVertex(42)) + mapping.setCell(adr('A2'), new ValueCellVertex(42, 42)) expect(mapping.getCell(adr('A1'))).toBe(null) }) @@ -50,8 +50,8 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it("set when there's already something in that column", () => { const mapping = builder(1, 2) - const vertex0 = new ValueCellVertex(42) - const vertex1 = new ValueCellVertex(42) + const vertex0 = new ValueCellVertex(42, 42) + const vertex1 = new ValueCellVertex(42, 42) mapping.setCell(adr('A1'), vertex0) mapping.setCell(adr('A2'), vertex1) @@ -62,8 +62,8 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('set overrides old value', () => { const mapping = builder(1, 1) - const vertex0 = new ValueCellVertex(42) - const vertex1 = new ValueCellVertex(42) + const vertex0 = new ValueCellVertex(42, 42) + const vertex1 = new ValueCellVertex(42, 42) mapping.setCell(adr('A1'), vertex0) mapping.setCell(adr('A1'), vertex1) @@ -86,7 +86,7 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('has when there was already something in that column', () => { const mapping = builder(1, 2) - mapping.setCell(adr('A2'), new ValueCellVertex(42)) + mapping.setCell(adr('A2'), new ValueCellVertex(42, 42)) expect(mapping.has(adr('A1'))).toBe(false) }) @@ -94,7 +94,7 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('has when there is a value', () => { const mapping = builder(1, 1) - mapping.setCell(adr('A1'), new ValueCellVertex(42)) + mapping.setCell(adr('A1'), new ValueCellVertex(42, 42)) expect(mapping.has(adr('A1'))).toBe(true) }) @@ -102,37 +102,37 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('addRows in the beginning of a mapping', () => { const mapping = builder(1, 1) - mapping.setCell(adr('A1'), new ValueCellVertex(42)) + mapping.setCell(adr('A1'), new ValueCellVertex(42, 42)) mapping.addRows(0, 0, 1) expect(mapping.getCell(adr('A1'))).toBe(null) - expect(mapping.fetchCell(adr('A2'))).toEqual(new ValueCellVertex(42)) + expect(mapping.fetchCell(adr('A2'))).toEqual(new ValueCellVertex(42, 42)) expect(mapping.getHeight(0)).toEqual(2) }) it('addRows in the middle of a mapping', () => { const mapping = builder(1, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(42)) - mapping.setCell(adr('A2'), new ValueCellVertex(43)) + mapping.setCell(adr('A1'), new ValueCellVertex(42, 42)) + mapping.setCell(adr('A2'), new ValueCellVertex(43, 43)) mapping.addRows(0, 1, 1) - expect(mapping.fetchCell(adr('A1'))).toEqual(new ValueCellVertex(42)) + expect(mapping.fetchCell(adr('A1'))).toEqual(new ValueCellVertex(42, 42)) expect(mapping.getCell(adr('A2'))).toBe(null) - expect(mapping.fetchCell(adr('A3'))).toEqual(new ValueCellVertex(43)) + expect(mapping.fetchCell(adr('A3'))).toEqual(new ValueCellVertex(43, 43)) expect(mapping.getHeight(0)).toEqual(3) }) it('addRows in the end of a mapping', () => { const mapping = builder(1, 1) - mapping.setCell(adr('A1'), new ValueCellVertex(42)) + mapping.setCell(adr('A1'), new ValueCellVertex(42, 42)) mapping.addRows(0, 1, 1) - expect(mapping.fetchCell(adr('A1'))).toEqual(new ValueCellVertex(42)) + expect(mapping.fetchCell(adr('A1'))).toEqual(new ValueCellVertex(42, 42)) expect(mapping.getCell(adr('A2'))).toBe(null) expect(mapping.getHeight(0)).toEqual(2) }) @@ -140,44 +140,44 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('addRows more than one row', () => { const mapping = builder(1, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(42)) - mapping.setCell(adr('A2'), new ValueCellVertex(43)) + mapping.setCell(adr('A1'), new ValueCellVertex(42, 42)) + mapping.setCell(adr('A2'), new ValueCellVertex(43, 43)) mapping.addRows(0, 1, 3) - expect(mapping.fetchCell(adr('A1'))).toEqual(new ValueCellVertex(42)) + expect(mapping.fetchCell(adr('A1'))).toEqual(new ValueCellVertex(42, 42)) expect(mapping.getCell(adr('A2'))).toBe(null) expect(mapping.getCell(adr('A3'))).toBe(null) expect(mapping.getCell(adr('A4'))).toBe(null) - expect(mapping.fetchCell(adr('A5'))).toEqual(new ValueCellVertex(43)) + expect(mapping.fetchCell(adr('A5'))).toEqual(new ValueCellVertex(43, 43)) expect(mapping.getHeight(0)).toEqual(5) }) it('addRows when more than one column present', () => { const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) + mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) + mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) mapping.addRows(0, 1, 1) - expect(mapping.fetchCell(adr('A1'))).toEqual(new ValueCellVertex(11)) - expect(mapping.fetchCell(adr('B1'))).toEqual(new ValueCellVertex(12)) + expect(mapping.fetchCell(adr('A1'))).toEqual(new ValueCellVertex(11, 11)) + expect(mapping.fetchCell(adr('B1'))).toEqual(new ValueCellVertex(12, 12)) expect(mapping.getCell(adr('A2'))).toBe(null) expect(mapping.getCell(adr('B2'))).toBe(null) - expect(mapping.fetchCell(adr('A3'))).toEqual(new ValueCellVertex(21)) - expect(mapping.fetchCell(adr('B3'))).toEqual(new ValueCellVertex(22)) + expect(mapping.fetchCell(adr('A3'))).toEqual(new ValueCellVertex(21, 21)) + expect(mapping.fetchCell(adr('B3'))).toEqual(new ValueCellVertex(22, 22)) expect(mapping.getHeight(0)).toEqual(3) }) it('removeRows - one row', () => { const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) // to remove - mapping.setCell(adr('B1'), new ValueCellVertex(12)) // to remove - mapping.setCell(adr('A2'), new ValueCellVertex(21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) // to remove + mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) // to remove + mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) expect(mapping.getHeight(0)).toBe(2) mapping.removeRows(new RowsSpan(0, 0, 0)) @@ -188,14 +188,14 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('removeRows - more than one row', () => { const mapping = builder(2, 4) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21)) // to - mapping.setCell(adr('B2'), new ValueCellVertex(22)) // re - mapping.setCell(adr('A3'), new ValueCellVertex(31)) // mo - mapping.setCell(adr('B3'), new ValueCellVertex(32)) // ve - mapping.setCell(adr('A4'), new ValueCellVertex(41)) - mapping.setCell(adr('B4'), new ValueCellVertex(42)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) + mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) + mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) // to + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) // re + mapping.setCell(adr('A3'), new ValueCellVertex(31, 31)) // mo + mapping.setCell(adr('B3'), new ValueCellVertex(32, 32)) // ve + mapping.setCell(adr('A4'), new ValueCellVertex(41, 41)) + mapping.setCell(adr('B4'), new ValueCellVertex(42, 42)) expect(mapping.getHeight(0)).toBe(4) mapping.removeRows(new RowsSpan(0, 1, 2)) @@ -206,10 +206,10 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('removeRows - remove more rows thant mapping size', () => { const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) + mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) + mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) expect(mapping.getHeight(0)).toBe(2) mapping.removeRows(new RowsSpan(0, 0, 5)) @@ -219,10 +219,10 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('removeRows - remove more cols than size, but still something left', () => { const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) + mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) + mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) mapping.removeRows(new RowsSpan(0, 1, 5)) @@ -233,10 +233,10 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('removeRows - sometimes nothing is removed', () => { const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) + mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) + mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) mapping.removeRows(new RowsSpan(0, 2, 3)) @@ -247,14 +247,14 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('removeColumns - more than one col', () => { const mapping = builder(4, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) - mapping.setCell(adr('A2'), new ValueCellVertex(12)) - mapping.setCell(adr('B1'), new ValueCellVertex(21)) // to - mapping.setCell(adr('B2'), new ValueCellVertex(22)) // re - mapping.setCell(adr('C1'), new ValueCellVertex(31)) // mo - mapping.setCell(adr('C2'), new ValueCellVertex(32)) // ve - mapping.setCell(adr('D1'), new ValueCellVertex(41)) - mapping.setCell(adr('D2'), new ValueCellVertex(42)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) + mapping.setCell(adr('A2'), new ValueCellVertex(12, 12)) + mapping.setCell(adr('B1'), new ValueCellVertex(21, 21)) // to + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) // re + mapping.setCell(adr('C1'), new ValueCellVertex(31, 31)) // mo + mapping.setCell(adr('C2'), new ValueCellVertex(32, 32)) // ve + mapping.setCell(adr('D1'), new ValueCellVertex(41, 41)) + mapping.setCell(adr('D2'), new ValueCellVertex(42, 42)) expect(mapping.getWidth(0)).toBe(4) mapping.removeColumns(new ColumnsSpan(0, 1, 2)) @@ -265,10 +265,10 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('removeColumns - remove more cols thant mapping size', () => { const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) + mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) + mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) expect(mapping.getHeight(0)).toBe(2) mapping.removeColumns(new ColumnsSpan(0, 0, 5)) @@ -278,10 +278,10 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('removeColumns - remove more cols than size, but still something left', () => { const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) + mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) + mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) mapping.removeColumns(new ColumnsSpan(0, 1, 5)) @@ -292,10 +292,10 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('removeColumns - sometimes nothing is removed', () => { const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) + mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) + mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) mapping.removeColumns(new ColumnsSpan(0, 2, 3)) @@ -328,7 +328,7 @@ describe('SparseStrategy', () => { const mapping = new AddressMapping(new AlwaysSparse()) mapping.addSheet(0, new SparseStrategy(4, 16)) - mapping.setCell(adr('D16'), new ValueCellVertex(42)) + mapping.setCell(adr('D16'), new ValueCellVertex(42, 42)) expect(mapping.getHeight(0)).toEqual(16) expect(mapping.getWidth(0)).toEqual(4) diff --git a/test/engine.spec.ts b/test/engine.spec.ts index 5feed86b73..0f67f710b4 100644 --- a/test/engine.spec.ts +++ b/test/engine.spec.ts @@ -400,7 +400,7 @@ describe('#getCellSerialized', () => { ]) expect(engine.getCellSerialized(adr('A1'))).toEqual(1) - expect(engine.getCellSerialized(adr('B1'))).toEqual(2) + expect(engine.getCellSerialized(adr('B1'))).toEqual('2') expect(engine.getCellSerialized(adr('C1'))).toEqual('foo') expect(engine.getCellSerialized(adr('D1'))).toEqual(true) }) @@ -479,7 +479,7 @@ describe('#getRangeSerialized', () => { const out = engine.getRangeSerialized(adr('A1'), 6, 1) - expectArrayWithSameContent([['=SUM(1, B1)', 2, '#VALUE!', null, '=#DIV/0!', '{=TRANSPOSE(A1:B1)}']], out) + expectArrayWithSameContent([['=SUM(1, B1)', '2', '#VALUE!', null, '=#DIV/0!', '{=TRANSPOSE(A1:B1)}']], out) }) }) diff --git a/test/graph-vertex.spec.ts b/test/graph-vertex.spec.ts index a613976510..8231f7112e 100644 --- a/test/graph-vertex.spec.ts +++ b/test/graph-vertex.spec.ts @@ -6,8 +6,8 @@ describe('Graph with Vertex', () => { it('#addNode works correctly with Vertex instances', () => { const graph = new Graph(dummyGetDependenciesQuery) - const v1 = new ValueCellVertex("1'") - const v2 = new ValueCellVertex('2') + const v1 = new ValueCellVertex("1'", "1'") + const v2 = new ValueCellVertex('2', '2') graph.addNode(v1) graph.addNode(v1) graph.addNode(v2) diff --git a/test/interpreter/precision.spec.ts b/test/interpreter/precision.spec.ts index df78c07a9b..5131381f6d 100644 --- a/test/interpreter/precision.spec.ts +++ b/test/interpreter/precision.spec.ts @@ -181,22 +181,22 @@ describe('Snap to zero', () => { ['=0+0'+chunk1, '=0+0'+chunk2], ], { smartRounding : true}) - expect(engine.dependencyGraph.getCellValue(adr('A1'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B1'))).toEqual(0) - expect(engine.dependencyGraph.getCellValue(adr('A2'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B2'))).toEqual(0) - expect(engine.dependencyGraph.getCellValue(adr('A3'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B3'))).toEqual(0) - expect(engine.dependencyGraph.getCellValue(adr('A4'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B4'))).toEqual(0) - expect(engine.dependencyGraph.getCellValue(adr('A5'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B5'))).toBeCloseTo(0.0000000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('A6'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B6'))).toBeCloseTo(0.0000000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('A7'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B7'))).toBeCloseTo(0.0000000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('A8'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B8'))).toBeCloseTo(0.0000000000001, 5) + expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.0000000001, 5) + expect(engine.getCellValue(adr('B1'))).toEqual(0) + expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0000000001, 5) + expect(engine.getCellValue(adr('B2'))).toEqual(0) + expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.0000000001, 5) + expect(engine.getCellValue(adr('B3'))).toEqual(0) + expect(engine.getCellValue(adr('A4'))).toBeCloseTo(0.0000000001, 5) + expect(engine.getCellValue(adr('B4'))).toEqual(0) + expect(engine.getCellValue(adr('A5'))).toBeCloseTo(0.0000000001, 5) + expect(engine.getCellValue(adr('B5'))).toBeCloseTo(0.0000000000001, 5) + expect(engine.getCellValue(adr('A6'))).toBeCloseTo(0.0000000001, 5) + expect(engine.getCellValue(adr('B6'))).toBeCloseTo(0.0000000000001, 5) + expect(engine.getCellValue(adr('A7'))).toBeCloseTo(0.0000000001, 5) + expect(engine.getCellValue(adr('B7'))).toBeCloseTo(0.0000000000001, 5) + expect(engine.getCellValue(adr('A8'))).toBeCloseTo(0.0000000001, 5) + expect(engine.getCellValue(adr('B8'))).toBeCloseTo(0.0000000000001, 5) }) }) diff --git a/test/serialization.spec.ts b/test/serialization.spec.ts new file mode 100644 index 0000000000..4bff8cacff --- /dev/null +++ b/test/serialization.spec.ts @@ -0,0 +1,73 @@ +import {HyperFormula} from '../src' +import {CellValueDetailedType} from '../src/Cell' +import {adr} from './testUtils' + +describe('serialization', () => { + it('should not loose sheet information on serialization', () => { + const engine1 = HyperFormula.buildFromArray([ + [1, '2', 'foo', true, '\'1', '33$', '12/01/15'] + ]) + + expect(engine1.getCellSerialized(adr('A1'))).toEqual(1) + expect(engine1.getCellValueFormat(adr('A1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('A1'))).toEqual(CellValueDetailedType.NUMBER_RAW) + + expect(engine1.getCellSerialized(adr('B1'))).toEqual('2') + expect(engine1.getCellValueFormat(adr('B1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('B1'))).toEqual(CellValueDetailedType.NUMBER_RAW) + + expect(engine1.getCellSerialized(adr('C1'))).toEqual('foo') + expect(engine1.getCellValueFormat(adr('C1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('C1'))).toEqual(CellValueDetailedType.STRING) + + expect(engine1.getCellSerialized(adr('D1'))).toEqual(true) + expect(engine1.getCellValueFormat(adr('D1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('D1'))).toEqual(CellValueDetailedType.BOOLEAN) + + expect(engine1.getCellSerialized(adr('E1'))).toEqual('\'1') + expect(engine1.getCellValueFormat(adr('E1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('E1'))).toEqual(CellValueDetailedType.STRING) + + expect(engine1.getCellSerialized(adr('F1'))).toEqual('33$') + expect(engine1.getCellValueFormat(adr('F1'))).toEqual('$') + expect(engine1.getCellValueDetailedType(adr('F1'))).toEqual(CellValueDetailedType.NUMBER_CURRENCY) + + expect(engine1.getCellSerialized(adr('G1'))).toEqual('12/01/15') + expect(engine1.getCellValueFormat(adr('G1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('G1'))).toEqual(CellValueDetailedType.NUMBER_DATE) + + // serialize and "send" data to server + const serialized = engine1.getAllSheetsSerialized() + + // reload data and "restore" the previous state + const engine2 = HyperFormula.buildFromSheets(serialized) + + expect(engine2.getCellSerialized(adr('A1'))).toEqual(1) + expect(engine2.getCellValueFormat(adr('A1'))).toEqual(undefined) + expect(engine2.getCellValueDetailedType(adr('A1'))).toEqual(CellValueDetailedType.NUMBER_RAW) + + expect(engine2.getCellSerialized(adr('B1'))).toEqual('2') + expect(engine2.getCellValueFormat(adr('B1'))).toEqual(undefined) + expect(engine2.getCellValueDetailedType(adr('B1'))).toEqual(CellValueDetailedType.NUMBER_RAW) + + expect(engine2.getCellSerialized(adr('C1'))).toEqual('foo') + expect(engine2.getCellValueFormat(adr('C1'))).toEqual(undefined) + expect(engine2.getCellValueDetailedType(adr('C1'))).toEqual(CellValueDetailedType.STRING) + + expect(engine2.getCellSerialized(adr('D1'))).toEqual(true) + expect(engine2.getCellValueFormat(adr('D1'))).toEqual(undefined) + expect(engine2.getCellValueDetailedType(adr('D1'))).toEqual(CellValueDetailedType.BOOLEAN) + + expect(engine2.getCellSerialized(adr('E1'))).toEqual('\'1') + expect(engine2.getCellValueFormat(adr('E1'))).toEqual(undefined) + expect(engine2.getCellValueDetailedType(adr('E1'))).toEqual(CellValueDetailedType.STRING) + + expect(engine2.getCellSerialized(adr('F1'))).toEqual('33$') + expect(engine2.getCellValueFormat(adr('F1'))).toEqual('$') + expect(engine2.getCellValueDetailedType(adr('F1'))).toEqual(CellValueDetailedType.NUMBER_CURRENCY) + + expect(engine2.getCellSerialized(adr('G1'))).toEqual('12/01/15') + expect(engine2.getCellValueFormat(adr('G1'))).toEqual(undefined) + expect(engine2.getCellValueDetailedType(adr('G1'))).toEqual(CellValueDetailedType.NUMBER_DATE) + }) +}) From 706ae9cd89078d48ea75023cadb0ccadfcfa9b73 Mon Sep 17 00:00:00 2001 From: izulin Date: Thu, 8 Apr 2021 18:08:20 +0200 Subject: [PATCH 09/14] minor issue --- test/engine.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/engine.spec.ts b/test/engine.spec.ts index 0f67f710b4..8f6cfe6de7 100644 --- a/test/engine.spec.ts +++ b/test/engine.spec.ts @@ -431,6 +431,7 @@ describe('#getCellSerialized', () => { ['1', '2'], ], {matrixDetection: true, matrixDetectionThreshold: 1}) + //we are losing info about original values for values inside matrices expect(engine.getCellSerialized(adr('A1'))).toEqual(1) expect(engine.getCellSerialized(adr('B1'))).toEqual(2) }) From 45635e03da5bc2b234ebc87f6cff38f401d15c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Uzna=C5=84ski?= <40573492+izulin@users.noreply.github.com> Date: Thu, 8 Apr 2021 20:45:42 +0200 Subject: [PATCH 10/14] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a146f5fd18..dec5923fe9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - GPU.js constructor needs to be provided directly to engine configuration. (#355) -- A `'` symbol on the input is preserved in the value for serialization. (#617) +- An input value should be preserved through serialization more precisely. (#617) ### Added - Added support for row and column reordering. (#343) From 9c46248959f5755153dba15cea14e12bcfd7e762 Mon Sep 17 00:00:00 2001 From: izulin Date: Mon, 12 Apr 2021 13:17:44 +0200 Subject: [PATCH 11/14] small refactor --- src/CellContentParser.ts | 1 - src/ClipboardOperations.ts | 2 +- .../AddressMapping/AddressMapping.ts | 2 +- src/DependencyGraph/DependencyGraph.ts | 13 ++++---- src/DependencyGraph/ValueCellVertex.ts | 33 ++++++++++--------- src/Operations.ts | 22 ++++++------- 6 files changed, 37 insertions(+), 36 deletions(-) diff --git a/src/CellContentParser.ts b/src/CellContentParser.ts index 4d45683588..bf0f4af96e 100644 --- a/src/CellContentParser.ts +++ b/src/CellContentParser.ts @@ -181,4 +181,3 @@ export class CellContentParser { } } } - diff --git a/src/ClipboardOperations.ts b/src/ClipboardOperations.ts index b6f9696e9f..fed79a0c89 100644 --- a/src/ClipboardOperations.ts +++ b/src/ClipboardOperations.ts @@ -31,7 +31,7 @@ export enum ClipboardCellType { export interface ClipboardCellValue { type: ClipboardCellType.VALUE, - value: ValueCellVertexValue, + parsedValue: ValueCellVertexValue, rawValue: RawCellContent, } diff --git a/src/DependencyGraph/AddressMapping/AddressMapping.ts b/src/DependencyGraph/AddressMapping/AddressMapping.ts index 30e70d3801..af79f988cd 100644 --- a/src/DependencyGraph/AddressMapping/AddressMapping.ts +++ b/src/DependencyGraph/AddressMapping/AddressMapping.ts @@ -81,7 +81,7 @@ export class AddressMapping { public getRawValue(address: SimpleCellAddress): RawCellContent { const vertex = this.getCell(address) if(vertex instanceof ValueCellVertex) { - return vertex.getRawValue() + return vertex.getValues().rawValue } else if (vertex instanceof MatrixVertex) { return vertex.getMatrixCellRawValue(address) } else { diff --git a/src/DependencyGraph/DependencyGraph.ts b/src/DependencyGraph/DependencyGraph.ts index b25da304e3..4e7cf78936 100644 --- a/src/DependencyGraph/DependencyGraph.ts +++ b/src/DependencyGraph/DependencyGraph.ts @@ -39,7 +39,7 @@ import {Graph, TopSortResult} from './Graph' import {MatrixMapping} from './MatrixMapping' import {RangeMapping} from './RangeMapping' import {SheetMapping} from './SheetMapping' -import {ValueCellVertexValue} from './ValueCellVertex' +import {RawAndParsedValue, ValueCellVertexValue} from './ValueCellVertex' import {FunctionRegistry} from '../interpreter/FunctionRegistry' import { EmptyValue, @@ -113,19 +113,18 @@ export class DependencyGraph { this.correctInfiniteRangesDependency(address) } - public setValueToCell(address: SimpleCellAddress, newValue: ValueCellVertexValue, rawValue: RawCellContent) { + public setValueToCell(address: SimpleCellAddress, value: RawAndParsedValue) { const vertex = this.addressMapping.getCell(address) this.ensureThatVertexIsNonMatrixCellVertex(vertex) if (vertex instanceof ValueCellVertex) { - const oldValue = vertex.getCellValue() - if (oldValue !== newValue) { - vertex.setCellValue(newValue) - vertex.setRawValue(rawValue) + const oldValue = vertex.getValues() + if (oldValue.rawValue !== value.rawValue) { + vertex.setValues(value) this.graph.markNodeAsSpecialRecentlyChanged(vertex) } } else { - const newVertex = new ValueCellVertex(newValue, rawValue) + const newVertex = new ValueCellVertex(value.parsedValue, value.rawValue) this.exchangeOrAddGraphNode(vertex, newVertex) this.addressMapping.setCell(address, newVertex) this.graph.markNodeAsSpecialRecentlyChanged(newVertex) diff --git a/src/DependencyGraph/ValueCellVertex.ts b/src/DependencyGraph/ValueCellVertex.ts index ebb8142a44..0ca172ad17 100644 --- a/src/DependencyGraph/ValueCellVertex.ts +++ b/src/DependencyGraph/ValueCellVertex.ts @@ -9,33 +9,36 @@ import {ExtendedNumber, RawScalarValue} from '../interpreter/InterpreterValue' export type ValueCellVertexValue = ExtendedNumber | boolean | string | CellError +export interface RawAndParsedValue { + parsedValue: ValueCellVertexValue, + rawValue: RawCellContent, +} + /** * Represents vertex which keeps static cell value */ export class ValueCellVertex { /** Static cell value. */ - constructor(private cellValue: ValueCellVertexValue, private rawValue: RawCellContent) { + constructor(private parsedValue: ValueCellVertexValue, private rawValue: RawCellContent) { } - /** - * Returns cell value stored in vertex - */ - public getCellValue(): ValueCellVertexValue { - return this.cellValue + public getValues(): RawAndParsedValue { + return {parsedValue: this.parsedValue, rawValue: this.rawValue} } - /** - * Sets computed cell value stored in this vertex - */ - public setCellValue(cellValue: ValueCellVertexValue) { - this.cellValue = cellValue + public setValues(values: RawAndParsedValue) { + this.parsedValue = values.parsedValue + this.rawValue = values.rawValue } - public getRawValue(): RawCellContent { - return this.rawValue + /** + * Returns cell value stored in vertex + */ + public getCellValue(): ValueCellVertexValue { + return this.parsedValue } - public setRawValue(rawValue: RawCellContent) { - this.rawValue = rawValue + public setCellValue(_cellValue: ValueCellVertexValue): never { + throw "SetCellValue is deprecated for ValueCellVertex" } } diff --git a/src/Operations.ts b/src/Operations.ts index 2fe8da342b..3eb3be190c 100644 --- a/src/Operations.ts +++ b/src/Operations.ts @@ -24,7 +24,7 @@ import { SparseStrategy, ValueCellVertex } from './DependencyGraph' -import {ValueCellVertexValue} from './DependencyGraph/ValueCellVertex' +import {RawAndParsedValue, ValueCellVertexValue} from './DependencyGraph/ValueCellVertex' import {AddColumnsTransformer} from './dependencyTransformers/AddColumnsTransformer' import {AddRowsTransformer} from './dependencyTransformers/AddRowsTransformer' import {MoveCellsTransformer} from './dependencyTransformers/MoveCellsTransformer' @@ -444,7 +444,7 @@ export class Operations { public restoreCell(address: SimpleCellAddress, clipboardCell: ClipboardCell) { switch (clipboardCell.type) { case ClipboardCellType.VALUE: { - this.setValueToCell(clipboardCell.value, clipboardCell.rawValue, address) + this.setValueToCell(clipboardCell, address) break } case ClipboardCellType.FORMULA: { @@ -577,9 +577,9 @@ export class Operations { if (vertex === null || vertex instanceof EmptyCellVertex) { return {type: ClipboardCellType.EMPTY} } else if (vertex instanceof ValueCellVertex) { - return {type: ClipboardCellType.VALUE, value: vertex.getCellValue(), rawValue: vertex.getRawValue()} + return {type: ClipboardCellType.VALUE, ...vertex.getValues()} } else if (vertex instanceof MatrixVertex) { - return {type: ClipboardCellType.VALUE, value: vertex.getMatrixCellValue(address), rawValue: vertex.getMatrixCellRawValue(address)} + return {type: ClipboardCellType.VALUE, parsedValue: vertex.getMatrixCellValue(address), rawValue: vertex.getMatrixCellRawValue(address)} } else if (vertex instanceof FormulaCellVertex) { return { type: ClipboardCellType.FORMULA, @@ -659,7 +659,7 @@ export class Operations { } else if (parsedCellContent instanceof CellContent.MatrixFormula) { throw new Error('Cant happen') } else { - this.setValueToCell(parsedCellContent.value, newCellContent, address) + this.setValueToCell({parsedValue: parsedCellContent.value, rawValue: newCellContent}, address) } } else { throw new Error('Illegal operation') @@ -676,11 +676,11 @@ export class Operations { } } - public setValueToCell(value: ValueCellVertexValue, rawValue: RawCellContent, address: SimpleCellAddress) { + public setValueToCell(value: RawAndParsedValue, address: SimpleCellAddress) { const oldValue = this.dependencyGraph.getCellValue(address) - this.dependencyGraph.setValueToCell(address, value, rawValue) - this.columnSearch.change(getRawValue(oldValue), getRawValue(value), address) - this.changes.addChange(value, address) + this.dependencyGraph.setValueToCell(address, value) + this.columnSearch.change(getRawValue(oldValue), getRawValue(value.parsedValue), address) + this.changes.addChange(value.parsedValue, address) } public setCellEmpty(address: SimpleCellAddress) { @@ -776,7 +776,7 @@ export class Operations { if (parsedCellContent instanceof CellContent.Empty) { this.setCellEmpty(address) } else { - this.setValueToCell(parsedCellContent.value, expression, address) + this.setValueToCell({parsedValue: parsedCellContent.value, rawValue: expression}, address) } } } @@ -847,7 +847,7 @@ export class Operations { } else if (sourceVertex instanceof EmptyCellVertex) { this.setCellEmpty(expression.address) } else if (sourceVertex instanceof ValueCellVertex) { - this.setValueToCell(sourceVertex.getCellValue(), sourceVertex.getRawValue(), expression.address) + this.setValueToCell(sourceVertex.getValues(), expression.address) } } return this.dependencyGraph.fetchCellOrCreateEmpty(expression.address) From ab20ae8a7f64217dba0bbef8be73659b4012df66 Mon Sep 17 00:00:00 2001 From: izulin Date: Mon, 12 Apr 2021 13:20:01 +0200 Subject: [PATCH 12/14] linter --- src/DependencyGraph/ValueCellVertex.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DependencyGraph/ValueCellVertex.ts b/src/DependencyGraph/ValueCellVertex.ts index 0ca172ad17..1c855935a0 100644 --- a/src/DependencyGraph/ValueCellVertex.ts +++ b/src/DependencyGraph/ValueCellVertex.ts @@ -39,6 +39,6 @@ export class ValueCellVertex { } public setCellValue(_cellValue: ValueCellVertexValue): never { - throw "SetCellValue is deprecated for ValueCellVertex" + throw 'SetCellValue is deprecated for ValueCellVertex' } } From b749c7faf07228d8f5ce8794a0d405d68f4a5db1 Mon Sep 17 00:00:00 2001 From: izulin Date: Wed, 14 Apr 2021 11:10:58 +0200 Subject: [PATCH 13/14] fixes to tests --- test/serialization.spec.ts | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/test/serialization.spec.ts b/test/serialization.spec.ts index 4bff8cacff..4445b37398 100644 --- a/test/serialization.spec.ts +++ b/test/serialization.spec.ts @@ -5,7 +5,7 @@ import {adr} from './testUtils' describe('serialization', () => { it('should not loose sheet information on serialization', () => { const engine1 = HyperFormula.buildFromArray([ - [1, '2', 'foo', true, '\'1', '33$', '12/01/15'] + [1, '2', 'foo', true, '\'1', '33$', '12/01/15', '1%', '=FOO(', '#DIV/0!', ] ]) expect(engine1.getCellSerialized(adr('A1'))).toEqual(1) @@ -33,9 +33,21 @@ describe('serialization', () => { expect(engine1.getCellValueDetailedType(adr('F1'))).toEqual(CellValueDetailedType.NUMBER_CURRENCY) expect(engine1.getCellSerialized(adr('G1'))).toEqual('12/01/15') - expect(engine1.getCellValueFormat(adr('G1'))).toEqual(undefined) + expect(engine1.getCellValueFormat(adr('G1'))).toEqual(undefined) //this is wrong, see issue 626 expect(engine1.getCellValueDetailedType(adr('G1'))).toEqual(CellValueDetailedType.NUMBER_DATE) + expect(engine1.getCellSerialized(adr('H1'))).toEqual('1%') + expect(engine1.getCellValueFormat(adr('H1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('H1'))).toEqual(CellValueDetailedType.NUMBER_PERCENT) + + expect(engine1.getCellSerialized(adr('I1'))).toEqual('=FOO(') + expect(engine1.getCellValueFormat(adr('I1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('I1'))).toEqual(CellValueDetailedType.ERROR) + + expect(engine1.getCellSerialized(adr('J1'))).toEqual('#DIV/0!') + expect(engine1.getCellValueFormat(adr('J1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('J1'))).toEqual(CellValueDetailedType.ERROR) + // serialize and "send" data to server const serialized = engine1.getAllSheetsSerialized() @@ -67,7 +79,19 @@ describe('serialization', () => { expect(engine2.getCellValueDetailedType(adr('F1'))).toEqual(CellValueDetailedType.NUMBER_CURRENCY) expect(engine2.getCellSerialized(adr('G1'))).toEqual('12/01/15') - expect(engine2.getCellValueFormat(adr('G1'))).toEqual(undefined) + expect(engine2.getCellValueFormat(adr('G1'))).toEqual(undefined) //this is wrong, see issue 626 expect(engine2.getCellValueDetailedType(adr('G1'))).toEqual(CellValueDetailedType.NUMBER_DATE) + + expect(engine1.getCellSerialized(adr('H1'))).toEqual('1%') + expect(engine1.getCellValueFormat(adr('H1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('H1'))).toEqual(CellValueDetailedType.NUMBER_PERCENT) + + expect(engine1.getCellSerialized(adr('I1'))).toEqual('=FOO(') + expect(engine1.getCellValueFormat(adr('I1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('I1'))).toEqual(CellValueDetailedType.ERROR) + + expect(engine1.getCellSerialized(adr('J1'))).toEqual('#DIV/0!') + expect(engine1.getCellValueFormat(adr('J1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('J1'))).toEqual(CellValueDetailedType.ERROR) }) }) From cebeeca38e120de197f8ad74c28708d9c9bbbef5 Mon Sep 17 00:00:00 2001 From: izulin Date: Wed, 14 Apr 2021 11:12:45 +0200 Subject: [PATCH 14/14] doc --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0dcc160e2..1ede7334bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - GPU.js constructor needs to be provided directly to engine configuration. (#355) -- An input value should be preserved through serialization more precisely. (#617) +- An input value should be preserved through serialization more precisely. A **Breaking change**: This changes the type of value returned via serialization. (#617) - A deprecated config option vlookupThreshold has been removed. (#620) ### Added