-
Notifications
You must be signed in to change notification settings - Fork 124
/
Copy pathClipboardOperations.ts
144 lines (119 loc) · 4.15 KB
/
ClipboardOperations.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/**
* @license
* Copyright (c) 2025 Handsoncode. All rights reserved.
*/
import {AbsoluteCellRange} from './AbsoluteCellRange'
import {invalidSimpleCellAddress, simpleCellAddress, SimpleCellAddress} from './Cell'
import {RawCellContent} from './CellContentParser'
import {Config} from './Config'
import {DependencyGraph} from './DependencyGraph'
import {ValueCellVertexValue} from './DependencyGraph/ValueCellVertex'
import {InvalidArgumentsError, SheetSizeLimitExceededError} from './errors'
import {Operations} from './Operations'
import {ParsingError} from './parser/Ast'
export type ClipboardCell = ClipboardCellValue | ClipboardCellFormula | ClipboardCellEmpty | ClipboardCellParsingError
enum ClipboardOperationType {
COPY,
CUT,
}
export enum ClipboardCellType {
VALUE,
EMPTY,
FORMULA,
PARSING_ERROR,
}
export interface ClipboardCellValue {
type: ClipboardCellType.VALUE,
parsedValue: ValueCellVertexValue,
rawValue: RawCellContent,
}
export interface ClipboardCellEmpty {
type: ClipboardCellType.EMPTY,
}
export interface ClipboardCellFormula {
type: ClipboardCellType.FORMULA,
hash: string,
}
export interface ClipboardCellParsingError {
type: ClipboardCellType.PARSING_ERROR,
rawInput: string,
errors: ParsingError[],
}
class Clipboard {
constructor(
public readonly sourceLeftCorner: SimpleCellAddress,
public readonly width: number,
public readonly height: number,
public readonly type: ClipboardOperationType,
public readonly content?: ClipboardCell[][],
) {
}
public* getContent(leftCorner: SimpleCellAddress): IterableIterator<[SimpleCellAddress, ClipboardCell]> {
if (this.content === undefined) {
return
} else {
for (let y = 0; y < this.height; ++y) {
for (let x = 0; x < this.width; ++x) {
yield [simpleCellAddress(leftCorner.sheet, leftCorner.col + x, leftCorner.row + y), this.content[y][x]]
}
}
}
}
}
export class ClipboardOperations {
public clipboard?: Clipboard
private maxRows: number
private maxColumns: number
constructor(
config: Config,
private readonly dependencyGraph: DependencyGraph,
private readonly operations: Operations,
) {
this.maxRows = config.maxRows
this.maxColumns = config.maxColumns
}
public cut(leftCorner: SimpleCellAddress, width: number, height: number): void {
this.clipboard = new Clipboard(leftCorner, width, height, ClipboardOperationType.CUT)
}
public copy(leftCorner: SimpleCellAddress, width: number, height: number): void {
const content: ClipboardCell[][] = []
for (let y = 0; y < height; ++y) {
content[y] = []
for (let x = 0; x < width; ++x) {
const clipboardCell = this.operations.getClipboardCell(simpleCellAddress(leftCorner.sheet, leftCorner.col + x, leftCorner.row + y))
content[y].push(clipboardCell)
}
}
this.clipboard = new Clipboard(leftCorner, width, height, ClipboardOperationType.COPY, content)
}
public abortCut(): void {
if (this.clipboard && this.clipboard.type === ClipboardOperationType.CUT) {
this.clear()
}
}
public clear(): void {
this.clipboard = undefined
}
public ensureItIsPossibleToCopyPaste(destinationLeftCorner: SimpleCellAddress): void {
if (this.clipboard === undefined) {
return
}
if (invalidSimpleCellAddress(destinationLeftCorner) ||
!this.dependencyGraph.sheetMapping.hasSheetWithId(destinationLeftCorner.sheet)) {
throw new InvalidArgumentsError('a valid target address.')
}
const targetRange = AbsoluteCellRange.spanFrom(destinationLeftCorner, this.clipboard.width, this.clipboard.height)
if (targetRange.exceedsSheetSizeLimits(this.maxColumns, this.maxRows)) {
throw new SheetSizeLimitExceededError()
}
if (this.dependencyGraph.arrayMapping.isFormulaArrayInRange(targetRange)) {
throw new Error('It is not possible to paste onto an array')
}
}
public isCutClipboard(): boolean {
return this.clipboard !== undefined && this.clipboard.type === ClipboardOperationType.CUT
}
public isCopyClipboard(): boolean {
return this.clipboard !== undefined && this.clipboard.type === ClipboardOperationType.COPY
}
}