mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-01-31 19:02:59 +01:00
Changeset: Avoid unnecessary StringAssembler
class
This commit is contained in:
parent
2d0e393839
commit
cca906e8fc
6 changed files with 54 additions and 83 deletions
|
@ -125,7 +125,7 @@ const getHTMLFromAtext = async (pad, atext, authorColors) => {
|
||||||
// becomes
|
// becomes
|
||||||
// <b>Just bold <i>Bold and italics</i></b> <i>Just italics</i>
|
// <b>Just bold <i>Bold and italics</i></b> <i>Just italics</i>
|
||||||
const taker = Changeset.stringIterator(text);
|
const taker = Changeset.stringIterator(text);
|
||||||
const assem = Changeset.stringAssembler();
|
let assem = '';
|
||||||
const openTags = [];
|
const openTags = [];
|
||||||
|
|
||||||
const getSpanClassFor = (i) => {
|
const getSpanClassFor = (i) => {
|
||||||
|
@ -161,16 +161,7 @@ const getHTMLFromAtext = async (pad, atext, authorColors) => {
|
||||||
const emitOpenTag = (i) => {
|
const emitOpenTag = (i) => {
|
||||||
openTags.unshift(i);
|
openTags.unshift(i);
|
||||||
const spanClass = getSpanClassFor(i);
|
const spanClass = getSpanClassFor(i);
|
||||||
|
assem += spanClass ? `<span class="${spanClass}">` : `<${tags[i]}>`;
|
||||||
if (spanClass) {
|
|
||||||
assem.append('<span class="');
|
|
||||||
assem.append(spanClass);
|
|
||||||
assem.append('">');
|
|
||||||
} else {
|
|
||||||
assem.append('<');
|
|
||||||
assem.append(tags[i]);
|
|
||||||
assem.append('>');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// this closes an open tag and removes its reference from openTags
|
// this closes an open tag and removes its reference from openTags
|
||||||
|
@ -178,14 +169,7 @@ const getHTMLFromAtext = async (pad, atext, authorColors) => {
|
||||||
openTags.shift();
|
openTags.shift();
|
||||||
const spanClass = getSpanClassFor(i);
|
const spanClass = getSpanClassFor(i);
|
||||||
const spanWithData = isSpanWithData(i);
|
const spanWithData = isSpanWithData(i);
|
||||||
|
assem += spanClass || spanWithData ? '</span>' : `</${tags[i]}>`;
|
||||||
if (spanClass || spanWithData) {
|
|
||||||
assem.append('</span>');
|
|
||||||
} else {
|
|
||||||
assem.append('</');
|
|
||||||
assem.append(tags[i]);
|
|
||||||
assem.append('>');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const urls = padutils.findURLs(text);
|
const urls = padutils.findURLs(text);
|
||||||
|
@ -246,7 +230,7 @@ const getHTMLFromAtext = async (pad, atext, authorColors) => {
|
||||||
// from but they break the abiword parser and are completly useless
|
// from but they break the abiword parser and are completly useless
|
||||||
s = s.replace(String.fromCharCode(12), '');
|
s = s.replace(String.fromCharCode(12), '');
|
||||||
|
|
||||||
assem.append(_encodeWhitespace(Security.escapeHTML(s)));
|
assem += _encodeWhitespace(Security.escapeHTML(s));
|
||||||
} // end iteration over spans in line
|
} // end iteration over spans in line
|
||||||
|
|
||||||
// close all the tags that are open after the last op
|
// close all the tags that are open after the last op
|
||||||
|
@ -269,14 +253,14 @@ const getHTMLFromAtext = async (pad, atext, authorColors) => {
|
||||||
// https://html.spec.whatwg.org/multipage/links.html#link-type-noopener
|
// https://html.spec.whatwg.org/multipage/links.html#link-type-noopener
|
||||||
// https://mathiasbynens.github.io/rel-noopener/
|
// https://mathiasbynens.github.io/rel-noopener/
|
||||||
// https://github.com/ether/etherpad-lite/pull/3636
|
// https://github.com/ether/etherpad-lite/pull/3636
|
||||||
assem.append(`<a href="${Security.escapeHTMLAttribute(url)}" rel="noreferrer noopener">`);
|
assem += `<a href="${Security.escapeHTMLAttribute(url)}" rel="noreferrer noopener">`;
|
||||||
processNextChars(urlLength);
|
processNextChars(urlLength);
|
||||||
assem.append('</a>');
|
assem += '</a>';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
processNextChars(text.length - idx);
|
processNextChars(text.length - idx);
|
||||||
|
|
||||||
return _processSpaces(assem.toString());
|
return _processSpaces(assem);
|
||||||
};
|
};
|
||||||
// end getLineHTML
|
// end getLineHTML
|
||||||
const pieces = [css];
|
const pieces = [css];
|
||||||
|
|
|
@ -67,7 +67,7 @@ const getTXTFromAtext = (pad, atext, authorColors) => {
|
||||||
// becomes
|
// becomes
|
||||||
// <b>Just bold <i>Bold and italics</i></b> <i>Just italics</i>
|
// <b>Just bold <i>Bold and italics</i></b> <i>Just italics</i>
|
||||||
const taker = Changeset.stringIterator(text);
|
const taker = Changeset.stringIterator(text);
|
||||||
const assem = Changeset.stringAssembler();
|
let assem = '';
|
||||||
|
|
||||||
let idx = 0;
|
let idx = 0;
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ const getTXTFromAtext = (pad, atext, authorColors) => {
|
||||||
// plugins from being able to display * at the beginning of a line
|
// plugins from being able to display * at the beginning of a line
|
||||||
// s = s.replace("*", ""); // Then remove it
|
// s = s.replace("*", ""); // Then remove it
|
||||||
|
|
||||||
assem.append(s);
|
assem += s;
|
||||||
} // end iteration over spans in line
|
} // end iteration over spans in line
|
||||||
|
|
||||||
const tags2close = [];
|
const tags2close = [];
|
||||||
|
@ -175,7 +175,7 @@ const getTXTFromAtext = (pad, atext, authorColors) => {
|
||||||
// end processNextChars
|
// end processNextChars
|
||||||
|
|
||||||
processNextChars(text.length - idx);
|
processNextChars(text.length - idx);
|
||||||
return (assem.toString());
|
return assem;
|
||||||
};
|
};
|
||||||
// end getLineHTML
|
// end getLineHTML
|
||||||
|
|
||||||
|
|
|
@ -328,21 +328,21 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
|
||||||
|
|
||||||
const nextText = (numChars) => {
|
const nextText = (numChars) => {
|
||||||
let len = 0;
|
let len = 0;
|
||||||
const assem = Changeset.stringAssembler();
|
let assem = '';
|
||||||
const firstString = linesGet(curLine).substring(curChar);
|
const firstString = linesGet(curLine).substring(curChar);
|
||||||
len += firstString.length;
|
len += firstString.length;
|
||||||
assem.append(firstString);
|
assem += firstString;
|
||||||
|
|
||||||
let lineNum = curLine + 1;
|
let lineNum = curLine + 1;
|
||||||
|
|
||||||
while (len < numChars) {
|
while (len < numChars) {
|
||||||
const nextString = linesGet(lineNum);
|
const nextString = linesGet(lineNum);
|
||||||
len += nextString.length;
|
len += nextString.length;
|
||||||
assem.append(nextString);
|
assem += nextString;
|
||||||
lineNum++;
|
lineNum++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return assem.toString().substring(0, numChars);
|
return assem.substring(0, numChars);
|
||||||
};
|
};
|
||||||
|
|
||||||
const cachedStrFunc = (func) => {
|
const cachedStrFunc = (func) => {
|
||||||
|
|
|
@ -731,7 +731,11 @@ class StringAssembler {
|
||||||
/**
|
/**
|
||||||
* @returns {StringAssembler}
|
* @returns {StringAssembler}
|
||||||
*/
|
*/
|
||||||
exports.stringAssembler = () => new StringAssembler();
|
exports.stringAssembler = () => {
|
||||||
|
padutils.warnDeprecated(
|
||||||
|
'Changeset.stringAssembler() is deprecated; build a string manually instead');
|
||||||
|
return new StringAssembler();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} StringArrayLike
|
* @typedef {object} StringArrayLike
|
||||||
|
@ -1158,7 +1162,7 @@ exports.applyToText = (cs, str) => {
|
||||||
assert(str.length === unpacked.oldLen, `mismatched apply: ${str.length} / ${unpacked.oldLen}`);
|
assert(str.length === unpacked.oldLen, `mismatched apply: ${str.length} / ${unpacked.oldLen}`);
|
||||||
const bankIter = exports.stringIterator(unpacked.charBank);
|
const bankIter = exports.stringIterator(unpacked.charBank);
|
||||||
const strIter = exports.stringIterator(str);
|
const strIter = exports.stringIterator(str);
|
||||||
const assem = new StringAssembler();
|
let assem = '';
|
||||||
for (const op of exports.deserializeOps(unpacked.ops)) {
|
for (const op of exports.deserializeOps(unpacked.ops)) {
|
||||||
switch (op.opcode) {
|
switch (op.opcode) {
|
||||||
case '+':
|
case '+':
|
||||||
|
@ -1167,7 +1171,7 @@ exports.applyToText = (cs, str) => {
|
||||||
if (op.lines !== bankIter.peek(op.chars).split('\n').length - 1) {
|
if (op.lines !== bankIter.peek(op.chars).split('\n').length - 1) {
|
||||||
throw new Error(`newline count is wrong in op +; cs:${cs} and text:${str}`);
|
throw new Error(`newline count is wrong in op +; cs:${cs} and text:${str}`);
|
||||||
}
|
}
|
||||||
assem.append(bankIter.take(op.chars));
|
assem += bankIter.take(op.chars);
|
||||||
break;
|
break;
|
||||||
case '-':
|
case '-':
|
||||||
// op is - and op.lines 0: no newlines must be in the deleted string
|
// op is - and op.lines 0: no newlines must be in the deleted string
|
||||||
|
@ -1183,12 +1187,12 @@ exports.applyToText = (cs, str) => {
|
||||||
if (op.lines !== strIter.peek(op.chars).split('\n').length - 1) {
|
if (op.lines !== strIter.peek(op.chars).split('\n').length - 1) {
|
||||||
throw new Error(`newline count is wrong in op =; cs:${cs} and text:${str}`);
|
throw new Error(`newline count is wrong in op =; cs:${cs} and text:${str}`);
|
||||||
}
|
}
|
||||||
assem.append(strIter.take(op.chars));
|
assem += strIter.take(op.chars);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assem.append(strIter.take(strIter.remaining()));
|
assem += strIter.take(strIter.remaining());
|
||||||
return assem.toString();
|
return assem;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1477,7 +1481,7 @@ exports.compose = (cs1, cs2, pool) => {
|
||||||
const len3 = unpacked2.newLen;
|
const len3 = unpacked2.newLen;
|
||||||
const bankIter1 = exports.stringIterator(unpacked1.charBank);
|
const bankIter1 = exports.stringIterator(unpacked1.charBank);
|
||||||
const bankIter2 = exports.stringIterator(unpacked2.charBank);
|
const bankIter2 = exports.stringIterator(unpacked2.charBank);
|
||||||
const bankAssem = new StringAssembler();
|
let bankAssem = '';
|
||||||
|
|
||||||
const newOps = applyZip(unpacked1.ops, unpacked2.ops, (op1, op2) => {
|
const newOps = applyZip(unpacked1.ops, unpacked2.ops, (op1, op2) => {
|
||||||
const op1code = op1.opcode;
|
const op1code = op1.opcode;
|
||||||
|
@ -1487,16 +1491,12 @@ exports.compose = (cs1, cs2, pool) => {
|
||||||
}
|
}
|
||||||
const opOut = slicerZipperFunc(op1, op2, pool);
|
const opOut = slicerZipperFunc(op1, op2, pool);
|
||||||
if (opOut.opcode === '+') {
|
if (opOut.opcode === '+') {
|
||||||
if (op2code === '+') {
|
bankAssem += (op2code === '+' ? bankIter2 : bankIter1).take(opOut.chars);
|
||||||
bankAssem.append(bankIter2.take(opOut.chars));
|
|
||||||
} else {
|
|
||||||
bankAssem.append(bankIter1.take(opOut.chars));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return opOut;
|
return opOut;
|
||||||
});
|
});
|
||||||
|
|
||||||
return exports.pack(len1, len3, newOps, bankAssem.toString());
|
return exports.pack(len1, len3, newOps, bankAssem);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1920,7 +1920,7 @@ class Builder {
|
||||||
constructor(oldLen) {
|
constructor(oldLen) {
|
||||||
this._oldLen = oldLen;
|
this._oldLen = oldLen;
|
||||||
this._ops = [];
|
this._ops = [];
|
||||||
this._charBank = new StringAssembler();
|
this._charBank = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1966,7 +1966,7 @@ class Builder {
|
||||||
*/
|
*/
|
||||||
insert(text, attribs = '', pool = null) {
|
insert(text, attribs = '', pool = null) {
|
||||||
this._ops.push(...opsFromText('+', text, attribs, pool));
|
this._ops.push(...opsFromText('+', text, attribs, pool));
|
||||||
this._charBank.append(text);
|
this._charBank += text;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1998,7 +1998,7 @@ class Builder {
|
||||||
oldLen: this._oldLen,
|
oldLen: this._oldLen,
|
||||||
newLen: this._oldLen + lengthChange,
|
newLen: this._oldLen + lengthChange,
|
||||||
ops: serializedOps,
|
ops: serializedOps,
|
||||||
charBank: this._charBank.toString(),
|
charBank: this._charBank,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2181,20 +2181,20 @@ exports.inverse = (cs, lines, alines, pool) => {
|
||||||
|
|
||||||
const nextText = (numChars) => {
|
const nextText = (numChars) => {
|
||||||
let len = 0;
|
let len = 0;
|
||||||
const assem = new StringAssembler();
|
let assem = '';
|
||||||
const firstString = linesGet(curLine).substring(curChar);
|
const firstString = linesGet(curLine).substring(curChar);
|
||||||
len += firstString.length;
|
len += firstString.length;
|
||||||
assem.append(firstString);
|
assem += firstString;
|
||||||
|
|
||||||
let lineNum = curLine + 1;
|
let lineNum = curLine + 1;
|
||||||
while (len < numChars) {
|
while (len < numChars) {
|
||||||
const nextString = linesGet(lineNum);
|
const nextString = linesGet(lineNum);
|
||||||
len += nextString.length;
|
len += nextString.length;
|
||||||
assem.append(nextString);
|
assem += nextString;
|
||||||
lineNum++;
|
lineNum++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return assem.toString().substring(0, numChars);
|
return assem.substring(0, numChars);
|
||||||
};
|
};
|
||||||
|
|
||||||
const cachedStrFunc = (func) => {
|
const cachedStrFunc = (func) => {
|
||||||
|
@ -2407,12 +2407,9 @@ const followAttributes = (att1, att2, pool) => {
|
||||||
return '';
|
return '';
|
||||||
});
|
});
|
||||||
// we've only removed attributes, so they're already sorted
|
// we've only removed attributes, so they're already sorted
|
||||||
const buf = new StringAssembler();
|
let buf = '';
|
||||||
for (const att of atts) {
|
for (const att of atts) buf += `*${exports.numToString(pool.putAttrib(att))}`;
|
||||||
buf.append('*');
|
return buf;
|
||||||
buf.append(exports.numToString(pool.putAttrib(att)));
|
|
||||||
}
|
|
||||||
return buf.toString();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.exportedForTestingOnly = {
|
exports.exportedForTestingOnly = {
|
||||||
|
|
|
@ -20,29 +20,19 @@ const poolOrArray = (attribs) => {
|
||||||
exports.poolOrArray = poolOrArray;
|
exports.poolOrArray = poolOrArray;
|
||||||
|
|
||||||
const randomInlineString = (len) => {
|
const randomInlineString = (len) => {
|
||||||
const assem = Changeset.stringAssembler();
|
let assem = '';
|
||||||
for (let i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) assem += String.fromCharCode(randInt(26) + 97);
|
||||||
assem.append(String.fromCharCode(randInt(26) + 97));
|
return assem;
|
||||||
}
|
|
||||||
return assem.toString();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const randomMultiline = (approxMaxLines, approxMaxCols) => {
|
const randomMultiline = (approxMaxLines, approxMaxCols) => {
|
||||||
const numParts = randInt(approxMaxLines * 2) + 1;
|
const numParts = randInt(approxMaxLines * 2) + 1;
|
||||||
const txt = Changeset.stringAssembler();
|
let txt = '';
|
||||||
txt.append(randInt(2) ? '\n' : '');
|
txt += randInt(2) ? '\n' : '';
|
||||||
for (let i = 0; i < numParts; i++) {
|
for (let i = 0; i < numParts; i++) {
|
||||||
if ((i % 2) === 0) {
|
txt += i % 2 === 0 && randInt(10) ? randomInlineString(randInt(approxMaxCols) + 1) : '\n';
|
||||||
if (randInt(10)) {
|
|
||||||
txt.append(randomInlineString(randInt(approxMaxCols) + 1));
|
|
||||||
} else {
|
|
||||||
txt.append('\n');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
txt.append('\n');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return txt.toString();
|
return txt;
|
||||||
};
|
};
|
||||||
exports.randomMultiline = randomMultiline;
|
exports.randomMultiline = randomMultiline;
|
||||||
|
|
||||||
|
@ -165,9 +155,9 @@ const randomTwoPropAttribs = (opcode) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const randomTestChangeset = (origText, withAttribs) => {
|
const randomTestChangeset = (origText, withAttribs) => {
|
||||||
const charBank = Changeset.stringAssembler();
|
let charBank = '';
|
||||||
let textLeft = origText; // always keep final newline
|
let textLeft = origText; // always keep final newline
|
||||||
const outTextAssem = Changeset.stringAssembler();
|
let outTextAssem = '';
|
||||||
const ops = [];
|
const ops = [];
|
||||||
const oldLen = origText.length;
|
const oldLen = origText.length;
|
||||||
|
|
||||||
|
@ -192,13 +182,13 @@ const randomTestChangeset = (origText, withAttribs) => {
|
||||||
const o = randomStringOperation(textLeft.length);
|
const o = randomStringOperation(textLeft.length);
|
||||||
if (o.insert) {
|
if (o.insert) {
|
||||||
const txt = o.insert;
|
const txt = o.insert;
|
||||||
charBank.append(txt);
|
charBank += txt;
|
||||||
outTextAssem.append(txt);
|
outTextAssem += txt;
|
||||||
appendMultilineOp('+', txt);
|
appendMultilineOp('+', txt);
|
||||||
} else if (o.skip) {
|
} else if (o.skip) {
|
||||||
const txt = textLeft.substring(0, o.skip);
|
const txt = textLeft.substring(0, o.skip);
|
||||||
textLeft = textLeft.substring(o.skip);
|
textLeft = textLeft.substring(o.skip);
|
||||||
outTextAssem.append(txt);
|
outTextAssem += txt;
|
||||||
appendMultilineOp('=', txt);
|
appendMultilineOp('=', txt);
|
||||||
} else if (o.remove) {
|
} else if (o.remove) {
|
||||||
const txt = textLeft.substring(0, o.remove);
|
const txt = textLeft.substring(0, o.remove);
|
||||||
|
@ -209,9 +199,9 @@ const randomTestChangeset = (origText, withAttribs) => {
|
||||||
|
|
||||||
while (textLeft.length > 1) doOp();
|
while (textLeft.length > 1) doOp();
|
||||||
for (let i = 0; i < 5; i++) doOp(); // do some more (only insertions will happen)
|
for (let i = 0; i < 5; i++) doOp(); // do some more (only insertions will happen)
|
||||||
const outText = `${outTextAssem.toString()}\n`;
|
const outText = `${outTextAssem}\n`;
|
||||||
const serializedOps = Changeset.serializeOps(Changeset.canonicalizeOps(ops, true));
|
const serializedOps = Changeset.serializeOps(Changeset.canonicalizeOps(ops, true));
|
||||||
const cs = Changeset.pack(oldLen, outText.length, serializedOps, charBank.toString());
|
const cs = Changeset.pack(oldLen, outText.length, serializedOps, charBank);
|
||||||
Changeset.checkRep(cs);
|
Changeset.checkRep(cs);
|
||||||
return [cs, outText];
|
return [cs, outText];
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,7 +15,7 @@ describe('easysync-mutations', function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
const mutationsToChangeset = (oldLen, arrayOfArrays) => {
|
const mutationsToChangeset = (oldLen, arrayOfArrays) => {
|
||||||
const bank = Changeset.stringAssembler();
|
let bank = '';
|
||||||
let oldPos = 0;
|
let oldPos = 0;
|
||||||
let newLen = 0;
|
let newLen = 0;
|
||||||
const ops = (function* () {
|
const ops = (function* () {
|
||||||
|
@ -34,7 +34,7 @@ describe('easysync-mutations', function () {
|
||||||
oldPos += op.chars;
|
oldPos += op.chars;
|
||||||
} else if (a[0] === 'insert') {
|
} else if (a[0] === 'insert') {
|
||||||
op.opcode = '+';
|
op.opcode = '+';
|
||||||
bank.append(a[1]);
|
bank += a[1];
|
||||||
op.chars = a[1].length;
|
op.chars = a[1].length;
|
||||||
op.lines = (a[2] || 0);
|
op.lines = (a[2] || 0);
|
||||||
newLen += op.chars;
|
newLen += op.chars;
|
||||||
|
@ -44,7 +44,7 @@ describe('easysync-mutations', function () {
|
||||||
})();
|
})();
|
||||||
const serializedOps = Changeset.serializeOps(Changeset.canonicalizeOps(ops, true));
|
const serializedOps = Changeset.serializeOps(Changeset.canonicalizeOps(ops, true));
|
||||||
newLen += oldLen - oldPos;
|
newLen += oldLen - oldPos;
|
||||||
return Changeset.pack(oldLen, newLen, serializedOps, bank.toString());
|
return Changeset.pack(oldLen, newLen, serializedOps, bank);
|
||||||
};
|
};
|
||||||
|
|
||||||
const runMutationTest = (testId, origLines, muts, correct) => {
|
const runMutationTest = (testId, origLines, muts, correct) => {
|
||||||
|
|
Loading…
Reference in a new issue