Skip to content

Commit 2ffa9df

Browse files
committed
Changeset: Avoid unnecessary StringAssembler class
1 parent c704d63 commit 2ffa9df

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
@@ -326,21 +326,21 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
326326

327327
const nextText = (numChars) => {
328328
let len = 0;
329-
const assem = Changeset.stringAssembler();
329+
let assem = '';
330330
const firstString = linesGet(curLine).substring(curChar);
331331
len += firstString.length;
332-
assem.append(firstString);
332+
assem += firstString;
333333

334334
let lineNum = curLine + 1;
335335

336336
while (len < numChars) {
337337
const nextString = linesGet(lineNum);
338338
len += nextString.length;
339-
assem.append(nextString);
339+
assem += nextString;
340340
lineNum++;
341341
}
342342

343-
return assem.toString().substring(0, numChars);
343+
return assem.substring(0, numChars);
344344
};
345345

346346
const cachedStrFunc = (func) => {

src/static/js/Changeset.js

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,10 @@ class StringAssembler {
683683
/**
684684
* A custom made StringBuffer
685685
*/
686-
exports.stringAssembler = () => new StringAssembler();
686+
exports.stringAssembler = () => {
687+
warnDeprecated('Changeset.stringAssembler() is deprecated; build a string manually instead');
688+
return new StringAssembler();
689+
};
687690

688691
/**
689692
* Class to iterate and modify texts which have several lines. It is used for applying Changesets on
@@ -1080,7 +1083,7 @@ exports.applyToText = (cs, str) => {
10801083
assert(str.length === unpacked.oldLen, 'mismatched apply: ', str.length, ' / ', unpacked.oldLen);
10811084
const bankIter = exports.stringIterator(unpacked.charBank);
10821085
const strIter = exports.stringIterator(str);
1083-
const assem = exports.stringAssembler();
1086+
let assem = '';
10841087
for (const op of new exports.OpIter(unpacked.ops)) {
10851088
switch (op.opcode) {
10861089
case '+':
@@ -1089,7 +1092,7 @@ exports.applyToText = (cs, str) => {
10891092
if (op.lines !== bankIter.peek(op.chars).split('\n').length - 1) {
10901093
throw new Error(`newline count is wrong in op +; cs:${cs} and text:${str}`);
10911094
}
1092-
assem.append(bankIter.take(op.chars));
1095+
assem += bankIter.take(op.chars);
10931096
break;
10941097
case '-':
10951098
// op is - and op.lines 0: no newlines must be in the deleted string
@@ -1105,12 +1108,12 @@ exports.applyToText = (cs, str) => {
11051108
if (op.lines !== strIter.peek(op.chars).split('\n').length - 1) {
11061109
throw new Error(`newline count is wrong in op =; cs:${cs} and text:${str}`);
11071110
}
1108-
assem.append(strIter.take(op.chars));
1111+
assem += strIter.take(op.chars);
11091112
break;
11101113
}
11111114
}
1112-
assem.append(strIter.take(strIter.remaining()));
1113-
return assem.toString();
1115+
assem += strIter.take(strIter.remaining());
1116+
return assem;
11141117
};
11151118

11161119
/**
@@ -1182,12 +1185,11 @@ exports.composeAttributes = (att1, att2, resultIsMutation, pool) => {
11821185
}
11831186
return '';
11841187
});
1185-
const buf = exports.stringAssembler();
1188+
let buf = '';
11861189
for (const att of [...atts].sort((a, b) => (a[0] > b[0]) - (a[0] < b[0]))) {
1187-
buf.append('*');
1188-
buf.append(exports.numToString(pool.putAttrib(att)));
1190+
buf += `*${exports.numToString(pool.putAttrib(att))}`;
11891191
}
1190-
return buf.toString();
1192+
return buf;
11911193
};
11921194

11931195
/**
@@ -1395,7 +1397,7 @@ exports.compose = (cs1, cs2, pool) => {
13951397
const len3 = unpacked2.newLen;
13961398
const bankIter1 = exports.stringIterator(unpacked1.charBank);
13971399
const bankIter2 = exports.stringIterator(unpacked2.charBank);
1398-
const bankAssem = exports.stringAssembler();
1400+
let bankAssem = '';
13991401

14001402
const newOps = applyZip(unpacked1.ops, unpacked2.ops, (op1, op2) => {
14011403
const op1code = op1.opcode;
@@ -1405,16 +1407,12 @@ exports.compose = (cs1, cs2, pool) => {
14051407
}
14061408
const opOut = slicerZipperFunc(op1, op2, pool);
14071409
if (opOut.opcode === '+') {
1408-
if (op2code === '+') {
1409-
bankAssem.append(bankIter2.take(opOut.chars));
1410-
} else {
1411-
bankAssem.append(bankIter1.take(opOut.chars));
1412-
}
1410+
bankAssem += (op2code === '+' ? bankIter2 : bankIter1).take(opOut.chars);
14131411
}
14141412
return opOut;
14151413
});
14161414

1417-
return exports.pack(len1, len3, newOps, bankAssem.toString());
1415+
return exports.pack(len1, len3, newOps, bankAssem);
14181416
};
14191417

14201418
/**
@@ -1781,7 +1779,7 @@ exports.Builder = class {
17811779
this._oldLen = oldLen;
17821780
this._ops = [];
17831781
this._packed = null;
1784-
this._charBank = exports.stringAssembler();
1782+
this._charBank = '';
17851783
}
17861784

17871785
/**
@@ -1807,7 +1805,7 @@ exports.Builder = class {
18071805
insert(text, attribs, pool) {
18081806
this._packed = null;
18091807
this._ops.push(...opsFromText('+', text, attribs, pool));
1810-
this._charBank.append(text);
1808+
this._charBank += text;
18111809
return this;
18121810
}
18131811

@@ -1828,7 +1826,7 @@ exports.Builder = class {
18281826
lengthChange = yield* exports.canonicalizeOps(this._ops, true);
18291827
}).call(this));
18301828
const newLen = this._oldLen + lengthChange;
1831-
this._packed = exports.pack(this._oldLen, newLen, serializedOps, this._charBank.toString());
1829+
this._packed = exports.pack(this._oldLen, newLen, serializedOps, this._charBank);
18321830
}
18331831
return this._packed;
18341832
}
@@ -1986,20 +1984,20 @@ exports.inverse = (cs, lines, alines, pool) => {
19861984

19871985
const nextText = (numChars) => {
19881986
let len = 0;
1989-
const assem = exports.stringAssembler();
1987+
let assem = '';
19901988
const firstString = linesGet(curLine).substring(curChar);
19911989
len += firstString.length;
1992-
assem.append(firstString);
1990+
assem += firstString;
19931991

19941992
let lineNum = curLine + 1;
19951993
while (len < numChars) {
19961994
const nextString = linesGet(lineNum);
19971995
len += nextString.length;
1998-
assem.append(nextString);
1996+
assem += nextString;
19991997
lineNum++;
20001998
}
20011999

2002-
return assem.toString().substring(0, numChars);
2000+
return assem.substring(0, numChars);
20032001
};
20042002

20052003
const cachedStrFunc = (func) => {
@@ -2212,12 +2210,9 @@ const followAttributes = (att1, att2, pool) => {
22122210
return '';
22132211
});
22142212
// we've only removed attributes, so they're already sorted
2215-
const buf = exports.stringAssembler();
2216-
for (const att of atts) {
2217-
buf.append('*');
2218-
buf.append(exports.numToString(pool.putAttrib(att)));
2219-
}
2220-
return buf.toString();
2213+
let buf = '';
2214+
for (const att of atts) buf += `*${exports.numToString(pool.putAttrib(att))}`;
2215+
return buf;
22212216
};
22222217

22232218
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)