Skip to content

Commit 7b4ad85

Browse files
committed
Changeset: Avoid unnecessary StringAssembler class
1 parent 6f5014a commit 7b4ad85

File tree

5 files changed

+56
-87
lines changed

5 files changed

+56
-87
lines changed

src/node/utils/ExportHtml.js

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ const getHTMLFromAtext = async (pad, atext, authorColors) => {
124124
// becomes
125125
// <b>Just bold <i>Bold and italics</i></b> <i>Just italics</i>
126126
const taker = Changeset.stringIterator(text);
127-
const assem = Changeset.stringAssembler();
127+
let assem = '';
128128
const openTags = [];
129129

130130
const getSpanClassFor = (i) => {
@@ -160,31 +160,15 @@ const getHTMLFromAtext = async (pad, atext, authorColors) => {
160160
const emitOpenTag = (i) => {
161161
openTags.unshift(i);
162162
const spanClass = getSpanClassFor(i);
163-
164-
if (spanClass) {
165-
assem.append('<span class="');
166-
assem.append(spanClass);
167-
assem.append('">');
168-
} else {
169-
assem.append('<');
170-
assem.append(tags[i]);
171-
assem.append('>');
172-
}
163+
assem += spanClass ? `<span class="${spanClass}">` : `<${tags[i]}>`;
173164
};
174165

175166
// this closes an open tag and removes its reference from openTags
176167
const emitCloseTag = (i) => {
177168
openTags.shift();
178169
const spanClass = getSpanClassFor(i);
179170
const spanWithData = isSpanWithData(i);
180-
181-
if (spanClass || spanWithData) {
182-
assem.append('</span>');
183-
} else {
184-
assem.append('</');
185-
assem.append(tags[i]);
186-
assem.append('>');
187-
}
171+
assem += spanClass || spanWithData ? '</span>' : `</${tags[i]}>`;
188172
};
189173

190174
const urls = padutils.findURLs(text);
@@ -245,7 +229,7 @@ const getHTMLFromAtext = async (pad, atext, authorColors) => {
245229
// from but they break the abiword parser and are completly useless
246230
s = s.replace(String.fromCharCode(12), '');
247231

248-
assem.append(_encodeWhitespace(Security.escapeHTML(s)));
232+
assem += _encodeWhitespace(Security.escapeHTML(s));
249233
} // end iteration over spans in line
250234

251235
// close all the tags that are open after the last op
@@ -268,14 +252,14 @@ const getHTMLFromAtext = async (pad, atext, authorColors) => {
268252
// https://html.spec.whatwg.org/multipage/links.html#link-type-noopener
269253
// https://mathiasbynens.github.io/rel-noopener/
270254
// https://github.com/ether/etherpad-lite/pull/3636
271-
assem.append(`<a href="${Security.escapeHTMLAttribute(url)}" rel="noreferrer noopener">`);
255+
assem += `<a href="${Security.escapeHTMLAttribute(url)}" rel="noreferrer noopener">`;
272256
processNextChars(urlLength);
273-
assem.append('</a>');
257+
assem += '</a>';
274258
});
275259
}
276260
processNextChars(text.length - idx);
277261

278-
return _processSpaces(assem.toString());
262+
return _processSpaces(assem);
279263
};
280264
// end getLineHTML
281265
const pieces = [css];

