Changeset: Migrate from OpIter
to deserializeOps()
This commit is contained in:
parent
0eca0251f2
commit
89fe40e080
16 changed files with 147 additions and 179 deletions
|
@ -21,6 +21,8 @@
|
||||||
* `eachAttribNumber()`
|
* `eachAttribNumber()`
|
||||||
* `makeAttribsString()`
|
* `makeAttribsString()`
|
||||||
* `opAttributeValue()`
|
* `opAttributeValue()`
|
||||||
|
* `opIterator()`: Deprecated in favor of the new `deserializeOps()` generator
|
||||||
|
function.
|
||||||
* `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.
|
||||||
|
|
|
@ -670,9 +670,8 @@ const Changeset = require('ep_etherpad-lite/static/js/Changeset');
|
||||||
|
|
||||||
exports.getLineHTMLForExport = async (hookName, context) => {
|
exports.getLineHTMLForExport = async (hookName, context) => {
|
||||||
if (!context.attribLine) return;
|
if (!context.attribLine) return;
|
||||||
const opIter = Changeset.opIterator(context.attribLine);
|
const [op] = Changeset.deserializeOps(context.attribLine);
|
||||||
if (!opIter.hasNext()) return;
|
if (op == null) return;
|
||||||
const op = opIter.next();
|
|
||||||
const heading = AttributeMap.fromString(op.attribs, context.apool).get('heading');
|
const heading = AttributeMap.fromString(op.attribs, context.apool).get('heading');
|
||||||
if (!heading) return;
|
if (!heading) return;
|
||||||
context.lineContent = `<${heading}>${context.lineContent}</${heading}>`;
|
context.lineContent = `<${heading}>${context.lineContent}</${heading}>`;
|
||||||
|
|
|
@ -527,12 +527,10 @@ exports.restoreRevision = async (padID, rev) => {
|
||||||
atext.text += '\n';
|
atext.text += '\n';
|
||||||
|
|
||||||
const eachAttribRun = (attribs, func) => {
|
const eachAttribRun = (attribs, func) => {
|
||||||
const attribsIter = Changeset.opIterator(attribs);
|
|
||||||
let textIndex = 0;
|
let textIndex = 0;
|
||||||
const newTextStart = 0;
|
const newTextStart = 0;
|
||||||
const newTextEnd = atext.text.length;
|
const newTextEnd = atext.text.length;
|
||||||
while (attribsIter.hasNext()) {
|
for (const op of Changeset.deserializeOps(attribs)) {
|
||||||
const op = attribsIter.next();
|
|
||||||
const nextIndex = textIndex + op.chars;
|
const nextIndex = textIndex + op.chars;
|
||||||
if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) {
|
if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) {
|
||||||
func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs);
|
func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs);
|
||||||
|
|
|
@ -586,12 +586,7 @@ const handleUserChanges = async (socket, message) => {
|
||||||
Changeset.checkRep(changeset);
|
Changeset.checkRep(changeset);
|
||||||
|
|
||||||
// Validate all added 'author' attribs to be the same value as the current user
|
// Validate all added 'author' attribs to be the same value as the current user
|
||||||
const iterator = Changeset.opIterator(Changeset.unpack(changeset).ops);
|
for (const op of Changeset.deserializeOps(Changeset.unpack(changeset).ops)) {
|
||||||
let op;
|
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
op = iterator.next();
|
|
||||||
|
|
||||||
// + can add text with attribs
|
// + can add text with attribs
|
||||||
// = can change or add attribs
|
// = can change or add attribs
|
||||||
// - can have attribs, but they are discarded and don't show up in the attribs -
|
// - can have attribs, but they are discarded and don't show up in the attribs -
|
||||||
|
@ -741,10 +736,8 @@ const _correctMarkersInPad = (atext, apool) => {
|
||||||
// collect char positions of line markers (e.g. bullets) in new atext
|
// collect char positions of line markers (e.g. bullets) in new atext
|
||||||
// that aren't at the start of a line
|
// that aren't at the start of a line
|
||||||
const badMarkers = [];
|
const badMarkers = [];
|
||||||
const iter = Changeset.opIterator(atext.attribs);
|
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
while (iter.hasNext()) {
|
for (const op of Changeset.deserializeOps(atext.attribs)) {
|
||||||
const op = iter.next();
|
|
||||||
const attribs = AttributeMap.fromString(op.attribs, apool);
|
const attribs = AttributeMap.fromString(op.attribs, apool);
|
||||||
const hasMarker = AttributeManager.lineAttributes.some((a) => attribs.has(a));
|
const hasMarker = AttributeManager.lineAttributes.some((a) => attribs.has(a));
|
||||||
if (hasMarker) {
|
if (hasMarker) {
|
||||||
|
|
|
@ -52,9 +52,8 @@ exports._analyzeLine = (text, aline, apool) => {
|
||||||
let lineMarker = 0;
|
let lineMarker = 0;
|
||||||
line.listLevel = 0;
|
line.listLevel = 0;
|
||||||
if (aline) {
|
if (aline) {
|
||||||
const opIter = Changeset.opIterator(aline);
|
const [op] = Changeset.deserializeOps(aline);
|
||||||
if (opIter.hasNext()) {
|
if (op != null) {
|
||||||
const op = opIter.next();
|
|
||||||
const attribs = AttributeMap.fromString(op.attribs, apool);
|
const attribs = AttributeMap.fromString(op.attribs, apool);
|
||||||
let listType = attribs.get('list');
|
let listType = attribs.get('list');
|
||||||
if (listType) {
|
if (listType) {
|
||||||
|
|
|
@ -197,13 +197,12 @@ const getHTMLFromAtext = async (pad, atext, authorColors) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const iter = Changeset.opIterator(Changeset.subattribution(attribs, idx, idx + numChars));
|
const ops = Changeset.deserializeOps(Changeset.subattribution(attribs, idx, idx + numChars));
|
||||||
idx += numChars;
|
idx += numChars;
|
||||||
|
|
||||||
// this iterates over every op string and decides which tags to open or to close
|
// this iterates over every op string and decides which tags to open or to close
|
||||||
// based on the attribs used
|
// based on the attribs used
|
||||||
while (iter.hasNext()) {
|
for (const o of ops) {
|
||||||
const o = iter.next();
|
|
||||||
const usedAttribs = [];
|
const usedAttribs = [];
|
||||||
|
|
||||||
// mark all attribs as used
|
// mark all attribs as used
|
||||||
|
|
|
@ -76,11 +76,10 @@ const getTXTFromAtext = (pad, atext, authorColors) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const iter = Changeset.opIterator(Changeset.subattribution(attribs, idx, idx + numChars));
|
const ops = Changeset.deserializeOps(Changeset.subattribution(attribs, idx, idx + numChars));
|
||||||
idx += numChars;
|
idx += numChars;
|
||||||
|
|
||||||
while (iter.hasNext()) {
|
for (const o of ops) {
|
||||||
const o = iter.next();
|
|
||||||
let propChanged = false;
|
let propChanged = false;
|
||||||
|
|
||||||
for (const a of attributes.decodeAttribString(o.attribs)) {
|
for (const a of attributes.decodeAttribString(o.attribs)) {
|
||||||
|
|
|
@ -67,12 +67,10 @@ exports.setPadHTML = async (pad, html) => {
|
||||||
const builder = Changeset.builder(1);
|
const builder = Changeset.builder(1);
|
||||||
|
|
||||||
// assemble each line into the builder
|
// assemble each line into the builder
|
||||||
const attribsIter = Changeset.opIterator(newAttribs);
|
|
||||||
let textIndex = 0;
|
let textIndex = 0;
|
||||||
const newTextStart = 0;
|
const newTextStart = 0;
|
||||||
const newTextEnd = newText.length;
|
const newTextEnd = newText.length;
|
||||||
while (attribsIter.hasNext()) {
|
for (const op of Changeset.deserializeOps(newAttribs)) {
|
||||||
const op = attribsIter.next();
|
|
||||||
const nextIndex = textIndex + op.chars;
|
const nextIndex = textIndex + op.chars;
|
||||||
if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) {
|
if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) {
|
||||||
const start = Math.max(newTextStart, textIndex);
|
const start = Math.max(newTextStart, textIndex);
|
||||||
|
|
|
@ -35,16 +35,10 @@ PadDiff.prototype._isClearAuthorship = function (changeset) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// lets iterator over the operators
|
const [clearOperator, anotherOp] = Changeset.deserializeOps(unpacked.ops);
|
||||||
const iterator = Changeset.opIterator(unpacked.ops);
|
|
||||||
|
|
||||||
// get the first operator, this should be a clear operator
|
|
||||||
const clearOperator = iterator.next();
|
|
||||||
|
|
||||||
// check if there is only one operator
|
// check if there is only one operator
|
||||||
if (iterator.hasNext() === true) {
|
if (anotherOp != null) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if this operator doesn't change text
|
// check if this operator doesn't change text
|
||||||
if (clearOperator.opcode !== '=') {
|
if (clearOperator.opcode !== '=') {
|
||||||
|
@ -212,7 +206,6 @@ PadDiff.prototype._extendChangesetWithAuthor = (changeset, author, apool) => {
|
||||||
// unpack
|
// unpack
|
||||||
const unpacked = Changeset.unpack(changeset);
|
const unpacked = Changeset.unpack(changeset);
|
||||||
|
|
||||||
const iterator = Changeset.opIterator(unpacked.ops);
|
|
||||||
const assem = Changeset.opAssembler();
|
const assem = Changeset.opAssembler();
|
||||||
|
|
||||||
// create deleted attribs
|
// create deleted attribs
|
||||||
|
@ -220,10 +213,7 @@ PadDiff.prototype._extendChangesetWithAuthor = (changeset, author, apool) => {
|
||||||
const deletedAttrib = apool.putAttrib(['removed', true]);
|
const deletedAttrib = apool.putAttrib(['removed', true]);
|
||||||
const attribs = `*${Changeset.numToString(authorAttrib)}*${Changeset.numToString(deletedAttrib)}`;
|
const attribs = `*${Changeset.numToString(authorAttrib)}*${Changeset.numToString(deletedAttrib)}`;
|
||||||
|
|
||||||
// iteratore over the operators of the changeset
|
for (const operator of Changeset.deserializeOps(unpacked.ops)) {
|
||||||
while (iterator.hasNext()) {
|
|
||||||
const operator = iterator.next();
|
|
||||||
|
|
||||||
if (operator.opcode === '-') {
|
if (operator.opcode === '-') {
|
||||||
// this is a delete operator, extend it with the author
|
// this is a delete operator, extend it with the author
|
||||||
operator.attribs = attribs;
|
operator.attribs = attribs;
|
||||||
|
@ -268,22 +258,23 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
|
||||||
|
|
||||||
let curLine = 0;
|
let curLine = 0;
|
||||||
let curChar = 0;
|
let curChar = 0;
|
||||||
let curLineOpIter = null;
|
let curLineOps = null;
|
||||||
let curLineOpIterLine;
|
let curLineOpsNext = null;
|
||||||
|
let curLineOpsLine;
|
||||||
let curLineNextOp = new Changeset.Op('+');
|
let curLineNextOp = new Changeset.Op('+');
|
||||||
|
|
||||||
const unpacked = Changeset.unpack(cs);
|
const unpacked = Changeset.unpack(cs);
|
||||||
const csIter = Changeset.opIterator(unpacked.ops);
|
|
||||||
const builder = Changeset.builder(unpacked.newLen);
|
const builder = Changeset.builder(unpacked.newLen);
|
||||||
|
|
||||||
const consumeAttribRuns = (numChars, func /* (len, attribs, endsLine)*/) => {
|
const consumeAttribRuns = (numChars, func /* (len, attribs, endsLine)*/) => {
|
||||||
if ((!curLineOpIter) || (curLineOpIterLine !== curLine)) {
|
if (!curLineOps || curLineOpsLine !== curLine) {
|
||||||
// create curLineOpIter and advance it to curChar
|
curLineOps = Changeset.deserializeOps(aLinesGet(curLine));
|
||||||
curLineOpIter = Changeset.opIterator(aLinesGet(curLine));
|
curLineOpsNext = curLineOps.next();
|
||||||
curLineOpIterLine = curLine;
|
curLineOpsLine = curLine;
|
||||||
let indexIntoLine = 0;
|
let indexIntoLine = 0;
|
||||||
while (curLineOpIter.hasNext()) {
|
while (!curLineOpsNext.done) {
|
||||||
curLineNextOp = curLineOpIter.next();
|
curLineNextOp = curLineOpsNext.value;
|
||||||
|
curLineOpsNext = curLineOps.next();
|
||||||
if (indexIntoLine + curLineNextOp.chars >= curChar) {
|
if (indexIntoLine + curLineNextOp.chars >= curChar) {
|
||||||
curLineNextOp.chars -= (curChar - indexIntoLine);
|
curLineNextOp.chars -= (curChar - indexIntoLine);
|
||||||
break;
|
break;
|
||||||
|
@ -293,16 +284,22 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
while (numChars > 0) {
|
while (numChars > 0) {
|
||||||
if ((!curLineNextOp.chars) && (!curLineOpIter.hasNext())) {
|
if (!curLineNextOp.chars && curLineOpsNext.done) {
|
||||||
curLine++;
|
curLine++;
|
||||||
curChar = 0;
|
curChar = 0;
|
||||||
curLineOpIterLine = curLine;
|
curLineOpsLine = curLine;
|
||||||
curLineNextOp.chars = 0;
|
curLineNextOp.chars = 0;
|
||||||
curLineOpIter = Changeset.opIterator(aLinesGet(curLine));
|
curLineOps = Changeset.deserializeOps(aLinesGet(curLine));
|
||||||
|
curLineOpsNext = curLineOps.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!curLineNextOp.chars) {
|
if (!curLineNextOp.chars) {
|
||||||
curLineNextOp = curLineOpIter.hasNext() ? curLineOpIter.next() : new Changeset.Op();
|
if (curLineOpsNext.done) {
|
||||||
|
curLineNextOp = new Changeset.Op();
|
||||||
|
} else {
|
||||||
|
curLineNextOp = curLineOpsNext.value;
|
||||||
|
curLineOpsNext = curLineOps.next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const charsToUse = Math.min(numChars, curLineNextOp.chars);
|
const charsToUse = Math.min(numChars, curLineNextOp.chars);
|
||||||
|
@ -314,7 +311,7 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
|
||||||
curChar += charsToUse;
|
curChar += charsToUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!curLineNextOp.chars) && (!curLineOpIter.hasNext())) {
|
if (!curLineNextOp.chars && curLineOpsNext.done) {
|
||||||
curLine++;
|
curLine++;
|
||||||
curChar = 0;
|
curChar = 0;
|
||||||
}
|
}
|
||||||
|
@ -324,7 +321,7 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
|
||||||
if (L) {
|
if (L) {
|
||||||
curLine += L;
|
curLine += L;
|
||||||
curChar = 0;
|
curChar = 0;
|
||||||
} else if (curLineOpIter && curLineOpIterLine === curLine) {
|
} else if (curLineOps && curLineOpsLine === curLine) {
|
||||||
consumeAttribRuns(N, () => {});
|
consumeAttribRuns(N, () => {});
|
||||||
} else {
|
} else {
|
||||||
curChar += N;
|
curChar += N;
|
||||||
|
@ -361,10 +358,7 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// iterate over all operators of this changeset
|
for (const csOp of Changeset.deserializeOps(unpacked.ops)) {
|
||||||
while (csIter.hasNext()) {
|
|
||||||
const csOp = csIter.next();
|
|
||||||
|
|
||||||
if (csOp.opcode === '=') {
|
if (csOp.opcode === '=') {
|
||||||
const textBank = nextText(csOp.chars);
|
const textBank = nextText(csOp.chars);
|
||||||
|
|
||||||
|
|
|
@ -150,9 +150,9 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
||||||
// get `attributeName` attribute of first char of line
|
// get `attributeName` attribute of first char of line
|
||||||
const aline = this.rep.alines[lineNum];
|
const aline = this.rep.alines[lineNum];
|
||||||
if (!aline) return '';
|
if (!aline) return '';
|
||||||
const opIter = Changeset.opIterator(aline);
|
const [op] = Changeset.deserializeOps(aline);
|
||||||
if (!opIter.hasNext()) return '';
|
if (op == null) return '';
|
||||||
return AttributeMap.fromString(opIter.next().attribs, this.rep.apool).get(attributeName) || '';
|
return AttributeMap.fromString(op.attribs, this.rep.apool).get(attributeName) || '';
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -163,9 +163,8 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
||||||
// get attributes of first char of line
|
// get attributes of first char of line
|
||||||
const aline = this.rep.alines[lineNum];
|
const aline = this.rep.alines[lineNum];
|
||||||
if (!aline) return [];
|
if (!aline) return [];
|
||||||
const opIter = Changeset.opIterator(aline);
|
const [op] = Changeset.deserializeOps(aline);
|
||||||
if (!opIter.hasNext()) return [];
|
if (op == null) return [];
|
||||||
const op = opIter.next();
|
|
||||||
return [...attributes.attribsFromString(op.attribs, this.rep.apool)];
|
return [...attributes.attribsFromString(op.attribs, this.rep.apool)];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -221,13 +220,8 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
||||||
const end = selEnd[1];
|
const end = selEnd[1];
|
||||||
let hasAttrib = true;
|
let hasAttrib = true;
|
||||||
|
|
||||||
// Iterate over attribs on this line
|
|
||||||
|
|
||||||
const opIter = Changeset.opIterator(rep.alines[lineNum]);
|
|
||||||
let indexIntoLine = 0;
|
let indexIntoLine = 0;
|
||||||
|
for (const op of Changeset.deserializeOps(rep.alines[lineNum])) {
|
||||||
while (opIter.hasNext()) {
|
|
||||||
const op = opIter.next();
|
|
||||||
const opStartInLine = indexIntoLine;
|
const opStartInLine = indexIntoLine;
|
||||||
const opEndInLine = opStartInLine + op.chars;
|
const opEndInLine = opStartInLine + op.chars;
|
||||||
if (!hasIt(op.attribs)) {
|
if (!hasIt(op.attribs)) {
|
||||||
|
@ -260,15 +254,11 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
||||||
if (!aline) {
|
if (!aline) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
// iterate through all operations of a line
|
|
||||||
const opIter = Changeset.opIterator(aline);
|
|
||||||
|
|
||||||
// we need to sum up how much characters each operations take until the wanted position
|
// we need to sum up how much characters each operations take until the wanted position
|
||||||
let currentPointer = 0;
|
let currentPointer = 0;
|
||||||
let currentOperation;
|
|
||||||
|
|
||||||
while (opIter.hasNext()) {
|
for (const currentOperation of Changeset.deserializeOps(aline)) {
|
||||||
currentOperation = opIter.next();
|
|
||||||
currentPointer += currentOperation.chars;
|
currentPointer += currentOperation.chars;
|
||||||
if (currentPointer <= column) continue;
|
if (currentPointer <= column) continue;
|
||||||
return [...attributes.attribsFromString(currentOperation.attribs, this.rep.apool)];
|
return [...attributes.attribsFromString(currentOperation.attribs, this.rep.apool)];
|
||||||
|
|
|
@ -187,7 +187,7 @@ exports.newLen = (cs) => exports.unpack(cs).newLen;
|
||||||
* @yields {Op}
|
* @yields {Op}
|
||||||
* @returns {Generator<Op>}
|
* @returns {Generator<Op>}
|
||||||
*/
|
*/
|
||||||
const deserializeOps = function* (ops) {
|
exports.deserializeOps = function* (ops) {
|
||||||
// TODO: Migrate to String.prototype.matchAll() once there is enough browser support.
|
// TODO: Migrate to String.prototype.matchAll() once there is enough browser support.
|
||||||
const regex = /((?:\*[0-9a-z]+)*)(?:\|([0-9a-z]+))?([-+=])([0-9a-z]+)|(.)/g;
|
const regex = /((?:\*[0-9a-z]+)*)(?:\|([0-9a-z]+))?([-+=])([0-9a-z]+)|(.)/g;
|
||||||
let match;
|
let match;
|
||||||
|
@ -206,13 +206,15 @@ const deserializeOps = function* (ops) {
|
||||||
* Iterator over a changeset's operations.
|
* Iterator over a changeset's operations.
|
||||||
*
|
*
|
||||||
* Note: This class does NOT implement the ECMAScript iterable or iterator protocols.
|
* Note: This class does NOT implement the ECMAScript iterable or iterator protocols.
|
||||||
|
*
|
||||||
|
* @deprecated Use `deserializeOps` instead.
|
||||||
*/
|
*/
|
||||||
class OpIter {
|
class OpIter {
|
||||||
/**
|
/**
|
||||||
* @param {string} ops - String encoding the change operations to iterate over.
|
* @param {string} ops - String encoding the change operations to iterate over.
|
||||||
*/
|
*/
|
||||||
constructor(ops) {
|
constructor(ops) {
|
||||||
this._gen = deserializeOps(ops);
|
this._gen = exports.deserializeOps(ops);
|
||||||
this._next = this._gen.next();
|
this._next = this._gen.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,10 +248,15 @@ class OpIter {
|
||||||
/**
|
/**
|
||||||
* Creates an iterator which decodes string changeset operations.
|
* Creates an iterator which decodes string changeset operations.
|
||||||
*
|
*
|
||||||
|
* @deprecated Use `deserializeOps` instead.
|
||||||
* @param {string} opsStr - String encoding of the change operations to perform.
|
* @param {string} opsStr - String encoding of the change operations to perform.
|
||||||
* @returns {OpIter} Operator iterator object.
|
* @returns {OpIter} Operator iterator object.
|
||||||
*/
|
*/
|
||||||
exports.opIterator = (opsStr) => new OpIter(opsStr);
|
exports.opIterator = (opsStr) => {
|
||||||
|
padutils.warnWithStack(
|
||||||
|
'Changeset.opIterator() is deprecated; use Changeset.deserializeOps() instead');
|
||||||
|
return new OpIter(opsStr);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleans an Op object.
|
* Cleans an Op object.
|
||||||
|
@ -374,9 +381,7 @@ exports.checkRep = (cs) => {
|
||||||
let oldPos = 0;
|
let oldPos = 0;
|
||||||
let calcNewLen = 0;
|
let calcNewLen = 0;
|
||||||
let numInserted = 0;
|
let numInserted = 0;
|
||||||
const iter = new OpIter(ops);
|
for (const o of exports.deserializeOps(ops)) {
|
||||||
while (iter.hasNext()) {
|
|
||||||
const o = iter.next();
|
|
||||||
switch (o.opcode) {
|
switch (o.opcode) {
|
||||||
case '=':
|
case '=':
|
||||||
oldPos += o.chars;
|
oldPos += o.chars;
|
||||||
|
@ -1027,15 +1032,18 @@ class TextLinesMutator {
|
||||||
* @returns {string} the integrated changeset
|
* @returns {string} the integrated changeset
|
||||||
*/
|
*/
|
||||||
const applyZip = (in1, in2, func) => {
|
const applyZip = (in1, in2, func) => {
|
||||||
const iter1 = new OpIter(in1);
|
const ops1 = exports.deserializeOps(in1);
|
||||||
const iter2 = new OpIter(in2);
|
const ops2 = exports.deserializeOps(in2);
|
||||||
|
let next1 = ops1.next();
|
||||||
|
let next2 = ops2.next();
|
||||||
const assem = exports.smartOpAssembler();
|
const assem = exports.smartOpAssembler();
|
||||||
const op1 = new Op();
|
while (!next1.done || !next2.done) {
|
||||||
const op2 = new Op();
|
if (!next1.done && !next1.value.opcode) next1 = ops1.next();
|
||||||
while (op1.opcode || iter1.hasNext() || op2.opcode || iter2.hasNext()) {
|
if (!next2.done && !next2.value.opcode) next2 = ops2.next();
|
||||||
if ((!op1.opcode) && iter1.hasNext()) iter1.next(op1);
|
if (next1.value == null) next1.value = new Op();
|
||||||
if ((!op2.opcode) && iter2.hasNext()) iter2.next(op2);
|
if (next2.value == null) next2.value = new Op();
|
||||||
const opOut = func(op1, op2);
|
if (!next1.value.opcode && !next2.value.opcode) break;
|
||||||
|
const opOut = func(next1.value, next2.value);
|
||||||
if (opOut && opOut.opcode) assem.append(opOut);
|
if (opOut && opOut.opcode) assem.append(opOut);
|
||||||
}
|
}
|
||||||
assem.endDocument();
|
assem.endDocument();
|
||||||
|
@ -1097,12 +1105,10 @@ exports.pack = (oldLen, newLen, opsStr, bank) => {
|
||||||
exports.applyToText = (cs, str) => {
|
exports.applyToText = (cs, str) => {
|
||||||
const unpacked = exports.unpack(cs);
|
const unpacked = exports.unpack(cs);
|
||||||
assert(str.length === unpacked.oldLen, `mismatched apply: ${str.length} / ${unpacked.oldLen}`);
|
assert(str.length === unpacked.oldLen, `mismatched apply: ${str.length} / ${unpacked.oldLen}`);
|
||||||
const csIter = new OpIter(unpacked.ops);
|
|
||||||
const bankIter = exports.stringIterator(unpacked.charBank);
|
const bankIter = exports.stringIterator(unpacked.charBank);
|
||||||
const strIter = exports.stringIterator(str);
|
const strIter = exports.stringIterator(str);
|
||||||
const assem = exports.stringAssembler();
|
const assem = exports.stringAssembler();
|
||||||
while (csIter.hasNext()) {
|
for (const op of exports.deserializeOps(unpacked.ops)) {
|
||||||
const op = csIter.next();
|
|
||||||
switch (op.opcode) {
|
switch (op.opcode) {
|
||||||
case '+':
|
case '+':
|
||||||
// op is + and op.lines 0: no newlines must be in op.chars
|
// op is + and op.lines 0: no newlines must be in op.chars
|
||||||
|
@ -1142,11 +1148,9 @@ exports.applyToText = (cs, str) => {
|
||||||
*/
|
*/
|
||||||
exports.mutateTextLines = (cs, lines) => {
|
exports.mutateTextLines = (cs, lines) => {
|
||||||
const unpacked = exports.unpack(cs);
|
const unpacked = exports.unpack(cs);
|
||||||
const csIter = new OpIter(unpacked.ops);
|
|
||||||
const bankIter = exports.stringIterator(unpacked.charBank);
|
const bankIter = exports.stringIterator(unpacked.charBank);
|
||||||
const mut = new TextLinesMutator(lines);
|
const mut = new TextLinesMutator(lines);
|
||||||
while (csIter.hasNext()) {
|
for (const op of exports.deserializeOps(unpacked.ops)) {
|
||||||
const op = csIter.next();
|
|
||||||
switch (op.opcode) {
|
switch (op.opcode) {
|
||||||
case '+':
|
case '+':
|
||||||
mut.insert(bankIter.take(op.chars), op.lines);
|
mut.insert(bankIter.take(op.chars), op.lines);
|
||||||
|
@ -1273,24 +1277,30 @@ exports.applyToAttribution = (cs, astr, pool) => {
|
||||||
|
|
||||||
exports.mutateAttributionLines = (cs, lines, pool) => {
|
exports.mutateAttributionLines = (cs, lines, pool) => {
|
||||||
const unpacked = exports.unpack(cs);
|
const unpacked = exports.unpack(cs);
|
||||||
const csIter = new OpIter(unpacked.ops);
|
const csOps = exports.deserializeOps(unpacked.ops);
|
||||||
|
let csOpsNext = csOps.next();
|
||||||
const csBank = unpacked.charBank;
|
const csBank = unpacked.charBank;
|
||||||
let csBankIndex = 0;
|
let csBankIndex = 0;
|
||||||
// treat the attribution lines as text lines, mutating a line at a time
|
// treat the attribution lines as text lines, mutating a line at a time
|
||||||
const mut = new TextLinesMutator(lines);
|
const mut = new TextLinesMutator(lines);
|
||||||
|
|
||||||
/** @type {?OpIter} */
|
/** @type {?Generator<Op>} */
|
||||||
let lineIter = null;
|
let lineOps = null;
|
||||||
|
let lineOpsNext = null;
|
||||||
|
|
||||||
const isNextMutOp = () => (lineIter && lineIter.hasNext()) || mut.hasMore();
|
const lineOpsHasNext = () => lineOpsNext && !lineOpsNext.done;
|
||||||
|
const isNextMutOp = () => lineOpsHasNext() || mut.hasMore();
|
||||||
|
|
||||||
const nextMutOp = () => {
|
const nextMutOp = () => {
|
||||||
if ((!(lineIter && lineIter.hasNext())) && mut.hasMore()) {
|
if (!lineOpsHasNext() && mut.hasMore()) {
|
||||||
const line = mut.removeLines(1);
|
const line = mut.removeLines(1);
|
||||||
lineIter = new OpIter(line);
|
lineOps = exports.deserializeOps(line);
|
||||||
|
lineOpsNext = lineOps.next();
|
||||||
}
|
}
|
||||||
if (!lineIter || !lineIter.hasNext()) return new Op();
|
if (!lineOpsHasNext()) return new Op();
|
||||||
return lineIter.next();
|
const op = lineOpsNext.value;
|
||||||
|
lineOpsNext = lineOps.next();
|
||||||
|
return op;
|
||||||
};
|
};
|
||||||
let lineAssem = null;
|
let lineAssem = null;
|
||||||
|
|
||||||
|
@ -1308,12 +1318,15 @@ exports.mutateAttributionLines = (cs, lines, pool) => {
|
||||||
|
|
||||||
let csOp = new Op();
|
let csOp = new Op();
|
||||||
let attOp = new Op();
|
let attOp = new Op();
|
||||||
while (csOp.opcode || csIter.hasNext() || attOp.opcode || isNextMutOp()) {
|
while (csOp.opcode || !csOpsNext.done || attOp.opcode || isNextMutOp()) {
|
||||||
if (!csOp.opcode && csIter.hasNext()) csOp = csIter.next();
|
if (!csOp.opcode && !csOpsNext.done) {
|
||||||
if ((!csOp.opcode) && (!attOp.opcode) && (!lineAssem) && (!(lineIter && lineIter.hasNext()))) {
|
csOp = csOpsNext.value;
|
||||||
|
csOpsNext = csOps.next();
|
||||||
|
}
|
||||||
|
if (!csOp.opcode && !attOp.opcode && !lineAssem && !lineOpsHasNext()) {
|
||||||
break; // done
|
break; // done
|
||||||
} else if (csOp.opcode === '=' && csOp.lines > 0 && (!csOp.attribs) &&
|
} else if (csOp.opcode === '=' && csOp.lines > 0 && !csOp.attribs && !attOp.opcode &&
|
||||||
(!attOp.opcode) && (!lineAssem) && (!(lineIter && lineIter.hasNext()))) {
|
!lineAssem && !lineOpsHasNext()) {
|
||||||
// skip multiple lines; this is what makes small changes not order of the document size
|
// skip multiple lines; this is what makes small changes not order of the document size
|
||||||
mut.skipLines(csOp.lines);
|
mut.skipLines(csOp.lines);
|
||||||
csOp.opcode = '';
|
csOp.opcode = '';
|
||||||
|
@ -1350,16 +1363,12 @@ exports.mutateAttributionLines = (cs, lines, pool) => {
|
||||||
exports.joinAttributionLines = (theAlines) => {
|
exports.joinAttributionLines = (theAlines) => {
|
||||||
const assem = exports.mergingOpAssembler();
|
const assem = exports.mergingOpAssembler();
|
||||||
for (const aline of theAlines) {
|
for (const aline of theAlines) {
|
||||||
const iter = new OpIter(aline);
|
for (const op of exports.deserializeOps(aline)) assem.append(op);
|
||||||
while (iter.hasNext()) {
|
|
||||||
assem.append(iter.next());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return assem.toString();
|
return assem.toString();
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.splitAttributionLines = (attrOps, text) => {
|
exports.splitAttributionLines = (attrOps, text) => {
|
||||||
const iter = new OpIter(attrOps);
|
|
||||||
const assem = exports.mergingOpAssembler();
|
const assem = exports.mergingOpAssembler();
|
||||||
const lines = [];
|
const lines = [];
|
||||||
let pos = 0;
|
let pos = 0;
|
||||||
|
@ -1373,8 +1382,7 @@ exports.splitAttributionLines = (attrOps, text) => {
|
||||||
pos += op.chars;
|
pos += op.chars;
|
||||||
};
|
};
|
||||||
|
|
||||||
while (iter.hasNext()) {
|
for (const op of exports.deserializeOps(attrOps)) {
|
||||||
const op = iter.next();
|
|
||||||
let numChars = op.chars;
|
let numChars = op.chars;
|
||||||
let numLines = op.lines;
|
let numLines = op.lines;
|
||||||
while (numLines > 1) {
|
while (numLines > 1) {
|
||||||
|
@ -1517,11 +1525,9 @@ const toSplices = (cs) => {
|
||||||
const splices = [];
|
const splices = [];
|
||||||
|
|
||||||
let oldPos = 0;
|
let oldPos = 0;
|
||||||
const iter = new OpIter(unpacked.ops);
|
|
||||||
const charIter = exports.stringIterator(unpacked.charBank);
|
const charIter = exports.stringIterator(unpacked.charBank);
|
||||||
let inSplice = false;
|
let inSplice = false;
|
||||||
while (iter.hasNext()) {
|
for (const op of exports.deserializeOps(unpacked.ops)) {
|
||||||
const op = iter.next();
|
|
||||||
if (op.opcode === '=') {
|
if (op.opcode === '=') {
|
||||||
oldPos += op.chars;
|
oldPos += op.chars;
|
||||||
inSplice = false;
|
inSplice = false;
|
||||||
|
@ -1764,11 +1770,10 @@ exports.copyAText = (atext1, atext2) => {
|
||||||
*/
|
*/
|
||||||
exports.opsFromAText = function* (atext) {
|
exports.opsFromAText = function* (atext) {
|
||||||
// intentionally skips last newline char of atext
|
// intentionally skips last newline char of atext
|
||||||
const iter = new OpIter(atext.attribs);
|
|
||||||
let lastOp = null;
|
let lastOp = null;
|
||||||
while (iter.hasNext()) {
|
for (const op of exports.deserializeOps(atext.attribs)) {
|
||||||
if (lastOp != null) yield lastOp;
|
if (lastOp != null) yield lastOp;
|
||||||
lastOp = iter.next();
|
lastOp = op;
|
||||||
}
|
}
|
||||||
if (lastOp == null) return;
|
if (lastOp == null) return;
|
||||||
// exclude final newline
|
// exclude final newline
|
||||||
|
@ -1986,15 +1991,19 @@ exports.makeAttribsString = (opcode, attribs, pool) => {
|
||||||
* Like "substring" but on a single-line attribution string.
|
* Like "substring" but on a single-line attribution string.
|
||||||
*/
|
*/
|
||||||
exports.subattribution = (astr, start, optEnd) => {
|
exports.subattribution = (astr, start, optEnd) => {
|
||||||
const iter = new OpIter(astr);
|
const attOps = exports.deserializeOps(astr);
|
||||||
|
let attOpsNext = attOps.next();
|
||||||
const assem = exports.smartOpAssembler();
|
const assem = exports.smartOpAssembler();
|
||||||
let attOp = new Op();
|
let attOp = new Op();
|
||||||
const csOp = new Op();
|
const csOp = new Op();
|
||||||
|
|
||||||
const doCsOp = () => {
|
const doCsOp = () => {
|
||||||
if (!csOp.chars) return;
|
if (!csOp.chars) return;
|
||||||
while (csOp.opcode && (attOp.opcode || iter.hasNext())) {
|
while (csOp.opcode && (attOp.opcode || !attOpsNext.done)) {
|
||||||
if (!attOp.opcode) attOp = iter.next();
|
if (!attOp.opcode) {
|
||||||
|
attOp = attOpsNext.value;
|
||||||
|
attOpsNext = attOps.next();
|
||||||
|
}
|
||||||
if (csOp.opcode && attOp.opcode && csOp.chars >= attOp.chars &&
|
if (csOp.opcode && attOp.opcode && csOp.chars >= attOp.chars &&
|
||||||
attOp.lines > 0 && csOp.lines <= 0) {
|
attOp.lines > 0 && csOp.lines <= 0) {
|
||||||
csOp.lines++;
|
csOp.lines++;
|
||||||
|
@ -2013,7 +2022,10 @@ exports.subattribution = (astr, start, optEnd) => {
|
||||||
if (attOp.opcode) {
|
if (attOp.opcode) {
|
||||||
assem.append(attOp);
|
assem.append(attOp);
|
||||||
}
|
}
|
||||||
while (iter.hasNext()) assem.append(iter.next());
|
while (!attOpsNext.done) {
|
||||||
|
assem.append(attOpsNext.value);
|
||||||
|
attOpsNext = attOps.next();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
csOp.opcode = '=';
|
csOp.opcode = '=';
|
||||||
csOp.chars = optEnd - start;
|
csOp.chars = optEnd - start;
|
||||||
|
@ -2050,22 +2062,23 @@ exports.inverse = (cs, lines, alines, pool) => {
|
||||||
|
|
||||||
let curLine = 0;
|
let curLine = 0;
|
||||||
let curChar = 0;
|
let curChar = 0;
|
||||||
let curLineOpIter = null;
|
let curLineOps = null;
|
||||||
let curLineOpIterLine;
|
let curLineOpsNext = null;
|
||||||
|
let curLineOpsLine;
|
||||||
let curLineNextOp = new Op('+');
|
let curLineNextOp = new Op('+');
|
||||||
|
|
||||||
const unpacked = exports.unpack(cs);
|
const unpacked = exports.unpack(cs);
|
||||||
const csIter = new OpIter(unpacked.ops);
|
|
||||||
const builder = exports.builder(unpacked.newLen);
|
const builder = exports.builder(unpacked.newLen);
|
||||||
|
|
||||||
const consumeAttribRuns = (numChars, func /* (len, attribs, endsLine)*/) => {
|
const consumeAttribRuns = (numChars, func /* (len, attribs, endsLine)*/) => {
|
||||||
if ((!curLineOpIter) || (curLineOpIterLine !== curLine)) {
|
if (!curLineOps || curLineOpsLine !== curLine) {
|
||||||
// create curLineOpIter and advance it to curChar
|
curLineOps = exports.deserializeOps(alinesGet(curLine));
|
||||||
curLineOpIter = new OpIter(alinesGet(curLine));
|
curLineOpsNext = curLineOps.next();
|
||||||
curLineOpIterLine = curLine;
|
curLineOpsLine = curLine;
|
||||||
let indexIntoLine = 0;
|
let indexIntoLine = 0;
|
||||||
while (curLineOpIter.hasNext()) {
|
while (!curLineOpsNext.done) {
|
||||||
curLineNextOp = curLineOpIter.next();
|
curLineNextOp = curLineOpsNext.value;
|
||||||
|
curLineOpsNext = curLineOps.next();
|
||||||
if (indexIntoLine + curLineNextOp.chars >= curChar) {
|
if (indexIntoLine + curLineNextOp.chars >= curChar) {
|
||||||
curLineNextOp.chars -= (curChar - indexIntoLine);
|
curLineNextOp.chars -= (curChar - indexIntoLine);
|
||||||
break;
|
break;
|
||||||
|
@ -2075,15 +2088,21 @@ exports.inverse = (cs, lines, alines, pool) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
while (numChars > 0) {
|
while (numChars > 0) {
|
||||||
if ((!curLineNextOp.chars) && (!curLineOpIter.hasNext())) {
|
if (!curLineNextOp.chars && curLineOpsNext.done) {
|
||||||
curLine++;
|
curLine++;
|
||||||
curChar = 0;
|
curChar = 0;
|
||||||
curLineOpIterLine = curLine;
|
curLineOpsLine = curLine;
|
||||||
curLineNextOp.chars = 0;
|
curLineNextOp.chars = 0;
|
||||||
curLineOpIter = new OpIter(alinesGet(curLine));
|
curLineOps = exports.deserializeOps(alinesGet(curLine));
|
||||||
|
curLineOpsNext = curLineOps.next();
|
||||||
}
|
}
|
||||||
if (!curLineNextOp.chars) {
|
if (!curLineNextOp.chars) {
|
||||||
curLineNextOp = curLineOpIter.hasNext() ? curLineOpIter.next() : new Op();
|
if (curLineOpsNext.done) {
|
||||||
|
curLineNextOp = new Op();
|
||||||
|
} else {
|
||||||
|
curLineNextOp = curLineOpsNext.value;
|
||||||
|
curLineOpsNext = curLineOps.next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const charsToUse = Math.min(numChars, curLineNextOp.chars);
|
const charsToUse = Math.min(numChars, curLineNextOp.chars);
|
||||||
func(charsToUse, curLineNextOp.attribs, charsToUse === curLineNextOp.chars &&
|
func(charsToUse, curLineNextOp.attribs, charsToUse === curLineNextOp.chars &&
|
||||||
|
@ -2093,7 +2112,7 @@ exports.inverse = (cs, lines, alines, pool) => {
|
||||||
curChar += charsToUse;
|
curChar += charsToUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!curLineNextOp.chars) && (!curLineOpIter.hasNext())) {
|
if (!curLineNextOp.chars && curLineOpsNext.done) {
|
||||||
curLine++;
|
curLine++;
|
||||||
curChar = 0;
|
curChar = 0;
|
||||||
}
|
}
|
||||||
|
@ -2103,7 +2122,7 @@ exports.inverse = (cs, lines, alines, pool) => {
|
||||||
if (L) {
|
if (L) {
|
||||||
curLine += L;
|
curLine += L;
|
||||||
curChar = 0;
|
curChar = 0;
|
||||||
} else if (curLineOpIter && curLineOpIterLine === curLine) {
|
} else if (curLineOps && curLineOpsLine === curLine) {
|
||||||
consumeAttribRuns(N, () => {});
|
consumeAttribRuns(N, () => {});
|
||||||
} else {
|
} else {
|
||||||
curChar += N;
|
curChar += N;
|
||||||
|
@ -2138,8 +2157,7 @@ exports.inverse = (cs, lines, alines, pool) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
while (csIter.hasNext()) {
|
for (const csOp of exports.deserializeOps(unpacked.ops)) {
|
||||||
const csOp = csIter.next();
|
|
||||||
if (csOp.opcode === '=') {
|
if (csOp.opcode === '=') {
|
||||||
if (csOp.attribs) {
|
if (csOp.attribs) {
|
||||||
const attribs = AttributeMap.fromString(csOp.attribs, pool);
|
const attribs = AttributeMap.fromString(csOp.attribs, pool);
|
||||||
|
|
|
@ -1576,13 +1576,8 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
const end = selEnd[1];
|
const end = selEnd[1];
|
||||||
let hasAttrib = true;
|
let hasAttrib = true;
|
||||||
|
|
||||||
// Iterate over attribs on this line
|
|
||||||
|
|
||||||
const opIter = Changeset.opIterator(rep.alines[lineNum]);
|
|
||||||
let indexIntoLine = 0;
|
let indexIntoLine = 0;
|
||||||
|
for (const op of Changeset.deserializeOps(rep.alines[lineNum])) {
|
||||||
while (opIter.hasNext()) {
|
|
||||||
const op = opIter.next();
|
|
||||||
const opStartInLine = indexIntoLine;
|
const opStartInLine = indexIntoLine;
|
||||||
const opEndInLine = opStartInLine + op.chars;
|
const opEndInLine = opStartInLine + op.chars;
|
||||||
if (!hasIt(op.attribs)) {
|
if (!hasIt(op.attribs)) {
|
||||||
|
@ -1615,7 +1610,6 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
const selStartLine = rep.selStart[0];
|
const selStartLine = rep.selStart[0];
|
||||||
const selEndLine = rep.selEnd[0];
|
const selEndLine = rep.selEnd[0];
|
||||||
for (let n = selStartLine; n <= selEndLine; n++) {
|
for (let n = selStartLine; n <= selEndLine; n++) {
|
||||||
const opIter = Changeset.opIterator(rep.alines[n]);
|
|
||||||
let indexIntoLine = 0;
|
let indexIntoLine = 0;
|
||||||
let selectionStartInLine = 0;
|
let selectionStartInLine = 0;
|
||||||
if (documentAttributeManager.lineHasMarker(n)) {
|
if (documentAttributeManager.lineHasMarker(n)) {
|
||||||
|
@ -1628,8 +1622,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
if (n === selEndLine) {
|
if (n === selEndLine) {
|
||||||
selectionEndInLine = rep.selEnd[1];
|
selectionEndInLine = rep.selEnd[1];
|
||||||
}
|
}
|
||||||
while (opIter.hasNext()) {
|
for (const op of Changeset.deserializeOps(rep.alines[n])) {
|
||||||
const op = opIter.next();
|
|
||||||
const opStartInLine = indexIntoLine;
|
const opStartInLine = indexIntoLine;
|
||||||
const opEndInLine = opStartInLine + op.chars;
|
const opEndInLine = opStartInLine + op.chars;
|
||||||
if (!hasIt(op.attribs)) {
|
if (!hasIt(op.attribs)) {
|
||||||
|
@ -1754,12 +1747,10 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const eachAttribRun = (attribs, func /* (startInNewText, endInNewText, attribs)*/) => {
|
const eachAttribRun = (attribs, func /* (startInNewText, endInNewText, attribs)*/) => {
|
||||||
const attribsIter = Changeset.opIterator(attribs);
|
|
||||||
let textIndex = 0;
|
let textIndex = 0;
|
||||||
const newTextStart = commonStart;
|
const newTextStart = commonStart;
|
||||||
const newTextEnd = newText.length - commonEnd - (shiftFinalNewlineToBeforeNewText ? 1 : 0);
|
const newTextEnd = newText.length - commonEnd - (shiftFinalNewlineToBeforeNewText ? 1 : 0);
|
||||||
while (attribsIter.hasNext()) {
|
for (const op of Changeset.deserializeOps(attribs)) {
|
||||||
const op = attribsIter.next();
|
|
||||||
const nextIndex = textIndex + op.chars;
|
const nextIndex = textIndex + op.chars;
|
||||||
if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) {
|
if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) {
|
||||||
func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs);
|
func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs);
|
||||||
|
@ -1873,9 +1864,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
const attribRuns = (attribs) => {
|
const attribRuns = (attribs) => {
|
||||||
const lengs = [];
|
const lengs = [];
|
||||||
const atts = [];
|
const atts = [];
|
||||||
const iter = Changeset.opIterator(attribs);
|
for (const op of Changeset.deserializeOps(attribs)) {
|
||||||
while (iter.hasNext()) {
|
|
||||||
const op = iter.next();
|
|
||||||
lengs.push(op.chars);
|
lengs.push(op.chars);
|
||||||
atts.push(op.attribs);
|
atts.push(op.attribs);
|
||||||
}
|
}
|
||||||
|
@ -2619,9 +2608,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
// TODO: There appears to be a race condition or so.
|
// TODO: There appears to be a race condition or so.
|
||||||
const authorIds = new Set();
|
const authorIds = new Set();
|
||||||
if (alineAttrs) {
|
if (alineAttrs) {
|
||||||
const opIter = Changeset.opIterator(alineAttrs);
|
for (const op of Changeset.deserializeOps(alineAttrs)) {
|
||||||
while (opIter.hasNext()) {
|
|
||||||
const op = opIter.next();
|
|
||||||
const authorId = AttributeMap.fromString(op.attribs, apool).get('author');
|
const authorId = AttributeMap.fromString(op.attribs, apool).get('author');
|
||||||
if (authorId) authorIds.add(authorId);
|
if (authorId) authorIds.add(authorId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,13 +162,8 @@ const loadBroadcastJS = (socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
|
||||||
// some chars are replaced (no attributes change and no length change)
|
// some chars are replaced (no attributes change and no length change)
|
||||||
// test if there are keep ops at the start of the cs
|
// test if there are keep ops at the start of the cs
|
||||||
if (lineChanged === undefined) {
|
if (lineChanged === undefined) {
|
||||||
lineChanged = 0;
|
const [op] = Changeset.deserializeOps(Changeset.unpack(changeset).ops);
|
||||||
const opIter = Changeset.opIterator(Changeset.unpack(changeset).ops);
|
lineChanged = op != null && op.opcode === '=' ? op.lines : 0;
|
||||||
|
|
||||||
if (opIter.hasNext()) {
|
|
||||||
const op = opIter.next();
|
|
||||||
if (op.opcode === '=') lineChanged += op.lines;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToLineNumber = (lineNumber) => {
|
const goToLineNumber = (lineNumber) => {
|
||||||
|
|
|
@ -143,12 +143,9 @@ 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 iterator = Changeset.opIterator(cs.ops);
|
|
||||||
let op;
|
|
||||||
const assem = Changeset.mergingOpAssembler();
|
const assem = Changeset.mergingOpAssembler();
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
for (const op of Changeset.deserializeOps(cs.ops)) {
|
||||||
op = iterator.next();
|
|
||||||
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');
|
||||||
|
|
|
@ -98,11 +98,13 @@ linestylefilter.getLineStyleFilter = (lineLength, aline, textAndClassFunc, apool
|
||||||
return classes.substring(1);
|
return classes.substring(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
const attributionIter = Changeset.opIterator(aline);
|
const attrOps = Changeset.deserializeOps(aline);
|
||||||
|
let attrOpsNext = attrOps.next();
|
||||||
let nextOp, nextOpClasses;
|
let nextOp, nextOpClasses;
|
||||||
|
|
||||||
const goNextOp = () => {
|
const goNextOp = () => {
|
||||||
nextOp = attributionIter.hasNext() ? attributionIter.next() : new Changeset.Op();
|
nextOp = attrOpsNext.done ? new Changeset.Op() : attrOpsNext.value;
|
||||||
|
if (!attrOpsNext.done) attrOpsNext = attrOps.next();
|
||||||
nextOpClasses = (nextOp.opcode && attribsToClasses(nextOp.attribs));
|
nextOpClasses = (nextOp.opcode && attribsToClasses(nextOp.attribs));
|
||||||
};
|
};
|
||||||
goNextOp();
|
goNextOp();
|
||||||
|
|
|
@ -31,17 +31,15 @@ const randInt = (maxValue) => Math.floor(Math.random() * maxValue);
|
||||||
describe('easysync', function () {
|
describe('easysync', function () {
|
||||||
it('throughIterator', async function () {
|
it('throughIterator', async function () {
|
||||||
const x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1';
|
const x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1';
|
||||||
const iter = Changeset.opIterator(x);
|
|
||||||
const assem = Changeset.opAssembler();
|
const assem = Changeset.opAssembler();
|
||||||
while (iter.hasNext()) assem.append(iter.next());
|
for (const op of Changeset.deserializeOps(x)) assem.append(op);
|
||||||
expect(assem.toString()).to.equal(x);
|
expect(assem.toString()).to.equal(x);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throughSmartAssembler', async function () {
|
it('throughSmartAssembler', async function () {
|
||||||
const x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1';
|
const x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1';
|
||||||
const iter = Changeset.opIterator(x);
|
|
||||||
const assem = Changeset.smartOpAssembler();
|
const assem = Changeset.smartOpAssembler();
|
||||||
while (iter.hasNext()) assem.append(iter.next());
|
for (const op of Changeset.deserializeOps(x)) assem.append(op);
|
||||||
assem.endDocument();
|
assem.endDocument();
|
||||||
expect(assem.toString()).to.equal(x);
|
expect(assem.toString()).to.equal(x);
|
||||||
});
|
});
|
||||||
|
@ -730,7 +728,7 @@ describe('easysync', function () {
|
||||||
p.putAttrib(['name', 'david']);
|
p.putAttrib(['name', 'david']);
|
||||||
p.putAttrib(['color', 'green']);
|
p.putAttrib(['color', 'green']);
|
||||||
|
|
||||||
const stringOp = (str) => Changeset.opIterator(str).next();
|
const stringOp = (str) => Changeset.deserializeOps(str).next().value;
|
||||||
|
|
||||||
expect(Changeset.opAttributeValue(stringOp('*0*1+1'), 'name', p)).to.equal('david');
|
expect(Changeset.opAttributeValue(stringOp('*0*1+1'), 'name', p)).to.equal('david');
|
||||||
expect(Changeset.opAttributeValue(stringOp('*0+1'), 'name', p)).to.equal('david');
|
expect(Changeset.opAttributeValue(stringOp('*0+1'), 'name', p)).to.equal('david');
|
||||||
|
|
Loading…
Reference in a new issue