diff --git a/lib/ace/anchor_test.js b/lib/ace/anchor_test.js index b9c5cd66..2d7fcb63 100644 --- a/lib/ace/anchor_test.js +++ b/lib/ace/anchor_test.js @@ -167,6 +167,17 @@ module.exports = { }); doc.remove(new Range(2, 0, 2, 1)); + }, + + "test insert/remove lines at the end of the document": function() { + var doc = new Document("juhu\nkinners\n123"); + var anchor = new Anchor(doc, 2, 4); + + doc.removeLines(0, 3); + assert.position(anchor.getPosition(), 0, 0); + doc.insertLines(0, ["a", "b", "c"]); + assert.position(anchor.getPosition(), 3, 0); + assert.equal(doc.getValue(), "a\nb\nc\n"); } }; diff --git a/lib/ace/document.js b/lib/ace/document.js index a60ed0e9..8a073f86 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -59,7 +59,7 @@ var Document = function(text) { if (text.length == 0) { this.$lines = [""]; } else if (Array.isArray(text)) { - this.insertLines(0, text); + this._insertLines(0, text); } else { this.insert({row: 0, column:0}, text); } @@ -92,7 +92,6 @@ var Document = function(text) { * @param {Number} row The row number to use * @param {Number} column The column number to use * - * **/ this.createAnchor = function(row, column) { return new Anchor(this, row, column); @@ -105,7 +104,6 @@ var Document = function(text) { * @param {String} text The text to work with * @returns {String} A String array, with each index containing a piece of the original `text` string. * - * **/ // check for IE split bug @@ -119,14 +117,9 @@ var Document = function(text) { }; - this.$detectNewLine = function(text) { var match = text.match(/^.*?(\r\n|\r|\n)/m); - if (match) { - this.$autoNewLine = match[1]; - } else { - this.$autoNewLine = "\n"; - } + this.$autoNewLine = match ? match[1] : "\n"; }; /** @@ -134,19 +127,14 @@ var Document = function(text) { * @returns {String} If `newLineMode == windows`, `\r\n` is returned. * If `newLineMode == unix`, `\n` is returned. * If `newLineMode == auto`, the value of `autoNewLine` is returned. - * * - * - * **/ this.getNewLineCharacter = function() { switch (this.$newLineMode) { case "windows": return "\r\n"; - case "unix": return "\n"; - default: return this.$autoNewLine; } @@ -157,7 +145,6 @@ var Document = function(text) { /** * [Sets the new line mode.]{: #Document.setNewLineMode.desc} * @param {String} newLineMode [The newline mode to use; can be either `windows`, `unix`, or `auto`]{: #Document.setNewLineMode.param} - * * **/ this.setNewLineMode = function(newLineMode) { @@ -179,8 +166,6 @@ var Document = function(text) { * Returns `true` if `text` is a newline character (either `\r\n`, `\r`, or `\n`). * @param {String} text The text to check * - * - * **/ this.isNewLine = function(text) { return (text == "\r\n" || text == "\r" || text == "\n"); @@ -190,8 +175,6 @@ var Document = function(text) { * Returns a verbatim copy of the given line as it is in the document * @param {Number} row The row index to retrieve * - * - * **/ this.getLine = function(row) { return this.$lines[row] || ""; @@ -202,8 +185,6 @@ var Document = function(text) { * @param {Number} firstRow The first row index to retrieve * @param {Number} lastRow The final row index to retrieve * - * - * **/ this.getLines = function(firstRow, lastRow) { return this.$lines.slice(firstRow, lastRow + 1); @@ -231,15 +212,15 @@ var Document = function(text) { **/ this.getTextRange = function(range) { if (range.start.row == range.end.row) { - return this.$lines[range.start.row].substring(range.start.column, - range.end.column); - } - else { - var lines = this.getLines(range.start.row+1, range.end.row-1); - lines.unshift((this.$lines[range.start.row] || "").substring(range.start.column)); - lines.push((this.$lines[range.end.row] || "").substring(0, range.end.column)); - return lines.join(this.getNewLineCharacter()); + return this.$lines[range.start.row] + .substring(range.start.column, range.end.column); } + var lines = this.getLines(range.start.row, range.end.row); + lines[0] = (lines[0] || "").substring(range.start.column); + var l = lines.length - 1; + if (range.end.row - range.start.row == l) + lines[l] = lines[l].substring(0, range.end.column); + return lines.join(this.getNewLineCharacter()); }; this.$clipPosition = function(position) { @@ -276,7 +257,7 @@ var Document = function(text) { position = this.insertInLine(position, firstLine); if (lastLine !== null) { position = this.insertNewLine(position); // terminate first line - position = this.insertLines(position.row, lines); + position = this._insertLines(position.row, lines); position = this.insertInLine(position, lastLine || ""); } return position; @@ -318,18 +299,20 @@ var Document = function(text) { * {row: row, column: 0} * ``` * - * - * - * **/ this.insertLines = function(row, lines) { + if (row >= this.getLength()) + return this.insert({row: row, column: 0}, "\n" + lines.join("\n")); + return this._insertLines(Math.max(row, 0), lines); + }; + this._insertLines = function(row, lines) { if (lines.length == 0) return {row: row, column: 0}; // apply doesn't work for big arrays (smallest threshold is on safari 0xFFFF) // to circumvent that we have to break huge inserts into smaller chunks here if (lines.length > 0xFFFF) { - var end = this.insertLines(row, lines.slice(0xFFFF)); + var end = this._insertLines(row, lines.slice(0xFFFF)); lines = lines.slice(0, 0xFFFF); } @@ -417,7 +400,6 @@ var Document = function(text) { * @param {Range} range A specified Range to remove * @returns {Object} Returns the new `start` property of the range, which contains `startRow` and `startColumn`. If `range` is empty, this function returns the unmodified value of `range.start`. * - * **/ this.remove = function(range) { // clip to document @@ -438,7 +420,7 @@ var Document = function(text) { this.removeInLine(lastRow, 0, range.end.column); if (lastFullRow >= firstFullRow) - this.removeLines(firstFullRow, lastFullRow); + this._removeLines(firstFullRow, lastFullRow); if (firstFullRow != firstRow) { this.removeInLine(firstRow, range.start.column, this.getLine(firstRow).length); @@ -458,7 +440,6 @@ var Document = function(text) { * @param {Number} endColumn The column to stop removing at * @returns {Object} Returns an object containing `startRow` and `startColumn`, indicating the new row and column values.
If `startColumn` is equal to `endColumn`, this function returns nothing. * - * **/ this.removeInLine = function(row, startColumn, endColumn) { if (startColumn == endColumn) @@ -484,10 +465,15 @@ var Document = function(text) { * @param {Number} firstRow The first row to be removed * @param {Number} lastRow The last row to be removed * @returns {[String]} Returns all the removed lines. - * * **/ this.removeLines = function(firstRow, lastRow) { + if (firstRow < 0 || lastRow >= this.getLength()) + return this.remove(new Range(firstRow, 0, lastRow + 1, 0)); + return this._removeLines(firstRow, lastRow); + }; + + this._removeLines = function(firstRow, lastRow) { var range = new Range(firstRow, 0, lastRow + 1, 0); var removed = this.$lines.splice(firstRow, lastRow - firstRow + 1); @@ -532,7 +518,6 @@ var Document = function(text) { * If the text and range are empty, this function returns an object containing the current `range.start` value. * If the text is the exact same as what currently exists, this function returns an object containing the current `range.end` value. * - * **/ this.replace = function(range, text) { if (text.length == 0 && range.isEmpty()) @@ -567,7 +552,7 @@ var Document = function(text) { else if (delta.action == "insertText") this.insert(range.start, delta.text); else if (delta.action == "removeLines") - this.removeLines(range.start.row, range.end.row - 1); + this._removeLines(range.start.row, range.end.row - 1); else if (delta.action == "removeText") this.remove(range); } @@ -583,11 +568,11 @@ var Document = function(text) { var range = Range.fromPoints(delta.range.start, delta.range.end); if (delta.action == "insertLines") - this.removeLines(range.start.row, range.end.row - 1); + this._removeLines(range.start.row, range.end.row - 1); else if (delta.action == "insertText") this.remove(range); else if (delta.action == "removeLines") - this.insertLines(range.start.row, delta.lines); + this._insertLines(range.start.row, delta.lines); else if (delta.action == "removeText") this.insert(range.start, delta.text); }