Changeset: Turn builder() into a real class

This commit is contained in:
Richard Hansen 2021-10-16 21:22:13 -04:00
parent d3d2090ca5
commit a1c4382386
8 changed files with 101 additions and 98 deletions

View file

@ -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

View file

@ -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) => {

View file

@ -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));

View file

@ -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;

View file

@ -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) {

View file

@ -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;

View file

@ -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) {

View file

@ -170,7 +170,7 @@ function Ace2Inner(editorInfo, cssManagers) {
// CCCCCCCCCCCCCCCCCCCC\n
// CCCC\n
// end[0]: <CCC end[1] CCC>-------\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