From 2e6f12725bc01011c545c99c1f1c1da6065063d9 Mon Sep 17 00:00:00 2001 From: aldendaniels Date: Wed, 1 Jan 2014 00:41:45 -0600 Subject: [PATCH 01/92] #1744: Refactor document delta handling Refactor delta handling code to: - Combine the "insertText" and "insertLines" delta types into a single "insert" delta type - Combine the "removeText" and "removeLines" delta types into a single "remove" delta type - Make all document mutations in a single applyDelta function. - Add basic delta validation (more needed . . . see TODOs) - Rework anchor logic to handle new delta types (also simplified) - Rename "insert()" to "insertText()" and "remove()" to "removeText()" - Rename "insertLines()" to "insertFullLines()" and "removeLines()" to "removeFullLines()" See related issue for more information. All tests are passing and the changes appear functional under preliminary testing, but careful review and testing will be necessary. --- lib/ace/anchor.js | 108 +++--- lib/ace/anchor_test.js | 16 +- lib/ace/autocomplete.js | 2 +- lib/ace/background_tokenizer.js | 2 +- lib/ace/background_tokenizer_test.js | 2 +- lib/ace/commands/default_commands.js | 4 +- lib/ace/document.js | 516 +++++++++++++------------ lib/ace/document_test.js | 18 +- lib/ace/edit_session.js | 58 ++- lib/ace/edit_session/folding.js | 2 +- lib/ace/edit_session_test.js | 18 +- lib/ace/editor.js | 39 +- lib/ace/editor_change_document_test.js | 2 +- lib/ace/editor_navigation_test.js | 2 +- lib/ace/ext/chromevox.js | 4 +- lib/ace/ext/elastic_tabstops_lite.js | 2 +- lib/ace/ext/whitespace.js | 2 +- lib/ace/keyboard/vim/commands.js | 10 +- lib/ace/keyboard/vim/maps/motions.js | 6 +- lib/ace/layer/gutter.js | 4 +- lib/ace/line_widgets.js | 2 +- lib/ace/mode/javascript_test.js | 2 +- lib/ace/mode/text.js | 12 +- lib/ace/mouse/dragdrop_handler.js | 2 +- lib/ace/multi_select.js | 10 +- lib/ace/occur_test.js | 12 +- lib/ace/placeholder.js | 12 +- lib/ace/placeholder_test.js | 18 +- 28 files changed, 437 insertions(+), 450 deletions(-) diff --git a/lib/ace/anchor.js b/lib/ace/anchor.js index 3a62e632..91a62eb0 100644 --- a/lib/ace/anchor.js +++ b/lib/ace/anchor.js @@ -100,67 +100,55 @@ var Anchor = exports.Anchor = function(doc, row, column) { * **/ this.onChange = function(e) { - var delta = e.data; - var range = delta.range; - - if (range.start.row == range.end.row && range.start.row != this.row) - return; - - if (range.start.row > this.row) - return; - - if (range.start.row == this.row && range.start.column > this.column) - return; - - var row = this.row; - var column = this.column; - var start = range.start; - var end = range.end; - - if (delta.action === "insertText") { - if (start.row === row && start.column <= column) { - if (start.column === column && this.$insertRight) { - // do nothing - } else if (start.row === end.row) { - column += end.column - start.column; - } else { - column -= start.column; - row += end.row - start.row; - } - } else if (start.row !== end.row && start.row < row) { - row += end.row - start.row; - } - } else if (delta.action === "insertLines") { - if (start.row <= row) { - row += end.row - start.row; - } - } else if (delta.action === "removeText") { - if (start.row === row && start.column < column) { - if (end.column >= column) - column = start.column; - else - column = Math.max(0, column - (end.column - start.column)); - - } else if (start.row !== end.row && start.row < row) { - if (end.row === row) - column = Math.max(0, column - end.column) + start.column; - row -= (end.row - start.row); - } else if (end.row === row) { - row -= end.row - start.row; - column = Math.max(0, column - end.column) + start.column; - } - } else if (delta.action == "removeLines") { - if (start.row <= row) { - if (end.row <= row) - row -= end.row - start.row; - else { - row = start.row; - column = 0; - } - } + + function _pointsInOrder(point1, point2, equalPointsInOrder) + { + var bColIsAfter = equalPointsInOrder ? point1.column <= point2.column : point1.column < point2.column; + return (point1.row < point2.row) || (point1.row == point2.row && bColIsAfter); } - - this.setPosition(row, column, true); + + function getTransformedPoint(delta, point, moveIfEqual) + { + // Get delta info. + var deltaIsInsert = (delta.action == 'insert') + var deltaRowShift = (deltaIsInsert ? 1 : -1) * (delta.range.end.row - delta.range.start.row); + var deltaColShift = (deltaIsInsert ? 1 : -1) * (delta.range.end.column - delta.range.start.column); + var deltaStart = delta.range.start; + var deltaEnd = (deltaIsInsert ? deltaStart : delta.range.end); // Collapse insert range. + + // DELTA AFTER POINT: No change needed. + if (_pointsInOrder(point, deltaStart, moveIfEqual)) + { + return ( + { + row: point.row, + column: point.column + }); + } + + // DELTA BEFORE POINT: Move point by delta shift. + if (_pointsInOrder(deltaEnd, point, !moveIfEqual)) + { + return ( + { + row: point.row + deltaRowShift, + column: point.column + (point.row == deltaEnd.row ? deltaColShift : 0) + }); + } + + // DELTA ENVELOPS POINT (delete only): Move point to delta start. + if (delta.action != 'delete') + throw 'Delete action expected.'; + return ( + { + row: deltaStart.row, + column: deltaStart.column + }); + } + + var delta = e.data; + var point = getTransformedPoint(delta, {row: this.row, column: this.column}, this.$insertRight); + this.setPosition(point.row, point.column, true); }; /** diff --git a/lib/ace/anchor_test.js b/lib/ace/anchor_test.js index 2d7fcb63..0588c9b6 100644 --- a/lib/ace/anchor_test.js +++ b/lib/ace/anchor_test.js @@ -54,7 +54,7 @@ module.exports = { var doc = new Document("juhu\nkinners"); var anchor = new Anchor(doc, 1, 4); - doc.insert({row: 1, column: 1}, "123"); + doc.insertText({row: 1, column: 1}, "123"); assert.position(anchor.getPosition(), 1, 7); }, @@ -62,7 +62,7 @@ module.exports = { var doc = new Document("juhu\nkinners"); var anchor = new Anchor(doc, 1, 4); - doc.insertLines(1, ["123", "456"]); + doc.insertFullLines(1, ["123", "456"]); assert.position(anchor.getPosition(), 3, 4); }, @@ -70,7 +70,7 @@ module.exports = { var doc = new Document("juhu\nkinners"); var anchor = new Anchor(doc, 1, 4); - doc.insertNewLine({row: 0, column: 0}); + doc.insertLines({row: 0, column: 0}, ['', '']); assert.position(anchor.getPosition(), 2, 4); }, @@ -78,7 +78,7 @@ module.exports = { var doc = new Document("juhu\nkinners"); var anchor = new Anchor(doc, 1, 4); - doc.insertNewLine({row: 1, column: 2}); + doc.insertLines({row: 1, column: 2}, ['', '']); assert.position(anchor.getPosition(), 2, 2); }, @@ -110,7 +110,7 @@ module.exports = { var doc = new Document("juhu\n1\n2\nkinners"); var anchor = new Anchor(doc, 3, 4); - doc.removeLines(1, 2); + doc.removeFullLines(1, 2); assert.position(anchor.getPosition(), 1, 4); }, @@ -134,7 +134,7 @@ module.exports = { var doc = new Document("juhu\nkinners\n123"); var anchor = new Anchor(doc, 1, 5); - doc.removeLines(1, 1); + doc.removeFullLines(1, 1); assert.position(anchor.getPosition(), 1, 0); }, @@ -173,9 +173,9 @@ module.exports = { var doc = new Document("juhu\nkinners\n123"); var anchor = new Anchor(doc, 2, 4); - doc.removeLines(0, 3); + doc.removeFullLines(0, 3); assert.position(anchor.getPosition(), 0, 0); - doc.insertLines(0, ["a", "b", "c"]); + doc.insertFullLines(0, ["a", "b", "c"]); assert.position(anchor.getPosition(), 3, 0); assert.equal(doc.getValue(), "a\nb\nc\n"); } diff --git a/lib/ace/autocomplete.js b/lib/ace/autocomplete.js index 50154c01..2344aede 100644 --- a/lib/ace/autocomplete.js +++ b/lib/ace/autocomplete.js @@ -170,7 +170,7 @@ var Autocomplete = function() { "Ctrl-Down|Ctrl-End": function(editor) { editor.completer.goTo("end"); }, "Esc": function(editor) { editor.completer.detach(); }, - "Space": function(editor) { editor.completer.detach(); editor.insert(" ");}, + "Space": function(editor) { editor.completer.detach(); editor.insertText(" ");}, "Return": function(editor) { editor.completer.insertMatch(); }, "Shift-Return": function(editor) { editor.completer.insertMatch(true); }, "Tab": function(editor) { editor.completer.insertMatch(); }, diff --git a/lib/ace/background_tokenizer.js b/lib/ace/background_tokenizer.js index 217be1b3..cb3ffe23 100644 --- a/lib/ace/background_tokenizer.js +++ b/lib/ace/background_tokenizer.js @@ -183,7 +183,7 @@ var BackgroundTokenizer = function(tokenizer, editor) { if (len === 0) { this.lines[startRow] = null; - } else if (delta.action == "removeText" || delta.action == "removeLines") { + } else if (delta.action == "delete") { this.lines.splice(startRow, len + 1, null); this.states.splice(startRow, len + 1, null); } else { diff --git a/lib/ace/background_tokenizer_test.js b/lib/ace/background_tokenizer_test.js index 7a4cc78c..16c63132 100644 --- a/lib/ace/background_tokenizer_test.js +++ b/lib/ace/background_tokenizer_test.js @@ -70,7 +70,7 @@ module.exports = { forceTokenize(doc) testStates(doc, ["comment_regex_allowed", "comment_regex_allowed"]) - doc.insert({row:0, column:2}, "\n*/") + doc.insertText({row:0, column:2}, "\n*/") testStates(doc, [undefined, undefined, "comment_regex_allowed"]) forceTokenize(doc) diff --git a/lib/ace/commands/default_commands.js b/lib/ace/commands/default_commands.js index e4a8212e..0fd9686c 100644 --- a/lib/ace/commands/default_commands.js +++ b/lib/ace/commands/default_commands.js @@ -508,13 +508,13 @@ exports.commands = [{ scrollIntoView: "selectionPart" }, { name: "insertstring", - exec: function(editor, str) { editor.insert(str); }, + exec: function(editor, str) { editor.insertText(str); }, multiSelectAction: "forEach", scrollIntoView: "cursor" }, { name: "inserttext", exec: function(editor, args) { - editor.insert(lang.stringRepeat(args.text || "", args.times || 1)); + editor.insertText(lang.stringRepeat(args.text || "", args.times || 1)); }, multiSelectAction: "forEach" }, { diff --git a/lib/ace/document.js b/lib/ace/document.js index 75a7920d..ace69e32 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -51,17 +51,17 @@ var Anchor = require("./anchor").Anchor; * @constructor **/ -var Document = function(text) { - this.$lines = []; +var Document = function(textOrLines) { + this.$lines = ['']; // There has to be one line at least in the document. If you pass an empty // string to the insert function, nothing will happen. Workaround. - if (text.length == 0) { + if (textOrLines.length == 0) { this.$lines = [""]; - } else if (Array.isArray(text)) { - this._insertLines(0, text); + } else if (Array.isArray(textOrLines)) { + this.insertLines({row: 0, column: 0}, textOrLines); } else { - this.insert({row: 0, column:0}, text); + this.insertText({row: 0, column:0}, textOrLines); } }; @@ -77,7 +77,7 @@ var Document = function(text) { this.setValue = function(text) { var len = this.getLength(); this.remove(new Range(0, 0, len, this.getLine(len-1).length)); - this.insert({row: 0, column:0}, text); + this.insertText({row: 0, column:0}, text); }; /** @@ -211,27 +211,85 @@ var Document = function(text) { * @returns {String} **/ this.getTextRange = function(range) { + return this._getLinesForRange(range).join(this.getNewLineCharacter()); + }; + + this._getLinesForRange = function(range) { if (range.start.row == range.end.row) { - return this.getLine(range.start.row) - .substring(range.start.column, range.end.column); + return [this.getLine(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()); + return lines; }; this.$clipPosition = function(position) { + var length = this.getLength(); - if (position.row >= length) { + if (position.row >= length) + { position.row = Math.max(0, length - 1); - position.column = this.getLine(length-1).length; - } else if (position.row < 0) - position.row = 0; + position.column = this.getLine(length - 1).length; + } + else + { + position.row = Math.max(0, position.row); + position.column = Math.min(Math.max(position.column, 0), this.getLine(position.row).length); + } return position; }; + + this.$getClippedRange = function(range) + { + // Get Range object. + if (!range instanceof Range) + range = Range.fromPoints(range.start, range.end); + + // Return clipped range. + this.$clipPosition(range.start); + this.$clipPosition(range.end); + return range; + } + + this.$validateDelta = function(delta) + { + function fnThrow(errorText) + { + errorText = 'Invalid Delta: ' + errorText; + console.log(errorText, delta); + throw errorText; + } + + if (!delta.lines instanceof Array) + { + fnThrow('Delta object lines must be an array'); + } + + if (!delta.range instanceof Range) + { + fnThrow('Range object is not an instance of the Range class'); + } + + var start = delta.range.start; + if (Math.min(Math.max(start.row, 0), this.getLength() - 1 ) != start.row || + Math.min(Math.max(start.column, 0), this.$lines[start.row].length) != start.column) + { + fnThrow('Range start point not contained in document'); + } + + if (delta.lines.length - 1 != delta.range.end.row - delta.range.start.row) + { + fnThrow('Range row offsets does not match delta lines'); + } + + // TODO: Validate that the ending column offset matches the lines. + // TODO: Validate for deletions that the lines deleted match the lines + // in the document. + }, /** * Inserts a block of `text` at the indicated `position`. @@ -240,54 +298,34 @@ var Document = function(text) { * @returns {Object} The position ({row, column}) of the last line of `text`. If the length of `text` is 0, this function simply returns `position`. * **/ - this.insert = function(position, text) { - if (!text || text.length === 0) - return position; - - position = this.$clipPosition(position); - - // only detect new lines if the document has no line break yet + this.insertText = function(position, text) { + + // Only detect new lines if the document has no line break yet. if (this.getLength() <= 1) this.$detectNewLine(text); - - var lines = this.$split(text); - var firstLine = lines.splice(0, 1)[0]; - var lastLine = lines.length == 0 ? null : lines.splice(lines.length - 1, 1)[0]; - - position = this.insertInLine(position, firstLine); - if (lastLine !== null) { - position = this.insertNewLine(position); // terminate first line - position = this._insertLines(position.row, lines); - position = this.insertInLine(position, lastLine || ""); - } - return position; + + return this.insertLines(position, this.$split(text)); }; /** - * Fires whenever the document changes. - * - * Several methods trigger different `"change"` events. Below is a list of each action type, followed by each property that's also available: - * - * * `"insertLines"` (emitted by [[Document.insertLines]]) - * * `range`: the [[Range]] of the change within the document - * * `lines`: the lines in the document that are changing - * * `"insertText"` (emitted by [[Document.insertNewLine]]) - * * `range`: the [[Range]] of the change within the document - * * `text`: the text that's being added - * * `"removeLines"` (emitted by [[Document.insertLines]]) - * * `range`: the [[Range]] of the change within the document - * * `lines`: the lines in the document that were removed - * * `nl`: the new line character (as defined by [[Document.getNewLineCharacter]]) - * * `"removeText"` (emitted by [[Document.removeInLine]] and [[Document.removeNewLine]]) - * * `range`: the [[Range]] of the change within the document - * * `text`: the text that's being removed - * - * @event change - * @param {Object} e Contains at least one property called `"action"`. `"action"` indicates the action that triggered the change. Each action also has a set of additional properties. - * - **/ + * Fires whenever the document changes. + * + * Several methods trigger different `"change"` events. Below is a list of each action type, followed by each property that's also available: + * + * * `"insert"` + * * `range`: the [[Range]] of the change within the document + * * `lines`: the lines being added + * * `"delete"` + * * `range`: the [[Range]] of the change within the document + * * `lines`: the lines being removed + * + * @event change + * @param {Object} e Contains at least one property called `"action"`. `"action"` indicates the action that triggered the change. Each action also has a set of additional properties. + * + **/ + /** - * Inserts the elements in `lines` into the document, starting at the row index given by `row`. This method also triggers the `'change'` event. + * Inserts the elements in `lines` into the document as full lines (does not merge with existing line), starting at the row index given by `row`. This method also triggers the `'change'` event. * @param {Number} row The index of the row to insert at * @param {Array} lines An array of strings * @returns {Object} Contains the final row and column, like this: @@ -300,100 +338,62 @@ var Document = function(text) { * ``` * **/ - 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)); - lines = lines.slice(0, 0xFFFF); + this.insertFullLines = function(row, lines) + { + // Clip to document. + // Allow one past the document end. + row = Math.min(Math.max(row, 0), this.getLength()); + + // Calculate insertion point. + var column = 0; + if (row < this.getLength()) // Insert before the specified row. + { + lines = lines.concat(['']); + column = 0; } + else // Insert after the last row in the document. + { + lines = [''].concat(lines); + row--; + var column = this.$lines[row].length; + } + + // Insert. + this.insertLines({row: row, column: column}, lines); + }, - var args = [row, 0]; - args.push.apply(args, lines); - this.$lines.splice.apply(this.$lines, args); + /** + * Inserts the elements in `lines` into the document, starting at the position index given by `row`. This method also triggers the `'change'` event. + * @param {Number} row The index of the row to insert at + * @param {Array} lines An array of strings + * @returns {Object} Contains the final row and column, like this: + * ``` + * {row: endRow, column: 0} + * ``` + * If `lines` is empty, this function returns an object containing the current row, and column, like this: + * ``` + * {row: row, column: 0} + * ``` + * + **/ + this.insertLines = function(position, lines){ - var range = new Range(row, 0, row + lines.length, 0); - var delta = { - action: "insertLines", - range: range, + // Calculate insertion range end point. + this.$clipPosition(position); + var endPoint = { + row : position.row + lines.length - 1, + column : (lines.length == 1 ? position.column : 0) + lines[lines.length - 1].length + }; + + // Apply delta (emits change). + this.applyDelta({ + action: "insert", + range: Range.fromPoints(position, endPoint), lines: lines - }; - this._emit("change", { data: delta }); - return end || range.end; - }; - - /** - * Inserts a new line into the document at the current row's `position`. This method also triggers the `'change'` event. - * @param {Object} position The position to insert at - * @returns {Object} Returns an object containing the final row and column, like this:
- * ``` - * {row: endRow, column: 0} - * ``` - * - **/ - this.insertNewLine = function(position) { - position = this.$clipPosition(position); - var line = this.$lines[position.row] || ""; - - this.$lines[position.row] = line.substring(0, position.column); - this.$lines.splice(position.row + 1, 0, line.substring(position.column, line.length)); - - var end = { - row : position.row + 1, - column : 0 - }; - - var delta = { - action: "insertText", - range: Range.fromPoints(position, end), - text: this.getNewLineCharacter() - }; - this._emit("change", { data: delta }); - - return end; - }; - - /** - * Inserts `text` into the `position` at the current row. This method also triggers the `'change'` event. - * @param {Object} position The position to insert at; it's an object that looks like `{ row: row, column: column}` - * @param {String} text A chunk of text - * @returns {Object} Returns an object containing the final row and column, like this: - * ``` - * {row: endRow, column: 0} - * ``` - * - **/ - this.insertInLine = function(position, text) { - if (text.length == 0) - return position; - - var line = this.$lines[position.row] || ""; - - this.$lines[position.row] = line.substring(0, position.column) + text - + line.substring(position.column); - - var end = { - row : position.row, - column : position.column + text.length - }; - - var delta = { - action: "insertText", - range: Range.fromPoints(position, end), - text: text - }; - this._emit("change", { data: delta }); - - return end; - }; + }); + + return endPoint; + } /** * Removes the `range` from the document. @@ -402,37 +402,16 @@ var Document = function(text) { * **/ this.remove = function(range) { - if (!range instanceof Range) - range = Range.fromPoints(range.start, range.end); - // clip to document - range.start = this.$clipPosition(range.start); - range.end = this.$clipPosition(range.end); - - if (range.isEmpty()) - return range.start; - - var firstRow = range.start.row; - var lastRow = range.end.row; - - if (range.isMultiLine()) { - var firstFullRow = range.start.column == 0 ? firstRow : firstRow + 1; - var lastFullRow = lastRow - 1; - - if (range.end.column > 0) - this.removeInLine(lastRow, 0, range.end.column); - - if (lastFullRow >= firstFullRow) - this._removeLines(firstFullRow, lastFullRow); - - if (firstFullRow != firstRow) { - this.removeInLine(firstRow, range.start.column, this.getLine(firstRow).length); - this.removeNewLine(range.start.row); - } - } - else { - this.removeInLine(firstRow, range.start.column, range.end.column); - } - return range.start; + + // Apply delta (emits change). + range = this.$getClippedRange(range); + this.applyDelta( + { + action: 'delete', + range: range, + lines: this._getLinesForRange(range), + }); + return range.start; }; /** @@ -444,21 +423,18 @@ var Document = function(text) { * **/ this.removeInLine = function(row, startColumn, endColumn) { - if (startColumn == endColumn) - return; - + + // Calculate deleteion range. var range = new Range(row, startColumn, row, endColumn); - var line = this.getLine(row); - var removed = line.substring(startColumn, endColumn); - var newLine = line.substring(0, startColumn) + line.substring(endColumn, line.length); - this.$lines.splice(row, 1, newLine); - - var delta = { - action: "removeText", + range = this.$getClippedRange(range); + + // Apply delta (emits change). + this.applyDelta({ + action: "delete", range: range, - text: removed - }; - this._emit("change", { data: delta }); + lines: this._getLinesForRange(range) + }); + return range.start; }; @@ -469,24 +445,35 @@ var Document = function(text) { * @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); - - var delta = { - action: "removeLines", + this.removeFullLines = function(firstRow, lastRow) { + + // Clip to document. + firstRow = Math.min(Math.max(0, firstRow), this.getLength() - 1); + lastRow = Math.min(Math.max(0, lastRow ), this.getLength() - 1); + + // Calculate deletion range. + // Delete the ending new line unless we're at the end of the document. + // If we're at the end of the document, delete the starting new line. + var deleteFirstNewLine = lastRow == this.getLength() - 1 && firstRow > 0; + var deleteLastNewLine = lastRow < this.getLength() - 1; + var startRow = ( deleteFirstNewLine ? firstRow - 1 : firstRow ); + var startCol = ( deleteFirstNewLine ? this.getLine(startRow).length : 0 ); + var endRow = ( deleteLastNewLine ? lastRow + 1 : lastRow ); + var endCol = ( deleteLastNewLine ? 0 : this.getLine(endRow).length ); + var range = new Range(startRow, startCol, endRow, endCol); + + // Store delelted lines with bounding newlines ommitted (maintains previous behavior). + var deletedLines = this.$lines.slice(firstRow, lastRow + 1); + + // Apply delta (emits change). + this.applyDelta({ + action: "delete", range: range, - nl: this.getNewLineCharacter(), - lines: removed - }; - this._emit("change", { data: delta }); - return removed; + lines: this._getLinesForRange(range) + }); + + // Return the deleted lines. + return deletedLines; }; /** @@ -495,20 +482,16 @@ var Document = function(text) { * **/ this.removeNewLine = function(row) { - var firstLine = this.getLine(row); - var secondLine = this.getLine(row+1); - - var range = new Range(row, firstLine.length, row+1, 0); - var line = firstLine + secondLine; - - this.$lines.splice(row, 2, line); - - var delta = { - action: "removeText", - range: range, - text: this.getNewLineCharacter() - }; - this._emit("change", { data: delta }); + + if (row < this.getLength() - 1 && row >= 0) + { + // Apply delta (emits change). + this.applyDelta({ + action: "delete", + range: new Range(row, this.getLine(row).length, row + 1, 0), + lines: ['', ''] + }); + } }; /** @@ -534,53 +517,90 @@ var Document = function(text) { this.remove(range); if (text) { - var end = this.insert(range.start, text); + var end = this.insertText(range.start, text); } else { end = range.start; } - + return end; }; /** - * Applies all the changes previously accumulated. These can be either `'includeText'`, `'insertLines'`, `'removeText'`, and `'removeLines'`. + * Applies all the changes previously accumulated. These can be either `'insert'` or `'delete'`. **/ this.applyDeltas = function(deltas) { for (var i=0; i=0; i--) { - var delta = deltas[i]; - - var range = Range.fromPoints(delta.range.start, delta.range.end); - - if (delta.action == "insertLines") - 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); - else if (delta.action == "removeText") - this.insert(range.start, delta.text); + this.revertDelta(deltas[i]); } }; + + this.revertDelta = function(delta) + { + this.applyDelta({ + action: (delta.action == 'insert' ? 'delete' : 'insert'), + range: delta.range.clone(), + lines: delta.lines.slice() + }); + }, /** * Converts an index position in a document to a `{row, column}` object. diff --git a/lib/ace/document_test.js b/lib/ace/document_test.js index 5c324db0..50bd8b49 100644 --- a/lib/ace/document_test.js +++ b/lib/ace/document_test.js @@ -48,7 +48,7 @@ module.exports = { var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); - doc.insert({row: 0, column: 1}, "juhu"); + doc.insertText({row: 0, column: 1}, "juhu"); assert.equal(doc.getValue(), ["1juhu2", "34"].join("\n")); var d = deltas.concat(); @@ -65,7 +65,7 @@ module.exports = { var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); - doc.insertNewLine({row: 0, column: 1}); + doc.insertLines({row: 0, column: 1}, ['', '']); assert.equal(doc.getValue(), ["1", "2", "34"].join("\n")); var d = deltas.concat(); @@ -82,7 +82,7 @@ module.exports = { var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); - doc.insertLines(0, ["aa", "bb"]); + doc.insertFullLines(0, ["aa", "bb"]); assert.equal(doc.getValue(), ["aa", "bb", "12", "34"].join("\n")); var d = deltas.concat(); @@ -99,7 +99,7 @@ module.exports = { var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); - doc.insertLines(2, ["aa", "bb"]); + doc.insertFullLines(2, ["aa", "bb"]); assert.equal(doc.getValue(), ["12", "34", "aa", "bb"].join("\n")); }, @@ -109,7 +109,7 @@ module.exports = { var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); - doc.insertLines(1, ["aa", "bb"]); + doc.insertFullLines(1, ["aa", "bb"]); assert.equal(doc.getValue(), ["12", "aa", "bb", "34"].join("\n")); var d = deltas.concat(); @@ -126,7 +126,7 @@ module.exports = { var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); - doc.insert({row: 0, column: 0}, "aa\nbb\ncc"); + doc.insertText({row: 0, column: 0}, "aa\nbb\ncc"); assert.equal(doc.getValue(), ["aa", "bb", "cc12", "34"].join("\n")); var d = deltas.concat(); @@ -143,7 +143,7 @@ module.exports = { var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); - doc.insert({row: 2, column: 0}, "aa\nbb\ncc"); + doc.insertText({row: 1, column: 2}, "aa\nbb\ncc"); assert.equal(doc.getValue(), ["12", "34aa", "bb", "cc"].join("\n")); var d = deltas.concat(); @@ -160,7 +160,7 @@ module.exports = { var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); - doc.insert({row: 0, column: 1}, "aa\nbb\ncc"); + doc.insertText({row: 0, column: 1}, "aa\nbb\ncc"); assert.equal(doc.getValue(), ["1aa", "bb", "cc2", "34"].join("\n")); var d = deltas.concat(); @@ -235,7 +235,7 @@ module.exports = { "test: remove lines should return the removed lines" : function() { var doc = new Document(["1234", "5678", "abcd"]); - var removed = doc.removeLines(1, 2); + var removed = doc.removeFullLines(1, 2); assert.equal(removed.join("\n"), ["5678", "abcd"].join("\n")); }, diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index a63ceeef..98c255a4 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -1136,8 +1136,8 @@ var EditSession = function(text, mode) { * * **/ - this.insert = function(position, text) { - return this.doc.insert(position, text); + this.insertText = function(position, text) { + return this.doc.insertText(position, text); }; /** @@ -1151,6 +1151,19 @@ var EditSession = function(text, mode) { this.remove = function(range) { return this.doc.remove(range); }; + + /** + * Removes a range of full lines. This method also triggers the `'change'` event. + * @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. + * + * @related Document.removeFullLines + * + **/ + this.removeFullLines = function(firstRow, lastRow){ + return this.doc.removeFullLines(firstRow, lastRow) + } /** * Reverts previous changes to your document. @@ -1227,9 +1240,7 @@ var EditSession = function(text, mode) { this.$getUndoSelection = function(deltas, isUndo, lastUndoRange) { function isInsert(delta) { - var insert = - delta.action === "insertText" || delta.action === "insertLines"; - return isUndo ? !insert : insert; + return isUndo ? delta.action !== "insert" : delta.action === "insert"; } var delta = deltas[0]; @@ -1339,7 +1350,7 @@ var EditSession = function(text, mode) { } } - toRange.end = this.insert(toRange.start, text); + toRange.end = this.insertText(toRange.start, text); if (folds.length) { var oldStart = fromRange.start; var newStart = toRange.start; @@ -1373,7 +1384,7 @@ var EditSession = function(text, mode) { this.indentRows = function(startRow, endRow, indentString) { indentString = indentString.replace(/\t/g, this.getTabString()); for (var row=startRow; row<=endRow; row++) - this.insert({row: row, column:0}, indentString); + this.insertText({row: row, column:0}, indentString); }; /** @@ -1430,11 +1441,11 @@ var EditSession = function(text, mode) { x.end.row += diff; return x; }); - + var lines = dir == 0 ? this.doc.getLines(firstRow, lastRow) - : this.doc.removeLines(firstRow, lastRow); - this.doc.insertLines(firstRow+diff, lines); + : this.doc.removeFullLines(firstRow, lastRow); + this.doc.insertFullLines(firstRow+diff, lines); folds.length && this.addFolds(folds); return diff; }; @@ -1444,8 +1455,6 @@ var EditSession = function(text, mode) { * @param {Number} lastRow The final row to move up * @returns {Number} If `firstRow` is less-than or equal to 0, this function returns 0. Otherwise, on success, it returns -1. * - * @related Document.insertLines - * **/ this.moveLinesUp = function(firstRow, lastRow) { return this.$moveLines(firstRow, lastRow, -1); @@ -1456,8 +1465,6 @@ var EditSession = function(text, mode) { * @param {Number} firstRow The starting row to move down * @param {Number} lastRow The final row to move down * @returns {Number} If `firstRow` is less-than or equal to 0, this function returns 0. Otherwise, on success, it returns -1. - * - * @related Document.insertLines **/ this.moveLinesDown = function(firstRow, lastRow) { return this.$moveLines(firstRow, lastRow, 1); @@ -1668,28 +1675,17 @@ var EditSession = function(text, mode) { this.$updateInternalDataOnChange = function(e) { var useWrapMode = this.$useWrapMode; - var len; var action = e.data.action; - var firstRow = e.data.range.start.row; - var lastRow = e.data.range.end.row; var start = e.data.range.start; var end = e.data.range.end; + var firstRow = start.row; + var lastRow = end.row; + var len = lastRow - firstRow; var removedFolds = null; - - if (action.indexOf("Lines") != -1) { - if (action == "insertLines") { - lastRow = firstRow + (e.data.lines.length); - } else { - lastRow = firstRow; - } - len = e.data.lines ? e.data.lines.length : lastRow - firstRow; - } else { - len = lastRow - firstRow; - } - + this.$updating = true; if (len != 0) { - if (action.indexOf("remove") != -1) { + if (action == "delete") { this[useWrapMode ? "$wrapData" : "$rowLengthCache"].splice(firstRow, len); var foldLines = this.$foldData; @@ -1764,7 +1760,7 @@ var EditSession = function(text, mode) { // Realign folds. E.g. if you add some new chars before a fold, the // fold should "move" to the right. len = Math.abs(e.data.range.start.column - e.data.range.end.column); - if (action.indexOf("remove") != -1) { + if (action == "delete") { // Get all the folds in the change range and remove them. removedFolds = this.getFoldsInRange(e.data.range); this.removeFolds(removedFolds); diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js index 474818b2..9df35593 100644 --- a/lib/ace/edit_session/folding.js +++ b/lib/ace/edit_session/folding.js @@ -833,7 +833,7 @@ function Folding() { if (len === 0) { this.foldWidgets[firstRow] = null; - } else if (delta.action == "removeText" || delta.action == "removeLines") { + } else if (delta.action == "delete") { this.foldWidgets.splice(firstRow, len + 1, null); } else { var args = Array(len + 1); diff --git a/lib/ace/edit_session_test.js b/lib/ace/edit_session_test.js index 87cc9567..855da4d1 100644 --- a/lib/ace/edit_session_test.js +++ b/lib/ace/edit_session_test.js @@ -430,12 +430,12 @@ module.exports = { session.setTabSize(4); assert.equal(session.getScreenWidth(), 2); - session.doc.insertNewLine({row: 0, column: Infinity}); - session.doc.insertLines(1, ["123"]); + session.doc.insertLines({row: 0, column: Infinity}, ['', '']); + session.doc.insertFullLines(1, ["123"]); assert.equal(session.getScreenWidth(), 3); - session.doc.insertNewLine({row: 0, column: Infinity}); - session.doc.insertLines(1, ["\t\t"]); + session.doc.insertLines({row: 0, column: Infinity}, ['', '']); + session.doc.insertFullLines(1, ["\t\t"]); assert.equal(session.getScreenWidth(), 8); @@ -459,9 +459,9 @@ module.exports = { session.setUseWrapMode(true); - document.insertLines(0, ["a", "b"]); - document.insertLines(2, ["c", "d"]); - document.removeLines(1, 2); + document.insertFullLines(0, ["a", "b"]); + document.insertFullLines(2, ["c", "d"]); + document.removeFullLines(1, 2); }, "test wrapMode init has to create wrapData array": function() { @@ -649,7 +649,7 @@ module.exports = { var foldLines = session.$foldData; function insert(row, column, text) { - session.insert({row: row, column: column}, text); + session.insertText({row: row, column: column}, text); // Force the session to store all changes made to the document NOW // on the undoManager's queue. Otherwise we can't undo in separate @@ -748,7 +748,7 @@ module.exports = { undoManager = session.getUndoManager(), foldLines = session.$foldData; function insert(row, column, text) { - session.insert({row: row, column: column}, text); + session.insertText({row: row, column: column}, text); // Force the session to store all changes made to the document NOW // on the undoManager's queue. Otherwise we can't undo in separate // steps later. diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 6486a881..3926fc81 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -601,19 +601,10 @@ var Editor = function(renderer, session) { * **/ this.onDocumentChange = function(e) { - var delta = e.data; - var range = delta.range; - var lastRow; - - if (range.start.row == range.end.row && delta.action != "insertLines" && delta.action != "removeLines") - lastRow = range.end.row; - else - lastRow = Infinity; - this.renderer.updateLines(range.start.row, lastRow); - + this.renderer.updateLines(e.data.range.start.row, e.data.range.end.row); this._emit("change", e); - - // update cursor because tab characters can influence the cursor position + + // update cursor because tab characters can influence the cursor position. this.$cursorChange(); }; @@ -827,7 +818,7 @@ var Editor = function(renderer, session) { if (this.$readOnly) return; this._emit("paste", text); - this.insert(text); + this.insertText(text); }; @@ -841,7 +832,7 @@ var Editor = function(renderer, session) { * * **/ - this.insert = function(text) { + this.insertText = function(text) { var session = this.session; var mode = session.getMode(); var cursor = this.getCursorPosition(); @@ -887,7 +878,7 @@ var Editor = function(renderer, session) { var lineState = session.getState(cursor.row); var line = session.getLine(cursor.row); var shouldOutdent = mode.checkOutdent(lineState, line, text); - var end = session.insert(cursor, text); + var end = session.insertText(cursor, text); if (transform && transform.selection) { if (transform.selection.length == 2) { // Transform relative to the current column @@ -906,7 +897,7 @@ var Editor = function(renderer, session) { if (session.getDocument().isNewLine(text)) { var lineIndent = mode.getNextLineIndent(lineState, line.slice(0, cursor.column), session.getTabString()); - session.insert({row: cursor.row+1, column: 0}, lineIndent); + session.insertText({row: cursor.row+1, column: 0}, lineIndent); } if (shouldOutdent) mode.autoOutdent(lineState, session, cursor.row); @@ -1279,7 +1270,7 @@ var Editor = function(renderer, session) { } var cursor = this.getCursorPosition(); - this.insert("\n"); + this.insertText("\n"); this.moveCursorToPosition(cursor); }; @@ -1378,7 +1369,7 @@ var Editor = function(renderer, session) { this.selection.setSelectionRange(range); indentString = "\t"; } - return this.insert(indentString); + return this.insertText(indentString); }; /** @@ -1516,15 +1507,7 @@ var Editor = function(renderer, session) { **/ this.removeLines = function() { var rows = this.$getSelectedRows(); - var range; - if (rows.first === 0 || rows.last+1 < this.session.getLength()) - range = new Range(rows.first, 0, rows.last+1, 0); - else - range = new Range( - rows.first-1, this.session.getLine(rows.first-1).length, - rows.last, this.session.getLine(rows.last).length - ); - this.session.remove(range); + this.session.removeFullLines(rows.first, rows.last); this.clearSelection(); }; @@ -1538,7 +1521,7 @@ var Editor = function(renderer, session) { doc.duplicateLines(row, row); } else { var point = reverse ? range.start : range.end; - var endPoint = doc.insert(point, doc.getTextRange(range), false); + var endPoint = doc.insertText(point, doc.getTextRange(range), false); range.start = point; range.end = endPoint; diff --git a/lib/ace/editor_change_document_test.js b/lib/ace/editor_change_document_test.js index a1fdaad9..83fffeb3 100644 --- a/lib/ace/editor_change_document_test.js +++ b/lib/ace/editor_change_document_test.js @@ -172,7 +172,7 @@ module.exports = { self.session1.setMode(new HtmlMode()); // 5. Try to type valid HTML - self.session1.insert({row: 0, column: 0}, ""); + self.session1.insertText({row: 0, column: 0}, ""); setTimeout(function() { assert.equal(Object.keys(self.session1.getAnnotations()).length, 0); diff --git a/lib/ace/editor_navigation_test.js b/lib/ace/editor_navigation_test.js index ab348241..b60d391d 100644 --- a/lib/ace/editor_navigation_test.js +++ b/lib/ace/editor_navigation_test.js @@ -150,7 +150,7 @@ module.exports = { var editor = new Editor(new MockRenderer(), new EditSession(["1234", "1234567890"])); editor.navigateTo(0, 3); - editor.insert("juhu"); + editor.insertText("juhu"); editor.navigateDown(); assert.position(editor.getCursorPosition(), 1, 7); diff --git a/lib/ace/ext/chromevox.js b/lib/ace/ext/chromevox.js index 9f7a7996..20bbb6c7 100644 --- a/lib/ace/ext/chromevox.js +++ b/lib/ace/ext/chromevox.js @@ -581,12 +581,12 @@ var onSelectionChange = function(evt) { var onChange = function(evt) { var data = evt.data; switch (data.action) { - case 'removeText': + case 'delete': cvox.Api.speak(data.text, 0, DELETED_PROP); /* Let the future cursor change event know it's from text change. */ changed = true; break; - case 'insertText': + case 'insert': cvox.Api.speak(data.text, 0); /* Let the future cursor change event know it's from text change. */ changed = true; diff --git a/lib/ace/ext/elastic_tabstops_lite.js b/lib/ace/ext/elastic_tabstops_lite.js index 9901c5df..4d3a40d4 100644 --- a/lib/ace/ext/elastic_tabstops_lite.js +++ b/lib/ace/ext/elastic_tabstops_lite.js @@ -236,7 +236,7 @@ var ElasticTabstopsLite = function(editor) { if (difference > 0) { // put the spaces after the tab and then delete the tab, so any insertion // points behave as expected - this.$editor.session.getDocument().insertInLine({row: row, column: it + 1}, Array(difference + 1).join(" ") + "\t"); + this.$editor.session.getDocument().insertText({row: row, column: it + 1}, Array(difference + 1).join(" ") + "\t"); this.$editor.session.getDocument().removeInLine(row, it, it + 1); bias += difference; diff --git a/lib/ace/ext/whitespace.js b/lib/ace/ext/whitespace.js index 83486fb0..736e991d 100644 --- a/lib/ace/ext/whitespace.js +++ b/lib/ace/ext/whitespace.js @@ -153,7 +153,7 @@ exports.convertIndentation = function(session, ch, len) { if (toInsert != match) { doc.removeInLine(i, 0, match.length); - doc.insertInLine({row: i, column: 0}, toInsert); + doc.insertText({row: i, column: 0}, toInsert); } } } diff --git a/lib/ace/keyboard/vim/commands.js b/lib/ace/keyboard/vim/commands.js index dd3357d6..defca037 100644 --- a/lib/ace/keyboard/vim/commands.js +++ b/lib/ace/keyboard/vim/commands.js @@ -104,7 +104,7 @@ var actions = exports.actions = { if (param && param.length) { if (param.length > 1) param = param == "return" ? "\n" : param == "tab" ? "\t" : param; - repeat(function() { editor.insert(param); }, count || 1); + repeat(function() { editor.insertText(param); }, count || 1); editor.navigateLeft(); } } @@ -224,12 +224,12 @@ var actions = exports.actions = { var pos = editor.getCursorPosition(); pos.column = editor.session.getLine(pos.row).length; var text = lang.stringRepeat("\n" + defaultReg.text, count || 1); - editor.session.insert(pos, text); + editor.session.insertText(pos, text); editor.moveCursorTo(pos.row + 1, 0); } else { editor.navigateRight(); - editor.insert(lang.stringRepeat(defaultReg.text, count || 1)); + editor.insertText(lang.stringRepeat(defaultReg.text, count || 1)); editor.navigateLeft(); } editor.setOverwrite(true); @@ -245,11 +245,11 @@ var actions = exports.actions = { var pos = editor.getCursorPosition(); pos.column = 0; var text = lang.stringRepeat(defaultReg.text + "\n", count || 1); - editor.session.insert(pos, text); + editor.session.insertText(pos, text); editor.moveCursorToPosition(pos); } else { - editor.insert(lang.stringRepeat(defaultReg.text, count || 1)); + editor.insertText(lang.stringRepeat(defaultReg.text, count || 1)); } editor.setOverwrite(true); editor.selection.clearSelection(); diff --git a/lib/ace/keyboard/vim/maps/motions.js b/lib/ace/keyboard/vim/maps/motions.js index 91c8b8af..2bf580b0 100644 --- a/lib/ace/keyboard/vim/maps/motions.js +++ b/lib/ace/keyboard/vim/maps/motions.js @@ -545,7 +545,7 @@ module.exports = { if (content.length) { editor.navigateLineEnd() - editor.insert(content); + editor.insertText(content); util.insertMode(editor); } } @@ -562,9 +562,9 @@ module.exports = { if(row > 0) { editor.navigateUp(); editor.navigateLineEnd() - editor.insert(content); + editor.insertText(content); } else { - editor.session.insert({row: 0, column: 0}, content); + editor.session.insertText({row: 0, column: 0}, content); editor.navigateUp(); } util.insertMode(editor); diff --git a/lib/ace/layer/gutter.js b/lib/ace/layer/gutter.js index 5a535094..9d813d2a 100644 --- a/lib/ace/layer/gutter.js +++ b/lib/ace/layer/gutter.js @@ -108,8 +108,8 @@ var Gutter = function(parentEl) { var len = range.end.row - firstRow; if (len === 0) { // do nothing - } else if (delta.action == "removeText" || delta.action == "removeLines") { - this.$annotations.splice(firstRow, len + 1, null); + } else if (delta.action == "delete") { + this.$annotations.splice(firstRow, len, null); } else { var args = new Array(len + 1); args.unshift(firstRow, 1); diff --git a/lib/ace/line_widgets.js b/lib/ace/line_widgets.js index 597b3ca2..4023bd55 100644 --- a/lib/ace/line_widgets.js +++ b/lib/ace/line_widgets.js @@ -123,7 +123,7 @@ function LineWidgets(session) { if (len === 0) { // return - } else if (delta.action == "removeText" || delta.action == "removeLines") { + } else if (delta.action == "delete") { var removed = cells.splice(startRow + 1, len); removed.forEach(function(w) { w && this.removeLineWidget(w); diff --git a/lib/ace/mode/javascript_test.js b/lib/ace/mode/javascript_test.js index b413765a..cc2b22d7 100644 --- a/lib/ace/mode/javascript_test.js +++ b/lib/ace/mode/javascript_test.js @@ -131,7 +131,7 @@ module.exports = { this.mode.toggleCommentLines("start", session, 0, 2); assert.equal([" abc", " cde", " fg"].join("\n"), session.toString()); - session.insert({row: 0, column: 0}, " "); + session.insertText({row: 0, column: 0}, " "); this.mode.toggleCommentLines("start", session, 0, 2); assert.equal(["// abc", "// cde", "// fg"].join("\n"), session.toString()); }, diff --git a/lib/ace/mode/text.js b/lib/ace/mode/text.js index d04fed78..534ac03c 100644 --- a/lib/ace/mode/text.js +++ b/lib/ace/mode/text.js @@ -92,8 +92,8 @@ var Mode = function() { if (testRemove(line, i)) return; if (!ignoreBlankLines || /\S/.test(line)) { - doc.insertInLine({row: i, column: line.length}, lineCommentEnd); - doc.insertInLine({row: i, column: minIndent}, lineCommentStart); + doc.insertText({row: i, column: line.length}, lineCommentEnd); + doc.insertText({row: i, column: minIndent}, lineCommentStart); } }; @@ -138,9 +138,9 @@ var Mode = function() { var comment = function(line, i) { if (!ignoreBlankLines || /\S/.test(line)) { if (shouldInsertSpace(line, minIndent, minIndent)) - doc.insertInLine({row: i, column: minIndent}, commentWithSpace); + doc.insertText({row: i, column: minIndent}, commentWithSpace); else - doc.insertInLine({row: i, column: minIndent}, lineCommentStart); + doc.insertText({row: i, column: minIndent}, lineCommentStart); } }; var testRemove = function(line, i) { @@ -244,8 +244,8 @@ var Mode = function() { } else { colDiff = comment.start.length startRow = range.start.row; - session.insert(range.end, comment.end); - session.insert(range.start, comment.start); + session.insertText(range.end, comment.end); + session.insertText(range.start, comment.start); } // todo: selection should have ended up in the right place automatically! if (initialRange.start.row == startRow) diff --git a/lib/ace/mouse/dragdrop_handler.js b/lib/ace/mouse/dragdrop_handler.js index 3a4bc188..3ad54bb8 100644 --- a/lib/ace/mouse/dragdrop_handler.js +++ b/lib/ace/mouse/dragdrop_handler.js @@ -176,7 +176,7 @@ function DragdropHandler(mouseHandler) { var dropData = dataTransfer.getData('Text'); range = { start: dragCursor, - end: editor.session.insert(dragCursor, dropData) + end: editor.session.insertText(dragCursor, dropData) }; editor.focus(); dragOperation = null; diff --git a/lib/ace/multi_select.js b/lib/ace/multi_select.js index 7f4c5355..883d9a7c 100644 --- a/lib/ace/multi_select.js +++ b/lib/ace/multi_select.js @@ -544,7 +544,7 @@ var Editor = require("./editor").Editor; this._signal("paste", text); if (!this.inMultiSelectMode || this.inVirtualSelectionMode) - return this.insert(text); + return this.insertText(text); var lines = text.split(/\r\n|\r|\n/); var ranges = this.selection.rangeList.ranges; @@ -557,7 +557,7 @@ var Editor = require("./editor").Editor; if (!range.isEmpty()) this.session.remove(range); - this.session.insert(range.start, lines[i]); + this.session.insertText(range.start, lines[i]); } }; @@ -740,9 +740,9 @@ var Editor = require("./editor").Editor; if (fr < 0) fr = 0; if (lr >= max) lr = max - 1; } - var lines = this.session.doc.removeLines(fr, lr); + var lines = this.session.removeFullLines(fr, lr); lines = this.$reAlignText(lines, guessRange); - this.session.doc.insert({row: fr, column: 0}, lines.join("\n") + "\n"); + this.session.insertText({row: fr, column: 0}, lines.join("\n") + "\n"); if (!guessRange) { range.start.column = 0; range.end.column = lines[lines.length - 1].length; @@ -778,7 +778,7 @@ var Editor = require("./editor").Editor; var l = maxCol - p.column; var d = spaceOffsets[i] - minSpace; if (l > d) - session.insert(p, lang.stringRepeat(" ", l - d)); + session.insertText(p, lang.stringRepeat(" ", l - d)); else session.remove(new Range(p.row, p.column, p.row, p.column - l + d)); diff --git a/lib/ace/occur_test.js b/lib/ace/occur_test.js index 59fb8548..a9b2c894 100644 --- a/lib/ace/occur_test.js +++ b/lib/ace/occur_test.js @@ -55,7 +55,7 @@ module.exports = { }, "test: find lines matching" : function() { - editor.session.insert({row: 0, column: 0}, 'abc\ndef\nxyz\nbcxbc'); + editor.session.insertText({row: 0, column: 0}, 'abc\ndef\nxyz\nbcxbc'); var result = occur.matchingLines(editor.session, {needle: 'bc'}), expected = [{row: 0, content: 'abc'}, {row: 3, content: 'bcxbc'}]; assert.deepEqual(result, expected); @@ -63,7 +63,7 @@ module.exports = { "test: display occurrences" : function() { var text = 'abc\ndef\nxyz\nbcx\n'; - editor.session.insert({row: 0, column: 0}, text); + editor.session.insertText({row: 0, column: 0}, text); occur.displayOccurContent(editor, {needle: 'bc'}); assert.equal(editor.getValue(), 'abc\nbcx'); occur.displayOriginalContent(editor); @@ -72,7 +72,7 @@ module.exports = { "test: original position from occur doc" : function() { var text = 'abc\ndef\nxyz\nbcx\n'; - editor.session.insert({row: 0, column: 0}, text); + editor.session.insertText({row: 0, column: 0}, text); occur.displayOccurContent(editor, {needle: 'bc'}); assert.equal(editor.getValue(), 'abc\nbcx'); var pos = occur.occurToOriginalPosition(editor.session, {row: 1, column: 2}); @@ -82,7 +82,7 @@ module.exports = { "test: occur command" : function() { // setup var text = 'hel\nlo\n\nwo\nrld\n'; - editor.session.insert({row: 0, column: 0}, text); + editor.session.insertText({row: 0, column: 0}, text); editor.commands.addCommand(occurStartCommand); // run occur for lines including 'o' @@ -106,7 +106,7 @@ module.exports = { "test: occur navigation" : function() { // setup var text = 'hel\nlo\n\nwo\nrld\n'; - editor.session.insert({row: 0, column: 0}, text); + editor.session.insertText({row: 0, column: 0}, text); editor.commands.addCommand(occurStartCommand); editor.moveCursorToPosition({row: 1, column: 1}); @@ -125,7 +125,7 @@ module.exports = { "test: recursive occur" : function() { // setup var text = 'x\nabc1\nx\nabc2\n'; - editor.session.insert({row: 0, column: 0}, text); + editor.session.insertText({row: 0, column: 0}, text); editor.commands.addCommand(occurStartCommand); // orig -> occur1 diff --git a/lib/ace/placeholder.js b/lib/ace/placeholder.js index bcb44ce2..9d0a3c3c 100644 --- a/lib/ace/placeholder.js +++ b/lib/ace/placeholder.js @@ -155,21 +155,21 @@ var PlaceHolder = function(session, length, pos, others, mainClass, othersClass) if(range.start.row !== this.pos.row) return; if (this.$updating) return; this.$updating = true; - var lengthDiff = delta.action === "insertText" ? range.end.column - range.start.column : range.start.column - range.end.column; + var lengthDiff = delta.action === "insert" ? range.end.column - range.start.column : range.start.column - range.end.column; if(range.start.column >= this.pos.column && range.start.column <= this.pos.column + this.length + 1) { var distanceFromStart = range.start.column - this.pos.column; this.length += lengthDiff; if(!this.session.$fromUndo) { - if(delta.action === "insertText") { + if(delta.action === "insert") { for (var i = this.others.length - 1; i >= 0; i--) { var otherPos = this.others[i]; var newPos = {row: otherPos.row, column: otherPos.column + distanceFromStart}; if(otherPos.row === range.start.row && range.start.column < otherPos.column) newPos.column += lengthDiff; - this.doc.insert(newPos, delta.text); + this.doc.insertLines(newPos, delta.lines); } - } else if(delta.action === "removeText") { + } else if(delta.action === "delete") { for (var i = this.others.length - 1; i >= 0; i--) { var otherPos = this.others[i]; var newPos = {row: otherPos.row, column: otherPos.column + distanceFromStart}; @@ -179,7 +179,7 @@ var PlaceHolder = function(session, length, pos, others, mainClass, othersClass) } } // Special case: insert in beginning - if(range.start.column === this.pos.column && delta.action === "insertText") { + if(range.start.column === this.pos.column && delta.action === "insert") { setTimeout(function() { this.pos.setPosition(this.pos.row, this.pos.column - lengthDiff); for (var i = 0; i < this.others.length; i++) { @@ -191,7 +191,7 @@ var PlaceHolder = function(session, length, pos, others, mainClass, othersClass) } }.bind(this), 0); } - else if(range.start.column === this.pos.column && delta.action === "removeText") { + else if(range.start.column === this.pos.column && delta.action === "delete") { setTimeout(function() { for (var i = 0; i < this.others.length; i++) { var other = this.others[i]; diff --git a/lib/ace/placeholder_test.js b/lib/ace/placeholder_test.js index 97e561fd..dc7e4b23 100644 --- a/lib/ace/placeholder_test.js +++ b/lib/ace/placeholder_test.js @@ -53,9 +53,9 @@ module.exports = { new PlaceHolder(session, 1, {row: 0, column: 4}, [{row: 1, column: 12}, {row: 1, column: 15}]); editor.moveCursorTo(0, 5); - editor.insert('b'); + editor.insertText('b'); assert.equal(session.doc.getValue(), "var ab = 10;\nconsole.log(ab, ab);"); - editor.insert('cd'); + editor.insertText('cd'); assert.equal(session.doc.getValue(), "var abcd = 10;\nconsole.log(abcd, abcd);"); editor.remove('left'); editor.remove('left'); @@ -70,7 +70,7 @@ module.exports = { new PlaceHolder(session, 1, {row: 0, column: 4}, [{row: 1, column: 12}, {row: 1, column: 15}]); editor.moveCursorTo(2, 0); - editor.insert('b'); + editor.insertText('b'); assert.equal(session.doc.getValue(), "var a = 10;\nconsole.log(a, a);\nb"); }, @@ -81,12 +81,12 @@ module.exports = { var p = new PlaceHolder(session, 1, {row: 0, column: 4}, [{row: 1, column: 12}, {row: 1, column: 15}]); editor.moveCursorTo(0, 4); - editor.insert('$'); + editor.insertText('$'); assert.equal(session.doc.getValue(), "var $a = 10;\nconsole.log($a, $a);"); editor.moveCursorTo(0, 4); // Have to put this in a setTimeout because the anchor is only fixed later. setTimeout(function() { - editor.insert('v'); + editor.insertText('v'); assert.equal(session.doc.getValue(), "var v$a = 10;\nconsole.log(v$a, v$a);"); next(); }, 10); @@ -99,10 +99,10 @@ module.exports = { var p = new PlaceHolder(session, 1, {row: 0, column: 4}, [{row: 1, column: 12}, {row: 1, column: 15}]); editor.moveCursorTo(0, 5); - editor.insert('b'); + editor.insertText('b'); assert.equal(session.doc.getValue(), "var ab = 10;\nconsole.log(ab, ab);"); p.detach(); - editor.insert('cd'); + editor.insertText('cd'); assert.equal(session.doc.getValue(), "var abcd = 10;\nconsole.log(ab, ab);"); }, @@ -136,8 +136,8 @@ module.exports = { var p = new PlaceHolder(session, 1, {row: 0, column: 4}, [{row: 1, column: 12}, {row: 1, column: 15}]); editor.moveCursorTo(0, 5); - editor.insert('b'); - editor.insert('cd'); + editor.insertText('b'); + editor.insertText('cd'); editor.remove('left'); assert.equal(session.doc.getValue(), "var abc = 10;\nconsole.log(abc, abc);"); // Wait a little for the changes to enter the undo stack From 27768230c86eadb6f3a65f6c212af6c0f6a2972b Mon Sep 17 00:00:00 2001 From: aldendaniels Date: Wed, 1 Jan 2014 11:05:27 -0600 Subject: [PATCH 02/92] Maintain public API and update coding convention This seeks to keep the public API in-tact while improving method names within ace by keeping the old methods as wrappers around the new better-named methods. For example, document.insert() now simply calls document.insertText() and warns the caller via a console.log() that they are using a deprecated method. I've also updated the coding style of my changes (where I noticed discrepancies) to match the rest of Ace. --- lib/ace/anchor.js | 22 ++++------ lib/ace/anchor_test.js | 4 +- lib/ace/document.js | 80 +++++++++++++++++++++--------------- lib/ace/document_test.js | 2 +- lib/ace/edit_session.js | 6 +++ lib/ace/edit_session_test.js | 4 +- lib/ace/editor.js | 6 +++ lib/ace/placeholder.js | 2 +- 8 files changed, 73 insertions(+), 53 deletions(-) diff --git a/lib/ace/anchor.js b/lib/ace/anchor.js index 91a62eb0..affb4f19 100644 --- a/lib/ace/anchor.js +++ b/lib/ace/anchor.js @@ -117,33 +117,29 @@ var Anchor = exports.Anchor = function(doc, row, column) { var deltaEnd = (deltaIsInsert ? deltaStart : delta.range.end); // Collapse insert range. // DELTA AFTER POINT: No change needed. - if (_pointsInOrder(point, deltaStart, moveIfEqual)) - { - return ( - { + if (_pointsInOrder(point, deltaStart, moveIfEqual)) { + return { row: point.row, column: point.column - }); + }; } // DELTA BEFORE POINT: Move point by delta shift. - if (_pointsInOrder(deltaEnd, point, !moveIfEqual)) - { - return ( - { + if (_pointsInOrder(deltaEnd, point, !moveIfEqual)) { + return { row: point.row + deltaRowShift, column: point.column + (point.row == deltaEnd.row ? deltaColShift : 0) - }); + }; } // DELTA ENVELOPS POINT (delete only): Move point to delta start. if (delta.action != 'delete') throw 'Delete action expected.'; - return ( - { + + return { row: deltaStart.row, column: deltaStart.column - }); + }; } var delta = e.data; diff --git a/lib/ace/anchor_test.js b/lib/ace/anchor_test.js index 0588c9b6..0661765b 100644 --- a/lib/ace/anchor_test.js +++ b/lib/ace/anchor_test.js @@ -70,7 +70,7 @@ module.exports = { var doc = new Document("juhu\nkinners"); var anchor = new Anchor(doc, 1, 4); - doc.insertLines({row: 0, column: 0}, ['', '']); + doc.insertMergedLines({row: 0, column: 0}, ['', '']); assert.position(anchor.getPosition(), 2, 4); }, @@ -78,7 +78,7 @@ module.exports = { var doc = new Document("juhu\nkinners"); var anchor = new Anchor(doc, 1, 4); - doc.insertLines({row: 1, column: 2}, ['', '']); + doc.insertMergedLines({row: 1, column: 2}, ['', '']); assert.position(anchor.getPosition(), 2, 2); }, diff --git a/lib/ace/document.js b/lib/ace/document.js index ace69e32..07726de7 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -59,7 +59,7 @@ var Document = function(textOrLines) { if (textOrLines.length == 0) { this.$lines = [""]; } else if (Array.isArray(textOrLines)) { - this.insertLines({row: 0, column: 0}, textOrLines); + this.insertMergedLines({row: 0, column: 0}, textOrLines); } else { this.insertText({row: 0, column:0}, textOrLines); } @@ -230,13 +230,10 @@ var Document = function(textOrLines) { this.$clipPosition = function(position) { var length = this.getLength(); - if (position.row >= length) - { + if (position.row >= length) { position.row = Math.max(0, length - 1); position.column = this.getLine(length - 1).length; - } - else - { + } else { position.row = Math.max(0, position.row); position.column = Math.min(Math.max(position.column, 0), this.getLine(position.row).length); } @@ -264,16 +261,15 @@ var Document = function(textOrLines) { throw errorText; } + // Validate lines. if (!delta.lines instanceof Array) - { fnThrow('Delta object lines must be an array'); - } + // Validate range type. if (!delta.range instanceof Range) - { fnThrow('Range object is not an instance of the Range class'); - } + // Validate start point. var start = delta.range.start; if (Math.min(Math.max(start.row, 0), this.getLength() - 1 ) != start.row || Math.min(Math.max(start.column, 0), this.$lines[start.row].length) != start.column) @@ -281,16 +277,37 @@ var Document = function(textOrLines) { fnThrow('Range start point not contained in document'); } + // Validate ending row offset. if (delta.lines.length - 1 != delta.range.end.row - delta.range.start.row) - { fnThrow('Range row offsets does not match delta lines'); - } - // TODO: Validate that the ending column offset matches the lines. - // TODO: Validate for deletions that the lines deleted match the lines - // in the document. + // TODO: + // - Validate that the ending column offset matches the lines. + // - Validate the deleted lines match the lines in the document. }, + // Deprecated methods retained for backwards compatibility. + this.insert = function(position, text){ + console.log('Warning: document.insert is deprecated. Use the insertText method instead.'); + return this.insertText(position, text); + } + this.insertLines = function(row, lines) { + console.log('Warning: document.insertLines is deprecated. Use the insertFullLines method instead.'); + return this.insertFullLines(row, lines); + } + this.removeLines = function(firstRow, lastRow) { + console.log('Warning: document.removeLines is deprecated. Use the removeFullLines method instead.'); + return this.removeFullLines(firstRow, lastRow); + } + this.insertNewLine = function(position) { + console.log('Warning: document.insertNewLine is deprecated. Use insertMergedLines(position, [\'\', \'\']) instead.'); + return this.insertMergedLines(position, ['', '']); + } + this.insertInLine = function(position, text) { + console.log('Warning: document.insertInLine is deprecated. Use insertText instead.'); + return this.insertText(position, text); + } + /** * Inserts a block of `text` at the indicated `position`. * @param {Object} position The position to start inserting at; it's an object that looks like `{ row: row, column: column}` @@ -304,9 +321,9 @@ var Document = function(textOrLines) { if (this.getLength() <= 1) this.$detectNewLine(text); - return this.insertLines(position, this.$split(text)); + return this.insertMergedLines(position, this.$split(text)); }; - + /** * Fires whenever the document changes. * @@ -338,28 +355,26 @@ var Document = function(textOrLines) { * ``` * **/ - this.insertFullLines = function(row, lines) - { + this.insertFullLines = function(row, lines) { // Clip to document. // Allow one past the document end. row = Math.min(Math.max(row, 0), this.getLength()); // Calculate insertion point. var column = 0; - if (row < this.getLength()) // Insert before the specified row. - { + if (row < this.getLength()) { + // Insert before the specified row. lines = lines.concat(['']); column = 0; - } - else // Insert after the last row in the document. - { + } else { + // Insert after the last row in the document. lines = [''].concat(lines); row--; var column = this.$lines[row].length; } // Insert. - this.insertLines({row: row, column: column}, lines); + this.insertMergedLines({row: row, column: column}, lines); }, /** @@ -376,7 +391,7 @@ var Document = function(textOrLines) { * ``` * **/ - this.insertLines = function(position, lines){ + this.insertMergedLines = function(position, lines) { // Calculate insertion range end point. this.$clipPosition(position); @@ -405,8 +420,7 @@ var Document = function(textOrLines) { // Apply delta (emits change). range = this.$getClippedRange(range); - this.applyDelta( - { + this.applyDelta({ action: 'delete', range: range, lines: this._getLinesForRange(range), @@ -483,14 +497,13 @@ var Document = function(textOrLines) { **/ this.removeNewLine = function(row) { - if (row < this.getLength() - 1 && row >= 0) - { + if (row < this.getLength() - 1 && row >= 0) { // Apply delta (emits change). this.applyDelta({ action: "delete", range: new Range(row, this.getLine(row).length, row + 1, 0), lines: ['', ''] - }); + }); } }; @@ -558,8 +571,7 @@ var Document = function(textOrLines) { { case 'insert': splitLine(this.$lines, delta.range.start); - for (var i = 0; i < delta.lines.length; i++) - { + for (var i = 0; i < delta.lines.length; i++) { var row = delta.range.start.row + 1 + i; this.$lines.splice(row, 0, delta.lines[i]); } @@ -571,7 +583,7 @@ var Document = function(textOrLines) { splitLine(this.$lines, delta.range.end); splitLine(this.$lines, delta.range.start); this.$lines.splice( - delta.range.start.row + 1, // Where to start deleting + delta.range.start.row + 1, // Where to start deleting delta.range.end.row - delta.range.start.row + 1 // Num lines to delete. ); joinLineWithNext(this.$lines, delta.range.start.row); diff --git a/lib/ace/document_test.js b/lib/ace/document_test.js index 50bd8b49..c29d6133 100644 --- a/lib/ace/document_test.js +++ b/lib/ace/document_test.js @@ -65,7 +65,7 @@ module.exports = { var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); - doc.insertLines({row: 0, column: 1}, ['', '']); + doc.insertMergedLines({row: 0, column: 1}, ['', '']); assert.equal(doc.getValue(), ["1", "2", "34"].join("\n")); var d = deltas.concat(); diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 98c255a4..e48bdc37 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -1127,6 +1127,12 @@ var EditSession = function(text, mode) { this.getTextRange = function(range) { return this.doc.getTextRange(range || this.selection.getRange()); }; + + // Deprecated method retained for backwards compatibility. + this.insert = function(position, text){ + console.log('Warning: editsession.insert is deprecated. Use the insertText method instead.'); + return this.insertText(position, text) + } /** * Inserts a block of `text` and the indicated `position`. diff --git a/lib/ace/edit_session_test.js b/lib/ace/edit_session_test.js index 855da4d1..fd20259c 100644 --- a/lib/ace/edit_session_test.js +++ b/lib/ace/edit_session_test.js @@ -430,11 +430,11 @@ module.exports = { session.setTabSize(4); assert.equal(session.getScreenWidth(), 2); - session.doc.insertLines({row: 0, column: Infinity}, ['', '']); + session.doc.insertMergedLines({row: 0, column: Infinity}, ['', '']); session.doc.insertFullLines(1, ["123"]); assert.equal(session.getScreenWidth(), 3); - session.doc.insertLines({row: 0, column: Infinity}, ['', '']); + session.doc.insertMergedLines({row: 0, column: Infinity}, ['', '']); session.doc.insertFullLines(1, ["\t\t"]); assert.equal(session.getScreenWidth(), 8); diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 3926fc81..2854eaaf 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -825,6 +825,12 @@ var Editor = function(renderer, session) { this.execCommand = function(command, args) { this.commands.exec(command, this, args); }; + + // Deprecated method retained for backwards compatibility. + this.insert = function(text){ + console.log('Warning: editor.insert is deprecated. Use the insertText method instead.'); + return this.insertText(text) + } /** * Inserts `text` into wherever the cursor is pointing. diff --git a/lib/ace/placeholder.js b/lib/ace/placeholder.js index 9d0a3c3c..c9a44e50 100644 --- a/lib/ace/placeholder.js +++ b/lib/ace/placeholder.js @@ -167,7 +167,7 @@ var PlaceHolder = function(session, length, pos, others, mainClass, othersClass) var newPos = {row: otherPos.row, column: otherPos.column + distanceFromStart}; if(otherPos.row === range.start.row && range.start.column < otherPos.column) newPos.column += lengthDiff; - this.doc.insertLines(newPos, delta.lines); + this.doc.insertMergedLines(newPos, delta.lines); } } else if(delta.action === "delete") { for (var i = this.others.length - 1; i >= 0; i--) { From 026af74016a2829003a7dfa2ccde906309abd40b Mon Sep 17 00:00:00 2001 From: aldendaniels Date: Wed, 1 Jan 2014 12:04:12 -0600 Subject: [PATCH 03/92] Fix render bugs Both were introduced in 2e6f12725bc01011c545c99c1f1c1da6065063d9. --- lib/ace/editor.js | 8 ++++++-- lib/ace/layer/gutter.js | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 2854eaaf..73873b4a 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -601,10 +601,14 @@ var Editor = function(renderer, session) { * **/ this.onDocumentChange = function(e) { - this.renderer.updateLines(e.data.range.start.row, e.data.range.end.row); + + // Rerender and emit "change" event. + var range = e.data.range; + var lastRow = (range.start.row == range.end.row ? range.end.row : Infinity); + this.renderer.updateLines(range.start.row, lastRow); this._emit("change", e); - // update cursor because tab characters can influence the cursor position. + // Update cursor because tab characters can influence the cursor position. this.$cursorChange(); }; diff --git a/lib/ace/layer/gutter.js b/lib/ace/layer/gutter.js index 9d813d2a..82dcf245 100644 --- a/lib/ace/layer/gutter.js +++ b/lib/ace/layer/gutter.js @@ -109,7 +109,7 @@ var Gutter = function(parentEl) { if (len === 0) { // do nothing } else if (delta.action == "delete") { - this.$annotations.splice(firstRow, len, null); + this.$annotations.splice(firstRow, len + 1, null); } else { var args = new Array(len + 1); args.unshift(firstRow, 1); From 810e196cc6f1d49da208c8c4ed98045e6c29ebbb Mon Sep 17 00:00:00 2001 From: aldendaniels Date: Wed, 1 Jan 2014 12:06:19 -0600 Subject: [PATCH 04/92] Use console.warn for deprecated methods console.warn makes better sense than console.log and matches similar warnings in ace (see gutter.js for example). --- lib/ace/document.js | 10 +++++----- lib/ace/edit_session.js | 2 +- lib/ace/editor.js | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/ace/document.js b/lib/ace/document.js index 07726de7..c689553c 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -288,23 +288,23 @@ var Document = function(textOrLines) { // Deprecated methods retained for backwards compatibility. this.insert = function(position, text){ - console.log('Warning: document.insert is deprecated. Use the insertText method instead.'); + console.warn('Use of document.insert is deprecated. Use the insertText method instead.'); return this.insertText(position, text); } this.insertLines = function(row, lines) { - console.log('Warning: document.insertLines is deprecated. Use the insertFullLines method instead.'); + console.warn('Use of document.insertLines is deprecated. Use the insertFullLines method instead.'); return this.insertFullLines(row, lines); } this.removeLines = function(firstRow, lastRow) { - console.log('Warning: document.removeLines is deprecated. Use the removeFullLines method instead.'); + console.warn('Use of document.removeLines is deprecated. Use the removeFullLines method instead.'); return this.removeFullLines(firstRow, lastRow); } this.insertNewLine = function(position) { - console.log('Warning: document.insertNewLine is deprecated. Use insertMergedLines(position, [\'\', \'\']) instead.'); + console.warn('Use of document.insertNewLine is deprecated. Use insertMergedLines(position, [\'\', \'\']) instead.'); return this.insertMergedLines(position, ['', '']); } this.insertInLine = function(position, text) { - console.log('Warning: document.insertInLine is deprecated. Use insertText instead.'); + console.warn('Use of document.insertInLine is deprecated. Use insertText instead.'); return this.insertText(position, text); } diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index e48bdc37..0845418b 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -1130,7 +1130,7 @@ var EditSession = function(text, mode) { // Deprecated method retained for backwards compatibility. this.insert = function(position, text){ - console.log('Warning: editsession.insert is deprecated. Use the insertText method instead.'); + console.warn('Use of editsession.insert is deprecated. Use the insertText method instead.'); return this.insertText(position, text) } diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 73873b4a..6f5980a1 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -832,7 +832,7 @@ var Editor = function(renderer, session) { // Deprecated method retained for backwards compatibility. this.insert = function(text){ - console.log('Warning: editor.insert is deprecated. Use the insertText method instead.'); + console.warn('Use of editor.insert is deprecated. Use the insertText method instead.'); return this.insertText(text) } From 612478e39f26e32c985ca8e66b8a4a37ca3834a5 Mon Sep 17 00:00:00 2001 From: aldendaniels Date: Wed, 1 Jan 2014 14:45:54 -0600 Subject: [PATCH 05/92] Speed up single-line deltas 2e6f12725bc01011c545c99c1f1c1da6065063d9 slowed down the application of deltas that only affect a single line. The slow-down, though trivial for a single line, is significant for operations than separately modify thousands of rows (such as indenting a large document). This commit speeds up single-line deltas by avoiding unnecessary calls to splitLine() and joinLineWithNext(). --- lib/ace/document.js | 70 ++++++++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 23 deletions(-) diff --git a/lib/ace/document.js b/lib/ace/document.js index c689553c..9d6aaac3 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -261,9 +261,13 @@ var Document = function(textOrLines) { throw errorText; } + // Validate action. + if (delta.action != 'insert' && delta.action != 'delete') + fnThrow('Delta action must be "insert" or "delete".'); + // Validate lines. if (!delta.lines instanceof Array) - fnThrow('Delta object lines must be an array'); + fnThrow('Delta lines must be an array'); // Validate range type. if (!delta.range instanceof Range) @@ -567,30 +571,50 @@ var Document = function(textOrLines) { this.$validateDelta(delta); // Apply delta. - switch (delta.action) + if (delta.range.start.row == delta.range.end.row) { - case 'insert': - splitLine(this.$lines, delta.range.start); - for (var i = 0; i < delta.lines.length; i++) { - var row = delta.range.start.row + 1 + i; - this.$lines.splice(row, 0, delta.lines[i]); - } - joinLineWithNext(this.$lines, delta.range.start.row); - joinLineWithNext(this.$lines, delta.range.end.row); - break; + // Apply single-line delta. + // Note: The multi-line code below correctly handle single-line + // deltas too, but we need to short-circuit for speed. + var row = delta.range.start.row; + var startColumn = delta.range.start.column; + var endColumn = delta.range.end.column; + var line = this.$lines[row]; + switch (delta.action) { + + case 'insert': + this.$lines[row] = line.substring(0, startColumn) + delta.lines[0] + line.substring(startColumn); + break; + + case 'delete': + this.$lines[row] = line.substring(0, startColumn) + line.substring(endColumn); + break; + } + } else { - case 'delete': - splitLine(this.$lines, delta.range.end); - splitLine(this.$lines, delta.range.start); - this.$lines.splice( - delta.range.start.row + 1, // Where to start deleting - delta.range.end.row - delta.range.start.row + 1 // Num lines to delete. - ); - joinLineWithNext(this.$lines, delta.range.start.row); - break; - - default: - throw 'Invalid delta type: ' + delta.action + // Apply multi-line delta. + switch (delta.action) { + + case 'insert': + splitLine(this.$lines, delta.range.start); + for (var i = 0; i < delta.lines.length; i++) { + var row = delta.range.start.row + 1 + i; + this.$lines.splice(row, 0, delta.lines[i]); + } + joinLineWithNext(this.$lines, delta.range.start.row); + joinLineWithNext(this.$lines, delta.range.end.row); + break; + + case 'delete': + splitLine(this.$lines, delta.range.end); + splitLine(this.$lines, delta.range.start); + this.$lines.splice( + delta.range.start.row + 1, // Where to start deleting + delta.range.end.row - delta.range.start.row + 1 // Num lines to delete. + ); + joinLineWithNext(this.$lines, delta.range.start.row); + break; + } } this._emit("change", { data: delta }); From b503e65e0370cb01a251a34719bde5ddb508e2c4 Mon Sep 17 00:00:00 2001 From: aldendaniels Date: Fri, 3 Jan 2014 14:59:22 -0600 Subject: [PATCH 06/92] Break on applyDelta into its own module This makes it possible to break out helper functions without exposing them to the rest of the document class. Also, long term, we may want to have a stand-alone test suite for applyDelta, so it makes sense in its own file. All other changes involve syntax corrections (some syntax issues were mine, others pre-existed) to make the documentation compilation work. --- lib/ace/apply_delta.js | 137 +++++++++++++++++++++++++++ lib/ace/document.js | 204 +++++++++++++--------------------------- lib/ace/edit_session.js | 14 +-- lib/ace/editor.js | 8 +- lib/ace/scrollbar.js | 8 +- 5 files changed, 215 insertions(+), 156 deletions(-) create mode 100644 lib/ace/apply_delta.js diff --git a/lib/ace/apply_delta.js b/lib/ace/apply_delta.js new file mode 100644 index 00000000..02c0a498 --- /dev/null +++ b/lib/ace/apply_delta.js @@ -0,0 +1,137 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { +"use strict"; + +var Range = require("./range").Range; + +function splitLine (lines, point) { + var text = lines[point.row]; + lines[point.row] = text.slice(0, point.column); + lines.splice(point.row + 1, 0, text.slice(point.column)); +} + +function joinLineWithNext(lines, row) { + lines[row] += lines[row + 1]; + lines.splice(row + 1, 1); +} + +function throwDeltaError(delta, errorText){ + errorText = 'Invalid Delta: ' + errorText; + console.log(errorText, delta); + throw errorText; +} + +function validateDelta(lines, delta) { + + // Validate action. + if (delta.action != 'insert' && delta.action != 'delete') + fnThrow('Delta action must be "insert" or "delete".'); + + // Validate lines. + if (!delta.lines instanceof Array) + fnThrow('Delta lines must be an array'); + + // Validate range type. + if (!delta.range instanceof Range) + fnThrow('Range object is not an instance of the Range class'); + + // Validate start point. + var start = delta.range.start; + if (Math.min(Math.max(start.row, 0), lines.length - 1 ) != start.row || + Math.min(Math.max(start.column, 0), lines[start.row].length) != start.column) + { + fnThrow('Range start point not contained in document'); + } + + // Validate ending row offset. + if (delta.lines.length - 1 != delta.range.end.row - delta.range.start.row) + fnThrow('Range row offsets does not match delta lines'); + + // TODO: + // - Validate that the ending column offset matches the lines. + // - Validate the deleted lines match the lines in the document. +} + + +exports.applyDelta = function(lines, delta) { + + // Validate delta. + validateDelta(lines, delta); + + // Apply delta. + if (delta.range.start.row == delta.range.end.row) + { + // Apply single-line delta. + // Note: The multi-line code below correctly handle single-line + // deltas too, but we need to short-circuit for speed. + var row = delta.range.start.row; + var startColumn = delta.range.start.column; + var endColumn = delta.range.end.column; + var line = lines[row]; + switch (delta.action) { + + case 'insert': + lines[row] = line.substring(0, startColumn) + delta.lines[0] + line.substring(startColumn); + break; + + case 'delete': + lines[row] = line.substring(0, startColumn) + line.substring(endColumn); + break; + } + } else { + + // Apply multi-line delta. + switch (delta.action) { + + case 'insert': + splitLine(lines, delta.range.start); + for (var i = 0; i < delta.lines.length; i++) { + var row = delta.range.start.row + 1 + i; + lines.splice(row, 0, delta.lines[i]); + } + joinLineWithNext(lines, delta.range.start.row); + joinLineWithNext(lines, delta.range.end.row); + break; + + case 'delete': + splitLine(lines, delta.range.end); + splitLine(lines, delta.range.start); + lines.splice( + delta.range.start.row + 1, // Where to start deleting + delta.range.end.row - delta.range.start.row + 1 // Num lines to delete. + ); + joinLineWithNext(lines, delta.range.start.row); + break; + } + } +} +}); diff --git a/lib/ace/document.js b/lib/ace/document.js index 9d6aaac3..768182a4 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -32,6 +32,7 @@ define(function(require, exports, module) { "use strict"; var oop = require("./lib/oop"); +var applyDelta = require("./apply_delta").applyDelta; var EventEmitter = require("./lib/event_emitter").EventEmitter; var Range = require("./range").Range; var Anchor = require("./anchor").Anchor; @@ -56,7 +57,7 @@ var Document = function(textOrLines) { // There has to be one line at least in the document. If you pass an empty // string to the insert function, nothing will happen. Workaround. - if (textOrLines.length == 0) { + if (textOrLines.length === 0) { this.$lines = [""]; } else if (Array.isArray(textOrLines)) { this.insertMergedLines({row: 0, column: 0}, textOrLines); @@ -107,10 +108,10 @@ var Document = function(textOrLines) { **/ // check for IE split bug - if ("aaa".split(/a/).length == 0) + if ("aaa".split(/a/).length === 0) this.$split = function(text) { return text.replace(/\r\n|\r/g, "\n").split("\n"); - } + }; else this.$split = function(text) { return text.split(/\r\n|\r|\n/); @@ -205,30 +206,41 @@ var Document = function(textOrLines) { }; /** - * [Given a range within the document, this function returns all the text within that range as a single string.]{: #Document.getTextRange.desc} - * @param {Range} range The range to work with + * Returns all the text within `range` as a single string. + * @param {Range} range The range to work with. * * @returns {String} **/ this.getTextRange = function(range) { - return this._getLinesForRange(range).join(this.getNewLineCharacter()); + return this.getLinesForRange(range).join(this.getNewLineCharacter()); }; - this._getLinesForRange = function(range) { + /** + * Returns all the text within `range` as an array of lines. + * @param {Range} range The range to work with. + * + * @returns {Array} + **/ + this.getLinesForRange = function(range) { + var lines; if (range.start.row == range.end.row) { - return [this.getLine(range.start.row) - .substring(range.start.column, range.end.column)]; + + // Handle a single-line range. + lines = [this.getLine(range.start.row).substring(range.start.column, range.end.column)]; + + } else { + + // Handle a multi-line range. + 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); } - 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; }; this.$clipPosition = function(position) { - var length = this.getLength(); if (position.row >= length) { position.row = Math.max(0, length - 1); @@ -240,8 +252,8 @@ var Document = function(textOrLines) { return position; }; - this.$getClippedRange = function(range) - { + this.$getClippedRange = function(range) { + // Get Range object. if (!range instanceof Range) range = Range.fromPoints(range.start, range.end); @@ -250,67 +262,29 @@ var Document = function(textOrLines) { this.$clipPosition(range.start); this.$clipPosition(range.end); return range; - } + }; - this.$validateDelta = function(delta) - { - function fnThrow(errorText) - { - errorText = 'Invalid Delta: ' + errorText; - console.log(errorText, delta); - throw errorText; - } - - // Validate action. - if (delta.action != 'insert' && delta.action != 'delete') - fnThrow('Delta action must be "insert" or "delete".'); - - // Validate lines. - if (!delta.lines instanceof Array) - fnThrow('Delta lines must be an array'); - - // Validate range type. - if (!delta.range instanceof Range) - fnThrow('Range object is not an instance of the Range class'); - - // Validate start point. - var start = delta.range.start; - if (Math.min(Math.max(start.row, 0), this.getLength() - 1 ) != start.row || - Math.min(Math.max(start.column, 0), this.$lines[start.row].length) != start.column) - { - fnThrow('Range start point not contained in document'); - } - - // Validate ending row offset. - if (delta.lines.length - 1 != delta.range.end.row - delta.range.start.row) - fnThrow('Range row offsets does not match delta lines'); - - // TODO: - // - Validate that the ending column offset matches the lines. - // - Validate the deleted lines match the lines in the document. - }, - // Deprecated methods retained for backwards compatibility. this.insert = function(position, text){ console.warn('Use of document.insert is deprecated. Use the insertText method instead.'); return this.insertText(position, text); - } + }; this.insertLines = function(row, lines) { console.warn('Use of document.insertLines is deprecated. Use the insertFullLines method instead.'); return this.insertFullLines(row, lines); - } + }; this.removeLines = function(firstRow, lastRow) { console.warn('Use of document.removeLines is deprecated. Use the removeFullLines method instead.'); return this.removeFullLines(firstRow, lastRow); - } + }; this.insertNewLine = function(position) { console.warn('Use of document.insertNewLine is deprecated. Use insertMergedLines(position, [\'\', \'\']) instead.'); return this.insertMergedLines(position, ['', '']); - } + }; this.insertInLine = function(position, text) { console.warn('Use of document.insertInLine is deprecated. Use insertText instead.'); return this.insertText(position, text); - } + }; /** * Inserts a block of `text` at the indicated `position`. @@ -374,12 +348,12 @@ var Document = function(textOrLines) { // Insert after the last row in the document. lines = [''].concat(lines); row--; - var column = this.$lines[row].length; + column = this.$lines[row].length; } // Insert. this.insertMergedLines({row: row, column: column}, lines); - }, + }; /** * Inserts the elements in `lines` into the document, starting at the position index given by `row`. This method also triggers the `'change'` event. @@ -412,7 +386,7 @@ var Document = function(textOrLines) { }); return endPoint; - } + }; /** * Removes the `range` from the document. @@ -427,7 +401,7 @@ var Document = function(textOrLines) { this.applyDelta({ action: 'delete', range: range, - lines: this._getLinesForRange(range), + lines: this.getLinesForRange(range), }); return range.start; }; @@ -450,7 +424,7 @@ var Document = function(textOrLines) { this.applyDelta({ action: "delete", range: range, - lines: this._getLinesForRange(range) + lines: this.getLinesForRange(range) }); return range.start; @@ -487,7 +461,7 @@ var Document = function(textOrLines) { this.applyDelta({ action: "delete", range: range, - lines: this._getLinesForRange(range) + lines: this.getLinesForRange(range) }); // Return the deleted lines. @@ -524,7 +498,7 @@ var Document = function(textOrLines) { this.replace = function(range, text) { if (!range instanceof Range) range = Range.fromPoints(range.start, range.end); - if (text.length == 0 && range.isEmpty()) + if (text.length === 0 && range.isEmpty()) return range.start; // Shortcut: If the text we want to insert is the same as it is already @@ -533,8 +507,9 @@ var Document = function(textOrLines) { return range.end; this.remove(range); + var end; if (text) { - var end = this.insertText(range.start, text); + end = this.insertText(range.start, text); } else { end = range.start; @@ -544,7 +519,8 @@ var Document = function(textOrLines) { }; /** - * Applies all the changes previously accumulated. These can be either `'insert'` or `'delete'`. + * Applies all changes in `deltas` to the document. + * @param {Array} deltas An array of delta objects (can include 'insert' and 'delete' actions) **/ this.applyDeltas = function(deltas) { for (var i=0; i=0; i--) { @@ -629,6 +538,19 @@ var Document = function(textOrLines) { } }; + /** + * Applies `delta` to the document. + * @param {Object} delta A delta object (can include 'insert' and 'delete' actions) + **/ + this.applyDelta = function(delta) { + applyDelta(this.$lines, delta); + this._emit("change", { data: delta }); + }; + + /** + * Reverts `delta` from the document. + * @param {Object} delta A delta object (can include 'insert' and 'delete' actions) + **/ this.revertDelta = function(delta) { this.applyDelta({ @@ -636,8 +558,8 @@ var Document = function(textOrLines) { range: delta.range.clone(), lines: delta.lines.slice() }); - }, - + }; + /** * Converts an index position in a document to a `{row, column}` object. * diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 0845418b..0d4df080 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -1131,8 +1131,8 @@ var EditSession = function(text, mode) { // Deprecated method retained for backwards compatibility. this.insert = function(position, text){ console.warn('Use of editsession.insert is deprecated. Use the insertText method instead.'); - return this.insertText(position, text) - } + return this.insertText(position, text); + }; /** * Inserts a block of `text` and the indicated `position`. @@ -1168,8 +1168,8 @@ var EditSession = function(text, mode) { * **/ this.removeFullLines = function(firstRow, lastRow){ - return this.doc.removeFullLines(firstRow, lastRow) - } + return this.doc.removeFullLines(firstRow, lastRow); + }; /** * Reverts previous changes to your document. @@ -1619,7 +1619,7 @@ var EditSession = function(text, mode) { * @private **/ this.adjustWrapLimit = function(desiredLimit, $printMargin) { - var limits = this.$wrapLimitRange + var limits = this.$wrapLimitRange; if (limits.max < 0) limits = {min: $printMargin, max: $printMargin}; var wrapLimit = this.$constrainWrapLimit(desiredLimit, limits.min, limits.max); @@ -1738,7 +1738,7 @@ var EditSession = function(text, mode) { var foldLine = this.getFoldLine(firstRow); var idx = 0; if (foldLine) { - var cmp = foldLine.range.compareInside(start.row, start.column) + var cmp = foldLine.range.compareInside(start.row, start.column); // Inside of the foldLine range. Need to split stuff up. if (cmp == 0) { foldLine = foldLine.split(start.row, start.column); @@ -2215,7 +2215,7 @@ var EditSession = function(text, mode) { return { row: maxRow, column: this.getLine(maxRow).length - } + }; } else { line = this.getLine(docRow); foldLine = null; diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 6f5980a1..79f59e68 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -833,8 +833,8 @@ var Editor = function(renderer, session) { // Deprecated method retained for backwards compatibility. this.insert = function(text){ console.warn('Use of editor.insert is deprecated. Use the insertText method instead.'); - return this.insertText(text) - } + return this.insertText(text); + }; /** * Inserts `text` into wherever the cursor is pointing. @@ -876,7 +876,7 @@ var Editor = function(renderer, session) { } if (text == "\n" || text == "\r\n") { - var line = session.getLine(cursor.row) + var line = session.getLine(cursor.row); if (cursor.column > line.search(/\S|$/)) { var d = line.substr(cursor.column).search(/\S|$/); session.doc.removeInLine(cursor.row, cursor.column, cursor.column + d); @@ -1362,7 +1362,7 @@ var Editor = function(renderer, session) { } } - var line = session.getLine(range.start.row) + var line = session.getLine(range.start.row); var position = range.start; var size = session.getTabSize(); var column = session.documentToScreenColumn(position.row, position.column); diff --git a/lib/ace/scrollbar.js b/lib/ace/scrollbar.js index 28f41fe4..466d1af1 100644 --- a/lib/ace/scrollbar.js +++ b/lib/ace/scrollbar.js @@ -156,9 +156,9 @@ oop.inherits(VScrollBar, ScrollBar); * Sets the scroll top of the scroll bar. * @param {Number} scrollTop The new scroll top **/ - // on chrome 17+ for small zoom levels after calling this function - // this.element.scrollTop != scrollTop which makes page to scroll up. this.setScrollTop = function(scrollTop) { + // on chrome 17+ for small zoom levels after calling this function + // this.element.scrollTop != scrollTop which makes page to scroll up. if (this.scrollTop != scrollTop) { this.skipEvent = true; this.scrollTop = this.element.scrollTop = scrollTop; @@ -249,9 +249,9 @@ oop.inherits(HScrollBar, ScrollBar); * Sets the scroll left of the scroll bar. * @param {Number} scrollTop The new scroll left **/ - // on chrome 17+ for small zoom levels after calling this function - // this.element.scrollTop != scrollTop which makes page to scroll up. this.setScrollLeft = function(scrollLeft) { + // on chrome 17+ for small zoom levels after calling this function + // this.element.scrollTop != scrollTop which makes page to scroll up. if (this.scrollLeft != scrollLeft) { this.skipEvent = true; this.scrollLeft = this.element.scrollLeft = scrollLeft; From 8624ab8dcb9bac917488feb8856e8b709418adb7 Mon Sep 17 00:00:00 2001 From: aldendaniels Date: Sat, 4 Jan 2014 01:59:30 -0600 Subject: [PATCH 07/92] Stop using splice.appy() in ace Since .apply() can't handle more than 65535 parameters, splice.apply() is brittle. It's also hard to read. This replaces splice.apply() calls throughout ace code with lang.spliceIntoArray(). --- lib/ace/background_tokenizer.js | 14 ++++++++------ lib/ace/edit_session.js | 14 +++++++------- lib/ace/edit_session/folding.js | 6 +++--- lib/ace/layer/gutter.js | 5 ++--- lib/ace/lib/lang.js | 14 ++++++++++++++ lib/ace/line_widgets.js | 5 ++--- lib/ace/mode/asciidoc_highlight_rules.js | 8 +++++--- lib/ace/mode/text_highlight_rules.js | 6 +++--- lib/ace/snippets.js | 18 +++++++++--------- 9 files changed, 53 insertions(+), 37 deletions(-) diff --git a/lib/ace/background_tokenizer.js b/lib/ace/background_tokenizer.js index cb3ffe23..4b82f19a 100644 --- a/lib/ace/background_tokenizer.js +++ b/lib/ace/background_tokenizer.js @@ -32,6 +32,7 @@ define(function(require, exports, module) { "use strict"; var oop = require("./lib/oop"); +var lang = require("./lib/lang"); var EventEmitter = require("./lib/event_emitter").EventEmitter; @@ -187,14 +188,15 @@ var BackgroundTokenizer = function(tokenizer, editor) { this.lines.splice(startRow, len + 1, null); this.states.splice(startRow, len + 1, null); } else { - var args = Array(len + 1); - args.unshift(startRow, 1); - this.lines.splice.apply(this.lines, args); - this.states.splice.apply(this.states, args); + this.lines.splice( startRow, 1); + this.states.splice(startRow, 1); + var toInsert = Array(len + 1); + lang.spliceIntoArray(this.lines, startRow, toInsert); + lang.spliceIntoArray(this.states, startRow, toInsert); } - + this.currentLine = Math.min(startRow, this.currentLine, this.doc.getLength()); - + this.stop(); }; diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 0d4df080..cd14c1a5 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -1723,15 +1723,15 @@ var EditSession = function(text, mode) { } else { var args; if (useWrapMode) { - args = [firstRow, 0]; - for (var i = 0; i < len; i++) args.push([]); - this.$wrapData.splice.apply(this.$wrapData, args); + var toInsert = []; + for (var i = 0; i < len; i++) + toInsert.push([]); + lang.spliceIntoArray(this.$wrapData, firstRow, toInsert); } else { - args = Array(len); - args.unshift(firstRow, 0); - this.$rowLengthCache.splice.apply(this.$rowLengthCache, args); + var toInsert = Array(len); + lang.spliceIntoArray(this.$rowLengthCache, firstRow, toInsert); } - + // If some new line is added inside of a foldLine, then split // the fold line up. var foldLines = this.$foldData; diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js index 9df35593..1563bd67 100644 --- a/lib/ace/edit_session/folding.js +++ b/lib/ace/edit_session/folding.js @@ -31,6 +31,7 @@ define(function(require, exports, module) { "use strict"; +var lang = require("../lib/lang"); var Range = require("../range").Range; var FoldLine = require("./fold_line").FoldLine; var Fold = require("./fold").Fold; @@ -836,9 +837,8 @@ function Folding() { } else if (delta.action == "delete") { this.foldWidgets.splice(firstRow, len + 1, null); } else { - var args = Array(len + 1); - args.unshift(firstRow, 1); - this.foldWidgets.splice.apply(this.foldWidgets, args); + this.foldWidgets.splice(firstRow, 1); + lang.spliceIntoArray(this.foldWidgets, firstRow, Array(len + 1)); } }; diff --git a/lib/ace/layer/gutter.js b/lib/ace/layer/gutter.js index 82dcf245..24f9c430 100644 --- a/lib/ace/layer/gutter.js +++ b/lib/ace/layer/gutter.js @@ -111,9 +111,8 @@ var Gutter = function(parentEl) { } else if (delta.action == "delete") { this.$annotations.splice(firstRow, len + 1, null); } else { - var args = new Array(len + 1); - args.unshift(firstRow, 1); - this.$annotations.splice.apply(this.$annotations, args); + this.$annotations.splice(firstRow, 1); + lang.spliceIntoArray(this.$annotations, firstRow, new Array(len + 1)); } }; diff --git a/lib/ace/lib/lang.js b/lib/ace/lib/lang.js index 4405b5c2..e0e9d826 100644 --- a/lib/ace/lib/lang.js +++ b/lib/ace/lib/lang.js @@ -123,6 +123,20 @@ exports.arrayRemove = function(array, value) { } }; +/* + * Splice the items in `a2` into `a1` at position `offset`. + */ +exports.spliceIntoArray = function(a1, offset, a2) { + + // Handle negative offsets. + if (offset < 0) + offset = Math.max(a1.length + offset, 0); + + // Splice. + for (var i = 0; i < a2.length; i++) + a1.splice(i + offset, 0, a2[i]); +} + exports.escapeRegExp = function(str) { return str.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1'); }; diff --git a/lib/ace/line_widgets.js b/lib/ace/line_widgets.js index 4023bd55..7cbe9809 100644 --- a/lib/ace/line_widgets.js +++ b/lib/ace/line_widgets.js @@ -32,6 +32,7 @@ define(function(require, exports, module) { "use strict"; var oop = require("./lib/oop"); +var lang = require("./lib/lang"); var dom = require("./lib/dom"); var Range = require("./range").Range; @@ -130,9 +131,7 @@ function LineWidgets(session) { }, this); this.$updateRows(); } else { - var args = Array(len); - args.unshift(startRow, 0); - cells.splice.apply(cells, args); + lang.spliceIntoArray(cells, startRow, new Array(len)); this.$updateRows(); } }; diff --git a/lib/ace/mode/asciidoc_highlight_rules.js b/lib/ace/mode/asciidoc_highlight_rules.js index c0d1a305..308fc8e4 100644 --- a/lib/ace/mode/asciidoc_highlight_rules.js +++ b/lib/ace/mode/asciidoc_highlight_rules.js @@ -32,6 +32,7 @@ define(function(require, exports, module) { "use strict"; var oop = require("../lib/oop"); +var lang = require("../lib/lang"); var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; var AsciidocHighlightRules = function() { @@ -215,13 +216,14 @@ var AsciidocHighlightRules = function() { for (var i = stateRules.length; i--; ) { var rule = stateRules[i]; if (rule.include || typeof rule == "string") { - var args = [i, 1].concat(this.$rules[rule.include || rule]); + var toInsert = this.$rules[rule.include || rule]; if (rule.noEscape) { - args = args.filter(function(x) { + toInsert = toInsert.filter(function(x) { return !x.next; }); } - stateRules.splice.apply(stateRules, args); + stateRules.splice(i, 1); + lang.spliceIntoArray(stateRules, i, toInsert); } else if (rule.token in tokenMap) { rule.token = tokenMap[rule.token]; } diff --git a/lib/ace/mode/text_highlight_rules.js b/lib/ace/mode/text_highlight_rules.js index 48e016b0..9e304a53 100644 --- a/lib/ace/mode/text_highlight_rules.js +++ b/lib/ace/mode/text_highlight_rules.js @@ -181,10 +181,10 @@ var TextHighlightRules = function() { toInsert = rule; if (toInsert) { - var args = [i, 1].concat(toInsert); if (rule.noEscape) - args = args.filter(function(x) {return !x.next;}); - state.splice.apply(state, args); + toInsert = toInsert.filter(function(x) {return !x.next;}); + state.splice(i, 1); + lang.spliceIntoArray(state, i, toInsert); // skip included rules since they are already processed //i += args.length - 3; i--; diff --git a/lib/ace/snippets.js b/lib/ace/snippets.js index ee1a5498..e5d585bb 100644 --- a/lib/ace/snippets.js +++ b/lib/ace/snippets.js @@ -335,12 +335,12 @@ var SnippetManager = function() { } var ts = tabstops[id]; - var arg = typeof ts.value == "string" ? [ts.value] : copyValue(ts.value); - arg.unshift(i + 1, Math.max(0, i1 - i)); - arg.push(p); + var toInsert = typeof ts.value == "string" ? [ts.value] : copyValue(ts.value); + toInsert.push(p); expanding[id] = p; - tokens.splice.apply(tokens, arg); - + tokens.splice(i + 1, Math.max(0, i1 - i)); + lang.spliceIntoArray(tokens, i + 1, toInsert); + if (ts.indexOf(p) === -1) ts.push(p); }; @@ -755,7 +755,7 @@ var TabstopManager = function(editor) { } var i = this.index; - var arg = [i, 0]; + var toInsert = []; var ranges = this.ranges; var editor = this.editor; tabstops.forEach(function(ts) { @@ -776,12 +776,12 @@ var TabstopManager = function(editor) { } if (!ts.firstNonLinked) ts.hasLinkedRanges = false; - arg.push(ts); + toInsert.push(ts); this.addTabstopMarkers(ts); }, this); // tabstop 0 is the last one - arg.push(arg.splice(2, 1)[0]); - this.tabstops.splice.apply(this.tabstops, arg); + toInsert.push(toInsert.splice(0, 1)[0]); + lang.spliceIntoArray(this.tabstops, this.index, toInsert); }; this.addTabstopMarkers = function(ts) { From 91df7cd6635110dca0a97e7419150739f8d9ffd6 Mon Sep 17 00:00:00 2001 From: aldendaniels Date: Wed, 8 Jan 2014 21:39:30 -0600 Subject: [PATCH 08/92] Revert "Stop using splice.appy() in ace" This reverts commit 8624ab8dcb9bac917488feb8856e8b709418adb7. --- lib/ace/background_tokenizer.js | 14 ++++++-------- lib/ace/edit_session.js | 14 +++++++------- lib/ace/edit_session/folding.js | 6 +++--- lib/ace/layer/gutter.js | 5 +++-- lib/ace/lib/lang.js | 14 -------------- lib/ace/line_widgets.js | 5 +++-- lib/ace/mode/asciidoc_highlight_rules.js | 8 +++----- lib/ace/mode/text_highlight_rules.js | 6 +++--- lib/ace/snippets.js | 18 +++++++++--------- 9 files changed, 37 insertions(+), 53 deletions(-) diff --git a/lib/ace/background_tokenizer.js b/lib/ace/background_tokenizer.js index 4b82f19a..cb3ffe23 100644 --- a/lib/ace/background_tokenizer.js +++ b/lib/ace/background_tokenizer.js @@ -32,7 +32,6 @@ define(function(require, exports, module) { "use strict"; var oop = require("./lib/oop"); -var lang = require("./lib/lang"); var EventEmitter = require("./lib/event_emitter").EventEmitter; @@ -188,15 +187,14 @@ var BackgroundTokenizer = function(tokenizer, editor) { this.lines.splice(startRow, len + 1, null); this.states.splice(startRow, len + 1, null); } else { - this.lines.splice( startRow, 1); - this.states.splice(startRow, 1); - var toInsert = Array(len + 1); - lang.spliceIntoArray(this.lines, startRow, toInsert); - lang.spliceIntoArray(this.states, startRow, toInsert); + var args = Array(len + 1); + args.unshift(startRow, 1); + this.lines.splice.apply(this.lines, args); + this.states.splice.apply(this.states, args); } - + this.currentLine = Math.min(startRow, this.currentLine, this.doc.getLength()); - + this.stop(); }; diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index cd14c1a5..0d4df080 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -1723,15 +1723,15 @@ var EditSession = function(text, mode) { } else { var args; if (useWrapMode) { - var toInsert = []; - for (var i = 0; i < len; i++) - toInsert.push([]); - lang.spliceIntoArray(this.$wrapData, firstRow, toInsert); + args = [firstRow, 0]; + for (var i = 0; i < len; i++) args.push([]); + this.$wrapData.splice.apply(this.$wrapData, args); } else { - var toInsert = Array(len); - lang.spliceIntoArray(this.$rowLengthCache, firstRow, toInsert); + args = Array(len); + args.unshift(firstRow, 0); + this.$rowLengthCache.splice.apply(this.$rowLengthCache, args); } - + // If some new line is added inside of a foldLine, then split // the fold line up. var foldLines = this.$foldData; diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js index 1563bd67..9df35593 100644 --- a/lib/ace/edit_session/folding.js +++ b/lib/ace/edit_session/folding.js @@ -31,7 +31,6 @@ define(function(require, exports, module) { "use strict"; -var lang = require("../lib/lang"); var Range = require("../range").Range; var FoldLine = require("./fold_line").FoldLine; var Fold = require("./fold").Fold; @@ -837,8 +836,9 @@ function Folding() { } else if (delta.action == "delete") { this.foldWidgets.splice(firstRow, len + 1, null); } else { - this.foldWidgets.splice(firstRow, 1); - lang.spliceIntoArray(this.foldWidgets, firstRow, Array(len + 1)); + var args = Array(len + 1); + args.unshift(firstRow, 1); + this.foldWidgets.splice.apply(this.foldWidgets, args); } }; diff --git a/lib/ace/layer/gutter.js b/lib/ace/layer/gutter.js index 24f9c430..82dcf245 100644 --- a/lib/ace/layer/gutter.js +++ b/lib/ace/layer/gutter.js @@ -111,8 +111,9 @@ var Gutter = function(parentEl) { } else if (delta.action == "delete") { this.$annotations.splice(firstRow, len + 1, null); } else { - this.$annotations.splice(firstRow, 1); - lang.spliceIntoArray(this.$annotations, firstRow, new Array(len + 1)); + var args = new Array(len + 1); + args.unshift(firstRow, 1); + this.$annotations.splice.apply(this.$annotations, args); } }; diff --git a/lib/ace/lib/lang.js b/lib/ace/lib/lang.js index e0e9d826..4405b5c2 100644 --- a/lib/ace/lib/lang.js +++ b/lib/ace/lib/lang.js @@ -123,20 +123,6 @@ exports.arrayRemove = function(array, value) { } }; -/* - * Splice the items in `a2` into `a1` at position `offset`. - */ -exports.spliceIntoArray = function(a1, offset, a2) { - - // Handle negative offsets. - if (offset < 0) - offset = Math.max(a1.length + offset, 0); - - // Splice. - for (var i = 0; i < a2.length; i++) - a1.splice(i + offset, 0, a2[i]); -} - exports.escapeRegExp = function(str) { return str.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1'); }; diff --git a/lib/ace/line_widgets.js b/lib/ace/line_widgets.js index 7cbe9809..4023bd55 100644 --- a/lib/ace/line_widgets.js +++ b/lib/ace/line_widgets.js @@ -32,7 +32,6 @@ define(function(require, exports, module) { "use strict"; var oop = require("./lib/oop"); -var lang = require("./lib/lang"); var dom = require("./lib/dom"); var Range = require("./range").Range; @@ -131,7 +130,9 @@ function LineWidgets(session) { }, this); this.$updateRows(); } else { - lang.spliceIntoArray(cells, startRow, new Array(len)); + var args = Array(len); + args.unshift(startRow, 0); + cells.splice.apply(cells, args); this.$updateRows(); } }; diff --git a/lib/ace/mode/asciidoc_highlight_rules.js b/lib/ace/mode/asciidoc_highlight_rules.js index 308fc8e4..c0d1a305 100644 --- a/lib/ace/mode/asciidoc_highlight_rules.js +++ b/lib/ace/mode/asciidoc_highlight_rules.js @@ -32,7 +32,6 @@ define(function(require, exports, module) { "use strict"; var oop = require("../lib/oop"); -var lang = require("../lib/lang"); var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; var AsciidocHighlightRules = function() { @@ -216,14 +215,13 @@ var AsciidocHighlightRules = function() { for (var i = stateRules.length; i--; ) { var rule = stateRules[i]; if (rule.include || typeof rule == "string") { - var toInsert = this.$rules[rule.include || rule]; + var args = [i, 1].concat(this.$rules[rule.include || rule]); if (rule.noEscape) { - toInsert = toInsert.filter(function(x) { + args = args.filter(function(x) { return !x.next; }); } - stateRules.splice(i, 1); - lang.spliceIntoArray(stateRules, i, toInsert); + stateRules.splice.apply(stateRules, args); } else if (rule.token in tokenMap) { rule.token = tokenMap[rule.token]; } diff --git a/lib/ace/mode/text_highlight_rules.js b/lib/ace/mode/text_highlight_rules.js index 9e304a53..48e016b0 100644 --- a/lib/ace/mode/text_highlight_rules.js +++ b/lib/ace/mode/text_highlight_rules.js @@ -181,10 +181,10 @@ var TextHighlightRules = function() { toInsert = rule; if (toInsert) { + var args = [i, 1].concat(toInsert); if (rule.noEscape) - toInsert = toInsert.filter(function(x) {return !x.next;}); - state.splice(i, 1); - lang.spliceIntoArray(state, i, toInsert); + args = args.filter(function(x) {return !x.next;}); + state.splice.apply(state, args); // skip included rules since they are already processed //i += args.length - 3; i--; diff --git a/lib/ace/snippets.js b/lib/ace/snippets.js index e5d585bb..ee1a5498 100644 --- a/lib/ace/snippets.js +++ b/lib/ace/snippets.js @@ -335,12 +335,12 @@ var SnippetManager = function() { } var ts = tabstops[id]; - var toInsert = typeof ts.value == "string" ? [ts.value] : copyValue(ts.value); - toInsert.push(p); + var arg = typeof ts.value == "string" ? [ts.value] : copyValue(ts.value); + arg.unshift(i + 1, Math.max(0, i1 - i)); + arg.push(p); expanding[id] = p; - tokens.splice(i + 1, Math.max(0, i1 - i)); - lang.spliceIntoArray(tokens, i + 1, toInsert); - + tokens.splice.apply(tokens, arg); + if (ts.indexOf(p) === -1) ts.push(p); }; @@ -755,7 +755,7 @@ var TabstopManager = function(editor) { } var i = this.index; - var toInsert = []; + var arg = [i, 0]; var ranges = this.ranges; var editor = this.editor; tabstops.forEach(function(ts) { @@ -776,12 +776,12 @@ var TabstopManager = function(editor) { } if (!ts.firstNonLinked) ts.hasLinkedRanges = false; - toInsert.push(ts); + arg.push(ts); this.addTabstopMarkers(ts); }, this); // tabstop 0 is the last one - toInsert.push(toInsert.splice(0, 1)[0]); - lang.spliceIntoArray(this.tabstops, this.index, toInsert); + arg.push(arg.splice(2, 1)[0]); + this.tabstops.splice.apply(this.tabstops, arg); }; this.addTabstopMarkers = function(ts) { From 6fe381f6335790f7a0ee6646cdcb9ab442893df5 Mon Sep 17 00:00:00 2001 From: aldendaniels Date: Wed, 8 Jan 2014 22:00:11 -0600 Subject: [PATCH 09/92] Rename insertText back to insert --- lib/ace/anchor_test.js | 2 +- lib/ace/autocomplete.js | 2 +- lib/ace/background_tokenizer_test.js | 2 +- lib/ace/commands/default_commands.js | 4 ++-- lib/ace/document.js | 16 ++++++---------- lib/ace/document_test.js | 8 ++++---- lib/ace/edit_session.js | 14 ++++---------- lib/ace/edit_session_test.js | 4 ++-- lib/ace/editor.js | 20 +++++++------------- lib/ace/editor_change_document_test.js | 2 +- lib/ace/editor_navigation_test.js | 2 +- lib/ace/ext/elastic_tabstops_lite.js | 2 +- lib/ace/ext/whitespace.js | 2 +- lib/ace/keyboard/vim/commands.js | 10 +++++----- lib/ace/keyboard/vim/maps/motions.js | 6 +++--- lib/ace/mode/javascript_test.js | 2 +- lib/ace/mode/text.js | 12 ++++++------ lib/ace/mouse/dragdrop_handler.js | 2 +- lib/ace/multi_select.js | 8 ++++---- lib/ace/occur_test.js | 12 ++++++------ lib/ace/placeholder_test.js | 18 +++++++++--------- 21 files changed, 67 insertions(+), 83 deletions(-) diff --git a/lib/ace/anchor_test.js b/lib/ace/anchor_test.js index 0661765b..d40d459b 100644 --- a/lib/ace/anchor_test.js +++ b/lib/ace/anchor_test.js @@ -54,7 +54,7 @@ module.exports = { var doc = new Document("juhu\nkinners"); var anchor = new Anchor(doc, 1, 4); - doc.insertText({row: 1, column: 1}, "123"); + doc.insert({row: 1, column: 1}, "123"); assert.position(anchor.getPosition(), 1, 7); }, diff --git a/lib/ace/autocomplete.js b/lib/ace/autocomplete.js index 2344aede..50154c01 100644 --- a/lib/ace/autocomplete.js +++ b/lib/ace/autocomplete.js @@ -170,7 +170,7 @@ var Autocomplete = function() { "Ctrl-Down|Ctrl-End": function(editor) { editor.completer.goTo("end"); }, "Esc": function(editor) { editor.completer.detach(); }, - "Space": function(editor) { editor.completer.detach(); editor.insertText(" ");}, + "Space": function(editor) { editor.completer.detach(); editor.insert(" ");}, "Return": function(editor) { editor.completer.insertMatch(); }, "Shift-Return": function(editor) { editor.completer.insertMatch(true); }, "Tab": function(editor) { editor.completer.insertMatch(); }, diff --git a/lib/ace/background_tokenizer_test.js b/lib/ace/background_tokenizer_test.js index 16c63132..7a4cc78c 100644 --- a/lib/ace/background_tokenizer_test.js +++ b/lib/ace/background_tokenizer_test.js @@ -70,7 +70,7 @@ module.exports = { forceTokenize(doc) testStates(doc, ["comment_regex_allowed", "comment_regex_allowed"]) - doc.insertText({row:0, column:2}, "\n*/") + doc.insert({row:0, column:2}, "\n*/") testStates(doc, [undefined, undefined, "comment_regex_allowed"]) forceTokenize(doc) diff --git a/lib/ace/commands/default_commands.js b/lib/ace/commands/default_commands.js index 0fd9686c..e4a8212e 100644 --- a/lib/ace/commands/default_commands.js +++ b/lib/ace/commands/default_commands.js @@ -508,13 +508,13 @@ exports.commands = [{ scrollIntoView: "selectionPart" }, { name: "insertstring", - exec: function(editor, str) { editor.insertText(str); }, + exec: function(editor, str) { editor.insert(str); }, multiSelectAction: "forEach", scrollIntoView: "cursor" }, { name: "inserttext", exec: function(editor, args) { - editor.insertText(lang.stringRepeat(args.text || "", args.times || 1)); + editor.insert(lang.stringRepeat(args.text || "", args.times || 1)); }, multiSelectAction: "forEach" }, { diff --git a/lib/ace/document.js b/lib/ace/document.js index 768182a4..a84d3bcc 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -62,7 +62,7 @@ var Document = function(textOrLines) { } else if (Array.isArray(textOrLines)) { this.insertMergedLines({row: 0, column: 0}, textOrLines); } else { - this.insertText({row: 0, column:0}, textOrLines); + this.insert({row: 0, column:0}, textOrLines); } }; @@ -78,7 +78,7 @@ var Document = function(textOrLines) { this.setValue = function(text) { var len = this.getLength(); this.remove(new Range(0, 0, len, this.getLine(len-1).length)); - this.insertText({row: 0, column:0}, text); + this.insert({row: 0, column:0}, text); }; /** @@ -265,10 +265,6 @@ var Document = function(textOrLines) { }; // Deprecated methods retained for backwards compatibility. - this.insert = function(position, text){ - console.warn('Use of document.insert is deprecated. Use the insertText method instead.'); - return this.insertText(position, text); - }; this.insertLines = function(row, lines) { console.warn('Use of document.insertLines is deprecated. Use the insertFullLines method instead.'); return this.insertFullLines(row, lines); @@ -282,8 +278,8 @@ var Document = function(textOrLines) { return this.insertMergedLines(position, ['', '']); }; this.insertInLine = function(position, text) { - console.warn('Use of document.insertInLine is deprecated. Use insertText instead.'); - return this.insertText(position, text); + console.warn('Use of document.insertInLine is deprecated. Use insert instead.'); + return this.insert(position, text); }; /** @@ -293,7 +289,7 @@ var Document = function(textOrLines) { * @returns {Object} The position ({row, column}) of the last line of `text`. If the length of `text` is 0, this function simply returns `position`. * **/ - this.insertText = function(position, text) { + this.insert = function(position, text) { // Only detect new lines if the document has no line break yet. if (this.getLength() <= 1) @@ -509,7 +505,7 @@ var Document = function(textOrLines) { this.remove(range); var end; if (text) { - end = this.insertText(range.start, text); + end = this.insert(range.start, text); } else { end = range.start; diff --git a/lib/ace/document_test.js b/lib/ace/document_test.js index c29d6133..a91fccf2 100644 --- a/lib/ace/document_test.js +++ b/lib/ace/document_test.js @@ -48,7 +48,7 @@ module.exports = { var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); - doc.insertText({row: 0, column: 1}, "juhu"); + doc.insert({row: 0, column: 1}, "juhu"); assert.equal(doc.getValue(), ["1juhu2", "34"].join("\n")); var d = deltas.concat(); @@ -126,7 +126,7 @@ module.exports = { var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); - doc.insertText({row: 0, column: 0}, "aa\nbb\ncc"); + doc.insert({row: 0, column: 0}, "aa\nbb\ncc"); assert.equal(doc.getValue(), ["aa", "bb", "cc12", "34"].join("\n")); var d = deltas.concat(); @@ -143,7 +143,7 @@ module.exports = { var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); - doc.insertText({row: 1, column: 2}, "aa\nbb\ncc"); + doc.insert({row: 1, column: 2}, "aa\nbb\ncc"); assert.equal(doc.getValue(), ["12", "34aa", "bb", "cc"].join("\n")); var d = deltas.concat(); @@ -160,7 +160,7 @@ module.exports = { var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); - doc.insertText({row: 0, column: 1}, "aa\nbb\ncc"); + doc.insert({row: 0, column: 1}, "aa\nbb\ncc"); assert.equal(doc.getValue(), ["1aa", "bb", "cc2", "34"].join("\n")); var d = deltas.concat(); diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 0d4df080..f140bdac 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -1127,12 +1127,6 @@ var EditSession = function(text, mode) { this.getTextRange = function(range) { return this.doc.getTextRange(range || this.selection.getRange()); }; - - // Deprecated method retained for backwards compatibility. - this.insert = function(position, text){ - console.warn('Use of editsession.insert is deprecated. Use the insertText method instead.'); - return this.insertText(position, text); - }; /** * Inserts a block of `text` and the indicated `position`. @@ -1142,8 +1136,8 @@ var EditSession = function(text, mode) { * * **/ - this.insertText = function(position, text) { - return this.doc.insertText(position, text); + this.insert = function(position, text) { + return this.doc.insert(position, text); }; /** @@ -1356,7 +1350,7 @@ var EditSession = function(text, mode) { } } - toRange.end = this.insertText(toRange.start, text); + toRange.end = this.insert(toRange.start, text); if (folds.length) { var oldStart = fromRange.start; var newStart = toRange.start; @@ -1390,7 +1384,7 @@ var EditSession = function(text, mode) { this.indentRows = function(startRow, endRow, indentString) { indentString = indentString.replace(/\t/g, this.getTabString()); for (var row=startRow; row<=endRow; row++) - this.insertText({row: row, column:0}, indentString); + this.insert({row: row, column:0}, indentString); }; /** diff --git a/lib/ace/edit_session_test.js b/lib/ace/edit_session_test.js index fd20259c..75abf468 100644 --- a/lib/ace/edit_session_test.js +++ b/lib/ace/edit_session_test.js @@ -649,7 +649,7 @@ module.exports = { var foldLines = session.$foldData; function insert(row, column, text) { - session.insertText({row: row, column: column}, text); + session.insert({row: row, column: column}, text); // Force the session to store all changes made to the document NOW // on the undoManager's queue. Otherwise we can't undo in separate @@ -748,7 +748,7 @@ module.exports = { undoManager = session.getUndoManager(), foldLines = session.$foldData; function insert(row, column, text) { - session.insertText({row: row, column: column}, text); + session.insert({row: row, column: column}, text); // Force the session to store all changes made to the document NOW // on the undoManager's queue. Otherwise we can't undo in separate // steps later. diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 79f59e68..af991ef1 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -822,19 +822,13 @@ var Editor = function(renderer, session) { if (this.$readOnly) return; this._emit("paste", text); - this.insertText(text); + this.insert(text); }; this.execCommand = function(command, args) { this.commands.exec(command, this, args); }; - - // Deprecated method retained for backwards compatibility. - this.insert = function(text){ - console.warn('Use of editor.insert is deprecated. Use the insertText method instead.'); - return this.insertText(text); - }; /** * Inserts `text` into wherever the cursor is pointing. @@ -842,7 +836,7 @@ var Editor = function(renderer, session) { * * **/ - this.insertText = function(text) { + this.insert = function(text) { var session = this.session; var mode = session.getMode(); var cursor = this.getCursorPosition(); @@ -888,7 +882,7 @@ var Editor = function(renderer, session) { var lineState = session.getState(cursor.row); var line = session.getLine(cursor.row); var shouldOutdent = mode.checkOutdent(lineState, line, text); - var end = session.insertText(cursor, text); + var end = session.insert(cursor, text); if (transform && transform.selection) { if (transform.selection.length == 2) { // Transform relative to the current column @@ -907,7 +901,7 @@ var Editor = function(renderer, session) { if (session.getDocument().isNewLine(text)) { var lineIndent = mode.getNextLineIndent(lineState, line.slice(0, cursor.column), session.getTabString()); - session.insertText({row: cursor.row+1, column: 0}, lineIndent); + session.insert({row: cursor.row+1, column: 0}, lineIndent); } if (shouldOutdent) mode.autoOutdent(lineState, session, cursor.row); @@ -1280,7 +1274,7 @@ var Editor = function(renderer, session) { } var cursor = this.getCursorPosition(); - this.insertText("\n"); + this.insert("\n"); this.moveCursorToPosition(cursor); }; @@ -1379,7 +1373,7 @@ var Editor = function(renderer, session) { this.selection.setSelectionRange(range); indentString = "\t"; } - return this.insertText(indentString); + return this.insert(indentString); }; /** @@ -1531,7 +1525,7 @@ var Editor = function(renderer, session) { doc.duplicateLines(row, row); } else { var point = reverse ? range.start : range.end; - var endPoint = doc.insertText(point, doc.getTextRange(range), false); + var endPoint = doc.insert(point, doc.getTextRange(range), false); range.start = point; range.end = endPoint; diff --git a/lib/ace/editor_change_document_test.js b/lib/ace/editor_change_document_test.js index 83fffeb3..a1fdaad9 100644 --- a/lib/ace/editor_change_document_test.js +++ b/lib/ace/editor_change_document_test.js @@ -172,7 +172,7 @@ module.exports = { self.session1.setMode(new HtmlMode()); // 5. Try to type valid HTML - self.session1.insertText({row: 0, column: 0}, ""); + self.session1.insert({row: 0, column: 0}, ""); setTimeout(function() { assert.equal(Object.keys(self.session1.getAnnotations()).length, 0); diff --git a/lib/ace/editor_navigation_test.js b/lib/ace/editor_navigation_test.js index b60d391d..ab348241 100644 --- a/lib/ace/editor_navigation_test.js +++ b/lib/ace/editor_navigation_test.js @@ -150,7 +150,7 @@ module.exports = { var editor = new Editor(new MockRenderer(), new EditSession(["1234", "1234567890"])); editor.navigateTo(0, 3); - editor.insertText("juhu"); + editor.insert("juhu"); editor.navigateDown(); assert.position(editor.getCursorPosition(), 1, 7); diff --git a/lib/ace/ext/elastic_tabstops_lite.js b/lib/ace/ext/elastic_tabstops_lite.js index 4d3a40d4..a8c3570c 100644 --- a/lib/ace/ext/elastic_tabstops_lite.js +++ b/lib/ace/ext/elastic_tabstops_lite.js @@ -236,7 +236,7 @@ var ElasticTabstopsLite = function(editor) { if (difference > 0) { // put the spaces after the tab and then delete the tab, so any insertion // points behave as expected - this.$editor.session.getDocument().insertText({row: row, column: it + 1}, Array(difference + 1).join(" ") + "\t"); + this.$editor.session.getDocument().insert({row: row, column: it + 1}, Array(difference + 1).join(" ") + "\t"); this.$editor.session.getDocument().removeInLine(row, it, it + 1); bias += difference; diff --git a/lib/ace/ext/whitespace.js b/lib/ace/ext/whitespace.js index 736e991d..be0cde28 100644 --- a/lib/ace/ext/whitespace.js +++ b/lib/ace/ext/whitespace.js @@ -153,7 +153,7 @@ exports.convertIndentation = function(session, ch, len) { if (toInsert != match) { doc.removeInLine(i, 0, match.length); - doc.insertText({row: i, column: 0}, toInsert); + doc.insert({row: i, column: 0}, toInsert); } } } diff --git a/lib/ace/keyboard/vim/commands.js b/lib/ace/keyboard/vim/commands.js index defca037..dd3357d6 100644 --- a/lib/ace/keyboard/vim/commands.js +++ b/lib/ace/keyboard/vim/commands.js @@ -104,7 +104,7 @@ var actions = exports.actions = { if (param && param.length) { if (param.length > 1) param = param == "return" ? "\n" : param == "tab" ? "\t" : param; - repeat(function() { editor.insertText(param); }, count || 1); + repeat(function() { editor.insert(param); }, count || 1); editor.navigateLeft(); } } @@ -224,12 +224,12 @@ var actions = exports.actions = { var pos = editor.getCursorPosition(); pos.column = editor.session.getLine(pos.row).length; var text = lang.stringRepeat("\n" + defaultReg.text, count || 1); - editor.session.insertText(pos, text); + editor.session.insert(pos, text); editor.moveCursorTo(pos.row + 1, 0); } else { editor.navigateRight(); - editor.insertText(lang.stringRepeat(defaultReg.text, count || 1)); + editor.insert(lang.stringRepeat(defaultReg.text, count || 1)); editor.navigateLeft(); } editor.setOverwrite(true); @@ -245,11 +245,11 @@ var actions = exports.actions = { var pos = editor.getCursorPosition(); pos.column = 0; var text = lang.stringRepeat(defaultReg.text + "\n", count || 1); - editor.session.insertText(pos, text); + editor.session.insert(pos, text); editor.moveCursorToPosition(pos); } else { - editor.insertText(lang.stringRepeat(defaultReg.text, count || 1)); + editor.insert(lang.stringRepeat(defaultReg.text, count || 1)); } editor.setOverwrite(true); editor.selection.clearSelection(); diff --git a/lib/ace/keyboard/vim/maps/motions.js b/lib/ace/keyboard/vim/maps/motions.js index 2bf580b0..91c8b8af 100644 --- a/lib/ace/keyboard/vim/maps/motions.js +++ b/lib/ace/keyboard/vim/maps/motions.js @@ -545,7 +545,7 @@ module.exports = { if (content.length) { editor.navigateLineEnd() - editor.insertText(content); + editor.insert(content); util.insertMode(editor); } } @@ -562,9 +562,9 @@ module.exports = { if(row > 0) { editor.navigateUp(); editor.navigateLineEnd() - editor.insertText(content); + editor.insert(content); } else { - editor.session.insertText({row: 0, column: 0}, content); + editor.session.insert({row: 0, column: 0}, content); editor.navigateUp(); } util.insertMode(editor); diff --git a/lib/ace/mode/javascript_test.js b/lib/ace/mode/javascript_test.js index cc2b22d7..b413765a 100644 --- a/lib/ace/mode/javascript_test.js +++ b/lib/ace/mode/javascript_test.js @@ -131,7 +131,7 @@ module.exports = { this.mode.toggleCommentLines("start", session, 0, 2); assert.equal([" abc", " cde", " fg"].join("\n"), session.toString()); - session.insertText({row: 0, column: 0}, " "); + session.insert({row: 0, column: 0}, " "); this.mode.toggleCommentLines("start", session, 0, 2); assert.equal(["// abc", "// cde", "// fg"].join("\n"), session.toString()); }, diff --git a/lib/ace/mode/text.js b/lib/ace/mode/text.js index 534ac03c..76ba15b5 100644 --- a/lib/ace/mode/text.js +++ b/lib/ace/mode/text.js @@ -92,8 +92,8 @@ var Mode = function() { if (testRemove(line, i)) return; if (!ignoreBlankLines || /\S/.test(line)) { - doc.insertText({row: i, column: line.length}, lineCommentEnd); - doc.insertText({row: i, column: minIndent}, lineCommentStart); + doc.insert({row: i, column: line.length}, lineCommentEnd); + doc.insert({row: i, column: minIndent}, lineCommentStart); } }; @@ -138,9 +138,9 @@ var Mode = function() { var comment = function(line, i) { if (!ignoreBlankLines || /\S/.test(line)) { if (shouldInsertSpace(line, minIndent, minIndent)) - doc.insertText({row: i, column: minIndent}, commentWithSpace); + doc.insert({row: i, column: minIndent}, commentWithSpace); else - doc.insertText({row: i, column: minIndent}, lineCommentStart); + doc.insert({row: i, column: minIndent}, lineCommentStart); } }; var testRemove = function(line, i) { @@ -244,8 +244,8 @@ var Mode = function() { } else { colDiff = comment.start.length startRow = range.start.row; - session.insertText(range.end, comment.end); - session.insertText(range.start, comment.start); + session.insert(range.end, comment.end); + session.insert(range.start, comment.start); } // todo: selection should have ended up in the right place automatically! if (initialRange.start.row == startRow) diff --git a/lib/ace/mouse/dragdrop_handler.js b/lib/ace/mouse/dragdrop_handler.js index 3ad54bb8..3a4bc188 100644 --- a/lib/ace/mouse/dragdrop_handler.js +++ b/lib/ace/mouse/dragdrop_handler.js @@ -176,7 +176,7 @@ function DragdropHandler(mouseHandler) { var dropData = dataTransfer.getData('Text'); range = { start: dragCursor, - end: editor.session.insertText(dragCursor, dropData) + end: editor.session.insert(dragCursor, dropData) }; editor.focus(); dragOperation = null; diff --git a/lib/ace/multi_select.js b/lib/ace/multi_select.js index 883d9a7c..965cd961 100644 --- a/lib/ace/multi_select.js +++ b/lib/ace/multi_select.js @@ -544,7 +544,7 @@ var Editor = require("./editor").Editor; this._signal("paste", text); if (!this.inMultiSelectMode || this.inVirtualSelectionMode) - return this.insertText(text); + return this.insert(text); var lines = text.split(/\r\n|\r|\n/); var ranges = this.selection.rangeList.ranges; @@ -557,7 +557,7 @@ var Editor = require("./editor").Editor; if (!range.isEmpty()) this.session.remove(range); - this.session.insertText(range.start, lines[i]); + this.session.insert(range.start, lines[i]); } }; @@ -742,7 +742,7 @@ var Editor = require("./editor").Editor; } var lines = this.session.removeFullLines(fr, lr); lines = this.$reAlignText(lines, guessRange); - this.session.insertText({row: fr, column: 0}, lines.join("\n") + "\n"); + this.session.insert({row: fr, column: 0}, lines.join("\n") + "\n"); if (!guessRange) { range.start.column = 0; range.end.column = lines[lines.length - 1].length; @@ -778,7 +778,7 @@ var Editor = require("./editor").Editor; var l = maxCol - p.column; var d = spaceOffsets[i] - minSpace; if (l > d) - session.insertText(p, lang.stringRepeat(" ", l - d)); + session.insert(p, lang.stringRepeat(" ", l - d)); else session.remove(new Range(p.row, p.column, p.row, p.column - l + d)); diff --git a/lib/ace/occur_test.js b/lib/ace/occur_test.js index a9b2c894..59fb8548 100644 --- a/lib/ace/occur_test.js +++ b/lib/ace/occur_test.js @@ -55,7 +55,7 @@ module.exports = { }, "test: find lines matching" : function() { - editor.session.insertText({row: 0, column: 0}, 'abc\ndef\nxyz\nbcxbc'); + editor.session.insert({row: 0, column: 0}, 'abc\ndef\nxyz\nbcxbc'); var result = occur.matchingLines(editor.session, {needle: 'bc'}), expected = [{row: 0, content: 'abc'}, {row: 3, content: 'bcxbc'}]; assert.deepEqual(result, expected); @@ -63,7 +63,7 @@ module.exports = { "test: display occurrences" : function() { var text = 'abc\ndef\nxyz\nbcx\n'; - editor.session.insertText({row: 0, column: 0}, text); + editor.session.insert({row: 0, column: 0}, text); occur.displayOccurContent(editor, {needle: 'bc'}); assert.equal(editor.getValue(), 'abc\nbcx'); occur.displayOriginalContent(editor); @@ -72,7 +72,7 @@ module.exports = { "test: original position from occur doc" : function() { var text = 'abc\ndef\nxyz\nbcx\n'; - editor.session.insertText({row: 0, column: 0}, text); + editor.session.insert({row: 0, column: 0}, text); occur.displayOccurContent(editor, {needle: 'bc'}); assert.equal(editor.getValue(), 'abc\nbcx'); var pos = occur.occurToOriginalPosition(editor.session, {row: 1, column: 2}); @@ -82,7 +82,7 @@ module.exports = { "test: occur command" : function() { // setup var text = 'hel\nlo\n\nwo\nrld\n'; - editor.session.insertText({row: 0, column: 0}, text); + editor.session.insert({row: 0, column: 0}, text); editor.commands.addCommand(occurStartCommand); // run occur for lines including 'o' @@ -106,7 +106,7 @@ module.exports = { "test: occur navigation" : function() { // setup var text = 'hel\nlo\n\nwo\nrld\n'; - editor.session.insertText({row: 0, column: 0}, text); + editor.session.insert({row: 0, column: 0}, text); editor.commands.addCommand(occurStartCommand); editor.moveCursorToPosition({row: 1, column: 1}); @@ -125,7 +125,7 @@ module.exports = { "test: recursive occur" : function() { // setup var text = 'x\nabc1\nx\nabc2\n'; - editor.session.insertText({row: 0, column: 0}, text); + editor.session.insert({row: 0, column: 0}, text); editor.commands.addCommand(occurStartCommand); // orig -> occur1 diff --git a/lib/ace/placeholder_test.js b/lib/ace/placeholder_test.js index dc7e4b23..97e561fd 100644 --- a/lib/ace/placeholder_test.js +++ b/lib/ace/placeholder_test.js @@ -53,9 +53,9 @@ module.exports = { new PlaceHolder(session, 1, {row: 0, column: 4}, [{row: 1, column: 12}, {row: 1, column: 15}]); editor.moveCursorTo(0, 5); - editor.insertText('b'); + editor.insert('b'); assert.equal(session.doc.getValue(), "var ab = 10;\nconsole.log(ab, ab);"); - editor.insertText('cd'); + editor.insert('cd'); assert.equal(session.doc.getValue(), "var abcd = 10;\nconsole.log(abcd, abcd);"); editor.remove('left'); editor.remove('left'); @@ -70,7 +70,7 @@ module.exports = { new PlaceHolder(session, 1, {row: 0, column: 4}, [{row: 1, column: 12}, {row: 1, column: 15}]); editor.moveCursorTo(2, 0); - editor.insertText('b'); + editor.insert('b'); assert.equal(session.doc.getValue(), "var a = 10;\nconsole.log(a, a);\nb"); }, @@ -81,12 +81,12 @@ module.exports = { var p = new PlaceHolder(session, 1, {row: 0, column: 4}, [{row: 1, column: 12}, {row: 1, column: 15}]); editor.moveCursorTo(0, 4); - editor.insertText('$'); + editor.insert('$'); assert.equal(session.doc.getValue(), "var $a = 10;\nconsole.log($a, $a);"); editor.moveCursorTo(0, 4); // Have to put this in a setTimeout because the anchor is only fixed later. setTimeout(function() { - editor.insertText('v'); + editor.insert('v'); assert.equal(session.doc.getValue(), "var v$a = 10;\nconsole.log(v$a, v$a);"); next(); }, 10); @@ -99,10 +99,10 @@ module.exports = { var p = new PlaceHolder(session, 1, {row: 0, column: 4}, [{row: 1, column: 12}, {row: 1, column: 15}]); editor.moveCursorTo(0, 5); - editor.insertText('b'); + editor.insert('b'); assert.equal(session.doc.getValue(), "var ab = 10;\nconsole.log(ab, ab);"); p.detach(); - editor.insertText('cd'); + editor.insert('cd'); assert.equal(session.doc.getValue(), "var abcd = 10;\nconsole.log(ab, ab);"); }, @@ -136,8 +136,8 @@ module.exports = { var p = new PlaceHolder(session, 1, {row: 0, column: 4}, [{row: 1, column: 12}, {row: 1, column: 15}]); editor.moveCursorTo(0, 5); - editor.insertText('b'); - editor.insertText('cd'); + editor.insert('b'); + editor.insert('cd'); editor.remove('left'); assert.equal(session.doc.getValue(), "var abc = 10;\nconsole.log(abc, abc);"); // Wait a little for the changes to enter the undo stack From f2a2e4e1a8ce7870824beea9c956ae2671aadc86 Mon Sep 17 00:00:00 2001 From: aldendaniels Date: Wed, 8 Jan 2014 22:18:05 -0600 Subject: [PATCH 10/92] Rename the 'delete' delta action to 'remove' Matches previous naming convention. --- lib/ace/anchor.js | 2 +- lib/ace/apply_delta.js | 8 ++++---- lib/ace/background_tokenizer.js | 2 +- lib/ace/document.js | 20 ++++++++++---------- lib/ace/edit_session.js | 4 ++-- lib/ace/edit_session/folding.js | 2 +- lib/ace/ext/chromevox.js | 2 +- lib/ace/layer/gutter.js | 2 +- lib/ace/line_widgets.js | 2 +- lib/ace/placeholder.js | 4 ++-- 10 files changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/ace/anchor.js b/lib/ace/anchor.js index affb4f19..fee42bb5 100644 --- a/lib/ace/anchor.js +++ b/lib/ace/anchor.js @@ -133,7 +133,7 @@ var Anchor = exports.Anchor = function(doc, row, column) { } // DELTA ENVELOPS POINT (delete only): Move point to delta start. - if (delta.action != 'delete') + if (delta.action != 'remove') throw 'Delete action expected.'; return { diff --git a/lib/ace/apply_delta.js b/lib/ace/apply_delta.js index 02c0a498..142d5e1c 100644 --- a/lib/ace/apply_delta.js +++ b/lib/ace/apply_delta.js @@ -53,8 +53,8 @@ function throwDeltaError(delta, errorText){ function validateDelta(lines, delta) { // Validate action. - if (delta.action != 'insert' && delta.action != 'delete') - fnThrow('Delta action must be "insert" or "delete".'); + if (delta.action != 'insert' && delta.action != 'remove') + fnThrow('Delta action must be "insert" or "remove".'); // Validate lines. if (!delta.lines instanceof Array) @@ -103,7 +103,7 @@ exports.applyDelta = function(lines, delta) { lines[row] = line.substring(0, startColumn) + delta.lines[0] + line.substring(startColumn); break; - case 'delete': + case 'remove': lines[row] = line.substring(0, startColumn) + line.substring(endColumn); break; } @@ -122,7 +122,7 @@ exports.applyDelta = function(lines, delta) { joinLineWithNext(lines, delta.range.end.row); break; - case 'delete': + case 'remove': splitLine(lines, delta.range.end); splitLine(lines, delta.range.start); lines.splice( diff --git a/lib/ace/background_tokenizer.js b/lib/ace/background_tokenizer.js index cb3ffe23..4d3d7034 100644 --- a/lib/ace/background_tokenizer.js +++ b/lib/ace/background_tokenizer.js @@ -183,7 +183,7 @@ var BackgroundTokenizer = function(tokenizer, editor) { if (len === 0) { this.lines[startRow] = null; - } else if (delta.action == "delete") { + } else if (delta.action == 'remove') { this.lines.splice(startRow, len + 1, null); this.states.splice(startRow, len + 1, null); } else { diff --git a/lib/ace/document.js b/lib/ace/document.js index a84d3bcc..de77582e 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -306,7 +306,7 @@ var Document = function(textOrLines) { * * `"insert"` * * `range`: the [[Range]] of the change within the document * * `lines`: the lines being added - * * `"delete"` + * * `"remove"` * * `range`: the [[Range]] of the change within the document * * `lines`: the lines being removed * @@ -395,7 +395,7 @@ var Document = function(textOrLines) { // Apply delta (emits change). range = this.$getClippedRange(range); this.applyDelta({ - action: 'delete', + action: 'remove', range: range, lines: this.getLinesForRange(range), }); @@ -418,7 +418,7 @@ var Document = function(textOrLines) { // Apply delta (emits change). this.applyDelta({ - action: "delete", + action: 'remove', range: range, lines: this.getLinesForRange(range) }); @@ -455,7 +455,7 @@ var Document = function(textOrLines) { // Apply delta (emits change). this.applyDelta({ - action: "delete", + action: 'remove', range: range, lines: this.getLinesForRange(range) }); @@ -474,7 +474,7 @@ var Document = function(textOrLines) { if (row < this.getLength() - 1 && row >= 0) { // Apply delta (emits change). this.applyDelta({ - action: "delete", + action: 'remove', range: new Range(row, this.getLine(row).length, row + 1, 0), lines: ['', ''] }); @@ -516,7 +516,7 @@ var Document = function(textOrLines) { /** * Applies all changes in `deltas` to the document. - * @param {Array} deltas An array of delta objects (can include 'insert' and 'delete' actions) + * @param {Array} deltas An array of delta objects (can include 'insert' and 'remove' actions) **/ this.applyDeltas = function(deltas) { for (var i=0; i=0; i--) { @@ -536,7 +536,7 @@ var Document = function(textOrLines) { /** * Applies `delta` to the document. - * @param {Object} delta A delta object (can include 'insert' and 'delete' actions) + * @param {Object} delta A delta object (can include 'insert' and 'remove' actions) **/ this.applyDelta = function(delta) { applyDelta(this.$lines, delta); @@ -545,12 +545,12 @@ var Document = function(textOrLines) { /** * Reverts `delta` from the document. - * @param {Object} delta A delta object (can include 'insert' and 'delete' actions) + * @param {Object} delta A delta object (can include 'insert' and 'remove' actions) **/ this.revertDelta = function(delta) { this.applyDelta({ - action: (delta.action == 'insert' ? 'delete' : 'insert'), + action: (delta.action == 'insert' ? 'remove' : 'insert'), range: delta.range.clone(), lines: delta.lines.slice() }); diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index f140bdac..7312f23b 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -1685,7 +1685,7 @@ var EditSession = function(text, mode) { this.$updating = true; if (len != 0) { - if (action == "delete") { + if (action == 'remove') { this[useWrapMode ? "$wrapData" : "$rowLengthCache"].splice(firstRow, len); var foldLines = this.$foldData; @@ -1760,7 +1760,7 @@ var EditSession = function(text, mode) { // Realign folds. E.g. if you add some new chars before a fold, the // fold should "move" to the right. len = Math.abs(e.data.range.start.column - e.data.range.end.column); - if (action == "delete") { + if (action == 'remove') { // Get all the folds in the change range and remove them. removedFolds = this.getFoldsInRange(e.data.range); this.removeFolds(removedFolds); diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js index 9df35593..583dba0d 100644 --- a/lib/ace/edit_session/folding.js +++ b/lib/ace/edit_session/folding.js @@ -833,7 +833,7 @@ function Folding() { if (len === 0) { this.foldWidgets[firstRow] = null; - } else if (delta.action == "delete") { + } else if (delta.action == 'remove') { this.foldWidgets.splice(firstRow, len + 1, null); } else { var args = Array(len + 1); diff --git a/lib/ace/ext/chromevox.js b/lib/ace/ext/chromevox.js index 20bbb6c7..4abb8278 100644 --- a/lib/ace/ext/chromevox.js +++ b/lib/ace/ext/chromevox.js @@ -581,7 +581,7 @@ var onSelectionChange = function(evt) { var onChange = function(evt) { var data = evt.data; switch (data.action) { - case 'delete': + case 'remove': cvox.Api.speak(data.text, 0, DELETED_PROP); /* Let the future cursor change event know it's from text change. */ changed = true; diff --git a/lib/ace/layer/gutter.js b/lib/ace/layer/gutter.js index 82dcf245..235425ab 100644 --- a/lib/ace/layer/gutter.js +++ b/lib/ace/layer/gutter.js @@ -108,7 +108,7 @@ var Gutter = function(parentEl) { var len = range.end.row - firstRow; if (len === 0) { // do nothing - } else if (delta.action == "delete") { + } else if (delta.action == 'remove') { this.$annotations.splice(firstRow, len + 1, null); } else { var args = new Array(len + 1); diff --git a/lib/ace/line_widgets.js b/lib/ace/line_widgets.js index 4023bd55..d0c7581e 100644 --- a/lib/ace/line_widgets.js +++ b/lib/ace/line_widgets.js @@ -123,7 +123,7 @@ function LineWidgets(session) { if (len === 0) { // return - } else if (delta.action == "delete") { + } else if (delta.action == 'remove') { var removed = cells.splice(startRow + 1, len); removed.forEach(function(w) { w && this.removeLineWidget(w); diff --git a/lib/ace/placeholder.js b/lib/ace/placeholder.js index c9a44e50..df0bfa9e 100644 --- a/lib/ace/placeholder.js +++ b/lib/ace/placeholder.js @@ -169,7 +169,7 @@ var PlaceHolder = function(session, length, pos, others, mainClass, othersClass) newPos.column += lengthDiff; this.doc.insertMergedLines(newPos, delta.lines); } - } else if(delta.action === "delete") { + } else if(delta.action === 'remove') { for (var i = this.others.length - 1; i >= 0; i--) { var otherPos = this.others[i]; var newPos = {row: otherPos.row, column: otherPos.column + distanceFromStart}; @@ -191,7 +191,7 @@ var PlaceHolder = function(session, length, pos, others, mainClass, othersClass) } }.bind(this), 0); } - else if(range.start.column === this.pos.column && delta.action === "delete") { + else if(range.start.column === this.pos.column && delta.action === 'remove') { setTimeout(function() { for (var i = 0; i < this.others.length; i++) { var other = this.others[i]; From 3a048cdf611b86151eb28a01af7db666bce39aca Mon Sep 17 00:00:00 2001 From: aldendaniels Date: Wed, 8 Jan 2014 22:51:39 -0600 Subject: [PATCH 11/92] Bring back insertInLine Avoids an extra $split call. --- lib/ace/document.js | 37 +++++++++++++++++++++++++--- lib/ace/ext/elastic_tabstops_lite.js | 2 +- lib/ace/ext/whitespace.js | 2 +- lib/ace/mode/text.js | 4 +-- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/lib/ace/document.js b/lib/ace/document.js index de77582e..6968e8f0 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -277,10 +277,6 @@ var Document = function(textOrLines) { console.warn('Use of document.insertNewLine is deprecated. Use insertMergedLines(position, [\'\', \'\']) instead.'); return this.insertMergedLines(position, ['', '']); }; - this.insertInLine = function(position, text) { - console.warn('Use of document.insertInLine is deprecated. Use insert instead.'); - return this.insert(position, text); - }; /** * Inserts a block of `text` at the indicated `position`. @@ -298,6 +294,39 @@ var Document = function(textOrLines) { return this.insertMergedLines(position, this.$split(text)); }; + /** + * Inserts `text` into the `position` at the current row. This method also triggers the `'change'` event. + * + * This differs from the `insert` method in two ways: + * 1. This does NOT handle newline characters (single-line text only). + * 2. This is faster than the `insert` method for single-line text insertions. + * + * @param {Object} position The position to insert at; it's an object that looks like `{ row: row, column: column}` + * @param {String} text A chunk of text + * @returns {Object} Returns an object containing the final row and column, like this: + * ``` + * {row: endRow, column: 0} + * ``` + **/ + this.insertInLine = function(position, text) { + + // Calculate insertion range end point. + this.$clipPosition(position); + var endPoint = { + row : position.row, + column : position.column + text.length + }; + + // Apply delta (emits change). + this.applyDelta({ + action: "insert", + range: Range.fromPoints(position, endPoint), + lines: [text] + }); + + return endPoint; + }; + /** * Fires whenever the document changes. * diff --git a/lib/ace/ext/elastic_tabstops_lite.js b/lib/ace/ext/elastic_tabstops_lite.js index a8c3570c..9901c5df 100644 --- a/lib/ace/ext/elastic_tabstops_lite.js +++ b/lib/ace/ext/elastic_tabstops_lite.js @@ -236,7 +236,7 @@ var ElasticTabstopsLite = function(editor) { if (difference > 0) { // put the spaces after the tab and then delete the tab, so any insertion // points behave as expected - this.$editor.session.getDocument().insert({row: row, column: it + 1}, Array(difference + 1).join(" ") + "\t"); + this.$editor.session.getDocument().insertInLine({row: row, column: it + 1}, Array(difference + 1).join(" ") + "\t"); this.$editor.session.getDocument().removeInLine(row, it, it + 1); bias += difference; diff --git a/lib/ace/ext/whitespace.js b/lib/ace/ext/whitespace.js index be0cde28..83486fb0 100644 --- a/lib/ace/ext/whitespace.js +++ b/lib/ace/ext/whitespace.js @@ -153,7 +153,7 @@ exports.convertIndentation = function(session, ch, len) { if (toInsert != match) { doc.removeInLine(i, 0, match.length); - doc.insert({row: i, column: 0}, toInsert); + doc.insertInLine({row: i, column: 0}, toInsert); } } } diff --git a/lib/ace/mode/text.js b/lib/ace/mode/text.js index 76ba15b5..343fc271 100644 --- a/lib/ace/mode/text.js +++ b/lib/ace/mode/text.js @@ -92,8 +92,8 @@ var Mode = function() { if (testRemove(line, i)) return; if (!ignoreBlankLines || /\S/.test(line)) { - doc.insert({row: i, column: line.length}, lineCommentEnd); - doc.insert({row: i, column: minIndent}, lineCommentStart); + doc.insertInLine({row: i, column: line.length}, lineCommentEnd); + doc.insertInLine({row: i, column: minIndent}, lineCommentStart); } }; From 08edcdfc988fe8eacc40af4da464fb8ebde8c61c Mon Sep 17 00:00:00 2001 From: aldendaniels Date: Fri, 10 Jan 2014 22:49:17 -0600 Subject: [PATCH 12/92] Stop doing a line-by-line splicing in applyDelta Also brings back the functionality where large deltas are split into smaller deltas so that .splice.apply() calls will work. --- lib/ace/apply_delta.js | 7 ++----- lib/ace/document.js | 30 +++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/lib/ace/apply_delta.js b/lib/ace/apply_delta.js index 142d5e1c..380c3289 100644 --- a/lib/ace/apply_delta.js +++ b/lib/ace/apply_delta.js @@ -79,9 +79,9 @@ function validateDelta(lines, delta) { // TODO: // - Validate that the ending column offset matches the lines. // - Validate the deleted lines match the lines in the document. + // - Vaiidate that an insert delta does not contain more than 65001 entries. } - exports.applyDelta = function(lines, delta) { // Validate delta. @@ -114,10 +114,7 @@ exports.applyDelta = function(lines, delta) { case 'insert': splitLine(lines, delta.range.start); - for (var i = 0; i < delta.lines.length; i++) { - var row = delta.range.start.row + 1 + i; - lines.splice(row, 0, delta.lines[i]); - } + lines.splice.apply(lines, [delta.range.start.row + 1, 0].concat(delta.lines)); joinLineWithNext(lines, delta.range.start.row); joinLineWithNext(lines, delta.range.end.row); break; diff --git a/lib/ace/document.js b/lib/ace/document.js index 6968e8f0..cefaa136 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -568,8 +568,36 @@ var Document = function(textOrLines) { * @param {Object} delta A delta object (can include 'insert' and 'remove' actions) **/ this.applyDelta = function(delta) { + + // Split large insert deltas. This is necessary because: + // 1. We need to support splicing delta lines into the document via $lines.splice.apply(...) + // 2. fn.apply() doesn't work for a large number of params. The mallest threshold is on safari 0xFFFF. + // + // To Do: Ideally we'd be consistent and also split 'delete' deltas. We don't do this now, because delete + // delta handling is too slow. If we make delete delta handling faster we can split all large deltas + // as shown in https://gist.github.com/aldendaniels/8367109#file-document-snippet-js + // If we do this, update validateDelta() to limit the number of lines in a delete delta. + var bIsInsert = delta.action == 'insert'; + while (bIsInsert && delta.lines.length > 65001) + { + // Get split deltas. + var lines = delta.lines.splice(0, 65000); + lines.push(''); + this.applyDelta({ + action: delta.action, + lines: lines, + range: new Range(delta.range.start.row, delta.range.start.column, + delta.range.start.row + 65000, 0) + }); + + // Update remaining delta. + delta.range.start.row += 65000; + delta.range.start.column = 0; + } + + // Apply. applyDelta(this.$lines, delta); - this._emit("change", { data: delta }); + this._emit("change", { data: delta }); }; /** From ef0e8da5223884d4ff041ff59b1711488ca76a4a Mon Sep 17 00:00:00 2001 From: aldendaniels Date: Sat, 11 Jan 2014 11:20:34 -0600 Subject: [PATCH 13/92] Fix / complete validateDelta This uncovered the fact that until now delta.range had not always been a Range object. This inconsistency has been resolved by my changes in mirror.js. --- lib/ace/apply_delta.js | 96 +++++++++++++++++++++------------------- lib/ace/worker/mirror.js | 13 +++++- 2 files changed, 62 insertions(+), 47 deletions(-) diff --git a/lib/ace/apply_delta.js b/lib/ace/apply_delta.js index 380c3289..3d40d614 100644 --- a/lib/ace/apply_delta.js +++ b/lib/ace/apply_delta.js @@ -33,59 +33,63 @@ define(function(require, exports, module) { var Range = require("./range").Range; -function splitLine (lines, point) { - var text = lines[point.row]; - lines[point.row] = text.slice(0, point.column); - lines.splice(point.row + 1, 0, text.slice(point.column)); +function splitLine (docLines, position) { + var text = docLines[position.row]; + docLines[position.row] = text.slice(0, position.column); + docLines.splice(position.row + 1, 0, text.slice(position.column)); } -function joinLineWithNext(lines, row) { - lines[row] += lines[row + 1]; - lines.splice(row + 1, 1); +function joinLineWithNext(docLines, row) { + docLines[row] += docLines[row + 1]; + docLines.splice(row + 1, 1); } function throwDeltaError(delta, errorText){ - errorText = 'Invalid Delta: ' + errorText; - console.log(errorText, delta); - throw errorText; + console.log('Invalid Delta:', delta); + throw 'Invalid Delta: ' + errorText; } -function validateDelta(lines, delta) { +function positionInDocument(docLines, position) +{ + return position.row >= 0 && position.row < docLines.length && + position.column >= 0 && position.column <= docLines[position.row].length; +} - // Validate action. +function validateDelta(docLines, delta) { + + // Validate action string. if (delta.action != 'insert' && delta.action != 'remove') - fnThrow('Delta action must be "insert" or "remove".'); - - // Validate lines. - if (!delta.lines instanceof Array) - fnThrow('Delta lines must be an array'); + throwDeltaError(delta, 'delta.action must be "insert" or "remove"'); + // Validate lines type. + if (!(delta.lines instanceof Array)) + throwDeltaError(delta, 'delta.lines must be an Array'); + // Validate range type. - if (!delta.range instanceof Range) - fnThrow('Range object is not an instance of the Range class'); - - // Validate start point. + if (!(delta.range instanceof Range)) + throwDeltaError(delta, 'delta.range must be an instance of the Range class'); + + // Validate that the start point is contained in the document. var start = delta.range.start; - if (Math.min(Math.max(start.row, 0), lines.length - 1 ) != start.row || - Math.min(Math.max(start.column, 0), lines[start.row].length) != start.column) - { - fnThrow('Range start point not contained in document'); - } + if (!positionInDocument(docLines, delta.range.start)) + throwDeltaError(delta, 'delta.range.start must be contained in document'); - // Validate ending row offset. - if (delta.lines.length - 1 != delta.range.end.row - delta.range.start.row) - fnThrow('Range row offsets does not match delta lines'); + // Validate that the end point is contained in the document (remove deltas only). + var end = delta.range.end; + if (delta.action == 'remove' && !positionInDocument(docLines, end)) + throwDeltaError(delta, 'delta.range.end must contained in document for "remove" actions'); - // TODO: - // - Validate that the ending column offset matches the lines. - // - Validate the deleted lines match the lines in the document. - // - Vaiidate that an insert delta does not contain more than 65001 entries. + // Validate that the .range size matches the .lines size. + var numRangeRows = end.row - start.row; + var numRangeLastLineChars = (end.column - (numRangeRows == 0 ? start.column : 0)); + if (numRangeRows != delta.lines.length - 1 || delta.lines[numRangeRows].length != numRangeLastLineChars) + throwDeltaError(delta, 'delta.range must match delta lines'); } -exports.applyDelta = function(lines, delta) { +exports.applyDelta = function(docLines, delta) { // Validate delta. - validateDelta(lines, delta); + validateDelta(docLines, delta); // Apply delta. if (delta.range.start.row == delta.range.end.row) @@ -96,15 +100,15 @@ exports.applyDelta = function(lines, delta) { var row = delta.range.start.row; var startColumn = delta.range.start.column; var endColumn = delta.range.end.column; - var line = lines[row]; + var line = docLines[row]; switch (delta.action) { case 'insert': - lines[row] = line.substring(0, startColumn) + delta.lines[0] + line.substring(startColumn); + docLines[row] = line.substring(0, startColumn) + delta.lines[0] + line.substring(startColumn); break; case 'remove': - lines[row] = line.substring(0, startColumn) + line.substring(endColumn); + docLines[row] = line.substring(0, startColumn) + line.substring(endColumn); break; } } else { @@ -113,20 +117,20 @@ exports.applyDelta = function(lines, delta) { switch (delta.action) { case 'insert': - splitLine(lines, delta.range.start); - lines.splice.apply(lines, [delta.range.start.row + 1, 0].concat(delta.lines)); - joinLineWithNext(lines, delta.range.start.row); - joinLineWithNext(lines, delta.range.end.row); + splitLine(docLines, delta.range.start); + docLines.splice.apply(docLines, [delta.range.start.row + 1, 0].concat(delta.lines)); + joinLineWithNext(docLines, delta.range.start.row); + joinLineWithNext(docLines, delta.range.end.row); break; case 'remove': - splitLine(lines, delta.range.end); - splitLine(lines, delta.range.start); - lines.splice( + splitLine(docLines, delta.range.end); + splitLine(docLines, delta.range.start); + docLines.splice( delta.range.start.row + 1, // Where to start deleting delta.range.end.row - delta.range.start.row + 1 // Num lines to delete. ); - joinLineWithNext(lines, delta.range.start.row); + joinLineWithNext(docLines, delta.range.start.row); break; } } diff --git a/lib/ace/worker/mirror.js b/lib/ace/worker/mirror.js index 7a3318fb..d55e84f4 100644 --- a/lib/ace/worker/mirror.js +++ b/lib/ace/worker/mirror.js @@ -1,6 +1,7 @@ define(function(require, exports, module) { "use strict"; +var Range = require("../range").Range; var Document = require("../document").Document; var lang = require("../lib/lang"); @@ -12,7 +13,17 @@ var Mirror = exports.Mirror = function(sender) { var _self = this; sender.on("change", function(e) { - doc.applyDeltas(e.data); + + // Convert delta.range back into a Range instance since + // window.onMessage loses non-primitive data. See http://jsfiddle.net/nqJfw/1/. + var deltas = e.data; + for (var i in deltas) + { + var delta = deltas[i]; + delta.range = Range.fromPoints(delta.range.start, delta.range.end); + } + + doc.applyDeltas(deltas); if (_self.$timeout) return deferredUpdate.schedule(_self.$timeout); _self.onUpdate(); From f59708a5ba7b049271db9846ae8a71e88a9f6625 Mon Sep 17 00:00:00 2001 From: aldendaniels Date: Sat, 11 Jan 2014 12:00:10 -0600 Subject: [PATCH 14/92] Add a doNotValidate param to applyDelta Set it to true in insertInLine/removeInLine. Also sped up indent/dedent by using insertInLine and removeInLine. --- lib/ace/apply_delta.js | 5 +++-- lib/ace/document.js | 12 ++++++------ lib/ace/edit_session.js | 22 +++++++++++----------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/lib/ace/apply_delta.js b/lib/ace/apply_delta.js index 3d40d614..1317ce98 100644 --- a/lib/ace/apply_delta.js +++ b/lib/ace/apply_delta.js @@ -86,10 +86,11 @@ function validateDelta(docLines, delta) { throwDeltaError(delta, 'delta.range must match delta lines'); } -exports.applyDelta = function(docLines, delta) { +exports.applyDelta = function(docLines, delta, doNotValidate) { // Validate delta. - validateDelta(docLines, delta); + if (!doNotValidate) + validateDelta(docLines, delta); // Apply delta. if (delta.range.start.row == delta.range.end.row) diff --git a/lib/ace/document.js b/lib/ace/document.js index cefaa136..c4f8a9eb 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -322,7 +322,7 @@ var Document = function(textOrLines) { action: "insert", range: Range.fromPoints(position, endPoint), lines: [text] - }); + }, true /*doNotValidate*/); return endPoint; }; @@ -449,8 +449,8 @@ var Document = function(textOrLines) { this.applyDelta({ action: 'remove', range: range, - lines: this.getLinesForRange(range) - }); + lines: this.getLinesForRange(range), + }, true /*doNotValidate*/); return range.start; }; @@ -567,7 +567,7 @@ var Document = function(textOrLines) { * Applies `delta` to the document. * @param {Object} delta A delta object (can include 'insert' and 'remove' actions) **/ - this.applyDelta = function(delta) { + this.applyDelta = function(delta, doNotValidate) { // Split large insert deltas. This is necessary because: // 1. We need to support splicing delta lines into the document via $lines.splice.apply(...) @@ -596,8 +596,8 @@ var Document = function(textOrLines) { } // Apply. - applyDelta(this.$lines, delta); - this._emit("change", { data: delta }); + applyDelta(this.$lines, delta, doNotValidate); + this._emit("change", { data: delta }); }; /** diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 7312f23b..414b68c1 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -1384,7 +1384,7 @@ var EditSession = function(text, mode) { this.indentRows = function(startRow, endRow, indentString) { indentString = indentString.replace(/\t/g, this.getTabString()); for (var row=startRow; row<=endRow; row++) - this.insert({row: row, column:0}, indentString); + this.doc.insertInLine({row: row, column: 0}, indentString); }; /** @@ -1395,25 +1395,25 @@ var EditSession = function(text, mode) { **/ this.outdentRows = function (range) { var rowRange = range.collapseRows(); - var deleteRange = new Range(0, 0, 0, 0); var size = this.getTabSize(); - + for (var i = rowRange.start.row; i <= rowRange.end.row; ++i) { var line = this.getLine(i); - - deleteRange.start.row = i; - deleteRange.end.row = i; + var row = i; + var startCol = 0; + var endCol = 0; + for (var j = 0; j < size; ++j) if (line.charAt(j) != ' ') break; if (j < size && line.charAt(j) == '\t') { - deleteRange.start.column = j; - deleteRange.end.column = j + 1; + startCol = j; + endCol = j + 1; } else { - deleteRange.start.column = 0; - deleteRange.end.column = j; + startCol = 0; + endCol = j; } - this.remove(deleteRange); + this.doc.removeInLine(row, startCol, endCol); } }; From ddd695ee3f2a1e851c842d4d17851341067d499b Mon Sep 17 00:00:00 2001 From: aldendaniels Date: Sat, 11 Jan 2014 14:43:47 -0600 Subject: [PATCH 15/92] Store single-line deltas as .text instead of .lines in undo history Stores single-line delta content as .text instead of .lines in undo history. This is done without modifying the original delta object in case the caller still retains a handle to the original. --- lib/ace/undomanager.js | 80 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 15 deletions(-) diff --git a/lib/ace/undomanager.js b/lib/ace/undomanager.js index 304dac23..4d978d46 100644 --- a/lib/ace/undomanager.js +++ b/lib/ace/undomanager.js @@ -61,14 +61,19 @@ var UndoManager = function() { * **/ this.execute = function(options) { - var deltas = options.args[0]; + + // Normalize deltas for storage. + var deltaSets = this.$serializeDeltas(options.args[0]); + + // Add deltas to undo stack. this.$doc = options.args[1]; if (options.merge && this.hasUndo()){ - deltas = this.$undoStack.pop().concat(deltas); + deltaSets = this.$undoStack.pop().concat(deltaSets); } - this.$undoStack.push(deltas); + this.$undoStack.push(deltaSets); + + // Reset redo stack. this.$redoStack = []; - if (this.dirtyCounter < 0) { // The user has made a change after undoing past the last clean state. // We can never get back to a clean state now until markClean() is called. @@ -85,12 +90,11 @@ var UndoManager = function() { * @returns {Range} The range of the undo. **/ this.undo = function(dontSelect) { - var deltas = this.$undoStack.pop(); + var deltaSets = this.$undoStack.pop(); var undoSelectionRange = null; - if (deltas) { - undoSelectionRange = - this.$doc.undoChanges(deltas, dontSelect); - this.$redoStack.push(deltas); + if (deltaSets) { + undoSelectionRange = this.$doc.undoChanges(this.$deserializeDeltas(deltaSets), dontSelect); + this.$redoStack.push(deltaSets); this.dirtyCounter--; } @@ -104,15 +108,14 @@ var UndoManager = function() { * **/ this.redo = function(dontSelect) { - var deltas = this.$redoStack.pop(); + var deltaSets = this.$redoStack.pop(); var redoSelectionRange = null; - if (deltas) { + if (deltaSets) { redoSelectionRange = - this.$doc.redoChanges(deltas, dontSelect); - this.$undoStack.push(deltas); + this.$doc.redoChanges(this.$deserializeDeltas(deltaSets), dontSelect); + this.$undoStack.push(deltaSets); this.dirtyCounter++; } - return redoSelectionRange; }; @@ -160,7 +163,54 @@ var UndoManager = function() { this.isClean = function() { return this.dirtyCounter === 0; }; - + + // Serializes deltaSets to reduce memory usage. + this.$serializeDeltas = function(deltaSets) + { + return this.$cloneDeltaSetsObj(deltaSets, $serializeDelta); + } + function $serializeDelta(delta){ + return { + action: delta.action, + range: delta.range, + lines: (delta.lines.length == 1 ? null : delta.lines), + text: (delta.lines.length == 1 ? delta.lines[0] : null ), + }; + } + + // Deserializes deltaSets to allow application to the document. + this.$deserializeDeltas = function(deltaSets) + { + return this.$cloneDeltaSetsObj(deltaSets, $deserializeDelta); + } + function $deserializeDelta(delta){ + return { + action: delta.action, + range: delta.range, + lines: (delta.text === null ? delta.lines : [delta.text]) + }; + } + + // Helper for delta serialization and deserialization. + this.$cloneDeltaSetsObj = function(deltaSets_old, fnGetModifiedDelta) + { + var deltaSets_new = new Array(deltaSets_old.length); + for (var i in deltaSets_old) + { + var deltaSet_old = deltaSets_old[i]; + var deltaSet_new = { group: deltaSet_old.group, deltas: new Array(deltaSet_old.length)}; + + for (var i_ in deltaSet_old.deltas) + { + var delta_old = deltaSet_old.deltas[i_]; + deltaSet_new.deltas[i_] = fnGetModifiedDelta(delta_old); + } + + deltaSets_new[i] = deltaSet_new; + } + return deltaSets_new; + } + }).call(UndoManager.prototype); exports.UndoManager = UndoManager; From 35a27fd1ba52cccb765f515f3924d9ba579a7eea Mon Sep 17 00:00:00 2001 From: aldendaniels Date: Sat, 11 Jan 2014 14:45:08 -0600 Subject: [PATCH 16/92] Treat deltas with empty ranges as NOOPs --- lib/ace/document.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/ace/document.js b/lib/ace/document.js index c4f8a9eb..e203d77c 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -569,6 +569,10 @@ var Document = function(textOrLines) { **/ this.applyDelta = function(delta, doNotValidate) { + // An empty range is a NOOP. + if (delta.range.isEmpty()) + return; + // Split large insert deltas. This is necessary because: // 1. We need to support splicing delta lines into the document via $lines.splice.apply(...) // 2. fn.apply() doesn't work for a large number of params. The mallest threshold is on safari 0xFFFF. From 6b280bf6bb48aa81fc73582f85877f5626f1eae0 Mon Sep 17 00:00:00 2001 From: aldendaniels Date: Sat, 11 Jan 2014 15:23:20 -0600 Subject: [PATCH 17/92] Code review fixes (mostly formatting) - Fix unconventional '{' formatting - Reformat `UndoManager` changes - Revert change from `insertInLine` to `insert` in text.js --- lib/ace/anchor.js | 7 +++---- lib/ace/apply_delta.js | 6 ++---- lib/ace/document.js | 12 ++++++------ lib/ace/mode/text.js | 4 ++-- lib/ace/placeholder.js | 4 ++-- lib/ace/undomanager.js | 32 ++++++++++++++------------------ lib/ace/worker/mirror.js | 3 +-- 7 files changed, 30 insertions(+), 38 deletions(-) diff --git a/lib/ace/anchor.js b/lib/ace/anchor.js index fee42bb5..5622bf5c 100644 --- a/lib/ace/anchor.js +++ b/lib/ace/anchor.js @@ -101,14 +101,13 @@ var Anchor = exports.Anchor = function(doc, row, column) { **/ this.onChange = function(e) { - function _pointsInOrder(point1, point2, equalPointsInOrder) - { + function _pointsInOrder(point1, point2, equalPointsInOrder) { var bColIsAfter = equalPointsInOrder ? point1.column <= point2.column : point1.column < point2.column; return (point1.row < point2.row) || (point1.row == point2.row && bColIsAfter); } - function getTransformedPoint(delta, point, moveIfEqual) - { + function getTransformedPoint(delta, point, moveIfEqual) { + // Get delta info. var deltaIsInsert = (delta.action == 'insert') var deltaRowShift = (deltaIsInsert ? 1 : -1) * (delta.range.end.row - delta.range.start.row); diff --git a/lib/ace/apply_delta.js b/lib/ace/apply_delta.js index 1317ce98..86bc89c6 100644 --- a/lib/ace/apply_delta.js +++ b/lib/ace/apply_delta.js @@ -49,8 +49,7 @@ function throwDeltaError(delta, errorText){ throw 'Invalid Delta: ' + errorText; } -function positionInDocument(docLines, position) -{ +function positionInDocument(docLines, position) { return position.row >= 0 && position.row < docLines.length && position.column >= 0 && position.column <= docLines[position.row].length; } @@ -93,8 +92,7 @@ exports.applyDelta = function(docLines, delta, doNotValidate) { validateDelta(docLines, delta); // Apply delta. - if (delta.range.start.row == delta.range.end.row) - { + if (delta.range.start.row == delta.range.end.row) { // Apply single-line delta. // Note: The multi-line code below correctly handle single-line // deltas too, but we need to short-circuit for speed. diff --git a/lib/ace/document.js b/lib/ace/document.js index e203d77c..97d69968 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -108,14 +108,15 @@ var Document = function(textOrLines) { **/ // check for IE split bug - if ("aaa".split(/a/).length === 0) + if ("aaa".split(/a/).length === 0) { this.$split = function(text) { return text.replace(/\r\n|\r/g, "\n").split("\n"); }; - else + } else { this.$split = function(text) { return text.split(/\r\n|\r|\n/); }; + } this.$detectNewLine = function(text) { @@ -582,8 +583,8 @@ var Document = function(textOrLines) { // as shown in https://gist.github.com/aldendaniels/8367109#file-document-snippet-js // If we do this, update validateDelta() to limit the number of lines in a delete delta. var bIsInsert = delta.action == 'insert'; - while (bIsInsert && delta.lines.length > 65001) - { + while (bIsInsert && delta.lines.length > 65001) { + // Get split deltas. var lines = delta.lines.splice(0, 65000); lines.push(''); @@ -608,8 +609,7 @@ var Document = function(textOrLines) { * Reverts `delta` from the document. * @param {Object} delta A delta object (can include 'insert' and 'remove' actions) **/ - this.revertDelta = function(delta) - { + this.revertDelta = function(delta) { this.applyDelta({ action: (delta.action == 'insert' ? 'remove' : 'insert'), range: delta.range.clone(), diff --git a/lib/ace/mode/text.js b/lib/ace/mode/text.js index 343fc271..d04fed78 100644 --- a/lib/ace/mode/text.js +++ b/lib/ace/mode/text.js @@ -138,9 +138,9 @@ var Mode = function() { var comment = function(line, i) { if (!ignoreBlankLines || /\S/.test(line)) { if (shouldInsertSpace(line, minIndent, minIndent)) - doc.insert({row: i, column: minIndent}, commentWithSpace); + doc.insertInLine({row: i, column: minIndent}, commentWithSpace); else - doc.insert({row: i, column: minIndent}, lineCommentStart); + doc.insertInLine({row: i, column: minIndent}, lineCommentStart); } }; var testRemove = function(line, i) { diff --git a/lib/ace/placeholder.js b/lib/ace/placeholder.js index df0bfa9e..1d345a24 100644 --- a/lib/ace/placeholder.js +++ b/lib/ace/placeholder.js @@ -161,7 +161,7 @@ var PlaceHolder = function(session, length, pos, others, mainClass, othersClass) var distanceFromStart = range.start.column - this.pos.column; this.length += lengthDiff; if(!this.session.$fromUndo) { - if(delta.action === "insert") { + if(delta.action === 'insert') { for (var i = this.others.length - 1; i >= 0; i--) { var otherPos = this.others[i]; var newPos = {row: otherPos.row, column: otherPos.column + distanceFromStart}; @@ -179,7 +179,7 @@ var PlaceHolder = function(session, length, pos, others, mainClass, othersClass) } } // Special case: insert in beginning - if(range.start.column === this.pos.column && delta.action === "insert") { + if(range.start.column === this.pos.column && delta.action === 'insert') { setTimeout(function() { this.pos.setPosition(this.pos.row, this.pos.column - lengthDiff); for (var i = 0; i < this.others.length; i++) { diff --git a/lib/ace/undomanager.js b/lib/ace/undomanager.js index 4d978d46..209cf3db 100644 --- a/lib/ace/undomanager.js +++ b/lib/ace/undomanager.js @@ -165,10 +165,15 @@ var UndoManager = function() { }; // Serializes deltaSets to reduce memory usage. - this.$serializeDeltas = function(deltaSets) - { - return this.$cloneDeltaSetsObj(deltaSets, $serializeDelta); - } + this.$serializeDeltas = function(deltaSets) { + return cloneDeltaSetsObj(deltaSets, $serializeDelta); + }; + + // Deserializes deltaSets to allow application to the document. + this.$deserializeDeltas = function(deltaSets) { + return cloneDeltaSetsObj(deltaSets, $deserializeDelta); + }; + function $serializeDelta(delta){ return { action: delta.action, @@ -177,13 +182,8 @@ var UndoManager = function() { text: (delta.lines.length == 1 ? delta.lines[0] : null ), }; } - - // Deserializes deltaSets to allow application to the document. - this.$deserializeDeltas = function(deltaSets) - { - return this.$cloneDeltaSetsObj(deltaSets, $deserializeDelta); - } - function $deserializeDelta(delta){ + + function $deserializeDelta(delta) { return { action: delta.action, range: delta.range, @@ -191,17 +191,13 @@ var UndoManager = function() { }; } - // Helper for delta serialization and deserialization. - this.$cloneDeltaSetsObj = function(deltaSets_old, fnGetModifiedDelta) - { + function cloneDeltaSetsObj(deltaSets_old, fnGetModifiedDelta) { var deltaSets_new = new Array(deltaSets_old.length); - for (var i in deltaSets_old) - { + for (var i in deltaSets_old) { var deltaSet_old = deltaSets_old[i]; var deltaSet_new = { group: deltaSet_old.group, deltas: new Array(deltaSet_old.length)}; - for (var i_ in deltaSet_old.deltas) - { + for (var i_ in deltaSet_old.deltas) { var delta_old = deltaSet_old.deltas[i_]; deltaSet_new.deltas[i_] = fnGetModifiedDelta(delta_old); } diff --git a/lib/ace/worker/mirror.js b/lib/ace/worker/mirror.js index d55e84f4..2f33fea6 100644 --- a/lib/ace/worker/mirror.js +++ b/lib/ace/worker/mirror.js @@ -17,8 +17,7 @@ var Mirror = exports.Mirror = function(sender) { // Convert delta.range back into a Range instance since // window.onMessage loses non-primitive data. See http://jsfiddle.net/nqJfw/1/. var deltas = e.data; - for (var i in deltas) - { + for (var i in deltas) { var delta = deltas[i]; delta.range = Range.fromPoints(delta.range.start, delta.range.end); } From 7f1bc7af2fcc431241c2e8812588164394444df9 Mon Sep 17 00:00:00 2001 From: aldendaniels Date: Sat, 11 Jan 2014 15:48:38 -0600 Subject: [PATCH 18/92] Break out Anchor.onChange helper functions This should be faster since we don't have to re-initialize the helper functions each time Anchor.onChange is fired. --- lib/ace/anchor.js | 78 +++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/lib/ace/anchor.js b/lib/ace/anchor.js index 5622bf5c..c9c27dfe 100644 --- a/lib/ace/anchor.js +++ b/lib/ace/anchor.js @@ -100,50 +100,50 @@ var Anchor = exports.Anchor = function(doc, row, column) { * **/ this.onChange = function(e) { + var delta = e.data; + var point = this.$getTransformedPoint(delta, {row: this.row, column: this.column}); + this.setPosition(point.row, point.column, true); + }; + + function $pointsInOrder(point1, point2, equalPointsInOrder) { + var bColIsAfter = equalPointsInOrder ? point1.column <= point2.column : point1.column < point2.column; + return (point1.row < point2.row) || (point1.row == point2.row && bColIsAfter); + } + + this.$getTransformedPoint = function (delta, point) { - function _pointsInOrder(point1, point2, equalPointsInOrder) { - var bColIsAfter = equalPointsInOrder ? point1.column <= point2.column : point1.column < point2.column; - return (point1.row < point2.row) || (point1.row == point2.row && bColIsAfter); - } - - function getTransformedPoint(delta, point, moveIfEqual) { - - // Get delta info. - var deltaIsInsert = (delta.action == 'insert') - var deltaRowShift = (deltaIsInsert ? 1 : -1) * (delta.range.end.row - delta.range.start.row); - var deltaColShift = (deltaIsInsert ? 1 : -1) * (delta.range.end.column - delta.range.start.column); - var deltaStart = delta.range.start; - var deltaEnd = (deltaIsInsert ? deltaStart : delta.range.end); // Collapse insert range. - - // DELTA AFTER POINT: No change needed. - if (_pointsInOrder(point, deltaStart, moveIfEqual)) { - return { - row: point.row, - column: point.column - }; - } - - // DELTA BEFORE POINT: Move point by delta shift. - if (_pointsInOrder(deltaEnd, point, !moveIfEqual)) { - return { - row: point.row + deltaRowShift, - column: point.column + (point.row == deltaEnd.row ? deltaColShift : 0) - }; - } - - // DELTA ENVELOPS POINT (delete only): Move point to delta start. - if (delta.action != 'remove') - throw 'Delete action expected.'; - + // Get delta info. + var moveIfEqual = this.$insertRight; + var deltaIsInsert = (delta.action == 'insert') + var deltaRowShift = (deltaIsInsert ? 1 : -1) * (delta.range.end.row - delta.range.start.row); + var deltaColShift = (deltaIsInsert ? 1 : -1) * (delta.range.end.column - delta.range.start.column); + var deltaStart = delta.range.start; + var deltaEnd = (deltaIsInsert ? deltaStart : delta.range.end); // Collapse insert range. + + // DELTA AFTER POINT: No change needed. + if ($pointsInOrder(point, deltaStart, moveIfEqual)) { return { - row: deltaStart.row, - column: deltaStart.column + row: point.row, + column: point.column }; } - var delta = e.data; - var point = getTransformedPoint(delta, {row: this.row, column: this.column}, this.$insertRight); - this.setPosition(point.row, point.column, true); + // DELTA BEFORE POINT: Move point by delta shift. + if ($pointsInOrder(deltaEnd, point, !moveIfEqual)) { + return { + row: point.row + deltaRowShift, + column: point.column + (point.row == deltaEnd.row ? deltaColShift : 0) + }; + } + + // DELTA ENVELOPS POINT (delete only): Move point to delta start. + if (delta.action != 'remove') + throw 'Delete action expected.'; + + return { + row: deltaStart.row, + column: deltaStart.column + }; }; /** From 4299db01bdb4b6a578e8ed678fe82c32457d7601 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sat, 18 Jan 2014 17:24:11 +0400 Subject: [PATCH 19/92] remove delta.range --- lib/ace/anchor.js | 17 ++-- lib/ace/apply_delta.js | 65 ++++++++------- lib/ace/background_tokenizer.js | 7 +- lib/ace/commands/command_manager.js | 4 +- lib/ace/document.js | 107 ++++++++++++------------- lib/ace/edit_session.js | 33 ++++---- lib/ace/edit_session/folding.js | 5 +- lib/ace/editor.js | 7 +- lib/ace/ext/elastic_tabstops_lite.js | 2 +- lib/ace/keyboard/vim/maps/operators.js | 2 - lib/ace/layer/gutter.js | 5 +- lib/ace/lib/lang.js | 1 - lib/ace/line_widgets.js | 5 +- lib/ace/undomanager.js | 20 ++--- lib/ace/worker/mirror.js | 11 +-- 15 files changed, 136 insertions(+), 155 deletions(-) diff --git a/lib/ace/anchor.js b/lib/ace/anchor.js index 421f9e5f..4511a4ce 100644 --- a/lib/ace/anchor.js +++ b/lib/ace/anchor.js @@ -111,14 +111,13 @@ var Anchor = exports.Anchor = function(doc, row, column) { } this.$getTransformedPoint = function (delta, point) { - // Get delta info. var moveIfEqual = this.$insertRight; - var deltaIsInsert = (delta.action == 'insert') - var deltaRowShift = (deltaIsInsert ? 1 : -1) * (delta.range.end.row - delta.range.start.row); - var deltaColShift = (deltaIsInsert ? 1 : -1) * (delta.range.end.column - delta.range.start.column); - var deltaStart = delta.range.start; - var deltaEnd = (deltaIsInsert ? deltaStart : delta.range.end); // Collapse insert range. + var deltaIsInsert = (delta.action == "insert") + var deltaRowShift = (deltaIsInsert ? 1 : -1) * (delta.end.row - delta.start.row); + var deltaColShift = (deltaIsInsert ? 1 : -1) * (delta.end.column - delta.start.column); + var deltaStart = delta.start; + var deltaEnd = (deltaIsInsert ? deltaStart : delta.end); // Collapse insert range. // DELTA AFTER POINT: No change needed. if ($pointsInOrder(point, deltaStart, moveIfEqual)) { @@ -137,8 +136,8 @@ var Anchor = exports.Anchor = function(doc, row, column) { } // DELTA ENVELOPS POINT (delete only): Move point to delta start. - if (delta.action != 'remove') - throw 'Delete action expected.'; + if (delta.action != "remove") + throw "Delete action expected."; return { row: deltaStart.row, @@ -181,7 +180,7 @@ var Anchor = exports.Anchor = function(doc, row, column) { }; /** - * When called, the `'change'` event listener is removed. + * When called, the `"change"` event listener is removed. * **/ this.detach = function() { diff --git a/lib/ace/apply_delta.js b/lib/ace/apply_delta.js index 86bc89c6..3996920c 100644 --- a/lib/ace/apply_delta.js +++ b/lib/ace/apply_delta.js @@ -41,12 +41,12 @@ function splitLine (docLines, position) { function joinLineWithNext(docLines, row) { docLines[row] += docLines[row + 1]; - docLines.splice(row + 1, 1); + docLines.splice(row + 1, 1); } function throwDeltaError(delta, errorText){ - console.log('Invalid Delta:', delta); - throw 'Invalid Delta: ' + errorText; + console.log("Invalid Delta:", delta); + throw "Invalid Delta: " + errorText; } function positionInDocument(docLines, position) { @@ -57,32 +57,32 @@ function positionInDocument(docLines, position) { function validateDelta(docLines, delta) { // Validate action string. - if (delta.action != 'insert' && delta.action != 'remove') - throwDeltaError(delta, 'delta.action must be "insert" or "remove"'); + if (delta.action != "insert" && delta.action != "remove") + throwDeltaError(delta, "delta.action must be 'insert' or 'remove'"); // Validate lines type. if (!(delta.lines instanceof Array)) - throwDeltaError(delta, 'delta.lines must be an Array'); + throwDeltaError(delta, "delta.lines must be an Array"); // Validate range type. - if (!(delta.range instanceof Range)) - throwDeltaError(delta, 'delta.range must be an instance of the Range class'); + if (!delta.start || !delta.end) + throwDeltaError(delta, "delta.start/end must be an present"); // Validate that the start point is contained in the document. - var start = delta.range.start; - if (!positionInDocument(docLines, delta.range.start)) - throwDeltaError(delta, 'delta.range.start must be contained in document'); + var start = delta.start; + if (!positionInDocument(docLines, delta.start)) + throwDeltaError(delta, "delta.start must be contained in document"); // Validate that the end point is contained in the document (remove deltas only). - var end = delta.range.end; - if (delta.action == 'remove' && !positionInDocument(docLines, end)) - throwDeltaError(delta, 'delta.range.end must contained in document for "remove" actions'); + var end = delta.end; + if (delta.action == "remove" && !positionInDocument(docLines, end)) + throwDeltaError(delta, "delta.end must contained in document for 'remove' actions"); // Validate that the .range size matches the .lines size. var numRangeRows = end.row - start.row; var numRangeLastLineChars = (end.column - (numRangeRows == 0 ? start.column : 0)); if (numRangeRows != delta.lines.length - 1 || delta.lines[numRangeRows].length != numRangeLastLineChars) - throwDeltaError(delta, 'delta.range must match delta lines'); + throwDeltaError(delta, "delta.range must match delta lines"); } exports.applyDelta = function(docLines, delta, doNotValidate) { @@ -92,21 +92,21 @@ exports.applyDelta = function(docLines, delta, doNotValidate) { validateDelta(docLines, delta); // Apply delta. - if (delta.range.start.row == delta.range.end.row) { + if (delta.start.row == delta.end.row) { // Apply single-line delta. // Note: The multi-line code below correctly handle single-line // deltas too, but we need to short-circuit for speed. - var row = delta.range.start.row; - var startColumn = delta.range.start.column; - var endColumn = delta.range.end.column; + var row = delta.start.row; + var startColumn = delta.start.column; + var endColumn = delta.end.column; var line = docLines[row]; switch (delta.action) { - case 'insert': + case "insert": docLines[row] = line.substring(0, startColumn) + delta.lines[0] + line.substring(startColumn); break; - case 'remove': + case "remove": docLines[row] = line.substring(0, startColumn) + line.substring(endColumn); break; } @@ -114,22 +114,21 @@ exports.applyDelta = function(docLines, delta, doNotValidate) { // Apply multi-line delta. switch (delta.action) { - - case 'insert': - splitLine(docLines, delta.range.start); - docLines.splice.apply(docLines, [delta.range.start.row + 1, 0].concat(delta.lines)); - joinLineWithNext(docLines, delta.range.start.row); - joinLineWithNext(docLines, delta.range.end.row); + case "insert": + splitLine(docLines, delta.start); + docLines.splice.apply(docLines, [delta.start.row + 1, 0].concat(delta.lines)); + joinLineWithNext(docLines, delta.start.row); + joinLineWithNext(docLines, delta.end.row); break; - case 'remove': - splitLine(docLines, delta.range.end); - splitLine(docLines, delta.range.start); + case "remove": + splitLine(docLines, delta.end); + splitLine(docLines, delta.start); docLines.splice( - delta.range.start.row + 1, // Where to start deleting - delta.range.end.row - delta.range.start.row + 1 // Num lines to delete. + delta.start.row + 1, // Where to start deleting + delta.end.row - delta.start.row + 1 // Num lines to delete. ); - joinLineWithNext(docLines, delta.range.start.row); + joinLineWithNext(docLines, delta.start.row); break; } } diff --git a/lib/ace/background_tokenizer.js b/lib/ace/background_tokenizer.js index 81ea4a0f..07862188 100644 --- a/lib/ace/background_tokenizer.js +++ b/lib/ace/background_tokenizer.js @@ -177,13 +177,12 @@ var BackgroundTokenizer = function(tokenizer, editor) { } this.$updateOnChange = function(delta) { - var range = delta.range; - var startRow = range.start.row; - var len = range.end.row - startRow; + var startRow = delta.start.row; + var len = delta.end.row - startRow; if (len === 0) { this.lines[startRow] = null; - } else if (delta.action == 'remove') { + } else if (delta.action == "remove") { this.lines.splice(startRow, len + 1, null); this.states.splice(startRow, len + 1, null); } else { diff --git a/lib/ace/commands/command_manager.js b/lib/ace/commands/command_manager.js index 72a9942d..537942ab 100644 --- a/lib/ace/commands/command_manager.js +++ b/lib/ace/commands/command_manager.js @@ -13,7 +13,7 @@ var EventEmitter = require("../lib/event_emitter").EventEmitter; /** * new CommandManager(platform, commands) - * @param {String} platform Identifier for the platform; must be either `'mac'` or `'win'` + * @param {String} platform Identifier for the platform; must be either `"mac"` or `"win"` * @param {Array} commands A list of commands * **/ @@ -33,7 +33,7 @@ oop.inherits(CommandManager, HashHandler); oop.implement(this, EventEmitter); this.exec = function(command, editor, args) { - if (typeof command === 'string') + if (typeof command === "string") command = this.commands[command]; if (!command) diff --git a/lib/ace/document.js b/lib/ace/document.js index 97d69968..43d1a73c 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -53,7 +53,7 @@ var Anchor = require("./anchor").Anchor; **/ var Document = function(textOrLines) { - this.$lines = ['']; + this.$lines = [""]; // There has to be one line at least in the document. If you pass an empty // string to the insert function, nothing will happen. Workaround. @@ -99,7 +99,7 @@ var Document = function(textOrLines) { }; /** - * Splits a string of text on any newline (`\n`) or carriage-return ('\r') characters. + * Splits a string of text on any newline (`\n`) or carriage-return (`\r`) characters. * * @method $split * @param {String} text The text to work with @@ -225,12 +225,9 @@ var Document = function(textOrLines) { this.getLinesForRange = function(range) { var lines; if (range.start.row == range.end.row) { - // Handle a single-line range. - lines = [this.getLine(range.start.row).substring(range.start.column, range.end.column)]; - + lines = [this.getLine(range.start.row).substring(range.start.column, range.end.column)]; } else { - // Handle a multi-line range. lines = this.getLines(range.start.row, range.end.row); lines[0] = (lines[0] || "").substring(range.start.column); @@ -254,7 +251,6 @@ var Document = function(textOrLines) { }; this.$getClippedRange = function(range) { - // Get Range object. if (!range instanceof Range) range = Range.fromPoints(range.start, range.end); @@ -267,16 +263,16 @@ var Document = function(textOrLines) { // Deprecated methods retained for backwards compatibility. this.insertLines = function(row, lines) { - console.warn('Use of document.insertLines is deprecated. Use the insertFullLines method instead.'); + console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."); return this.insertFullLines(row, lines); }; this.removeLines = function(firstRow, lastRow) { - console.warn('Use of document.removeLines is deprecated. Use the removeFullLines method instead.'); + console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."); return this.removeFullLines(firstRow, lastRow); }; this.insertNewLine = function(position) { - console.warn('Use of document.insertNewLine is deprecated. Use insertMergedLines(position, [\'\', \'\']) instead.'); - return this.insertMergedLines(position, ['', '']); + console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, [\'\', \'\']) instead."); + return this.insertMergedLines(position, ["", ""]); }; /** @@ -287,7 +283,6 @@ var Document = function(textOrLines) { * **/ this.insert = function(position, text) { - // Only detect new lines if the document has no line break yet. if (this.getLength() <= 1) this.$detectNewLine(text); @@ -296,7 +291,7 @@ var Document = function(textOrLines) { }; /** - * Inserts `text` into the `position` at the current row. This method also triggers the `'change'` event. + * Inserts `text` into the `position` at the current row. This method also triggers the `"change"` event. * * This differs from the `insert` method in two ways: * 1. This does NOT handle newline characters (single-line text only). @@ -310,7 +305,6 @@ var Document = function(textOrLines) { * ``` **/ this.insertInLine = function(position, text) { - // Calculate insertion range end point. this.$clipPosition(position); var endPoint = { @@ -318,10 +312,12 @@ var Document = function(textOrLines) { column : position.column + text.length }; + var range = Range.fromPoints(position, endPoint); // Apply delta (emits change). this.applyDelta({ action: "insert", - range: Range.fromPoints(position, endPoint), + start: range.start, + end: range.end, lines: [text] }, true /*doNotValidate*/); @@ -346,7 +342,7 @@ var Document = function(textOrLines) { **/ /** - * Inserts the elements in `lines` into the document as full lines (does not merge with existing line), starting at the row index given by `row`. This method also triggers the `'change'` event. + * Inserts the elements in `lines` into the document as full lines (does not merge with existing line), starting at the row index given by `row`. This method also triggers the `"change"` event. * @param {Number} row The index of the row to insert at * @param {Array} lines An array of strings * @returns {Object} Contains the final row and column, like this: @@ -368,11 +364,11 @@ var Document = function(textOrLines) { var column = 0; if (row < this.getLength()) { // Insert before the specified row. - lines = lines.concat(['']); + lines = lines.concat([""]); column = 0; } else { // Insert after the last row in the document. - lines = [''].concat(lines); + lines = [""].concat(lines); row--; column = this.$lines[row].length; } @@ -382,7 +378,7 @@ var Document = function(textOrLines) { }; /** - * Inserts the elements in `lines` into the document, starting at the position index given by `row`. This method also triggers the `'change'` event. + * Inserts the elements in `lines` into the document, starting at the position index given by `row`. This method also triggers the `"change"` event. * @param {Number} row The index of the row to insert at * @param {Array} lines An array of strings * @returns {Object} Contains the final row and column, like this: @@ -396,7 +392,6 @@ var Document = function(textOrLines) { * **/ this.insertMergedLines = function(position, lines) { - // Calculate insertion range end point. this.$clipPosition(position); var endPoint = { @@ -407,7 +402,8 @@ var Document = function(textOrLines) { // Apply delta (emits change). this.applyDelta({ action: "insert", - range: Range.fromPoints(position, endPoint), + start: position, + end: endPoint, lines: lines }); @@ -421,19 +417,19 @@ var Document = function(textOrLines) { * **/ this.remove = function(range) { - // Apply delta (emits change). range = this.$getClippedRange(range); this.applyDelta({ - action: 'remove', - range: range, + action: "remove", + start: range.start, + end: range.end, lines: this.getLinesForRange(range), }); - return range.start; + return range.start; }; /** - * Removes the specified columns from the `row`. This method also triggers the `'change'` event. + * Removes the specified columns from the `row`. This method also triggers the `"change"` event. * @param {Number} row The row to remove from * @param {Number} startColumn The column to start removing at * @param {Number} endColumn The column to stop removing at @@ -441,15 +437,15 @@ var Document = function(textOrLines) { * **/ this.removeInLine = function(row, startColumn, endColumn) { - // Calculate deleteion range. var range = new Range(row, startColumn, row, endColumn); range = this.$getClippedRange(range); // Apply delta (emits change). this.applyDelta({ - action: 'remove', - range: range, + action: "remove", + start: range.start, + end: range.end, lines: this.getLinesForRange(range), }, true /*doNotValidate*/); @@ -457,14 +453,13 @@ var Document = function(textOrLines) { }; /** - * Removes a range of full lines. This method also triggers the `'change'` event. + * Removes a range of full lines. This method also triggers the `"change"` event. * @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.removeFullLines = function(firstRow, lastRow) { - // Clip to document. firstRow = Math.min(Math.max(0, firstRow), this.getLength() - 1); lastRow = Math.min(Math.max(0, lastRow ), this.getLength() - 1); @@ -485,8 +480,9 @@ var Document = function(textOrLines) { // Apply delta (emits change). this.applyDelta({ - action: 'remove', - range: range, + action: "remove", + start: range.start, + end: range.end, lines: this.getLinesForRange(range) }); @@ -495,18 +491,19 @@ var Document = function(textOrLines) { }; /** - * Removes the new line between `row` and the row immediately following it. This method also triggers the `'change'` event. + * Removes the new line between `row` and the row immediately following it. This method also triggers the `"change"` event. * @param {Number} row The row to check * **/ this.removeNewLine = function(row) { - if (row < this.getLength() - 1 && row >= 0) { + var range = new Range(row, this.getLine(row).length, row + 1, 0); // Apply delta (emits change). this.applyDelta({ - action: 'remove', - range: new Range(row, this.getLine(row).length, row + 1, 0), - lines: ['', ''] + action: "remove", + start: range.start, + end: range.end, + lines: ["", ""] }); } }; @@ -546,7 +543,7 @@ var Document = function(textOrLines) { /** * Applies all changes in `deltas` to the document. - * @param {Array} deltas An array of delta objects (can include 'insert' and 'remove' actions) + * @param {Array} deltas An array of delta objects (can include "insert" and "remove" actions) **/ this.applyDeltas = function(deltas) { for (var i=0; i=0; i--) { @@ -566,12 +563,11 @@ var Document = function(textOrLines) { /** * Applies `delta` to the document. - * @param {Object} delta A delta object (can include 'insert' and 'remove' actions) + * @param {Object} delta A delta object (can include "insert" and "remove" actions) **/ this.applyDelta = function(delta, doNotValidate) { - // An empty range is a NOOP. - if (delta.range.isEmpty()) + if (!Range.comparePoints(delta.start, delta.end)) return; // Split large insert deltas. This is necessary because: @@ -582,37 +578,40 @@ var Document = function(textOrLines) { // delta handling is too slow. If we make delete delta handling faster we can split all large deltas // as shown in https://gist.github.com/aldendaniels/8367109#file-document-snippet-js // If we do this, update validateDelta() to limit the number of lines in a delete delta. - var bIsInsert = delta.action == 'insert'; + var bIsInsert = delta.action == "insert"; while (bIsInsert && delta.lines.length > 65001) { - // Get split deltas. var lines = delta.lines.splice(0, 65000); - lines.push(''); + lines.push(""); + var range = new Range(delta.start.row, delta.start.column, + delta.start.row + 65000, 0) this.applyDelta({ action: delta.action, lines: lines, - range: new Range(delta.range.start.row, delta.range.start.column, - delta.range.start.row + 65000, 0) + start: range.start, + end: range.end, }); // Update remaining delta. - delta.range.start.row += 65000; - delta.range.start.column = 0; + delta.start.row += 65000; + delta.start.column = 0; } // Apply. applyDelta(this.$lines, delta, doNotValidate); - this._emit("change", { data: delta }); + this._emit("change", { data: delta }); }; /** * Reverts `delta` from the document. - * @param {Object} delta A delta object (can include 'insert' and 'remove' actions) + * @param {Object} delta A delta object (can include "insert" and "remove" actions) **/ this.revertDelta = function(delta) { + var range = Range.fromPoints(delta.start, delta.end); this.applyDelta({ - action: (delta.action == 'insert' ? 'remove' : 'insert'), - range: delta.range.clone(), + action: (delta.action == "insert" ? "remove" : "insert"), + start: range.start, + end: range.end, lines: delta.lines.slice() }); }; diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 7cfa344c..4baa525f 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -253,7 +253,7 @@ var EditSession = function(text, mode) { var delta = e.data; this.$modified = true; - this.$resetRowCache(delta.range.start.row); + this.$resetRowCache(delta.start.row); var removedFolds = this.$updateInternalDataOnChange(e); if (!this.$fromUndo && this.$undoManager && !delta.ignore) { @@ -1247,30 +1247,29 @@ var EditSession = function(text, mode) { var range, point; var lastDeltaIsInsert = false; if (isInsert(delta)) { - range = Range.fromPoints(delta.range.start, delta.range.end); + range = Range.fromPoints(delta.start, delta.end); lastDeltaIsInsert = true; } else { - range = Range.fromPoints(delta.range.start, delta.range.start); + range = Range.fromPoints(delta.start, delta.start); lastDeltaIsInsert = false; } for (var i = 1; i < deltas.length; i++) { delta = deltas[i]; if (isInsert(delta)) { - point = delta.range.start; + point = delta.start; if (range.compare(point.row, point.column) == -1) { - range.setStart(delta.range.start); + range.setStart(point); } - point = delta.range.end; + point = delta.end; if (range.compare(point.row, point.column) == 1) { - range.setEnd(delta.range.end); + range.setEnd(point); } lastDeltaIsInsert = true; } else { - point = delta.range.start; + point = delta.start; if (range.compare(point.row, point.column) == -1) { - range = - Range.fromPoints(delta.range.start, delta.range.start); + range = Range.fromPoints(delta.start, delta.start); } lastDeltaIsInsert = false; } @@ -1676,8 +1675,8 @@ var EditSession = function(text, mode) { this.$updateInternalDataOnChange = function(e) { var useWrapMode = this.$useWrapMode; var action = e.data.action; - var start = e.data.range.start; - var end = e.data.range.end; + var start = e.data.start; + var end = e.data.end; var firstRow = start.row; var lastRow = end.row; var len = lastRow - firstRow; @@ -1685,11 +1684,11 @@ var EditSession = function(text, mode) { this.$updating = true; if (len != 0) { - if (action == 'remove') { + if (action == "remove") { this[useWrapMode ? "$wrapData" : "$rowLengthCache"].splice(firstRow, len); var foldLines = this.$foldData; - removedFolds = this.getFoldsInRange(e.data.range); + removedFolds = this.getFoldsInRange(e.data); this.removeFolds(removedFolds); var foldLine = this.getFoldLine(end.row); @@ -1759,10 +1758,10 @@ var EditSession = function(text, mode) { } else { // Realign folds. E.g. if you add some new chars before a fold, the // fold should "move" to the right. - len = Math.abs(e.data.range.start.column - e.data.range.end.column); - if (action == 'remove') { + len = Math.abs(e.data.start.column - e.data.end.column); + if (action == "remove") { // Get all the folds in the change range and remove them. - removedFolds = this.getFoldsInRange(e.data.range); + removedFolds = this.getFoldsInRange(e.data); this.removeFolds(removedFolds); len = -len; diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js index 25adc909..857382d8 100644 --- a/lib/ace/edit_session/folding.js +++ b/lib/ace/edit_session/folding.js @@ -830,9 +830,8 @@ function Folding() { this.updateFoldWidgets = function(e) { var delta = e.data; - var range = delta.range; - var firstRow = range.start.row; - var len = range.end.row - firstRow; + var firstRow = delta.start.row; + var len = delta.end.row - firstRow; if (len === 0) { this.foldWidgets[firstRow] = null; diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 6b046a89..16d79785 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -607,11 +607,10 @@ var Editor = function(renderer, session) { * **/ this.onDocumentChange = function(e) { - // Rerender and emit "change" event. - var range = e.data.range; - var lastRow = (range.start.row == range.end.row ? range.end.row : Infinity); - this.renderer.updateLines(range.start.row, lastRow); + var delta = e.data; + var lastRow = (delta.start.row == delta.end.row ? delta.end.row : Infinity); + this.renderer.updateLines(delta.start.row, lastRow); this._signal("change", e); // Update cursor because tab characters can influence the cursor position. diff --git a/lib/ace/ext/elastic_tabstops_lite.js b/lib/ace/ext/elastic_tabstops_lite.js index 9901c5df..ce5e55f9 100644 --- a/lib/ace/ext/elastic_tabstops_lite.js +++ b/lib/ace/ext/elastic_tabstops_lite.js @@ -45,7 +45,7 @@ var ElasticTabstopsLite = function(editor) { recordChanges = true; }; this.onChange = function(e) { - var range = e.data.range + var range = e.data; if (recordChanges) { if (changedRows.indexOf(range.start.row) == -1) changedRows.push(range.start.row); diff --git a/lib/ace/keyboard/vim/maps/operators.js b/lib/ace/keyboard/vim/maps/operators.js index 067562a0..73c6922e 100644 --- a/lib/ace/keyboard/vim/maps/operators.js +++ b/lib/ace/keyboard/vim/maps/operators.js @@ -94,11 +94,9 @@ module.exports = { editor.removeLines(); util.insertMode(editor); } - break; default: if (range) { - // range.end.column ++; editor.session.remove(range); util.insertMode(editor); diff --git a/lib/ace/layer/gutter.js b/lib/ace/layer/gutter.js index 45c34565..59aa35fd 100644 --- a/lib/ace/layer/gutter.js +++ b/lib/ace/layer/gutter.js @@ -103,9 +103,8 @@ var Gutter = function(parentEl) { if (!this.$annotations.length) return; var delta = e.data; - var range = delta.range; - var firstRow = range.start.row; - var len = range.end.row - firstRow; + var firstRow = delta.start.row; + var len = delta.end.row - firstRow; if (len === 0) { // do nothing } else if (delta.action == 'remove') { diff --git a/lib/ace/lib/lang.js b/lib/ace/lib/lang.js index d6a98149..863bbb55 100644 --- a/lib/ace/lib/lang.js +++ b/lib/ace/lib/lang.js @@ -150,7 +150,6 @@ exports.getMatchOffsets = function(string, regExp) { /* deprecated */ exports.deferredCall = function(fcn) { - var timer = null; var callback = function() { timer = null; diff --git a/lib/ace/line_widgets.js b/lib/ace/line_widgets.js index 651f038d..185f6de4 100644 --- a/lib/ace/line_widgets.js +++ b/lib/ace/line_widgets.js @@ -119,9 +119,8 @@ function LineWidgets(session) { if (!lineWidgets) return; var delta = e.data; - var range = delta.range; - var startRow = range.start.row; - var len = range.end.row - startRow; + var startRow = delta.start.row; + var len = delta.end.row - startRow; if (len === 0) { // return diff --git a/lib/ace/undomanager.js b/lib/ace/undomanager.js index bd95bae9..71b4ba65 100644 --- a/lib/ace/undomanager.js +++ b/lib/ace/undomanager.js @@ -178,29 +178,31 @@ var UndoManager = function() { function $serializeDelta(delta){ return { action: delta.action, - range: delta.range, - lines: (delta.lines.length == 1 ? null : delta.lines), - text: (delta.lines.length == 1 ? delta.lines[0] : null ), + start: delta.start, + end: delta.end, + lines: delta.lines.length == 1 ? null : delta.lines, + text: delta.lines.length == 1 ? delta.lines[0] : null, }; } function $deserializeDelta(delta) { return { action: delta.action, - range: delta.range, - lines: (delta.text === null ? delta.lines : [delta.text]) + start: delta.start, + end: delta.end, + lines: delta.lines || [delta.text] }; } function cloneDeltaSetsObj(deltaSets_old, fnGetModifiedDelta) { var deltaSets_new = new Array(deltaSets_old.length); - for (var i in deltaSets_old) { + for (var i = 0; i < deltaSets_old.length; i++) { var deltaSet_old = deltaSets_old[i]; var deltaSet_new = { group: deltaSet_old.group, deltas: new Array(deltaSet_old.length)}; - for (var i_ in deltaSet_old.deltas) { - var delta_old = deltaSet_old.deltas[i_]; - deltaSet_new.deltas[i_] = fnGetModifiedDelta(delta_old); + for (var j = 0; j < deltaSet_old.deltas.length; j++) { + var delta_old = deltaSet_old.deltas[j]; + deltaSet_new.deltas[j] = fnGetModifiedDelta(delta_old); } deltaSets_new[i] = deltaSet_new; diff --git a/lib/ace/worker/mirror.js b/lib/ace/worker/mirror.js index 2f33fea6..8254f205 100644 --- a/lib/ace/worker/mirror.js +++ b/lib/ace/worker/mirror.js @@ -13,16 +13,7 @@ var Mirror = exports.Mirror = function(sender) { var _self = this; sender.on("change", function(e) { - - // Convert delta.range back into a Range instance since - // window.onMessage loses non-primitive data. See http://jsfiddle.net/nqJfw/1/. - var deltas = e.data; - for (var i in deltas) { - var delta = deltas[i]; - delta.range = Range.fromPoints(delta.range.start, delta.range.end); - } - - doc.applyDeltas(deltas); + doc.applyDeltas(e.data); if (_self.$timeout) return deferredUpdate.schedule(_self.$timeout); _self.onUpdate(); From c8d1df203ed8772805369c331f7c160f04af4bfc Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 19 Jan 2014 04:26:02 +0400 Subject: [PATCH 20/92] restore single line delta optimization in anchor.js --- lib/ace/anchor.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/ace/anchor.js b/lib/ace/anchor.js index 4511a4ce..3431b3c4 100644 --- a/lib/ace/anchor.js +++ b/lib/ace/anchor.js @@ -101,6 +101,12 @@ var Anchor = exports.Anchor = function(doc, row, column) { **/ this.onChange = function(e) { var delta = e.data; + if (delta.start.row == delta.end.row && delta.start.row != this.row) + return; + + if (delta.start.row > this.row) + return; + var point = this.$getTransformedPoint(delta, {row: this.row, column: this.column}); this.setPosition(point.row, point.column, true); }; From 46f3d77068efbf8d1bfc3e0b3fb840c0bb90630d Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 19 Jan 2014 04:27:07 +0400 Subject: [PATCH 21/92] simplify apply_delta.js --- lib/ace/apply_delta.js | 45 +++++++++++++----------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/lib/ace/apply_delta.js b/lib/ace/apply_delta.js index 3996920c..ed019ccb 100644 --- a/lib/ace/apply_delta.js +++ b/lib/ace/apply_delta.js @@ -31,19 +31,6 @@ define(function(require, exports, module) { "use strict"; -var Range = require("./range").Range; - -function splitLine (docLines, position) { - var text = docLines[position.row]; - docLines[position.row] = text.slice(0, position.column); - docLines.splice(position.row + 1, 0, text.slice(position.column)); -} - -function joinLineWithNext(docLines, row) { - docLines[row] += docLines[row + 1]; - docLines.splice(row + 1, 1); -} - function throwDeltaError(delta, errorText){ console.log("Invalid Delta:", delta); throw "Invalid Delta: " + errorText; @@ -91,44 +78,40 @@ exports.applyDelta = function(docLines, delta, doNotValidate) { if (!doNotValidate) validateDelta(docLines, delta); + var row = delta.start.row; + var startColumn = delta.start.column; + var line = docLines[row]; // Apply delta. - if (delta.start.row == delta.end.row) { + if (row == delta.end.row) { // Apply single-line delta. // Note: The multi-line code below correctly handle single-line // deltas too, but we need to short-circuit for speed. - var row = delta.start.row; - var startColumn = delta.start.column; - var endColumn = delta.end.column; - var line = docLines[row]; + var endColumn = delta.end.column; switch (delta.action) { - case "insert": docLines[row] = line.substring(0, startColumn) + delta.lines[0] + line.substring(startColumn); break; - case "remove": docLines[row] = line.substring(0, startColumn) + line.substring(endColumn); break; } } else { - // Apply multi-line delta. switch (delta.action) { case "insert": - splitLine(docLines, delta.start); - docLines.splice.apply(docLines, [delta.start.row + 1, 0].concat(delta.lines)); - joinLineWithNext(docLines, delta.start.row); - joinLineWithNext(docLines, delta.end.row); + var line = docLines[row]; + var args = [row, 1].concat(delta.lines); + docLines.splice.apply(docLines, args); + docLines[row] = line.substring(0, startColumn) + docLines[row]; + docLines[row + delta.lines.length - 1] += line.substring(startColumn); break; - case "remove": - splitLine(docLines, delta.end); - splitLine(docLines, delta.start); + var endRow docLines.splice( - delta.start.row + 1, // Where to start deleting - delta.end.row - delta.start.row + 1 // Num lines to delete. + row, // Where to start deleting + delta.end.row - delta.start.row + 1, // Num lines to delete. + line.substring(0, startColumn) + docLines[delta.end.row].substring(delta.end.column) ); - joinLineWithNext(docLines, delta.start.row); break; } } From 6b60bcbbd672aef1211ddcb58ca21225b3dba369 Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 14 Feb 2014 22:35:22 +0400 Subject: [PATCH 22/92] continue refactoring --- lib/ace/apply_delta.js | 45 ++++------ lib/ace/document.js | 155 ++++++++++++++++---------------- lib/ace/edit_session.js | 24 ++--- lib/ace/range_list.js | 12 +-- lib/ace/undomanager.js | 7 +- lib/ace/worker/mirror.js | 14 ++- lib/ace/worker/worker_client.js | 12 ++- 7 files changed, 135 insertions(+), 134 deletions(-) diff --git a/lib/ace/apply_delta.js b/lib/ace/apply_delta.js index ed019ccb..98a1a857 100644 --- a/lib/ace/apply_delta.js +++ b/lib/ace/apply_delta.js @@ -42,7 +42,6 @@ function positionInDocument(docLines, position) { } function validateDelta(docLines, delta) { - // Validate action string. if (delta.action != "insert" && delta.action != "remove") throwDeltaError(delta, "delta.action must be 'insert' or 'remove'"); @@ -73,47 +72,37 @@ function validateDelta(docLines, delta) { } exports.applyDelta = function(docLines, delta, doNotValidate) { - - // Validate delta. if (!doNotValidate) validateDelta(docLines, delta); var row = delta.start.row; var startColumn = delta.start.column; var line = docLines[row]; - // Apply delta. - if (row == delta.end.row) { - // Apply single-line delta. - // Note: The multi-line code below correctly handle single-line - // deltas too, but we need to short-circuit for speed. - var endColumn = delta.end.column; - switch (delta.action) { - case "insert": + switch (delta.action) { + case "insert": + var lines = delta.lines; + if (lines.length === 1) { docLines[row] = line.substring(0, startColumn) + delta.lines[0] + line.substring(startColumn); - break; - case "remove": - docLines[row] = line.substring(0, startColumn) + line.substring(endColumn); - break; - } - } else { - // Apply multi-line delta. - switch (delta.action) { - case "insert": + } else { var line = docLines[row]; var args = [row, 1].concat(delta.lines); docLines.splice.apply(docLines, args); docLines[row] = line.substring(0, startColumn) + docLines[row]; docLines[row + delta.lines.length - 1] += line.substring(startColumn); - break; - case "remove": - var endRow + } + break; + case "remove": + var endColumn = delta.end.column; + var endRow = delta.end.row; + if (row === endRow) { + docLines[row] = line.substring(0, startColumn) + line.substring(endColumn); + } else { docLines.splice( - row, // Where to start deleting - delta.end.row - delta.start.row + 1, // Num lines to delete. - line.substring(0, startColumn) + docLines[delta.end.row].substring(delta.end.column) + row, endRow - row + 1, + line.substring(0, startColumn) + docLines[endRow].substring(endColumn) ); - break; - } + } + break; } } }); diff --git a/lib/ace/document.js b/lib/ace/document.js index 43d1a73c..1abb95a2 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -39,13 +39,12 @@ var Anchor = require("./anchor").Anchor; /** * Contains the text of the document. Document can be attached to several [[EditSession `EditSession`]]s. - * * At its core, `Document`s are just an array of strings, with each row in the document matching up to the array index. * * @class Document **/ - /** +/** * * Creates a new `Document`. If `text` is included, the `Document` contains those strings; otherwise, it's empty. * @param {String | Array} text The starting text @@ -76,9 +75,9 @@ var Document = function(textOrLines) { * @param {String} text The text to use **/ this.setValue = function(text) { - var len = this.getLength(); - this.remove(new Range(0, 0, len, this.getLine(len-1).length)); - this.insert({row: 0, column:0}, text); + var len = this.getLength() - 1; + this.remove(new Range(0, 0, len, this.getLine(len).length)); + this.insert({row: 0, column: 0}, text); }; /** @@ -224,7 +223,7 @@ var Document = function(textOrLines) { **/ this.getLinesForRange = function(range) { var lines; - if (range.start.row == range.end.row) { + if (range.start.row === range.end.row) { // Handle a single-line range. lines = [this.getLine(range.start.row).substring(range.start.column, range.end.column)]; } else { @@ -238,29 +237,6 @@ var Document = function(textOrLines) { return lines; }; - this.$clipPosition = function(position) { - var length = this.getLength(); - if (position.row >= length) { - position.row = Math.max(0, length - 1); - position.column = this.getLine(length - 1).length; - } else { - position.row = Math.max(0, position.row); - position.column = Math.min(Math.max(position.column, 0), this.getLine(position.row).length); - } - return position; - }; - - this.$getClippedRange = function(range) { - // Get Range object. - if (!range instanceof Range) - range = Range.fromPoints(range.start, range.end); - - // Return clipped range. - this.$clipPosition(range.start); - this.$clipPosition(range.end); - return range; - }; - // Deprecated methods retained for backwards compatibility. this.insertLines = function(row, lines) { console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."); @@ -305,25 +281,54 @@ var Document = function(textOrLines) { * ``` **/ this.insertInLine = function(position, text) { - // Calculate insertion range end point. - this.$clipPosition(position); - var endPoint = { - row : position.row, - column : position.column + text.length - }; + var start = this.clippedPos(position.row, position.column); + var end = this.pos(position.row, position.column + text.length); - var range = Range.fromPoints(position, endPoint); - // Apply delta (emits change). this.applyDelta({ action: "insert", - start: range.start, - end: range.end, + start: start, + end: end, lines: [text] - }, true /*doNotValidate*/); + }, true); - return endPoint; + return this.clonePos(end); }; + this.clippedPos = function(row, column) { + var length = this.getLength(); + if (row === undefined) { + row = length; + } else if (row < 0) { + row = 0; + } else if (row >= length) { + row = length - 1; + column = undefined + } + var line = this.getLine(row); + column = Math.min(Math.max(column, 0), line.length); + return {row: row, column: column}; + }; + + this.clonePos = function(pos) { + return {row: pos.row, column: pos.column}; + }; + + this.pos = function(row, column) { + return {row: row, column: column}; + }; + + this.$clipPosition = function(position) { + var length = this.getLength(); + if (position.row >= length) { + position.row = Math.max(0, length - 1); + position.column = this.getLine(length - 1).length; + } else { + position.row = Math.max(0, position.row); + position.column = Math.min(Math.max(position.column, 0), this.getLine(position.row).length); + } + return position; + }; + /** * Fires whenever the document changes. * @@ -392,14 +397,12 @@ var Document = function(textOrLines) { * **/ this.insertMergedLines = function(position, lines) { - // Calculate insertion range end point. this.$clipPosition(position); var endPoint = { row : position.row + lines.length - 1, column : (lines.length == 1 ? position.column : 0) + lines[lines.length - 1].length }; - // Apply delta (emits change). this.applyDelta({ action: "insert", start: position, @@ -417,15 +420,14 @@ var Document = function(textOrLines) { * **/ this.remove = function(range) { - // Apply delta (emits change). - range = this.$getClippedRange(range); + var start = this.clippedPos(range.start.row, range.start.column); this.applyDelta({ action: "remove", - start: range.start, - end: range.end, + start: start, + end: this.clippedPos(range.end.row, range.end.column), lines: this.getLinesForRange(range), }); - return range.start; + return this.clonePos(start); }; /** @@ -437,19 +439,16 @@ var Document = function(textOrLines) { * **/ this.removeInLine = function(row, startColumn, endColumn) { - // Calculate deleteion range. - var range = new Range(row, startColumn, row, endColumn); - range = this.$getClippedRange(range); + var start = this.clippedPos(row, startColumn); - // Apply delta (emits change). this.applyDelta({ action: "remove", - start: range.start, - end: range.end, + start: start, + end: this.clippedPos(row, endColumn), lines: this.getLinesForRange(range), - }, true /*doNotValidate*/); + }, true); - return range.start; + return this.clonePos(start); }; /** @@ -478,7 +477,6 @@ var Document = function(textOrLines) { // Store delelted lines with bounding newlines ommitted (maintains previous behavior). var deletedLines = this.$lines.slice(firstRow, lastRow + 1); - // Apply delta (emits change). this.applyDelta({ action: "remove", start: range.start, @@ -496,13 +494,11 @@ var Document = function(textOrLines) { * **/ this.removeNewLine = function(row) { - if (row < this.getLength() - 1 && row >= 0) { - var range = new Range(row, this.getLine(row).length, row + 1, 0); - // Apply delta (emits change). + if (row < this.getLength() - 1 && row >= 0) { this.applyDelta({ action: "remove", - start: range.start, - end: range.end, + start: this.pos(row, this.getLine(row).length), + end: this.pos(row + 1, 0), lines: ["", ""] }); } @@ -566,10 +562,21 @@ var Document = function(textOrLines) { * @param {Object} delta A delta object (can include "insert" and "remove" actions) **/ this.applyDelta = function(delta, doNotValidate) { + var isInsert = delta.action == "insert"; // An empty range is a NOOP. - if (!Range.comparePoints(delta.start, delta.end)) + if (isInsert ? !delta.lines.length + : !Range.comparePoints(delta.start, delta.end)) return; + if (isInsert && delta.lines.length > 0xFFFF) + this.$splitAndapplyLargeDelta(delta); + + // Apply. + applyDelta(this.$lines, delta, doNotValidate); + this._signal("change", {data: delta}); + }; + + this.$splitAndapplyLargeDelta = function(delta) { // Split large insert deltas. This is necessary because: // 1. We need to support splicing delta lines into the document via $lines.splice.apply(...) // 2. fn.apply() doesn't work for a large number of params. The mallest threshold is on safari 0xFFFF. @@ -578,28 +585,18 @@ var Document = function(textOrLines) { // delta handling is too slow. If we make delete delta handling faster we can split all large deltas // as shown in https://gist.github.com/aldendaniels/8367109#file-document-snippet-js // If we do this, update validateDelta() to limit the number of lines in a delete delta. - var bIsInsert = delta.action == "insert"; - while (bIsInsert && delta.lines.length > 65001) { + while (delta.lines.length > 0xFFFF) { // Get split deltas. - var lines = delta.lines.splice(0, 65000); + var lines = delta.lines.splice(0, 0xFFFF); lines.push(""); - var range = new Range(delta.start.row, delta.start.column, - delta.start.row + 65000, 0) + var start = delta.start; this.applyDelta({ action: delta.action, lines: lines, - start: range.start, - end: range.end, - }); - - // Update remaining delta. - delta.start.row += 65000; - delta.start.column = 0; + start: this.pos(start.row, start.column), + end: this.pos(start.row += 0xFFFF, start.column = 0) // Updates remaining delta. + }, true); } - - // Apply. - applyDelta(this.$lines, delta, doNotValidate); - this._emit("change", { data: delta }); }; /** diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 4baa525f..c9759fb1 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -1394,25 +1394,25 @@ var EditSession = function(text, mode) { **/ this.outdentRows = function (range) { var rowRange = range.collapseRows(); + var deleteRange = new Range(0, 0, 0, 0); var size = this.getTabSize(); - + for (var i = rowRange.start.row; i <= rowRange.end.row; ++i) { var line = this.getLine(i); - var row = i; - var startCol = 0; - var endCol = 0; - + + deleteRange.start.row = i; + deleteRange.end.row = i; for (var j = 0; j < size; ++j) if (line.charAt(j) != ' ') break; if (j < size && line.charAt(j) == '\t') { - startCol = j; - endCol = j + 1; + deleteRange.start.column = j; + deleteRange.end.column = j + 1; } else { - startCol = 0; - endCol = j; + deleteRange.start.column = 0; + deleteRange.end.column = j; } - this.doc.removeInLine(row, startCol, endCol); + this.remove(deleteRange); } }; @@ -1684,7 +1684,7 @@ var EditSession = function(text, mode) { this.$updating = true; if (len != 0) { - if (action == "remove") { + if (action === "remove") { this[useWrapMode ? "$wrapData" : "$rowLengthCache"].splice(firstRow, len); var foldLines = this.$foldData; @@ -1759,7 +1759,7 @@ var EditSession = function(text, mode) { // Realign folds. E.g. if you add some new chars before a fold, the // fold should "move" to the right. len = Math.abs(e.data.start.column - e.data.end.column); - if (action == "remove") { + if (action === "remove") { // Get all the folds in the change range and remove them. removedFolds = this.getFoldsInRange(e.data); this.removeFolds(removedFolds); diff --git a/lib/ace/range_list.js b/lib/ace/range_list.js index 0cbcc394..a112139a 100644 --- a/lib/ace/range_list.js +++ b/lib/ace/range_list.js @@ -181,13 +181,13 @@ var RangeList = function() { }; this.$onChange = function(e) { - var changeRange = e.data.range; - if (e.data.action[0] == "i"){ - var start = changeRange.start; - var end = changeRange.end; + var delta = e.data; + if (delta.action == "insert"){ + var start = delta.start; + var end = delta.end; } else { - var end = changeRange.start; - var start = changeRange.end; + var end = delta.start; + var start = delta.end; } var startRow = start.row; var endRow = end.row; diff --git a/lib/ace/undomanager.js b/lib/ace/undomanager.js index 71b4ba65..6da50a8b 100644 --- a/lib/ace/undomanager.js +++ b/lib/ace/undomanager.js @@ -61,10 +61,9 @@ var UndoManager = function() { * **/ this.execute = function(options) { - // Normalize deltas for storage. - var deltaSets = this.$serializeDeltas(options.args[0]); - + // var deltaSets = this.$serializeDeltas(options.args[0]); + var deltaSets = options.args[0]; // Add deltas to undo stack. this.$doc = options.args[1]; if (options.merge && this.hasUndo()){ @@ -207,7 +206,7 @@ var UndoManager = function() { deltaSets_new[i] = deltaSet_new; } - return deltaSets_new; + return deltaSets_new; } }).call(UndoManager.prototype); diff --git a/lib/ace/worker/mirror.js b/lib/ace/worker/mirror.js index 8254f205..514d377f 100644 --- a/lib/ace/worker/mirror.js +++ b/lib/ace/worker/mirror.js @@ -13,7 +13,19 @@ var Mirror = exports.Mirror = function(sender) { var _self = this; sender.on("change", function(e) { - doc.applyDeltas(e.data); + var data = e.data; + if (data[0].start) { + doc.applyDeltas(data); + } else { + for (var i = 0; i < data.length; i += 2) { + if (Array.isArray(data[i+1])) + var d = {action: "insert", start: data[i], lines: data[i+1]}; + else + var d = {action: "remove", start: data[i],end: data[i+1]}; + + doc.applyDelta(d, true); + } + } if (_self.$timeout) return deferredUpdate.schedule(_self.$timeout); _self.onUpdate(); diff --git a/lib/ace/worker/worker_client.js b/lib/ace/worker/worker_client.js index cb18998f..f5902f90 100644 --- a/lib/ace/worker/worker_client.js +++ b/lib/ace/worker/worker_client.js @@ -162,17 +162,21 @@ var WorkerClient = function(topLevelNamespaces, mod, classname, workerUrl) { this.changeListener = function(e) { if (!this.deltaQueue) { - this.deltaQueue = [e.data]; + this.deltaQueue = []; setTimeout(this.$sendDeltaQueue, 0); - } else - this.deltaQueue.push(e.data); + } + var delta = e.data; + if (delta.action == "insert") + this.deltaQueue.push(delta.start, delta.lines); + else + this.deltaQueue.push(delta.start, delta.end); }; this.$sendDeltaQueue = function() { var q = this.deltaQueue; if (!q) return; this.deltaQueue = null; - if (q.length > 20 && q.length > this.$doc.getLength() >> 1) { + if (q.length > 50 && q.length > this.$doc.getLength() >> 1) { this.call("setValue", [this.$doc.getValue()]); } else this.emit("change", {data: q}); From 27b6d6dcd38557b2836a71d3933db8270ee7a343 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sat, 15 Feb 2014 00:01:30 +0400 Subject: [PATCH 23/92] apply arguments max length also includes actual call stack --- lib/ace/document.js | 45 +++++++++++++++++++++++------------------- lib/ace/placeholder.js | 2 +- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/lib/ace/document.js b/lib/ace/document.js index 1abb95a2..99b7bb2d 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -302,9 +302,11 @@ var Document = function(textOrLines) { row = 0; } else if (row >= length) { row = length - 1; - column = undefined - } + column = undefined; + } var line = this.getLine(row); + if (column == undefined) + column = line.length; column = Math.min(Math.max(column, 0), line.length); return {row: row, column: column}; }; @@ -421,31 +423,33 @@ var Document = function(textOrLines) { **/ this.remove = function(range) { var start = this.clippedPos(range.start.row, range.start.column); + var end = this.clippedPos(range.end.row, range.end.column); this.applyDelta({ action: "remove", start: start, - end: this.clippedPos(range.end.row, range.end.column), - lines: this.getLinesForRange(range), + end: end, + lines: this.getLinesForRange({start: start, end: end}), }); return this.clonePos(start); }; /** - * Removes the specified columns from the `row`. This method also triggers the `"change"` event. - * @param {Number} row The row to remove from - * @param {Number} startColumn The column to start removing at - * @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. - * - **/ + * Removes the specified columns from the `row`. This method also triggers a `"change"` event. + * @param {Number} row The row to remove from + * @param {Number} startColumn The column to start removing at + * @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) { - var start = this.clippedPos(row, startColumn); + var start = this.clippedPos(row, startColumn); + var end = this.clippedPos(row, endColumn); this.applyDelta({ action: "remove", start: start, - end: this.clippedPos(row, endColumn), - lines: this.getLinesForRange(range), + end: end, + lines: this.getLinesForRange({start: start, end: end}), }, true); return this.clonePos(start); @@ -568,7 +572,7 @@ var Document = function(textOrLines) { : !Range.comparePoints(delta.start, delta.end)) return; - if (isInsert && delta.lines.length > 0xFFFF) + if (isInsert && delta.lines.length > 0xF000) this.$splitAndapplyLargeDelta(delta); // Apply. @@ -579,22 +583,23 @@ var Document = function(textOrLines) { this.$splitAndapplyLargeDelta = function(delta) { // Split large insert deltas. This is necessary because: // 1. We need to support splicing delta lines into the document via $lines.splice.apply(...) - // 2. fn.apply() doesn't work for a large number of params. The mallest threshold is on safari 0xFFFF. + // 2. fn.apply() doesn't work for a large number of params. The smallest threshold is on safari 0xFFFF. + // we use 0xF000 to leave some space for actual stack // // To Do: Ideally we'd be consistent and also split 'delete' deltas. We don't do this now, because delete // delta handling is too slow. If we make delete delta handling faster we can split all large deltas // as shown in https://gist.github.com/aldendaniels/8367109#file-document-snippet-js // If we do this, update validateDelta() to limit the number of lines in a delete delta. - while (delta.lines.length > 0xFFFF) { + while (delta.lines.length > 0xF000) { // Get split deltas. - var lines = delta.lines.splice(0, 0xFFFF); + var lines = delta.lines.splice(0, 0xF000); lines.push(""); var start = delta.start; this.applyDelta({ action: delta.action, lines: lines, - start: this.pos(start.row, start.column), - end: this.pos(start.row += 0xFFFF, start.column = 0) // Updates remaining delta. + start: this.pos(start.row, start.column), + end: this.pos(start.row += 0xF000, start.column = 0) // Updates remaining delta. }, true); } }; diff --git a/lib/ace/placeholder.js b/lib/ace/placeholder.js index 1d345a24..d7a2d6e9 100644 --- a/lib/ace/placeholder.js +++ b/lib/ace/placeholder.js @@ -150,7 +150,7 @@ var PlaceHolder = function(session, length, pos, others, mainClass, othersClass) **/ this.onUpdate = function(event) { var delta = event.data; - var range = delta.range; + var range = delta; if(range.start.row !== range.end.row) return; if(range.start.row !== this.pos.row) return; if (this.$updating) return; From ad54d2c46cb8b161295ec215d4495c4b69adb661 Mon Sep 17 00:00:00 2001 From: nightwing Date: Wed, 2 Jul 2014 22:00:51 +0400 Subject: [PATCH 24/92] disabled validateDelta since it breaks autocompletion popup --- lib/ace/apply_delta.js | 10 +++++----- lib/ace/autocomplete/popup.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/ace/apply_delta.js b/lib/ace/apply_delta.js index 98a1a857..98bfe148 100644 --- a/lib/ace/apply_delta.js +++ b/lib/ace/apply_delta.js @@ -52,7 +52,7 @@ function validateDelta(docLines, delta) { // Validate range type. if (!delta.start || !delta.end) - throwDeltaError(delta, "delta.start/end must be an present"); + throwDeltaError(delta, "delta.start/end must be an present"); // Validate that the start point is contained in the document. var start = delta.start; @@ -72,19 +72,19 @@ function validateDelta(docLines, delta) { } exports.applyDelta = function(docLines, delta, doNotValidate) { - if (!doNotValidate) - validateDelta(docLines, delta); + // disabled validation since it breaks autocompletion popup + // if (!doNotValidate) + // validateDelta(docLines, delta); var row = delta.start.row; var startColumn = delta.start.column; - var line = docLines[row]; + var line = docLines[row] || ""; switch (delta.action) { case "insert": var lines = delta.lines; if (lines.length === 1) { docLines[row] = line.substring(0, startColumn) + delta.lines[0] + line.substring(startColumn); } else { - var line = docLines[row]; var args = [row, 1].concat(delta.lines); docLines.splice.apply(docLines, args); docLines[row] = line.substring(0, startColumn) + docLines[row]; diff --git a/lib/ace/autocomplete/popup.js b/lib/ace/autocomplete/popup.js index a0ab7604..471cdb90 100644 --- a/lib/ace/autocomplete/popup.js +++ b/lib/ace/autocomplete/popup.js @@ -213,8 +213,8 @@ var AcePopup = function(parentNode) { popup.data = []; popup.setData = function(list) { - popup.data = list || []; popup.setValue(lang.stringRepeat("\n", list.length), -1); + popup.data = list || []; popup.setRow(0); }; popup.getData = function(row) { From 525fe1ffe306a6258578c238b17114a00f71888a Mon Sep 17 00:00:00 2001 From: nightwing Date: Sat, 18 Oct 2014 00:56:13 +0400 Subject: [PATCH 25/92] do not wrap change events in additional e.data --- lib/ace/anchor.js | 4 ++-- lib/ace/document.js | 2 +- lib/ace/document_test.js | 24 ++++++++++++------------ lib/ace/edit_session.js | 21 ++++++++++----------- lib/ace/edit_session/folding.js | 3 +-- lib/ace/editor.js | 5 ++--- lib/ace/ext/chromevox.js | 3 +-- lib/ace/ext/elastic_tabstops_lite.js | 11 +++++------ lib/ace/layer/gutter.js | 3 +-- lib/ace/line_widgets.js | 5 ++--- lib/ace/placeholder.js | 3 +-- lib/ace/range_list.js | 3 +-- lib/ace/snippets.js | 10 +++++----- lib/ace/worker/worker_client.js | 3 +-- 14 files changed, 45 insertions(+), 55 deletions(-) diff --git a/lib/ace/anchor.js b/lib/ace/anchor.js index f3a60277..ff85c13b 100644 --- a/lib/ace/anchor.js +++ b/lib/ace/anchor.js @@ -70,6 +70,7 @@ var Anchor = exports.Anchor = function(doc, row, column) { * @returns {Object} **/ this.getPosition = function() { + //process.exit(2) return this.$clipPositionToDocument(this.row, this.column); }; @@ -99,8 +100,7 @@ var Anchor = exports.Anchor = function(doc, row, column) { * - `value`: An object describing the new Anchor position * **/ - this.onChange = function(e) { - var delta = e.data; + this.onChange = function(delta) { if (delta.start.row == delta.end.row && delta.start.row != this.row) return; diff --git a/lib/ace/document.js b/lib/ace/document.js index 51f6fe8d..c5ccb71b 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -579,7 +579,7 @@ var Document = function(textOrLines) { // Apply. applyDelta(this.$lines, delta, doNotValidate); - this._signal("change", {data: delta}); + this._signal("change", delta); }; this.$splitAndapplyLargeDelta = function(delta) { diff --git a/lib/ace/document_test.js b/lib/ace/document_test.js index a91fccf2..ee27f00b 100644 --- a/lib/ace/document_test.js +++ b/lib/ace/document_test.js @@ -46,7 +46,7 @@ module.exports = { var doc = new Document(["12", "34"]); var deltas = []; - doc.on("change", function(e) { deltas.push(e.data); }); + doc.on("change", function(e) { deltas.push(e); }); doc.insert({row: 0, column: 1}, "juhu"); assert.equal(doc.getValue(), ["1juhu2", "34"].join("\n")); @@ -63,7 +63,7 @@ module.exports = { var doc = new Document(["12", "34"]); var deltas = []; - doc.on("change", function(e) { deltas.push(e.data); }); + doc.on("change", function(e) { deltas.push(e); }); doc.insertMergedLines({row: 0, column: 1}, ['', '']); assert.equal(doc.getValue(), ["1", "2", "34"].join("\n")); @@ -80,7 +80,7 @@ module.exports = { var doc = new Document(["12", "34"]); var deltas = []; - doc.on("change", function(e) { deltas.push(e.data); }); + doc.on("change", function(e) { deltas.push(e); }); doc.insertFullLines(0, ["aa", "bb"]); assert.equal(doc.getValue(), ["aa", "bb", "12", "34"].join("\n")); @@ -97,7 +97,7 @@ module.exports = { var doc = new Document(["12", "34"]); var deltas = []; - doc.on("change", function(e) { deltas.push(e.data); }); + doc.on("change", function(e) { deltas.push(e); }); doc.insertFullLines(2, ["aa", "bb"]); assert.equal(doc.getValue(), ["12", "34", "aa", "bb"].join("\n")); @@ -107,7 +107,7 @@ module.exports = { var doc = new Document(["12", "34"]); var deltas = []; - doc.on("change", function(e) { deltas.push(e.data); }); + doc.on("change", function(e) { deltas.push(e); }); doc.insertFullLines(1, ["aa", "bb"]); assert.equal(doc.getValue(), ["12", "aa", "bb", "34"].join("\n")); @@ -124,7 +124,7 @@ module.exports = { var doc = new Document(["12", "34"]); var deltas = []; - doc.on("change", function(e) { deltas.push(e.data); }); + doc.on("change", function(e) { deltas.push(e); }); doc.insert({row: 0, column: 0}, "aa\nbb\ncc"); assert.equal(doc.getValue(), ["aa", "bb", "cc12", "34"].join("\n")); @@ -141,7 +141,7 @@ module.exports = { var doc = new Document(["12", "34"]); var deltas = []; - doc.on("change", function(e) { deltas.push(e.data); }); + doc.on("change", function(e) { deltas.push(e); }); doc.insert({row: 1, column: 2}, "aa\nbb\ncc"); assert.equal(doc.getValue(), ["12", "34aa", "bb", "cc"].join("\n")); @@ -158,7 +158,7 @@ module.exports = { var doc = new Document(["12", "34"]); var deltas = []; - doc.on("change", function(e) { deltas.push(e.data); }); + doc.on("change", function(e) { deltas.push(e); }); doc.insert({row: 0, column: 1}, "aa\nbb\ncc"); assert.equal(doc.getValue(), ["1aa", "bb", "cc2", "34"].join("\n")); @@ -175,7 +175,7 @@ module.exports = { var doc = new Document(["1234", "5678"]); var deltas = []; - doc.on("change", function(e) { deltas.push(e.data); }); + doc.on("change", function(e) { deltas.push(e); }); doc.remove(new Range(0, 1, 0, 3)); assert.equal(doc.getValue(), ["14", "5678"].join("\n")); @@ -192,7 +192,7 @@ module.exports = { var doc = new Document(["1234", "5678"]); var deltas = []; - doc.on("change", function(e) { deltas.push(e.data); }); + doc.on("change", function(e) { deltas.push(e); }); doc.remove(new Range(0, 4, 1, 0)); assert.equal(doc.getValue(), ["12345678"].join("\n")); @@ -209,7 +209,7 @@ module.exports = { var doc = new Document(["1234", "5678", "abcd"]); var deltas = []; - doc.on("change", function(e) { deltas.push(e.data); }); + doc.on("change", function(e) { deltas.push(e); }); doc.remove(new Range(0, 2, 2, 2)); assert.equal(doc.getValue(), ["12cd"].join("\n")); @@ -226,7 +226,7 @@ module.exports = { var doc = new Document(["1234", "5678", "abcd"]); var deltas = []; - doc.on("change", function(e) { deltas.push(e.data); }); + doc.on("change", function(e) { deltas.push(e); }); doc.remove(new Range(1, 0, 3, 0)); assert.equal(doc.getValue(), ["1234", ""].join("\n")); diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 53e98baa..377df45b 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -249,13 +249,12 @@ var EditSession = function(text, mode) { this.$resetRowCache(fold.start.row); }; - this.onChange = function(e) { - var delta = e.data; + this.onChange = function(delta) { this.$modified = true; this.$resetRowCache(delta.start.row); - var removedFolds = this.$updateInternalDataOnChange(e); + var removedFolds = this.$updateInternalDataOnChange(delta); if (!this.$fromUndo && this.$undoManager && !delta.ignore) { this.$deltasDoc.push(delta); if (removedFolds && removedFolds.length != 0) { @@ -269,7 +268,7 @@ var EditSession = function(text, mode) { } this.bgTokenizer && this.bgTokenizer.$updateOnChange(delta); - this._signal("change", e); + this._signal("change", delta); }; /** @@ -1667,11 +1666,11 @@ var EditSession = function(text, mode) { }; }; - this.$updateInternalDataOnChange = function(e) { + this.$updateInternalDataOnChange = function(delta) { var useWrapMode = this.$useWrapMode; - var action = e.data.action; - var start = e.data.start; - var end = e.data.end; + var action = delta.action; + var start = delta.start; + var end = delta.end; var firstRow = start.row; var lastRow = end.row; var len = lastRow - firstRow; @@ -1683,7 +1682,7 @@ var EditSession = function(text, mode) { this[useWrapMode ? "$wrapData" : "$rowLengthCache"].splice(firstRow, len); var foldLines = this.$foldData; - removedFolds = this.getFoldsInRange(e.data); + removedFolds = this.getFoldsInRange(delta); this.removeFolds(removedFolds); var foldLine = this.getFoldLine(end.row); @@ -1748,10 +1747,10 @@ var EditSession = function(text, mode) { } else { // Realign folds. E.g. if you add some new chars before a fold, the // fold should "move" to the right. - len = Math.abs(e.data.start.column - e.data.end.column); + len = Math.abs(delta.start.column - delta.end.column); if (action === "remove") { // Get all the folds in the change range and remove them. - removedFolds = this.getFoldsInRange(e.data); + removedFolds = this.getFoldsInRange(delta); this.removeFolds(removedFolds); len = -len; diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js index d5052266..0c1914ce 100644 --- a/lib/ace/edit_session/folding.js +++ b/lib/ace/edit_session/folding.js @@ -827,8 +827,7 @@ function Folding() { } }; - this.updateFoldWidgets = function(e) { - var delta = e.data; + this.updateFoldWidgets = function(delta) { var firstRow = delta.start.row; var len = delta.end.row - firstRow; diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 97c4a8e6..9574d703 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -674,14 +674,13 @@ var Editor = function(renderer, session) { * * **/ - this.onDocumentChange = function(e) { + this.onDocumentChange = function(delta) { // Rerender and emit "change" event. - var delta = e.data; var wrap = this.session.$useWrapMode; var lastRow = (delta.start.row == delta.end.row ? delta.end.row : Infinity); this.renderer.updateLines(delta.start.row, lastRow, wrap); - this._signal("change", e); + this._signal("change", delta); // Update cursor because tab characters can influence the cursor position. this.$cursorChange(); diff --git a/lib/ace/ext/chromevox.js b/lib/ace/ext/chromevox.js index 4abb8278..52a180d4 100644 --- a/lib/ace/ext/chromevox.js +++ b/lib/ace/ext/chromevox.js @@ -578,8 +578,7 @@ var onSelectionChange = function(evt) { * and deleting text. * @param {!Event} evt The event. */ -var onChange = function(evt) { - var data = evt.data; +var onChange = function(delta) { switch (data.action) { case 'remove': cvox.Api.speak(data.text, 0, DELETED_PROP); diff --git a/lib/ace/ext/elastic_tabstops_lite.js b/lib/ace/ext/elastic_tabstops_lite.js index ce5e55f9..0f89423a 100644 --- a/lib/ace/ext/elastic_tabstops_lite.js +++ b/lib/ace/ext/elastic_tabstops_lite.js @@ -44,13 +44,12 @@ var ElasticTabstopsLite = function(editor) { this.onExec = function() { recordChanges = true; }; - this.onChange = function(e) { - var range = e.data; + this.onChange = function(delta) { if (recordChanges) { - if (changedRows.indexOf(range.start.row) == -1) - changedRows.push(range.start.row); - if (range.end.row != range.start.row) - changedRows.push(range.end.row); + if (changedRows.indexOf(delta.start.row) == -1) + changedRows.push(delta.start.row); + if (delta.end.row != delta.start.row) + changedRows.push(delta.end.row); } }; }; diff --git a/lib/ace/layer/gutter.js b/lib/ace/layer/gutter.js index 9608868c..dc1055c2 100644 --- a/lib/ace/layer/gutter.js +++ b/lib/ace/layer/gutter.js @@ -100,10 +100,9 @@ var Gutter = function(parentEl) { } }; - this.$updateAnnotations = function (e) { + this.$updateAnnotations = function (delta) { if (!this.$annotations.length) return; - var delta = e.data; var firstRow = delta.start.row; var len = delta.end.row - firstRow; if (len === 0) { diff --git a/lib/ace/line_widgets.js b/lib/ace/line_widgets.js index 4e5ae39d..ba87d857 100644 --- a/lib/ace/line_widgets.js +++ b/lib/ace/line_widgets.js @@ -113,11 +113,10 @@ function LineWidgets(session) { }); }; - this.updateOnChange = function(e) { + this.updateOnChange = function(delta) { var lineWidgets = this.session.lineWidgets; if (!lineWidgets) return; - - var delta = e.data; + var startRow = delta.start.row; var len = delta.end.row - startRow; diff --git a/lib/ace/placeholder.js b/lib/ace/placeholder.js index d3bf1969..0e299594 100644 --- a/lib/ace/placeholder.js +++ b/lib/ace/placeholder.js @@ -150,8 +150,7 @@ var PlaceHolder = function(session, length, pos, others, mainClass, othersClass) * Emitted when the place holder updates. * **/ - this.onUpdate = function(event) { - var delta = event.data; + this.onUpdate = function(delta) { var range = delta; if(range.start.row !== range.end.row) return; if(range.start.row !== this.pos.row) return; diff --git a/lib/ace/range_list.js b/lib/ace/range_list.js index a112139a..326bc41b 100644 --- a/lib/ace/range_list.js +++ b/lib/ace/range_list.js @@ -180,8 +180,7 @@ var RangeList = function() { this.session = null; }; - this.$onChange = function(e) { - var delta = e.data; + this.$onChange = function(delta) { if (delta.action == "insert"){ var start = delta.start; var end = delta.end; diff --git a/lib/ace/snippets.js b/lib/ace/snippets.js index 10c34bf0..699cd6e9 100644 --- a/lib/ace/snippets.js +++ b/lib/ace/snippets.js @@ -667,11 +667,11 @@ var TabstopManager = function(editor) { this.editor = null; }; - this.onChange = function(e) { - var changeRange = e.data.range; - var isRemove = e.data.action[0] == "r"; - var start = changeRange.start; - var end = changeRange.end; + this.onChange = function(delta) { + var changeRange = delta; + var isRemove = delta.action[0] == "r"; + var start = delta.start; + var end = delta.end; var startRow = start.row; var endRow = end.row; var lineDif = endRow - startRow; diff --git a/lib/ace/worker/worker_client.js b/lib/ace/worker/worker_client.js index 255a2cea..ba4f20a0 100644 --- a/lib/ace/worker/worker_client.js +++ b/lib/ace/worker/worker_client.js @@ -162,12 +162,11 @@ var WorkerClient = function(topLevelNamespaces, mod, classname, workerUrl) { doc.on("change", this.changeListener); }; - this.changeListener = function(e) { + this.changeListener = function(delta) { if (!this.deltaQueue) { this.deltaQueue = []; setTimeout(this.$sendDeltaQueue, 0); } - var delta = e.data; if (delta.action == "insert") this.deltaQueue.push(delta.start, delta.lines); else From bacaeb07d81d0fd25ae6a8f597a4c4a5f8e1af55 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 11 Nov 2014 11:40:18 +0400 Subject: [PATCH 26/92] cleanup --- lib/ace/document.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/ace/document.js b/lib/ace/document.js index c5ccb71b..44897d7c 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -227,7 +227,7 @@ var Document = function(textOrLines) { var lines; if (range.start.row === range.end.row) { // Handle a single-line range. - lines = [this.getLine(range.start.row).substring(range.start.column, range.end.column)]; + lines = [this.getLine(range.start.row).substring(range.start.column, range.end.column)]; } else { // Handle a multi-line range. lines = this.getLines(range.start.row, range.end.row); @@ -287,9 +287,9 @@ var Document = function(textOrLines) { var end = this.pos(position.row, position.column + text.length); this.applyDelta({ - action: "insert", start: start, end: end, + action: "insert", lines: [text] }, true); @@ -408,9 +408,9 @@ var Document = function(textOrLines) { }; this.applyDelta({ - action: "insert", start: position, end: endPoint, + action: "insert", lines: lines }); @@ -427,10 +427,10 @@ var Document = function(textOrLines) { var start = this.clippedPos(range.start.row, range.start.column); var end = this.clippedPos(range.end.row, range.end.column); this.applyDelta({ - action: "remove", start: start, end: end, - lines: this.getLinesForRange({start: start, end: end}), + action: "remove", + lines: this.getLinesForRange({start: start, end: end}) }); return this.clonePos(start); }; @@ -448,10 +448,10 @@ var Document = function(textOrLines) { var end = this.clippedPos(row, endColumn); this.applyDelta({ - action: "remove", start: start, end: end, - lines: this.getLinesForRange({start: start, end: end}), + action: "remove", + lines: this.getLinesForRange({start: start, end: end}) }, true); return this.clonePos(start); @@ -484,9 +484,9 @@ var Document = function(textOrLines) { var deletedLines = this.$lines.slice(firstRow, lastRow + 1); this.applyDelta({ - action: "remove", start: range.start, end: range.end, + action: "remove", lines: this.getLinesForRange(range) }); @@ -500,11 +500,11 @@ var Document = function(textOrLines) { * **/ this.removeNewLine = function(row) { - if (row < this.getLength() - 1 && row >= 0) { + if (row < this.getLength() - 1 && row >= 0) { this.applyDelta({ - action: "remove", start: this.pos(row, this.getLine(row).length), end: this.pos(row + 1, 0), + action: "remove", lines: ["", ""] }); } @@ -598,10 +598,10 @@ var Document = function(textOrLines) { lines.push(""); var start = delta.start; this.applyDelta({ - action: delta.action, - lines: lines, start: this.pos(start.row, start.column), - end: this.pos(start.row += 0xF000, start.column = 0) // Updates remaining delta. + end: this.pos(start.row += 0xF000, start.column = 0), // Updates remaining delta. + action: delta.action, + lines: lines }, true); } }; @@ -613,9 +613,9 @@ var Document = function(textOrLines) { this.revertDelta = function(delta) { var range = Range.fromPoints(delta.start, delta.end); this.applyDelta({ - action: (delta.action == "insert" ? "remove" : "insert"), start: range.start, end: range.end, + action: delta.action == "insert" ? "remove" : "insert", lines: delta.lines.slice() }); }; From ffaf44ec0b0ee57cd357baa7a306681b9464d860 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 11 Nov 2014 11:44:17 +0400 Subject: [PATCH 27/92] copy insert start position before storing it in undoStack --- lib/ace/document.js | 14 +++++++------- lib/ace/multi_select_test.js | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/ace/document.js b/lib/ace/document.js index 44897d7c..cd586ce8 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -401,20 +401,20 @@ var Document = function(textOrLines) { * **/ this.insertMergedLines = function(position, lines) { - this.$clipPosition(position); - var endPoint = { - row : position.row + lines.length - 1, - column : (lines.length == 1 ? position.column : 0) + lines[lines.length - 1].length + var start = this.clippedPos(position.row, position.column); + var end = { + row: start.row + lines.length - 1, + column: (lines.length == 1 ? start.column : 0) + lines[lines.length - 1].length }; this.applyDelta({ - start: position, - end: endPoint, + start: start, + end: end, action: "insert", lines: lines }); - return endPoint; + return this.clonePos(end); }; /** diff --git a/lib/ace/multi_select_test.js b/lib/ace/multi_select_test.js index 45ab4965..0963a37b 100644 --- a/lib/ace/multi_select_test.js +++ b/lib/ace/multi_select_test.js @@ -30,6 +30,7 @@ if (typeof process !== "undefined") { require("amd-loader"); + require("./test/mockdom"); } define(function(require, exports, module) { @@ -41,6 +42,7 @@ var MockRenderer = require("./test/mockrenderer").MockRenderer; var Range = require("./range").Range; var assert = require("./test/assertions"); var MultiSelect = require("./multi_select").MultiSelect; +var UndoManager = require("./undomanager").UndoManager; var editor; var exec = function(name, times, args) { @@ -195,6 +197,20 @@ module.exports = { selection.fromJSON(after); assert.ok(!selection.isEqual(before)); assert.ok(selection.isEqual(after)); + }, + + "test multiselect align": function() { + var doc = new EditSession(["l1", "l2", "l3"]); + doc.setUndoManager(new UndoManager()); + editor = new Editor(new MockRenderer(), doc); + var selection = editor.selection; + selection.addRange(new Range(1,0,1,0)) + selection.addRange(new Range(2,2,2,2)) + editor.execCommand("alignCursors"); + assert.equal(' l1\n l2\nl3', editor.getValue()); + doc.markUndoGroup(); + editor.execCommand("undo"); + assert.equal('l1\nl2\nl3', editor.getValue()); } }; From 64f2fc4015cd8ebdccfb97bb1f1e0af3ff2d312e Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 11 Nov 2014 11:45:20 +0400 Subject: [PATCH 28/92] add newLineCharacter argument to getValue --- lib/ace/document.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ace/document.js b/lib/ace/document.js index cd586ce8..e92aa969 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -83,8 +83,8 @@ var Document = function(textOrLines) { /** * Returns all the lines in the document as a single string, joined by the new line character. **/ - this.getValue = function() { - return this.getAllLines().join(this.getNewLineCharacter()); + this.getValue = function(newLineCharacter) { + return this.getAllLines().join(newLineCharacter || this.getNewLineCharacter()); }; /** @@ -213,8 +213,8 @@ var Document = function(textOrLines) { * * @returns {String} **/ - this.getTextRange = function(range) { - return this.getLinesForRange(range).join(this.getNewLineCharacter()); + this.getTextRange = function(range, newLineCharacter) { + return this.getLinesForRange(range).join(newLineCharacter || this.getNewLineCharacter()); }; /** From 27264bb9d93e24c8c1c8a8eae54b0828d1bd57f4 Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 20 Nov 2014 23:01:53 +0400 Subject: [PATCH 29/92] fix infinite loop when inserting more than 0xF000 lines at once --- lib/ace/document.js | 36 ++++++++++++++++++++++++------------ lib/ace/document_test.js | 19 +++++++++++++++++++ 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/lib/ace/document.js b/lib/ace/document.js index e92aa969..9207b3f5 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -575,14 +575,14 @@ var Document = function(textOrLines) { return; if (isInsert && delta.lines.length > 0xF000) - this.$splitAndapplyLargeDelta(delta); + this.$splitAndapplyLargeDelta(delta, 0xF000); // Apply. applyDelta(this.$lines, delta, doNotValidate); this._signal("change", delta); }; - this.$splitAndapplyLargeDelta = function(delta) { + this.$splitAndapplyLargeDelta = function(delta, MAX) { // Split large insert deltas. This is necessary because: // 1. We need to support splicing delta lines into the document via $lines.splice.apply(...) // 2. fn.apply() doesn't work for a large number of params. The smallest threshold is on safari 0xFFFF. @@ -592,18 +592,30 @@ var Document = function(textOrLines) { // delta handling is too slow. If we make delete delta handling faster we can split all large deltas // as shown in https://gist.github.com/aldendaniels/8367109#file-document-snippet-js // If we do this, update validateDelta() to limit the number of lines in a delete delta. - while (delta.lines.length > 0xF000) { - // Get split deltas. - var lines = delta.lines.splice(0, 0xF000); - lines.push(""); - var start = delta.start; + var lines = delta.lines; + var l = lines.length; + var row = delta.start.row; + var column = delta.start.column; + var from = 0, to = 0; + do { + from = to; + to += MAX - 1; + var chunk = lines.slice(from, to); + if (to > l) { + // Update remaining delta. + delta.lines = chunk; + delta.start.row = row + from; + delta.start.column = column; + break; + } + chunk.push(""); this.applyDelta({ - start: this.pos(start.row, start.column), - end: this.pos(start.row += 0xF000, start.column = 0), // Updates remaining delta. + start: this.pos(row + from, column), + end: this.pos(row + to, column = 0), action: delta.action, - lines: lines + lines: chunk }, true); - } + } while(true); }; /** @@ -615,7 +627,7 @@ var Document = function(textOrLines) { this.applyDelta({ start: range.start, end: range.end, - action: delta.action == "insert" ? "remove" : "insert", + action: (delta.action == "insert" ? "remove" : "insert"), lines: delta.lines.slice() }); }; diff --git a/lib/ace/document_test.js b/lib/ace/document_test.js index ee27f00b..d7cc7ccb 100644 --- a/lib/ace/document_test.js +++ b/lib/ace/document_test.js @@ -296,6 +296,25 @@ module.exports = { "test: empty document has to contain one line": function() { var doc = new Document(""); assert.equal(doc.$lines.length, 1); + }, + + "test: inserting huge delta": function() { + var doc = new Document(""); + var val = ""; + var MAX = 0xF000; + for (var i = 0; i < 10 * MAX; i++) { + val += i + "\n" + } + doc.setValue(val); + assert.equal(doc.getValue(), val); + + for (var i = 3 * MAX + 2; i >= 3 * MAX - 2; i--) { + val = doc.getLines(0, i).join("\n"); + doc.setValue("\nab"); + assert.equal(doc.getValue(), "\nab"); + doc.insert({row: 1, column: 1}, val); + assert.equal(doc.getValue(), "\na" + val + "b"); + } } }; From d619e4e746fe553f2f64cc33538bc1d9486347e5 Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 20 Nov 2014 23:02:34 +0400 Subject: [PATCH 30/92] allow passing newLineCharacter ot getValue --- lib/ace/document.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ace/document.js b/lib/ace/document.js index 9207b3f5..a06e3e33 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -83,8 +83,8 @@ var Document = function(textOrLines) { /** * Returns all the lines in the document as a single string, joined by the new line character. **/ - this.getValue = function(newLineCharacter) { - return this.getAllLines().join(newLineCharacter || this.getNewLineCharacter()); + this.getValue = function() { + return this.getAllLines().join(this.getNewLineCharacter()); }; /** @@ -213,8 +213,8 @@ var Document = function(textOrLines) { * * @returns {String} **/ - this.getTextRange = function(range, newLineCharacter) { - return this.getLinesForRange(range).join(newLineCharacter || this.getNewLineCharacter()); + this.getTextRange = function(range) { + return this.getLinesForRange(range).join(this.getNewLineCharacter()); }; /** From 004a19855a6ca4c0636f48db28f0fb902cfe2ffc Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 20 Nov 2014 23:23:46 +0400 Subject: [PATCH 31/92] do not throw for broken anchors since that can break editor --- lib/ace/anchor.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/ace/anchor.js b/lib/ace/anchor.js index ff85c13b..39b78e0f 100644 --- a/lib/ace/anchor.js +++ b/lib/ace/anchor.js @@ -70,7 +70,6 @@ var Anchor = exports.Anchor = function(doc, row, column) { * @returns {Object} **/ this.getPosition = function() { - //process.exit(2) return this.$clipPositionToDocument(this.row, this.column); }; @@ -107,7 +106,7 @@ var Anchor = exports.Anchor = function(doc, row, column) { if (delta.start.row > this.row) return; - var point = this.$getTransformedPoint(delta, {row: this.row, column: this.column}); + var point = $getTransformedPoint(delta, {row: this.row, column: this.column}, this.$insertRight); this.setPosition(point.row, point.column, true); }; @@ -116,14 +115,13 @@ var Anchor = exports.Anchor = function(doc, row, column) { return (point1.row < point2.row) || (point1.row == point2.row && bColIsAfter); } - this.$getTransformedPoint = function (delta, point) { + function $getTransformedPoint(delta, point, moveIfEqual) { // Get delta info. - var moveIfEqual = this.$insertRight; - var deltaIsInsert = (delta.action == "insert") + var deltaIsInsert = delta.action == "insert"; var deltaRowShift = (deltaIsInsert ? 1 : -1) * (delta.end.row - delta.start.row); var deltaColShift = (deltaIsInsert ? 1 : -1) * (delta.end.column - delta.start.column); - var deltaStart = delta.start; - var deltaEnd = (deltaIsInsert ? deltaStart : delta.end); // Collapse insert range. + var deltaStart = delta.start; + var deltaEnd = deltaIsInsert ? deltaStart : delta.end; // Collapse insert range. // DELTA AFTER POINT: No change needed. if ($pointsInOrder(point, deltaStart, moveIfEqual)) { @@ -142,14 +140,13 @@ var Anchor = exports.Anchor = function(doc, row, column) { } // DELTA ENVELOPS POINT (delete only): Move point to delta start. - if (delta.action != "remove") - throw "Delete action expected."; + // TODO warn if delta.action != "remove" ? return { row: deltaStart.row, column: deltaStart.column }; - }; + } /** * Sets the anchor position to the specified row and column. If `noClip` is `true`, the position is not clipped. From eec012b24e8a39fef32631aab509d7e363f296ab Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 20 Nov 2014 23:28:09 +0400 Subject: [PATCH 32/92] fix ignoring empty delta --- lib/ace/document.js | 2 +- lib/ace/document_test.js | 10 ++++++++++ lib/ace/worker/mirror.js | 8 ++++---- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/ace/document.js b/lib/ace/document.js index a06e3e33..cc72bc63 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -570,7 +570,7 @@ var Document = function(textOrLines) { this.applyDelta = function(delta, doNotValidate) { var isInsert = delta.action == "insert"; // An empty range is a NOOP. - if (isInsert ? !delta.lines.length + if (isInsert ? delta.lines.length <= 1 && !delta.lines[0] : !Range.comparePoints(delta.start, delta.end)) return; diff --git a/lib/ace/document_test.js b/lib/ace/document_test.js index d7cc7ccb..ada56cd2 100644 --- a/lib/ace/document_test.js +++ b/lib/ace/document_test.js @@ -298,6 +298,16 @@ module.exports = { assert.equal(doc.$lines.length, 1); }, + "test: ignore empty delta": function() { + var doc = new Document(""); + doc.on("change", function() { + throw "should ignore empty delta"; + }) + doc.insert({row: 0, column: 0}, ""); + doc.insert({row: 1, column: 1}, ""); + doc.remove({start: {row: 1, column: 1}, end: {row: 1, column: 1}}); + }, + "test: inserting huge delta": function() { var doc = new Document(""); var val = ""; diff --git a/lib/ace/worker/mirror.js b/lib/ace/worker/mirror.js index 514d377f..ef6e2aa3 100644 --- a/lib/ace/worker/mirror.js +++ b/lib/ace/worker/mirror.js @@ -18,11 +18,11 @@ var Mirror = exports.Mirror = function(sender) { doc.applyDeltas(data); } else { for (var i = 0; i < data.length; i += 2) { - if (Array.isArray(data[i+1])) + if (Array.isArray(data[i+1])) { var d = {action: "insert", start: data[i], lines: data[i+1]}; - else - var d = {action: "remove", start: data[i],end: data[i+1]}; - + } else { + var d = {action: "remove", start: data[i], end: data[i+1]}; + } doc.applyDelta(d, true); } } From 60dd224d96c266c0c77dd0b9c95d55c9299de3a0 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sat, 20 Dec 2014 19:53:51 +0400 Subject: [PATCH 33/92] remove old delta shim from vim mode --- lib/ace/keyboard/vim.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/ace/keyboard/vim.js b/lib/ace/keyboard/vim.js index d3a01ebc..c2aa161b 100644 --- a/lib/ace/keyboard/vim.js +++ b/lib/ace/keyboard/vim.js @@ -170,13 +170,6 @@ define(function(require, exports, module) { return this.ace.inVirtualSelectionMode && this.ace.selection.index; }; this.onChange = function(delta) { - var oldDelta = delta.data; - delta = { - start: oldDelta.range.start, - end: oldDelta.range.end, - action: oldDelta.action, - lines: oldDelta.lines || [oldDelta.text] - };// v1.2 api compatibility if (delta.action[0] == 'i') { var change = { text: delta.lines }; var curOp = this.curOp = this.curOp || {}; @@ -559,7 +552,6 @@ define(function(require, exports, module) { highlight.session = null; }; highlight.updateOnChange = function(delta) { - delta = delta.data.range;// v1.2 api compatibility var row = delta.start.row; if (row == delta.end.row) highlight.cache[row] = undefined; else highlight.cache.splice(row, highlight.cache.length); From 56cb246c7841e5197b3fce08ee9684dda56d271a Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 21 Dec 2014 12:22:17 +0400 Subject: [PATCH 34/92] max stack size on chrome is smaller now --- lib/ace/document.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/ace/document.js b/lib/ace/document.js index cc72bc63..7b3e6221 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -571,11 +571,12 @@ var Document = function(textOrLines) { var isInsert = delta.action == "insert"; // An empty range is a NOOP. if (isInsert ? delta.lines.length <= 1 && !delta.lines[0] - : !Range.comparePoints(delta.start, delta.end)) + : !Range.comparePoints(delta.start, delta.end)) { return; + } - if (isInsert && delta.lines.length > 0xF000) - this.$splitAndapplyLargeDelta(delta, 0xF000); + if (isInsert && delta.lines.length > 20000) + this.$splitAndapplyLargeDelta(delta, 20000); // Apply. applyDelta(this.$lines, delta, doNotValidate); @@ -585,8 +586,8 @@ var Document = function(textOrLines) { this.$splitAndapplyLargeDelta = function(delta, MAX) { // Split large insert deltas. This is necessary because: // 1. We need to support splicing delta lines into the document via $lines.splice.apply(...) - // 2. fn.apply() doesn't work for a large number of params. The smallest threshold is on safari 0xFFFF. - // we use 0xF000 to leave some space for actual stack + // 2. fn.apply() doesn't work for a large number of params. The smallest threshold is on chrome 40 ~42000. + // we use 20000 to leave some space for actual stack // // To Do: Ideally we'd be consistent and also split 'delete' deltas. We don't do this now, because delete // delta handling is too slow. If we make delete delta handling faster we can split all large deltas @@ -623,10 +624,9 @@ var Document = function(textOrLines) { * @param {Object} delta A delta object (can include "insert" and "remove" actions) **/ this.revertDelta = function(delta) { - var range = Range.fromPoints(delta.start, delta.end); this.applyDelta({ - start: range.start, - end: range.end, + start: this.clonePos(delta.start), + end: this.clonePos(delta.end), action: (delta.action == "insert" ? "remove" : "insert"), lines: delta.lines.slice() }); From 5482db1c4d4286a1d9fbc1ccc9fbf58e0d2911e2 Mon Sep 17 00:00:00 2001 From: Kevin Ushey Date: Sat, 18 Apr 2015 15:19:28 -0700 Subject: [PATCH 35/92] [vim] fix 'Y' behaviour in visual mode --- lib/ace/keyboard/vim.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/keyboard/vim.js b/lib/ace/keyboard/vim.js index 2eae6b28..697af972 100644 --- a/lib/ace/keyboard/vim.js +++ b/lib/ace/keyboard/vim.js @@ -6085,5 +6085,5 @@ dom.importCssString(".normal-mode .ace_cursor{\ exports.handler.actions = actions; exports.Vim = Vim; - Vim.map("Y", "yy"); + Vim.map("Y", "yy", "normal"); }); From b0ce5630c91bab91eb8424a337fde267f6d251de Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 25 Jan 2015 16:13:24 +0400 Subject: [PATCH 36/92] add support for bindkey.position --- lib/ace/keyboard/hash_handler.js | 35 ++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/lib/ace/keyboard/hash_handler.js b/lib/ace/keyboard/hash_handler.js index 06badccd..7a0396fc 100644 --- a/lib/ace/keyboard/hash_handler.js +++ b/lib/ace/keyboard/hash_handler.js @@ -87,9 +87,12 @@ MultiHashHandler.prototype = HashHandler.prototype; } }; - this.bindKey = function(key, command, asDefault) { - if (typeof key == "object") + this.bindKey = function(key, command, position) { + if (typeof key == "object") { + if (position == undefined) + position = key.position; key = key[this.platform]; + } if (!key) return; if (typeof command == "function") @@ -110,11 +113,15 @@ MultiHashHandler.prototype = HashHandler.prototype; } var binding = this.parseKeys(keyPart); var id = KEY_MODS[binding.hashId] + binding.key; - this._addCommandToBinding(chain + id, command, asDefault); + this._addCommandToBinding(chain + id, command, position); }, this); }; - this._addCommandToBinding = function(keyId, command, asDefault) { + function getPosition(command) { + return typeof command == "object" && command.bindKey + && command.bindKey.position || 0; + } + this._addCommandToBinding = function(keyId, command, position) { var ckb = this.commandKeyBinding, i; if (!command) { delete ckb[keyId]; @@ -126,11 +133,21 @@ MultiHashHandler.prototype = HashHandler.prototype; } else if ((i = ckb[keyId].indexOf(command)) != -1) { ckb[keyId].splice(i, 1); } - - if (asDefault || command.isDefault) - ckb[keyId].unshift(command); - else - ckb[keyId].push(command); + + if (typeof position != "number") { + if (position || command.isDefault) + position = -100; + else + position = getPosition(command); + } + var commands = ckb[keyId]; + for (i = 0; i < commands.length; i++) { + var other = commands[i]; + var otherPos = getPosition(other); + if (otherPos > position) + break; + } + commands.splice(i, 0, command); } }; From 2713d0a8db79bf02dbb9ef21ff0889c085725ce8 Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 26 Jan 2015 19:44:27 +0400 Subject: [PATCH 37/92] center cursor after vim jumplist motions Conflicts: lib/ace/editor.js --- lib/ace/editor.js | 10 +++++++--- lib/ace/keyboard/vim.js | 4 ++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 6ce561bb..8f051394 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -162,8 +162,12 @@ var Editor = function(renderer, session) { var command = this.curOp.command; if (command.name && this.$blockScrolling > 0) this.$blockScrolling--; - if (command && command.scrollIntoView) { - switch (command.scrollIntoView) { + var scrollIntoView = command && command.scrollIntoView; + if (scrollIntoView) { + switch (scrollIntoView) { + case "center-animate": + scrollIntoView = "animate"; + /* fall through */ case "center": this.renderer.scrollCursorIntoView(null, 0.5); break; @@ -181,7 +185,7 @@ var Editor = function(renderer, session) { default: break; } - if (command.scrollIntoView == "animate") + if (scrollIntoView == "animate") this.renderer.animateScrolling(this.curOp.scrollTop); } diff --git a/lib/ace/keyboard/vim.js b/lib/ace/keyboard/vim.js index 1b4507b3..5538c01f 100644 --- a/lib/ace/keyboard/vim.js +++ b/lib/ace/keyboard/vim.js @@ -264,6 +264,7 @@ define(function(require, exports, module) { } if (!this.ace.inVirtualSelectionMode) this.ace.exitMultiSelectMode(); + this.ace.session.unfold({row: line, column: ch}); this.ace.selection.moveTo(line, ch); }; this.getCursor = function(p) { @@ -2204,6 +2205,8 @@ dom.importCssString(".normal-mode .ace_cursor{\ return; } if (motionArgs.toJumplist) { + if (!operator) + cm.ace.curOp.command.scrollIntoView = "center-animate"; // ace patch var jumpList = vimGlobalState.jumpList; // if the current motion is # or *, use cachedCursor var cachedCursor = jumpList.cachedCursor; @@ -2890,6 +2893,7 @@ dom.importCssString(".normal-mode .ace_cursor{\ var markPos = mark ? mark.find() : undefined; markPos = markPos ? markPos : cm.getCursor(); cm.setCursor(markPos); + cm.ace.curOp.command.scrollIntoView = "center-animate"; // ace patch }, scroll: function(cm, actionArgs, vim) { if (vim.visualMode) { From 487408e84dc3cb4853d803f0b89264b8173aada5 Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 20 Feb 2015 14:49:50 +0400 Subject: [PATCH 38/92] fix +6291 Hard to tell what pane has focus in Vim mode --- lib/ace/keyboard/vim.js | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/lib/ace/keyboard/vim.js b/lib/ace/keyboard/vim.js index 5538c01f..c1ac3738 100644 --- a/lib/ace/keyboard/vim.js +++ b/lib/ace/keyboard/vim.js @@ -738,10 +738,14 @@ CodeMirror.defineExtension = function(name, fn) { CodeMirror.prototype[name] = fn; }; dom.importCssString(".normal-mode .ace_cursor{\ - border: 0!important;\ + border: 1px solid red;\ background-color: red;\ opacity: 0.5;\ -}.ace_dialog {\ +}\ +.normal-mode .ace_hidden-cursors .ace_cursor{\ + background-color: transparent;\ +}\ +.ace_dialog {\ position: absolute;\ left: 0; right: 0;\ background: white;\ @@ -767,23 +771,6 @@ dom.importCssString(".normal-mode .ace_cursor{\ font-family: monospace;\ }", "vimMode"); (function() { - function dialogDiv(cm, template, bottom) { - var wrap = cm.ace.container; - var dialog; - dialog = wrap.appendChild(document.createElement("div")); - if (bottom) - dialog.className = "ace_dialog ace_dialog-bottom"; - else - dialog.className = "ace_dialog ace_dialog-top"; - - if (typeof template == "string") { - dialog.innerHTML = template; - } else { // Assuming it's a detached DOM element. - dialog.appendChild(template); - } - return dialog; - } - function closeNotification(cm, newVal) { if (cm.state.currentNotificationClose) cm.state.currentNotificationClose(); @@ -5944,7 +5931,7 @@ dom.importCssString(".normal-mode .ace_cursor{\ }, true); } return isHandled; - }; + } exports.CodeMirror = CodeMirror; var getVim = Vim.maybeInitVimState_; exports.handler = { @@ -5958,9 +5945,9 @@ dom.importCssString(".normal-mode .ace_cursor{\ if (!vim.insertMode) { var isbackwards = !sel.cursor ? session.selection.isBackwards() || session.selection.isEmpty() - : Range.comparePoints(sel.cursor, sel.start) <= 0 + : Range.comparePoints(sel.cursor, sel.start) <= 0; if (!isbackwards && left > w) - left -= w + left -= w; } if (!vim.insertMode && vim.status) { h = h / 2; @@ -6124,13 +6111,13 @@ dom.importCssString(".normal-mode .ace_cursor{\ }; var renderVirtualNumbers = { getText: function(session, row) { - return (Math.abs(session.selection.lead.row - row) || (row + 1 + (row < 9? "\xb7" : "" ))) + "" + return (Math.abs(session.selection.lead.row - row) || (row + 1 + (row < 9? "\xb7" : "" ))) + ""; }, getWidth: function(session, lastLineNumber, config) { return session.getLength().toString().length * config.characterWidth; }, update: function(e, editor) { - editor.renderer.$loop.schedule(editor.renderer.CHANGE_GUTTER) + editor.renderer.$loop.schedule(editor.renderer.CHANGE_GUTTER); }, attach: function(editor) { editor.renderer.$gutterLayer.$renderer = this; @@ -6175,7 +6162,7 @@ dom.importCssString(".normal-mode .ace_cursor{\ if (cm.ace.inVirtualSelectionMode) cm.ace.on("beforeEndOperation", delayedExecAceCommand); else - delayedExecAceCommand(null, cm.ace) + delayedExecAceCommand(null, cm.ace); }; function delayedExecAceCommand(op, ace) { ace.off("beforeEndOperation", delayedExecAceCommand); From ff4429dd5a7aec9b31d30111ef02694fc946b3fc Mon Sep 17 00:00:00 2001 From: nightwing Date: Sat, 28 Feb 2015 01:20:42 +0400 Subject: [PATCH 39/92] fixes for emacs mode --- .../commands/incremental_search_commands.js | 77 +++++++------------ lib/ace/incremental_search.js | 34 ++++---- lib/ace/keyboard/emacs.js | 4 +- lib/ace/keyboard/hash_handler.js | 12 ++- 4 files changed, 58 insertions(+), 69 deletions(-) diff --git a/lib/ace/commands/incremental_search_commands.js b/lib/ace/commands/incremental_search_commands.js index a0a0fe5f..59083a85 100644 --- a/lib/ace/commands/incremental_search_commands.js +++ b/lib/ace/commands/incremental_search_commands.js @@ -69,18 +69,14 @@ exports.iSearchCommands = [{ bindKey: {win: "Ctrl-F", mac: "Command-F"}, exec: function(iSearch) { iSearch.cancelSearch(true); - }, - readOnly: true, - isIncrementalSearchCommand: true + } }, { name: "searchForward", bindKey: {win: "Ctrl-S|Ctrl-K", mac: "Ctrl-S|Command-G"}, exec: function(iSearch, options) { options.useCurrentOrPrevSearch = true; iSearch.next(options); - }, - readOnly: true, - isIncrementalSearchCommand: true + } }, { name: "searchBackward", bindKey: {win: "Ctrl-R|Ctrl-Shift-K", mac: "Ctrl-R|Command-Shift-G"}, @@ -88,42 +84,30 @@ exports.iSearchCommands = [{ options.useCurrentOrPrevSearch = true; options.backwards = true; iSearch.next(options); - }, - readOnly: true, - isIncrementalSearchCommand: true + } }, { name: "extendSearchTerm", exec: function(iSearch, string) { iSearch.addString(string); - }, - readOnly: true, - isIncrementalSearchCommand: true + } }, { name: "extendSearchTermSpace", bindKey: "space", - exec: function(iSearch) { iSearch.addString(' '); }, - readOnly: true, - isIncrementalSearchCommand: true + exec: function(iSearch) { iSearch.addString(' '); } }, { name: "shrinkSearchTerm", bindKey: "backspace", exec: function(iSearch) { iSearch.removeChar(); - }, - readOnly: true, - isIncrementalSearchCommand: true + } }, { name: 'confirmSearch', bindKey: 'return', - exec: function(iSearch) { iSearch.deactivate(); }, - readOnly: true, - isIncrementalSearchCommand: true + exec: function(iSearch) { iSearch.deactivate(); } }, { name: 'cancelSearch', bindKey: 'esc|Ctrl-G', - exec: function(iSearch) { iSearch.deactivate(true); }, - readOnly: true, - isIncrementalSearchCommand: true + exec: function(iSearch) { iSearch.deactivate(true); } }, { name: 'occurisearch', bindKey: 'Ctrl-O', @@ -131,9 +115,7 @@ exports.iSearchCommands = [{ var options = oop.mixin({}, iSearch.$options); iSearch.deactivate(); occurStartCommand.exec(iSearch.$editor, options); - }, - readOnly: true, - isIncrementalSearchCommand: true + } }, { name: "yankNextWord", bindKey: "Ctrl-w", @@ -142,9 +124,7 @@ exports.iSearchCommands = [{ range = ed.selection.getRangeOfMovements(function(sel) { sel.moveCursorWordRight(); }), string = ed.session.getTextRange(range); iSearch.addString(string); - }, - readOnly: true, - isIncrementalSearchCommand: true + } }, { name: "yankNextChar", bindKey: "Ctrl-Alt-y", @@ -153,15 +133,11 @@ exports.iSearchCommands = [{ range = ed.selection.getRangeOfMovements(function(sel) { sel.moveCursorRight(); }), string = ed.session.getTextRange(range); iSearch.addString(string); - }, - readOnly: true, - isIncrementalSearchCommand: true + } }, { name: 'recenterTopBottom', bindKey: 'Ctrl-l', - exec: function(iSearch) { iSearch.$editor.execCommand('recenterTopBottom'); }, - readOnly: true, - isIncrementalSearchCommand: true + exec: function(iSearch) { iSearch.$editor.execCommand('recenterTopBottom'); } }, { name: 'selectAllMatches', bindKey: 'Ctrl-space', @@ -173,18 +149,19 @@ exports.iSearchCommands = [{ return ranges.concat(ea ? ea : []); }, []) : []; iSearch.deactivate(false); ranges.forEach(ed.selection.addRange.bind(ed.selection)); - }, - readOnly: true, - isIncrementalSearchCommand: true + } }, { name: 'searchAsRegExp', bindKey: 'Alt-r', exec: function(iSearch) { iSearch.convertNeedleToRegExp(); - }, - readOnly: true, - isIncrementalSearchCommand: true -}]; + } +}].map(function(cmd) { + cmd.readOnly = true; + cmd.isIncrementalSearchCommand = true; + cmd.scrollIntoView = "animate-cursor"; + return cmd; +}); function IncrementalSearchKeyboardHandler(iSearch) { this.$iSearch = iSearch; @@ -192,7 +169,7 @@ function IncrementalSearchKeyboardHandler(iSearch) { oop.inherits(IncrementalSearchKeyboardHandler, HashHandler); -;(function() { +(function() { this.attach = function(editor) { var iSearch = this.$iSearch; @@ -201,15 +178,19 @@ oop.inherits(IncrementalSearchKeyboardHandler, HashHandler); if (!e.command.isIncrementalSearchCommand) return undefined; e.stopPropagation(); e.preventDefault(); - return e.command.exec(iSearch, e.args || {}); + var scrollTop = editor.session.getScrollTop(); + var result = e.command.exec(iSearch, e.args || {}); + editor.renderer.scrollCursorIntoView(null, 0.5); + editor.renderer.animateScrolling(scrollTop); + return result; }); - } + }; this.detach = function(editor) { if (!this.$commandExecHandler) return; editor.commands.removeEventListener('exec', this.$commandExecHandler); delete this.$commandExecHandler; - } + }; var handleKeyboard$super = this.handleKeyboard; this.handleKeyboard = function(data, hashId, key, keyCode) { @@ -222,7 +203,7 @@ oop.inherits(IncrementalSearchKeyboardHandler, HashHandler); if (extendCmd) { return {command: extendCmd, args: key}; } } return {command: "null", passEvent: hashId == 0 || hashId == 4}; - } + }; }).call(IncrementalSearchKeyboardHandler.prototype); diff --git a/lib/ace/incremental_search.js b/lib/ace/incremental_search.js index a64e1857..e4c1bdf8 100644 --- a/lib/ace/incremental_search.js +++ b/lib/ace/incremental_search.js @@ -105,7 +105,7 @@ function objectToRegExp(obj) { this.$mousedownHandler = ed.addEventListener('mousedown', this.onMouseDown.bind(this)); this.selectionFix(ed); this.statusMessage(true); - } + }; this.deactivate = function(reset) { this.cancelSearch(reset); @@ -117,7 +117,7 @@ function objectToRegExp(obj) { } ed.onPaste = this.$originalEditorOnPaste; this.message(''); - } + }; this.selectionFix = function(editor) { // Fix selection bug: When clicked inside the editor @@ -128,7 +128,7 @@ function objectToRegExp(obj) { if (editor.selection.isEmpty() && !editor.session.$emacsMark) { editor.clearSelection(); } - } + }; this.highlight = function(regexp) { var sess = this.$editor.session, @@ -136,7 +136,7 @@ function objectToRegExp(obj) { new SearchHighlight(null, "ace_isearch-result", "text")); hl.setRegexp(regexp); sess._emit("changeBackMarker"); // force highlight layer redraw - } + }; this.cancelSearch = function(reset) { var e = this.$editor; @@ -150,7 +150,7 @@ function objectToRegExp(obj) { } this.highlight(null); return Range.fromPoints(this.$currentPos, this.$currentPos); - } + }; this.highlightAndFindWithNeedle = function(moveToNext, needleUpdateFunc) { if (!this.$editor) return null; @@ -163,7 +163,7 @@ function objectToRegExp(obj) { if (options.needle.length === 0) { this.statusMessage(true); return this.cancelSearch(true); - }; + } // try to find the next occurence and enable highlighting marker options.start = this.$currentPos; @@ -176,13 +176,13 @@ function objectToRegExp(obj) { this.$editor.selection.setRange(Range.fromPoints(shouldSelect ? this.$startPos : found.end, found.end)); if (moveToNext) this.$currentPos = found.end; // highlight after cursor move, so selection works properly - this.highlight(options.re) + this.highlight(options.re); } this.statusMessage(found); return found; - } + }; this.addString = function(s) { return this.highlightAndFindWithNeedle(false, function(needle) { @@ -192,7 +192,7 @@ function objectToRegExp(obj) { reObj.expression += s; return objectToRegExp(reObj); }); - } + }; this.removeChar = function(c) { return this.highlightAndFindWithNeedle(false, function(needle) { @@ -202,7 +202,7 @@ function objectToRegExp(obj) { reObj.expression = reObj.expression.substring(0, reObj.expression.length-1); return objectToRegExp(reObj); }); - } + }; this.next = function(options) { // try to find the next occurence of whatever we have searched for @@ -215,29 +215,29 @@ function objectToRegExp(obj) { return options.useCurrentOrPrevSearch && needle.length === 0 ? this.$prevNeedle || '' : needle; }); - } + }; this.onMouseDown = function(evt) { // when mouse interaction happens then we quit incremental search this.deactivate(); return true; - } + }; this.onPaste = function(text) { this.addString(text); - } + }; this.convertNeedleToRegExp = function() { return this.highlightAndFindWithNeedle(false, function(needle) { return isRegExp(needle) ? needle : stringToRegExp(needle, 'ig'); }); - } + }; this.convertNeedleToString = function() { return this.highlightAndFindWithNeedle(false, function(needle) { return isRegExp(needle) ? regExpToObject(needle).expression : needle; }); - } + }; this.statusMessage = function(found) { var options = this.$options, msg = ''; @@ -245,7 +245,7 @@ function objectToRegExp(obj) { msg += 'isearch: ' + options.needle; msg += found ? '' : ' (not found)'; this.message(msg); - } + }; this.message = function(msg) { if (this.$editor.showCommandLine) { @@ -254,7 +254,7 @@ function objectToRegExp(obj) { } else { console.log(msg); } - } + }; }).call(IncrementalSearch.prototype); diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 945eddf3..8cecad1b 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -428,7 +428,7 @@ exports.emacsKeys = { "M-;": "togglecomment", "C-/|C-x u|S-C--|C-z": "undo", - "S-C-/|S-C-x u|C--|S-C-z": "redo", //infinite undo? + "S-C-/|S-C-x u|C--|S-C-z": "redo", // infinite undo? // vertical editing "C-x r": "selectRectangularRegion", "M-x": {command: "focusCommandLine", args: "M-x "} @@ -483,7 +483,7 @@ exports.handler.addCommands({ // different. Deactivate the mark when setMark is run with active // mark if (transientMarkModeActive && (mark || !hasNoSelection)) { - if (editor.inMultiSelectMode) editor.forEachSelection({exec: editor.clearSelection.bind(editor)}) + if (editor.inMultiSelectMode) editor.forEachSelection({exec: editor.clearSelection.bind(editor)}); else editor.clearSelection(); if (mark) editor.pushEmacsMark(null); return; diff --git a/lib/ace/keyboard/hash_handler.js b/lib/ace/keyboard/hash_handler.js index 7a0396fc..a7dc1a93 100644 --- a/lib/ace/keyboard/hash_handler.js +++ b/lib/ace/keyboard/hash_handler.js @@ -236,10 +236,18 @@ MultiHashHandler.prototype = HashHandler.prototype; } } - if (data.$keyChain && keyCode > 0) - data.$keyChain = ""; + if (data.$keyChain) { + if ((!hashId || hashId == 4) && keyString.length == 1) + data.$keyChain = data.$keyChain.slice(0, -key.length - 1); // wait for input + else if (hashId == -1 || keyCode > 0) + data.$keyChain = ""; // reset keyChain + } return {command: command}; }; + + this.getStatusText = function(editor, data) { + return data.$keyChain || ""; + }; }).call(HashHandler.prototype); From ee90854917cb5ec02158a0a471ece29fd0f09df8 Mon Sep 17 00:00:00 2001 From: sevin7676 Date: Sun, 19 Apr 2015 08:18:04 -0400 Subject: [PATCH 40/92] bug fix & typo fix --- demo/kitchen-sink/docs/sqlserver.sqlserver | 19 ++++++++++++------- lib/ace/mode/sqlserver_highlight_rules.js | 6 ++++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/demo/kitchen-sink/docs/sqlserver.sqlserver b/demo/kitchen-sink/docs/sqlserver.sqlserver index 329930af..86c679a5 100644 --- a/demo/kitchen-sink/docs/sqlserver.sqlserver +++ b/demo/kitchen-sink/docs/sqlserver.sqlserver @@ -1,9 +1,9 @@ -- ============================================= -- Author: Morgan Yarbrough -- Create date: 4/27/2015 --- Description: Test Procedure that shows off language features. --- Includes non-standard folding using region comments using either --- line comments or block comments (both are demonstrated below) +-- Description: Test procedure that shows off language features. +-- Includes non-standard folding with region comments using either +-- line comments or block comments (both are demonstrated below). -- This mode imitates SSMS and it designed to be used with SQL Server theme. -- ============================================= CREATE PROCEDURE dbo.TestProcedure @@ -11,7 +11,7 @@ CREATE PROCEDURE dbo.TestProcedure --#region parameters @vint INT = 1 ,@vdate DATE = NULL - ,@vdatetime DATETIME = DATEADD (dd, 1, GETDATE()) + ,@vdatetime DATETIME = DATEADD(dd, 1, GETDATE()) ,@vvarchar VARCHAR(MAX) = '' --#endregion @@ -38,8 +38,8 @@ BEGIN -- another folding demonstration IF @vint = 1 BEGIN - SET @vvarchar='one' - SET @vint = DATEDIFFT(dd, @vdate, @vdatetime) + SET @vvarchar = 'one' + SET @vint = DATEDIFF(dd, @vdate, @vdatetime) END -- this mode handles strings properly @@ -47,7 +47,7 @@ BEGIN FROM Orders WHERE @OrderDate > GETDATE()' - -- this mode is aware of build in stored procedures + -- this mode is aware of built in stored procedures EXECUTE sp_executesql @sql -- demonstrating some syntax highlighting @@ -60,4 +60,9 @@ BEGIN WHERE CompanyName NOT LIKE '%something' OR CompanyName IS NULL OR CompanyName IN ('bla', 'nothing') + + -- this mode includes snippets + -- place your cusor at the end of the line below and trigger auto complete (Ctrl+Space) + createpr + END diff --git a/lib/ace/mode/sqlserver_highlight_rules.js b/lib/ace/mode/sqlserver_highlight_rules.js index 42e193d7..5e703e5b 100644 --- a/lib/ace/mode/sqlserver_highlight_rules.js +++ b/lib/ace/mode/sqlserver_highlight_rules.js @@ -121,10 +121,12 @@ var SqlServerHighlightRules = function() { // createKeywordMapper ignores case which we want because SqlServer keywords are not case sensitive which causes our keywords to get changed to lowercase. // However, the preferred standard for keywords is uppercase, so this transforms them back to uppercase for code completion - // EXCEPTION: build in stored procedures are lower case + // EXCEPTION: built in stored procedures are lower case + builtInStoredProcedures = builtInStoredProcedures.split('|'); for (var i = 0; i < this.$keywordList.length; i++) { var keyword = this.$keywordList[i]; if (builtInStoredProcedures.indexOf(keyword) !== -1) continue; + this.$keywordList[i] = keyword.toUpperCase(); } @@ -171,7 +173,7 @@ var SqlServerHighlightRules = function() { regex: "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b" }, { token: keywordMapper, - regex: "@{0,2}[a-zA-Z_$][a-zA-Z0-9_$]*\\b" //up to 2 @symbols for some build in functions + regex: "@{0,2}[a-zA-Z_$][a-zA-Z0-9_$]*\\b" //up to 2 @symbols for some built in functions }, { token: "constant.class", regex: "@@?[a-zA-Z_$][a-zA-Z0-9_$]*\\b" From cf0c81a25fa5373409b086d824e4405698622f64 Mon Sep 17 00:00:00 2001 From: sevin7676 Date: Sun, 19 Apr 2015 08:37:22 -0400 Subject: [PATCH 41/92] more snippets --- lib/ace/snippets/sqlserver.snippets | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/ace/snippets/sqlserver.snippets b/lib/ace/snippets/sqlserver.snippets index 51c00ae4..403bd6bc 100644 --- a/lib/ace/snippets/sqlserver.snippets +++ b/lib/ace/snippets/sqlserver.snippets @@ -22,6 +22,16 @@ snippet dateadd # DATEFROMPARTS snippet datefromparts DATEFROMPARTS(${1:year}, ${2:month}, ${3:day}) +# OBJECT_DEFINITION +snippet objectdef + SELECT OBJECT_DEFINITION(OBJECT_ID('${1:sys.server_permissions /*object name*/}')) +# STUFF XML +snippet stuffxml + STUFF((SELECT ', ' + ${1:ColumnName} + FROM ${2:TableName} + WHERE ${3:WhereClause} + FOR XML PATH('')), 1, 1, '') AS ${4:Alias} + ${5:/*https://msdn.microsoft.com/en-us/library/ms188043.aspx*/} # Create Procedure snippet createproc -- ============================================= @@ -30,12 +40,14 @@ snippet createproc -- Description: ${3:Description} -- ============================================= CREATE PROCEDURE ${4:Procedure_Name} - -- Add the parameters for the stored procedure here + ${5:/*Add the parameters for the stored procedure here*/} AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from interfering with SELECT statements. SET NOCOUNT ON; + ${6:/*Add the T-SQL statements to compute the return value here*/} + END GO # Create Scalar Function @@ -52,5 +64,7 @@ snippet createfn BEGIN DECLARE @Result ${5:Function_Data_Type} + ${6:/*Add the T-SQL statements to compute the return value here*/} + END GO \ No newline at end of file From 80f43beb7396f4cdb4adb4f076205b784c6bb2fc Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 31 Mar 2015 11:06:33 +0400 Subject: [PATCH 42/92] fix typo --- lib/ace/multi_select_test.js | 12 ++++++------ lib/ace/selection.js | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/ace/multi_select_test.js b/lib/ace/multi_select_test.js index 1eccc4ce..f63038d6 100644 --- a/lib/ace/multi_select_test.js +++ b/lib/ace/multi_select_test.js @@ -82,11 +82,11 @@ function setSelection(editor, data) { return isBackwards ? { start: end, end: start, - isBackwards: true + isBackwards: isBackwards } : { start: start, end: end, - isBackwards: true + isBackwards: isBackwards }; })); } @@ -223,19 +223,19 @@ module.exports = { assert.equal(editor.getValue(),"l1\nl1\nl2\nl2\nl3\nl3\nl4\nl4"); testSelection(editor, [[1,0],[3,0],[5,0],[7,0]]); - setSelection(editor, [[1,2],[1,0,1,1],[3,0,3,1],[5,0,5,1],[7,0,7,1]]); + setSelection(editor, [[1,2],[1,1,1,0],[3,0,3,1],[5,0,5,1],[7,0,7,1]]); exec("copylinesdown"); exec("copylinesup"); assert.equal(editor.getValue(),"l1\nl1\nl1\nl1\nl2\nl2\nl2\nl2\nl3\nl3\nl3\nl3\nl4\nl4\nl4\nl4"); - testSelection(editor, [[2,2],[2,0,2,1],[6,0,6,1],[10,0,10,1],[14,0,14,1]]); + testSelection(editor, [[2,2],[2,1,2,0],[6,0,6,1],[10,0,10,1],[14,0,14,1]]); exec("movelinesdown", 12); assert.equal(editor.getValue(),"l1\nl1\nl1\nl2\nl2\nl2\nl3\nl3\nl3\nl4\nl4\nl4\nl1\nl2\nl3\nl4"); - testSelection(editor, [[12,2],[12,0,12,1],[13,0,13,1],[14,0,14,1],[15,0,15,1]]); + testSelection(editor, [[12,2],[12,1,12,0],[13,0,13,1],[14,0,14,1],[15,0,15,1]]); exec("movelinesup", 12); assert.equal(editor.getValue(),"l1\nl2\nl3\nl4\nl1\nl1\nl1\nl2\nl2\nl2\nl3\nl3\nl3\nl4\nl4\nl4"); - testSelection(editor, [[0,2],[0,0,0,1],[1,0,1,1],[2,0,2,1],[3,0,3,1]]); + testSelection(editor, [[0,2],[0,1,0,0],[1,0,1,1],[2,0,2,1],[3,0,3,1]]); }, "test multiselect fromJSON/toJSON": function() { diff --git a/lib/ace/selection.js b/lib/ace/selection.js index b712fc6b..ed57682f 100644 --- a/lib/ace/selection.js +++ b/lib/ace/selection.js @@ -923,7 +923,7 @@ var Selection = function(session) { this.toSingleRange(data[0]); for (var i = data.length; i--; ) { var r = Range.fromPoints(data[i].start, data[i].end); - if (data.isBackwards) + if (data[i].isBackwards) r.cursor = r.start; this.addRange(r, true); } From 4e6d48bf2e6908e6f17fcaa4a3850c27f645fc53 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 31 Mar 2015 17:00:25 +0400 Subject: [PATCH 43/92] make sure operationEnd events are not emitted to wrong session --- lib/ace/editor.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 8f051394..f00f74f6 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -278,6 +278,10 @@ var Editor = function(renderer, session) { this.setSession = function(session) { if (this.session == session) return; + + // make sure operationEnd events are not emitted to wrong session + if (this.curOp) this.endOperation(); + this.curOp = {}; var oldSession = this.session; if (oldSession) { @@ -377,6 +381,8 @@ var Editor = function(renderer, session) { oldSession: oldSession }); + this.curOp = null; + oldSession && oldSession._signal("changeEditor", {oldEditor: this}); session && session._signal("changeEditor", {editor: this}); }; From 04f4292fbcb4faebdf544c1b896b753866fac5d6 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 10 Mar 2015 20:56:20 +0400 Subject: [PATCH 44/92] enable Emmet for handlebars mode --- lib/ace/ext/emmet.js | 2 +- lib/ace/mode/xml.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ace/ext/emmet.js b/lib/ace/ext/emmet.js index ceb155ef..4faacef2 100644 --- a/lib/ace/ext/emmet.js +++ b/lib/ace/ext/emmet.js @@ -393,7 +393,7 @@ exports.updateCommands = function(editor, enabled) { }; exports.isSupportedMode = function(modeId) { - return modeId && /css|less|scss|sass|stylus|html|php|twig|ejs/.test(modeId); + return modeId && /css|less|scss|sass|stylus|html|php|twig|ejs|handlebars/.test(modeId); }; var onChangeMode = function(e, target) { diff --git a/lib/ace/mode/xml.js b/lib/ace/mode/xml.js index 8c7033f1..38861eee 100644 --- a/lib/ace/mode/xml.js +++ b/lib/ace/mode/xml.js @@ -53,7 +53,7 @@ oop.inherits(Mode, TextMode); this.blockComment = {start: ""}; - this.createWorker = function(session) { + this.createWorker = function(session) { var worker = new WorkerClient(["ace"], "ace/mode/xml_worker", "Worker"); worker.attachToDocument(session.getDocument()); From 27284cd29ff284769b49e453b7473467881b75be Mon Sep 17 00:00:00 2001 From: sevin7676 Date: Sun, 19 Apr 2015 11:23:10 -0400 Subject: [PATCH 45/92] added completionModifier allows a highlight rule modify keyword completions --- lib/ace/mode/text.js | 6 +++++- lib/ace/mode/text_highlight_rules.js | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/ace/mode/text.js b/lib/ace/mode/text.js index f04f1722..f8f0581d 100644 --- a/lib/ace/mode/text.js +++ b/lib/ace/mode/text.js @@ -368,14 +368,18 @@ var Mode = function() { }; this.getCompletions = function(state, session, pos, prefix) { + var self = this; var keywords = this.$keywordList || this.$createKeywordList(); return keywords.map(function(word) { - return { + var r = { name: word, value: word, score: 0, meta: "keyword" }; + return self.$highlightRules.completionModifier ? self.$highlightRules.completionModifier.call(self, r) : r; + }).filter(function(value) { + return value !== undefined; }); }; diff --git a/lib/ace/mode/text_highlight_rules.js b/lib/ace/mode/text_highlight_rules.js index ae72040b..fc481442 100644 --- a/lib/ace/mode/text_highlight_rules.js +++ b/lib/ace/mode/text_highlight_rules.js @@ -227,6 +227,14 @@ var TextHighlightRules = function() { this.getKeywords = function() { return this.$keywords; }; + + /** + * Function that can be set by HighlightRules to modify a keyword completion. + * The function receives {name, value, score, meta} + * and should return the same type or undefined to skip adding the result to + * the completion list. + */ + this.completionModifier = null; }).call(TextHighlightRules.prototype); From 961f8c4893ff397b9ca5e5bfff8b4cf04c04586f Mon Sep 17 00:00:00 2001 From: sevin7676 Date: Sun, 19 Apr 2015 11:24:30 -0400 Subject: [PATCH 46/92] completion modifier for SQL and JavaScript --- lib/ace/mode/javascript_highlight_rules.js | 24 +++++++++++ lib/ace/mode/sqlserver_highlight_rules.js | 49 +++++++++++++++------- 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/lib/ace/mode/javascript_highlight_rules.js b/lib/ace/mode/javascript_highlight_rules.js index 5bdcce12..fd23e7d9 100644 --- a/lib/ace/mode/javascript_highlight_rules.js +++ b/lib/ace/mode/javascript_highlight_rules.js @@ -64,6 +64,30 @@ var JavaScriptHighlightRules = function(options) { "alert", "constant.language.boolean": "true|false" }, "identifier"); + + // modify keyword completions meta display and remove things that are irrelevant + var modCache = {}; + this.completionModifier = function(obj) { + var n = obj.name; + if (n.search('__parent__|__count__|proto__') !== -1) return; //can't cache reserved keys + + if (modCache[n] === undefined) { + if (n.search('enum|await|implements|package|protected|static|interface|private|public') !== -1) modCache[n] = ''; //future + else if (n.search('Namespace|QName|XML|XMLList') !== -1) modCache[n] = ''; //E4X is obsolete + else if (n.search('Iterator|ParallelArray|StopIeration') !== -1) modCache[n] = ''; //non standard + else if (n.search('Array|Boolean|Date|Function|Number|Object|RegExp|String|Proxy') !== -1) modCache[n] = 'object'; + else if (n.search('Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError') !== -1) modCache[n] = 'object'; + else if (n.search('alert|eval|isFinite|isNan|parseFloat|parseInt|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|escape|unescape') !== -1) modCache[n] = 'function'; + else if (n.search('null|Infinity|NaN|undefined') !== -1) modCache[n] = 'constant'; + else if (n.search('const|let|var|function|class|get|set') !== -1) modCache[n] = 'declaration'; + else if (n.search('true|false') !== -1) modCache[n] = 'boolean'; + else modCache[n] = 'keyword'; + } + + obj.meta = modCache[n]; + if (obj.meta === '') return; //don't show obsolete + return obj; + }; // keywords which can be followed by regular expressions var kwBeforeRe = "case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void"; diff --git a/lib/ace/mode/sqlserver_highlight_rules.js b/lib/ace/mode/sqlserver_highlight_rules.js index 5e703e5b..1098764e 100644 --- a/lib/ace/mode/sqlserver_highlight_rules.js +++ b/lib/ace/mode/sqlserver_highlight_rules.js @@ -77,7 +77,9 @@ var SqlServerHighlightRules = function() { /* https://msdn.microsoft.com/en-us/library/ms177520.aspx */ "@@CONNECTIONS|@@CPU_BUSY|@@IDLE|@@IO_BUSY|@@PACKET_ERRORS|@@PACK_RECEIVED|@@PACK_SENT|@@TIMETICKS|@@TOTAL_ERRORS|@@TOTAL_READ|@@TOTAL_WRITE|FN_VIRTUALFILESTATS|" + /* https://msdn.microsoft.com/en-us/library/ms188353.aspx */ - "PATINDEX|TEXTPTR|TEXTVALID" + "PATINDEX|TEXTPTR|TEXTVALID|" + + /* other */ + "COALESCE|NULLIF" ); @@ -104,12 +106,14 @@ var SqlServerHighlightRules = function() { keywords += "|TYPE"; - //remove any other built in things from key word list + //remove specific built in types from keyword list keywords = keywords.split('|'); keywords = keywords.filter(function(value, index, self) { return logicalOperators.split('|').indexOf(value) === -1 && builtinFunctions.split('|').indexOf(value) === -1 && dataTypes.split('|').indexOf(value) === -1; }); keywords = keywords.sort().join('|'); + + var keywordMapper = this.createKeywordMapper({ "constant.language": logicalOperators, "storage.type": dataTypes, @@ -117,19 +121,7 @@ var SqlServerHighlightRules = function() { "support.storedprocedure": builtInStoredProcedures, "keyword": keywords, }, "identifier", true); - - - // createKeywordMapper ignores case which we want because SqlServer keywords are not case sensitive which causes our keywords to get changed to lowercase. - // However, the preferred standard for keywords is uppercase, so this transforms them back to uppercase for code completion - // EXCEPTION: built in stored procedures are lower case - builtInStoredProcedures = builtInStoredProcedures.split('|'); - for (var i = 0; i < this.$keywordList.length; i++) { - var keyword = this.$keywordList[i]; - if (builtInStoredProcedures.indexOf(keyword) !== -1) continue; - this.$keywordList[i] = keyword.toUpperCase(); - } - //https://msdn.microsoft.com/en-us/library/ms190356.aspx var setStatements = "SET ANSI_DEFAULTS|SET ANSI_NULLS|SET ANSI_NULL_DFLT_OFF|SET ANSI_NULL_DFLT_ON|SET ANSI_PADDING|SET ANSI_WARNINGS|SET ARITHABORT|SET ARITHIGNORE|SET CONCAT_NULL_YIELDS_NULL|SET CURSOR_CLOSE_ON_COMMIT|SET DATEFIRST|SET DATEFORMAT|SET DEADLOCK_PRIORITY|SET FIPS_FLAGGER|SET FMTONLY|SET FORCEPLAN|SET IDENTITY_INSERT|SET IMPLICIT_TRANSACTIONS|SET LANGUAGE|SET LOCK_TIMEOUT|SET NOCOUNT|SET NOEXEC|SET NUMERIC_ROUNDABORT|SET OFFSETS|SET PARSEONLY|SET QUERY_GOVERNOR_COST_LIMIT|SET QUOTED_IDENTIFIER|SET REMOTE_PROC_TRANSACTIONS|SET ROWCOUNT|SET SHOWPLAN_ALL|SET SHOWPLAN_TEXT|SET SHOWPLAN_XML|SET STATISTICS IO|SET STATISTICS PROFILE|SET STATISTICS TIME|SET STATISTICS XML|SET TEXTSIZE|SET XACT_ABORT".split('|'); @@ -218,6 +210,35 @@ var SqlServerHighlightRules = function() { this.embedRules(DocCommentHighlightRules, "doc-", [DocCommentHighlightRules.getEndRule("start")]); this.normalizeRules(); + + + // prepare key word types for completion modifier + builtInStoredProcedures = builtInStoredProcedures.split('|'); + logicalOperators = logicalOperators.split('|'); + builtinFunctions = builtinFunctions.split('|'); + dataTypes = dataTypes.split('|'); + + this.completionModifier = function(obj) { + var word = obj.name, + meta = 'keyword'; + + if (builtInStoredProcedures.indexOf(word) !== -1) meta = "storedProcedure"; + else { + //all others are upper case + word = word.toUpperCase(); + if (builtinFunctions.indexOf(word) !== -1) meta = "function"; + else if (dataTypes.indexOf(word) !== -1) meta = "dataType"; + else if (setStatements.indexOf(word) !== -1) meta = "setStatement"; + else if (logicalOperators.indexOf(word) !== -1) meta = "logicalOperator"; + } + + return { + name: word, + value: word, + score: 0, + meta: meta + }; + }; }; oop.inherits(SqlServerHighlightRules, TextHighlightRules); From 1027dcea7c892c6aa076148a373f3ada99f882b4 Mon Sep 17 00:00:00 2001 From: sevin7676 Date: Sun, 19 Apr 2015 11:52:54 -0400 Subject: [PATCH 47/92] cache modifier --- lib/ace/mode/sqlserver_highlight_rules.js | 36 +++++++++++++---------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/lib/ace/mode/sqlserver_highlight_rules.js b/lib/ace/mode/sqlserver_highlight_rules.js index 1098764e..6d23f6a3 100644 --- a/lib/ace/mode/sqlserver_highlight_rules.js +++ b/lib/ace/mode/sqlserver_highlight_rules.js @@ -218,26 +218,30 @@ var SqlServerHighlightRules = function() { builtinFunctions = builtinFunctions.split('|'); dataTypes = dataTypes.split('|'); + var modCache = {}; this.completionModifier = function(obj) { var word = obj.name, meta = 'keyword'; - - if (builtInStoredProcedures.indexOf(word) !== -1) meta = "storedProcedure"; - else { - //all others are upper case - word = word.toUpperCase(); - if (builtinFunctions.indexOf(word) !== -1) meta = "function"; - else if (dataTypes.indexOf(word) !== -1) meta = "dataType"; - else if (setStatements.indexOf(word) !== -1) meta = "setStatement"; - else if (logicalOperators.indexOf(word) !== -1) meta = "logicalOperator"; + + if (modCache[word] === undefined) { + if (builtInStoredProcedures.indexOf(word) !== -1) meta = "storedProcedure"; + else { + //all others are upper case + word = word.toUpperCase(); + if (builtinFunctions.indexOf(word) !== -1) meta = "function"; + else if (dataTypes.indexOf(word) !== -1) meta = "dataType"; + else if (setStatements.indexOf(word) !== -1) meta = "setStatement"; + else if (logicalOperators.indexOf(word) !== -1) meta = "logicalOperator"; + } + modCache[word] = { + word: word, + value: word, + meta: meta, + score: 0 + }; } - - return { - name: word, - value: word, - score: 0, - meta: meta - }; + + return modCache[word]; }; }; From 1ec6eb7b5d1f779972f08e1d293e1c39174a57f6 Mon Sep 17 00:00:00 2001 From: sevin7676 Date: Sun, 19 Apr 2015 11:59:31 -0400 Subject: [PATCH 48/92] shorter names --- lib/ace/mode/sqlserver_highlight_rules.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ace/mode/sqlserver_highlight_rules.js b/lib/ace/mode/sqlserver_highlight_rules.js index 6d23f6a3..4a7a7658 100644 --- a/lib/ace/mode/sqlserver_highlight_rules.js +++ b/lib/ace/mode/sqlserver_highlight_rules.js @@ -224,14 +224,14 @@ var SqlServerHighlightRules = function() { meta = 'keyword'; if (modCache[word] === undefined) { - if (builtInStoredProcedures.indexOf(word) !== -1) meta = "storedProcedure"; + if (builtInStoredProcedures.indexOf(word) !== -1) meta = "procedure"; else { //all others are upper case word = word.toUpperCase(); if (builtinFunctions.indexOf(word) !== -1) meta = "function"; - else if (dataTypes.indexOf(word) !== -1) meta = "dataType"; - else if (setStatements.indexOf(word) !== -1) meta = "setStatement"; - else if (logicalOperators.indexOf(word) !== -1) meta = "logicalOperator"; + else if (dataTypes.indexOf(word) !== -1) meta = "type"; + else if (setStatements.indexOf(word) !== -1) meta = "statement"; + else if (logicalOperators.indexOf(word) !== -1) meta = "operator"; } modCache[word] = { word: word, From e610365e675695a7e083724b0752c9de839d46bd Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 20 Apr 2015 01:11:10 +0400 Subject: [PATCH 49/92] fix #2432 setPolling(false) doesn't turn off polling --- lib/ace/layer/font_metrics.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ace/layer/font_metrics.js b/lib/ace/layer/font_metrics.js index 7e71f8a8..715c6cf3 100644 --- a/lib/ace/layer/font_metrics.js +++ b/lib/ace/layer/font_metrics.js @@ -120,9 +120,9 @@ var FontMetrics = exports.FontMetrics = function(parentEl, interval) { this.setPolling = function(val) { if (val) { this.$pollSizeChanges(); - } else { - if (this.$pollSizeChangesTimer) - this.$pollSizeChangesTimer; + } else if (this.$pollSizeChangesTimer) { + clearInterval(this.$pollSizeChangesTimer); + this.$pollSizeChangesTimer = 0; } }; From e58fa3e6d74c6aeecdcac435e38ff390b060fb39 Mon Sep 17 00:00:00 2001 From: sevin7676 Date: Mon, 20 Apr 2015 04:50:25 -0400 Subject: [PATCH 50/92] remove completionModifer, one off for SQL Server Mode --- lib/ace/mode/javascript_highlight_rules.js | 24 ------------- lib/ace/mode/sqlserver.js | 30 ++++++++++++++++ lib/ace/mode/sqlserver_highlight_rules.js | 40 +++++----------------- lib/ace/mode/text.js | 6 +--- lib/ace/mode/text_highlight_rules.js | 8 ----- 5 files changed, 40 insertions(+), 68 deletions(-) diff --git a/lib/ace/mode/javascript_highlight_rules.js b/lib/ace/mode/javascript_highlight_rules.js index fd23e7d9..5bdcce12 100644 --- a/lib/ace/mode/javascript_highlight_rules.js +++ b/lib/ace/mode/javascript_highlight_rules.js @@ -64,30 +64,6 @@ var JavaScriptHighlightRules = function(options) { "alert", "constant.language.boolean": "true|false" }, "identifier"); - - // modify keyword completions meta display and remove things that are irrelevant - var modCache = {}; - this.completionModifier = function(obj) { - var n = obj.name; - if (n.search('__parent__|__count__|proto__') !== -1) return; //can't cache reserved keys - - if (modCache[n] === undefined) { - if (n.search('enum|await|implements|package|protected|static|interface|private|public') !== -1) modCache[n] = ''; //future - else if (n.search('Namespace|QName|XML|XMLList') !== -1) modCache[n] = ''; //E4X is obsolete - else if (n.search('Iterator|ParallelArray|StopIeration') !== -1) modCache[n] = ''; //non standard - else if (n.search('Array|Boolean|Date|Function|Number|Object|RegExp|String|Proxy') !== -1) modCache[n] = 'object'; - else if (n.search('Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError') !== -1) modCache[n] = 'object'; - else if (n.search('alert|eval|isFinite|isNan|parseFloat|parseInt|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|escape|unescape') !== -1) modCache[n] = 'function'; - else if (n.search('null|Infinity|NaN|undefined') !== -1) modCache[n] = 'constant'; - else if (n.search('const|let|var|function|class|get|set') !== -1) modCache[n] = 'declaration'; - else if (n.search('true|false') !== -1) modCache[n] = 'boolean'; - else modCache[n] = 'keyword'; - } - - obj.meta = modCache[n]; - if (obj.meta === '') return; //don't show obsolete - return obj; - }; // keywords which can be followed by regular expressions var kwBeforeRe = "case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void"; diff --git a/lib/ace/mode/sqlserver.js b/lib/ace/mode/sqlserver.js index 0f66c1e5..352e2ac8 100644 --- a/lib/ace/mode/sqlserver.js +++ b/lib/ace/mode/sqlserver.js @@ -46,6 +46,36 @@ oop.inherits(Mode, TextMode); (function() { this.lineCommentStart = "--"; this.blockComment = {start: "/*", end: "*/"}; + + /** + * Override keyword completions to ensure proper case for completions + * and use smart meta tags instead of 'keyword' for all completions. + */ + this.getCompletions = function(state, session, pos, prefix) { + if (this.getCompletionsResult) return this.getCompletionsResult; + + var keywords = this.$keywordList || this.$createKeywordList(); + var types = session.$mode.$highlightRules.keywordTypes; + + this.getCompletionsResult = keywords.map(function(word) { + var meta = 'keyword'; + if (types.builtInStoredProcedures.indexOf(word) !== -1) meta = "procedure"; + else { + word = word.toUpperCase(); //all others are upper case + if (types.builtinFunctions.indexOf(word) !== -1) meta = "function"; + else if (types.dataTypes.indexOf(word) !== -1) meta = "type"; + else if (types.setStatements.indexOf(word) !== -1) meta = "statement"; + else if (types.logicalOperators.indexOf(word) !== -1) meta = "operator"; + } + return { + name: word, + value: word, + score: 0, + meta: meta + }; + }); + return this.getCompletionsResult; + }; this.$id = "ace/mode/sql"; }).call(Mode.prototype); diff --git a/lib/ace/mode/sqlserver_highlight_rules.js b/lib/ace/mode/sqlserver_highlight_rules.js index 4a7a7658..c8770641 100644 --- a/lib/ace/mode/sqlserver_highlight_rules.js +++ b/lib/ace/mode/sqlserver_highlight_rules.js @@ -211,41 +211,19 @@ var SqlServerHighlightRules = function() { this.normalizeRules(); - - // prepare key word types for completion modifier - builtInStoredProcedures = builtInStoredProcedures.split('|'); - logicalOperators = logicalOperators.split('|'); - builtinFunctions = builtinFunctions.split('|'); - dataTypes = dataTypes.split('|'); - - var modCache = {}; - this.completionModifier = function(obj) { - var word = obj.name, - meta = 'keyword'; - - if (modCache[word] === undefined) { - if (builtInStoredProcedures.indexOf(word) !== -1) meta = "procedure"; - else { - //all others are upper case - word = word.toUpperCase(); - if (builtinFunctions.indexOf(word) !== -1) meta = "function"; - else if (dataTypes.indexOf(word) !== -1) meta = "type"; - else if (setStatements.indexOf(word) !== -1) meta = "statement"; - else if (logicalOperators.indexOf(word) !== -1) meta = "operator"; - } - modCache[word] = { - word: word, - value: word, - meta: meta, - score: 0 - }; - } - - return modCache[word]; + // export types for overriding get completions + this.keywordTypes = { + builtInStoredProcedures: builtInStoredProcedures.split('|'), + logicalOperators: logicalOperators.split('|'), + builtinFunctions: builtinFunctions.split('|'), + dataTypes: dataTypes.split('|'), + setStatements: setStatements, }; + }; oop.inherits(SqlServerHighlightRules, TextHighlightRules); exports.SqlHighlightRules = SqlServerHighlightRules; +console.log('exports',exports); }); diff --git a/lib/ace/mode/text.js b/lib/ace/mode/text.js index f8f0581d..f04f1722 100644 --- a/lib/ace/mode/text.js +++ b/lib/ace/mode/text.js @@ -368,18 +368,14 @@ var Mode = function() { }; this.getCompletions = function(state, session, pos, prefix) { - var self = this; var keywords = this.$keywordList || this.$createKeywordList(); return keywords.map(function(word) { - var r = { + return { name: word, value: word, score: 0, meta: "keyword" }; - return self.$highlightRules.completionModifier ? self.$highlightRules.completionModifier.call(self, r) : r; - }).filter(function(value) { - return value !== undefined; }); }; diff --git a/lib/ace/mode/text_highlight_rules.js b/lib/ace/mode/text_highlight_rules.js index fc481442..ae72040b 100644 --- a/lib/ace/mode/text_highlight_rules.js +++ b/lib/ace/mode/text_highlight_rules.js @@ -227,14 +227,6 @@ var TextHighlightRules = function() { this.getKeywords = function() { return this.$keywords; }; - - /** - * Function that can be set by HighlightRules to modify a keyword completion. - * The function receives {name, value, score, meta} - * and should return the same type or undefined to skip adding the result to - * the completion list. - */ - this.completionModifier = null; }).call(TextHighlightRules.prototype); From 68d531ecb29d5d23bedcbc157533c9b067d83f31 Mon Sep 17 00:00:00 2001 From: sevin7676 Date: Mon, 20 Apr 2015 04:52:18 -0400 Subject: [PATCH 51/92] cleanup --- lib/ace/mode/sqlserver_highlight_rules.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ace/mode/sqlserver_highlight_rules.js b/lib/ace/mode/sqlserver_highlight_rules.js index c8770641..1f5a8697 100644 --- a/lib/ace/mode/sqlserver_highlight_rules.js +++ b/lib/ace/mode/sqlserver_highlight_rules.js @@ -225,5 +225,4 @@ var SqlServerHighlightRules = function() { oop.inherits(SqlServerHighlightRules, TextHighlightRules); exports.SqlHighlightRules = SqlServerHighlightRules; -console.log('exports',exports); }); From 662cd11975b368753f54a2970cd318edd374b6ac Mon Sep 17 00:00:00 2001 From: sevin7676 Date: Mon, 20 Apr 2015 05:08:54 -0400 Subject: [PATCH 52/92] no cache as its storing reference in filtered list --- lib/ace/mode/sqlserver.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/ace/mode/sqlserver.js b/lib/ace/mode/sqlserver.js index 352e2ac8..82542640 100644 --- a/lib/ace/mode/sqlserver.js +++ b/lib/ace/mode/sqlserver.js @@ -52,12 +52,10 @@ oop.inherits(Mode, TextMode); * and use smart meta tags instead of 'keyword' for all completions. */ this.getCompletions = function(state, session, pos, prefix) { - if (this.getCompletionsResult) return this.getCompletionsResult; - var keywords = this.$keywordList || this.$createKeywordList(); var types = session.$mode.$highlightRules.keywordTypes; - this.getCompletionsResult = keywords.map(function(word) { + return keywords.map(function(word) { var meta = 'keyword'; if (types.builtInStoredProcedures.indexOf(word) !== -1) meta = "procedure"; else { @@ -74,7 +72,6 @@ oop.inherits(Mode, TextMode); meta: meta }; }); - return this.getCompletionsResult; }; this.$id = "ace/mode/sql"; From 67c7da75b67aea2e49aee4111117cf1c2f6e1700 Mon Sep 17 00:00:00 2001 From: sevin7676 Date: Mon, 20 Apr 2015 08:52:03 -0400 Subject: [PATCH 53/92] fix escape character was using wrong escape char for string, sqlserver uses double single quote to escape a single quote --- lib/ace/mode/sqlserver_highlight_rules.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/mode/sqlserver_highlight_rules.js b/lib/ace/mode/sqlserver_highlight_rules.js index 1f5a8697..335e702c 100644 --- a/lib/ace/mode/sqlserver_highlight_rules.js +++ b/lib/ace/mode/sqlserver_highlight_rules.js @@ -141,7 +141,7 @@ var SqlServerHighlightRules = function() { regex: "'", next: [{ token: "constant.language.escape", - regex: /\\'/ + regex: /''/ }, { token: "string.end", next: "start", From 694d7431b7f3b7cfec0c23161c73fa85e9387c67 Mon Sep 17 00:00:00 2001 From: Brian Jordan Date: Tue, 21 Apr 2015 14:58:29 -0700 Subject: [PATCH 54/92] Remove unused variable `text` --- lib/ace/ext/language_tools.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ace/ext/language_tools.js b/lib/ace/ext/language_tools.js index f02390ee..563fe58a 100644 --- a/lib/ace/ext/language_tools.js +++ b/lib/ace/ext/language_tools.js @@ -155,7 +155,6 @@ function getCompletionPrefix(editor) { var doLiveAutocomplete = function(e) { var editor = e.editor; - var text = e.args || ""; var hasCompleter = editor.completer && editor.completer.activated; // We don't want to autocomplete with no prefix From 636ace51780710b6d6a34bf92811b01069692f30 Mon Sep 17 00:00:00 2001 From: nightwing Date: Wed, 22 Apr 2015 13:20:39 +0400 Subject: [PATCH 55/92] fix typo --- lib/ace/lib/event.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/lib/event.js b/lib/ace/lib/event.js index 06932d3c..16805ce2 100644 --- a/lib/ace/lib/event.js +++ b/lib/ace/lib/event.js @@ -244,7 +244,7 @@ function normalizeCommandKeys(callback, e, keyCode) { if (pressedKeys[keyCode] == 1) ts = e.timeStamp; } else if (keyCode === 18 && hashId === 3 && location === 2) { - var dt = e.timestamp - ts; + var dt = e.timeStamp - ts; if (dt < 50) pressedKeys.altGr = true; } From 82453d884b1ee588ae54924e4792d18b6c38a988 Mon Sep 17 00:00:00 2001 From: sevin7676 Date: Wed, 22 Apr 2015 05:28:31 -0400 Subject: [PATCH 56/92] better completions, no unneeded string rule --- lib/ace/mode/sqlserver.js | 26 ++--------- lib/ace/mode/sqlserver_highlight_rules.js | 57 ++++++++++++----------- 2 files changed, 33 insertions(+), 50 deletions(-) diff --git a/lib/ace/mode/sqlserver.js b/lib/ace/mode/sqlserver.js index 82542640..5f24730c 100644 --- a/lib/ace/mode/sqlserver.js +++ b/lib/ace/mode/sqlserver.js @@ -48,32 +48,12 @@ oop.inherits(Mode, TextMode); this.blockComment = {start: "/*", end: "*/"}; /** - * Override keyword completions to ensure proper case for completions - * and use smart meta tags instead of 'keyword' for all completions. + * Override keyword completions using list created in highlight rules */ this.getCompletions = function(state, session, pos, prefix) { - var keywords = this.$keywordList || this.$createKeywordList(); - var types = session.$mode.$highlightRules.keywordTypes; - - return keywords.map(function(word) { - var meta = 'keyword'; - if (types.builtInStoredProcedures.indexOf(word) !== -1) meta = "procedure"; - else { - word = word.toUpperCase(); //all others are upper case - if (types.builtinFunctions.indexOf(word) !== -1) meta = "function"; - else if (types.dataTypes.indexOf(word) !== -1) meta = "type"; - else if (types.setStatements.indexOf(word) !== -1) meta = "statement"; - else if (types.logicalOperators.indexOf(word) !== -1) meta = "operator"; - } - return { - name: word, - value: word, - score: 0, - meta: meta - }; - }); + return session.$mode.$highlightRules.completions; }; - + this.$id = "ace/mode/sql"; }).call(Mode.prototype); diff --git a/lib/ace/mode/sqlserver_highlight_rules.js b/lib/ace/mode/sqlserver_highlight_rules.js index 335e702c..c0179e93 100644 --- a/lib/ace/mode/sqlserver_highlight_rules.js +++ b/lib/ace/mode/sqlserver_highlight_rules.js @@ -86,15 +86,15 @@ var SqlServerHighlightRules = function() { // https://msdn.microsoft.com/en-us/library/ms187752.aspx var dataTypes = ("BIGINT|BINARY|BIT|CHAR|CURSOR|DATE|DATETIME|DATETIME2|DATETIMEOFFSET|DECIMAL|FLOAT|HIERARCHYID|IMAGE|INTEGER|INT|MONEY|NCHAR|NTEXT|NUMERIC|NVARCHAR|REAL|SMALLDATETIME|SMALLINT|SMALLMONEY|SQL_VARIANT|TABLE|TEXT|TIME|TIMESTAMP|TINYINT|UNIQUEIDENTIFIER|VARBINARY|VARCHAR|XML"); - + //https://msdn.microsoft.com/en-us/library/ms176007.aspx (these are lower case!) var builtInStoredProcedures = "sp_addextendedproc|sp_addextendedproperty|sp_addmessage|sp_addtype|sp_addumpdevice|sp_add_data_file_recover_suspect_db|sp_add_log_file_recover_suspect_db|sp_altermessage|sp_attach_db|sp_attach_single_file_db|sp_autostats|sp_bindefault|sp_bindrule|sp_bindsession|sp_certify_removable|sp_clean_db_file_free_space|sp_clean_db_free_space|sp_configure|sp_control_plan_guide|sp_createstats|sp_create_plan_guide|sp_create_plan_guide_from_handle|sp_create_removable|sp_cycle_errorlog|sp_datatype_info|sp_dbcmptlevel|sp_dbmmonitoraddmonitoring|sp_dbmmonitorchangealert|sp_dbmmonitorchangemonitoring|sp_dbmmonitordropalert|sp_dbmmonitordropmonitoring|sp_dbmmonitorhelpalert|sp_dbmmonitorhelpmonitoring|sp_dbmmonitorresults|sp_db_increased_partitions|sp_delete_backuphistory|sp_depends|sp_describe_first_result_set|sp_describe_undeclared_parameters|sp_detach_db|sp_dropdevice|sp_dropextendedproc|sp_dropextendedproperty|sp_dropmessage|sp_droptype|sp_execute|sp_executesql|sp_getapplock|sp_getbindtoken|sp_help|sp_helpconstraint|sp_helpdb|sp_helpdevice|sp_helpextendedproc|sp_helpfile|sp_helpfilegroup|sp_helpindex|sp_helplanguage|sp_helpserver|sp_helpsort|sp_helpstats|sp_helptext|sp_helptrigger|sp_indexoption|sp_invalidate_textptr|sp_lock|sp_monitor|sp_prepare|sp_prepexec|sp_prepexecrpc|sp_procoption|sp_recompile|sp_refreshview|sp_releaseapplock|sp_rename|sp_renamedb|sp_resetstatus|sp_sequence_get_range|sp_serveroption|sp_setnetname|sp_settriggerorder|sp_spaceused|sp_tableoption|sp_unbindefault|sp_unbindrule|sp_unprepare|sp_updateextendedproperty|sp_updatestats|sp_validname|sp_who|sys.sp_merge_xtp_checkpoint_files|sys.sp_xtp_bind_db_resource_pool|sys.sp_xtp_checkpoint_force_garbage_collection|sys.sp_xtp_control_proc_exec_stats|sys.sp_xtp_control_query_exec_stats|sys.sp_xtp_unbind_db_resource_pool"; - + // https://msdn.microsoft.com/en-us/library/ms189822.aspx var keywords = "ABSOLUTE|ACTION|ADA|ADD|ADMIN|AFTER|AGGREGATE|ALIAS|ALL|ALLOCATE|ALTER|AND|ANY|ARE|ARRAY|AS|ASC|ASENSITIVE|ASSERTION|ASYMMETRIC|AT|ATOMIC|AUTHORIZATION|BACKUP|BEFORE|BEGIN|BETWEEN|BIT_LENGTH|BLOB|BOOLEAN|BOTH|BREADTH|BREAK|BROWSE|BULK|BY|CALL|CALLED|CARDINALITY|CASCADE|CASCADED|CASE|CATALOG|CHARACTER|CHARACTER_LENGTH|CHAR_LENGTH|CHECK|CHECKPOINT|CLASS|CLOB|CLOSE|CLUSTERED|COALESCE|COLLATE|COLLATION|COLLECT|COLUMN|COMMIT|COMPLETION|COMPUTE|CONDITION|CONNECT|CONNECTION|CONSTRAINT|CONSTRAINTS|CONSTRUCTOR|CONTAINS|CONTAINSTABLE|CONTINUE|CORR|CORRESPONDING|COVAR_POP|COVAR_SAMP|CREATE|CROSS|CUBE|CUME_DIST|CURRENT|CURRENT_CATALOG|CURRENT_DATE|CURRENT_DEFAULT_TRANSFORM_GROUP|CURRENT_PATH|CURRENT_ROLE|CURRENT_SCHEMA|CURRENT_TIME|CURRENT_TRANSFORM_GROUP_FOR_TYPE|CYCLE|DATA|DATABASE|DBCC|DEALLOCATE|DEC|DECLARE|DEFAULT|DEFERRABLE|DEFERRED|DELETE|DENY|DEPTH|DEREF|DESC|DESCRIBE|DESCRIPTOR|DESTROY|DESTRUCTOR|DETERMINISTIC|DIAGNOSTICS|DICTIONARY|DISCONNECT|DISK|DISTINCT|DISTRIBUTED|DOMAIN|DOUBLE|DROP|DUMP|DYNAMIC|EACH|ELEMENT|ELSE|END|END-EXEC|EQUALS|ERRLVL|ESCAPE|EVERY|EXCEPT|EXCEPTION|EXEC|EXECUTE|EXISTS|EXIT|EXTERNAL|EXTRACT|FETCH|FILE|FILLFACTOR|FILTER|FIRST|FOR|FOREIGN|FORTRAN|FOUND|FREE|FREETEXT|FREETEXTTABLE|FROM|FULL|FULLTEXTTABLE|FUNCTION|FUSION|GENERAL|GET|GLOBAL|GO|GOTO|GRANT|GROUP|HAVING|HOLD|HOLDLOCK|HOST|HOUR|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IGNORE|IMMEDIATE|IN|INCLUDE|INDEX|INDICATOR|INITIALIZE|INITIALLY|INNER|INOUT|INPUT|INSENSITIVE|INSERT|INTEGER|INTERSECT|INTERSECTION|INTERVAL|INTO|IS|ISOLATION|ITERATE|JOIN|KEY|KILL|LANGUAGE|LARGE|LAST|LATERAL|LEADING|LESS|LEVEL|LIKE|LIKE_REGEX|LIMIT|LINENO|LN|LOAD|LOCAL|LOCALTIME|LOCALTIMESTAMP|LOCATOR|MAP|MATCH|MEMBER|MERGE|METHOD|MINUTE|MOD|MODIFIES|MODIFY|MODULE|MULTISET|NAMES|NATIONAL|NATURAL|NCLOB|NEW|NEXT|NO|NOCHECK|NONCLUSTERED|NONE|NORMALIZE|NOT|NULL|NULLIF|OBJECT|OCCURRENCES_REGEX|OCTET_LENGTH|OF|OFF|OFFSETS|OLD|ON|ONLY|OPEN|OPERATION|OPTION|OR|ORDER|ORDINALITY|OUT|OUTER|OUTPUT|OVER|OVERLAPS|OVERLAY|PAD|PARAMETER|PARAMETERS|PARTIAL|PARTITION|PASCAL|PATH|PERCENT|PERCENTILE_CONT|PERCENTILE_DISC|PERCENT_RANK|PIVOT|PLAN|POSITION|POSITION_REGEX|POSTFIX|PRECISION|PREFIX|PREORDER|PREPARE|PRESERVE|PRIMARY|PRINT|PRIOR|PRIVILEGES|PROC|PROCEDURE|PUBLIC|RAISERROR|RANGE|READ|READS|READTEXT|RECONFIGURE|RECURSIVE|REF|REFERENCES|REFERENCING|REGR_AVGX|REGR_AVGY|REGR_COUNT|REGR_INTERCEPT|REGR_R2|REGR_SLOPE|REGR_SXX|REGR_SXY|REGR_SYY|RELATIVE|RELEASE|REPLICATION|RESTORE|RESTRICT|RESULT|RETURN|RETURNS|REVERT|REVOKE|ROLE|ROLLBACK|ROLLUP|ROUTINE|ROW|ROWCOUNT|ROWGUIDCOL|ROWS|RULE|SAVE|SAVEPOINT|SCHEMA|SCOPE|SCROLL|SEARCH|SECOND|SECTION|SECURITYAUDIT|SELECT|SEMANTICKEYPHRASETABLE|SEMANTICSIMILARITYDETAILSTABLE|SEMANTICSIMILARITYTABLE|SENSITIVE|SEQUENCE|SESSION|SET|SETS|SETUSER|SHUTDOWN|SIMILAR|SIZE|SOME|SPECIFIC|SPECIFICTYPE|SQL|SQLCA|SQLCODE|SQLERROR|SQLEXCEPTION|SQLSTATE|SQLWARNING|START|STATE|STATEMENT|STATIC|STATISTICS|STDDEV_POP|STDDEV_SAMP|STRUCTURE|SUBMULTISET|SUBSTRING_REGEX|SYMMETRIC|SYSTEM|TABLESAMPLE|TEMPORARY|TERMINATE|TEXTSIZE|THAN|THEN|TIMEZONE_HOUR|TIMEZONE_MINUTE|TO|TOP|TRAILING|TRAN|TRANSACTION|TRANSLATE|TRANSLATE_REGEX|TRANSLATION|TREAT|TRIGGER|TRIM|TRUNCATE|TSEQUAL|UESCAPE|UNDER|UNION|UNIQUE|UNKNOWN|UNNEST|UNPIVOT|UPDATE|UPDATETEXT|USAGE|USE|USER|USING|VALUE|VALUES|VARIABLE|VARYING|VAR_POP|VAR_SAMP|VIEW|WAITFOR|WHEN|WHENEVER|WHERE|WHILE|WIDTH_BUCKET|WINDOW|WITH|WITHIN|WITHIN GROUP|WITHOUT|WORK|WRITE|WRITETEXT|XMLAGG|XMLATTRIBUTES|XMLBINARY|XMLCAST|XMLCOMMENT|XMLCONCAT|XMLDOCUMENT|XMLELEMENT|XMLEXISTS|XMLFOREST|XMLITERATE|XMLNAMESPACES|XMLPARSE|XMLPI|XMLQUERY|XMLSERIALIZE|XMLTABLE|XMLTEXT|XMLVALIDATE|ZONE"; - + // Microsoft's keyword list is missing a lot of things that are located on various other pages // https://msdn.microsoft.com/en-us/library/ms187373.aspx, https://msdn.microsoft.com/en-us/library/ms181714.aspx keywords += "|KEEPIDENTITY|KEEPDEFAULTS|IGNORE_CONSTRAINTS|IGNORE_TRIGGERS|XLOCK|FORCESCAN|FORCESEEK|HOLDLOCK|NOLOCK|NOWAIT|PAGLOCK|READCOMMITTED|READCOMMITTEDLOCK|READPAST|READUNCOMMITTED|REPEATABLEREAD|ROWLOCK|SERIALIZABLE|SNAPSHOT|SPATIAL_WINDOW_MAX_CELLS|TABLOCK|TABLOCKX|UPDLOCK|XLOCK|IGNORE_NONCLUSTERED_COLUMNSTORE_INDEX|EXPAND|VIEWS|FAST|FORCE|KEEP|KEEPFIXED|MAXDOP|MAXRECURSION|OPTIMIZE|PARAMETERIZATION|SIMPLE|FORCED|RECOMPILE|ROBUST|PLAN|SPATIAL_WINDOW_MAX_CELLS|NOEXPAND|HINT"; @@ -105,7 +105,7 @@ var SqlServerHighlightRules = function() { // highlighted words in SSMS that I'm not even sure where they come from keywords += "|TYPE"; - + //remove specific built in types from keyword list keywords = keywords.split('|'); keywords = keywords.filter(function(value, index, self) { @@ -122,19 +122,15 @@ var SqlServerHighlightRules = function() { "keyword": keywords, }, "identifier", true); - + //https://msdn.microsoft.com/en-us/library/ms190356.aspx var setStatements = "SET ANSI_DEFAULTS|SET ANSI_NULLS|SET ANSI_NULL_DFLT_OFF|SET ANSI_NULL_DFLT_ON|SET ANSI_PADDING|SET ANSI_WARNINGS|SET ARITHABORT|SET ARITHIGNORE|SET CONCAT_NULL_YIELDS_NULL|SET CURSOR_CLOSE_ON_COMMIT|SET DATEFIRST|SET DATEFORMAT|SET DEADLOCK_PRIORITY|SET FIPS_FLAGGER|SET FMTONLY|SET FORCEPLAN|SET IDENTITY_INSERT|SET IMPLICIT_TRANSACTIONS|SET LANGUAGE|SET LOCK_TIMEOUT|SET NOCOUNT|SET NOEXEC|SET NUMERIC_ROUNDABORT|SET OFFSETS|SET PARSEONLY|SET QUERY_GOVERNOR_COST_LIMIT|SET QUOTED_IDENTIFIER|SET REMOTE_PROC_TRANSACTIONS|SET ROWCOUNT|SET SHOWPLAN_ALL|SET SHOWPLAN_TEXT|SET SHOWPLAN_XML|SET STATISTICS IO|SET STATISTICS PROFILE|SET STATISTICS TIME|SET STATISTICS XML|SET TEXTSIZE|SET XACT_ABORT".split('|'); var isolationLevels = "READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SNAPSHOP|SERIALIZABLE".split('|'); for (var i = 0; i < isolationLevels.length; i++) { setStatements.push('SET TRANSACTION ISOLATION LEVEL ' + isolationLevels[i]); } - //add set statements to keywordList for completions - for (var i = 0; i < setStatements.length; i++) { - this.$keywordList.push(setStatements[i]); - } - + this.$rules = { start: [{ token: "string.start", @@ -157,9 +153,6 @@ var SqlServerHighlightRules = function() { token: "comment", start: "/\\*", end: "\\*/" - }, { - token: "string", // ' string - regex: "'.*?'" }, { token: "constant.numeric", // float regex: "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b" @@ -196,30 +189,40 @@ var SqlServerHighlightRules = function() { caseInsensitive: true }], }; - + //add each set statment as regex at top of rules so that they are processed first because they require multiple words - //note, this makes the statements not match if they are not upper case.. which is not ideal but I don't know of an easy way to fix this + //note: this makes the statements not match if they are not upper case.. which is not ideal but I don't know of an easy way to fix this for (var i = 0; i < setStatements.length; i++) { - var statement = setStatements[i]; this.$rules.start.unshift({ token: "set.statement", - regex: statement + regex: setStatements[i] }); } - + this.embedRules(DocCommentHighlightRules, "doc-", [DocCommentHighlightRules.getEndRule("start")]); - this.normalizeRules(); - // export types for overriding get completions - this.keywordTypes = { - builtInStoredProcedures: builtInStoredProcedures.split('|'), - logicalOperators: logicalOperators.split('|'), - builtinFunctions: builtinFunctions.split('|'), - dataTypes: dataTypes.split('|'), - setStatements: setStatements, - }; + //prepare custom keyword completions used by mode to override default completor + //this allows for custom 'meta' and proper case of completions + var completions = []; + var addCompletions = function(arr, meta) { + arr.forEach(function(v) { + completions.push({ + name: v, + value: v, + score: 0, + meta: meta, + }); + }); + }; + addCompletions(builtInStoredProcedures.split('|'), 'procedure'); + addCompletions(logicalOperators.split('|'), 'operator'); + addCompletions(builtinFunctions.split('|'), 'function'); + addCompletions(dataTypes.split('|'), 'type'); + addCompletions(setStatements, 'statement'); + + this.completions = completions; }; oop.inherits(SqlServerHighlightRules, TextHighlightRules); From 87044e881680fd88997987a71a9ab1bb98a398a0 Mon Sep 17 00:00:00 2001 From: sevin7676 Date: Wed, 22 Apr 2015 05:38:01 -0400 Subject: [PATCH 57/92] forgot keyword completions --- lib/ace/mode/sqlserver_highlight_rules.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ace/mode/sqlserver_highlight_rules.js b/lib/ace/mode/sqlserver_highlight_rules.js index c0179e93..5844be18 100644 --- a/lib/ace/mode/sqlserver_highlight_rules.js +++ b/lib/ace/mode/sqlserver_highlight_rules.js @@ -221,6 +221,7 @@ var SqlServerHighlightRules = function() { addCompletions(builtinFunctions.split('|'), 'function'); addCompletions(dataTypes.split('|'), 'type'); addCompletions(setStatements, 'statement'); + addCompletions(keywords.split('|'), 'keyword'); this.completions = completions; }; From 2d7ebc34b69ff647214cf9575cf95331143480f6 Mon Sep 17 00:00:00 2001 From: sevin7676 Date: Wed, 22 Apr 2015 06:05:54 -0400 Subject: [PATCH 58/92] no keyword if in brackets --- demo/kitchen-sink/docs/sqlserver.sqlserver | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/demo/kitchen-sink/docs/sqlserver.sqlserver b/demo/kitchen-sink/docs/sqlserver.sqlserver index 86c679a5..7efd2b7e 100644 --- a/demo/kitchen-sink/docs/sqlserver.sqlserver +++ b/demo/kitchen-sink/docs/sqlserver.sqlserver @@ -65,4 +65,8 @@ BEGIN -- place your cusor at the end of the line below and trigger auto complete (Ctrl+Space) createpr + -- SQL Server allows using keywords as object names (not recommended) as long as they are wrapped in brackets + DATABASE -- keyword + [DATABASE] -- not a keyword + END From 5a3bf2570f09af9c915b364bede48e8cd05df163 Mon Sep 17 00:00:00 2001 From: sevin7676 Date: Wed, 22 Apr 2015 06:06:52 -0400 Subject: [PATCH 59/92] no keyword if in brackets --- lib/ace/mode/sqlserver_highlight_rules.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/mode/sqlserver_highlight_rules.js b/lib/ace/mode/sqlserver_highlight_rules.js index 5844be18..c914c0ae 100644 --- a/lib/ace/mode/sqlserver_highlight_rules.js +++ b/lib/ace/mode/sqlserver_highlight_rules.js @@ -158,7 +158,7 @@ var SqlServerHighlightRules = function() { regex: "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b" }, { token: keywordMapper, - regex: "@{0,2}[a-zA-Z_$][a-zA-Z0-9_$]*\\b" //up to 2 @symbols for some built in functions + regex: "@{0,2}[a-zA-Z_$][a-zA-Z0-9_$]*\\b(?!])" //up to 2 @symbols for some built in functions }, { token: "constant.class", regex: "@@?[a-zA-Z_$][a-zA-Z0-9_$]*\\b" From a842dcdee349905c8c83830c1ec5716bbe8640b8 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 26 Apr 2015 14:38:49 +0400 Subject: [PATCH 60/92] do not retain ref to the first editor (fixes #2469) --- experiments/debug_mem_leak.html | 59 +++++++++++++++++++++++++++++++++ lib/ace/lib/event.js | 9 ++--- 2 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 experiments/debug_mem_leak.html diff --git a/experiments/debug_mem_leak.html b/experiments/debug_mem_leak.html new file mode 100644 index 00000000..b8ee8726 --- /dev/null +++ b/experiments/debug_mem_leak.html @@ -0,0 +1,59 @@ + + + + + + + + +

+ +
+ + + \ No newline at end of file diff --git a/lib/ace/lib/event.js b/lib/ace/lib/event.js index 16805ce2..57924056 100644 --- a/lib/ace/lib/event.js +++ b/lib/ace/lib/event.js @@ -326,13 +326,14 @@ exports.addCommandKeyListener = function(el, callback) { }); if (!pressedKeys) { - pressedKeys = Object.create(null); - addListener(window, "focus", function(e) { - pressedKeys = Object.create(null); - }); + resetPressedKeys(); + addListener(window, "focus", resetPressedKeys); } } }; +function resetPressedKeys(e) { + pressedKeys = Object.create(null); +} if (window.postMessage && !useragent.isOldIE) { var postMessageId = 1; From 3ce8d76c8943ad424e1821819e48fffd60768ac5 Mon Sep 17 00:00:00 2001 From: Phill Campbell Date: Mon, 27 Apr 2015 18:49:12 +0100 Subject: [PATCH 61/92] Expose ClipboardEvent in editor paste event --- lib/ace/editor.js | 4 ++-- lib/ace/keyboard/textinput.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ace/editor.js b/lib/ace/editor.js index f00f74f6..19d4b84e 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -920,12 +920,12 @@ var Editor = function(renderer, session) { * * **/ - this.onPaste = function(text) { + this.onPaste = function(text, event) { // todo this should change when paste becomes a command if (this.$readOnly) return; - var e = {text: text}; + var e = {text: text, event: event}; this._signal("paste", e); text = e.text; if (!this.inMultiSelectMode || this.inVirtualSelectionMode) { diff --git a/lib/ace/keyboard/textinput.js b/lib/ace/keyboard/textinput.js index 0bd57eed..4a2bb72c 100644 --- a/lib/ace/keyboard/textinput.js +++ b/lib/ace/keyboard/textinput.js @@ -306,7 +306,7 @@ var TextInput = function(parentNode, host) { var data = handleClipboardData(e); if (typeof data == "string") { if (data) - host.onPaste(data); + host.onPaste(data, e); if (useragent.isIE) setTimeout(resetSelection); event.preventDefault(e); From d080fe51d4433c9217bc40acbbc5876843254e7a Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 28 Apr 2015 19:01:24 +0400 Subject: [PATCH 62/92] fix #2468 error in strict mode --- lib/ace/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/config.js b/lib/ace/config.js index 34bf50fd..fe9312fd 100644 --- a/lib/ace/config.js +++ b/lib/ace/config.js @@ -39,7 +39,7 @@ var AppConfig = require("./lib/app_config").AppConfig; module.exports = exports = new AppConfig(); var global = (function() { - return this; + return this || typeof window != "undefined" && window; })(); var options = { From e72f1d833751756fdb237a35ff0a9549487bf0bf Mon Sep 17 00:00:00 2001 From: Alex Gilding Date: Tue, 28 Apr 2015 20:24:13 +0100 Subject: [PATCH 63/92] Add indentation for Scheme Just copied over the Clojure indentation rules to Scheme and changed the keywords to suit the language, so that Scheme programs can be automatically indented too --- lib/ace/mode/scheme.js | 77 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/lib/ace/mode/scheme.js b/lib/ace/mode/scheme.js index aa462a10..2360b3da 100644 --- a/lib/ace/mode/scheme.js +++ b/lib/ace/mode/scheme.js @@ -39,15 +39,92 @@ define(function(require, exports, module) { var oop = require("../lib/oop"); var TextMode = require("./text").Mode; var SchemeHighlightRules = require("./scheme_highlight_rules").SchemeHighlightRules; +var MatchingParensOutdent = require("./matching_parens_outdent").MatchingParensOutdent; var Mode = function() { this.HighlightRules = SchemeHighlightRules; + this.$outdent = new MatchingParensOutdent(); }; oop.inherits(Mode, TextMode); (function() { this.lineCommentStart = ";"; + this.minorIndentFunctions = ["define", "lambda", "define-macro", "define-syntax", "syntax-rules", "define-record-type", "define-structure"]; + + this.$toIndent = function(str) { + return str.split('').map(function(ch) { + if (/\s/.exec(ch)) { + return ch; + } else { + return ' '; + } + }).join(''); + }; + + this.$calculateIndent = function(line, tab) { + var baseIndent = this.$getIndent(line); + var delta = 0; + var isParen, ch; + // Walk back from end of line, find matching braces + for (var i = line.length - 1; i >= 0; i--) { + ch = line[i]; + if (ch === '(') { + delta--; + isParen = true; + } else if (ch === '(' || ch === '[' || ch === '{') { + delta--; + isParen = false; + } else if (ch === ')' || ch === ']' || ch === '}') { + delta++; + } + if (delta < 0) { + break; + } + } + if (delta < 0 && isParen) { + // Were more brackets opened than closed and was a ( left open? + i += 1; + var iBefore = i; + var fn = ''; + while (true) { + ch = line[i]; + if (ch === ' ' || ch === '\t') { + if(this.minorIndentFunctions.indexOf(fn) !== -1) { + return this.$toIndent(line.substring(0, iBefore - 1) + tab); + } else { + return this.$toIndent(line.substring(0, i + 1)); + } + } else if (ch === undefined) { + return this.$toIndent(line.substring(0, iBefore - 1) + tab); + } + fn += line[i]; + i++; + } + } else if(delta < 0 && !isParen) { + // Were more brackets openend than closed and was it not a (? + return this.$toIndent(line.substring(0, i+1)); + } else if(delta > 0) { + // Mere more brackets closed than opened? Outdent. + baseIndent = baseIndent.substring(0, baseIndent.length - tab.length); + return baseIndent; + } else { + // Were they nicely matched? Just indent like line before. + return baseIndent; + } + }; + + this.getNextLineIndent = function(state, line, tab) { + return this.$calculateIndent(line, tab); + }; + + this.checkOutdent = function(state, line, input) { + return this.$outdent.checkOutdent(line, input); + }; + + this.autoOutdent = function(state, doc, row) { + this.$outdent.autoOutdent(doc, row); + }; this.$id = "ace/mode/scheme"; }).call(Mode.prototype); From a61d304cbb348432fa3c71099ffa3809ccabd3ee Mon Sep 17 00:00:00 2001 From: Brian Jordan Date: Thu, 30 Apr 2015 17:14:16 -0700 Subject: [PATCH 64/92] Remove unused variable `EditSession` --- lib/ace/autocomplete/popup.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ace/autocomplete/popup.js b/lib/ace/autocomplete/popup.js index a151c746..ead0c47c 100644 --- a/lib/ace/autocomplete/popup.js +++ b/lib/ace/autocomplete/popup.js @@ -31,7 +31,6 @@ define(function(require, exports, module) { "use strict"; -var EditSession = require("../edit_session").EditSession; var Renderer = require("../virtual_renderer").VirtualRenderer; var Editor = require("../editor").Editor; var Range = require("../range").Range; From cd8099b0ad952c916f512b2309d82920da388103 Mon Sep 17 00:00:00 2001 From: Brian Jordan Date: Thu, 30 Apr 2015 17:38:19 -0700 Subject: [PATCH 65/92] Remove unused parameter `mutateData` & add ;s --- lib/ace/autocomplete.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ace/autocomplete.js b/lib/ace/autocomplete.js index 7e5674ae..d3b41b65 100644 --- a/lib/ace/autocomplete.js +++ b/lib/ace/autocomplete.js @@ -142,7 +142,7 @@ var Autocomplete = function() { // we have to check if activeElement is a child of popup because // on IE preventDefault doesn't stop scrollbar from being focussed var el = document.activeElement; - var text = this.editor.textInput.getElement() + var text = this.editor.textInput.getElement(); if (el != text && ( !this.popup || el.parentNode != this.popup.container ) && el != this.tooltipNode && e.relatedTarget != this.tooltipNode && e.relatedTarget != text @@ -349,7 +349,7 @@ var Autocomplete = function() { doc = selected; if (typeof doc == "string") - doc = {docText: doc} + doc = {docText: doc}; if (!doc || !(doc.docHTML || doc.docText)) return this.hideDocTooltip(); this.showDocTooltip(doc); @@ -416,7 +416,7 @@ Autocomplete.startCommand = { bindKey: "Ctrl-Space|Ctrl-Shift-Space|Alt-Space" }; -var FilteredList = function(array, filterText, mutateData) { +var FilteredList = function(array, filterText) { this.all = array; this.filtered = array; this.filterText = filterText || ""; From ad22b71cfe9293dda3fe1c689299d08580f29441 Mon Sep 17 00:00:00 2001 From: Brian Jordan Date: Fri, 1 May 2015 10:56:08 -0700 Subject: [PATCH 66/92] Update comment, add missing semicolon [ci skip] --- lib/ace/edit_session.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 3e941375..7296dc34 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -151,7 +151,7 @@ var EditSession = function(text, mode) { this.$foldData = []; this.$foldData.toString = function() { return this.join("\n"); - } + }; this.on("changeFold", this.onChangeFold.bind(this)); this.$onChange = this.onChange.bind(this); @@ -679,10 +679,10 @@ var EditSession = function(text, mode) { }; /** - * Returns an array containing the IDs of all the markers, either front or back. + * Returns an object containing the all of the markers, either front or back. * @param {Boolean} inFront If `true`, indicates you only want front markers; `false` indicates only back markers * - * @returns {Array} + * @returns {Object} **/ this.getMarkers = function(inFront) { return inFront ? this.$frontMarkers : this.$backMarkers; From 9323a2616b07d97d2288d1aecd55791b8866b9d9 Mon Sep 17 00:00:00 2001 From: Joey Payne Date: Mon, 4 May 2015 22:54:09 -0600 Subject: [PATCH 67/92] Added support for nim programming language. --- lib/ace/ext/modelist.js | 1 + lib/ace/ext/themelist.js | 1 + lib/ace/mode/nim.js | 113 ++++++++++ lib/ace/mode/nim_highlight_rules.js | 238 +++++++++++++++++++++ lib/ace/snippets/mask.js | 7 + lib/ace/snippets/mask.snippets | 0 lib/ace/snippets/nim.js | 7 + lib/ace/snippets/nim.snippets | 130 +++++++++++ lib/ace/theme/the_night_after_tomorrow.css | 139 ++++++++++++ lib/ace/theme/the_night_after_tomorrow.js | 39 ++++ 10 files changed, 675 insertions(+) create mode 100644 lib/ace/mode/nim.js create mode 100644 lib/ace/mode/nim_highlight_rules.js create mode 100644 lib/ace/snippets/mask.js create mode 100644 lib/ace/snippets/mask.snippets create mode 100644 lib/ace/snippets/nim.js create mode 100644 lib/ace/snippets/nim.snippets create mode 100644 lib/ace/theme/the_night_after_tomorrow.css create mode 100644 lib/ace/theme/the_night_after_tomorrow.js diff --git a/lib/ace/ext/modelist.js b/lib/ace/ext/modelist.js index 7a5a1e9c..5ab899a5 100644 --- a/lib/ace/ext/modelist.js +++ b/lib/ace/ext/modelist.js @@ -117,6 +117,7 @@ var supportedModes = { MUSHCode: ["mc|mush"], MySQL: ["mysql"], Nix: ["nix"], + Nim: ["nim"], ObjectiveC: ["m|mm"], OCaml: ["ml|mli"], Pascal: ["pas|p"], diff --git a/lib/ace/ext/themelist.js b/lib/ace/ext/themelist.js index 2350a2e2..0df76b02 100644 --- a/lib/ace/ext/themelist.js +++ b/lib/ace/ext/themelist.js @@ -73,6 +73,7 @@ var themeData = [ ["Solarized Dark" ,"solarized_dark" , "dark"], ["Terminal" ,"terminal" , "dark"], ["Tomorrow Night" ,"tomorrow_night" , "dark"], + ["The Night After Tomorrow" ,"the_night_after_tomorrow" , "dark"], ["Tomorrow Night Blue" ,"tomorrow_night_blue" , "dark"], ["Tomorrow Night Bright","tomorrow_night_bright" , "dark"], ["Tomorrow Night 80s" ,"tomorrow_night_eighties" , "dark"], diff --git a/lib/ace/mode/nim.js b/lib/ace/mode/nim.js new file mode 100644 index 00000000..cbf48ef2 --- /dev/null +++ b/lib/ace/mode/nim.js @@ -0,0 +1,113 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { +"use strict"; + +var oop = require("../lib/oop"); +var TextMode = require("./text").Mode; +var NimHighlightRules = require("./nim_highlight_rules").NimHighlightRules; +var NimFoldMode = require("./folding/pythonic").FoldMode; +var Range = require("../range").Range; + +var Mode = function() { + this.HighlightRules = NimHighlightRules; + this.foldingRules = new NimFoldMode("\\:|="); +}; +oop.inherits(Mode, TextMode); + +(function() { + + this.lineCommentStart = "#"; + + this.getNextLineIndent = function(state, line, tab) { + var indent = this.$getIndent(line); + + var tokenizedLine = this.getTokenizer().getLineTokens(line, state); + var tokens = tokenizedLine.tokens; + + if (tokens.length && tokens[tokens.length-1].type == "comment") { + return indent; + } + + if (state == "start") { + var match = line.match(/^.*[\{\(\[\:=]\s*$/); + if (match) { + indent += tab; + } + } + + return indent; + }; + + var outdents = { + "discard": 1, + "return": 1, + "raise": 1, + "break": 1, + "continue": 1 + }; + + this.checkOutdent = function(state, line, input) { + if (input !== "\r\n" && input !== "\r" && input !== "\n") + return false; + + var tokens = this.getTokenizer().getLineTokens(line.trim(), state).tokens; + + if (!tokens) + return false; + + // ignore trailing comments + do { + var last = tokens.pop(); + } while (last && (last.type == "comment" || (last.type == "text" && last.value.match(/^\s+$/)))); + + if (!last) + return false; + + return (last.type == "keyword" && outdents[last.value]); + }; + + this.autoOutdent = function(state, doc, row) { + // outdenting in python is slightly different because it always applies + // to the next line and only of a new line is inserted + + row += 1; + var indent = this.$getIndent(doc.getLine(row)); + var tab = doc.getTabString(); + if (indent.slice(-tab.length) == tab) + doc.remove(new Range(row, indent.length-tab.length, row, indent.length)); + }; + + this.$id = "ace/mode/nim"; +}).call(Mode.prototype); + +exports.Mode = Mode; +}); diff --git a/lib/ace/mode/nim_highlight_rules.js b/lib/ace/mode/nim_highlight_rules.js new file mode 100644 index 00000000..e762a636 --- /dev/null +++ b/lib/ace/mode/nim_highlight_rules.js @@ -0,0 +1,238 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ +/* + * TODO: nim delimiters + */ + +define(function(require, exports, module) { +"use strict"; + +var oop = require("../lib/oop"); +var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; +var num_suffix = "(\'[Ff]32|\'[Ff]64|\'[IiUu]8|\'[IiUu]16|\'[IiUu]32|\'[IiUu]64)"; + +var NimHighlightRules = function() { + + var keywords = ( + "addr|and|as|asm|atomic|bind|block|break|case|cast|const|continue|" + + "converter|defer|discard|distinct|div|do|elif|else|end|enum|except|" + + "export|finally|for|from|func|generic|if|import|in|include|interface|" + + "is|isnot|iterator|let|macro|method|mixin|mod|nil|not|notin|object|of|" + + "or|out|proc|ptr|raise|ref|return|shl|shr|static|template|try|tuple|" + + "type|using|var|when|while|with|without|xor|yield" + ); + + var builtinConstants = ( + "true|false|nil|NotImplemented|Ellipsis" + ); + + var storageType = ( + "string|seq|array|expr|stmt|typed|untyped|any|auto|bool|cdouble|cfloat|"+ + "cchar|clongdouble|clong|clonglong|cshort|csize|cstring|cstringarray|"+ + "culong|culonglong|cushort|guarded|natural|openarray|ordinal|pointer|"+ + "range|set|shared|static|typedesc|varargs|void" + ); + + var builtinFunctions = ( + "defined|declared|declaredInScope|echo|$" + ); + + //var futureReserved = ""; + var keywordMapper = this.createKeywordMapper({ + "invalid.deprecated": "debugger", + "support.function": builtinFunctions, + //"invalid.illegal": futureReserved, + "constant.language": builtinConstants, + "storage.type" : storageType, + "keyword": keywords + }, "identifier"); + + var strPre = "(?:r|R)?"; + + var decimalInteger = "(?:(?:[1-9]\\d*)|(?:0))"; + var octInteger = "(?:0[o]?[0-7]+)"; + var hexInteger = "(?:0[xX][\\dA-Fa-f]+)"; + var binInteger = "(?:0[bB][01]+)"; + var integer = "(?:" + decimalInteger + "|" + octInteger + "|" + hexInteger + "|" + binInteger + ")"; + + var exponent = "(?:[eE][+-]?\\d+)"; + var fraction = "(?:\\.\\d+)"; + var intPart = "(?:\\d+)"; + var pointFloat = "(?:(?:" + intPart + "?" + fraction + ")|(?:" + intPart + "\\.))"; + var exponentFloat = "(?:(?:" + pointFloat + "|" + intPart + ")" + exponent + ")"; + var floatNumber = "(?:" + exponentFloat + "|" + pointFloat + ")"; + + var stringEscape = "\\\\(x[0-9A-Fa-f]{2}|[0-7]{3}|[\\\\abfnrtv'\"]|U[0-9A-Fa-f]{8}|u[0-9A-Fa-f]{4})"; + + this.$rules = { + "start" : [ { + token : "comment", + regex : "#.*$" + }, { + token : "keyword", + regex : "(proc|method|temdplate|macro|macromethod|converter|func|iterator) ", + next : "qproc_name" + }, { + token : "storage.type", + regex : "(c|cu|u)?int(8|16|32|64)?", + }, { + token : "storage.type", + regex : "(c|cu|u|cs)?char", + }, { + token : "storage.type", + regex : "float(32|64)?", + }, { + token : "docstring", + regex : "##.*$" + }, { + token : "string", // multi line """ string start + regex : strPre + '"{3}', + next : "qqstring3" + }, { + token : "string", // " string + regex : strPre + '"(?=.)', + next : "qqstring" + }, { + token : "string", // multi line ''' string start + regex : strPre + "'{3}", + next : "qstring3" + }, { + token : "backtick", // ` string + regex : strPre + "`(?=.)", + next : "qxstring" + }, { + token : "string", // ' string + regex : strPre + "'(?=.)", + next : "qstring" + }, { + token : "constant.numeric", // imaginary + regex : "(?:" + floatNumber + "|\\d+)[jJ]\\b" + }, { + token : "constant.numeric", // float + regex : floatNumber + }, { + token : "constant.numeric", // long integer + regex : integer + "[lL]\\b" + }, { + token : "constant.numeric", // integer + regex : integer + "\\b" + }, { + token : keywordMapper, + regex : "[a-zA-Z_$][a-zA-Z0-9_$]*\\b" + }, { + token : "keyword.operator", + regex : "\\+|\\-|\\*|\\*\\*|\\/|\\/\\/|%|<<|>>|&|\\||\\^|~|<|>|<=|=>|==|!=|<>|=" + }, { + token : "paren.lparen", + regex : "[\\[\\(\\{]" + }, { + token : "paren.rparen", + regex : "[\\]\\)\\}]" + }, { + token : "text", + regex : "\\s+" + } ], + "qqstring3" : [ { + token : "constant.language.escape", + regex : stringEscape + }, { + token : "string", // multi line """ string end + regex : '"{3}', + next : "start" + }, { + defaultToken : "string" + } ], + "qstring3" : [ { + token : "constant.language.escape", + regex : stringEscape + }, { + token : "string", // multi line ''' string end + regex : "'{3}", + next : "start" + }, { + defaultToken : "string" + } ], + "qqstring" : [{ + token : "constant.language.escape", + regex : stringEscape + }, { + token : "string", + regex : "\\\\$", + next : "qqstring" + }, { + token : "string", + regex : '"|$', + next : "start" + }, { + defaultToken: "string" + }], + "qstring" : [{ + token : "constant.language.escape", + regex : stringEscape + }, { + token : "string", + regex : "\\\\$", + next : "qstring" + }, { + token : "string", + regex : "'|$", + next : "start" + }, { + defaultToken: "string" + }], + "qxstring" : [{ + token : "constant.language.escape", + regex : stringEscape + }, { + token : "backtick", + regex : "\\\\$", + next : "qxstring" + }, { + token : "backtick", + regex : "`|$", + next : "start" + }, { + defaultToken: "backtick" + }], + "qproc_name":[{ + token : "proc_name", + regex : "\\w+", + },{ + token : "start_bracket", + regex : "(\\(|=|$)", + next : "start" + }], + }; +}; + +oop.inherits(NimHighlightRules, TextHighlightRules); + +exports.NimHighlightRules = NimHighlightRules; +}); diff --git a/lib/ace/snippets/mask.js b/lib/ace/snippets/mask.js new file mode 100644 index 00000000..7fbca678 --- /dev/null +++ b/lib/ace/snippets/mask.js @@ -0,0 +1,7 @@ +define(function(require, exports, module) { +"use strict"; + +exports.snippetText = require("../requirejs/text!./mask.snippets"); +exports.scope = "mask"; + +}); diff --git a/lib/ace/snippets/mask.snippets b/lib/ace/snippets/mask.snippets new file mode 100644 index 00000000..e69de29b diff --git a/lib/ace/snippets/nim.js b/lib/ace/snippets/nim.js new file mode 100644 index 00000000..6e6d02bb --- /dev/null +++ b/lib/ace/snippets/nim.js @@ -0,0 +1,7 @@ +define(function(require, exports, module) { +"use strict"; + +exports.snippetText = require("../requirejs/text!./nim.snippets"); +exports.scope = "nim"; + +}); diff --git a/lib/ace/snippets/nim.snippets b/lib/ace/snippets/nim.snippets new file mode 100644 index 00000000..a8a20a00 --- /dev/null +++ b/lib/ace/snippets/nim.snippets @@ -0,0 +1,130 @@ +snippet #! + #!/usr/bin/env nim +snippet imp + import ${1:module} +snippet from + from ${1:package} import ${2:module} +# Module Docstring +snippet docs + ## File: ${1:FILENAME:file_name} + ## Author: ${2:author} + ## Description: ${3} +snippet wh + while ${1:condition}: + ${2:# TODO: write code...} +# dowh - does the same as do...while in other languages +snippet dowh + while true: + ${1:# TODO: write code...} + if ${2:condition}: + break +snippet with + with ${1:expr} as ${2:var}: + ${3:# TODO: write code...} +# New Function +snippet proc + proc ${1:fname}(${2:`indent('.') ? 'self' : ''`}): ${3: return type} = + ##${4:docstring for $1} + ${5:# TODO: write code...} +snippet deff + def ${1:fname}(${2:`indent('.') ? 'self' : ''`}): + ${3:# TODO: write code...} +# New Method +snippet defs + def ${1:mname}(self, ${2:arg}): + ${3:# TODO: write code...} +# New Property +snippet property + def ${1:foo}(): + doc = "${2:The $1 property.}" + def fget(self): + ${3:return self._$1} + def fset(self, value): + ${4:self._$1 = value} +# Ifs +snippet if + if ${1:condition}: + ${2:# TODO: write code...} +snippet el + else: + ${1:# TODO: write code...} +snippet ei + elif ${1:condition}: + ${2:# TODO: write code...} +# For +snippet for + for ${1:item} in ${2:items}: + ${3:# TODO: write code...} +# Encodes +snippet cutf8 + # -*- coding: utf-8 -*- +snippet clatin1 + # -*- coding: latin-1 -*- +snippet cascii + # -*- coding: ascii -*- +# Lambda +snippet ld + ${1:var} = lambda ${2:vars} : ${3:action} +snippet . + self. +snippet try Try/Except + try: + ${1:# TODO: write code...} + except ${2:Exception}, ${3:e}: + ${4:raise $3} +snippet try Try/Except/Else + try: + ${1:# TODO: write code...} + except ${2:Exception}, ${3:e}: + ${4:raise $3} + else: + ${5:# TODO: write code...} +snippet try Try/Except/Finally + try: + ${1:# TODO: write code...} + except ${2:Exception}, ${3:e}: + ${4:raise $3} + finally: + ${5:# TODO: write code...} +snippet try Try/Except/Else/Finally + try: + ${1:# TODO: write code...} + except ${2:Exception}, ${3:e}: + ${4:raise $3} + else: + ${5:# TODO: write code...} + finally: + ${6:# TODO: write code...} +# if __name__ == '__main__': +snippet ifmain + if isMainModule: + ${1:main()} +snippet " + ## ${1:doc} +# test function/method +snippet test + def test_${1:description}(${2:self}): + ${3:# TODO: write code...} +# test case +snippet testcase + class ${1:ExampleCase}(unittest.TestCase): + + def test_${2:description}(self): + ${3:# TODO: write code...} +#getopt +snippet getopt + try: + # Short option syntax: "hv:" + # Long option syntax: "help" or "verbose=" + opts, args = getopt.getopt(sys.argv[1:], "${1:short_options}", [${2:long_options}]) + + except getopt.GetoptError, err: + # Print debug info + print str(err) + ${3:error_action} + + for option, argument in opts: + if option in ("-h", "--help"): + ${4} + elif option in ("-v", "--verbose"): + verbose = argument diff --git a/lib/ace/theme/the_night_after_tomorrow.css b/lib/ace/theme/the_night_after_tomorrow.css new file mode 100644 index 00000000..48d2602c --- /dev/null +++ b/lib/ace/theme/the_night_after_tomorrow.css @@ -0,0 +1,139 @@ +.ace-tomorrow-night .ace_gutter { + background: #25282c; + color: #C5C8C6 +} + +.ace-tomorrow-night .ace_print-margin { + width: 1px; + background: #25282c +} + +.ace-tomorrow-night { + background-color: #1D1F21; + color: #C5C8C6 +} + +.ace-tomorrow-night .ace_cursor { + color: #FFFFFF +} + +.ace-tomorrow-night .ace_marker-layer .ace_selection { + background: #373B41 +} + +.ace-tomorrow-night.ace_multiselect .ace_selection.ace_start { + box-shadow: 0 0 3px 0px #1D1F21; + border-radius: 2px +} + +.ace-tomorrow-night .ace_marker-layer .ace_step { + background: rgb(102, 82, 0) +} + +.ace-tomorrow-night .ace_marker-layer .ace_bracket { + margin: -1px 0 0 -1px; + border: 1px solid #4B4E55 +} + +.ace-tomorrow-night .ace_marker-layer .ace_active-line { + background: #282A2E +} + +.ace-tomorrow-night .ace_gutter-active-line { + background-color: #282A2E +} + +.ace-tomorrow-night .ace_marker-layer .ace_selected-word { + border: 1px solid #373B41 +} + +.ace-tomorrow-night .ace_invisible { + color: #4B4E55 +} + +.ace-tomorrow-night .ace_keyword, +.ace-tomorrow-night .ace_meta, +.ace-tomorrow-night .ace_support.ace_type { + color: #C4A400 +} + +.ace-tomorrow-night .ace_storage, +.ace-tomorrow-night .ace_storage.ace_type { + color: #327FD7 +} + +.ace-tomorrow-night .ace_keyword.ace_operator { + color: #8ABEB7 +} + +.ace-tomorrow-night .ace_constant.ace_language{ + color: #934B9F +} + +.ace-tomorrow-night .ace_constant.ace_character, +.ace-tomorrow-night .ace_constant.ace_numeric, +.ace-tomorrow-night .ace_keyword.ace_other.ace_unit, +.ace-tomorrow-night .ace_support.ace_constant, +.ace-tomorrow-night .ace_variable.ace_parameter { + color: #37BC9B +} + +.ace-tomorrow-night .ace_constant.ace_other { + color: #CED1CF +} + +.ace-tomorrow-night .ace_invalid { + color: #CED2CF; + background-color: #DF5F5F +} + +.ace-tomorrow-night .ace_invalid.ace_deprecated { + color: #CED2CF; + background-color: #B798BF +} + +.ace-tomorrow-night .ace_fold { + background-color: #62A5D6; + border-color: #C5C8C6 +} + +.ace-tomorrow-night .ace_entity.ace_name.ace_function, +.ace-tomorrow-night .ace_support.ace_function, +.ace-tomorrow-night .ace_variable { + color: #62A5D6 +} + +.ace-tomorrow-night .ace_support.ace_class, +.ace-tomorrow-night .ace_support.ace_type { + color: #F0C674 +} + +.ace-tomorrow-night .ace_heading, +.ace-tomorrow-night .ace_markup.ace_heading, +.ace-tomorrow-night .ace_string { + color: #CD0000 +} + +.ace-tomorrow-night .ace_proc_name { + color: #04939A +} + +.ace-tomorrow-night .ace_backtick { + color: #1DAA49 +} + +.ace-tomorrow-night .ace_entity.ace_name.ace_tag, +.ace-tomorrow-night .ace_entity.ace_other.ace_attribute-name, +.ace-tomorrow-night .ace_meta.ace_tag, +.ace-tomorrow-night .ace_string.ace_regexp, +.ace-tomorrow-night .ace_variable { + color: #FFFFFF +} + +.ace-tomorrow-night .ace_comment { + color: #3465A4 +} + +.ace-tomorrow-night .ace_indent-guide { + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYHB3d/8PAAOIAdULw8qMAAAAAElFTkSuQmCC) right repeat-y +} diff --git a/lib/ace/theme/the_night_after_tomorrow.js b/lib/ace/theme/the_night_after_tomorrow.js new file mode 100644 index 00000000..3108e2a9 --- /dev/null +++ b/lib/ace/theme/the_night_after_tomorrow.js @@ -0,0 +1,39 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { + +exports.isDark = true; +exports.cssClass = "ace-tomorrow-night"; +exports.cssText = require("../requirejs/text!./the_night_after_tomorrow.css"); + +var dom = require("../lib/dom"); +dom.importCssString(exports.cssText, exports.cssClass); +}); From ef4537a09981f142b11a79b47c6e242e04bd2a80 Mon Sep 17 00:00:00 2001 From: Brian Jordan Date: Tue, 5 May 2015 09:07:46 -0700 Subject: [PATCH 68/92] Update edit_session.js Fix extra `the` --- lib/ace/edit_session.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 7296dc34..f9b43a76 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -679,7 +679,7 @@ var EditSession = function(text, mode) { }; /** - * Returns an object containing the all of the markers, either front or back. + * Returns an object containing all of the markers, either front or back. * @param {Boolean} inFront If `true`, indicates you only want front markers; `false` indicates only back markers * * @returns {Object} From 0abd9ec30e4bc6225cb01b62e34cc5ba1fd23b1f Mon Sep 17 00:00:00 2001 From: nightwing Date: Sat, 11 Oct 2014 02:38:06 +0400 Subject: [PATCH 69/92] rounded corners for selections --- lib/ace/css/editor.css | 27 ++++++++++++ lib/ace/layer/marker.js | 51 ++++++++++++----------- lib/ace/theme/clouds.css | 1 - lib/ace/theme/clouds_midnight.css | 1 - lib/ace/theme/cobalt.css | 1 - lib/ace/theme/dawn.css | 1 - lib/ace/theme/idle_fingers.css | 1 - lib/ace/theme/katzenmilch.css | 1 - lib/ace/theme/kr_theme.css | 1 - lib/ace/theme/merbivore.css | 1 - lib/ace/theme/merbivore_soft.css | 1 - lib/ace/theme/mono_industrial.css | 1 - lib/ace/theme/monokai.css | 1 - lib/ace/theme/pastel_on_dark.css | 1 - lib/ace/theme/solarized_dark.css | 1 - lib/ace/theme/solarized_light.css | 1 - lib/ace/theme/terminal.css | 1 - lib/ace/theme/textmate.css | 1 - lib/ace/theme/tomorrow.css | 1 - lib/ace/theme/tomorrow_night.css | 1 - lib/ace/theme/tomorrow_night_blue.css | 1 - lib/ace/theme/tomorrow_night_bright.css | 1 - lib/ace/theme/tomorrow_night_eighties.css | 1 - lib/ace/theme/twilight.css | 1 - lib/ace/theme/vibrant_ink.css | 1 - lib/ace/theme/xcode.css | 1 - 26 files changed, 54 insertions(+), 48 deletions(-) diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index cef995c8..87eb67b3 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -422,3 +422,30 @@ position: absolute; z-index: 8; } + +/* +styles = [] +for (var i = 1; i < 16; i++) { + styles.push(".ace_br" + i + "{" + ( + ["top-left", "top-right", "bottom-right", "bottom-left"] + ).map(function(x, j) { + return i & (1< next, row == end), + layerConfig, row == end ? 0 : 1, extraStyle); } }; @@ -136,7 +137,7 @@ var Marker = function(parentEl) { extraStyle = extraStyle || ""; stringBuilder.push( - "
Date: Wed, 20 May 2015 19:48:01 +0400 Subject: [PATCH 76/92] update changelog --- ChangeLog.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ChangeLog.txt b/ChangeLog.txt index e9c41e4c..3077d171 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,5 +1,16 @@ +Version 1.2.0-pre + +* New Features + - Indented soft wrap (danyaPostfactum) + +* API Changes + - unified delta types `{start, end, action, lines}` (Alden Daniels https://github.com/ajaxorg/ace/pull/1745) + - "change" event listeners on session and editor get delta objects directly + 2015.04.03 Version 1.1.9 + - Small Enhancements and Bugfixes + 2014.11.08 Version 1.1.8 * API Changes From fe96eef206b15b88f6bd8d6364b3980ede970472 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 10 Mar 2015 20:14:36 +0400 Subject: [PATCH 77/92] fix require for paths ending with .js in the worker --- lib/ace/worker/worker.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/ace/worker/worker.js b/lib/ace/worker/worker.js index 928000dd..7a047584 100644 --- a/lib/ace/worker/worker.js +++ b/lib/ace/worker/worker.js @@ -69,7 +69,8 @@ window.require = function(parentId, id) { if (!window.require.tlns) return console.log("unable to load " + id); chunks[0] = window.require.tlns[chunks[0]] || chunks[0]; - var path = chunks.join("/") + ".js"; + var path = chunks.join("/"); + if (path.slice(-3) != ".js") path += ".js"; window.require.id = id; importScripts(path); From af5c7a9c3b44da1a64517bd8d5e4a07df32d20ba Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 22 May 2015 02:34:30 +0400 Subject: [PATCH 78/92] improve require support in worker --- lib/ace/worker/worker.js | 83 +++++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 30 deletions(-) diff --git a/lib/ace/worker/worker.js b/lib/ace/worker/worker.js index 7a047584..28fc0fe2 100644 --- a/lib/ace/worker/worker.js +++ b/lib/ace/worker/worker.js @@ -1,8 +1,9 @@ "no use strict"; ;(function(window) { -if (typeof window.window != "undefined" && window.document) { +if (typeof window.window != "undefined" && window.document) + return; +if (window.require && window.define) return; -} window.console = function() { var msgs = Array.prototype.slice.call(arguments, 0); @@ -19,6 +20,7 @@ window.ace = window; window.onerror = function(message, file, line, col, err) { postMessage({type: "error", data: { message: message, + data: err.data, file: file, line: line, col: col, @@ -37,7 +39,7 @@ window.normalizeModule = function(parentId, moduleName) { var base = parentId.split("/").slice(0, -1).join("/"); moduleName = (base ? base + "/" : "") + moduleName; - while(moduleName.indexOf(".") !== -1 && previous != moduleName) { + while (moduleName.indexOf(".") !== -1 && previous != moduleName) { var previous = moduleName; moduleName = moduleName.replace(/^\.\//, "").replace(/\/\.\//, "/").replace(/[^\/]+\/\.\.\//, ""); } @@ -46,7 +48,7 @@ window.normalizeModule = function(parentId, moduleName) { return moduleName; }; -window.require = function(parentId, id) { +window.require = function require(parentId, id) { if (!id) { id = parentId; parentId = null; @@ -64,18 +66,36 @@ window.require = function(parentId, id) { } return module.exports; } - - var chunks = id.split("/"); + if (!window.require.tlns) return console.log("unable to load " + id); - chunks[0] = window.require.tlns[chunks[0]] || chunks[0]; - var path = chunks.join("/"); + + var path = resolveModuleId(id, window.require.tlns); if (path.slice(-3) != ".js") path += ".js"; window.require.id = id; + window.require.modules[id] = {}; // prevent infinite loop on broken modules importScripts(path); return window.require(parentId, id); }; +function resolveModuleId(id, paths) { + var testPath = id, tail = ""; + while (testPath) { + var alias = paths[testPath]; + if (typeof alias == "string") { + return alias + tail; + } else if (alias) { + return alias.location.replace(/\/*$/, "/") + (tail || alias.main || alias.name); + } else if (alias === false) { + return ""; + } + var i = testPath.lastIndexOf("/"); + if (i === -1) break; + tail = testPath.substr(i) + tail; + testPath = testPath.slice(0, i); + } + return id; +} window.require.modules = {}; window.require.tlns = {}; @@ -101,9 +121,9 @@ window.define = function(id, deps, factory) { } if (!deps.length) - // If there is no dependencies, we inject 'require', 'exports' and - // 'module' as dependencies, to provide CommonJS compatibility. - deps = ['require', 'exports', 'module']; + // If there is no dependencies, we inject "require", "exports" and + // "module" as dependencies, to provide CommonJS compatibility. + deps = ["require", "exports", "module"]; var req = function(childId) { return window.require(id, childId); @@ -114,16 +134,16 @@ window.define = function(id, deps, factory) { factory: function() { var module = this; var returnExports = factory.apply(this, deps.map(function(dep) { - switch(dep) { - // Because 'require', 'exports' and 'module' aren't actual - // dependencies, we must handle them seperately. - case 'require': return req; - case 'exports': return module.exports; - case 'module': return module; - // But for all other dependencies, we can just go ahead and - // require them. - default: return req(dep); - } + switch (dep) { + // Because "require", "exports" and "module" aren't actual + // dependencies, we must handle them seperately. + case "require": return req; + case "exports": return module.exports; + case "module": return module; + // But for all other dependencies, we can just go ahead and + // require them. + default: return req(dep); + } })); if (returnExports) module.exports = returnExports; @@ -132,9 +152,10 @@ window.define = function(id, deps, factory) { }; }; window.define.amd = {}; - +require.tlns = {}; window.initBaseUrls = function initBaseUrls(topLevelNamespaces) { - require.tlns = topLevelNamespaces; + for (var i in topLevelNamespaces) + require.tlns[i] = topLevelNamespaces[i]; }; window.initSender = function initSender() { @@ -174,21 +195,23 @@ var sender = window.sender = null; window.onmessage = function(e) { var msg = e.data; - if (msg.command) { + if (msg.event && sender) { + sender._signal(msg.event, msg.data); + } + else if (msg.command) { if (main[msg.command]) main[msg.command].apply(main, msg.args); + else if (window[msg.command]) + window[msg.command].apply(window, msg.args); else throw new Error("Unknown command:" + msg.command); } - else if (msg.init) { - initBaseUrls(msg.tlns); + else if (msg.init) { + window.initBaseUrls(msg.tlns); require("ace/lib/es5-shim"); - sender = window.sender = initSender(); + sender = window.sender = window.initSender(); var clazz = require(msg.module)[msg.classname]; main = window.main = new clazz(sender); - } - else if (msg.event && sender) { - sender._signal(msg.event, msg.data); } }; })(this); \ No newline at end of file From a799a4086f1e9e9912c110e9c329b4101a4047af Mon Sep 17 00:00:00 2001 From: nightwing Date: Sat, 25 Apr 2015 03:02:08 +0400 Subject: [PATCH 79/92] fix quote pairing for "\n" --- lib/ace/mode/behaviour/behaviour_test.js | 10 ++++++++++ lib/ace/mode/behaviour/cstyle.js | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/ace/mode/behaviour/behaviour_test.js b/lib/ace/mode/behaviour/behaviour_test.js index 9dc27bf8..245edf99 100644 --- a/lib/ace/mode/behaviour/behaviour_test.js +++ b/lib/ace/mode/behaviour/behaviour_test.js @@ -144,6 +144,16 @@ module.exports = { exec("selectleft", 1); exec("insertstring", 1, '"'); assert.equal(editor.getValue(), '("foo")'); + + editor.setValue("", 1); + exec("selectleft", 1); + exec("insertstring", 1, '"'); + assert.equal(editor.getValue(), '""'); + exec("insertstring", 1, '\\'); + exec("insertstring", 1, 'n'); + exec("insertstring", 1, '"'); + assert.equal(editor.getValue(), '"\\n"'); + }, "test: xml": function() { editor = new Editor(new MockRenderer()); diff --git a/lib/ace/mode/behaviour/cstyle.js b/lib/ace/mode/behaviour/cstyle.js index a1dce91e..dd1b0d14 100644 --- a/lib/ace/mode/behaviour/cstyle.js +++ b/lib/ace/mode/behaviour/cstyle.js @@ -269,8 +269,8 @@ var CstyleBehaviour = function() { if (leftChar == "\\" && token && /escape/.test(token.type)) return null; - var stringBefore = token && /string/.test(token.type); - var stringAfter = !rightToken || /string/.test(rightToken.type); + var stringBefore = token && /string|escape/.test(token.type); + var stringAfter = !rightToken || /string|escape/.test(rightToken.type); var pair; if (rightChar == quote) { From 2e2d9bcdc9fa97b7b30e51dd0b864911c33e115c Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 26 Apr 2015 21:27:25 +0400 Subject: [PATCH 80/92] add more php extensions --- lib/ace/ext/modelist.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/ext/modelist.js b/lib/ace/ext/modelist.js index 7a5a1e9c..6bc79c8b 100644 --- a/lib/ace/ext/modelist.js +++ b/lib/ace/ext/modelist.js @@ -122,7 +122,7 @@ var supportedModes = { Pascal: ["pas|p"], Perl: ["pl|pm"], pgSQL: ["pgsql"], - PHP: ["php|phtml"], + PHP: ["php|phtml|shtml|php3|php4|php5|phps|phpt|aw|ctp"], Powershell: ["ps1"], Praat: ["praat|praatscript|psc|proc"], Prolog: ["plg|prolog"], From 5705a7c2c1090605877b8fdf7e9a7a9ee782d257 Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 30 Apr 2015 03:55:19 +0400 Subject: [PATCH 81/92] fix toggle comments in handlebars mode --- lib/ace/mode/handlebars.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/ace/mode/handlebars.js b/lib/ace/mode/handlebars.js index 3f2e7179..164ad43f 100644 --- a/lib/ace/mode/handlebars.js +++ b/lib/ace/mode/handlebars.js @@ -13,7 +13,6 @@ var Mode = function() { HtmlMode.call(this); this.HighlightRules = HandlebarsHighlightRules; this.$behaviour = new HtmlBehaviour(); - this.foldingRules = new HtmlFoldMode(); }; @@ -21,7 +20,7 @@ var Mode = function() { oop.inherits(Mode, HtmlMode); (function() { - this.blockComment = {start: "{!--", end: "--}"}; + this.blockComment = {start: "{{!--", end: "--}}"}; this.$id = "ace/mode/handlebars"; }).call(Mode.prototype); From dc7643db8df6611de25b5ce37468ee233fee5ff2 Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 22 May 2015 21:57:13 +0400 Subject: [PATCH 82/92] better invisible tabs (fixes #2109) --- lib/ace/layer/text.js | 7 +++---- lib/ace/layer/text_test.js | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/ace/layer/text.js b/lib/ace/layer/text.js index a105768d..01afb9f3 100644 --- a/lib/ace/layer/text.js +++ b/lib/ace/layer/text.js @@ -52,7 +52,7 @@ var Text = function(parentEl) { this.EOL_CHAR_LF = "\xAC"; this.EOL_CHAR_CRLF = "\xa4"; this.EOL_CHAR = this.EOL_CHAR_LF; - this.TAB_CHAR = "\u2192"; //"\u21E5"; + this.TAB_CHAR = "\u2014"; //"\u21E5"; this.SPACE_CHAR = "\xB7"; this.$padding = 0; @@ -128,8 +128,7 @@ var Text = function(parentEl) { for (var i = 1; i < tabSize + 1; i++) { if (this.showInvisibles) { tabStr.push("" - + this.TAB_CHAR - + lang.stringRepeat(" ", i - 1) + + lang.stringRepeat(this.TAB_CHAR, i) + ""); } else { tabStr.push(lang.stringRepeat(" ", i)); @@ -145,7 +144,7 @@ var Text = function(parentEl) { spaceClass = " ace_invisible_space"; tabClass = " ace_invisible_tab"; var spaceContent = lang.stringRepeat(this.SPACE_CHAR, this.tabSize); - var tabContent = this.TAB_CHAR + lang.stringRepeat(" ", this.tabSize - 1); + var tabContent = lang.stringRepeat(this.TAB_CHAR, this.tabSize); } else{ var spaceContent = lang.stringRepeat(" ", this.tabSize); var tabContent = spaceContent; diff --git a/lib/ace/layer/text_test.js b/lib/ace/layer/text_test.js index e3403ca4..3946ec66 100644 --- a/lib/ace/layer/text_test.js +++ b/lib/ace/layer/text_test.js @@ -91,7 +91,7 @@ module.exports = { var EOL = "" + textLayer.EOL_CHAR + ""; var SPACE = function(i) {return Array(i+1).join(" ")} var DOT = function(i) {return Array(i+1).join(textLayer.SPACE_CHAR)} - var TAB = function(i) {return textLayer.TAB_CHAR + SPACE(i-1)} + var TAB = function(i) {return Array(i+1).join(textLayer.TAB_CHAR)} function testRender(results) { for (var i = results.length; i--; ) { var stringBuilder = []; From 386c508042e80aeab4fb388816babee9f74466ce Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 22 May 2015 22:14:15 +0400 Subject: [PATCH 83/92] convert paste into command --- lib/ace/commands/default_commands.js | 12 ++++++++++++ lib/ace/editor.js | 13 +++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/ace/commands/default_commands.js b/lib/ace/commands/default_commands.js index f1e267c1..de14df85 100644 --- a/lib/ace/commands/default_commands.js +++ b/lib/ace/commands/default_commands.js @@ -423,6 +423,12 @@ exports.commands = [{ exec: function() {}, passEvent: true, readOnly: true +}, { + name: "copy", + exec: function(editor) { + // placeholder for replay macro + }, + readOnly: true }, // commands disabled in readOnly mode @@ -439,6 +445,12 @@ exports.commands = [{ }, scrollIntoView: "cursor", multiSelectAction: "forEach" +}, { + name: "paste", + exec: function(editor, args) { + editor.$handlePaste(args); + }, + scrollIntoView: "cursor" }, { name: "removeline", bindKey: bindKey("Ctrl-D", "Command-D"), diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 22b43dd2..5da8a961 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -916,13 +916,15 @@ var Editor = function(renderer, session) { * **/ this.onPaste = function(text, event) { - // todo this should change when paste becomes a command - if (this.$readOnly) - return; - var e = {text: text, event: event}; + this.commands.exec("paste", this, e); + }; + + this.$handlePaste = function(e) { + if (typeof e == "string") + e = {text: e}; this._signal("paste", e); - text = e.text; + var text = e.text; if (!this.inMultiSelectMode || this.inVirtualSelectionMode) { this.insert(text); } else { @@ -940,7 +942,6 @@ var Editor = function(renderer, session) { this.session.insert(range.start, lines[i]); } } - this.renderer.scrollCursorIntoView(); }; this.execCommand = function(command, args) { From 2a089863e24472836116a32973852af4a66d7f1e Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 18 May 2015 16:24:10 +0400 Subject: [PATCH 84/92] fix highlighting of line-height:1.5 in less mode --- lib/ace/mode/less_highlight_rules.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/mode/less_highlight_rules.js b/lib/ace/mode/less_highlight_rules.js index d39bdeae..a0162765 100644 --- a/lib/ace/mode/less_highlight_rules.js +++ b/lib/ace/mode/less_highlight_rules.js @@ -231,7 +231,7 @@ var LessHighlightRules = function() { regex: "\\.[a-z0-9-_]+" }, { token: "variable.language", - regex: ":[a-z0-9-_]+" + regex: ":[a-z_][a-z0-9-_]*" }, { token: "constant", regex: "[a-z0-9-_]+" From 9f57989043842d906e88e6744da952ac55101260 Mon Sep 17 00:00:00 2001 From: nightwing Date: Wed, 27 May 2015 19:56:28 +0400 Subject: [PATCH 85/92] fix highlighting of tables in Gherkin mode --- lib/ace/mode/_test/tokens_gherkin.json | 40 ++++++++-------- lib/ace/mode/gherkin_highlight_rules.js | 62 +++++++++++++------------ 2 files changed, 53 insertions(+), 49 deletions(-) diff --git a/lib/ace/mode/_test/tokens_gherkin.json b/lib/ace/mode/_test/tokens_gherkin.json index d16ffc6c..173d9798 100644 --- a/lib/ace/mode/_test/tokens_gherkin.json +++ b/lib/ace/mode/_test/tokens_gherkin.json @@ -56,34 +56,34 @@ ],[ "start", ["text"," "], - ["comment","| "], - ["string","start "], - ["comment","| "], - ["string","eat "], - ["comment","| "], - ["string","left "], + ["comment","|"], + ["string"," start "], + ["comment","|"], + ["string"," eat "], + ["comment","|"], + ["string"," left "], ["comment","|"] ],[ "start", ["text"," "], - ["comment","| "], - ["string"," 12 "], - ["comment","| "], - ["string"," 5 "], - ["comment","| "], - ["string"," 7 "], + ["comment","|"], + ["string"," 12 "], + ["comment","|"], + ["string"," 5 "], + ["comment","|"], + ["string"," 7 "], ["comment","|"] ],[ "start", ["text"," "], - ["comment","| "], - ["string"," 20 "], - ["comment","| "], - ["string"," 5 "], - ["comment","| "], - ["string"," 15 "], - ["comment","| "], - ["string"," "] + ["comment","|"], + ["string"," 20 "], + ["comment","|"], + ["string"," 5 "], + ["comment","|"], + ["string"," 15 "], + ["comment","|"], + ["string"," "] ],[ "start" ],[ diff --git a/lib/ace/mode/gherkin_highlight_rules.js b/lib/ace/mode/gherkin_highlight_rules.js index d54db204..04ce877c 100644 --- a/lib/ace/mode/gherkin_highlight_rules.js +++ b/lib/ace/mode/gherkin_highlight_rules.js @@ -36,18 +36,18 @@ var stringEscape = "\\\\(x[0-9A-Fa-f]{2}|[0-7]{3}|[\\\\abfnrtv'\"]|U[0-9A-Fa-f] var GherkinHighlightRules = function() { - // need to include constant ints + // need to include constant ints this.$rules = { - start : [{ + start : [{ token: 'constant.numeric', regex: "(?:(?:[1-9]\\d*)|(?:0))" - }, { - token : "comment", - regex : "#.*$" - }, { - token : "keyword", - regex : "Feature:|Background:|Scenario:|Scenario\ Outline:|Examples:|Given|When|Then|And|But|\\*", - }, { + }, { + token : "comment", + regex : "#.*$" + }, { + token : "keyword", + regex : "Feature:|Background:|Scenario:|Scenario\ Outline:|Examples:|Given|When|Then|And|But|\\*", + }, { token : "string", // multi line """ string start regex : '"{3}', next : "qqstring3" @@ -56,22 +56,22 @@ var GherkinHighlightRules = function() { regex : '"', next : "qqstring" }, { - token : "comment", - regex : "@[A-Za-z0-9]+", - next : "start" + token : "comment", + regex : "@[A-Za-z0-9]+", + next : "start" }, { - token : "comment", - regex : "<.+>" + token : "comment", + regex : "<.+>" }, { - token : "comment", - regex : "\\| ", - next : "table-item" + token : "comment", + regex : "\\|(?=.)", + next : "table-item" }, { - token : "comment", - regex : "\\|$", - next : "start" + token : "comment", + regex : "\\|$", + next : "start" }], - "qqstring3" : [ { + "qqstring3" : [ { token : "constant.language.escape", regex : stringEscape }, { @@ -81,7 +81,7 @@ var GherkinHighlightRules = function() { }, { defaultToken : "string" }], - "qqstring" : [{ + "qqstring" : [{ token : "constant.language.escape", regex : stringEscape }, { @@ -96,15 +96,19 @@ var GherkinHighlightRules = function() { defaultToken: "string" }], "table-item" : [{ + token : "comment", + regex : /$/, + next : "start" + }, { + token : "comment", + regex : /\|/ + }, { token : "string", - regex : "[A-Za-z0-9 ]*", - next : "start" - }], + regex : /\\./ + }, { + defaultToken : "string" + }] }; - - - //new TextHighlightRules().getRules(); - } oop.inherits(GherkinHighlightRules, TextHighlightRules); From 8c1d0ab7cffb7d7efc2b49e5ec15ab9da92c18f1 Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 28 May 2015 13:56:33 +0400 Subject: [PATCH 86/92] fix autocomplete blur handler --- lib/ace/autocomplete.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/ace/autocomplete.js b/lib/ace/autocomplete.js index d3b41b65..331c2001 100644 --- a/lib/ace/autocomplete.js +++ b/lib/ace/autocomplete.js @@ -143,9 +143,10 @@ var Autocomplete = function() { // on IE preventDefault doesn't stop scrollbar from being focussed var el = document.activeElement; var text = this.editor.textInput.getElement(); - if (el != text && ( !this.popup || el.parentNode != this.popup.container ) - && el != this.tooltipNode && e.relatedTarget != this.tooltipNode - && e.relatedTarget != text + var fromTooltip = e.relatedTarget && e.relatedTarget == this.tooltipNode; + var container = this.popup && this.popup.container; + if (el != text && el.parentNode != container && !fromTooltip + && el != this.tooltipNode && e.relatedTarget != text ) { this.detach(); } From abdc4c7510e413ce072ba59c11d93b4ec4c0f26e Mon Sep 17 00:00:00 2001 From: Brian McKenna Date: Fri, 29 May 2015 09:55:02 -0600 Subject: [PATCH 87/92] Fix invalid syntax in autoresize.html The options literal had a duplicate key. --- demo/autoresize.html | 1 - 1 file changed, 1 deletion(-) diff --git a/demo/autoresize.html b/demo/autoresize.html index ef4cbe04..b0464ecd 100644 --- a/demo/autoresize.html +++ b/demo/autoresize.html @@ -49,7 +49,6 @@ require(["ace/ace"], function(ace) { var editor = ace.edit("editor3"); editor.setOptions({ - maxLines: 100, autoScrollEditorIntoView: true, maxLines: 8 }); From 446e3e1bf9fa5346100324b1e870329493bda1ef Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 31 May 2015 16:48:35 +0400 Subject: [PATCH 88/92] fix #2522 Popup position is off when gutter isn't shown --- demo/kitchen-sink/dev_util.js | 2 +- lib/ace/autocomplete.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/kitchen-sink/dev_util.js b/demo/kitchen-sink/dev_util.js index 8dab857c..f466285d 100644 --- a/demo/kitchen-sink/dev_util.js +++ b/demo/kitchen-sink/dev_util.js @@ -39,7 +39,7 @@ function warn() { s.shift(); // remove the getter s = s.join("\n"); // allow easy access to ace in console, but not in ace code - if (!/at Object.InjectedScript.|@debugger eval|snippets:\/{3}/.test(s)) { + if (!/at Object.InjectedScript.|@debugger eval|snippets:\/{3}|\(:\d+:\d+\)/.test(s)) { console.error("trying to access to global variable"); } } diff --git a/lib/ace/autocomplete.js b/lib/ace/autocomplete.js index d3b41b65..caef17ca 100644 --- a/lib/ace/autocomplete.js +++ b/lib/ace/autocomplete.js @@ -100,7 +100,7 @@ var Autocomplete = function() { var rect = editor.container.getBoundingClientRect(); pos.top += rect.top - renderer.layerConfig.offset; pos.left += rect.left - editor.renderer.scrollLeft; - pos.left += renderer.$gutterLayer.gutterWidth; + pos.left += renderer.gutterWidth; this.popup.show(pos, lineHeight); } else if (keepPopupPosition && !prefix) { From 1514d5f374b1b295894f5f911cb8e0ebbda0febe Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 31 May 2015 17:06:32 +0400 Subject: [PATCH 89/92] fix #2483 Exception on ctrl-alt-shift-click when $enableJumpToDef is true --- lib/ace/mouse/multi_select_handler.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ace/mouse/multi_select_handler.js b/lib/ace/mouse/multi_select_handler.js index 45704b39..649043fa 100644 --- a/lib/ace/mouse/multi_select_handler.js +++ b/lib/ace/mouse/multi_select_handler.js @@ -83,7 +83,7 @@ function onMouseDown(e) { var selectionMode; if (editor.$mouseHandler.$enableJumpToDef) { if (ctrl && alt || accel && alt) - selectionMode = "add"; + selectionMode = shift ? "block" : "add"; else if (alt && editor.$blockSelectEnabled) selectionMode = "block"; } else { @@ -117,7 +117,7 @@ function onMouseDown(e) { if (shift) { oldRange = null; - range = selection.ranges[0]; + range = selection.ranges[0] || range; editor.removeSelectionMarker(range); } editor.once("mouseup", function() { From 2249d06337d561508eebcf00966a011cbef061f1 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 31 May 2015 17:41:34 +0400 Subject: [PATCH 90/92] fix #2484 Hard-coded characterWidth in static_highlight.js and remove dependence on mockdom --- demo/static-highlighter/server.js | 17 +++++++++++------ lib/ace/ext/static_highlight.js | 10 +++++----- lib/ace/lib/dom.js | 8 +++++--- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/demo/static-highlighter/server.js b/demo/static-highlighter/server.js index 0878fec2..ea8361d4 100644 --- a/demo/static-highlighter/server.js +++ b/demo/static-highlighter/server.js @@ -2,15 +2,13 @@ * Simple node.js server, which generates the synax highlighted version of itself * using the Ace modes and themes on the server and serving a static web page. */ -// $' + // include ace search path and modules require("amd-loader"); -// load jsdom, which is required by Ace -require("../../lib/ace/test/mockdom"); - var http = require("http"); var fs = require("fs"); +var resolve = require("path").resolve; // load the highlighter and the desired mode and theme var highlighter = require("../../lib/ace/ext/static_highlight"); @@ -20,15 +18,22 @@ var theme = require("../../lib/ace/theme/twilight"); var port = process.env.PORT || 2222; http.createServer(function(req, res) { + var url = req.url; + var path = /[^#?\x00]*/.exec(url)[0]; + var root = resolve(__dirname + "/../../").replace(/\\/g, "/"); + path = resolve(root + "/" + path).replace(/\\/g, "/"); + if (path.indexOf(root + "/") != 0) + path = __filename; res.writeHead(200, {"Content-Type": "text/html; charset=utf-8"}); - fs.readFile(__dirname + "/../../build/src/ace.js", "utf8", function(err, data) { + fs.readFile(path, "utf8", function(err, data) { + if (err) data = err.message; var highlighted = highlighter.render(data, new JavaScriptMode(), theme); res.end( '\n' + '\n' + - highlighted.html + + highlighted.html + '' ); }); diff --git a/lib/ace/ext/static_highlight.js b/lib/ace/ext/static_highlight.js index b2287f15..2acb3ac5 100644 --- a/lib/ace/ext/static_highlight.js +++ b/lib/ace/ext/static_highlight.js @@ -37,6 +37,10 @@ var baseStyles = require("../requirejs/text!./static.css"); var config = require("../config"); var dom = require("../lib/dom"); +var SimpleTextLayer = function() { + this.config = {}; +}; +SimpleTextLayer.prototype = TextLayer.prototype; var highlight = function(el, opts, callback) { var m = el.className.match(/lang-(\w+)/); @@ -149,12 +153,8 @@ highlight.renderSync = function(input, mode, theme, lineStart, disableGutter) { session.setUseWorker(false); session.setMode(mode); - var textLayer = new TextLayer(document.createElement("div")); + var textLayer = new SimpleTextLayer(); textLayer.setSession(session); - textLayer.config = { - characterWidth: 10, - lineHeight: 20 - }; session.setValue(input); diff --git a/lib/ace/lib/dom.js b/lib/ace/lib/dom.js index ef2f7caa..2cbfe23e 100644 --- a/lib/ace/lib/dom.js +++ b/lib/ace/lib/dom.js @@ -91,6 +91,11 @@ exports.toggleCssClass = function(el, name) { return add; }; +if (typeof document == "undefined") { + exports.importCssString = function() {}; + return; +} + /* * Add or remove a CSS class from the list of classes on the given node * depending on the value of include @@ -173,9 +178,6 @@ exports.getInnerHeight = function(element) { }; -if (typeof document == "undefined") - return; - if (window.pageYOffset !== undefined) { exports.getPageScrollTop = function() { return window.pageYOffset; From 3f31ca57ed5b31443e0874dd40e15f2990d552b2 Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 1 Jun 2015 01:06:39 +0400 Subject: [PATCH 91/92] fix typo --- static.js | 3 ++- tool/mode_creator.js | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/static.js b/static.js index a711715e..3b75c2b6 100755 --- a/static.js +++ b/static.js @@ -21,7 +21,7 @@ http.createServer(function(req, res) { if (req.method == "PUT") { if (!allowSave) return error(res, 404, "Saving not allowed pass --allow-save to enable"); - save(req, res, filename); + return save(req, res, filename); } fs.exists(filename, function(exists) { @@ -86,6 +86,7 @@ function save(req, res, filePath) { } res.statusCode = 200; res.end("OK"); + console.log("saved ", filePath); }); } diff --git a/tool/mode_creator.js b/tool/mode_creator.js index 7a2aea59..44aa67d0 100644 --- a/tool/mode_creator.js +++ b/tool/mode_creator.js @@ -126,8 +126,8 @@ function handleSaveResult(err, editor) { return log( "Write access to this file is disabled.\n"+ "To enable saving your changes to disk, clone the Ace repository\n"+ - "and run the included web server with the --allow-write option\n"+ - "`node static.js --allow-write` or `static.py --puttable=*`" + "and run the included web server with the --allow-save option\n"+ + "`node static.js --allow-save` or `static.py --puttable=*`" ); } editor.session.getUndoManager().markClean(); From 6cec0b28b2d8f7a41b8e16242bbb170ae5fab87d Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 1 Jun 2015 13:50:32 +0400 Subject: [PATCH 92/92] improve tmlanguage importer --- tool/lib.js | 105 +++++++++++++++++++++++++++++++++++++++++++-- tool/tmlanguage.js | 4 +- 2 files changed, 104 insertions(+), 5 deletions(-) diff --git a/tool/lib.js b/tool/lib.js index 8809595c..3d57b6e0 100644 --- a/tool/lib.js +++ b/tool/lib.js @@ -14,8 +14,16 @@ exports.parsePlist = function(xmlOrJSON, callback) { }); } else { try { - xmlOrJSON = xmlOrJSON.replace(/^\s*\/\/.*/gm, ""); - json = JSON.parse(xmlOrJSON) + xmlOrJSON = xmlOrJSON.replace( + /("(?:\\.|[^"])*")|(?:,\s*)+([\]\}])|(\w+)\s*:|([\]\}]\s*[\[\{])|(\/\/.*|\/\*(?:[^\*]|\*(?=[^\/]))*?\*\/)/g, + function(_, str, extraComma, noQuote, missingComma, comment) { + if (comment) + return ""; + if (missingComma) + return missingComma[0] + "," + missingComma.slice(1); + return str || extraComma || '"' + noQuote + '":'; + }); + json = JSON.parse(xmlOrJSON); } catch(e) { json = cson.parse(xmlOrJSON); } @@ -24,10 +32,101 @@ exports.parsePlist = function(xmlOrJSON, callback) { return json; }; + exports.formatJSON = function(object, initialIndent) { - return util.inspect(object, false, 40).replace(/^/gm, initialIndent||""); + return JSON.stringify(object, null, 4).replace(/^/gm, initialIndent||""); }; +exports.formatJS = function(object, initialIndent) { + return formatJS(object, 4, initialIndent); +}; + +function formatJS(object, indent, initialIndent) { + if (typeof indent == "number") + indent = Array(indent + 1).join(" "); + + function $format(buffer, totalIndent, state, o) { + if (typeof o != "object" || !o) { + if (typeof o == "string") + buffer.push(JSON.stringify(o)); + else + buffer.push("" + o); + } + else if (Array.isArray(o)) { + buffer.push("[") + + var len = totalIndent.length + var oneLine = true; + for (var i = 0; i < o.length; i++) { + if (typeof o[i] == "string") { + len += o[i].length + 2 + } else if (!o[i]) { + len += (o[i] + "").length + } else { + oneLine = false; + break; + } + len += 2; + if (len > 60) { + oneLine = false; + break; + } + } + + for (var i = 0; i < o.length; i++) { + if (o[i] && typeof o[i] == "object") { + $format(buffer, totalIndent, state, o[i]); + if (i < o.length - 1) + buffer.push(", "); + } else { + if (oneLine) + i && buffer.push(" "); + else + buffer.push("\n", totalIndent + indent) + $format(buffer, totalIndent + indent, state, o[i]); + if (i < o.length - 1) + buffer.push(","); + } + + } + if (!oneLine && buffer[buffer.length - 1] != "}") + buffer.push("\n" + totalIndent) + buffer.push("]") + } + else { + var keys = Object.keys(o); + buffer.push("{", "\n"); + for (var i = 0; i < keys.length; i++) { + buffer.push(totalIndent + indent); + if (/^\w+$/.test(keys[i])) + buffer.push(keys[i]); + else + buffer.push(JSON.stringify(keys[i])); + buffer.push(": ") + + if (keys[i] == "regex" && typeof o[keys[i]] == "string") { + try { + var re = new RegExp(o[keys[i]]); + buffer.push("/" + re.source.replace(/\\.|\//g, function(f) { + return f.length == 1 ? "\\" + f : f; + }) + "/"); + } catch(e) { + $format(buffer, totalIndent + indent, state, o[keys[i]]); + } + } else { + $format(buffer, totalIndent + indent, state, o[keys[i]]); + } + + if (i < keys.length - 1) + buffer.push(",", "\n"); + } + buffer.push("\n", totalIndent, "}"); + } + } + var buffer = []; + $format(buffer, initialIndent || "", {}, object); + return buffer.join(""); +} exports.fillTemplate = function(template, replacements) { return template.replace(/%(.+?)%/g, function(str, m) { diff --git a/tool/tmlanguage.js b/tool/tmlanguage.js index 0e458330..b5d8b851 100644 --- a/tool/tmlanguage.js +++ b/tool/tmlanguage.js @@ -662,10 +662,10 @@ function convertTmLanguage(name, langStr) { var languageHighlightRules = lib.fillTemplate(modeHighlightTemplate, { language: languageNameSanitized, - languageTokens: lib.formatJSON(patterns, " ").trim(), + languageTokens: lib.formatJS(patterns, " ").trim(), uuid: language.uuid, name: name, - metaData: lib.formatJSON(language, " ").trim() + metaData: lib.formatJS(language, "").trim() }); if (devMode) {