split off the document from the edit session
This commit is contained in:
parent
ebac43d329
commit
c2f7944a75
4 changed files with 1284 additions and 869 deletions
|
|
@ -40,38 +40,31 @@ define(function(require, exports, module) {
|
||||||
var oop = require("pilot/oop");
|
var oop = require("pilot/oop");
|
||||||
var lang = require("pilot/lang");
|
var lang = require("pilot/lang");
|
||||||
var EventEmitter = require("pilot/event_emitter").EventEmitter;
|
var EventEmitter = require("pilot/event_emitter").EventEmitter;
|
||||||
var Selection = require("ace/selection").Selection;
|
|
||||||
var TextMode = require("ace/mode/text").Mode;
|
|
||||||
var Range = require("ace/range").Range;
|
var Range = require("ace/range").Range;
|
||||||
|
|
||||||
var NO_CHANGE_DELTAS = {};
|
var Document = function(text) {
|
||||||
|
|
||||||
var Document = function(text, mode) {
|
|
||||||
|
|
||||||
this.modified = true;
|
|
||||||
this.$lines = [];
|
this.$lines = [];
|
||||||
this.selection = new Selection(this);
|
|
||||||
this.$breakpoints = [];
|
|
||||||
|
|
||||||
this.listeners = [];
|
|
||||||
if (mode) {
|
|
||||||
this.setMode(mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(text)) {
|
if (Array.isArray(text)) {
|
||||||
this.$insertLines(0, text);
|
this.insertLines(0, text);
|
||||||
} else {
|
} else {
|
||||||
this.$insert({row: 0, column: 0}, text);
|
this.insert({row: 0, column:0}, text);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
oop.implement(this, EventEmitter);
|
oop.implement(this, EventEmitter);
|
||||||
|
|
||||||
this.$undoManager = null;
|
this.setValue = function(text) {
|
||||||
|
this.remove(new Range(0, 0, this.$lines.length, this.$lines[this.$lines.length-1].length));
|
||||||
|
this.insertLines(0, this.$split(text));
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getValue = function() {
|
||||||
|
return this.$lines.join(this.getNewLineCharacter());
|
||||||
|
};
|
||||||
|
|
||||||
// check for IE split bug
|
// check for IE split bug
|
||||||
if ("aaa".split(/a/).length == 0)
|
if ("aaa".split(/a/).length == 0)
|
||||||
this.$split = function(text) {
|
this.$split = function(text) {
|
||||||
|
|
@ -82,125 +75,7 @@ var Document = function(text, mode) {
|
||||||
return text.split(/\r\n|\r|\n/);
|
return text.split(/\r\n|\r|\n/);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setValue = function(text) {
|
|
||||||
var args = [0, this.$lines.length];
|
|
||||||
args.push.apply(args, this.$split(text));
|
|
||||||
this.$lines.splice.apply(this.$lines, args);
|
|
||||||
this.modified = true;
|
|
||||||
this.fireChangeEvent(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.toString = function() {
|
|
||||||
return this.$lines.join(this.$getNewLineCharacter());
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getValue = this.toString;
|
|
||||||
|
|
||||||
this.getSelection = function() {
|
|
||||||
return this.selection;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.fireChangeEvent = function(firstRow, lastRow) {
|
|
||||||
var data = {
|
|
||||||
firstRow: firstRow,
|
|
||||||
lastRow: lastRow
|
|
||||||
};
|
|
||||||
this._dispatchEvent("change", { data: data});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setUndoManager = function(undoManager) {
|
|
||||||
this.$undoManager = undoManager;
|
|
||||||
this.$deltas = [];
|
|
||||||
|
|
||||||
if (this.$informUndoManager) {
|
|
||||||
this.$informUndoManager.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (undoManager) {
|
|
||||||
var self = this;
|
|
||||||
this.$informUndoManager = lang.deferredCall(function() {
|
|
||||||
if (self.$deltas.length > 0)
|
|
||||||
undoManager.execute({
|
|
||||||
action : "aceupdate",
|
|
||||||
args : [self.$deltas, self]
|
|
||||||
});
|
|
||||||
self.$deltas = [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$defaultUndoManager = {
|
|
||||||
undo: function() {},
|
|
||||||
redo: function() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getUndoManager = function() {
|
|
||||||
return this.$undoManager || this.$defaultUndoManager;
|
|
||||||
},
|
|
||||||
|
|
||||||
this.getTabString = function() {
|
|
||||||
if (this.getUseSoftTabs()) {
|
|
||||||
return lang.stringRepeat(" ", this.getTabSize());
|
|
||||||
} else {
|
|
||||||
return "\t";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$useSoftTabs = true;
|
|
||||||
this.setUseSoftTabs = function(useSoftTabs) {
|
|
||||||
if (this.$useSoftTabs === useSoftTabs) return;
|
|
||||||
|
|
||||||
this.$useSoftTabs = useSoftTabs;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getUseSoftTabs = function() {
|
|
||||||
return this.$useSoftTabs;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$tabSize = 4;
|
|
||||||
this.setTabSize = function(tabSize) {
|
|
||||||
if (isNaN(tabSize) || this.$tabSize === tabSize) return;
|
|
||||||
|
|
||||||
this.modified = true;
|
|
||||||
this.$tabSize = tabSize;
|
|
||||||
this._dispatchEvent("changeTabSize");
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getTabSize = function() {
|
|
||||||
return this.$tabSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.isTabStop = function(position) {
|
|
||||||
return this.$useSoftTabs && (position.column % this.$tabSize == 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getBreakpoints = function() {
|
|
||||||
return this.$breakpoints;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setBreakpoints = function(rows) {
|
|
||||||
this.$breakpoints = [];
|
|
||||||
for (var i=0; i<rows.length; i++) {
|
|
||||||
this.$breakpoints[rows[i]] = true;
|
|
||||||
}
|
|
||||||
this._dispatchEvent("changeBreakpoint", {});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.clearBreakpoints = function() {
|
|
||||||
this.$breakpoints = [];
|
|
||||||
this._dispatchEvent("changeBreakpoint", {});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setBreakpoint = function(row) {
|
|
||||||
this.$breakpoints[row] = true;
|
|
||||||
this._dispatchEvent("changeBreakpoint", {});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.clearBreakpoint = function(row) {
|
|
||||||
delete this.$breakpoints[row];
|
|
||||||
this._dispatchEvent("changeBreakpoint", {});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$detectNewLine = function(text) {
|
this.$detectNewLine = function(text) {
|
||||||
var match = text.match(/^.*?(\r?\n)/m);
|
var match = text.match(/^.*?(\r?\n)/m);
|
||||||
if (match) {
|
if (match) {
|
||||||
|
|
@ -210,41 +85,7 @@ var Document = function(text, mode) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.tokenRe = /^[\w\d]+/g;
|
this.getNewLineCharacter = function() {
|
||||||
this.nonTokenRe = /^[^\w\d]+/g;
|
|
||||||
|
|
||||||
this.getWordRange = function(row, column) {
|
|
||||||
var line = this.getLine(row);
|
|
||||||
|
|
||||||
var inToken = false;
|
|
||||||
if (column > 0) {
|
|
||||||
inToken = !!line.charAt(column - 1).match(this.tokenRe);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!inToken) {
|
|
||||||
inToken = !!line.charAt(column).match(this.tokenRe);
|
|
||||||
}
|
|
||||||
|
|
||||||
var re = inToken ? this.tokenRe : this.nonTokenRe;
|
|
||||||
|
|
||||||
var start = column;
|
|
||||||
if (start > 0) {
|
|
||||||
do {
|
|
||||||
start--;
|
|
||||||
}
|
|
||||||
while (start >= 0 && line.charAt(start).match(re));
|
|
||||||
start++;
|
|
||||||
}
|
|
||||||
|
|
||||||
var end = column;
|
|
||||||
while (end < line.length && line.charAt(end).match(re)) {
|
|
||||||
end++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Range(row, start, row, end);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$getNewLineCharacter = function() {
|
|
||||||
switch (this.$newLineMode) {
|
switch (this.$newLineMode) {
|
||||||
case "windows":
|
case "windows":
|
||||||
return "\r\n";
|
return "\r\n";
|
||||||
|
|
@ -268,66 +109,9 @@ var Document = function(text, mode) {
|
||||||
this.getNewLineMode = function() {
|
this.getNewLineMode = function() {
|
||||||
return this.$newLineMode;
|
return this.$newLineMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$mode = null;
|
this.isNewLine = function(text) {
|
||||||
this.setMode = function(mode) {
|
return (text == "\r\n" || text == "\r" || text == "\n");
|
||||||
if (this.$mode === mode) return;
|
|
||||||
|
|
||||||
this.$mode = mode;
|
|
||||||
this._dispatchEvent("changeMode");
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getMode = function() {
|
|
||||||
if (!this.$mode) {
|
|
||||||
this.$mode = new TextMode();
|
|
||||||
}
|
|
||||||
return this.$mode;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$scrollTop = 0;
|
|
||||||
this.setScrollTopRow = function(scrollTopRow) {
|
|
||||||
if (this.$scrollTop === scrollTopRow) return;
|
|
||||||
|
|
||||||
this.$scrollTop = scrollTopRow;
|
|
||||||
this._dispatchEvent("changeScrollTop");
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getScrollTopRow = function() {
|
|
||||||
return this.$scrollTop;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getWidth = function() {
|
|
||||||
this.$computeWidth();
|
|
||||||
return this.width;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getScreenWidth = function() {
|
|
||||||
this.$computeWidth();
|
|
||||||
return this.screenWidth;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$computeWidth = function() {
|
|
||||||
if (this.modified) {
|
|
||||||
this.modified = false;
|
|
||||||
|
|
||||||
var lines = this.$lines;
|
|
||||||
var longestLine = 0;
|
|
||||||
var longestScreenLine = 0;
|
|
||||||
var tabSize = this.getTabSize();
|
|
||||||
|
|
||||||
for ( var i = 0; i < lines.length; i++) {
|
|
||||||
var len = lines[i].length;
|
|
||||||
longestLine = Math.max(longestLine, len);
|
|
||||||
|
|
||||||
lines[i].replace("\t", function(m) {
|
|
||||||
len += tabSize-1;
|
|
||||||
return m;
|
|
||||||
});
|
|
||||||
longestScreenLine = Math.max(longestScreenLine, len);
|
|
||||||
}
|
|
||||||
this.width = longestLine;
|
|
||||||
this.screenWidth = longestScreenLine;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -337,14 +121,6 @@ var Document = function(text, mode) {
|
||||||
return this.$lines[row] || "";
|
return this.$lines[row] || "";
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a line as it is displayed on screen. Tabs are replaced by spaces.
|
|
||||||
*/
|
|
||||||
this.getDisplayLine = function(row) {
|
|
||||||
var tab = new Array(this.getTabSize()+1).join(" ");
|
|
||||||
return this.$lines[row].replace(/\t/g, tab);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getLines = function(firstRow, lastRow) {
|
this.getLines = function(firstRow, lastRow) {
|
||||||
return this.$lines.slice(firstRow, lastRow+1);
|
return this.$lines.slice(firstRow, lastRow+1);
|
||||||
};
|
};
|
||||||
|
|
@ -363,482 +139,184 @@ var Document = function(text, mode) {
|
||||||
lines.push(this.$lines[range.start.row].substring(range.start.column));
|
lines.push(this.$lines[range.start.row].substring(range.start.column));
|
||||||
lines.push.apply(lines, this.getLines(range.start.row+1, range.end.row-1));
|
lines.push.apply(lines, this.getLines(range.start.row+1, range.end.row-1));
|
||||||
lines.push(this.$lines[range.end.row].substring(0, range.end.column));
|
lines.push(this.$lines[range.end.row].substring(0, range.end.column));
|
||||||
return lines.join(this.$getNewLineCharacter());
|
return lines.join(this.getNewLineCharacter());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.findMatchingBracket = function(position) {
|
this.insert = function(position, text) {
|
||||||
if (position.column == 0) return null;
|
|
||||||
|
|
||||||
var charBeforeCursor = this.getLine(position.row).charAt(position.column-1);
|
|
||||||
if (charBeforeCursor == "") return null;
|
|
||||||
|
|
||||||
var match = charBeforeCursor.match(/([\(\[\{])|([\)\]\}])/);
|
|
||||||
if (!match) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match[1]) {
|
|
||||||
return this.$findClosingBracket(match[1], position);
|
|
||||||
} else {
|
|
||||||
return this.$findOpeningBracket(match[2], position);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$brackets = {
|
|
||||||
")": "(",
|
|
||||||
"(": ")",
|
|
||||||
"]": "[",
|
|
||||||
"[": "]",
|
|
||||||
"{": "}",
|
|
||||||
"}": "{"
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$findOpeningBracket = function(bracket, position) {
|
|
||||||
var openBracket = this.$brackets[bracket];
|
|
||||||
|
|
||||||
var column = position.column - 2;
|
|
||||||
var row = position.row;
|
|
||||||
var depth = 1;
|
|
||||||
|
|
||||||
var line = this.getLine(row);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
while(column >= 0) {
|
|
||||||
var ch = line.charAt(column);
|
|
||||||
if (ch == openBracket) {
|
|
||||||
depth -= 1;
|
|
||||||
if (depth == 0) {
|
|
||||||
return {row: row, column: column};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (ch == bracket) {
|
|
||||||
depth +=1;
|
|
||||||
}
|
|
||||||
column -= 1;
|
|
||||||
}
|
|
||||||
row -=1;
|
|
||||||
if (row < 0) break;
|
|
||||||
|
|
||||||
var line = this.getLine(row);
|
|
||||||
var column = line.length-1;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$findClosingBracket = function(bracket, position) {
|
|
||||||
var closingBracket = this.$brackets[bracket];
|
|
||||||
|
|
||||||
var column = position.column;
|
|
||||||
var row = position.row;
|
|
||||||
var depth = 1;
|
|
||||||
|
|
||||||
var line = this.getLine(row);
|
|
||||||
var lineCount = this.getLength();
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
while(column < line.length) {
|
|
||||||
var ch = line.charAt(column);
|
|
||||||
if (ch == closingBracket) {
|
|
||||||
depth -= 1;
|
|
||||||
if (depth == 0) {
|
|
||||||
return {row: row, column: column};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (ch == bracket) {
|
|
||||||
depth +=1;
|
|
||||||
}
|
|
||||||
column += 1;
|
|
||||||
}
|
|
||||||
row +=1;
|
|
||||||
if (row >= lineCount) break;
|
|
||||||
|
|
||||||
var line = this.getLine(row);
|
|
||||||
var column = 0;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.insert = function(position, text, fromUndo) {
|
|
||||||
var end = this.$insert(position, text, fromUndo);
|
|
||||||
this.fireChangeEvent(position.row, position.row == end.row ? position.row
|
|
||||||
: undefined);
|
|
||||||
return end;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param rows Array[Integer] sorted list of rows
|
|
||||||
*/
|
|
||||||
this.multiRowInsert = function(rows, column, text) {
|
|
||||||
var lines = this.$lines;
|
|
||||||
|
|
||||||
for (var i=rows.length-1; i>=0; i--) {
|
|
||||||
var row = rows[i];
|
|
||||||
if (row >= lines.length)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var diff = column - lines[row].length;
|
|
||||||
if ( diff > 0) {
|
|
||||||
var padded = lang.stringRepeat(" ", diff) + text;
|
|
||||||
var offset = -diff;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
padded = text;
|
|
||||||
offset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var end = this.$insert({row: row, column: column+offset}, padded, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (end) {
|
|
||||||
this.fireChangeEvent(rows[0], rows[rows.length-1] + end.row - rows[0]);
|
|
||||||
return {
|
|
||||||
rows: end.row - rows[0],
|
|
||||||
columns: end.column - column
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return {
|
|
||||||
rows: 0,
|
|
||||||
columns: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$insertLines = function(row, lines, fromUndo) {
|
|
||||||
if (lines.length == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var args = [row, 0];
|
|
||||||
args.push.apply(args, lines);
|
|
||||||
this.$lines.splice.apply(this.$lines, args);
|
|
||||||
|
|
||||||
var nl = this.$getNewLineCharacter();
|
|
||||||
var delta = {
|
|
||||||
action: "insertText",
|
|
||||||
range: new Range(row, 0, row + lines.length, 0),
|
|
||||||
text: lines.join(nl) + nl
|
|
||||||
};
|
|
||||||
if (!fromUndo && this.$undoManager) {
|
|
||||||
this.$deltas.push(delta);
|
|
||||||
this.$informUndoManager.schedule();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fromUndo !== NO_CHANGE_DELTAS) {
|
|
||||||
this._dispatchEvent("changeDelta", { data: delta });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
this.$insert = function(position, text, fromUndo) {
|
|
||||||
if (text.length == 0)
|
if (text.length == 0)
|
||||||
return position;
|
return position;
|
||||||
|
|
||||||
this.modified = true;
|
|
||||||
if (this.$lines.length <= 1) {
|
if (this.$lines.length <= 1) {
|
||||||
this.$detectNewLine(text);
|
this.$detectNewLine(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
var newLines = this.$split(text);
|
var newLines = this.$split(text);
|
||||||
|
|
||||||
if (this.$isNewLine(text)) {
|
if (this.isNewLine(text)) {
|
||||||
var line = this.$lines[position.row] || "";
|
var end = this.insertNewLine(position);
|
||||||
this.$lines[position.row] = line.substring(0, position.column);
|
|
||||||
this.$lines.splice(position.row + 1, 0, line.substring(position.column));
|
|
||||||
|
|
||||||
var end = {
|
|
||||||
row : position.row + 1,
|
|
||||||
column : 0
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
else if (newLines.length == 1) {
|
else if (newLines.length == 1) {
|
||||||
var line = this.$lines[position.row] || "";
|
var end = this.insertInLine(position, text);
|
||||||
this.$lines[position.row] = line.substring(0, position.column) + text
|
|
||||||
+ line.substring(position.column);
|
|
||||||
|
|
||||||
var end = {
|
|
||||||
row : position.row,
|
|
||||||
column : position.column + text.length
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var line = this.$lines[position.row] || "";
|
this.insertInLine(position, newLines[0]);
|
||||||
var firstLine = line.substring(0, position.column) + newLines[0];
|
if (newLines.length > 2)
|
||||||
var lastLine = newLines[newLines.length - 1] + line.substring(position.column);
|
this.insertLines(position.row+1, newLines.slice(1, newLines.length-1));
|
||||||
|
|
||||||
this.$lines[position.row] = firstLine;
|
var end = this.insertInLine({row: position.row + newLines.length - 1, column: 0}, newLines[newLines.length-1]);
|
||||||
this.$insertLines(position.row + 1, [lastLine], NO_CHANGE_DELTAS);
|
|
||||||
|
|
||||||
if (newLines.length > 2) {
|
|
||||||
this.$insertLines(position.row + 1, newLines.slice(1, -1), NO_CHANGE_DELTAS);
|
|
||||||
}
|
|
||||||
|
|
||||||
var end = {
|
|
||||||
row : position.row + newLines.length - 1,
|
|
||||||
column : newLines[newLines.length - 1].length
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return end;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.insertLines = function(row, lines) {
|
||||||
|
if (lines.length == 0)
|
||||||
|
return {row: row, column: 0};
|
||||||
|
|
||||||
|
var args = [row, 0];
|
||||||
|
args.push.apply(args, lines);
|
||||||
|
this.$lines.splice.apply(this.$lines, args);
|
||||||
|
|
||||||
|
var range = new Range(row, 0, row + lines.length, 0);
|
||||||
|
var delta = {
|
||||||
|
action: "insertLines",
|
||||||
|
nl: this.getNewLineCharacter(),
|
||||||
|
range: range,
|
||||||
|
lines: lines
|
||||||
|
};
|
||||||
|
this._dispatchEvent("changeDelta", { data: delta });
|
||||||
|
return range.end;
|
||||||
|
},
|
||||||
|
|
||||||
|
this.insertNewLine = function(position) {
|
||||||
|
console.log(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._dispatchEvent("changeDelta", { data: delta });
|
||||||
|
|
||||||
|
return end;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.insertInLine = function(position, text) {
|
||||||
|
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 = {
|
var delta = {
|
||||||
action: "insertText",
|
action: "insertText",
|
||||||
range: Range.fromPoints(position, end),
|
range: Range.fromPoints(position, end),
|
||||||
text: text
|
text: text
|
||||||
};
|
};
|
||||||
if (!fromUndo && this.$undoManager) {
|
|
||||||
this.$deltas.push(delta);
|
|
||||||
this.$informUndoManager.schedule();
|
|
||||||
}
|
|
||||||
this._dispatchEvent("changeDelta", { data: delta });
|
this._dispatchEvent("changeDelta", { data: delta });
|
||||||
|
|
||||||
return end;
|
return end;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$isNewLine = function(text) {
|
this.remove = function(range) {
|
||||||
return (text == "\r\n" || text == "\r" || text == "\n");
|
|
||||||
};
|
|
||||||
|
|
||||||
this.remove = function(range, fromUndo) {
|
|
||||||
if (range.isEmpty())
|
if (range.isEmpty())
|
||||||
return range.start;
|
return range.start;
|
||||||
|
|
||||||
this.$remove(range, fromUndo);
|
if (range.isMultiLine()) {
|
||||||
|
var firstRow = range.start.row;
|
||||||
this.fireChangeEvent(range.start.row, range.isMultiLine() ? undefined : range.start.row);
|
var lastRow = range.end.row;
|
||||||
|
|
||||||
return range.start;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.multiRowRemove = function(rows, range) {
|
|
||||||
if (range.start.row !== rows[0])
|
|
||||||
throw new TypeError("range must start in the first row!");
|
|
||||||
|
|
||||||
var height = range.end.row - rows[0];
|
// TODO removeInLine can be optimized away!
|
||||||
for (var i=rows.length-1; i>=0; i--) {
|
this.removeInLine(lastRow, 0, range.end.column);
|
||||||
var row = rows[i];
|
if (lastRow - firstRow >= 2)
|
||||||
if (row >= this.$lines.length)
|
this.removeLines(firstRow + 1, lastRow - 1);
|
||||||
continue;
|
this.removeInLine(firstRow, range.start.column, this.$lines[firstRow].length);
|
||||||
|
this.removeNewLine(range.start.row);
|
||||||
var end = this.$remove(new Range(row, range.start.column, row+height, range.end.column), false);
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
if (end) {
|
this.removeInLine(range)
|
||||||
if (height < 0)
|
|
||||||
this.fireChangeEvent(rows[0]+height, undefined);
|
|
||||||
else
|
|
||||||
this.fireChangeEvent(rows[0], height == 0 ? rows[rows.length-1] : undefined);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$remove = function(range, fromUndo) {
|
this.removeInLine = function(row, startColumn, endColumn) {
|
||||||
if (range.isEmpty())
|
var range = new Range(row, startColumn, row, endColumn);
|
||||||
return;
|
var line = this.$lines[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 = {
|
var delta = {
|
||||||
action: "removeText",
|
action: "removeText",
|
||||||
range: range.clone(),
|
range: range,
|
||||||
text: this.getTextRange(range)
|
text: removed
|
||||||
};
|
};
|
||||||
if (!fromUndo && this.$undoManager) {
|
|
||||||
this.$deltas.push(delta);
|
|
||||||
this.$informUndoManager.schedule();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.modified = true;
|
|
||||||
|
|
||||||
var firstRow = range.start.row;
|
|
||||||
var lastRow = range.end.row;
|
|
||||||
|
|
||||||
var row = this.getLine(firstRow).substring(0, range.start.column)
|
|
||||||
+ this.getLine(lastRow).substring(range.end.column);
|
|
||||||
|
|
||||||
if (row != "")
|
|
||||||
this.$lines.splice(firstRow, lastRow - firstRow + 1, row);
|
|
||||||
else
|
|
||||||
this.$lines.splice(firstRow, lastRow - firstRow + 1, "");
|
|
||||||
|
|
||||||
this._dispatchEvent("changeDelta", { data: delta });
|
|
||||||
return range.start;
|
return range.start;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.undoChanges = function(deltas) {
|
this.removeLines = function(firstRow, lastRow) {
|
||||||
this.selection.clearSelection();
|
var range = new Range(firstRow, 0, lastRow, this.$lines[lastRow].length);
|
||||||
for (var i=deltas.length-1; i>=0; i--) {
|
var removed = this.$lines.splice(firstRow, lastRow - firstRow + 1);
|
||||||
var delta = deltas[i];
|
|
||||||
if (delta.action == "insertText") {
|
var delta = {
|
||||||
this.remove(delta.range, true);
|
action: "removeLines",
|
||||||
this.selection.moveCursorToPosition(delta.range.start);
|
range: range,
|
||||||
} else {
|
nl: this.getNewLineCharacter(),
|
||||||
this.insert(delta.range.start, delta.text, true);
|
lines: removed
|
||||||
this.selection.clearSelection();
|
};
|
||||||
}
|
this._dispatchEvent("changeDelta", { data: delta });
|
||||||
}
|
};
|
||||||
},
|
|
||||||
|
this.removeNewLine = function(row) {
|
||||||
this.redoChanges = function(deltas) {
|
var firstLine = this.getLine(row);
|
||||||
this.selection.clearSelection();
|
var secondLine = this.getLine(row+1);
|
||||||
for (var i=0; i<deltas.length; i++) {
|
|
||||||
var delta = deltas[i];
|
var range = new Range(row, firstLine.length, row+1, 0);
|
||||||
if (delta.action == "insertText") {
|
var line = firstLine + secondLine;
|
||||||
this.insert(delta.range.start, delta.text, true);
|
|
||||||
this.selection.setSelectionRange(delta.range);
|
this.$lines.splice(row, 2, line);
|
||||||
} else {
|
|
||||||
this.remove(delta.range, true);
|
var delta = {
|
||||||
this.selection.moveCursorToPosition(delta.range.start);
|
action: "removeText",
|
||||||
}
|
range: range,
|
||||||
}
|
text: this.getNewLineCharacter()
|
||||||
},
|
};
|
||||||
|
this._dispatchEvent("changeDelta", { data: delta });
|
||||||
|
};
|
||||||
|
|
||||||
this.replace = function(range, text) {
|
this.replace = function(range, text) {
|
||||||
// Shortcut: Nothing to replace in this case.
|
// Shortcut: Nothing to replace in this case.
|
||||||
if (text.length == 0 && range.isEmpty()) {
|
if (text.length == 0 && range.isEmpty())
|
||||||
return range.start;
|
return range.start;
|
||||||
} else
|
|
||||||
// Shortcut: If the text we want to insert is the same as it is already
|
// Shortcut: If the text we want to insert is the same as it is already
|
||||||
// in the document, we don't have to replace anything.
|
// in the document, we don't have to replace anything.
|
||||||
if (text == this.getTextRange(range)) {
|
if (text == this.getTextRange(range)) {
|
||||||
return range.end;
|
return range.end;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$remove(range);
|
this.remove(range);
|
||||||
if (text) {
|
if (text) {
|
||||||
var end = this.$insert(range.start, text);
|
var end = this.insert(range.start, text);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
end = range.start;
|
end = range.start;
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastRemoved = range.end.column == 0 ? range.end.column - 1
|
|
||||||
: range.end.column;
|
|
||||||
this.fireChangeEvent(range.start.row, lastRemoved == end.row ? lastRemoved
|
|
||||||
: undefined);
|
|
||||||
|
|
||||||
return end;
|
return end;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.indentRows = function(startRow, endRow, indentString) {
|
|
||||||
indentString = indentString.replace("\t", this.getTabString());
|
|
||||||
for (var row=startRow; row<=endRow; row++) {
|
|
||||||
this.$insert({row: row, column:0}, indentString);
|
|
||||||
}
|
|
||||||
this.fireChangeEvent(startRow, endRow);
|
|
||||||
return indentString.length;
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
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;
|
|
||||||
} else {
|
|
||||||
deleteRange.start.column = 0;
|
|
||||||
deleteRange.end.column = j;
|
|
||||||
}
|
|
||||||
if (i == range.start.row)
|
|
||||||
range.start.column -= deleteRange.end.column - deleteRange.start.column;
|
|
||||||
if (i == range.end.row)
|
|
||||||
range.end.column -= deleteRange.end.column - deleteRange.start.column;
|
|
||||||
this.$remove(deleteRange);
|
|
||||||
}
|
|
||||||
this.fireChangeEvent(range.start.row, range.end.row);
|
|
||||||
return range;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.moveLinesUp = function(firstRow, lastRow) {
|
|
||||||
if (firstRow <= 0) return 0;
|
|
||||||
|
|
||||||
var removed = this.$lines.slice(firstRow, lastRow + 1);
|
|
||||||
this.$remove(new Range(firstRow-1, this.$lines[firstRow-1].length, lastRow, this.$lines[lastRow].length));
|
|
||||||
this.$insertLines(firstRow - 1, removed);
|
|
||||||
|
|
||||||
this.fireChangeEvent(firstRow - 1, lastRow);
|
|
||||||
return -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.moveLinesDown = function(firstRow, lastRow) {
|
|
||||||
if (lastRow >= this.$lines.length-1) return 0;
|
|
||||||
|
|
||||||
var removed = this.$lines.slice(firstRow, lastRow + 1);
|
|
||||||
this.$remove(new Range(firstRow, 0, lastRow + 1, 0));
|
|
||||||
this.$insertLines(firstRow+1, removed);
|
|
||||||
|
|
||||||
this.fireChangeEvent(firstRow, lastRow + 1);
|
|
||||||
return 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.duplicateLines = function(firstRow, lastRow) {
|
|
||||||
var firstRow = this.$clipRowToDocument(firstRow);
|
|
||||||
var lastRow = this.$clipRowToDocument(lastRow);
|
|
||||||
|
|
||||||
var lines = this.getLines(firstRow, lastRow);
|
|
||||||
this.$insertLines(firstRow, lines);
|
|
||||||
|
|
||||||
var addedRows = lastRow - firstRow + 1;
|
|
||||||
this.fireChangeEvent(firstRow);
|
|
||||||
|
|
||||||
return addedRows;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$clipRowToDocument = function(row) {
|
|
||||||
return Math.max(0, Math.min(row, this.$lines.length-1));
|
|
||||||
};
|
|
||||||
|
|
||||||
this.documentToScreenColumn = function(row, docColumn) {
|
|
||||||
var tabSize = this.getTabSize();
|
|
||||||
|
|
||||||
var screenColumn = 0;
|
|
||||||
var remaining = docColumn;
|
|
||||||
|
|
||||||
var line = this.getLine(row).split("\t");
|
|
||||||
for (var i=0; i<line.length; i++) {
|
|
||||||
var len = line[i].length;
|
|
||||||
if (remaining > len) {
|
|
||||||
remaining -= (len + 1);
|
|
||||||
screenColumn += len + tabSize;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
screenColumn += remaining;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return screenColumn;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.screenToDocumentColumn = function(row, screenColumn) {
|
|
||||||
var tabSize = this.getTabSize();
|
|
||||||
|
|
||||||
var docColumn = 0;
|
|
||||||
var remaining = screenColumn;
|
|
||||||
|
|
||||||
var line = this.getLine(row).split("\t");
|
|
||||||
for (var i=0; i<line.length; i++) {
|
|
||||||
var len = line[i].length;
|
|
||||||
if (remaining >= len + tabSize) {
|
|
||||||
remaining -= (len + tabSize);
|
|
||||||
docColumn += (len + 1);
|
|
||||||
}
|
|
||||||
else if (remaining > len){
|
|
||||||
docColumn += len;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
docColumn += remaining;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return docColumn;
|
|
||||||
};
|
|
||||||
|
|
||||||
}).call(Document.prototype);
|
}).call(Document.prototype);
|
||||||
|
|
||||||
exports.Document = Document;
|
exports.Document = Document;
|
||||||
|
|
|
||||||
845
lib/ace/edit_session.js
Normal file
845
lib/ace/edit_session.js
Normal file
|
|
@ -0,0 +1,845 @@
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Ajax.org Code Editor (ACE).
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* Ajax.org B.V.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Fabian Jakobs <fabian AT ajax DOT org>
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
define(function(require, exports, module) {
|
||||||
|
|
||||||
|
var oop = require("pilot/oop");
|
||||||
|
var lang = require("pilot/lang");
|
||||||
|
var EventEmitter = require("pilot/event_emitter").EventEmitter;
|
||||||
|
var Selection = require("ace/selection").Selection;
|
||||||
|
var TextMode = require("ace/mode/text").Mode;
|
||||||
|
var Range = require("ace/range").Range;
|
||||||
|
|
||||||
|
var NO_CHANGE_DELTAS = {};
|
||||||
|
|
||||||
|
var EditSession = function(text, mode) {
|
||||||
|
|
||||||
|
this.modified = true;
|
||||||
|
this.$lines = [];
|
||||||
|
this.selection = new Selection(this);
|
||||||
|
this.$breakpoints = [];
|
||||||
|
|
||||||
|
this.listeners = [];
|
||||||
|
if (mode) {
|
||||||
|
this.setMode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(text)) {
|
||||||
|
this.$insertLines(0, text);
|
||||||
|
} else {
|
||||||
|
this.$insert({row: 0, column: 0}, text);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
oop.implement(this, EventEmitter);
|
||||||
|
|
||||||
|
this.$undoManager = null;
|
||||||
|
|
||||||
|
// check for IE split bug
|
||||||
|
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/);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setValue = function(text) {
|
||||||
|
var args = [0, this.$lines.length];
|
||||||
|
args.push.apply(args, this.$split(text));
|
||||||
|
this.$lines.splice.apply(this.$lines, args);
|
||||||
|
this.modified = true;
|
||||||
|
this.fireChangeEvent(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.toString = function() {
|
||||||
|
return this.$lines.join(this.$getNewLineCharacter());
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getValue = this.toString;
|
||||||
|
|
||||||
|
this.getSelection = function() {
|
||||||
|
return this.selection;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.fireChangeEvent = function(firstRow, lastRow) {
|
||||||
|
var data = {
|
||||||
|
firstRow: firstRow,
|
||||||
|
lastRow: lastRow
|
||||||
|
};
|
||||||
|
this._dispatchEvent("change", { data: data});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setUndoManager = function(undoManager) {
|
||||||
|
this.$undoManager = undoManager;
|
||||||
|
this.$deltas = [];
|
||||||
|
|
||||||
|
if (this.$informUndoManager) {
|
||||||
|
this.$informUndoManager.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (undoManager) {
|
||||||
|
var self = this;
|
||||||
|
this.$informUndoManager = lang.deferredCall(function() {
|
||||||
|
if (self.$deltas.length > 0)
|
||||||
|
undoManager.execute({
|
||||||
|
action : "aceupdate",
|
||||||
|
args : [self.$deltas, self]
|
||||||
|
});
|
||||||
|
self.$deltas = [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$defaultUndoManager = {
|
||||||
|
undo: function() {},
|
||||||
|
redo: function() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getUndoManager = function() {
|
||||||
|
return this.$undoManager || this.$defaultUndoManager;
|
||||||
|
},
|
||||||
|
|
||||||
|
this.getTabString = function() {
|
||||||
|
if (this.getUseSoftTabs()) {
|
||||||
|
return lang.stringRepeat(" ", this.getTabSize());
|
||||||
|
} else {
|
||||||
|
return "\t";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$useSoftTabs = true;
|
||||||
|
this.setUseSoftTabs = function(useSoftTabs) {
|
||||||
|
if (this.$useSoftTabs === useSoftTabs) return;
|
||||||
|
|
||||||
|
this.$useSoftTabs = useSoftTabs;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getUseSoftTabs = function() {
|
||||||
|
return this.$useSoftTabs;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$tabSize = 4;
|
||||||
|
this.setTabSize = function(tabSize) {
|
||||||
|
if (isNaN(tabSize) || this.$tabSize === tabSize) return;
|
||||||
|
|
||||||
|
this.modified = true;
|
||||||
|
this.$tabSize = tabSize;
|
||||||
|
this._dispatchEvent("changeTabSize");
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getTabSize = function() {
|
||||||
|
return this.$tabSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.isTabStop = function(position) {
|
||||||
|
return this.$useSoftTabs && (position.column % this.$tabSize == 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getBreakpoints = function() {
|
||||||
|
return this.$breakpoints;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setBreakpoints = function(rows) {
|
||||||
|
this.$breakpoints = [];
|
||||||
|
for (var i=0; i<rows.length; i++) {
|
||||||
|
this.$breakpoints[rows[i]] = true;
|
||||||
|
}
|
||||||
|
this._dispatchEvent("changeBreakpoint", {});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.clearBreakpoints = function() {
|
||||||
|
this.$breakpoints = [];
|
||||||
|
this._dispatchEvent("changeBreakpoint", {});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setBreakpoint = function(row) {
|
||||||
|
this.$breakpoints[row] = true;
|
||||||
|
this._dispatchEvent("changeBreakpoint", {});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.clearBreakpoint = function(row) {
|
||||||
|
delete this.$breakpoints[row];
|
||||||
|
this._dispatchEvent("changeBreakpoint", {});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$detectNewLine = function(text) {
|
||||||
|
var match = text.match(/^.*?(\r?\n)/m);
|
||||||
|
if (match) {
|
||||||
|
this.$autoNewLine = match[1];
|
||||||
|
} else {
|
||||||
|
this.$autoNewLine = "\n";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.tokenRe = /^[\w\d]+/g;
|
||||||
|
this.nonTokenRe = /^[^\w\d]+/g;
|
||||||
|
|
||||||
|
this.getWordRange = function(row, column) {
|
||||||
|
var line = this.getLine(row);
|
||||||
|
|
||||||
|
var inToken = false;
|
||||||
|
if (column > 0) {
|
||||||
|
inToken = !!line.charAt(column - 1).match(this.tokenRe);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inToken) {
|
||||||
|
inToken = !!line.charAt(column).match(this.tokenRe);
|
||||||
|
}
|
||||||
|
|
||||||
|
var re = inToken ? this.tokenRe : this.nonTokenRe;
|
||||||
|
|
||||||
|
var start = column;
|
||||||
|
if (start > 0) {
|
||||||
|
do {
|
||||||
|
start--;
|
||||||
|
}
|
||||||
|
while (start >= 0 && line.charAt(start).match(re));
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var end = column;
|
||||||
|
while (end < line.length && line.charAt(end).match(re)) {
|
||||||
|
end++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Range(row, start, row, end);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$getNewLineCharacter = function() {
|
||||||
|
switch (this.$newLineMode) {
|
||||||
|
case "windows":
|
||||||
|
return "\r\n";
|
||||||
|
|
||||||
|
case "unix":
|
||||||
|
return "\n";
|
||||||
|
|
||||||
|
case "auto":
|
||||||
|
return this.$autoNewLine;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
this.$autoNewLine = "\n";
|
||||||
|
this.$newLineMode = "auto";
|
||||||
|
this.setNewLineMode = function(newLineMode) {
|
||||||
|
if (this.$newLineMode === newLineMode) return;
|
||||||
|
|
||||||
|
this.$newLineMode = newLineMode;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getNewLineMode = function() {
|
||||||
|
return this.$newLineMode;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$mode = null;
|
||||||
|
this.setMode = function(mode) {
|
||||||
|
if (this.$mode === mode) return;
|
||||||
|
|
||||||
|
this.$mode = mode;
|
||||||
|
this._dispatchEvent("changeMode");
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getMode = function() {
|
||||||
|
if (!this.$mode) {
|
||||||
|
this.$mode = new TextMode();
|
||||||
|
}
|
||||||
|
return this.$mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$scrollTop = 0;
|
||||||
|
this.setScrollTopRow = function(scrollTopRow) {
|
||||||
|
if (this.$scrollTop === scrollTopRow) return;
|
||||||
|
|
||||||
|
this.$scrollTop = scrollTopRow;
|
||||||
|
this._dispatchEvent("changeScrollTop");
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getScrollTopRow = function() {
|
||||||
|
return this.$scrollTop;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getWidth = function() {
|
||||||
|
this.$computeWidth();
|
||||||
|
return this.width;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getScreenWidth = function() {
|
||||||
|
this.$computeWidth();
|
||||||
|
return this.screenWidth;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$computeWidth = function() {
|
||||||
|
if (this.modified) {
|
||||||
|
this.modified = false;
|
||||||
|
|
||||||
|
var lines = this.$lines;
|
||||||
|
var longestLine = 0;
|
||||||
|
var longestScreenLine = 0;
|
||||||
|
var tabSize = this.getTabSize();
|
||||||
|
|
||||||
|
for ( var i = 0; i < lines.length; i++) {
|
||||||
|
var len = lines[i].length;
|
||||||
|
longestLine = Math.max(longestLine, len);
|
||||||
|
|
||||||
|
lines[i].replace("\t", function(m) {
|
||||||
|
len += tabSize-1;
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
longestScreenLine = Math.max(longestScreenLine, len);
|
||||||
|
}
|
||||||
|
this.width = longestLine;
|
||||||
|
this.screenWidth = longestScreenLine;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a verbatim copy of the given line as it is in the document
|
||||||
|
*/
|
||||||
|
this.getLine = function(row) {
|
||||||
|
return this.$lines[row] || "";
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a line as it is displayed on screen. Tabs are replaced by spaces.
|
||||||
|
*/
|
||||||
|
this.getDisplayLine = function(row) {
|
||||||
|
var tab = new Array(this.getTabSize()+1).join(" ");
|
||||||
|
return this.$lines[row].replace(/\t/g, tab);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getLines = function(firstRow, lastRow) {
|
||||||
|
return this.$lines.slice(firstRow, lastRow+1);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getLength = function() {
|
||||||
|
return this.$lines.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getTextRange = function(range) {
|
||||||
|
if (range.start.row == range.end.row) {
|
||||||
|
return this.$lines[range.start.row].substring(range.start.column,
|
||||||
|
range.end.column);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var lines = [];
|
||||||
|
lines.push(this.$lines[range.start.row].substring(range.start.column));
|
||||||
|
lines.push.apply(lines, this.getLines(range.start.row+1, range.end.row-1));
|
||||||
|
lines.push(this.$lines[range.end.row].substring(0, range.end.column));
|
||||||
|
return lines.join(this.$getNewLineCharacter());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.findMatchingBracket = function(position) {
|
||||||
|
if (position.column == 0) return null;
|
||||||
|
|
||||||
|
var charBeforeCursor = this.getLine(position.row).charAt(position.column-1);
|
||||||
|
if (charBeforeCursor == "") return null;
|
||||||
|
|
||||||
|
var match = charBeforeCursor.match(/([\(\[\{])|([\)\]\}])/);
|
||||||
|
if (!match) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match[1]) {
|
||||||
|
return this.$findClosingBracket(match[1], position);
|
||||||
|
} else {
|
||||||
|
return this.$findOpeningBracket(match[2], position);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$brackets = {
|
||||||
|
")": "(",
|
||||||
|
"(": ")",
|
||||||
|
"]": "[",
|
||||||
|
"[": "]",
|
||||||
|
"{": "}",
|
||||||
|
"}": "{"
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$findOpeningBracket = function(bracket, position) {
|
||||||
|
var openBracket = this.$brackets[bracket];
|
||||||
|
|
||||||
|
var column = position.column - 2;
|
||||||
|
var row = position.row;
|
||||||
|
var depth = 1;
|
||||||
|
|
||||||
|
var line = this.getLine(row);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
while(column >= 0) {
|
||||||
|
var ch = line.charAt(column);
|
||||||
|
if (ch == openBracket) {
|
||||||
|
depth -= 1;
|
||||||
|
if (depth == 0) {
|
||||||
|
return {row: row, column: column};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ch == bracket) {
|
||||||
|
depth +=1;
|
||||||
|
}
|
||||||
|
column -= 1;
|
||||||
|
}
|
||||||
|
row -=1;
|
||||||
|
if (row < 0) break;
|
||||||
|
|
||||||
|
var line = this.getLine(row);
|
||||||
|
var column = line.length-1;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$findClosingBracket = function(bracket, position) {
|
||||||
|
var closingBracket = this.$brackets[bracket];
|
||||||
|
|
||||||
|
var column = position.column;
|
||||||
|
var row = position.row;
|
||||||
|
var depth = 1;
|
||||||
|
|
||||||
|
var line = this.getLine(row);
|
||||||
|
var lineCount = this.getLength();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
while(column < line.length) {
|
||||||
|
var ch = line.charAt(column);
|
||||||
|
if (ch == closingBracket) {
|
||||||
|
depth -= 1;
|
||||||
|
if (depth == 0) {
|
||||||
|
return {row: row, column: column};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ch == bracket) {
|
||||||
|
depth +=1;
|
||||||
|
}
|
||||||
|
column += 1;
|
||||||
|
}
|
||||||
|
row +=1;
|
||||||
|
if (row >= lineCount) break;
|
||||||
|
|
||||||
|
var line = this.getLine(row);
|
||||||
|
var column = 0;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.insert = function(position, text, fromUndo) {
|
||||||
|
var end = this.$insert(position, text, fromUndo);
|
||||||
|
this.fireChangeEvent(position.row, position.row == end.row ? position.row
|
||||||
|
: undefined);
|
||||||
|
return end;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param rows Array[Integer] sorted list of rows
|
||||||
|
*/
|
||||||
|
this.multiRowInsert = function(rows, column, text) {
|
||||||
|
var lines = this.$lines;
|
||||||
|
|
||||||
|
for (var i=rows.length-1; i>=0; i--) {
|
||||||
|
var row = rows[i];
|
||||||
|
if (row >= lines.length)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var diff = column - lines[row].length;
|
||||||
|
if ( diff > 0) {
|
||||||
|
var padded = lang.stringRepeat(" ", diff) + text;
|
||||||
|
var offset = -diff;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
padded = text;
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var end = this.$insert({row: row, column: column+offset}, padded, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end) {
|
||||||
|
this.fireChangeEvent(rows[0], rows[rows.length-1] + end.row - rows[0]);
|
||||||
|
return {
|
||||||
|
rows: end.row - rows[0],
|
||||||
|
columns: end.column - column
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return {
|
||||||
|
rows: 0,
|
||||||
|
columns: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$insertLines = function(row, lines, fromUndo) {
|
||||||
|
if (lines.length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var args = [row, 0];
|
||||||
|
args.push.apply(args, lines);
|
||||||
|
this.$lines.splice.apply(this.$lines, args);
|
||||||
|
|
||||||
|
var nl = this.$getNewLineCharacter();
|
||||||
|
var delta = {
|
||||||
|
action: "insertText",
|
||||||
|
range: new Range(row, 0, row + lines.length, 0),
|
||||||
|
text: lines.join(nl) + nl
|
||||||
|
};
|
||||||
|
if (!fromUndo && this.$undoManager) {
|
||||||
|
this.$deltas.push(delta);
|
||||||
|
this.$informUndoManager.schedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromUndo !== NO_CHANGE_DELTAS) {
|
||||||
|
this._dispatchEvent("changeDelta", { data: delta });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
this.$insert = function(position, text, fromUndo) {
|
||||||
|
if (text.length == 0)
|
||||||
|
return position;
|
||||||
|
|
||||||
|
this.modified = true;
|
||||||
|
if (this.$lines.length <= 1) {
|
||||||
|
this.$detectNewLine(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
var newLines = this.$split(text);
|
||||||
|
|
||||||
|
if (this.$isNewLine(text)) {
|
||||||
|
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));
|
||||||
|
|
||||||
|
var end = {
|
||||||
|
row : position.row + 1,
|
||||||
|
column : 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (newLines.length == 1) {
|
||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var line = this.$lines[position.row] || "";
|
||||||
|
var firstLine = line.substring(0, position.column) + newLines[0];
|
||||||
|
var lastLine = newLines[newLines.length - 1] + line.substring(position.column);
|
||||||
|
|
||||||
|
this.$lines[position.row] = firstLine;
|
||||||
|
this.$insertLines(position.row + 1, [lastLine], NO_CHANGE_DELTAS);
|
||||||
|
|
||||||
|
if (newLines.length > 2) {
|
||||||
|
this.$insertLines(position.row + 1, newLines.slice(1, -1), NO_CHANGE_DELTAS);
|
||||||
|
}
|
||||||
|
|
||||||
|
var end = {
|
||||||
|
row : position.row + newLines.length - 1,
|
||||||
|
column : newLines[newLines.length - 1].length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var delta = {
|
||||||
|
action: "insertText",
|
||||||
|
range: Range.fromPoints(position, end),
|
||||||
|
text: text
|
||||||
|
};
|
||||||
|
if (!fromUndo && this.$undoManager) {
|
||||||
|
this.$deltas.push(delta);
|
||||||
|
this.$informUndoManager.schedule();
|
||||||
|
}
|
||||||
|
this._dispatchEvent("changeDelta", { data: delta });
|
||||||
|
|
||||||
|
return end;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$isNewLine = function(text) {
|
||||||
|
return (text == "\r\n" || text == "\r" || text == "\n");
|
||||||
|
};
|
||||||
|
|
||||||
|
this.remove = function(range, fromUndo) {
|
||||||
|
if (range.isEmpty())
|
||||||
|
return range.start;
|
||||||
|
|
||||||
|
this.$remove(range, fromUndo);
|
||||||
|
|
||||||
|
this.fireChangeEvent(range.start.row, range.isMultiLine() ? undefined : range.start.row);
|
||||||
|
|
||||||
|
return range.start;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.multiRowRemove = function(rows, range) {
|
||||||
|
if (range.start.row !== rows[0])
|
||||||
|
throw new TypeError("range must start in the first row!");
|
||||||
|
|
||||||
|
var height = range.end.row - rows[0];
|
||||||
|
for (var i=rows.length-1; i>=0; i--) {
|
||||||
|
var row = rows[i];
|
||||||
|
if (row >= this.$lines.length)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var end = this.$remove(new Range(row, range.start.column, row+height, range.end.column), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end) {
|
||||||
|
if (height < 0)
|
||||||
|
this.fireChangeEvent(rows[0]+height, undefined);
|
||||||
|
else
|
||||||
|
this.fireChangeEvent(rows[0], height == 0 ? rows[rows.length-1] : undefined);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$remove = function(range, fromUndo) {
|
||||||
|
if (range.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var delta = {
|
||||||
|
action: "removeText",
|
||||||
|
range: range.clone(),
|
||||||
|
text: this.getTextRange(range)
|
||||||
|
};
|
||||||
|
if (!fromUndo && this.$undoManager) {
|
||||||
|
this.$deltas.push(delta);
|
||||||
|
this.$informUndoManager.schedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.modified = true;
|
||||||
|
|
||||||
|
var firstRow = range.start.row;
|
||||||
|
var lastRow = range.end.row;
|
||||||
|
|
||||||
|
var row = this.getLine(firstRow).substring(0, range.start.column)
|
||||||
|
+ this.getLine(lastRow).substring(range.end.column);
|
||||||
|
|
||||||
|
if (row != "")
|
||||||
|
this.$lines.splice(firstRow, lastRow - firstRow + 1, row);
|
||||||
|
else
|
||||||
|
this.$lines.splice(firstRow, lastRow - firstRow + 1, "");
|
||||||
|
|
||||||
|
this._dispatchEvent("changeDelta", { data: delta });
|
||||||
|
return range.start;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.undoChanges = function(deltas) {
|
||||||
|
this.selection.clearSelection();
|
||||||
|
for (var i=deltas.length-1; i>=0; i--) {
|
||||||
|
var delta = deltas[i];
|
||||||
|
if (delta.action == "insertText") {
|
||||||
|
this.remove(delta.range, true);
|
||||||
|
this.selection.moveCursorToPosition(delta.range.start);
|
||||||
|
} else {
|
||||||
|
this.insert(delta.range.start, delta.text, true);
|
||||||
|
this.selection.clearSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
this.redoChanges = function(deltas) {
|
||||||
|
this.selection.clearSelection();
|
||||||
|
for (var i=0; i<deltas.length; i++) {
|
||||||
|
var delta = deltas[i];
|
||||||
|
if (delta.action == "insertText") {
|
||||||
|
this.insert(delta.range.start, delta.text, true);
|
||||||
|
this.selection.setSelectionRange(delta.range);
|
||||||
|
} else {
|
||||||
|
this.remove(delta.range, true);
|
||||||
|
this.selection.moveCursorToPosition(delta.range.start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
this.replace = function(range, text) {
|
||||||
|
// Shortcut: Nothing to replace in this case.
|
||||||
|
if (text.length == 0 && range.isEmpty()) {
|
||||||
|
return range.start;
|
||||||
|
} else
|
||||||
|
// Shortcut: If the text we want to insert is the same as it is already
|
||||||
|
// in the document, we don't have to replace anything.
|
||||||
|
if (text == this.getTextRange(range)) {
|
||||||
|
return range.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$remove(range);
|
||||||
|
if (text) {
|
||||||
|
var end = this.$insert(range.start, text);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
end = range.start;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastRemoved = range.end.column == 0 ? range.end.column - 1
|
||||||
|
: range.end.column;
|
||||||
|
this.fireChangeEvent(range.start.row, lastRemoved == end.row ? lastRemoved
|
||||||
|
: undefined);
|
||||||
|
|
||||||
|
return end;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.indentRows = function(startRow, endRow, indentString) {
|
||||||
|
indentString = indentString.replace("\t", this.getTabString());
|
||||||
|
for (var row=startRow; row<=endRow; row++) {
|
||||||
|
this.$insert({row: row, column:0}, indentString);
|
||||||
|
}
|
||||||
|
this.fireChangeEvent(startRow, endRow);
|
||||||
|
return indentString.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
} else {
|
||||||
|
deleteRange.start.column = 0;
|
||||||
|
deleteRange.end.column = j;
|
||||||
|
}
|
||||||
|
if (i == range.start.row)
|
||||||
|
range.start.column -= deleteRange.end.column - deleteRange.start.column;
|
||||||
|
if (i == range.end.row)
|
||||||
|
range.end.column -= deleteRange.end.column - deleteRange.start.column;
|
||||||
|
this.$remove(deleteRange);
|
||||||
|
}
|
||||||
|
this.fireChangeEvent(range.start.row, range.end.row);
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.moveLinesUp = function(firstRow, lastRow) {
|
||||||
|
if (firstRow <= 0) return 0;
|
||||||
|
|
||||||
|
var removed = this.$lines.slice(firstRow, lastRow + 1);
|
||||||
|
this.$remove(new Range(firstRow-1, this.$lines[firstRow-1].length, lastRow, this.$lines[lastRow].length));
|
||||||
|
this.$insertLines(firstRow - 1, removed);
|
||||||
|
|
||||||
|
this.fireChangeEvent(firstRow - 1, lastRow);
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.moveLinesDown = function(firstRow, lastRow) {
|
||||||
|
if (lastRow >= this.$lines.length-1) return 0;
|
||||||
|
|
||||||
|
var removed = this.$lines.slice(firstRow, lastRow + 1);
|
||||||
|
this.$remove(new Range(firstRow, 0, lastRow + 1, 0));
|
||||||
|
this.$insertLines(firstRow+1, removed);
|
||||||
|
|
||||||
|
this.fireChangeEvent(firstRow, lastRow + 1);
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.duplicateLines = function(firstRow, lastRow) {
|
||||||
|
var firstRow = this.$clipRowToDocument(firstRow);
|
||||||
|
var lastRow = this.$clipRowToDocument(lastRow);
|
||||||
|
|
||||||
|
var lines = this.getLines(firstRow, lastRow);
|
||||||
|
this.$insertLines(firstRow, lines);
|
||||||
|
|
||||||
|
var addedRows = lastRow - firstRow + 1;
|
||||||
|
this.fireChangeEvent(firstRow);
|
||||||
|
|
||||||
|
return addedRows;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$clipRowToDocument = function(row) {
|
||||||
|
return Math.max(0, Math.min(row, this.$lines.length-1));
|
||||||
|
};
|
||||||
|
|
||||||
|
this.documentToScreenColumn = function(row, docColumn) {
|
||||||
|
var tabSize = this.getTabSize();
|
||||||
|
|
||||||
|
var screenColumn = 0;
|
||||||
|
var remaining = docColumn;
|
||||||
|
|
||||||
|
var line = this.getLine(row).split("\t");
|
||||||
|
for (var i=0; i<line.length; i++) {
|
||||||
|
var len = line[i].length;
|
||||||
|
if (remaining > len) {
|
||||||
|
remaining -= (len + 1);
|
||||||
|
screenColumn += len + tabSize;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
screenColumn += remaining;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return screenColumn;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.screenToDocumentColumn = function(row, screenColumn) {
|
||||||
|
var tabSize = this.getTabSize();
|
||||||
|
|
||||||
|
var docColumn = 0;
|
||||||
|
var remaining = screenColumn;
|
||||||
|
|
||||||
|
var line = this.getLine(row).split("\t");
|
||||||
|
for (var i=0; i<line.length; i++) {
|
||||||
|
var len = line[i].length;
|
||||||
|
if (remaining >= len + tabSize) {
|
||||||
|
remaining -= (len + tabSize);
|
||||||
|
docColumn += (len + 1);
|
||||||
|
}
|
||||||
|
else if (remaining > len){
|
||||||
|
docColumn += len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
docColumn += remaining;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return docColumn;
|
||||||
|
};
|
||||||
|
|
||||||
|
}).call(EditSession.prototype);
|
||||||
|
|
||||||
|
exports.EditSession = EditSession;
|
||||||
|
});
|
||||||
|
|
@ -35,259 +35,56 @@
|
||||||
*
|
*
|
||||||
* ***** END LICENSE BLOCK ***** */
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
define(function(require, exports, module) {
|
|
||||||
|
|
||||||
var Document = require("../document").Document,
|
var Document = require("../document").Document,
|
||||||
UndoManager = require("../undomanager").UndoManager,
|
|
||||||
MockRenderer = require("./mockrenderer"),
|
|
||||||
Range = require("../range").Range,
|
Range = require("../range").Range,
|
||||||
assert = require("./assertions"),
|
assert = require("./assertions"),
|
||||||
async = require("async");
|
async = require("async");
|
||||||
|
|
||||||
var Test = {
|
var Test = {
|
||||||
|
|
||||||
"test: find matching opening bracket" : function() {
|
|
||||||
var doc = new Document(["(()(", "())))"]);
|
|
||||||
|
|
||||||
assert.position(doc.findMatchingBracket({row: 0, column: 3}), 0, 1);
|
|
||||||
assert.position(doc.findMatchingBracket({row: 1, column: 2}), 1, 0);
|
|
||||||
assert.position(doc.findMatchingBracket({row: 1, column: 3}), 0, 3);
|
|
||||||
assert.position(doc.findMatchingBracket({row: 1, column: 4}), 0, 0);
|
|
||||||
assert.equal(doc.findMatchingBracket({row: 1, column: 5}), null);
|
|
||||||
},
|
|
||||||
|
|
||||||
"test: find matching closing bracket" : function() {
|
|
||||||
var doc = new Document(["(()(", "())))"]);
|
|
||||||
|
|
||||||
assert.position(doc.findMatchingBracket({row: 1, column: 1}), 1, 1);
|
|
||||||
assert.position(doc.findMatchingBracket({row: 1, column: 1}), 1, 1);
|
|
||||||
assert.position(doc.findMatchingBracket({row: 0, column: 4}), 1, 2);
|
|
||||||
assert.position(doc.findMatchingBracket({row: 0, column: 2}), 0, 2);
|
|
||||||
assert.position(doc.findMatchingBracket({row: 0, column: 1}), 1, 3);
|
|
||||||
assert.equal(doc.findMatchingBracket({row: 0, column: 0}), null);
|
|
||||||
},
|
|
||||||
|
|
||||||
"test: match different bracket types" : function() {
|
|
||||||
var doc = new Document(["({[", ")]}"]);
|
|
||||||
|
|
||||||
assert.position(doc.findMatchingBracket({row: 0, column: 1}), 1, 0);
|
|
||||||
assert.position(doc.findMatchingBracket({row: 0, column: 2}), 1, 2);
|
|
||||||
assert.position(doc.findMatchingBracket({row: 0, column: 3}), 1, 1);
|
|
||||||
|
|
||||||
assert.position(doc.findMatchingBracket({row: 1, column: 1}), 0, 0);
|
|
||||||
assert.position(doc.findMatchingBracket({row: 1, column: 2}), 0, 2);
|
|
||||||
assert.position(doc.findMatchingBracket({row: 1, column: 3}), 0, 1);
|
|
||||||
},
|
|
||||||
|
|
||||||
"test: move lines down" : function() {
|
|
||||||
var doc = new Document(["a1", "a2", "a3", "a4"]);
|
|
||||||
|
|
||||||
doc.moveLinesDown(0, 1);
|
|
||||||
assert.equal(doc.toString(), ["a3", "a1", "a2", "a4"].join("\n"));
|
|
||||||
|
|
||||||
doc.moveLinesDown(1, 2);
|
|
||||||
assert.equal(doc.toString(), ["a3", "a4", "a1", "a2"].join("\n"));
|
|
||||||
|
|
||||||
doc.moveLinesDown(2, 3);
|
|
||||||
assert.equal(doc.toString(), ["a3", "a4", "a1", "a2"].join("\n"));
|
|
||||||
|
|
||||||
doc.moveLinesDown(2, 2);
|
|
||||||
assert.equal(doc.toString(), ["a3", "a4", "a2", "a1"].join("\n"));
|
|
||||||
},
|
|
||||||
|
|
||||||
"test: move lines up" : function() {
|
|
||||||
var doc = new Document(["a1", "a2", "a3", "a4"]);
|
|
||||||
|
|
||||||
doc.moveLinesUp(2, 3);
|
|
||||||
assert.equal(doc.toString(), ["a1", "a3", "a4", "a2"].join("\n"));
|
|
||||||
|
|
||||||
doc.moveLinesUp(1, 2);
|
|
||||||
assert.equal(doc.toString(), ["a3", "a4", "a1", "a2"].join("\n"));
|
|
||||||
|
|
||||||
doc.moveLinesUp(0, 1);
|
|
||||||
assert.equal(doc.toString(), ["a3", "a4", "a1", "a2"].join("\n"));
|
|
||||||
|
|
||||||
doc.moveLinesUp(2, 2);
|
|
||||||
assert.equal(doc.toString(), ["a3", "a1", "a4", "a2"].join("\n"));
|
|
||||||
},
|
|
||||||
|
|
||||||
"test: duplicate lines" : function() {
|
|
||||||
var doc = new Document(["1", "2", "3", "4"]);
|
|
||||||
|
|
||||||
doc.duplicateLines(1, 2);
|
|
||||||
assert.equal(doc.toString(), ["1", "2", "3", "2", "3", "4"].join("\n"));
|
|
||||||
},
|
|
||||||
|
|
||||||
"test: duplicate last line" : function() {
|
|
||||||
var doc = new Document(["1", "2", "3"]);
|
|
||||||
|
|
||||||
doc.duplicateLines(2, 2);
|
|
||||||
assert.equal(doc.toString(), ["1", "2", "3", "3"].join("\n"));
|
|
||||||
},
|
|
||||||
|
|
||||||
"test: duplicate first line" : function() {
|
|
||||||
var doc = new Document(["1", "2", "3"]);
|
|
||||||
|
|
||||||
doc.duplicateLines(0, 0);
|
|
||||||
assert.equal(doc.toString(), ["1", "1", "2", "3"].join("\n"));
|
|
||||||
},
|
|
||||||
|
|
||||||
"test: should handle unix style new lines" : function() {
|
"test: should handle unix style new lines" : function() {
|
||||||
var doc = new Document(["1", "2", "3"]);
|
var doc = new Document(["1", "2", "3"]);
|
||||||
|
|
||||||
assert.equal(doc.toString(), ["1", "2", "3"].join("\n"));
|
assert.equal(doc.getValue(), ["1", "2", "3"].join("\n"));
|
||||||
},
|
},
|
||||||
|
|
||||||
"test: should handle windows style new lines" : function() {
|
"test: should handle windows style new lines" : function() {
|
||||||
var doc = new Document(["1", "2", "3"].join("\r\n"));
|
var doc = new Document(["1", "2", "3"].join("\r\n"));
|
||||||
|
|
||||||
doc.setNewLineMode("unix");
|
doc.setNewLineMode("unix");
|
||||||
assert.equal(doc.toString(), ["1", "2", "3"].join("\n"));
|
assert.equal(doc.getValue(), ["1", "2", "3"].join("\n"));
|
||||||
},
|
},
|
||||||
|
|
||||||
"test: set new line mode to 'windows' should use '\r\n' as new lines": function() {
|
"test: set new line mode to 'windows' should use '\r\n' as new lines": function() {
|
||||||
var doc = new Document(["1", "2", "3"].join("\n"));
|
var doc = new Document(["1", "2", "3"].join("\n"));
|
||||||
doc.setNewLineMode("windows");
|
doc.setNewLineMode("windows");
|
||||||
assert.equal(doc.toString(), ["1", "2", "3"].join("\r\n"));
|
assert.equal(doc.getValue(), ["1", "2", "3"].join("\r\n"));
|
||||||
},
|
},
|
||||||
|
|
||||||
"test: set new line mode to 'unix' should use '\n' as new lines": function() {
|
"test: set new line mode to 'unix' should use '\n' as new lines": function() {
|
||||||
var doc = new Document(["1", "2", "3"].join("\r\n"));
|
var doc = new Document(["1", "2", "3"].join("\r\n"));
|
||||||
|
|
||||||
doc.setNewLineMode("unix");
|
doc.setNewLineMode("unix");
|
||||||
assert.equal(doc.toString(), ["1", "2", "3"].join("\n"));
|
assert.equal(doc.getValue(), ["1", "2", "3"].join("\n"));
|
||||||
},
|
},
|
||||||
|
|
||||||
"test: set new line mode to 'auto' should detect the incoming nl type": function() {
|
"test: set new line mode to 'auto' should detect the incoming nl type": function() {
|
||||||
var doc = new Document(["1", "2", "3"].join("\n"));
|
var doc = new Document(["1", "2", "3"].join("\n"));
|
||||||
|
|
||||||
doc.setNewLineMode("auto");
|
doc.setNewLineMode("auto");
|
||||||
assert.equal(doc.toString(), ["1", "2", "3"].join("\n"));
|
assert.equal(doc.getValue(), ["1", "2", "3"].join("\n"));
|
||||||
|
|
||||||
var doc = new Document(["1", "2", "3"].join("\r\n"));
|
var doc = new Document(["1", "2", "3"].join("\r\n"));
|
||||||
|
|
||||||
doc.setNewLineMode("auto");
|
doc.setNewLineMode("auto");
|
||||||
assert.equal(doc.toString(), ["1", "2", "3"].join("\r\n"));
|
assert.equal(doc.getValue(), ["1", "2", "3"].join("\r\n"));
|
||||||
|
|
||||||
doc.replace(new Range(0, 0, 2, 1), ["4", "5", "6"].join("\n"));
|
doc.replace(new Range(0, 0, 2, 1), ["4", "5", "6"].join("\n"));
|
||||||
assert.equal(["4", "5", "6"].join("\n"), doc.toString());
|
assert.equal(["4", "5", "6"].join("\n"), doc.getValue());
|
||||||
},
|
|
||||||
|
|
||||||
"test: convert document to screen coordinates" : function() {
|
|
||||||
var doc = new Document("01234\t567890\t1234");
|
|
||||||
doc.setTabSize(4);
|
|
||||||
|
|
||||||
assert.equal(doc.documentToScreenColumn(0, 0), 0);
|
|
||||||
assert.equal(doc.documentToScreenColumn(0, 4), 4);
|
|
||||||
assert.equal(doc.documentToScreenColumn(0, 5), 5);
|
|
||||||
assert.equal(doc.documentToScreenColumn(0, 6), 9);
|
|
||||||
assert.equal(doc.documentToScreenColumn(0, 12), 15);
|
|
||||||
assert.equal(doc.documentToScreenColumn(0, 13), 19);
|
|
||||||
|
|
||||||
doc.setTabSize(2);
|
|
||||||
|
|
||||||
assert.equal(doc.documentToScreenColumn(0, 0), 0);
|
|
||||||
assert.equal(doc.documentToScreenColumn(0, 4), 4);
|
|
||||||
assert.equal(doc.documentToScreenColumn(0, 5), 5);
|
|
||||||
assert.equal(doc.documentToScreenColumn(0, 6), 7);
|
|
||||||
assert.equal(doc.documentToScreenColumn(0, 12), 13);
|
|
||||||
assert.equal(doc.documentToScreenColumn(0, 13), 15);
|
|
||||||
},
|
|
||||||
|
|
||||||
"test: convert document to scrren coordinates with leading tabs": function() {
|
|
||||||
var doc = new Document("\t\t123");
|
|
||||||
doc.setTabSize(4);
|
|
||||||
|
|
||||||
assert.equal(doc.documentToScreenColumn(0, 0), 0);
|
|
||||||
assert.equal(doc.documentToScreenColumn(0, 1), 4);
|
|
||||||
assert.equal(doc.documentToScreenColumn(0, 2), 8);
|
|
||||||
assert.equal(doc.documentToScreenColumn(0, 3), 9);
|
|
||||||
},
|
|
||||||
|
|
||||||
"test: convert screen to document coordinates" : function() {
|
|
||||||
var doc = new Document("01234\t567890\t1234");
|
|
||||||
doc.setTabSize(4);
|
|
||||||
|
|
||||||
assert.equal(doc.screenToDocumentColumn(0, 0), 0);
|
|
||||||
assert.equal(doc.screenToDocumentColumn(0, 4), 4);
|
|
||||||
assert.equal(doc.screenToDocumentColumn(0, 5), 5);
|
|
||||||
assert.equal(doc.screenToDocumentColumn(0, 6), 5);
|
|
||||||
assert.equal(doc.screenToDocumentColumn(0, 7), 5);
|
|
||||||
assert.equal(doc.screenToDocumentColumn(0, 8), 5);
|
|
||||||
assert.equal(doc.screenToDocumentColumn(0, 9), 6);
|
|
||||||
assert.equal(doc.screenToDocumentColumn(0, 15), 12);
|
|
||||||
assert.equal(doc.screenToDocumentColumn(0, 19), 13);
|
|
||||||
},
|
|
||||||
|
|
||||||
"test: insert text in multiple rows": function() {
|
|
||||||
var doc = new Document(["12", "", "abcd"]);
|
|
||||||
|
|
||||||
var inserted = doc.multiRowInsert([0, 1, 2], 2, "juhu 1");
|
|
||||||
assert.equal(inserted.rows, 0);
|
|
||||||
assert.equal(inserted.columns, 6);
|
|
||||||
|
|
||||||
assert.equal(doc.toString(), ["12juhu 1", " juhu 1", "abjuhu 1cd"].join("\n"));
|
|
||||||
},
|
|
||||||
|
|
||||||
"test: undo insert text in multiple rows": function() {
|
|
||||||
var doc = new Document(["12", "", "abcd"]);
|
|
||||||
|
|
||||||
var undoManager = new UndoManager();
|
|
||||||
doc.setUndoManager(undoManager);
|
|
||||||
|
|
||||||
doc.multiRowInsert([0, 1, 2], 2, "juhu 1");
|
|
||||||
doc.$informUndoManager.call();
|
|
||||||
assert.equal(doc.toString(), ["12juhu 1", " juhu 1", "abjuhu 1cd"].join("\n"));
|
|
||||||
|
|
||||||
undoManager.undo();
|
|
||||||
assert.equal(doc.toString(), ["12", "", "abcd"].join("\n"));
|
|
||||||
|
|
||||||
undoManager.redo();
|
|
||||||
assert.equal(doc.toString(), ["12juhu 1", " juhu 1", "abjuhu 1cd"].join("\n"));
|
|
||||||
},
|
|
||||||
|
|
||||||
"test: insert new line in multiple rows": function() {
|
|
||||||
var doc = new Document(["12", "", "abcd"]);
|
|
||||||
|
|
||||||
var inserted = doc.multiRowInsert([0, 1, 2], 2, "\n");
|
|
||||||
assert.equal(inserted.rows, 1);
|
|
||||||
assert.equal(doc.toString(), ["12\n", " \n", "ab\ncd"].join("\n"));
|
|
||||||
},
|
|
||||||
|
|
||||||
"test: insert multi line text in multiple rows": function() {
|
|
||||||
var doc = new Document(["12", "", "abcd"]);
|
|
||||||
|
|
||||||
var inserted = doc.multiRowInsert([0, 1, 2], 2, "juhu\n12");
|
|
||||||
assert.equal(inserted.rows, 1);
|
|
||||||
assert.equal(doc.toString(), ["12juhu\n12", " juhu\n12", "abjuhu\n12cd"].join("\n"));
|
|
||||||
},
|
|
||||||
|
|
||||||
"test: remove right in multiple rows" : function() {
|
|
||||||
var doc = new Document(["12", "", "abcd"]);
|
|
||||||
|
|
||||||
doc.multiRowRemove([0, 1, 2], new Range(0, 2, 0, 3));
|
|
||||||
assert.equal(doc.toString(), ["12", "", "abd"].join("\n"));
|
|
||||||
},
|
|
||||||
|
|
||||||
"test: undo remove right in multiple rows" : function() {
|
|
||||||
var doc = new Document(["12", "", "abcd"]);
|
|
||||||
var undoManager = new UndoManager();
|
|
||||||
doc.setUndoManager(undoManager);
|
|
||||||
|
|
||||||
doc.multiRowRemove([0, 1, 2], new Range(0, 1, 0, 3));
|
|
||||||
doc.$informUndoManager.call();
|
|
||||||
assert.equal(doc.toString(), ["1", "", "ad"].join("\n"));
|
|
||||||
|
|
||||||
undoManager.undo();
|
|
||||||
assert.equal(doc.toString(), ["12", "", "abcd"].join("\n"));
|
|
||||||
|
|
||||||
undoManager.redo();
|
|
||||||
assert.equal(doc.toString(), ["1", "", "ad"].join("\n"));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = require("async/test").testcase(Test);
|
module.exports = require("async/test").testcase(Test);
|
||||||
});
|
|
||||||
|
|
||||||
if (module === require.main) {
|
if (module === require.main) {
|
||||||
require("../../../support/paths");
|
require("../../../support/paths");
|
||||||
|
|
|
||||||
295
lib/ace/test/edit_session_test.js
Normal file
295
lib/ace/test/edit_session_test.js
Normal file
|
|
@ -0,0 +1,295 @@
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Ajax.org Code Editor (ACE).
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* Ajax.org B.V.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Fabian Jakobs <fabian AT ajax DOT org>
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
define(function(require, exports, module) {
|
||||||
|
|
||||||
|
var Document = require("../document").Document,
|
||||||
|
UndoManager = require("../undomanager").UndoManager,
|
||||||
|
MockRenderer = require("./mockrenderer"),
|
||||||
|
Range = require("../range").Range,
|
||||||
|
assert = require("./assertions"),
|
||||||
|
async = require("async");
|
||||||
|
|
||||||
|
var Test = {
|
||||||
|
|
||||||
|
"test: find matching opening bracket" : function() {
|
||||||
|
var doc = new Document(["(()(", "())))"]);
|
||||||
|
|
||||||
|
assert.position(doc.findMatchingBracket({row: 0, column: 3}), 0, 1);
|
||||||
|
assert.position(doc.findMatchingBracket({row: 1, column: 2}), 1, 0);
|
||||||
|
assert.position(doc.findMatchingBracket({row: 1, column: 3}), 0, 3);
|
||||||
|
assert.position(doc.findMatchingBracket({row: 1, column: 4}), 0, 0);
|
||||||
|
assert.equal(doc.findMatchingBracket({row: 1, column: 5}), null);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: find matching closing bracket" : function() {
|
||||||
|
var doc = new Document(["(()(", "())))"]);
|
||||||
|
|
||||||
|
assert.position(doc.findMatchingBracket({row: 1, column: 1}), 1, 1);
|
||||||
|
assert.position(doc.findMatchingBracket({row: 1, column: 1}), 1, 1);
|
||||||
|
assert.position(doc.findMatchingBracket({row: 0, column: 4}), 1, 2);
|
||||||
|
assert.position(doc.findMatchingBracket({row: 0, column: 2}), 0, 2);
|
||||||
|
assert.position(doc.findMatchingBracket({row: 0, column: 1}), 1, 3);
|
||||||
|
assert.equal(doc.findMatchingBracket({row: 0, column: 0}), null);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: match different bracket types" : function() {
|
||||||
|
var doc = new Document(["({[", ")]}"]);
|
||||||
|
|
||||||
|
assert.position(doc.findMatchingBracket({row: 0, column: 1}), 1, 0);
|
||||||
|
assert.position(doc.findMatchingBracket({row: 0, column: 2}), 1, 2);
|
||||||
|
assert.position(doc.findMatchingBracket({row: 0, column: 3}), 1, 1);
|
||||||
|
|
||||||
|
assert.position(doc.findMatchingBracket({row: 1, column: 1}), 0, 0);
|
||||||
|
assert.position(doc.findMatchingBracket({row: 1, column: 2}), 0, 2);
|
||||||
|
assert.position(doc.findMatchingBracket({row: 1, column: 3}), 0, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: move lines down" : function() {
|
||||||
|
var doc = new Document(["a1", "a2", "a3", "a4"]);
|
||||||
|
|
||||||
|
doc.moveLinesDown(0, 1);
|
||||||
|
assert.equal(doc.toString(), ["a3", "a1", "a2", "a4"].join("\n"));
|
||||||
|
|
||||||
|
doc.moveLinesDown(1, 2);
|
||||||
|
assert.equal(doc.toString(), ["a3", "a4", "a1", "a2"].join("\n"));
|
||||||
|
|
||||||
|
doc.moveLinesDown(2, 3);
|
||||||
|
assert.equal(doc.toString(), ["a3", "a4", "a1", "a2"].join("\n"));
|
||||||
|
|
||||||
|
doc.moveLinesDown(2, 2);
|
||||||
|
assert.equal(doc.toString(), ["a3", "a4", "a2", "a1"].join("\n"));
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: move lines up" : function() {
|
||||||
|
var doc = new Document(["a1", "a2", "a3", "a4"]);
|
||||||
|
|
||||||
|
doc.moveLinesUp(2, 3);
|
||||||
|
assert.equal(doc.toString(), ["a1", "a3", "a4", "a2"].join("\n"));
|
||||||
|
|
||||||
|
doc.moveLinesUp(1, 2);
|
||||||
|
assert.equal(doc.toString(), ["a3", "a4", "a1", "a2"].join("\n"));
|
||||||
|
|
||||||
|
doc.moveLinesUp(0, 1);
|
||||||
|
assert.equal(doc.toString(), ["a3", "a4", "a1", "a2"].join("\n"));
|
||||||
|
|
||||||
|
doc.moveLinesUp(2, 2);
|
||||||
|
assert.equal(doc.toString(), ["a3", "a1", "a4", "a2"].join("\n"));
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: duplicate lines" : function() {
|
||||||
|
var doc = new Document(["1", "2", "3", "4"]);
|
||||||
|
|
||||||
|
doc.duplicateLines(1, 2);
|
||||||
|
assert.equal(doc.toString(), ["1", "2", "3", "2", "3", "4"].join("\n"));
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: duplicate last line" : function() {
|
||||||
|
var doc = new Document(["1", "2", "3"]);
|
||||||
|
|
||||||
|
doc.duplicateLines(2, 2);
|
||||||
|
assert.equal(doc.toString(), ["1", "2", "3", "3"].join("\n"));
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: duplicate first line" : function() {
|
||||||
|
var doc = new Document(["1", "2", "3"]);
|
||||||
|
|
||||||
|
doc.duplicateLines(0, 0);
|
||||||
|
assert.equal(doc.toString(), ["1", "1", "2", "3"].join("\n"));
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: should handle unix style new lines" : function() {
|
||||||
|
var doc = new Document(["1", "2", "3"]);
|
||||||
|
|
||||||
|
assert.equal(doc.toString(), ["1", "2", "3"].join("\n"));
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: should handle windows style new lines" : function() {
|
||||||
|
var doc = new Document(["1", "2", "3"].join("\r\n"));
|
||||||
|
|
||||||
|
doc.setNewLineMode("unix");
|
||||||
|
assert.equal(doc.toString(), ["1", "2", "3"].join("\n"));
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: set new line mode to 'windows' should use '\r\n' as new lines": function() {
|
||||||
|
var doc = new Document(["1", "2", "3"].join("\n"));
|
||||||
|
doc.setNewLineMode("windows");
|
||||||
|
assert.equal(doc.toString(), ["1", "2", "3"].join("\r\n"));
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: set new line mode to 'unix' should use '\n' as new lines": function() {
|
||||||
|
var doc = new Document(["1", "2", "3"].join("\r\n"));
|
||||||
|
|
||||||
|
doc.setNewLineMode("unix");
|
||||||
|
assert.equal(doc.toString(), ["1", "2", "3"].join("\n"));
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: set new line mode to 'auto' should detect the incoming nl type": function() {
|
||||||
|
var doc = new Document(["1", "2", "3"].join("\n"));
|
||||||
|
|
||||||
|
doc.setNewLineMode("auto");
|
||||||
|
assert.equal(doc.toString(), ["1", "2", "3"].join("\n"));
|
||||||
|
|
||||||
|
var doc = new Document(["1", "2", "3"].join("\r\n"));
|
||||||
|
|
||||||
|
doc.setNewLineMode("auto");
|
||||||
|
assert.equal(doc.toString(), ["1", "2", "3"].join("\r\n"));
|
||||||
|
|
||||||
|
doc.replace(new Range(0, 0, 2, 1), ["4", "5", "6"].join("\n"));
|
||||||
|
assert.equal(["4", "5", "6"].join("\n"), doc.toString());
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: convert document to screen coordinates" : function() {
|
||||||
|
var doc = new Document("01234\t567890\t1234");
|
||||||
|
doc.setTabSize(4);
|
||||||
|
|
||||||
|
assert.equal(doc.documentToScreenColumn(0, 0), 0);
|
||||||
|
assert.equal(doc.documentToScreenColumn(0, 4), 4);
|
||||||
|
assert.equal(doc.documentToScreenColumn(0, 5), 5);
|
||||||
|
assert.equal(doc.documentToScreenColumn(0, 6), 9);
|
||||||
|
assert.equal(doc.documentToScreenColumn(0, 12), 15);
|
||||||
|
assert.equal(doc.documentToScreenColumn(0, 13), 19);
|
||||||
|
|
||||||
|
doc.setTabSize(2);
|
||||||
|
|
||||||
|
assert.equal(doc.documentToScreenColumn(0, 0), 0);
|
||||||
|
assert.equal(doc.documentToScreenColumn(0, 4), 4);
|
||||||
|
assert.equal(doc.documentToScreenColumn(0, 5), 5);
|
||||||
|
assert.equal(doc.documentToScreenColumn(0, 6), 7);
|
||||||
|
assert.equal(doc.documentToScreenColumn(0, 12), 13);
|
||||||
|
assert.equal(doc.documentToScreenColumn(0, 13), 15);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: convert document to scrren coordinates with leading tabs": function() {
|
||||||
|
var doc = new Document("\t\t123");
|
||||||
|
doc.setTabSize(4);
|
||||||
|
|
||||||
|
assert.equal(doc.documentToScreenColumn(0, 0), 0);
|
||||||
|
assert.equal(doc.documentToScreenColumn(0, 1), 4);
|
||||||
|
assert.equal(doc.documentToScreenColumn(0, 2), 8);
|
||||||
|
assert.equal(doc.documentToScreenColumn(0, 3), 9);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: convert screen to document coordinates" : function() {
|
||||||
|
var doc = new Document("01234\t567890\t1234");
|
||||||
|
doc.setTabSize(4);
|
||||||
|
|
||||||
|
assert.equal(doc.screenToDocumentColumn(0, 0), 0);
|
||||||
|
assert.equal(doc.screenToDocumentColumn(0, 4), 4);
|
||||||
|
assert.equal(doc.screenToDocumentColumn(0, 5), 5);
|
||||||
|
assert.equal(doc.screenToDocumentColumn(0, 6), 5);
|
||||||
|
assert.equal(doc.screenToDocumentColumn(0, 7), 5);
|
||||||
|
assert.equal(doc.screenToDocumentColumn(0, 8), 5);
|
||||||
|
assert.equal(doc.screenToDocumentColumn(0, 9), 6);
|
||||||
|
assert.equal(doc.screenToDocumentColumn(0, 15), 12);
|
||||||
|
assert.equal(doc.screenToDocumentColumn(0, 19), 13);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: insert text in multiple rows": function() {
|
||||||
|
var doc = new Document(["12", "", "abcd"]);
|
||||||
|
|
||||||
|
var inserted = doc.multiRowInsert([0, 1, 2], 2, "juhu 1");
|
||||||
|
assert.equal(inserted.rows, 0);
|
||||||
|
assert.equal(inserted.columns, 6);
|
||||||
|
|
||||||
|
assert.equal(doc.toString(), ["12juhu 1", " juhu 1", "abjuhu 1cd"].join("\n"));
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: undo insert text in multiple rows": function() {
|
||||||
|
var doc = new Document(["12", "", "abcd"]);
|
||||||
|
|
||||||
|
var undoManager = new UndoManager();
|
||||||
|
doc.setUndoManager(undoManager);
|
||||||
|
|
||||||
|
doc.multiRowInsert([0, 1, 2], 2, "juhu 1");
|
||||||
|
doc.$informUndoManager.call();
|
||||||
|
assert.equal(doc.toString(), ["12juhu 1", " juhu 1", "abjuhu 1cd"].join("\n"));
|
||||||
|
|
||||||
|
undoManager.undo();
|
||||||
|
assert.equal(doc.toString(), ["12", "", "abcd"].join("\n"));
|
||||||
|
|
||||||
|
undoManager.redo();
|
||||||
|
assert.equal(doc.toString(), ["12juhu 1", " juhu 1", "abjuhu 1cd"].join("\n"));
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: insert new line in multiple rows": function() {
|
||||||
|
var doc = new Document(["12", "", "abcd"]);
|
||||||
|
|
||||||
|
var inserted = doc.multiRowInsert([0, 1, 2], 2, "\n");
|
||||||
|
assert.equal(inserted.rows, 1);
|
||||||
|
assert.equal(doc.toString(), ["12\n", " \n", "ab\ncd"].join("\n"));
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: insert multi line text in multiple rows": function() {
|
||||||
|
var doc = new Document(["12", "", "abcd"]);
|
||||||
|
|
||||||
|
var inserted = doc.multiRowInsert([0, 1, 2], 2, "juhu\n12");
|
||||||
|
assert.equal(inserted.rows, 1);
|
||||||
|
assert.equal(doc.toString(), ["12juhu\n12", " juhu\n12", "abjuhu\n12cd"].join("\n"));
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: remove right in multiple rows" : function() {
|
||||||
|
var doc = new Document(["12", "", "abcd"]);
|
||||||
|
|
||||||
|
doc.multiRowRemove([0, 1, 2], new Range(0, 2, 0, 3));
|
||||||
|
assert.equal(doc.toString(), ["12", "", "abd"].join("\n"));
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: undo remove right in multiple rows" : function() {
|
||||||
|
var doc = new Document(["12", "", "abcd"]);
|
||||||
|
var undoManager = new UndoManager();
|
||||||
|
doc.setUndoManager(undoManager);
|
||||||
|
|
||||||
|
doc.multiRowRemove([0, 1, 2], new Range(0, 1, 0, 3));
|
||||||
|
doc.$informUndoManager.call();
|
||||||
|
assert.equal(doc.toString(), ["1", "", "ad"].join("\n"));
|
||||||
|
|
||||||
|
undoManager.undo();
|
||||||
|
assert.equal(doc.toString(), ["12", "", "abcd"].join("\n"));
|
||||||
|
|
||||||
|
undoManager.redo();
|
||||||
|
assert.equal(doc.toString(), ["1", "", "ad"].join("\n"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = require("async/test").testcase(Test);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (module === require.main) {
|
||||||
|
require("../../../support/paths");
|
||||||
|
exports.exec()
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue