#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.
This commit is contained in:
aldendaniels 2014-01-01 00:41:45 -06:00
commit 2e6f12725b
28 changed files with 437 additions and 450 deletions

View file

@ -100,67 +100,55 @@ var Anchor = exports.Anchor = function(doc, row, column) {
* *
**/ **/
this.onChange = function(e) { this.onChange = function(e) {
var delta = e.data;
var range = delta.range; function _pointsInOrder(point1, point2, equalPointsInOrder)
{
if (range.start.row == range.end.row && range.start.row != this.row) var bColIsAfter = equalPointsInOrder ? point1.column <= point2.column : point1.column < point2.column;
return; return (point1.row < point2.row) || (point1.row == point2.row && bColIsAfter);
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;
}
}
} }
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);
}; };
/** /**

View file

@ -54,7 +54,7 @@ module.exports = {
var doc = new Document("juhu\nkinners"); var doc = new Document("juhu\nkinners");
var anchor = new Anchor(doc, 1, 4); 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); assert.position(anchor.getPosition(), 1, 7);
}, },
@ -62,7 +62,7 @@ module.exports = {
var doc = new Document("juhu\nkinners"); var doc = new Document("juhu\nkinners");
var anchor = new Anchor(doc, 1, 4); var anchor = new Anchor(doc, 1, 4);
doc.insertLines(1, ["123", "456"]); doc.insertFullLines(1, ["123", "456"]);
assert.position(anchor.getPosition(), 3, 4); assert.position(anchor.getPosition(), 3, 4);
}, },
@ -70,7 +70,7 @@ module.exports = {
var doc = new Document("juhu\nkinners"); var doc = new Document("juhu\nkinners");
var anchor = new Anchor(doc, 1, 4); 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); assert.position(anchor.getPosition(), 2, 4);
}, },
@ -78,7 +78,7 @@ module.exports = {
var doc = new Document("juhu\nkinners"); var doc = new Document("juhu\nkinners");
var anchor = new Anchor(doc, 1, 4); 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); assert.position(anchor.getPosition(), 2, 2);
}, },
@ -110,7 +110,7 @@ module.exports = {
var doc = new Document("juhu\n1\n2\nkinners"); var doc = new Document("juhu\n1\n2\nkinners");
var anchor = new Anchor(doc, 3, 4); var anchor = new Anchor(doc, 3, 4);
doc.removeLines(1, 2); doc.removeFullLines(1, 2);
assert.position(anchor.getPosition(), 1, 4); assert.position(anchor.getPosition(), 1, 4);
}, },
@ -134,7 +134,7 @@ module.exports = {
var doc = new Document("juhu\nkinners\n123"); var doc = new Document("juhu\nkinners\n123");
var anchor = new Anchor(doc, 1, 5); var anchor = new Anchor(doc, 1, 5);
doc.removeLines(1, 1); doc.removeFullLines(1, 1);
assert.position(anchor.getPosition(), 1, 0); assert.position(anchor.getPosition(), 1, 0);
}, },
@ -173,9 +173,9 @@ module.exports = {
var doc = new Document("juhu\nkinners\n123"); var doc = new Document("juhu\nkinners\n123");
var anchor = new Anchor(doc, 2, 4); var anchor = new Anchor(doc, 2, 4);
doc.removeLines(0, 3); doc.removeFullLines(0, 3);
assert.position(anchor.getPosition(), 0, 0); 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.position(anchor.getPosition(), 3, 0);
assert.equal(doc.getValue(), "a\nb\nc\n"); assert.equal(doc.getValue(), "a\nb\nc\n");
} }

View file

@ -170,7 +170,7 @@ var Autocomplete = function() {
"Ctrl-Down|Ctrl-End": function(editor) { editor.completer.goTo("end"); }, "Ctrl-Down|Ctrl-End": function(editor) { editor.completer.goTo("end"); },
"Esc": function(editor) { editor.completer.detach(); }, "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(); }, "Return": function(editor) { editor.completer.insertMatch(); },
"Shift-Return": function(editor) { editor.completer.insertMatch(true); }, "Shift-Return": function(editor) { editor.completer.insertMatch(true); },
"Tab": function(editor) { editor.completer.insertMatch(); }, "Tab": function(editor) { editor.completer.insertMatch(); },

View file

@ -183,7 +183,7 @@ var BackgroundTokenizer = function(tokenizer, editor) {
if (len === 0) { if (len === 0) {
this.lines[startRow] = null; 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.lines.splice(startRow, len + 1, null);
this.states.splice(startRow, len + 1, null); this.states.splice(startRow, len + 1, null);
} else { } else {

View file

@ -70,7 +70,7 @@ module.exports = {
forceTokenize(doc) forceTokenize(doc)
testStates(doc, ["comment_regex_allowed", "comment_regex_allowed"]) 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"]) testStates(doc, [undefined, undefined, "comment_regex_allowed"])
forceTokenize(doc) forceTokenize(doc)

View file

@ -508,13 +508,13 @@ exports.commands = [{
scrollIntoView: "selectionPart" scrollIntoView: "selectionPart"
}, { }, {
name: "insertstring", name: "insertstring",
exec: function(editor, str) { editor.insert(str); }, exec: function(editor, str) { editor.insertText(str); },
multiSelectAction: "forEach", multiSelectAction: "forEach",
scrollIntoView: "cursor" scrollIntoView: "cursor"
}, { }, {
name: "inserttext", name: "inserttext",
exec: function(editor, args) { exec: function(editor, args) {
editor.insert(lang.stringRepeat(args.text || "", args.times || 1)); editor.insertText(lang.stringRepeat(args.text || "", args.times || 1));
}, },
multiSelectAction: "forEach" multiSelectAction: "forEach"
}, { }, {

View file

@ -51,17 +51,17 @@ var Anchor = require("./anchor").Anchor;
* @constructor * @constructor
**/ **/
var Document = function(text) { var Document = function(textOrLines) {
this.$lines = []; this.$lines = [''];
// There has to be one line at least in the document. If you pass an empty // 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. // string to the insert function, nothing will happen. Workaround.
if (text.length == 0) { if (textOrLines.length == 0) {
this.$lines = [""]; this.$lines = [""];
} else if (Array.isArray(text)) { } else if (Array.isArray(textOrLines)) {
this._insertLines(0, text); this.insertLines({row: 0, column: 0}, textOrLines);
} else { } 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) { this.setValue = function(text) {
var len = this.getLength(); var len = this.getLength();
this.remove(new Range(0, 0, len, this.getLine(len-1).length)); 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} * @returns {String}
**/ **/
this.getTextRange = function(range) { this.getTextRange = function(range) {
return this._getLinesForRange(range).join(this.getNewLineCharacter());
};
this._getLinesForRange = function(range) {
if (range.start.row == range.end.row) { if (range.start.row == range.end.row) {
return this.getLine(range.start.row) return [this.getLine(range.start.row)
.substring(range.start.column, range.end.column); .substring(range.start.column, range.end.column)];
} }
var lines = this.getLines(range.start.row, range.end.row); var lines = this.getLines(range.start.row, range.end.row);
lines[0] = (lines[0] || "").substring(range.start.column); lines[0] = (lines[0] || "").substring(range.start.column);
var l = lines.length - 1; var l = lines.length - 1;
if (range.end.row - range.start.row == l) if (range.end.row - range.start.row == l)
lines[l] = lines[l].substring(0, range.end.column); lines[l] = lines[l].substring(0, range.end.column);
return lines.join(this.getNewLineCharacter()); return lines;
}; };
this.$clipPosition = function(position) { this.$clipPosition = function(position) {
var length = this.getLength(); var length = this.getLength();
if (position.row >= length) { if (position.row >= length)
{
position.row = Math.max(0, length - 1); position.row = Math.max(0, length - 1);
position.column = this.getLine(length-1).length; position.column = this.getLine(length - 1).length;
} else if (position.row < 0) }
position.row = 0; else
{
position.row = Math.max(0, position.row);
position.column = Math.min(Math.max(position.column, 0), this.getLine(position.row).length);
}
return position; 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`. * 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`. * @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) { this.insertText = function(position, text) {
if (!text || text.length === 0)
return position; // Only detect new lines if the document has no line break yet.
position = this.$clipPosition(position);
// only detect new lines if the document has no line break yet
if (this.getLength() <= 1) if (this.getLength() <= 1)
this.$detectNewLine(text); this.$detectNewLine(text);
var lines = this.$split(text); return this.insertLines(position, 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;
}; };
/** /**
* Fires whenever the document changes. * 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: * 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]]) * * `"insert"`
* * `range`: the [[Range]] of the change within the document * * `range`: the [[Range]] of the change within the document
* * `lines`: the lines in the document that are changing * * `lines`: the lines being added
* * `"insertText"` (emitted by [[Document.insertNewLine]]) * * `"delete"`
* * `range`: the [[Range]] of the change within the document * * `range`: the [[Range]] of the change within the document
* * `text`: the text that's being added * * `lines`: the lines being removed
* * `"removeLines"` (emitted by [[Document.insertLines]]) *
* * `range`: the [[Range]] of the change within the document * @event change
* * `lines`: the lines in the document that were removed * @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.
* * `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.
*
**/
/** /**
* 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 {Number} row The index of the row to insert at
* @param {Array} lines An array of strings * @param {Array} lines An array of strings
* @returns {Object} Contains the final row and column, like this: * @returns {Object} Contains the final row and column, like this:
@ -300,100 +338,62 @@ var Document = function(text) {
* ``` * ```
* *
**/ **/
this.insertLines = function(row, lines) { this.insertFullLines = function(row, lines)
if (row >= this.getLength()) {
return this.insert({row: row, column: 0}, "\n" + lines.join("\n")); // Clip to document.
return this._insertLines(Math.max(row, 0), lines); // Allow one past the document end.
}; row = Math.min(Math.max(row, 0), this.getLength());
this._insertLines = function(row, lines) {
if (lines.length == 0) // Calculate insertion point.
return {row: row, column: 0}; var column = 0;
if (row < this.getLength()) // Insert before the specified row.
// 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 lines = lines.concat(['']);
if (lines.length > 0xFFFF) { column = 0;
var end = this._insertLines(row, lines.slice(0xFFFF));
lines = lines.slice(0, 0xFFFF);
} }
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); * Inserts the elements in `lines` into the document, starting at the position index given by `row`. This method also triggers the `'change'` event.
this.$lines.splice.apply(this.$lines, args); * @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); // Calculate insertion range end point.
var delta = { this.$clipPosition(position);
action: "insertLines", var endPoint = {
range: range, 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 lines: lines
}; });
this._emit("change", { data: delta });
return end || range.end; return endPoint;
}; }
/**
* 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:<br/>
* ```
* {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;
};
/** /**
* Removes the `range` from the document. * Removes the `range` from the document.
@ -402,37 +402,16 @@ var Document = function(text) {
* *
**/ **/
this.remove = function(range) { this.remove = function(range) {
if (!range instanceof Range)
range = Range.fromPoints(range.start, range.end); // Apply delta (emits change).
// clip to document range = this.$getClippedRange(range);
range.start = this.$clipPosition(range.start); this.applyDelta(
range.end = this.$clipPosition(range.end); {
action: 'delete',
if (range.isEmpty()) range: range,
return range.start; lines: this._getLinesForRange(range),
});
var firstRow = range.start.row; return range.start;
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;
}; };
/** /**
@ -444,21 +423,18 @@ var Document = function(text) {
* *
**/ **/
this.removeInLine = function(row, startColumn, endColumn) { this.removeInLine = function(row, startColumn, endColumn) {
if (startColumn == endColumn)
return; // Calculate deleteion range.
var range = new Range(row, startColumn, row, endColumn); var range = new Range(row, startColumn, row, endColumn);
var line = this.getLine(row); range = this.$getClippedRange(range);
var removed = line.substring(startColumn, endColumn);
var newLine = line.substring(0, startColumn) + line.substring(endColumn, line.length); // Apply delta (emits change).
this.$lines.splice(row, 1, newLine); this.applyDelta({
action: "delete",
var delta = {
action: "removeText",
range: range, range: range,
text: removed lines: this._getLinesForRange(range)
}; });
this._emit("change", { data: delta });
return range.start; return range.start;
}; };
@ -469,24 +445,35 @@ var Document = function(text) {
* @returns {[String]} Returns all the removed lines. * @returns {[String]} Returns all the removed lines.
* *
**/ **/
this.removeLines = function(firstRow, lastRow) { this.removeFullLines = function(firstRow, lastRow) {
if (firstRow < 0 || lastRow >= this.getLength())
return this.remove(new Range(firstRow, 0, lastRow + 1, 0)); // Clip to document.
return this._removeLines(firstRow, lastRow); firstRow = Math.min(Math.max(0, firstRow), this.getLength() - 1);
}; lastRow = Math.min(Math.max(0, lastRow ), this.getLength() - 1);
this._removeLines = function(firstRow, lastRow) { // Calculate deletion range.
var range = new Range(firstRow, 0, lastRow + 1, 0); // Delete the ending new line unless we're at the end of the document.
var removed = this.$lines.splice(firstRow, lastRow - firstRow + 1); // If we're at the end of the document, delete the starting new line.
var deleteFirstNewLine = lastRow == this.getLength() - 1 && firstRow > 0;
var delta = { var deleteLastNewLine = lastRow < this.getLength() - 1;
action: "removeLines", 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, range: range,
nl: this.getNewLineCharacter(), lines: this._getLinesForRange(range)
lines: removed });
};
this._emit("change", { data: delta }); // Return the deleted lines.
return removed; return deletedLines;
}; };
/** /**
@ -495,20 +482,16 @@ var Document = function(text) {
* *
**/ **/
this.removeNewLine = function(row) { this.removeNewLine = function(row) {
var firstLine = this.getLine(row);
var secondLine = this.getLine(row+1); if (row < this.getLength() - 1 && row >= 0)
{
var range = new Range(row, firstLine.length, row+1, 0); // Apply delta (emits change).
var line = firstLine + secondLine; this.applyDelta({
action: "delete",
this.$lines.splice(row, 2, line); range: new Range(row, this.getLine(row).length, row + 1, 0),
lines: ['', '']
var delta = { });
action: "removeText", }
range: range,
text: this.getNewLineCharacter()
};
this._emit("change", { data: delta });
}; };
/** /**
@ -534,53 +517,90 @@ var Document = function(text) {
this.remove(range); this.remove(range);
if (text) { if (text) {
var end = this.insert(range.start, text); var end = this.insertText(range.start, text);
} }
else { else {
end = range.start; end = range.start;
} }
return end; 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) { this.applyDeltas = function(deltas) {
for (var i=0; i<deltas.length; i++) { for (var i=0; i<deltas.length; i++) {
var delta = deltas[i]; this.applyDelta(deltas[i]);
var range = Range.fromPoints(delta.range.start, delta.range.end);
if (delta.action == "insertLines")
this.insertLines(range.start.row, delta.lines);
else if (delta.action == "insertText")
this.insert(range.start, delta.text);
else if (delta.action == "removeLines")
this._removeLines(range.start.row, range.end.row - 1);
else if (delta.action == "removeText")
this.remove(range);
} }
}; };
this.applyDelta = function(delta) {
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);
}
// Validate delta.
this.$validateDelta(delta);
// Apply 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;
default:
throw 'Invalid delta type: ' + delta.action
}
this._emit("change", { data: delta });
},
/** /**
* Reverts any changes previously applied. These can be either `'includeText'`, `'insertLines'`, `'removeText'`, and `'removeLines'`. * Reverts any changes previously applied. These can be either `'insert'` or `'delete'`.
**/ **/
this.revertDeltas = function(deltas) { this.revertDeltas = function(deltas) {
for (var i=deltas.length-1; i>=0; i--) { for (var i=deltas.length-1; i>=0; i--) {
var delta = deltas[i]; this.revertDelta(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 = 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. * Converts an index position in a document to a `{row, column}` object.

View file

@ -48,7 +48,7 @@ module.exports = {
var deltas = []; var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); }); 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")); assert.equal(doc.getValue(), ["1juhu2", "34"].join("\n"));
var d = deltas.concat(); var d = deltas.concat();
@ -65,7 +65,7 @@ module.exports = {
var deltas = []; var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); }); 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")); assert.equal(doc.getValue(), ["1", "2", "34"].join("\n"));
var d = deltas.concat(); var d = deltas.concat();
@ -82,7 +82,7 @@ module.exports = {
var deltas = []; var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); }); 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")); assert.equal(doc.getValue(), ["aa", "bb", "12", "34"].join("\n"));
var d = deltas.concat(); var d = deltas.concat();
@ -99,7 +99,7 @@ module.exports = {
var deltas = []; var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); }); 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")); assert.equal(doc.getValue(), ["12", "34", "aa", "bb"].join("\n"));
}, },
@ -109,7 +109,7 @@ module.exports = {
var deltas = []; var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); }); 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")); assert.equal(doc.getValue(), ["12", "aa", "bb", "34"].join("\n"));
var d = deltas.concat(); var d = deltas.concat();
@ -126,7 +126,7 @@ module.exports = {
var deltas = []; var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); }); 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")); assert.equal(doc.getValue(), ["aa", "bb", "cc12", "34"].join("\n"));
var d = deltas.concat(); var d = deltas.concat();
@ -143,7 +143,7 @@ module.exports = {
var deltas = []; var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); }); 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")); assert.equal(doc.getValue(), ["12", "34aa", "bb", "cc"].join("\n"));
var d = deltas.concat(); var d = deltas.concat();
@ -160,7 +160,7 @@ module.exports = {
var deltas = []; var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); }); 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")); assert.equal(doc.getValue(), ["1aa", "bb", "cc2", "34"].join("\n"));
var d = deltas.concat(); var d = deltas.concat();
@ -235,7 +235,7 @@ module.exports = {
"test: remove lines should return the removed lines" : function() { "test: remove lines should return the removed lines" : function() {
var doc = new Document(["1234", "5678", "abcd"]); 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")); assert.equal(removed.join("\n"), ["5678", "abcd"].join("\n"));
}, },

View file

@ -1136,8 +1136,8 @@ var EditSession = function(text, mode) {
* *
* *
**/ **/
this.insert = function(position, text) { this.insertText = function(position, text) {
return this.doc.insert(position, text); return this.doc.insertText(position, text);
}; };
/** /**
@ -1151,6 +1151,19 @@ var EditSession = function(text, mode) {
this.remove = function(range) { this.remove = function(range) {
return this.doc.remove(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. * Reverts previous changes to your document.
@ -1227,9 +1240,7 @@ var EditSession = function(text, mode) {
this.$getUndoSelection = function(deltas, isUndo, lastUndoRange) { this.$getUndoSelection = function(deltas, isUndo, lastUndoRange) {
function isInsert(delta) { function isInsert(delta) {
var insert = return isUndo ? delta.action !== "insert" : delta.action === "insert";
delta.action === "insertText" || delta.action === "insertLines";
return isUndo ? !insert : insert;
} }
var delta = deltas[0]; 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) { if (folds.length) {
var oldStart = fromRange.start; var oldStart = fromRange.start;
var newStart = toRange.start; var newStart = toRange.start;
@ -1373,7 +1384,7 @@ var EditSession = function(text, mode) {
this.indentRows = function(startRow, endRow, indentString) { this.indentRows = function(startRow, endRow, indentString) {
indentString = indentString.replace(/\t/g, this.getTabString()); indentString = indentString.replace(/\t/g, this.getTabString());
for (var row=startRow; row<=endRow; row++) 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; x.end.row += diff;
return x; return x;
}); });
var lines = dir == 0 var lines = dir == 0
? this.doc.getLines(firstRow, lastRow) ? this.doc.getLines(firstRow, lastRow)
: this.doc.removeLines(firstRow, lastRow); : this.doc.removeFullLines(firstRow, lastRow);
this.doc.insertLines(firstRow+diff, lines); this.doc.insertFullLines(firstRow+diff, lines);
folds.length && this.addFolds(folds); folds.length && this.addFolds(folds);
return diff; return diff;
}; };
@ -1444,8 +1455,6 @@ var EditSession = function(text, mode) {
* @param {Number} lastRow The final row to move up * @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. * @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) { this.moveLinesUp = function(firstRow, lastRow) {
return this.$moveLines(firstRow, lastRow, -1); 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} firstRow The starting row to move down
* @param {Number} lastRow The final 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. * @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) { this.moveLinesDown = function(firstRow, lastRow) {
return this.$moveLines(firstRow, lastRow, 1); return this.$moveLines(firstRow, lastRow, 1);
@ -1668,28 +1675,17 @@ var EditSession = function(text, mode) {
this.$updateInternalDataOnChange = function(e) { this.$updateInternalDataOnChange = function(e) {
var useWrapMode = this.$useWrapMode; var useWrapMode = this.$useWrapMode;
var len;
var action = e.data.action; 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 start = e.data.range.start;
var end = e.data.range.end; var end = e.data.range.end;
var firstRow = start.row;
var lastRow = end.row;
var len = lastRow - firstRow;
var removedFolds = null; 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; this.$updating = true;
if (len != 0) { if (len != 0) {
if (action.indexOf("remove") != -1) { if (action == "delete") {
this[useWrapMode ? "$wrapData" : "$rowLengthCache"].splice(firstRow, len); this[useWrapMode ? "$wrapData" : "$rowLengthCache"].splice(firstRow, len);
var foldLines = this.$foldData; 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 // Realign folds. E.g. if you add some new chars before a fold, the
// fold should "move" to the right. // fold should "move" to the right.
len = Math.abs(e.data.range.start.column - e.data.range.end.column); 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. // Get all the folds in the change range and remove them.
removedFolds = this.getFoldsInRange(e.data.range); removedFolds = this.getFoldsInRange(e.data.range);
this.removeFolds(removedFolds); this.removeFolds(removedFolds);

View file

@ -833,7 +833,7 @@ function Folding() {
if (len === 0) { if (len === 0) {
this.foldWidgets[firstRow] = null; this.foldWidgets[firstRow] = null;
} else if (delta.action == "removeText" || delta.action == "removeLines") { } else if (delta.action == "delete") {
this.foldWidgets.splice(firstRow, len + 1, null); this.foldWidgets.splice(firstRow, len + 1, null);
} else { } else {
var args = Array(len + 1); var args = Array(len + 1);

View file

@ -430,12 +430,12 @@ module.exports = {
session.setTabSize(4); session.setTabSize(4);
assert.equal(session.getScreenWidth(), 2); assert.equal(session.getScreenWidth(), 2);
session.doc.insertNewLine({row: 0, column: Infinity}); session.doc.insertLines({row: 0, column: Infinity}, ['', '']);
session.doc.insertLines(1, ["123"]); session.doc.insertFullLines(1, ["123"]);
assert.equal(session.getScreenWidth(), 3); assert.equal(session.getScreenWidth(), 3);
session.doc.insertNewLine({row: 0, column: Infinity}); session.doc.insertLines({row: 0, column: Infinity}, ['', '']);
session.doc.insertLines(1, ["\t\t"]); session.doc.insertFullLines(1, ["\t\t"]);
assert.equal(session.getScreenWidth(), 8); assert.equal(session.getScreenWidth(), 8);
@ -459,9 +459,9 @@ module.exports = {
session.setUseWrapMode(true); session.setUseWrapMode(true);
document.insertLines(0, ["a", "b"]); document.insertFullLines(0, ["a", "b"]);
document.insertLines(2, ["c", "d"]); document.insertFullLines(2, ["c", "d"]);
document.removeLines(1, 2); document.removeFullLines(1, 2);
}, },
"test wrapMode init has to create wrapData array": function() { "test wrapMode init has to create wrapData array": function() {
@ -649,7 +649,7 @@ module.exports = {
var foldLines = session.$foldData; var foldLines = session.$foldData;
function insert(row, column, text) { 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 // Force the session to store all changes made to the document NOW
// on the undoManager's queue. Otherwise we can't undo in separate // on the undoManager's queue. Otherwise we can't undo in separate
@ -748,7 +748,7 @@ module.exports = {
undoManager = session.getUndoManager(), undoManager = session.getUndoManager(),
foldLines = session.$foldData; foldLines = session.$foldData;
function insert(row, column, text) { 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 // Force the session to store all changes made to the document NOW
// on the undoManager's queue. Otherwise we can't undo in separate // on the undoManager's queue. Otherwise we can't undo in separate
// steps later. // steps later.

View file

@ -601,19 +601,10 @@ var Editor = function(renderer, session) {
* *
**/ **/
this.onDocumentChange = function(e) { this.onDocumentChange = function(e) {
var delta = e.data; this.renderer.updateLines(e.data.range.start.row, e.data.range.end.row);
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._emit("change", e); 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(); this.$cursorChange();
}; };
@ -827,7 +818,7 @@ var Editor = function(renderer, session) {
if (this.$readOnly) if (this.$readOnly)
return; return;
this._emit("paste", text); 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 session = this.session;
var mode = session.getMode(); var mode = session.getMode();
var cursor = this.getCursorPosition(); var cursor = this.getCursorPosition();
@ -887,7 +878,7 @@ var Editor = function(renderer, session) {
var lineState = session.getState(cursor.row); var lineState = session.getState(cursor.row);
var line = session.getLine(cursor.row); var line = session.getLine(cursor.row);
var shouldOutdent = mode.checkOutdent(lineState, line, text); 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 && transform.selection) {
if (transform.selection.length == 2) { // Transform relative to the current column 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)) { if (session.getDocument().isNewLine(text)) {
var lineIndent = mode.getNextLineIndent(lineState, line.slice(0, cursor.column), session.getTabString()); 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) if (shouldOutdent)
mode.autoOutdent(lineState, session, cursor.row); mode.autoOutdent(lineState, session, cursor.row);
@ -1279,7 +1270,7 @@ var Editor = function(renderer, session) {
} }
var cursor = this.getCursorPosition(); var cursor = this.getCursorPosition();
this.insert("\n"); this.insertText("\n");
this.moveCursorToPosition(cursor); this.moveCursorToPosition(cursor);
}; };
@ -1378,7 +1369,7 @@ var Editor = function(renderer, session) {
this.selection.setSelectionRange(range); this.selection.setSelectionRange(range);
indentString = "\t"; indentString = "\t";
} }
return this.insert(indentString); return this.insertText(indentString);
}; };
/** /**
@ -1516,15 +1507,7 @@ var Editor = function(renderer, session) {
**/ **/
this.removeLines = function() { this.removeLines = function() {
var rows = this.$getSelectedRows(); var rows = this.$getSelectedRows();
var range; this.session.removeFullLines(rows.first, rows.last);
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.clearSelection(); this.clearSelection();
}; };
@ -1538,7 +1521,7 @@ var Editor = function(renderer, session) {
doc.duplicateLines(row, row); doc.duplicateLines(row, row);
} else { } else {
var point = reverse ? range.start : range.end; 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.start = point;
range.end = endPoint; range.end = endPoint;

View file

@ -172,7 +172,7 @@ module.exports = {
self.session1.setMode(new HtmlMode()); self.session1.setMode(new HtmlMode());
// 5. Try to type valid HTML // 5. Try to type valid HTML
self.session1.insert({row: 0, column: 0}, "<html></html>"); self.session1.insertText({row: 0, column: 0}, "<html></html>");
setTimeout(function() { setTimeout(function() {
assert.equal(Object.keys(self.session1.getAnnotations()).length, 0); assert.equal(Object.keys(self.session1.getAnnotations()).length, 0);

View file

@ -150,7 +150,7 @@ module.exports = {
var editor = new Editor(new MockRenderer(), new EditSession(["1234", "1234567890"])); var editor = new Editor(new MockRenderer(), new EditSession(["1234", "1234567890"]));
editor.navigateTo(0, 3); editor.navigateTo(0, 3);
editor.insert("juhu"); editor.insertText("juhu");
editor.navigateDown(); editor.navigateDown();
assert.position(editor.getCursorPosition(), 1, 7); assert.position(editor.getCursorPosition(), 1, 7);

View file

@ -581,12 +581,12 @@ var onSelectionChange = function(evt) {
var onChange = function(evt) { var onChange = function(evt) {
var data = evt.data; var data = evt.data;
switch (data.action) { switch (data.action) {
case 'removeText': case 'delete':
cvox.Api.speak(data.text, 0, DELETED_PROP); cvox.Api.speak(data.text, 0, DELETED_PROP);
/* Let the future cursor change event know it's from text change. */ /* Let the future cursor change event know it's from text change. */
changed = true; changed = true;
break; break;
case 'insertText': case 'insert':
cvox.Api.speak(data.text, 0); cvox.Api.speak(data.text, 0);
/* Let the future cursor change event know it's from text change. */ /* Let the future cursor change event know it's from text change. */
changed = true; changed = true;

View file

@ -236,7 +236,7 @@ var ElasticTabstopsLite = function(editor) {
if (difference > 0) { if (difference > 0) {
// put the spaces after the tab and then delete the tab, so any insertion // put the spaces after the tab and then delete the tab, so any insertion
// points behave as expected // 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); this.$editor.session.getDocument().removeInLine(row, it, it + 1);
bias += difference; bias += difference;

View file

@ -153,7 +153,7 @@ exports.convertIndentation = function(session, ch, len) {
if (toInsert != match) { if (toInsert != match) {
doc.removeInLine(i, 0, match.length); doc.removeInLine(i, 0, match.length);
doc.insertInLine({row: i, column: 0}, toInsert); doc.insertText({row: i, column: 0}, toInsert);
} }
} }
} }

View file

@ -104,7 +104,7 @@ var actions = exports.actions = {
if (param && param.length) { if (param && param.length) {
if (param.length > 1) if (param.length > 1)
param = param == "return" ? "\n" : param == "tab" ? "\t" : param; param = param == "return" ? "\n" : param == "tab" ? "\t" : param;
repeat(function() { editor.insert(param); }, count || 1); repeat(function() { editor.insertText(param); }, count || 1);
editor.navigateLeft(); editor.navigateLeft();
} }
} }
@ -224,12 +224,12 @@ var actions = exports.actions = {
var pos = editor.getCursorPosition(); var pos = editor.getCursorPosition();
pos.column = editor.session.getLine(pos.row).length; pos.column = editor.session.getLine(pos.row).length;
var text = lang.stringRepeat("\n" + defaultReg.text, count || 1); 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); editor.moveCursorTo(pos.row + 1, 0);
} }
else { else {
editor.navigateRight(); editor.navigateRight();
editor.insert(lang.stringRepeat(defaultReg.text, count || 1)); editor.insertText(lang.stringRepeat(defaultReg.text, count || 1));
editor.navigateLeft(); editor.navigateLeft();
} }
editor.setOverwrite(true); editor.setOverwrite(true);
@ -245,11 +245,11 @@ var actions = exports.actions = {
var pos = editor.getCursorPosition(); var pos = editor.getCursorPosition();
pos.column = 0; pos.column = 0;
var text = lang.stringRepeat(defaultReg.text + "\n", count || 1); var text = lang.stringRepeat(defaultReg.text + "\n", count || 1);
editor.session.insert(pos, text); editor.session.insertText(pos, text);
editor.moveCursorToPosition(pos); editor.moveCursorToPosition(pos);
} }
else { else {
editor.insert(lang.stringRepeat(defaultReg.text, count || 1)); editor.insertText(lang.stringRepeat(defaultReg.text, count || 1));
} }
editor.setOverwrite(true); editor.setOverwrite(true);
editor.selection.clearSelection(); editor.selection.clearSelection();

View file

@ -545,7 +545,7 @@ module.exports = {
if (content.length) { if (content.length) {
editor.navigateLineEnd() editor.navigateLineEnd()
editor.insert(content); editor.insertText(content);
util.insertMode(editor); util.insertMode(editor);
} }
} }
@ -562,9 +562,9 @@ module.exports = {
if(row > 0) { if(row > 0) {
editor.navigateUp(); editor.navigateUp();
editor.navigateLineEnd() editor.navigateLineEnd()
editor.insert(content); editor.insertText(content);
} else { } else {
editor.session.insert({row: 0, column: 0}, content); editor.session.insertText({row: 0, column: 0}, content);
editor.navigateUp(); editor.navigateUp();
} }
util.insertMode(editor); util.insertMode(editor);

View file

@ -108,8 +108,8 @@ var Gutter = function(parentEl) {
var len = range.end.row - firstRow; var len = range.end.row - firstRow;
if (len === 0) { if (len === 0) {
// do nothing // do nothing
} else if (delta.action == "removeText" || delta.action == "removeLines") { } else if (delta.action == "delete") {
this.$annotations.splice(firstRow, len + 1, null); this.$annotations.splice(firstRow, len, null);
} else { } else {
var args = new Array(len + 1); var args = new Array(len + 1);
args.unshift(firstRow, 1); args.unshift(firstRow, 1);

View file

@ -123,7 +123,7 @@ function LineWidgets(session) {
if (len === 0) { if (len === 0) {
// return // return
} else if (delta.action == "removeText" || delta.action == "removeLines") { } else if (delta.action == "delete") {
var removed = cells.splice(startRow + 1, len); var removed = cells.splice(startRow + 1, len);
removed.forEach(function(w) { removed.forEach(function(w) {
w && this.removeLineWidget(w); w && this.removeLineWidget(w);

View file

@ -131,7 +131,7 @@ module.exports = {
this.mode.toggleCommentLines("start", session, 0, 2); this.mode.toggleCommentLines("start", session, 0, 2);
assert.equal([" abc", " cde", " fg"].join("\n"), session.toString()); 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); this.mode.toggleCommentLines("start", session, 0, 2);
assert.equal(["// abc", "// cde", "// fg"].join("\n"), session.toString()); assert.equal(["// abc", "// cde", "// fg"].join("\n"), session.toString());
}, },

View file

@ -92,8 +92,8 @@ var Mode = function() {
if (testRemove(line, i)) if (testRemove(line, i))
return; return;
if (!ignoreBlankLines || /\S/.test(line)) { if (!ignoreBlankLines || /\S/.test(line)) {
doc.insertInLine({row: i, column: line.length}, lineCommentEnd); doc.insertText({row: i, column: line.length}, lineCommentEnd);
doc.insertInLine({row: i, column: minIndent}, lineCommentStart); doc.insertText({row: i, column: minIndent}, lineCommentStart);
} }
}; };
@ -138,9 +138,9 @@ var Mode = function() {
var comment = function(line, i) { var comment = function(line, i) {
if (!ignoreBlankLines || /\S/.test(line)) { if (!ignoreBlankLines || /\S/.test(line)) {
if (shouldInsertSpace(line, minIndent, minIndent)) if (shouldInsertSpace(line, minIndent, minIndent))
doc.insertInLine({row: i, column: minIndent}, commentWithSpace); doc.insertText({row: i, column: minIndent}, commentWithSpace);
else else
doc.insertInLine({row: i, column: minIndent}, lineCommentStart); doc.insertText({row: i, column: minIndent}, lineCommentStart);
} }
}; };
var testRemove = function(line, i) { var testRemove = function(line, i) {
@ -244,8 +244,8 @@ var Mode = function() {
} else { } else {
colDiff = comment.start.length colDiff = comment.start.length
startRow = range.start.row; startRow = range.start.row;
session.insert(range.end, comment.end); session.insertText(range.end, comment.end);
session.insert(range.start, comment.start); session.insertText(range.start, comment.start);
} }
// todo: selection should have ended up in the right place automatically! // todo: selection should have ended up in the right place automatically!
if (initialRange.start.row == startRow) if (initialRange.start.row == startRow)

View file

@ -176,7 +176,7 @@ function DragdropHandler(mouseHandler) {
var dropData = dataTransfer.getData('Text'); var dropData = dataTransfer.getData('Text');
range = { range = {
start: dragCursor, start: dragCursor,
end: editor.session.insert(dragCursor, dropData) end: editor.session.insertText(dragCursor, dropData)
}; };
editor.focus(); editor.focus();
dragOperation = null; dragOperation = null;

View file

@ -544,7 +544,7 @@ var Editor = require("./editor").Editor;
this._signal("paste", text); this._signal("paste", text);
if (!this.inMultiSelectMode || this.inVirtualSelectionMode) if (!this.inMultiSelectMode || this.inVirtualSelectionMode)
return this.insert(text); return this.insertText(text);
var lines = text.split(/\r\n|\r|\n/); var lines = text.split(/\r\n|\r|\n/);
var ranges = this.selection.rangeList.ranges; var ranges = this.selection.rangeList.ranges;
@ -557,7 +557,7 @@ var Editor = require("./editor").Editor;
if (!range.isEmpty()) if (!range.isEmpty())
this.session.remove(range); 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 (fr < 0) fr = 0;
if (lr >= max) lr = max - 1; 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); 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) { if (!guessRange) {
range.start.column = 0; range.start.column = 0;
range.end.column = lines[lines.length - 1].length; range.end.column = lines[lines.length - 1].length;
@ -778,7 +778,7 @@ var Editor = require("./editor").Editor;
var l = maxCol - p.column; var l = maxCol - p.column;
var d = spaceOffsets[i] - minSpace; var d = spaceOffsets[i] - minSpace;
if (l > d) if (l > d)
session.insert(p, lang.stringRepeat(" ", l - d)); session.insertText(p, lang.stringRepeat(" ", l - d));
else else
session.remove(new Range(p.row, p.column, p.row, p.column - l + d)); session.remove(new Range(p.row, p.column, p.row, p.column - l + d));

View file

@ -55,7 +55,7 @@ module.exports = {
}, },
"test: find lines matching" : function() { "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'}), var result = occur.matchingLines(editor.session, {needle: 'bc'}),
expected = [{row: 0, content: 'abc'}, {row: 3, content: 'bcxbc'}]; expected = [{row: 0, content: 'abc'}, {row: 3, content: 'bcxbc'}];
assert.deepEqual(result, expected); assert.deepEqual(result, expected);
@ -63,7 +63,7 @@ module.exports = {
"test: display occurrences" : function() { "test: display occurrences" : function() {
var text = 'abc\ndef\nxyz\nbcx\n'; 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'}); occur.displayOccurContent(editor, {needle: 'bc'});
assert.equal(editor.getValue(), 'abc\nbcx'); assert.equal(editor.getValue(), 'abc\nbcx');
occur.displayOriginalContent(editor); occur.displayOriginalContent(editor);
@ -72,7 +72,7 @@ module.exports = {
"test: original position from occur doc" : function() { "test: original position from occur doc" : function() {
var text = 'abc\ndef\nxyz\nbcx\n'; 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'}); occur.displayOccurContent(editor, {needle: 'bc'});
assert.equal(editor.getValue(), 'abc\nbcx'); assert.equal(editor.getValue(), 'abc\nbcx');
var pos = occur.occurToOriginalPosition(editor.session, {row: 1, column: 2}); var pos = occur.occurToOriginalPosition(editor.session, {row: 1, column: 2});
@ -82,7 +82,7 @@ module.exports = {
"test: occur command" : function() { "test: occur command" : function() {
// setup // setup
var text = 'hel\nlo\n\nwo\nrld\n'; 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.commands.addCommand(occurStartCommand);
// run occur for lines including 'o' // run occur for lines including 'o'
@ -106,7 +106,7 @@ module.exports = {
"test: occur navigation" : function() { "test: occur navigation" : function() {
// setup // setup
var text = 'hel\nlo\n\nwo\nrld\n'; 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.commands.addCommand(occurStartCommand);
editor.moveCursorToPosition({row: 1, column: 1}); editor.moveCursorToPosition({row: 1, column: 1});
@ -125,7 +125,7 @@ module.exports = {
"test: recursive occur" : function() { "test: recursive occur" : function() {
// setup // setup
var text = 'x\nabc1\nx\nabc2\n'; 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); editor.commands.addCommand(occurStartCommand);
// orig -> occur1 // orig -> occur1

View file

@ -155,21 +155,21 @@ var PlaceHolder = function(session, length, pos, others, mainClass, othersClass)
if(range.start.row !== this.pos.row) return; if(range.start.row !== this.pos.row) return;
if (this.$updating) return; if (this.$updating) return;
this.$updating = true; 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) { if(range.start.column >= this.pos.column && range.start.column <= this.pos.column + this.length + 1) {
var distanceFromStart = range.start.column - this.pos.column; var distanceFromStart = range.start.column - this.pos.column;
this.length += lengthDiff; this.length += lengthDiff;
if(!this.session.$fromUndo) { if(!this.session.$fromUndo) {
if(delta.action === "insertText") { if(delta.action === "insert") {
for (var i = this.others.length - 1; i >= 0; i--) { for (var i = this.others.length - 1; i >= 0; i--) {
var otherPos = this.others[i]; var otherPos = this.others[i];
var newPos = {row: otherPos.row, column: otherPos.column + distanceFromStart}; var newPos = {row: otherPos.row, column: otherPos.column + distanceFromStart};
if(otherPos.row === range.start.row && range.start.column < otherPos.column) if(otherPos.row === range.start.row && range.start.column < otherPos.column)
newPos.column += lengthDiff; 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--) { for (var i = this.others.length - 1; i >= 0; i--) {
var otherPos = this.others[i]; var otherPos = this.others[i];
var newPos = {row: otherPos.row, column: otherPos.column + distanceFromStart}; 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 // 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() { setTimeout(function() {
this.pos.setPosition(this.pos.row, this.pos.column - lengthDiff); this.pos.setPosition(this.pos.row, this.pos.column - lengthDiff);
for (var i = 0; i < this.others.length; i++) { 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); }.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() { setTimeout(function() {
for (var i = 0; i < this.others.length; i++) { for (var i = 0; i < this.others.length; i++) {
var other = this.others[i]; var other = this.others[i];

View file

@ -53,9 +53,9 @@ module.exports = {
new PlaceHolder(session, 1, {row: 0, column: 4}, [{row: 1, column: 12}, {row: 1, column: 15}]); new PlaceHolder(session, 1, {row: 0, column: 4}, [{row: 1, column: 12}, {row: 1, column: 15}]);
editor.moveCursorTo(0, 5); editor.moveCursorTo(0, 5);
editor.insert('b'); editor.insertText('b');
assert.equal(session.doc.getValue(), "var ab = 10;\nconsole.log(ab, ab);"); 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);"); assert.equal(session.doc.getValue(), "var abcd = 10;\nconsole.log(abcd, abcd);");
editor.remove('left'); editor.remove('left');
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}]); new PlaceHolder(session, 1, {row: 0, column: 4}, [{row: 1, column: 12}, {row: 1, column: 15}]);
editor.moveCursorTo(2, 0); editor.moveCursorTo(2, 0);
editor.insert('b'); editor.insertText('b');
assert.equal(session.doc.getValue(), "var a = 10;\nconsole.log(a, a);\nb"); 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}]); var p = new PlaceHolder(session, 1, {row: 0, column: 4}, [{row: 1, column: 12}, {row: 1, column: 15}]);
editor.moveCursorTo(0, 4); editor.moveCursorTo(0, 4);
editor.insert('$'); editor.insertText('$');
assert.equal(session.doc.getValue(), "var $a = 10;\nconsole.log($a, $a);"); assert.equal(session.doc.getValue(), "var $a = 10;\nconsole.log($a, $a);");
editor.moveCursorTo(0, 4); editor.moveCursorTo(0, 4);
// Have to put this in a setTimeout because the anchor is only fixed later. // Have to put this in a setTimeout because the anchor is only fixed later.
setTimeout(function() { setTimeout(function() {
editor.insert('v'); editor.insertText('v');
assert.equal(session.doc.getValue(), "var v$a = 10;\nconsole.log(v$a, v$a);"); assert.equal(session.doc.getValue(), "var v$a = 10;\nconsole.log(v$a, v$a);");
next(); next();
}, 10); }, 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}]); var p = new PlaceHolder(session, 1, {row: 0, column: 4}, [{row: 1, column: 12}, {row: 1, column: 15}]);
editor.moveCursorTo(0, 5); editor.moveCursorTo(0, 5);
editor.insert('b'); editor.insertText('b');
assert.equal(session.doc.getValue(), "var ab = 10;\nconsole.log(ab, ab);"); assert.equal(session.doc.getValue(), "var ab = 10;\nconsole.log(ab, ab);");
p.detach(); p.detach();
editor.insert('cd'); editor.insertText('cd');
assert.equal(session.doc.getValue(), "var abcd = 10;\nconsole.log(ab, ab);"); 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}]); var p = new PlaceHolder(session, 1, {row: 0, column: 4}, [{row: 1, column: 12}, {row: 1, column: 15}]);
editor.moveCursorTo(0, 5); editor.moveCursorTo(0, 5);
editor.insert('b'); editor.insertText('b');
editor.insert('cd'); editor.insertText('cd');
editor.remove('left'); editor.remove('left');
assert.equal(session.doc.getValue(), "var abc = 10;\nconsole.log(abc, abc);"); assert.equal(session.doc.getValue(), "var abc = 10;\nconsole.log(abc, abc);");
// Wait a little for the changes to enter the undo stack // Wait a little for the changes to enter the undo stack