Skip to content

Commit

Permalink
Array CRUDs improvements (part #1) (#708)
Browse files Browse the repository at this point in the history
* Tranpose works with any type

* CHANGELOG

* Remove obsolete test

* Allow to move cells in address mapping

* Compare matrices in graph comparator

* Adjust matrices address mapping after adding row

* TODO

* Obsolete test

* Fix cherry-pick

* Compare matrix mappings in graph comparator

* Remove unused code

* Add simple tests

* Move MatrixMapping entries when adding rows

* Spike

* Exchanging matrix vertex works

* Fix addres mapping for simple case of removing row

* Make matrix REF if no space after removing row

* Tidy up

* Shrink matrix before swap

* Fix gathering matrices related to ranges

* Basic tests for removing rows works

* Test with potential CYCLE

* Simplify condition by making use of MatrixSize function

* Reuse and simplify

* Basic cases for setCellContent

* Set no space if writing over matrix

* Shrink matrix during building graph if needed

* Tests cleanup

* Allow to setCellContent onto matrix

* Replacing matrix should work

* We do not forbid to change cell content any more when there is array

* Remove matrix when removing row with corner

* Do not change size of a REFed matrix

* Test for references

* Add test specially for dep correction

* Compare only ref flag

* Fix adjusting edges

* Skipped test for later

* just setNoSpace()

* Test for undo redo and changes

* Keep changes with old content in map

* Ensure range content changes are properly exported

* More specs

* Use string key in ContentChanges

* Gather changes from dependency graph

* Do nothing when trying to set internal array cell empty

* Track SimpleRangeValue changes

* Compare raw input of parsing errors

* Handle ParsingError case

* Cleanup

* Working undo/redo

* Do not allow to move rows inside array

* Update js docs

* Fix rebase

* imports

* Enable tests

* ColumnIndex specs for arrays

* Fix test

* Ensure DependencyGraph changes are cleared after building graph

* Ignore EmptyValue in columnIndex

* Apply array changes in all cases

* Gather changes when removing rows

* Make sure range edges are adjusted properly

* Linter

* Remove console.log

* Cleanup

* Fix lgtm complaints

* Fix browser tests

* Adjust tsdoc

* Update CHANGELOG

* Adjust tsdocs

* SPILL array error

* SPILL instead of REF

* CHANGELOG

* CHANGELOG

* Unused import

Co-authored-by: izulin <izulin@gmail.com>
  • Loading branch information
voodoo11 and izulin committed Jul 5, 2021
1 parent 880126e commit 72862d7
Show file tree
Hide file tree
Showing 53 changed files with 2,151 additions and 562 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Expand Up @@ -11,9 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Breaking change**: Removed support for matrix formulas (`{=FORMULA}`) notation. Engine now supports formulas returning array of values (instead of only scalars). (#652)
- **Breaking change**: Removed numeric matrix detection along with matrixDetection and matrixDetectionThreshold config options. (#669)
- **Breaking change**: Changed API of the following methods to take `SimpleCellRange` type argument: `copy`, `cut`, `getCellDependents`, `getCellPrecedents`, `getFillRangeData`, `getRangeFormulas`, `getRangeSerialized`, `getRangeValues`, `isItPossibleToMoveCells`, `isItPossibleToSetCellContents`, `moveCells`. (#687)
- Changed SWITCH function so it takes array as its first argument.
- **Breaking change**: Changed the AGPLv3 license to GPLv3.
- **Breaking change**: Removed the free non-commercial license.
- **Breaking change**: Changed behaviour of `setCellContents` so that it is possible to override space occupied by spilled array. (#708)
- **Breaking change**: Changed behaviour of `addRows/removeRows` so that it is possible to add/remove rows across spilled array without changing array size. (#708)
- Changed SWITCH function, so it takes array as its first argument.
- Changed TRANSPOSE function, so it works with data of any type. (#708)

### Added
- Added support for array arithmetic. (#628)
Expand All @@ -34,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added pretty print for detailedCellError. (#712)
- Added `simpleCellRangeFromString` and `simpleCellRangeToString` helpers. (#720)
- Added `CellError` to exports. (#736)
- Added `#SPILL!` error type. (#708)

### Fixed
- Fixed an issue with arrays and cruds. (#651)
Expand Down
26 changes: 12 additions & 14 deletions src/AbsoluteCellRange.ts
Expand Up @@ -4,7 +4,9 @@
*/

import {
CellRange, isSimpleCellAddress,
CellRange,
equalSimpleCellAddress,
isSimpleCellAddress,
simpleCellAddress,
SimpleCellAddress,
SimpleColumnAddress,
Expand All @@ -13,7 +15,7 @@ import {
import {DependencyGraph} from './DependencyGraph'
import {SheetsNotEqual} from './errors'
import {Maybe} from './Maybe'
import {AstNodeType, CellAddress, CellRangeAst} from './parser'
import {AstNodeType, CellRangeAst} from './parser'
import {ColumnRangeAst, RowRangeAst} from './parser/Ast'
import {RowsSpan, Span} from './Span'

Expand Down Expand Up @@ -94,11 +96,6 @@ export class AbsoluteCellRange implements SimpleCellRange {
return new AbsoluteCellRange(simpleCellAddress(sheet, x1, y1), simpleCellAddress(sheet, x2, y2))
}

public static singleRangeFromCellAddress(cellAddress: CellAddress, baseAddress: SimpleCellAddress): AbsoluteCellRange {
const simpleCellAddress = cellAddress.toSimpleCellAddress(baseAddress)
return new AbsoluteCellRange(simpleCellAddress, simpleCellAddress)
}

constructor(
start: SimpleCellAddress,
end: SimpleCellAddress,
Expand Down Expand Up @@ -132,12 +129,10 @@ export class AbsoluteCellRange implements SimpleCellRange {
return false
}

if (this.start.row <= address.row && this.end.row >= address.row
&& this.start.col <= address.col && this.end.col >= address.col) {
return true
}

return false
return this.start.row <= address.row
&& this.end.row >= address.row
&& this.start.col <= address.col
&& this.end.col >= address.col
}

public columnInRange(address: SimpleColumnAddress): boolean {
Expand Down Expand Up @@ -301,6 +296,10 @@ export class AbsoluteCellRange implements SimpleCellRange {
return this.width() === other.width() && this.height() === other.height()
}

public sameAs(other: AbsoluteCellRange) {
return equalSimpleCellAddress(this.start, other.start) && equalSimpleCellAddress(this.end, other.end)
}

public addressesArrayMap<T>(dependencyGraph: DependencyGraph, op: (arg: SimpleCellAddress) => T): T[][] {
const ret = []
let currentRow = this.start.row
Expand Down Expand Up @@ -418,7 +417,6 @@ export class AbsoluteColumnRange extends AbsoluteCellRange {
return this.width() <= 0
}


public shiftByRows(_numberOfRows: number) {
return
}
Expand Down
30 changes: 16 additions & 14 deletions src/Cell.ts
Expand Up @@ -3,14 +3,7 @@
* Copyright (c) 2021 Handsoncode. All rights reserved.
*/

import {
CellVertex,
FormulaCellVertex,
MatrixVertex,
ParsingErrorVertex,
ValueCellVertex,
Vertex
} from './DependencyGraph'
import {CellVertex, FormulaCellVertex, MatrixVertex, ParsingErrorVertex, ValueCellVertex} from './DependencyGraph'
import {ErrorMessage} from './error-message'
import {
EmptyValue,
Expand Down Expand Up @@ -44,6 +37,9 @@ export enum ErrorType {
/** Wrong address reference. */
REF = 'REF',

/** Array spill error. */
SPILL = 'SPILL',

/** Invalid/missing licence error. */
LIC = 'LIC',

Expand Down Expand Up @@ -117,27 +113,27 @@ export const getCellValueType = (cellValue: InterpreterValue): CellValueType =>
return CellValueType.ERROR
}

if(typeof cellValue === 'string') {
if (typeof cellValue === 'string') {
return CellValueType.STRING
} else if(isExtendedNumber(cellValue)) {
} else if (isExtendedNumber(cellValue)) {
return CellValueType.NUMBER
} else if(typeof cellValue === 'boolean') {
} else if (typeof cellValue === 'boolean') {
return CellValueType.BOOLEAN
}

throw new Error('Cell value not computed')
}

export const getCellValueDetailedType = (cellValue: InterpreterValue): CellValueDetailedType => {
if(isExtendedNumber(cellValue)) {
if (isExtendedNumber(cellValue)) {
return getTypeOfExtendedNumber(cellValue)
} else {
return getCellValueType(cellValue) as CellValueDetailedType
}
}

export const getCellValueFormat = (cellValue: InterpreterValue): string | undefined => {
if(isExtendedNumber(cellValue)) {
if (isExtendedNumber(cellValue)) {
return getFormatOfExtendedNumber(cellValue)
} else {
return undefined
Expand Down Expand Up @@ -195,8 +191,10 @@ export const movedSimpleCellAddress = (address: SimpleCellAddress, toSheet: numb
return simpleCellAddress(toSheet, address.col + toRight, address.row + toBottom)
}

export const addressKey = (address: SimpleCellAddress) => `${address.sheet},${address.row},${address.col}`

export function isSimpleCellAddress(obj: any): obj is SimpleCellAddress {
if( obj && (typeof obj === 'object' || typeof obj === 'function')) {
if (obj && (typeof obj === 'object' || typeof obj === 'function')) {
return 'col' in obj && typeof obj.col === 'number' && 'row' in obj && typeof obj.row === 'number' && 'sheet' in obj && typeof obj.sheet === 'number'
} else {
return false
Expand All @@ -207,6 +205,10 @@ export const absoluteSheetReference = (address: AddressWithSheet, baseAddress: S
return address.sheet ?? baseAddress.sheet
}

export const equalSimpleCellAddress = (left: SimpleCellAddress, right: SimpleCellAddress) => {
return left.sheet === right.sheet && left.col === right.col && left.row === right.row
}

export interface SheetCellAddress {
col: number,
row: number,
Expand Down
48 changes: 28 additions & 20 deletions src/ContentChanges.ts
Expand Up @@ -3,14 +3,14 @@
* Copyright (c) 2021 Handsoncode. All rights reserved.
*/

import {SimpleCellAddress} from './Cell'
import {addressKey, SimpleCellAddress} from './Cell'
import {InterpreterValue} from './interpreter/InterpreterValue'
import {SimpleRangeValue} from './interpreter/SimpleRangeValue'

export interface CellValueChange {
sheet: number,
row: number,
col: number,
address: SimpleCellAddress,
value: InterpreterValue,
oldValue?: InterpreterValue,
}

export interface ChangeExporter<T> {
Expand All @@ -20,24 +20,23 @@ export interface ChangeExporter<T> {
export type ChangeList = CellValueChange[]

export class ContentChanges {

public static empty() {
return new ContentChanges()
}

private changes: ChangeList = []
private constructor() {}

private changes: Map<string, CellValueChange> = new Map()

public addAll(other: ContentChanges): ContentChanges {
this.changes.push(...other.changes)
for (const change of other.changes.values()) {
this.add(change.address, change)
}
return this
}

public addChange(newValue: InterpreterValue, address: SimpleCellAddress): void {
this.addSingleCellValue(newValue, address)
}

public add(...change: ChangeList) {
this.changes.push(...change)
public addChange(newValue: InterpreterValue, address: SimpleCellAddress, oldValue?: InterpreterValue): void {
this.addInterpreterValue(newValue, address, oldValue)
}

public exportChanges<T>(exporter: ChangeExporter<T>): T[] {
Expand All @@ -54,19 +53,28 @@ export class ContentChanges {
}

public getChanges(): ChangeList {
return this.changes
return Array.from(this.changes.values())
}

public isEmpty(): boolean {
return this.changes.length === 0
return this.changes.size === 0
}

private add(address: SimpleCellAddress, change: CellValueChange) {
const value = change.value
if (value instanceof SimpleRangeValue) {
for (const cellAddress of value.effectiveAddressesFromData(address)) {
this.changes.delete(`${cellAddress.sheet},${cellAddress.col},${cellAddress.row}`)
}
}
this.changes.set(addressKey((address)), change)
}

private addSingleCellValue(value: InterpreterValue, address: SimpleCellAddress) {
this.add({
sheet: address.sheet,
col: address.col,
row: address.row,
private addInterpreterValue(value: InterpreterValue, address: SimpleCellAddress, oldValue?: InterpreterValue) {
this.add(address, {
address,
value,
oldValue
})
}
}

0 comments on commit 72862d7

Please sign in to comment.