diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c8d64813..43195c227 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ generator function (combined with `serializeOps()`). * `appendATextToAssembler()`: Deprecated in favor of the new `opsFromAText()` generator function. + * `builder()`: Deprecated in favor of the new `Builder` class. * `newOp()`: Deprecated in favor of the new `Op` class. # 1.8.16 diff --git a/src/node/db/API.js b/src/node/db/API.js index 040abf5a6..7493ccce8 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -538,7 +538,7 @@ exports.restoreRevision = async (padID, rev) => { }; // create a new changeset with a helper builder object - const builder = Changeset.builder(oldText.length); + const builder = new Changeset.Builder(oldText.length); // assemble each line into the builder eachAttribRun(atext.attribs, (start, end, attribs) => { diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 3460983bd..89dbfef1a 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -759,7 +759,7 @@ const _correctMarkersInPad = (atext, apool) => { // create changeset that removes these bad markers offset = 0; - const builder = Changeset.builder(text.length); + const builder = new Changeset.Builder(text.length); badMarkers.forEach((pos) => { builder.keepText(text.substring(offset, pos)); diff --git a/src/node/utils/ImportHtml.js b/src/node/utils/ImportHtml.js index 12a99ef79..30c8c999a 100644 --- a/src/node/utils/ImportHtml.js +++ b/src/node/utils/ImportHtml.js @@ -64,7 +64,7 @@ exports.setPadHTML = async (pad, html) => { const newAttribs = `${result.lineAttribs.join('|1+1')}|1+1`; // create a new changeset with a helper builder object - const builder = Changeset.builder(1); + const builder = new Changeset.Builder(1); // assemble each line into the builder let textIndex = 0; diff --git a/src/node/utils/padDiff.js b/src/node/utils/padDiff.js index dd93e84ff..d3fcea611 100644 --- a/src/node/utils/padDiff.js +++ b/src/node/utils/padDiff.js @@ -69,7 +69,7 @@ PadDiff.prototype._createClearAuthorship = async function (rev) { const atext = await this._pad.getInternalRevisionAText(rev); // build clearAuthorship changeset - const builder = Changeset.builder(atext.text.length); + const builder = new Changeset.Builder(atext.text.length); builder.keepText(atext.text, [['author', '']], this._pad.pool); const changeset = builder.toString(); @@ -262,7 +262,7 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) { let curLineNextOp = new Changeset.Op('+'); const unpacked = Changeset.unpack(cs); - const builder = Changeset.builder(unpacked.newLen); + const builder = new Changeset.Builder(unpacked.newLen); const consumeAttribRuns = (numChars, func /* (len, attribs, endsLine)*/) => { if (!curLineOps || curLineOpsLine !== curLine) { diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index f508af641..bc43d5ed2 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -125,7 +125,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ * @param attribs an array of attributes */ _setAttributesOnRangeByLine(row, startCol, endCol, attribs) { - const builder = Changeset.builder(this.rep.lines.totalWidth()); + const builder = new Changeset.Builder(this.rep.lines.totalWidth()); ChangesetUtils.buildKeepToStartOfRange(this.rep, builder, [row, startCol]); ChangesetUtils.buildKeepRange( this.rep, builder, [row, startCol], [row, endCol], attribs, this.rep.apool); @@ -285,7 +285,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ */ setAttributeOnLine(lineNum, attributeName, attributeValue) { let loc = [0, 0]; - const builder = Changeset.builder(this.rep.lines.totalWidth()); + const builder = new Changeset.Builder(this.rep.lines.totalWidth()); const hasMarker = this.lineHasMarker(lineNum); ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 0])); @@ -314,7 +314,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ * @param attributeValue if given only attributes with equal value will be removed */ removeAttributeOnLine(lineNum, attributeName, attributeValue) { - const builder = Changeset.builder(this.rep.lines.totalWidth()); + const builder = new Changeset.Builder(this.rep.lines.totalWidth()); const hasMarker = this.lineHasMarker(lineNum); let found = false; diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index 6a35e2eaf..a783d4243 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -1915,98 +1915,100 @@ exports.attribsAttributeValue = (attribs, key, pool) => { /** * Incrementally builds a Changeset. - * - * @typedef {object} Builder - * @property {Function} insert - - * @property {Function} keep - - * @property {Function} keepText - - * @property {Function} remove - - * @property {Function} toString - */ +class Builder { + /** + * @param {number} oldLen - Old length + */ + constructor(oldLen) { + this._oldLen = oldLen; + this._ops = []; + this._charBank = exports.stringAssembler(); + } + + /** + * @param {number} N - Number of characters to keep. + * @param {number} L - Number of newlines among the `N` characters. If positive, the last + * character must be a newline. + * @param {(string|Attribute[])} attribs - Either [[key1,value1],[key2,value2],...] or '*0*1...' + * (no pool needed in latter case). + * @param {?AttributePool} pool - Attribute pool, only required if `attribs` is a list of + * attribute key, value pairs. + * @returns {Builder} this + */ + keep(N, L, attribs, pool) { + const o = new Op('='); + o.attribs = typeof attribs === 'string' + ? attribs : new AttributeMap(pool).update(attribs || []).toString(); + o.chars = N; + o.lines = (L || 0); + this._ops.push(o); + return this; + } + + /** + * @param {string} text - Text to keep. + * @param {(string|Attribute[])} attribs - Either [[key1,value1],[key2,value2],...] or '*0*1...' + * (no pool needed in latter case). + * @param {?AttributePool} pool - Attribute pool, only required if `attribs` is a list of + * attribute key, value pairs. + * @returns {Builder} this + */ + keepText(text, attribs, pool) { + this._ops.push(...opsFromText('=', text, attribs, pool)); + return this; + } + + /** + * @param {string} text - Text to insert. + * @param {(string|Attribute[])} attribs - Either [[key1,value1],[key2,value2],...] or '*0*1...' + * (no pool needed in latter case). + * @param {?AttributePool} pool - Attribute pool, only required if `attribs` is a list of + * attribute key, value pairs. + * @returns {Builder} this + */ + insert(text, attribs, pool) { + this._ops.push(...opsFromText('+', text, attribs, pool)); + this._charBank.append(text); + return this; + } + + /** + * @param {number} N - Number of characters to remove. + * @param {number} L - Number of newlines among the `N` characters. If positive, the last + * character must be a newline. + * @returns {Builder} this + */ + remove(N, L) { + const o = new Op('-'); + o.attribs = ''; + o.chars = N; + o.lines = (L || 0); + this._ops.push(o); + return this; + } + + toString() { + /** @type {number} */ + let lengthChange; + const serializedOps = exports.serializeOps((function* () { + lengthChange = yield* exports.canonicalizeOps(this._ops, true); + }).call(this)); + const newLen = this._oldLen + lengthChange; + return exports.pack(this._oldLen, newLen, serializedOps, this._charBank.toString()); + } +} +exports.Builder = Builder; /** + * @deprecated Use the `Builder` class instead. * @param {number} oldLen - Old length * @returns {Builder} */ exports.builder = (oldLen) => { - const ops = []; - const charBank = exports.stringAssembler(); - - const self = { - /** - * @param {number} N - Number of characters to keep. - * @param {number} L - Number of newlines among the `N` characters. If positive, the last - * character must be a newline. - * @param {(string|Attribute[])} attribs - Either [[key1,value1],[key2,value2],...] or '*0*1...' - * (no pool needed in latter case). - * @param {?AttributePool} pool - Attribute pool, only required if `attribs` is a list of - * attribute key, value pairs. - * @returns {Builder} this - */ - keep: (N, L, attribs, pool) => { - const o = new Op('='); - o.attribs = typeof attribs === 'string' - ? attribs : new AttributeMap(pool).update(attribs || []).toString(); - o.chars = N; - o.lines = (L || 0); - ops.push(o); - return self; - }, - - /** - * @param {string} text - Text to keep. - * @param {(string|Attribute[])} attribs - Either [[key1,value1],[key2,value2],...] or '*0*1...' - * (no pool needed in latter case). - * @param {?AttributePool} pool - Attribute pool, only required if `attribs` is a list of - * attribute key, value pairs. - * @returns {Builder} this - */ - keepText: (text, attribs, pool) => { - ops.push(...opsFromText('=', text, attribs, pool)); - return self; - }, - - /** - * @param {string} text - Text to insert. - * @param {(string|Attribute[])} attribs - Either [[key1,value1],[key2,value2],...] or '*0*1...' - * (no pool needed in latter case). - * @param {?AttributePool} pool - Attribute pool, only required if `attribs` is a list of - * attribute key, value pairs. - * @returns {Builder} this - */ - insert: (text, attribs, pool) => { - ops.push(...opsFromText('+', text, attribs, pool)); - charBank.append(text); - return self; - }, - - /** - * @param {number} N - Number of characters to remove. - * @param {number} L - Number of newlines among the `N` characters. If positive, the last - * character must be a newline. - * @returns {Builder} this - */ - remove: (N, L) => { - const o = new Op('-'); - o.attribs = ''; - o.chars = N; - o.lines = (L || 0); - ops.push(o); - return self; - }, - - toString: () => { - /** @type {number} */ - let lengthChange; - const serializedOps = exports.serializeOps((function* () { - lengthChange = yield* exports.canonicalizeOps(ops, true); - })()); - const newLen = oldLen + lengthChange; - return exports.pack(oldLen, newLen, serializedOps, charBank.toString()); - }, - }; - - return self; + padutils.warnDeprecated( + 'Changeset.builder() is deprecated; use the Changeset.Builder class instead'); + return new Builder(oldLen); }; /** @@ -2107,7 +2109,7 @@ exports.inverse = (cs, lines, alines, pool) => { let curLineNextOp = new Op('+'); const unpacked = exports.unpack(cs); - const builder = exports.builder(unpacked.newLen); + const builder = new Builder(unpacked.newLen); const consumeAttribRuns = (numChars, func /* (len, attribs, endsLine)*/) => { if (!curLineOps || curLineOpsLine !== curLine) { diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 8f1f1a083..4b3cd5708 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -170,7 +170,7 @@ function Ace2Inner(editorInfo, cssManagers) { // CCCCCCCCCCCCCCCCCCCC\n // CCCC\n // end[0]: -------\n - const builder = Changeset.builder(rep.lines.totalWidth()); + const builder = new Changeset.Builder(rep.lines.totalWidth()); ChangesetUtils.buildKeepToStartOfRange(rep, builder, start); ChangesetUtils.buildRemoveRange(rep, builder, start, end); builder.insert(newText, [ @@ -1272,7 +1272,7 @@ function Ace2Inner(editorInfo, cssManagers) { if (shouldIndent && /[[(:{]\s*$/.exec(prevLineText)) { theIndent += THE_TAB; } - const cs = Changeset.builder(rep.lines.totalWidth()).keep( + const cs = new Changeset.Builder(rep.lines.totalWidth()).keep( rep.lines.offsetOfIndex(lineNum), lineNum).insert( theIndent, [ ['author', thisAuthor], @@ -1746,7 +1746,7 @@ function Ace2Inner(editorInfo, cssManagers) { const spliceStartLineStart = rep.lines.offsetOfIndex(spliceStartLine); const startBuilder = () => { - const builder = Changeset.builder(oldLen); + const builder = new Changeset.Builder(oldLen); builder.keep(spliceStartLineStart, spliceStartLine); builder.keep(spliceStart - spliceStartLineStart); return builder; @@ -2297,7 +2297,7 @@ function Ace2Inner(editorInfo, cssManagers) { // 3-renumber every list item of the same level from the beginning, level 1 // IMPORTANT: never skip a level because there imbrication may be arbitrary - const builder = Changeset.builder(rep.lines.totalWidth()); + const builder = new Changeset.Builder(rep.lines.totalWidth()); let loc = [0, 0]; const applyNumberList = (line, level) => { // init