Changeset: Migrate from mergingOpAssembler() to squashOps()

This commit is contained in:
Richard Hansen 2021-10-21 00:49:04 -04:00
parent daa6b9074a
commit 2448fb8e41
4 changed files with 63 additions and 58 deletions

View file

@ -37,6 +37,8 @@
* `opIterator()`: Deprecated in favor of the new `deserializeOps()` generator * `opIterator()`: Deprecated in favor of the new `deserializeOps()` generator
function. function.
* `opAssembler()`: Deprecated in favor of the new `serializeOps()` function. * `opAssembler()`: Deprecated in favor of the new `serializeOps()` function.
* `mergingOpAssembler()`: Deprecated in favor of the new `squashOps()`
generator function (combined with `serializeOps()`).
* `appendATextToAssembler()`: Deprecated in favor of the new `opsFromAText()` * `appendATextToAssembler()`: Deprecated in favor of the new `opsFromAText()`
generator function. generator function.
* `newOp()`: Deprecated in favor of the new `Op` class. * `newOp()`: Deprecated in favor of the new `Op` class.

View file

@ -338,7 +338,7 @@ class OpAssembler {
* @yields {Op} The squashed operations. * @yields {Op} The squashed operations.
* @returns {Generator<Op>} * @returns {Generator<Op>}
*/ */
const squashOps = function* (ops, finalize) { exports.squashOps = function* (ops, finalize) {
let prevOp = new Op(); let prevOp = new Op();
// If we get, for example, insertions [xxx\n,yyy], those don't merge, but if we get // If we get, for example, insertions [xxx\n,yyy], those don't merge, but if we get
// [xxx\n,yyy,zzz\n], that merges to [xxx\nyyyzzz\n]. This variable stores the length of yyy and // [xxx\n,yyy,zzz\n], that merges to [xxx\nyyyzzz\n]. This variable stores the length of yyy and
@ -402,7 +402,7 @@ class MergingOpAssembler {
} }
_serialize(finalize) { _serialize(finalize) {
this._serialized = exports.serializeOps(squashOps(this._ops, finalize)); this._serialized = exports.serializeOps(exports.squashOps(this._ops, finalize));
} }
append(op) { append(op) {
@ -465,32 +465,29 @@ const opsFromText = function* (opcode, text, attribs = '', pool = null) {
*/ */
class SmartOpAssembler { class SmartOpAssembler {
constructor() { constructor() {
this._minusAssem = new MergingOpAssembler();
this._plusAssem = new MergingOpAssembler();
this._keepAssem = new MergingOpAssembler();
this._assem = exports.stringAssembler(); this._assem = exports.stringAssembler();
this.clear(); this.clear();
} }
clear() { clear() {
this._minusAssem.clear(); this._minusAssem = [];
this._plusAssem.clear(); this._plusAssem = [];
this._keepAssem.clear(); this._keepAssem = [];
this._assem.clear(); this._assem.clear();
this._lastOpcode = ''; this._lastOpcode = '';
this._lengthChange = 0; this._lengthChange = 0;
} }
_flushKeeps() { _flushKeeps(finalize) {
this._assem.append(this._keepAssem.toString()); this._assem.append(exports.serializeOps(exports.squashOps(this._keepAssem, finalize)));
this._keepAssem.clear(); this._keepAssem = [];
} }
_flushPlusMinus() { _flushPlusMinus() {
this._assem.append(this._minusAssem.toString()); this._assem.append(exports.serializeOps(exports.squashOps(this._minusAssem, false)));
this._minusAssem.clear(); this._minusAssem = [];
this._assem.append(this._plusAssem.toString()); this._assem.append(exports.serializeOps(exports.squashOps(this._plusAssem, false)));
this._plusAssem.clear(); this._plusAssem = [];
} }
append(op) { append(op) {
@ -499,21 +496,21 @@ class SmartOpAssembler {
if (op.opcode === '-') { if (op.opcode === '-') {
if (this._lastOpcode === '=') { if (this._lastOpcode === '=') {
this._flushKeeps(); this._flushKeeps(false);
} }
this._minusAssem.append(op); this._minusAssem.push(copyOp(op));
this._lengthChange -= op.chars; this._lengthChange -= op.chars;
} else if (op.opcode === '+') { } else if (op.opcode === '+') {
if (this._lastOpcode === '=') { if (this._lastOpcode === '=') {
this._flushKeeps(); this._flushKeeps(false);
} }
this._plusAssem.append(op); this._plusAssem.push(copyOp(op));
this._lengthChange += op.chars; this._lengthChange += op.chars;
} else if (op.opcode === '=') { } else if (op.opcode === '=') {
if (this._lastOpcode !== '=') { if (this._lastOpcode !== '=') {
this._flushPlusMinus(); this._flushPlusMinus();
} }
this._keepAssem.append(op); this._keepAssem.push(copyOp(op));
} }
this._lastOpcode = op.opcode; this._lastOpcode = op.opcode;
} }
@ -537,12 +534,13 @@ class SmartOpAssembler {
toString() { toString() {
this._flushPlusMinus(); this._flushPlusMinus();
this._flushKeeps(); this._flushKeeps(false);
return this._assem.toString(); return this._assem.toString();
} }
endDocument() { endDocument() {
this._keepAssem.endDocument(); this._flushPlusMinus();
this._flushKeeps(true);
} }
getLengthChange() { getLengthChange() {
@ -611,9 +609,14 @@ exports.checkRep = (cs) => {
exports.smartOpAssembler = () => new SmartOpAssembler(); exports.smartOpAssembler = () => new SmartOpAssembler();
/** /**
* @deprecated Use `squashOps` with `serializeOps` instead.
* @returns {MergingOpAssembler} * @returns {MergingOpAssembler}
*/ */
exports.mergingOpAssembler = () => new MergingOpAssembler(); exports.mergingOpAssembler = () => {
padutils.warnDeprecated(
'Changeset.mergingOpAssembler() is deprecated; use Changeset.squashOps() instead');
return new MergingOpAssembler();
};
/** /**
* @deprecated Use `serializeOps` instead. * @deprecated Use `serializeOps` instead.
@ -1323,12 +1326,12 @@ exports.mutateAttributionLines = (cs, lines, pool) => {
let lineAssem = null; let lineAssem = null;
const outputMutOp = (op) => { const outputMutOp = (op) => {
if (!lineAssem) lineAssem = new MergingOpAssembler(); if (!lineAssem) lineAssem = [];
lineAssem.append(op); lineAssem.push(op);
if (op.lines <= 0) return; if (op.lines <= 0) return;
assert(op.lines === 1, `Can't have op.lines of ${op.lines} in attribution lines`); assert(op.lines === 1, `Can't have op.lines of ${op.lines} in attribution lines`);
// ship it to the mut // ship it to the mut
mut.insert(lineAssem.toString(), 1); mut.insert(exports.serializeOps(exports.squashOps(lineAssem, false)), 1);
lineAssem = null; lineAssem = null;
}; };
@ -1377,23 +1380,22 @@ exports.mutateAttributionLines = (cs, lines, pool) => {
* @returns {string} joined Attribution lines * @returns {string} joined Attribution lines
*/ */
exports.joinAttributionLines = (theAlines) => { exports.joinAttributionLines = (theAlines) => {
const assem = new MergingOpAssembler(); const ops = (function* () {
for (const aline of theAlines) { for (const aline of theAlines) yield* exports.deserializeOps(aline);
for (const op of exports.deserializeOps(aline)) assem.append(op); })();
} return exports.serializeOps(exports.squashOps(ops, false));
return assem.toString();
}; };
exports.splitAttributionLines = (attrOps, text) => { exports.splitAttributionLines = (attrOps, text) => {
const assem = new MergingOpAssembler(); let ops = [];
const lines = []; const lines = [];
let pos = 0; let pos = 0;
const appendOp = (op) => { const appendOp = (op) => {
assem.append(op); ops.push(op);
if (op.lines > 0) { if (op.lines > 0) {
lines.push(assem.toString()); lines.push(exports.serializeOps(exports.squashOps(ops, false)));
assem.clear(); ops = [];
} }
pos += op.chars; pos += op.chars;
}; };

View file

@ -143,21 +143,21 @@ const makeChangesetTracker = (scheduler, apool, aceCallbacksProvider) => {
// Sanitize authorship: Replace all author attributes with this user's author ID in case the // Sanitize authorship: Replace all author attributes with this user's author ID in case the
// text was copied from another author. // text was copied from another author.
const cs = Changeset.unpack(userChangeset); const cs = Changeset.unpack(userChangeset);
const assem = Changeset.mergingOpAssembler(); const ops = (function* () {
for (const op of Changeset.deserializeOps(cs.ops)) {
for (const op of Changeset.deserializeOps(cs.ops)) { if (op.opcode === '+') {
if (op.opcode === '+') { const attribs = AttributeMap.fromString(op.attribs, apool);
const attribs = AttributeMap.fromString(op.attribs, apool); const oldAuthorId = attribs.get('author');
const oldAuthorId = attribs.get('author'); if (oldAuthorId != null && oldAuthorId !== authorId) {
if (oldAuthorId != null && oldAuthorId !== authorId) { attribs.set('author', authorId);
attribs.set('author', authorId); op.attribs = attribs.toString();
op.attribs = attribs.toString(); }
} }
yield op;
} }
assem.append(op); })();
} const serializedOps = Changeset.serializeOps(Changeset.squashOps(ops, true));
assem.endDocument(); userChangeset = Changeset.pack(cs.oldLen, cs.newLen, serializedOps, cs.charBank);
userChangeset = Changeset.pack(cs.oldLen, cs.newLen, assem.toString(), cs.charBank);
Changeset.checkRep(userChangeset); Changeset.checkRep(userChangeset);
if (Changeset.isIdentity(userChangeset)) toSubmit = null; if (Changeset.isIdentity(userChangeset)) toSubmit = null;

View file

@ -129,16 +129,17 @@ describe('easysync-other', function () {
describe('split/join attribution lines', function () { describe('split/join attribution lines', function () {
const testSplitJoinAttributionLines = (randomSeed) => { const testSplitJoinAttributionLines = (randomSeed) => {
const stringToOps = (str) => { const stringToOps = (str) => {
const assem = Changeset.mergingOpAssembler(); const ops = (function* () {
const o = new Changeset.Op('+'); for (let i = 0; i < str.length; i++) {
o.chars = 1; const c = str.charAt(i);
for (let i = 0; i < str.length; i++) { const o = new Changeset.Op('+');
const c = str.charAt(i); o.chars = 1;
o.lines = (c === '\n' ? 1 : 0); o.lines = (c === '\n' ? 1 : 0);
o.attribs = (c === 'a' || c === 'b' ? `*${c}` : ''); o.attribs = (c === 'a' || c === 'b' ? `*${c}` : '');
assem.append(o); yield o;
} }
return assem.toString(); })();
return Changeset.serializeOps(Changeset.squashOps(ops, false));
}; };
it(`testSplitJoinAttributionLines#${randomSeed}`, async function () { it(`testSplitJoinAttributionLines#${randomSeed}`, async function () {