Skip to content

Commit 59e8c4f

Browse files
committed
imp(batchedOp): improvements
1 parent 2a339e1 commit 59e8c4f

File tree

3 files changed

+52
-51
lines changed

3 files changed

+52
-51
lines changed

packages/collaboration-manager/src/BatchedOperation.ts

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
1-
import { InvertedOperationType, Operation, OperationType, SerializedOperation } from './Operation.js';
2-
3-
/**
4-
* Batch debounce time
5-
*/
6-
const DEBOUNCE_TIMEOUT = 500;
1+
import { InvertedOperationType, Operation, OperationType, type SerializedOperation } from './Operation.js';
72

83
/**
94
* Class to batch Text operations (maybe others in the future) for Undo/Redo purposes
105
*
116
* Operations are batched on timeout basis or if batch is terminated from the outside
127
*/
13-
export class BatchedOperation<T extends OperationType> extends Operation<T> {
8+
export class BatchedOperation<T extends OperationType = OperationType> extends Operation<T> {
149
/**
1510
* Array of operations to batch
1611
*/
@@ -29,16 +24,6 @@ export class BatchedOperation<T extends OperationType> extends Operation<T> {
2924
}
3025
}
3126

32-
/**
33-
* Adds an operation to the batch
34-
* Make sure, that operation could be added to the batch
35-
*
36-
* @param op - operation to add
37-
*/
38-
public add(op: Operation<T> | Operation<OperationType.Neutral>): void {
39-
this.operations.push(op);
40-
}
41-
4227
/**
4328
* Create a new operation batch from an array of operations
4429
*
@@ -63,7 +48,7 @@ export class BatchedOperation<T extends OperationType> extends Operation<T> {
6348
/**
6449
* Every batch should have at least one operation
6550
*/
66-
const batch = new BatchedOperation(opBatchOrJSON.operations.shift()!);
51+
const batch = new BatchedOperation(Operation.from(opBatchOrJSON.operations.shift()!));
6752

6853
opBatchOrJSON.operations.forEach((op) => {
6954
/**
@@ -80,6 +65,16 @@ export class BatchedOperation<T extends OperationType> extends Operation<T> {
8065
}
8166
}
8267

68+
/**
69+
* Adds an operation to the batch
70+
* Make sure, that operation could be added to the batch
71+
*
72+
* @param op - operation to add
73+
*/
74+
public add(op: Operation<T> | Operation<OperationType.Neutral>): void {
75+
this.operations.push(op);
76+
}
77+
8378
/**
8479
* Method that inverses all of the operations in the batch
8580
*
@@ -91,11 +86,7 @@ export class BatchedOperation<T extends OperationType> extends Operation<T> {
9186
*/
9287
const newBatchedOperation = new BatchedOperation<InvertedOperationType<T> | OperationType.Neutral>(this.operations.pop()!.inverse())
9388

94-
while (this.operations.length > 0) {
95-
const op = this.operations.pop()!.inverse();
96-
97-
newBatchedOperation.add(op);
98-
}
89+
this.operations.toReversed().map(op => newBatchedOperation.add(op.inverse()));
9990

10091
return newBatchedOperation as BatchedOperation<InvertedOperationType<T>>;
10192
}
@@ -110,15 +101,8 @@ export class BatchedOperation<T extends OperationType> extends Operation<T> {
110101
const transformedOp = this.operations.shift()!.transform(againstOp);
111102

112103
const newBatchedOperation = new BatchedOperation(transformedOp);
113-
114-
/**
115-
* We either have a new operations batch or all operations were not transformable
116-
*/
117-
for (const op of this.operations) {
118-
const transformedOp = op.transform(againstOp);
119104

120-
newBatchedOperation.add(transformedOp);
121-
}
105+
this.operations.map(op => newBatchedOperation.add(op.transform(againstOp)));
122106

123107
return newBatchedOperation;
124108
}

packages/collaboration-manager/src/CollaborationManager.ts

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,7 @@ export class CollaborationManager {
9191
* Undo last operation in the local stack
9292
*/
9393
public undo(): void {
94-
if (this.#currentBatch !== null) {
95-
this.#undoRedoManager.put(this.#currentBatch);
96-
97-
this.#currentBatch = null;
98-
}
94+
this.#emptyBatch();
9995

10096
const operation = this.#undoRedoManager.undo();
10197

@@ -106,13 +102,7 @@ export class CollaborationManager {
106102
// Disable handling
107103
this.#shouldHandleEvents = false;
108104

109-
if (operation instanceof BatchedOperation) {
110-
operation.operations.forEach((op) => {
111-
this.applyOperation(op);
112-
});
113-
} else {
114-
this.applyOperation(operation);
115-
}
105+
this.applyOperation(operation);
116106

117107
// Re-enable event handling
118108
this.#shouldHandleEvents = true;
@@ -122,11 +112,7 @@ export class CollaborationManager {
122112
* Redo last undone operation in the local stack
123113
*/
124114
public redo(): void {
125-
if (this.#currentBatch !== null) {
126-
this.#undoRedoManager.put(this.#currentBatch);
127-
128-
this.#currentBatch = null;
129-
}
115+
this.#emptyBatch();
130116

131117
const operation = this.#undoRedoManager.redo();
132118

@@ -154,7 +140,16 @@ export class CollaborationManager {
154140
*
155141
* @param operation - operation to apply
156142
*/
157-
public applyOperation(operation: Operation): void {
143+
public applyOperation(operation: Operation | BatchedOperation): void {
144+
/**
145+
* If operation is a batcher operation, apply all operations in the batch
146+
*/
147+
if (operation instanceof BatchedOperation) {
148+
operation.operations.forEach(op => this.applyOperation(op));
149+
150+
return;
151+
}
152+
158153
if (operation.type === OperationType.Neutral) {
159154
return;
160155
}
@@ -244,7 +239,7 @@ export class CollaborationManager {
244239

245240
/**
246241
* If we got a new remote operation - transform current batch
247-
* If batch is not transormable - clear it
242+
* If batch is empty leave it as it is
248243
*/
249244
this.#currentBatch = this.#currentBatch?.transform(operation) ?? null;
250245

@@ -266,6 +261,7 @@ export class CollaborationManager {
266261

267262
/**
268263
* If current operation could not be added to the batch, then terminate current batch and create a new one with current operation
264+
* @todo - add debounce timeout 500ms
269265
*/
270266
if (!this.#currentBatch.canAdd(operation)) {
271267
this.#undoRedoManager.put(this.#currentBatch);
@@ -277,4 +273,15 @@ export class CollaborationManager {
277273

278274
this.#currentBatch.add(operation);
279275
}
276+
277+
/**
278+
* Puts current batch to the undo stack and clears the batch
279+
*/
280+
#emptyBatch(): void {
281+
if (this.#currentBatch !== null) {
282+
this.#undoRedoManager.put(this.#currentBatch);
283+
284+
this.#currentBatch = null;
285+
}
286+
}
280287
}

packages/collaboration-manager/src/OperationsTransformer.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,20 @@ export class OperationsTransformer {
155155
* @returns New operation
156156
*/
157157
#transformAgainstTextOperation<T extends OperationType>(operation: Operation<T>, againstOp: Operation<OperationType>): Operation<T> | Operation<OperationType.Neutral> {
158+
const index = operation.index;
159+
const againstIndex = againstOp.index;
160+
158161
/**
159162
* Cover case 1
160163
*/
161-
if (operation.index.isBlockIndex) {
164+
if (index.isBlockIndex) {
165+
return Operation.from(operation);
166+
}
167+
168+
/**
169+
* Check that againstOp affects current operation
170+
*/
171+
if (index.dataKey === againstIndex.dataKey && index.blockIndex === againstIndex.blockIndex && againstIndex.textRange![0] > index.textRange![1]) {
162172
return Operation.from(operation);
163173
}
164174

0 commit comments

Comments
 (0)