diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index 812a91154da..6b6157f2821 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -711,9 +711,14 @@ exports.textLinesMutator = (lines) => { curSplice[1] += L - 1; const sline = curSplice.length - 1; removed = curSplice[sline].substring(curCol) + removed; - curSplice[sline] = curSplice[sline].substring(0, curCol) + - linesGet(curSplice[0] + curSplice[1]); - curSplice[1] += 1; + const line = linesGet(curSplice[0] + curSplice[1]); + // if no line follows the splice + if (!line) { + curSplice[sline] = curSplice[sline].substring(0, curCol); + } else { + curSplice[sline] = curSplice[sline].substring(0, curCol) + line; + curSplice[1] += 1; + } } } else { removed = nextKLinesText(L); @@ -775,14 +780,27 @@ exports.textLinesMutator = (lines) => { curLine += newLines.length; // insert the remaining chars from the "old" line (e.g. the line we were in // when we started to insert new lines) - curSplice.push(theLine.substring(lineCol)); + // if nothing is left we don't push an empty string + if (theLine.substring(lineCol)) { + curSplice.push(theLine.substring(lineCol)); + } curCol = 0; // TODO(doc) why is this not set to the length of last line? } else { Array.prototype.push.apply(curSplice, newLines); curLine += newLines.length; } + } else if (lines_get(curSplice[0] + curSplice[1]) === undefined) { + // find out if there is a line in splice that is not finished processing + if (isCurLineInSplice()) { // if yes, we can add our text to it + const sline = curSplice.length - 1; + curSplice[sline] = + curSplice[sline].substring(0, curCol) + text + curSplice[sline].substring(curCol); + curCol += text.length; + } else { // if no, we need to add the text in a new line + Array.prototype.push.apply(curSplice, [text]); + curCol += text.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) const sline = putCurLineInSplice(); diff --git a/src/tests/frontend/specs/easysync.js b/src/tests/frontend/specs/easysync.js index 5c4c47ae43d..e361e12fd4e 100644 --- a/src/tests/frontend/specs/easysync.js +++ b/src/tests/frontend/specs/easysync.js @@ -178,6 +178,34 @@ describe('easysync', function () { ['skip', 1, 1, true], ], ['banana\n', 'cabbage\n', 'duffle\n']); + // #2836 regressions + runMutationTest(8, ['\n', 'foo\n', '\n'], [ + ['remove', 1, 1, '\n'], + ['skip', 4, 1, false], + ['remove', 1, 1, '\n'], + ['insert', 'c'], + ], ['foo\n', 'c']); + runMutationTest(9, ['\n', 'foo\n', '\n'], [ + ['remove', 1, 1, '\n'], + ['skip', 3, 0, false], + ['remove', 2, 2, '\n\n'], + ['insert', 'c'], + ], ['fooc']); + runMutationTest(10, ['\n'], [ + ['remove', 1, 1, '\n'], + ['insert', 'c', 0], + ], ['c']); // TODO find out if c must have a newline because of unknown constraints + runMutationTest(11, ['\n'], [ + ['remove', 1, 1, '\n'], + ['insert', 'a'], + ['insert', 'c\n', 1], + ], ['ac\n']); + runMutationTest(12, ['\n'], [ + ['remove', 1, 1, '\n'], + ['insert', 'a\n', 1], + ['insert', 'c'], + ], ['a\n', 'c']); // TODO find out if c must have a newline because of unknown constraints + const poolOrArray = (attribs) => { if (attribs.getAttrib) { return attribs; // it's already an attrib pool