src/node/utils/ExportTxt.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ const getTXTFromAtext = (pad, atext, authorColors) => {
6666
// becomes
6767
// <b>Just bold <i>Bold and italics</i></b> <i>Just italics</i>
6868
const taker = Changeset.stringIterator(text);
69-
const assem = Changeset.stringAssembler();
69+
let assem = '';
7070

7171
let idx = 0;
7272

@@ -160,7 +160,7 @@ const getTXTFromAtext = (pad, atext, authorColors) => {
160160
// plugins from being able to display * at the beginning of a line
161161
// s = s.replace("*", ""); // Then remove it
162162

163-
assem.append(s);
163+
assem += s;
164164
} // end iteration over spans in line
165165

166166
const tags2close = [];
@@ -174,7 +174,7 @@ const getTXTFromAtext = (pad, atext, authorColors) => {
174174
// end processNextChars
175175

176176
processNextChars(text.length - idx);
177-
return (assem.toString());
177+
return assem;
178178
};
179179
// end getLineHTML
180180

src/node/utils/padDiff.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -331,21 +331,21 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
331331

332332
const nextText = (numChars) => {
333333
let len = 0;
334-
const assem = Changeset.stringAssembler();
334+
let assem = '';
335335
const firstString = linesGet(curLine).substring(curChar);
336336
len += firstString.length;
337-
assem.append(firstString);
337+
assem += firstString;
338338

339339
let lineNum = curLine + 1;
340340

341341
while (len < numChars) {
342342
const nextString = linesGet(lineNum);
343343
len += nextString.length;
344-
assem.append(nextString);
344+
assem += nextString;
345345
lineNum++;
346346
}
347347

348-
return assem.toString().substring(0, numChars);
348+
return assem.substring(0, numChars);
349349
};
350350

351351
const cachedStrFunc = (func) => {

src/static/js/Changeset.js

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,10 @@ class StringAssembler {
737737
/**
738738
* @returns {StringAssembler}
739739
*/
740-
exports.stringAssembler = () => new StringAssembler();
740+
exports.stringAssembler = () => {
741+
warnDeprecated('Changeset.stringAssembler() is deprecated; build a string manually instead');
742+
return new StringAssembler();
743+
};
741744

742745
/**
743746
* @typedef {object} StringArrayLike
@@ -1166,7 +1169,7 @@ exports.applyToText = (cs, str) => {
11661169
assert(str.length === unpacked.oldLen, `mismatched apply: ${str.length} / ${unpacked.oldLen}`);
11671170
const bankIter = exports.stringIterator(unpacked.charBank);
11681171
const strIter = exports.stringIterator(str);
1169-
const assem = new StringAssembler();
1172+
let assem = '';
11701173
for (const op of exports.deserializeOps(unpacked.ops)) {
11711174
switch (op.opcode) {
11721175
case '+':
@@ -1175,7 +1178,7 @@ exports.applyToText = (cs, str) => {
11751178
if (op.lines !== bankIter.peek(op.chars).split('\n').length - 1) {
11761179
throw new Error(`newline count is wrong in op +; cs:${cs} and text:${str}`);
11771180
}
1178-
assem.append(bankIter.take(op.chars));
1181+
assem += bankIter.take(op.chars);
11791182
break;
11801183
case '-':
11811184
// op is - and op.lines 0: no newlines must be in the deleted string
@@ -1191,12 +1194,12 @@ exports.applyToText = (cs, str) => {
11911194
if (op.lines !== strIter.peek(op.chars).split('\n').length - 1) {
11921195
throw new Error(`newline count is wrong in op =; cs:${cs} and text:${str}`);
11931196
}
1194-
assem.append(strIter.take(op.chars));
1197+
assem += strIter.take(op.chars);
11951198
break;
11961199
}
11971200
}
1198-
assem.append(strIter.take(strIter.remaining()));
1199-
return assem.toString();
1201+
assem += strIter.take(strIter.remaining());
1202+
return assem;
12001203
};
12011204

12021205
/**
@@ -1280,12 +1283,11 @@ exports.composeAttributes = (att1, att2, resultIsMutation, pool) => {
12801283
}
12811284
return '';
12821285
});
1283-
const buf = new StringAssembler();
1286+
let buf = '';
12841287
for (const att of sortAttribs([...atts])) {
1285-
buf.append('*');
1286-
buf.append(exports.numToString(pool.putAttrib(att)));
1288+
buf += `*${exports.numToString(pool.putAttrib(att))}`;
12871289
}
1288-
return buf.toString();
1290+
return buf;
12891291
};
12901292

12911293
/**
@@ -1514,7 +1516,7 @@ exports.compose = (cs1, cs2, pool) => {
15141516
const len3 = unpacked2.newLen;
15151517
const bankIter1 = exports.stringIterator(unpacked1.charBank);
15161518
const bankIter2 = exports.stringIterator(unpacked2.charBank);
1517-
const bankAssem = new StringAssembler();
1519+
let bankAssem = '';
15181520

15191521
const newOps = applyZip(unpacked1.ops, unpacked2.ops, (op1, op2) => {
15201522
const op1code = op1.opcode;
@@ -1524,16 +1526,12 @@ exports.compose = (cs1, cs2, pool) => {
15241526
}
15251527
const opOut = slicerZipperFunc(op1, op2, pool);
15261528
if (opOut.opcode === '+') {
1527-
if (op2code === '+') {
1528-
bankAssem.append(bankIter2.take(opOut.chars));
1529-
} else {
1530-
bankAssem.append(bankIter1.take(opOut.chars));
1531-
}
1529+
bankAssem += (op2code === '+' ? bankIter2 : bankIter1).take(opOut.chars);
15321530
}
15331531
return opOut;
15341532
});
15351533

1536-
return exports.pack(len1, len3, newOps, bankAssem.toString());
1534+
return exports.pack(len1, len3, newOps, bankAssem);
15371535
};
15381536

15391537
/**
@@ -1946,7 +1944,7 @@ class Builder {
19461944
constructor(oldLen) {
19471945
this._oldLen = oldLen;
19481946
this._ops = [];
1949-
this._charBank = new StringAssembler();
1947+
this._charBank = '';
19501948
}
19511949

19521950
/**
@@ -1991,7 +1989,7 @@ class Builder {
19911989
*/
19921990
insert(text, attribs = '', pool = null) {
19931991
this._ops.push(...opsFromText('+', text, attribs, pool));
1994-
this._charBank.append(text);
1992+
this._charBank += text;
19951993
return this;
19961994
}
19971995

@@ -2023,7 +2021,7 @@ class Builder {
20232021
oldLen: this._oldLen,
20242022
newLen: this._oldLen + lengthChange,
20252023
ops: serializedOps,
2026-
charBank: this._charBank.toString(),
2024+
charBank: this._charBank,
20272025
};
20282026
}
20292027

@@ -2201,20 +2199,20 @@ exports.inverse = (cs, lines, alines, pool) => {
22012199

22022200
const nextText = (numChars) => {
22032201
let len = 0;
2204-
const assem = new StringAssembler();
2202+
let assem = '';
22052203
const firstString = linesGet(curLine).substring(curChar);
22062204
len += firstString.length;
2207-
assem.append(firstString);
2205+
assem += firstString;
22082206

22092207
let lineNum = curLine + 1;
22102208
while (len < numChars) {
22112209
const nextString = linesGet(lineNum);
22122210
len += nextString.length;
2213-
assem.append(nextString);
2211+
assem += nextString;
22142212
lineNum++;
22152213
}
22162214

2217-
return assem.toString().substring(0, numChars);
2215+
return assem.substring(0, numChars);
22182216
};
22192217

22202218
const cachedStrFunc = (func) => {
@@ -2427,12 +2425,9 @@ const followAttributes = (att1, att2, pool) => {
24272425
return '';
24282426
});
24292427
// we've only removed attributes, so they're already sorted
2430-
const buf = new StringAssembler();
2431-
for (const att of atts) {
2432-
buf.append('*');
2433-
buf.append(exports.numToString(pool.putAttrib(att)));
2434-
}
2435-
return buf.toString();
2428+
let buf = '';
2429+
for (const att of atts) buf += `*${exports.numToString(pool.putAttrib(att))}`;
2430+
return buf;
24362431
};
24372432

24382433
exports.exportedForTestingOnly = {

src/tests/frontend/specs/easysync.js

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ describe('easysync', function () {
5050
};
5151

5252
const mutationsToChangeset = (oldLen, arrayOfArrays) => {
53-
const bank = Changeset.stringAssembler();
53+
let bank = '';
5454
let oldPos = 0;
5555
let newLen = 0;
5656
const ops = (function* () {
@@ -69,7 +69,7 @@ describe('easysync', function () {
6969
oldPos += op.chars;
7070
} else if (a[0] === 'insert') {
7171
op.opcode = '+';
72-
bank.append(a[1]);
72+
bank += a[1];
7373
op.chars = a[1].length;
7474
op.lines = (a[2] || 0);
7575
newLen += op.chars;
@@ -79,7 +79,7 @@ describe('easysync', function () {
7979
})();
8080
const serializedOps = Changeset.serializeOps(Changeset.canonicalizeOps(ops, true));
8181
newLen += oldLen - oldPos;
82-
return Changeset.pack(oldLen, newLen, serializedOps, bank.toString());
82+
return Changeset.pack(oldLen, newLen, serializedOps, bank);
8383
};
8484

8585
const runMutationTest = (testId, origLines, muts, correct) => {
@@ -350,29 +350,19 @@ describe('easysync', function () {
350350
]);
351351

352352
const randomInlineString = (len) => {
353-
const assem = Changeset.stringAssembler();
354-
for (let i = 0; i < len; i++) {
355-
assem.append(String.fromCharCode(randInt(26) + 97));
356-
}
357-
return assem.toString();
353+
let assem = '';
354+
for (let i = 0; i < len; i++) assem += String.fromCharCode(randInt(26) + 97);
355+
return assem;
358356
};
359357

360358
const randomMultiline = (approxMaxLines, approxMaxCols) => {
361359
const numParts = randInt(approxMaxLines * 2) + 1;
362-
const txt = Changeset.stringAssembler();
363-
txt.append(randInt(2) ? '\n' : '');
360+
let txt = '';
361+
txt += randInt(2) ? '\n' : '';
364362
for (let i = 0; i < numParts; i++) {
365-
if ((i % 2) === 0) {
366-
if (randInt(10)) {
367-
txt.append(randomInlineString(randInt(approxMaxCols) + 1));
368-
} else {
369-
txt.append('\n');
370-
}
371-
} else {
372-
txt.append('\n');
373-
}
363+
txt += i % 2 === 0 && randInt(10) ? randomInlineString(randInt(approxMaxCols) + 1) : '\n';
374364
}
375-
return txt.toString();
365+
return txt;
376366
};
377367

378368
const randomStringOperation = (numCharsLeft) => {
@@ -494,9 +484,9 @@ describe('easysync', function () {
494484
};
495485

496486
const randomTestChangeset = (origText, withAttribs) => {
497-
const charBank = Changeset.stringAssembler();
487+
let charBank = '';
498488
let textLeft = origText; // always keep final newline
499-
const outTextAssem = Changeset.stringAssembler();
489+
let outTextAssem = '';
500490
const ops = [];
501491
const oldLen = origText.length;
502492

@@ -521,13 +511,13 @@ describe('easysync', function () {
521511
const o = randomStringOperation(textLeft.length);
522512
if (o.insert) {
523513
const txt = o.insert;
524-
charBank.append(txt);
525-
outTextAssem.append(txt);
514+
charBank += txt;
515+
outTextAssem += txt;
526516
appendMultilineOp('+', txt);
527517
} else if (o.skip) {
528518
const txt = textLeft.substring(0, o.skip);
529519
textLeft = textLeft.substring(o.skip);
530-
outTextAssem.append(txt);
520+
outTextAssem += txt;
531521
appendMultilineOp('=', txt);
532522
} else if (o.remove) {
533523
const txt = textLeft.substring(0, o.remove);
@@ -538,9 +528,9 @@ describe('easysync', function () {
538528

539529
while (textLeft.length > 1) doOp();
540530
for (let i = 0; i < 5; i++) doOp(); // do some more (only insertions will happen)
541-
const outText = `${outTextAssem.toString()}\n`;
531+
const outText = `${outTextAssem}\n`;
542532
const serializedOps = Changeset.serializeOps(Changeset.canonicalizeOps(ops, true));
543-
const cs = Changeset.pack(oldLen, outText.length, serializedOps, charBank.toString());
533+
const cs = Changeset.pack(oldLen, outText.length, serializedOps, charBank);
544534
Changeset.checkRep(cs);
545535
return [cs, outText];
546536
};

0 commit comments

Comments
 (0)