Skip to content

Commit d58e3b8

Browse files
committed
Changeset: Use a generator to implement SmartOpAssembler
Eventually all uses of the class will be switched to the generator.
1 parent 2140d7c commit d58e3b8

File tree

1 file changed

+71
-47
lines changed

1 file changed

+71
-47
lines changed

src/static/js/Changeset.js

Lines changed: 71 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,62 @@ class MergingOpAssembler {
438438
}
439439
}
440440

441+
/**
442+
* Canonicalizes a sequence of operations. Specifically:
443+
* - Skips no-op changes.
444+
* - Reorders consecutive '-' and '+' operations.
445+
* - Combines consecutive operations when possible.
446+
*
447+
* @param {Iterable.<Op>} ops - Iterable of operations to combine.
448+
* @param {boolean} finalize - If truthy, omits the final op if it is an attributeless keep op.
449+
* @yields {Op} The canonicalized operations.
450+
* @returns {number} How much the sequence of operations changes the length (in characters).
451+
*/
452+
const canonicalizeOps = function* (ops, finalize) {
453+
let minusOps = [];
454+
let plusOps = [];
455+
let keepOps = [];
456+
let prevOpcode = '';
457+
let lengthChange = 0;
458+
459+
const flushPlusMinus = function* () {
460+
yield* squashOps(minusOps, false);
461+
minusOps = [];
462+
yield* squashOps(plusOps, false);
463+
plusOps = [];
464+
};
465+
466+
const flushKeeps = function* (finalize) {
467+
yield* squashOps(keepOps, finalize);
468+
keepOps = [];
469+
};
470+
471+
for (const op of ops) {
472+
if (!op.opcode || !op.chars) continue;
473+
switch (op.opcode) {
474+
case '-':
475+
if (prevOpcode === '=') yield* flushKeeps(false);
476+
minusOps.push(op);
477+
lengthChange -= op.chars;
478+
break;
479+
case '+':
480+
if (prevOpcode === '=') yield* flushKeeps(false);
481+
plusOps.push(op);
482+
lengthChange += op.chars;
483+
break;
484+
case '=':
485+
if (prevOpcode !== '=') yield* flushPlusMinus();
486+
keepOps.push(op);
487+
break;
488+
}
489+
prevOpcode = op.opcode;
490+
}
491+
492+
yield* flushPlusMinus();
493+
yield* flushKeeps(finalize);
494+
return lengthChange;
495+
};
496+
441497
/**
442498
* Creates an object that allows you to append operations (type Op) and also compresses them if
443499
* possible. Like MergingOpAssembler, but able to produce conforming exportss from slightly looser
@@ -449,49 +505,19 @@ class MergingOpAssembler {
449505
*/
450506
class SmartOpAssembler {
451507
constructor() {
452-
this._minusAssem = new MergingOpAssembler();
453-
this._plusAssem = new MergingOpAssembler();
454-
this._keepAssem = new MergingOpAssembler();
455-
this._assem = exports.stringAssembler();
456-
this._lastOpcode = '';
457-
this._lengthChange = 0;
458-
}
459-
460-
_flushKeeps() {
461-
this._assem.append(this._keepAssem.toString());
462-
this._keepAssem.clear();
508+
this.clear();
463509
}
464510

465-
_flushPlusMinus() {
466-
this._assem.append(this._minusAssem.toString());
467-
this._minusAssem.clear();
468-
this._assem.append(this._plusAssem.toString());
469-
this._plusAssem.clear();
511+
_serialize(finalize) {
512+
this._serialized = serializeOps((function* () {
513+
this._lengthChange = yield* canonicalizeOps(this._ops, finalize);
514+
}).call(this));
470515
}
471516

472517
append(op) {
473-
if (!op.opcode) return;
474-
if (!op.chars) return;
475-
476-
if (op.opcode === '-') {
477-
if (this._lastOpcode === '=') {
478-
this._flushKeeps();
479-
}
480-
this._minusAssem.append(op);
481-
this._lengthChange -= op.chars;
482-
} else if (op.opcode === '+') {
483-
if (this._lastOpcode === '=') {
484-
this._flushKeeps();
485-
}
486-
this._plusAssem.append(op);
487-
this._lengthChange += op.chars;
488-
} else if (op.opcode === '=') {
489-
if (this._lastOpcode !== '=') {
490-
this._flushPlusMinus();
491-
}
492-
this._keepAssem.append(op);
493-
}
494-
this._lastOpcode = op.opcode;
518+
this._serialized = null;
519+
this._lengthChange = null;
520+
this._ops.push(copyOp(op));
495521
}
496522

497523
appendOpWithText(opcode, text, attribs, pool) {
@@ -513,24 +539,22 @@ class SmartOpAssembler {
513539
}
514540

515541
toString() {
516-
this._flushPlusMinus();
517-
this._flushKeeps();
518-
return this._assem.toString();
542+
if (this._serialized == null) this._serialize(false);
543+
return this._serialized;
519544
}
520545

521546
clear() {
522-
this._minusAssem.clear();
523-
this._plusAssem.clear();
524-
this._keepAssem.clear();
525-
this._assem.clear();
526-
this._lengthChange = 0;
547+
this._ops = [];
548+
this._serialized = null;
549+
this._lengthChange = null;
527550
}
528551

529552
endDocument() {
530-
this._keepAssem.endDocument();
553+
this._serialize(true);
531554
}
532555

533556
getLengthChange() {
557+
if (this._lengthChange == null) this._serialize(false);
534558
return this._lengthChange;
535559
}
536560
}

0 commit comments

Comments
 (0)