diff --git a/src/node/easysync_tests.js b/src/node/easysync_tests.js index 3c5c0d9307c..9b73e9651a1 100644 --- a/src/node/easysync_tests.js +++ b/src/node/easysync_tests.js @@ -218,16 +218,28 @@ const runTests = () => { ], ['banana\n', 'cabbage\n', 'duffle\n']); // #2836 regressions - runMutationTest(8, ['\n'], [ + 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']); - runMutationTest(9, ['\n'], [ + ], ['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(10, ['\n'], [ + runMutationTest(12, ['\n'], [ ['remove', 1, 1, '\n'], ['insert', 'a\n', 1], ['insert', 'c'], diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index 73dde69386f..b64e8cf7b66 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -758,9 +758,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) + - lines_get(curSplice[0] + curSplice[1]); - curSplice[1] += 1; + const line = lines_get(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); @@ -813,18 +818,38 @@ exports.textLinesMutator = (lines) => { const sline = curSplice.length - 1; const theLine = curSplice[sline]; const lineCol = curCol; + // insert the first new line curSplice[sline] = theLine.substring(0, lineCol) + newLines[0]; curLine++; newLines.splice(0, 1); + // insert the remaining new lines Array.prototype.push.apply(curSplice, newLines); curLine += newLines.length; - curSplice.push(theLine.substring(lineCol)); - curCol = 0; + // insert the remaining chars from the "old" line (e.g. the line we were in + // when we started to insert new lines) + // 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 { + // 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(); if (!curSplice[sline]) { console.error('curSplice[sline] not populated, actual curSplice contents is ', curSplice, '. Possibly related to https://github.com/ether/etherpad-lite/issues/2802');