From 395cbc01bb2cc4889fd5ac9aeb61d4934595cd13 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Wed, 20 Oct 2021 21:52:37 +0200 Subject: [PATCH] Changeset.js: refine comments --- src/static/js/Changeset.js | 54 +++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index dada72260..13bb47466 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -525,6 +525,9 @@ exports.mergingOpAssembler = () => { // ops immediately after it. let bufOpAdditionalCharsAfterNewline = 0; + /** + * @param {boolean} [isEndDocument] + */ const flush = (isEndDocument) => { if (!bufOp.opcode) return; if (isEndDocument && bufOp.opcode === '=' && !bufOp.attribs) { @@ -803,7 +806,7 @@ class TextLinesMutator { /** * Indicates if curLine is already in the splice. This is necessary because the last element in - * curSplice is curLine when this line is currently worked on (e.g. when skipping are inserting). + * curSplice is curLine when this line is currently worked on (e.g. when skipping or inserting). * * TODO(doc) why aren't removals considered? * @@ -831,7 +834,7 @@ class TextLinesMutator { * It will skip some newlines by putting them into the splice. * * @param {number} L - - * @param {boolean} includeInSplice - indicates if attributes are present + * @param {boolean} includeInSplice - Indicates that attributes are present. */ skipLines(L, includeInSplice) { if (!L) return; @@ -960,7 +963,7 @@ class TextLinesMutator { /** @type {string} */ const theLine = this._curSplice[sline]; const lineCol = this._curCol; - // insert the first new line + // Insert the chars up to `curCol` and the first new line. this._curSplice[sline] = theLine.substring(0, lineCol) + newLines[0]; this._curLine++; newLines.splice(0, 1); @@ -976,9 +979,8 @@ class TextLinesMutator { this._curLine += newLines.length; } } else { - // there are no additional lines - // although the line is put into splice, curLine is not increased, because - // there may be more chars in the line (newline is not reached) + // There are no additional lines. Although the line is put into splice, curLine is not + // increased because there may be more chars in the line (newline is not reached). const sline = this._putCurLineInSplice(); if (!this._curSplice[sline]) { const err = new Error( @@ -1277,6 +1279,13 @@ exports.applyToAttribution = (cs, astr, pool) => { return applyZip(astr, unpacked.ops, (op1, op2) => slicerZipperFunc(op1, op2, pool)); }; +/** + * Applies a changeset to an array of attribute lines. + * + * @param {string} cs - The encoded changeset. + * @param {Array} lines - Attribute lines. Modified in place. + * @param {AttributePool} pool - Attribute pool. + */ exports.mutateAttributionLines = (cs, lines, pool) => { const unpacked = exports.unpack(cs); const csOps = exports.deserializeOps(unpacked.ops); @@ -1286,26 +1295,47 @@ exports.mutateAttributionLines = (cs, lines, pool) => { // treat the attribution lines as text lines, mutating a line at a time const mut = new TextLinesMutator(lines); - /** @type {?Generator} */ + /** + * The Ops in the current line from `lines`. + * + * @type {?Generator} + */ let lineOps = null; let lineOpsNext = null; const lineOpsHasNext = () => lineOpsNext && !lineOpsNext.done; + /** + * Returns false if we are on the last attribute line in `lines` and there is no additional op in + * that line. + * + * @returns {boolean} True if there are more ops to go through. + */ const isNextMutOp = () => lineOpsHasNext() || mut.hasMore(); + /** + * @returns {Op} The next Op from `lineIter`. If there are no more Ops, `lineIter` is reset to + * iterate over the next line, which is consumed from `mut`. If there are no more lines, + * returns a null Op. + */ const nextMutOp = () => { if (!lineOpsHasNext() && mut.hasMore()) { + // There are more attribute lines in `lines` to do AND either we just started so `lineIter` is + // still null or there are no more ops in current `lineIter`. const line = mut.removeLines(1); lineOps = exports.deserializeOps(line); lineOpsNext = lineOps.next(); } - if (!lineOpsHasNext()) return new Op(); + if (!lineOpsHasNext()) return new Op(); // No more ops and no more lines. const op = lineOpsNext.value; lineOpsNext = lineOps.next(); return op; }; let lineAssem = null; + /** + * Appends an op to `lineAssem`. In case `lineAssem` includes one single newline, adds it to the + * `lines` mutator. + */ const outputMutOp = (op) => { if (!lineAssem) { lineAssem = exports.mergingOpAssembler(); @@ -1322,6 +1352,7 @@ exports.mutateAttributionLines = (cs, lines, pool) => { let attOp = new Op(); while (csOp.opcode || !csOpsNext.done || attOp.opcode || isNextMutOp()) { if (!csOp.opcode && !csOpsNext.done) { + // coOp done, but more ops in cs. csOp = csOpsNext.value; csOpsNext = csOps.next(); } @@ -1329,18 +1360,21 @@ exports.mutateAttributionLines = (cs, lines, pool) => { break; // done } else if (csOp.opcode === '=' && csOp.lines > 0 && !csOp.attribs && !attOp.opcode && !lineAssem && !lineOpsHasNext()) { - // skip multiple lines; this is what makes small changes not order of the document size + // Skip multiple lines without attributes; this is what makes small changes not order of the + // document size. mut.skipLines(csOp.lines); csOp.opcode = ''; } else if (csOp.opcode === '+') { const opOut = copyOp(csOp); if (csOp.lines > 1) { + // Copy the first line from `csOp` to `opOut`. const firstLineLen = csBank.indexOf('\n', csBankIndex) + 1 - csBankIndex; csOp.chars -= firstLineLen; csOp.lines--; opOut.lines = 1; opOut.chars = firstLineLen; } else { + // Either one or no newlines in '+' `csOp`, copy to `opOut` and reset `csOp`. csOp.opcode = ''; } outputMutOp(opOut); @@ -1758,7 +1792,7 @@ exports.copyAText = (atext1, atext2) => { }; /** - * Convert AText to a series of operations. + * Convert AText to a series of operations. Strips final newline. * * @param {AText} atext - The AText to convert. * @yields {Op}