mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-01-19 14:13:34 +01:00
Changeset: Migrate from mergingOpAssembler()
to squashOps()
This commit is contained in:
parent
daa6b9074a
commit
2448fb8e41
4 changed files with 63 additions and 58 deletions
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 () {
|
||||||
|
|
Loading…
Reference in a new issue