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();