Conflicts:
	Makefile.dryice.js
	build_support/boot.js
	build_support/mini_require.js
	lib/ace/edit_session.js
	package.json
	support/pilot
This commit is contained in:
Joe Walker 2011-02-19 23:38:07 +00:00
commit f332454b38
196 changed files with 51983 additions and 2086 deletions

182
lib/ace/anchor.js Normal file
View file

@ -0,0 +1,182 @@
/* ***** 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 EventEmitter = require("pilot/event_emitter").EventEmitter;
/**
* An Anchor is a floating pointer in the document. Whenever text is inserted or
* deleted before the cursor, the position of the cursor is updated
*/
var Anchor = exports.Anchor = function(doc, row, column) {
this.document = doc;
if (typeof column == "undefined")
this.setPosition(row.row, row.column)
else
this.setPosition(row, column);
this.$onChange = this.onChange.bind(this);
doc.on("change", this.$onChange);
};
(function() {
oop.implement(this, EventEmitter);
this.getPosition = function() {
return this.$clipPositionToDocument(this.row, this.column);
};
this.getDocument = function() {
return this.document;
};
this.onChange = function(e) {
var delta = e.data;
var range = delta.range;
if (range.start.row == range.end.row && range.start.row != this.row)
return;
if (range.start.row > this.row)
return;
if (range.start.row == this.row && range.start.column > this.column)
return;
var row = this.row;
var column = this.column;
if (delta.action === "insertText") {
if (range.start.row === row && range.start.column <= column) {
if (range.start.row === range.end.row) {
column += range.end.column - range.start.column;
}
else {
column -= range.start.column;
row += range.end.row - range.start.row;
}
}
else if (range.start.row !== range.end.row && range.start.row < row) {
row += range.end.row - range.start.row;
}
} else if (delta.action === "insertLines") {
if (range.start.row <= row) {
row += range.end.row - range.start.row;
}
}
else if (delta.action == "removeText") {
if (range.start.row == row && range.start.column < column) {
if (range.end.column >= column)
column = range.start.column;
else
column = Math.max(0, column - (range.end.column - range.start.column));
} else if (range.start.row !== range.end.row && range.start.row < row) {
if (range.end.row == row) {
column = Math.max(0, column - range.end.column) + range.start.column;
}
row -= (range.end.row - range.start.row);
}
else if (range.end.row == row) {
row -= range.end.row - range.start.row;
column = Math.max(0, column - range.end.column) + range.start.column;
}
} else if (delta.action == "removeLines") {
if (range.start.row <= row) {
if (range.end.row <= row)
row -= range.end.row - range.start.row;
else {
row = range.start.row;
column = 0;
}
}
}
this.setPosition(row, column);
};
this.setPosition = function(row, column) {
pos = this.$clipPositionToDocument(row, column);
if (this.row == pos.row && this.column == pos.column)
return;
var old = {
row: this.row,
column: this.column
};
this.row = pos.row;
this.column = pos.column;
this._dispatchEvent("change", {
old: old,
value: pos
});
};
this.detach = function() {
this.document.removeEventListener("change", this.$onChange);
};
this.$clipPositionToDocument = function(row, column) {
var pos = {};
if (row >= this.document.getLength()) {
pos.row = Math.max(0, this.document.getLength() - 1);
pos.column = this.document.getLine(pos.row).length;
}
else if (row < 0) {
pos.row = 0;
pos.column = 0;
}
else {
pos.row = row;
pos.column = Math.min(this.document.getLine(pos.row).length, Math.max(0, column));
}
if (column < 0)
pos.column = 0;
return pos;
};
}).call(Anchor.prototype);
});

View file

@ -41,8 +41,7 @@ var oop = require("pilot/oop");
var EventEmitter = require("pilot/event_emitter").EventEmitter;
var BackgroundTokenizer = function(tokenizer, editor) {
this.running = false;
this.doc = [];
this.running = false;
this.lines = [];
this.currentLine = 0;
this.tokenizer = tokenizer;
@ -134,6 +133,9 @@ var BackgroundTokenizer = function(tokenizer, editor) {
};
this.$tokenizeRows = function(firstRow, lastRow) {
if (!this.doc)
return [];
var rows = [];
// determine start state

View file

@ -1,4 +1,5 @@
/* ***** BEGIN LICENSE BLOCK *****
/* vim:ts=4:sts=4:sw=4:
* ***** 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
@ -21,6 +22,7 @@
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
* Julian Viereck <julian.viereck@gmail.com>
* Mihai Sucan <mihai.sucan@gmail.com>
*
* 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
@ -48,7 +50,7 @@ canon.addCommand({
canon.addCommand({
name: "selectall",
exec: function(env, args, request) { env.editor.getSelection().selectAll(); }
exec: function(env, args, request) { env.editor.selectAll(); }
});
canon.addCommand({
name: "removeline",
@ -82,6 +84,30 @@ canon.addCommand({
env.editor.find(needle);
}
});
canon.addCommand({
name: "replace",
exec: function(env, args, request) {
var needle = prompt("Find:");
if (!needle)
return;
var replacement = prompt("Replacement:");
if (!replacement)
return;
env.editor.replace(replacement, {needle: needle});
}
});
canon.addCommand({
name: "replaceall",
exec: function(env, args, request) {
var needle = prompt("Find:");
if (!needle)
return;
var replacement = prompt("Replacement:");
if (!replacement)
return;
env.editor.replaceAll(replacement, {needle: needle});
}
});
canon.addCommand({
name: "undo",
exec: function(env, args, request) { env.editor.undo(); }
@ -242,6 +268,22 @@ canon.addCommand({
name: "backspace",
exec: function(env, args, request) { env.editor.removeLeft(); }
});
canon.addCommand({
name: "removetolinestart",
exec: function(env, args, request) { env.editor.removeToLineStart(); }
});
canon.addCommand({
name: "removetolineend",
exec: function(env, args, request) { env.editor.removeToLineEnd(); }
});
canon.addCommand({
name: "removewordleft",
exec: function(env, args, request) { env.editor.removeWordLeft(); }
});
canon.addCommand({
name: "removewordright",
exec: function(env, args, request) { env.editor.removeWordRight(); }
});
canon.addCommand({
name: "outdent",
exec: function(env, args, request) { env.editor.blockOutdent(); }
@ -253,9 +295,21 @@ canon.addCommand({
canon.addCommand({
name: "inserttext",
exec: function(env, args, request) {
env.editor.insert(lang.stringRepeat(args.text || "",
args.times || 1));
env.editor.insert(lang.stringRepeat(args.text || "", args.times || 1));
}
});
canon.addCommand({
name: "centerselection",
exec: function(env, args, request) { env.editor.centerSelection(); }
});
canon.addCommand({
name: "splitline",
exec: function(env, args, request) { env.editor.splitLine(); }
});
canon.addCommand({
name: "transposeletters",
exec: function(env, args, request) { env.editor.transposeLetters(); }
});
});

View file

@ -1,98 +1,154 @@
.ace_editor {
position: absolute;
overflow: hidden;
position: absolute;
overflow: hidden;
font-family: "Menlo", "Monaco", "Courier New", monospace;
font-size: 12px;
font-family: "Menlo", "Monaco", "Courier New", monospace;
font-size: 12px;
}
.ace_scroller {
position: absolute;
overflow-x: scroll;
overflow-y: hidden;
position: absolute;
overflow-x: scroll;
overflow-y: hidden;
}
.ace_content {
position: absolute;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
.ace_composition {
position: absolute;
background: #555;
color: #DDD;
z-index: 4;
}
.ace_gutter {
position: absolute;
overflow-x: hidden;
overflow-y: hidden;
height: 100%;
position: absolute;
overflow-x: hidden;
overflow-y: hidden;
height: 100%;
}
.ace_gutter-cell.ace_error {
background-image: url("data:image/gif,GIF89a%10%00%10%00%D5%00%00%F5or%F5%87%88%F5nr%F4ns%EBmq%F5z%7F%DDJT%DEKS%DFOW%F1Yc%F2ah%CE(7%CE)8%D18E%DD%40M%F2KZ%EBU%60%F4%60m%DCir%C8%16(%C8%19*%CE%255%F1%3FR%F1%3FS%E6%AB%B5%CA%5DI%CEn%5E%F7%A2%9A%C9G%3E%E0a%5B%F7%89%85%F5yy%F6%82%80%ED%82%80%FF%BF%BF%E3%C4%C4%FF%FF%FF%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%25%00%2C%00%00%00%00%10%00%10%00%00%06p%C0%92pH%2C%1A%8F%C8%D2H%93%E1d4%23%E4%88%D3%09mB%1DN%B48%F5%90%40%60%92G%5B%94%20%3E%22%D2%87%24%FA%20%24%C5%06A%00%20%B1%07%02B%A38%89X.v%17%82%11%13q%10%0Fi%24%0F%8B%10%7BD%12%0Ei%09%92%09%0EpD%18%15%24%0A%9Ci%05%0C%18F%18%0B%07%04%01%04%06%A0H%18%12%0D%14%0D%12%A1I%B3%B4%B5IA%00%3B");
background-repeat: no-repeat;
background-position: 4px center;
}
.ace_gutter-cell.ace_warning {
background-image: url("data:image/gif,GIF89a%10%00%10%00%D5%00%00%FF%DBr%FF%DE%81%FF%E2%8D%FF%E2%8F%FF%E4%96%FF%E3%97%FF%E5%9D%FF%E6%9E%FF%EE%C1%FF%C8Z%FF%CDk%FF%D0s%FF%D4%81%FF%D5%82%FF%D5%83%FF%DC%97%FF%DE%9D%FF%E7%B8%FF%CCl%7BQ%13%80U%15%82W%16%81U%16%89%5B%18%87%5B%18%8C%5E%1A%94d%1D%C5%83-%C9%87%2F%C6%84.%C6%85.%CD%8B2%C9%871%CB%8A3%CD%8B5%DC%98%3F%DF%9BB%E0%9CC%E1%A5U%CB%871%CF%8B5%D1%8D6%DB%97%40%DF%9AB%DD%99B%E3%B0p%E7%CC%AE%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%2F%00%2C%00%00%00%00%10%00%10%00%00%06a%C0%97pH%2C%1A%8FH%A1%ABTr%25%87%2B%04%82%F4%7C%B9X%91%08%CB%99%1C!%26%13%84*iJ9(%15G%CA%84%14%01%1A%97%0C%03%80%3A%9A%3E%81%84%3E%11%08%B1%8B%20%02%12%0F%18%1A%0F%0A%03'F%1C%04%0B%10%16%18%10%0B%05%1CF%1D-%06%07%9A%9A-%1EG%1B%A0%A1%A0U%A4%A5%A6BA%00%3B");
background-repeat: no-repeat;
background-position: 4px center;
}
.ace_editor .ace_sb {
position: absolute;
overflow-x: hidden;
overflow-y: scroll;
right: 0;
position: absolute;
overflow-x: hidden;
overflow-y: scroll;
right: 0;
}
.ace_editor .ace_sb div {
position: absolute;
width: 1px;
left: 0px;
position: absolute;
width: 1px;
left: 0;
}
.ace_editor .ace_print_margin_layer {
z-index: 0;
position: absolute;
overflow: hidden;
margin: 0px;
left: 0px;
height: 100%;
width: 100%;
z-index: 0;
position: absolute;
overflow: hidden;
margin: 0;
left: 0;
height: 100%;
width: 100%;
}
.ace_editor .ace_print_margin {
position: absolute;
height: 100%;
position: absolute;
height: 100%;
}
.ace_editor textarea {
position: fixed;
z-index: -1;
width: 10px;
height: 30px;
opacity: 0;
background: transparent;
appearance: none;
border: none;
resize: none;
outline: none;
overflow: hidden;
}
.ace_layer {
z-index: 1;
position: absolute;
overflow: hidden;
white-space: nowrap;
height: 100%;
width: 100%;
z-index: 1;
position: absolute;
overflow: hidden;
white-space: nowrap;
height: 100%;
width: 100%;
}
.ace_text-layer {
font-family: Monaco, "Courier New", monospace;
color: black;
font-family: Monaco, "Courier New", monospace;
color: black;
}
.ace_cjk {
display: inline-block;
text-align: center;
}
.ace_cursor-layer {
cursor: text;
z-index: 4;
cursor: text;
pointer-events: none;
}
.ace_cursor {
z-index: 4;
position: absolute;
z-index: 4;
position: absolute;
}
.ace_line {
white-space: nowrap;
white-space: nowrap;
}
.ace_marker-layer {
cursor: text;
}
.ace_marker-layer .ace_step {
position: absolute;
z-index: 3;
position: absolute;
z-index: 3;
}
.ace_marker-layer .ace_selection {
position: absolute;
z-index: 4;
position: absolute;
z-index: 4;
}
.ace_marker-layer .ace_bracket {
position: absolute;
z-index: 5;
position: absolute;
z-index: 5;
}
.ace_marker-layer .ace_active_line {
position: absolute;
z-index: 2;
position: absolute;
z-index: 2;
}
.ace_marker-layer .ace_selected_word {
position: absolute;
z-index: 6;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}

51
lib/ace/defaults.js Normal file
View file

@ -0,0 +1,51 @@
/* vim:ts=4:sts=4:sw=4:
* ***** 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):
* Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)
*
* 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 settings = require("ace/settings/default-settings")
exports.startup = function startup(data, reason) {
settings.startup(data, reason)
}
exports.shutdown = function shutdown(data, reason) {
settings.shutdown(data, reason)
}
})

View file

@ -40,12 +40,18 @@ define(function(require, exports, module) {
var oop = require("pilot/oop");
var EventEmitter = require("pilot/event_emitter").EventEmitter;
var Range = require("ace/range").Range;
var Anchor = require("ace/anchor").Anchor;
var Document = function(text) {
this.$lines = [];
if (Array.isArray(text)) {
this.insertLines(0, text);
}
// 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.
else if (text.length == 0) {
this.$lines = [""];
} else {
this.insert({row: 0, column:0}, text);
}
@ -58,15 +64,19 @@ var Document = function(text) {
this.setValue = function(text) {
var len = this.getLength();
this.remove(new Range(0, 0, len, this.getLine(len-1).length));
this.insertLines(0, this.$split(text));
this.insert({row: 0, column:0}, text);
};
this.getValue = function() {
return this.$lines.join(this.getNewLineCharacter());
return this.getAllLines().join(this.getNewLineCharacter());
};
this.createAnchor = function(row, column) {
return new Anchor(this, row, column);
};
// check for IE split bug
if ("aaa".split(/a/).length == 0)
if ("aaa".split(/a/).length == 0)
this.$split = function(text) {
return text.replace(/\r\n|\r/g, "\n").split("\n");
}
@ -75,7 +85,7 @@ var Document = function(text) {
return text.split(/\r\n|\r|\n/);
};
this.$detectNewLine = function(text) {
var match = text.match(/^.*?(\r?\n)/m);
if (match) {
@ -84,7 +94,7 @@ var Document = function(text) {
this.$autoNewLine = "\n";
}
};
this.getNewLineCharacter = function() {
switch (this.$newLineMode) {
case "windows":
@ -109,28 +119,28 @@ var Document = function(text) {
this.getNewLineMode = function() {
return this.$newLineMode;
};
this.isNewLine = function(text) {
return (text == "\r\n" || text == "\r" || text == "\n");
};
/**
* Get a verbatim copy of the given line as it is in the document
* Get a verbatim copy of the given line as it is in the document
*/
this.getLine = function(row) {
return this.$lines[row] || "";
return this.getLines(row, row + 1)[0] || "";
};
this.getLines = function(firstRow, lastRow) {
return this.$lines.slice(firstRow, lastRow+1);
return this.$lines.slice(firstRow, lastRow + 1);
};
/**
* Returns all lines in the document as string array. Warning: The caller
* Returns all lines in the document as string array. Warning: The caller
* should not modify this array!
*/
this.getAllLines = function() {
return this.$lines;
return this.getLines(0, this.getLength());
};
this.getLength = function() {
@ -157,81 +167,74 @@ var Document = function(text) {
position.row = Math.max(0, length - 1);
position.column = this.getLine(length-1).length;
}
return position;
return position;
}
this.insert = function(position, text) {
if (text.length == 0)
return position;
position = this.$clipPosition(position);
if (this.getLength() <= 1)
this.$detectNewLine(text);
var newLines = this.$split(text);
var lines = this.$split(text);
var firstLine = lines.splice(0, 1)[0];
var lastLine = lines.length == 0 ? null : lines.splice(lines.length - 1, 1)[0];
if (this.isNewLine(text)) {
var end = this.insertNewLine(position);
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 || "");
}
else if (newLines.length == 1) {
var end = this.insertInLine(position, text);
}
else {
var end = this.insertInLine(position, newLines[0]);
this.insertNewLine(end);
if (newLines.length > 2)
this.insertLines(position.row+1, newLines.slice(1, newLines.length-1));
var end = this.insertInLine({row: position.row + newLines.length - 1, column: 0}, newLines[newLines.length-1]);
}
return end;
return position;
};
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 - 1, 0);
var range = new Range(row, 0, row + lines.length, 0);
var delta = {
action: "insertLines",
range: range,
lines: lines
};
this._dispatchEvent("change", { data: delta });
this._dispatchEvent("change", { data: delta });
return range.end;
},
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._dispatchEvent("change", { data: delta });
return end;
};
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);
@ -240,14 +243,14 @@ var Document = function(text) {
row : position.row,
column : position.column + text.length
};
var delta = {
action: "insertText",
range: Range.fromPoints(position, end),
text: text
};
this._dispatchEvent("change", { data: delta });
return end;
};
@ -258,35 +261,41 @@ var Document = function(text) {
if (range.isEmpty())
return range.start;
var firstRow = range.start.row;
var lastRow = range.end.row;
if (range.isMultiLine()) {
// TODO removeInLine can be optimized away!
this.removeInLine(lastRow, 0, range.end.column);
if (lastRow - firstRow >= 2)
this.removeLines(firstRow + 1, lastRow - 1);
this.removeInLine(firstRow, range.start.column, this.$lines[firstRow].length);
this.removeNewLine(range.start.row);
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;
};
this.removeInLine = function(row, startColumn, endColumn) {
if (startColumn == endColumn)
return;
var range = new Range(row, startColumn, row, endColumn);
var line = this.getLine(row);
var removed = line.substring(startColumn, endColumn);
var newLine = line.substring(0, startColumn) + line.substring(endColumn, line.length);
this.$lines.splice(row, 1, newLine);
var delta = {
action: "removeText",
range: range,
@ -295,18 +304,18 @@ var Document = function(text) {
this._dispatchEvent("change", { data: delta });
return range.start;
};
/**
* Removes a range of full lines
*
*
* @param firstRow {Integer} The first row to be removed
* @param lastRow {Integer} The last row to be removed
* @return {String[]} The removed lines
*/
this.removeLines = function(firstRow, lastRow) {
var range = new Range(firstRow, 0, lastRow, this.$lines[lastRow].length);
var range = new Range(firstRow, 0, lastRow + 1, 0);
var removed = this.$lines.splice(firstRow, lastRow - firstRow + 1);
var delta = {
action: "removeLines",
range: range,
@ -316,16 +325,16 @@ var Document = function(text) {
this._dispatchEvent("change", { data: delta });
return removed;
};
this.removeNewLine = function(row) {
var firstLine = this.getLine(row);
var secondLine = this.getLine(row+1);
var range = new Range(row, firstLine.length, row+1, 0);
var line = firstLine + secondLine;
this.$lines.splice(row, 2, line);
var delta = {
action: "removeText",
range: range,
@ -333,16 +342,16 @@ var Document = function(text) {
};
this._dispatchEvent("change", { data: delta });
};
this.replace = function(range, text) {
if (text.length == 0 && range.isEmpty())
return range.start;
// 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);
@ -350,40 +359,40 @@ var Document = function(text) {
else {
end = range.start;
}
return end;
};
this.applyDeltas = function(deltas) {
for (var i=0; i<deltas.length; i++) {
var delta = 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)
this.removeLines(range.start.row, range.end.row - 1)
else if (delta.action == "removeText")
this.remove(range)
}
}
};
this.revertDeltas = function(deltas) {
for (var i=deltas.length-1; i>=0; i--) {
var delta = deltas[i];
var range = Range.fromPoints(delta.range.start, delta.range.end);
if (delta.action == "insertLines")
this.removeLines(range.start.row, range.end.row)
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)
}
}
};
}).call(Document.prototype);

View file

@ -45,23 +45,23 @@ var TextMode = require("ace/mode/text").Mode;
var Range = require("ace/range").Range;
var Document = require("ace/document").Document;
var NO_CHANGE_DELTAS = {};
var EditSession = function(text, mode) {
this.$modified = true;
this.selection = new Selection(this);
this.$breakpoints = [];
this.listeners = [];
if (mode) {
this.setMode(mode);
}
this.$frontMarkers = {};
this.$backMarkers = {};
this.$markerId = 1;
this.$wrapData = [];
if (text instanceof Document) {
this.setDocument(text);
} else {
this.setDocument(new Document(text));
}
this.selection = new Selection(this);
if (mode)
this.setMode(mode);
};
@ -88,12 +88,15 @@ var EditSession = function(text, mode) {
this.$deltas.push(delta);
this.$informUndoManager.schedule();
}
this.$updateWrapDataOnChange(e);
this._dispatchEvent("change", e);
};
this.setValue = function(text) {
this.doc.setValue(text);
this.$deltas = [];
this.doc.setValue(text);
this.$deltas = [];
this.$undoManager.reset();
};
this.getValue =
@ -128,7 +131,8 @@ var EditSession = function(text, mode) {
this.$defaultUndoManager = {
undo: function() {},
redo: function() {}
redo: function() {},
reset: function() {}
};
this.getUndoManager = function() {
@ -198,6 +202,79 @@ var EditSession = function(text, mode) {
this._dispatchEvent("changeBreakpoint", {});
};
this.getBreakpoints = function() {
return this.$breakpoints;
};
this.addMarker = function(range, clazz, type, inFront) {
var id = this.$markerId++;
var marker = {
range : range,
type : type || "line",
renderer: typeof type == "function" ? type : null,
clazz : clazz,
inFront: !!inFront
}
if (inFront) {
this.$frontMarkers[id] = marker;
this._dispatchEvent("changeFrontMarker")
} else {
this.$backMarkers[id] = marker;
this._dispatchEvent("changeBackMarker")
}
return id;
};
this.removeMarker = function(markerId) {
var marker = this.$frontMarkers[markerId] || this.$backMarkers[markerId];
if (!marker)
return;
var markers = marker.inFront ? this.$frontMarkers : this.$backMarkers;
if (marker) {
delete (markers[markerId]);
this._dispatchEvent(marker.inFront ? "changeFrontMarker" : "changeBackMarker");
}
};
this.getMarkers = function(inFront) {
return inFront ? this.$frontMarkers : this.$backMarkers;
};
/**
* Error:
* {
* row: 12,
* column: 2, //can be undefined
* text: "Missing argument",
* type: "error" // or "warning" or "info"
* }
*/
this.setAnnotations = function(annotations) {
this.$annotations = {};
for (var i=0; i<annotations.length; i++) {
var annotation = annotations[i];
var row = annotation.row;
if (this.$annotations[row])
this.$annotations[row].push(annotation);
else
this.$annotations[row] = [annotation];
}
this._dispatchEvent("changeAnnotation", {});
};
this.getAnnotations = function() {
return this.$annotations;
};
this.clearAnnotations = function() {
this.$annotations = {};
this._dispatchEvent("changeAnnotation", {});
};
this.$detectNewLine = function(text) {
var match = text.match(/^.*?(\r?\n)/m);
if (match) {
@ -208,7 +285,7 @@ var EditSession = function(text, mode) {
};
this.tokenRe = /^[\w\d]+/g;
this.nonTokenRe = /^[^\w\d]+/g;
this.nonTokenRe = /^(?:[^\w\d]|[\u3040-\u309F]|[\u30A0-\u30FF]|[\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF])+/g;
this.getWordRange = function(row, column) {
var line = this.getLine(row);
@ -253,6 +330,14 @@ var EditSession = function(text, mode) {
this.setMode = function(mode) {
if (this.$mode === mode) return;
if (this.$worker)
this.$worker.terminate();
if (window.Worker && !require.noWorker)
this.$worker = mode.createWorker(this);
else
this.$worker = null;
this.$mode = mode;
this._dispatchEvent("changeMode");
};
@ -286,8 +371,8 @@ var EditSession = function(text, mode) {
return this.screenWidth;
};
this.$computeWidth = function() {
if (this.$modified) {
this.$computeWidth = function(force) {
if (this.$modified || force) {
this.$modified = false;
var lines = this.doc.getAllLines();
@ -306,7 +391,12 @@ var EditSession = function(text, mode) {
longestScreenLine = Math.max(longestScreenLine, len);
}
this.width = longestLine;
this.screenWidth = longestScreenLine;
if (this.$useWrapMode) {
this.screenWidth = this.$wrapLimit;
} else {
this.screenWidth = longestScreenLine;
}
}
};
@ -433,52 +523,10 @@ var EditSession = function(text, mode) {
return this.doc.insert(position, text);
};
/**
* @param rows Array[Integer] sorted list of rows
*/
this.multiRowInsert = function(rows, column, text) {
for (var i=rows.length-1; i>=0; i--) {
var row = rows[i];
if (row >= this.doc.getLength())
continue;
var diff = column - this.doc.getLine(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);
}
return {
rows: end ? end.row - rows[0] : 0,
columns: end ? end.column - column : 0
};
};
this.remove = function(range) {
return this.doc.remove(range);
};
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.doc.getLength())
continue;
var end = this.remove(new Range(row, range.start.column, row+height, range.end.column));
}
};
this.undoChanges = function(deltas) {
if (!deltas.length)
return;
@ -487,15 +535,7 @@ var EditSession = function(text, mode) {
this.doc.revertDeltas(deltas);
this.$fromUndo = false;
// update the selection
var firstDelta = deltas[0];
var lastDelta = deltas[deltas.length-1];
this.selection.clearSelection();
if (firstDelta.action == "insertText" || firstDelta.action == "insertLines")
this.selection.moveCursorToPosition(firstDelta.range.start);
if (firstDelta.action == "removeText" || firstDelta.action == "removeLines")
this.selection.setSelectionRange(Range.fromPoints(firstDelta.range.start, lastDelta.range.end));
this.$setUndoSelection(deltas, true);
},
this.redoChanges = function(deltas) {
@ -506,15 +546,53 @@ var EditSession = function(text, mode) {
this.doc.applyDeltas(deltas);
this.$fromUndo = false;
// update the selection
var firstDelta = deltas[0];
var lastDelta = deltas[deltas.length-1];
this.$setUndoSelection(deltas, false);
},
this.$setUndoSelection = function(deltas, isUndo) {
// invert deltas is they are an undo
if (isUndo)
deltas = deltas.map(function(delta) {
var d = {
range: delta.range
}
if (delta.action == "insertText" || delta.action == "insertLines")
d.action = "removeText"
else
d.action = "insertText"
return d;
}).reverse();
var actions = [{}];
// collapse insert and remove operations
for (var i=0; i<deltas.length; i++) {
var delta = deltas[i];
var isInsert = delta.action == "insertText" || delta.action == "insertLines";
var action = actions[actions.length-1];
if (action.isInsert !== isInsert) {
actions.push({
isInsert: isInsert,
start: isInsert ? delta.range.start : delta.range.end,
end: isInsert ? delta.range.end : delta.range.start
})
}
else {
if (isInsert)
action.end = delta.range.end;
else
action.start = delta.range.start;
}
}
// update selection based on last operation
this.selection.clearSelection();
if (firstDelta.action == "insertText" ||firstDelta.action == "insertLines")
this.selection.setSelectionRange(Range.fromPoints(firstDelta.range.start, lastDelta.range.end));
if (firstDelta.action == "removeText" ||firstDelta.action == "removeLines")
this.selection.moveCursorToPosition(firstDelta.range.start);
var action = actions[actions.length-1];
if (action.isInsert)
this.selection.setSelectionRange(Range.fromPoints(action.start, action.end));
else
this.selection.moveCursorToPosition(action.end);
},
this.replace = function(range, text) {
@ -526,7 +604,6 @@ var EditSession = function(text, mode) {
for (var row=startRow; row<=endRow; row++) {
this.insert({row: row, column:0}, indentString);
}
return indentString.length;
};
this.outdentRows = function (range) {
@ -549,13 +626,8 @@ var EditSession = function(text, mode) {
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);
}
return range;
};
this.moveLinesUp = function(firstRow, lastRow) {
@ -589,53 +661,563 @@ var EditSession = function(text, mode) {
return Math.max(0, Math.min(row, this.doc.getLength()-1));
};
this.documentToScreenColumn = function(row, docColumn) {
// WRAPMODE
this.$wrapLimit = 80;
this.$useWrapMode = false;
this.$wrapLimitRange = {
min : null,
max : null
};
this.setUseWrapMode = function(useWrapMode) {
if (useWrapMode != this.$useWrapMode) {
this.$useWrapMode = useWrapMode;
this.$modified = true;
// If wrapMode is activaed, the wrapData array has to be initialized.
if (useWrapMode) {
var len = this.getLength();
this.$wrapData = [];
for (i = 0; i < len; i++) {
this.$wrapData.push([]);
}
this.$updateWrapData(0, len - 1);
}
this._dispatchEvent("changeWrapMode");
}
};
this.getUseWrapMode = function() {
return this.$useWrapMode;
};
// Allow the wrap limit to move freely between min and max. Either
// parameter can be null to allow the wrap limit to be unconstrained
// in that direction. Or set both parameters to the same number to pin
// the limit to that value.
this.setWrapLimitRange = function(min, max) {
if (this.$wrapLimitRange.min !== min || this.$wrapLimitRange.max !== max) {
this.$wrapLimitRange.min = min;
this.$wrapLimitRange.max = max;
this.$modified = true;
// This will force a recalculation of the wrap limit
this._dispatchEvent("changeWrapMode");
}
};
// This should generally only be called by the renderer when a resize
// is detected.
this.adjustWrapLimit = function(desiredLimit) {
var wrapLimit = this.$constrainWrapLimit(desiredLimit);
if (wrapLimit != this.$wrapLimit && wrapLimit > 0) {
this.$wrapLimit = wrapLimit;
this.$modified = true;
if (this.$useWrapMode) {
this.$updateWrapData(0, this.getLength() - 1);
this._dispatchEvent("changeWrapLimit");
}
return true;
}
return false;
};
this.$constrainWrapLimit = function(wrapLimit) {
var min = this.$wrapLimitRange.min;
if (min)
wrapLimit = Math.max(min, wrapLimit);
var max = this.$wrapLimitRange.max;
if (max)
wrapLimit = Math.min(max, wrapLimit);
// What would a limit of 0 even mean?
return Math.max(1, wrapLimit);
};
this.getWrapLimit = function() {
return this.$wrapLimit;
};
this.getWrapLimitRange = function() {
// Avoid unexpected mutation by returning a copy
return {
min : this.$wrapLimitRange.min,
max : this.$wrapLimitRange.max
};
};
this.$updateWrapDataOnChange = function(e) {
if (!this.$useWrapMode) {
return;
}
var len;
var action = e.data.action;
var firstRow = e.data.range.start.row,
lastRow = e.data.range.end.row;
if (action.indexOf("Lines") != -1) {
if (action == "insertLines") {
lastRow = firstRow + (e.data.lines.length);
} else {
lastRow = firstRow;
}
len = e.data.lines.length;
} else {
len = lastRow - firstRow;
}
if (len != 0) {
if (action.indexOf("remove") != -1) {
this.$wrapData.splice(firstRow, len);
lastRow = firstRow;
} else {
var args = [firstRow, 0];
for (var i = 0; i < len; i++) args.push([]);
this.$wrapData.splice.apply(this.$wrapData, args);
}
}
if (this.$wrapData.length != this.doc.$lines.length) {
console.error("The length of doc.$lines and $wrapData have to be the same!");
}
this.$updateWrapData(firstRow, lastRow);
};
this.$updateWrapData = function(firstRow, lastRow) {
var lines = this.doc.getAllLines();
var tabSize = this.getTabSize();
var wrapData = this.$wrapData;
var wrapLimit = this.$wrapLimit;
for (var row = firstRow; row <= lastRow; row++) {
wrapData[row] =
this.$computeWrapSplits(lines[row], wrapLimit, tabSize);
}
};
// "Tokens"
var CHAR = 1,
CHAR_EXT = 2,
SPACE = 3,
TAB = 4,
TAB_SPACE = 5;
this.$computeWrapSplits = function(textLine, wrapLimit, tabSize) {
textLine = textLine.trimRight();
if (textLine.length == 0) {
return [];
}
var tabSize = this.getTabSize();
var splits = [];
var tokens = this.$getDisplayTokens(textLine);
var displayLength = tokens.length;
var lastSplit = 0, lastDocSplit = 0;
function addSplit(screenPos) {
var displayed = tokens.slice(lastSplit, screenPos);
// The document size is the current size - the extra width for tabs
// and multipleWidth characters.
var len = displayed.length;
displayed.join("").
// Get all the tabs.
replace(/4/g, function(m) {
len -= tabSize - 1;
}).
// Get all the multipleWidth characters.
replace(/2/g, function(m) {
len -= 1;
});
lastDocSplit += len;
splits.push(lastDocSplit);
lastSplit = screenPos;
}
while (displayLength - lastSplit > wrapLimit) {
// This is, where the split should be.
var split = lastSplit + wrapLimit;
// If there is a space or tab at this split position.
if (tokens[split] >= SPACE) {
// Include all following spaces + tabs in this split as well.
while (tokens[split] >= SPACE) {
split ++;
}
addSplit(split);
} else {
// Search for the first non space/tab token.
for (split; split != lastSplit - 1; split--) {
if (tokens[split] >= SPACE) {
split++;
break;
}
}
// If we found one, then add the split.
if (split > lastSplit) {
addSplit(split);
}
// No space or tab around? Well, force a split then.
else {
addSplit(lastSplit + wrapLimit);
}
}
}
return splits;
}
this.$getDisplayTokens = function(str) {
var arr = [];
var tabSize = this.getTabSize();
for (var i = 0; i < str.length; i++) {
var c = str.charCodeAt(i);
// Tab
if (c == 9) {
arr.push(TAB);
for (var n = 1; n < tabSize; n++) {
arr.push(TAB_SPACE);
}
}
// Space
else if(c == 32) {
arr.push(SPACE);
}
// CJK characters
else if (
c >= 0x3040 && c <= 0x309F || // Hiragana
c >= 0x30A0 && c <= 0x30FF || // Katakana
c >= 0x4E00 && c <= 0x9FFF || // Single CJK ideographs
c >= 0xF900 && c <= 0xFAFF ||
c >= 0x3400 && c <= 0x4DBF
) {
arr.push(CHAR, CHAR_EXT);
} else {
arr.push(CHAR);
}
}
return arr;
}
/**
* Calculates the width of the a string on the screen while assuming that
* the string starts at the first column on the screen.
*
* @param string str String to calculate the screen width of
* @return int number of columns for str on screen.
*/
this.$getStringScreenWidth = function(str) {
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;
for (var i=0; i<str.length; i++) {
var c = str.charCodeAt(i);
// tab
if (c == 9) {
screenColumn += tabSize;
}
// CJK characters
else if (
c >= 0x3040 && c <= 0x309F || // Hiragana
c >= 0x30A0 && c <= 0x30FF || // Katakana
c >= 0x4E00 && c <= 0x9FFF || // Single CJK ideographs
c >= 0xF900 && c <= 0xFAFF ||
c >= 0x3400 && c <= 0x4DBF
) {
screenColumn += 2;
} else {
screenColumn += 1;
}
}
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);
return screenColumn;
}
this.getRowHeight = function(config, row) {
var rows;
if (!this.$useWrapMode || !this.$wrapData[row]) {
rows = 1;
} else {
rows = this.$wrapData[row].length + 1;
}
return rows * config.lineHeight;
}
this.getScreenLastRowColumn = function(screenRow, returnDocPosition) {
if (!this.$useWrapMode) {
return this.$getStringScreenWidth(this.getLine(screenRow));
}
var rowData = this.$screenToDocumentRow(screenRow);
var docRow = rowData[0],
row = rowData[1];
var start, end;
if (this.$wrapData[docRow][row]) {
start = (this.$wrapData[docRow][row - 1] || 0);
end = this.$wrapData[docRow][row];
returnDocPosition && end--;
} else {
end = this.getLine(docRow).length;
start = (this.$wrapData[docRow][row - 1] || 0);
}
if (!returnDocPosition) {
return this.$getStringScreenWidth(this.getLine(docRow).substring(start, end));
} else {
return end;
}
};
this.getDocumentLastRowColumn = function(docRow, docColumn) {
if (!this.$useWrapMode) {
return this.getLine(docRow).length;
}
var screenRow = this.documentToScreenRow(docRow, docColumn);
return this.getScreenLastRowColumn(screenRow, true);
}
this.getScreenFirstRowColumn = function(screenRow) {
if (!this.$useWrapMode) {
return 0;
}
var rowData = this.$screenToDocumentRow(screenRow);
var docRow = rowData[0],
row = rowData[1];
return this.$wrapData[docRow][row - 1] || 0;
};
this.getRowSplitData = function(row) {
if (!this.$useWrapMode) {
return undefined;
} else {
return this.$wrapData[row];
}
};
/**
*
* @returns array
* - array[0]: The documentRow equivalent.
* - array[1]: The screenRowOffset to the first documentRow on the screen.
*/
this.$screenToDocumentRow = function(row) {
if (!this.$useWrapMode) {
return [row, 0];
}
var wrapData = this.$wrapData, linesCount = this.getLength();
var docRow = 0;
while (docRow < linesCount && row >= wrapData[docRow].length + 1) {
row -= wrapData[docRow].length + 1;
docRow ++;
}
return [docRow, row];
};
this.screenToDocumentRow = function(screenRow) {
return this.$screenToDocumentRow(screenRow)[0];
};
this.screenToDocumentColumn = function(screenRow, screenColumn) {
return this.screenToDocumentPosition(screenRow, screenColumn).column;
};
this.screenToDocumentPosition = function(row, column) {
var line;
var docRow;
var docColumn;
var remaining = column;
var linesCount = this.getLength();
if (!this.$useWrapMode) {
docRow = row >= linesCount? linesCount-1 : (row < 0 ? 0 : row);
row = 0;
docColumn = 0;
line = this.getLine(docRow);
} else {
var wrapData = this.$wrapData;
var docRow = 0;
while (docRow < linesCount && row >= wrapData[docRow].length + 1) {
row -= wrapData[docRow].length + 1;
docRow ++;
}
if (docRow >= linesCount) {
docRow = linesCount-1
row = wrapData[docRow].length;
}
else if (remaining > len){
docColumn += len;
break;
}
else {
docColumn += remaining;
docColumn = wrapData[docRow][row - 1] || 0;
line = this.getLine(docRow).substring(docColumn);
}
var tabSize = this.getTabSize();
for(var i = 0; i < line.length; i++) {
var c = line.charCodeAt(i);
if (remaining > 0) {
docColumn += 1;
// tab
if (c == 9) {
if (remaining >= tabSize) {
remaining -= tabSize;
} else {
remaining = 0;
docColumn -= 1;
}
}
// CJK characters
else if (
c >= 0x3040 && c <= 0x309F || // Hiragana
c >= 0x30A0 && c <= 0x30FF || // Katakana
c >= 0x4E00 && c <= 0x9FFF || // Single CJK ideographs
c >= 0xF900 && c <= 0xFAFF ||
c >= 0x3400 && c <= 0x4DBF
) {
if (remaining >= 2) {
remaining -= 2;
} else {
remaining = 0;
docColumn -= 1;
}
} else {
remaining -= 1;
}
} else {
break;
}
}
return docColumn;
// Clamp docColumn.
if (this.$useWrapMode) {
column = wrapData[docRow][row]
if (docColumn >= column) {
// We remove one character at the end such that the docColumn
// position returned is not associated to the next row on the
// screen.
docColumn = column - 1;
}
} else if (line) {
docColumn = Math.min(docColumn, line.length);
}
return {
row: docRow,
column: docColumn
};
};
this.documentToScreenColumn = function(row, docColumn) {
return this.documentToScreenPosition(row, docColumn).column;
};
/**
*
* @return array[2]
* - array[0]: The number of the row on the screen (aka screenRow)
* - array[1]: The number of rows from the first docRow on the screen
* (aka screenRowOffset);
*/
this.$documentToScreenRow = function(docRow, docColumn) {
if (!this.$useWrapMode) {
return [docRow, 0];
}
var wrapData = this.$wrapData;
var screenRow = 0;
// Handle special case where the row is outside of the range of lines.
if (docRow > wrapData.length - 1) {
return [
this.getScreenLength(),
wrapData.length == 0 ? 0 : (wrapData[wrapData.length - 1].length - 1)
];
}
for (var i = 0; i < docRow; i++) {
screenRow += wrapData[i].length + 1;
}
var screenRowOffset = 0;
while (docColumn >= wrapData[docRow][screenRowOffset]) {
screenRow ++;
screenRowOffset++;
}
return [screenRow, screenRowOffset];
}
this.documentToScreenRow = function(docRow, docColumn) {
return this.$documentToScreenRow(docRow, docColumn)[0];
}
this.documentToScreenPosition = function(pos, column) {
var str;
var tabSize = this.getTabSize();
// Normalize the passed in arguments.
var row;
if (column != null) {
row = pos;
} else {
row = pos.row;
column = pos.column;
}
if (!this.$useWrapMode) {
str = this.getLine(row).substring(0, column);
column = this.$getStringScreenWidth(str);
return {
row: row,
column: column
};
}
var rowData = this.$documentToScreenRow(row, column);
var screenRow = rowData[0];
if (row >= this.getLength()) {
return {
row: screenRow,
column: 0
};
}
var split;
var wrapRowData = this.$wrapData[row];
var screenColumn;
var screenRowOffset = rowData[1];
str = this.getLine(row).substring(
wrapRowData[screenRowOffset - 1] || 0, column);
screenColumn = this.$getStringScreenWidth(str);
return {
row: screenRow,
column: screenColumn
};
};
this.getScreenLength = function() {
if (!this.$useWrapMode) {
return this.getLength();
}
var screenRows = 0;
for (var row = 0; row < this.$wrapData.length; row++) {
screenRows += this.$wrapData[row].length + 1;
}
return screenRows;
}
}).call(EditSession.prototype);
exports.EditSession = EditSession;

View file

@ -1,4 +1,5 @@
/* ***** BEGIN LICENSE BLOCK *****
/* vim:ts=4:sts=4:sw=4:
* ***** 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
@ -20,6 +21,8 @@
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
* Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)
* Julian Viereck <julian.viereck@gmail.com>
*
* 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
@ -37,10 +40,15 @@
define(function(require, exports, module) {
require("pilot/fixoldbrowsers");
var oop = require("pilot/oop");
var event = require("pilot/event");
var lang = require("pilot/lang");
var useragent = require("pilot/useragent");
var TextInput = require("ace/keyboard/textinput").TextInput;
var MouseHandler = require("ace/mouse_handler").MouseHandler;
//var TouchHandler = require("ace/touch_handler").TouchHandler;
var KeyBinding = require("ace/keyboard/keybinding").KeyBinding;
var EditSession = require("ace/edit_session").EditSession;
var Search = require("ace/search").Search;
@ -53,33 +61,22 @@ var Editor =function(renderer, session) {
this.container = container;
this.renderer = renderer;
this.textInput = new TextInput(container, this);
this.textInput = new TextInput(renderer.getTextAreaContainer(), this);
this.keyBinding = new KeyBinding(this);
var self = this;
event.addListener(container, "mousedown", function(e) {
setTimeout(function() {self.focus();});
return event.preventDefault(e);
});
event.addListener(container, "selectstart", function(e) {
return event.preventDefault(e);
});
var mouseTarget = renderer.getMouseEventTarget();
event.addListener(mouseTarget, "mousedown", this.onMouseDown.bind(this));
event.addMultiMouseDownListener(mouseTarget, 0, 2, 500, this.onMouseDoubleClick.bind(this));
event.addMultiMouseDownListener(mouseTarget, 0, 3, 600, this.onMouseTripleClick.bind(this));
event.addMouseWheelListener(mouseTarget, this.onMouseWheel.bind(this));
this.$selectionMarker = null;
this.$highlightLineMarker = null;
this.$blockScrolling = false;
// TODO detect touch event support
if (useragent.isIPad) {
//this.$mouseHandler = new TouchHandler(this);
} else {
this.$mouseHandler = new MouseHandler(this);
}
this.$blockScrolling = 0;
this.$search = new Search().set({
wrap: true
});
this.setSession(session || new EditSession(""));
this.focus();
};
(function(){
@ -122,10 +119,16 @@ var Editor =function(renderer, session) {
if (this.session == session) return;
if (this.session) {
var oldSession = this.session;
this.session.removeEventListener("change", this.$onDocumentChange);
this.session.removeEventListener("changeMode", this.$onDocumentModeChange);
this.session.removeEventListener("changeTabSize", this.$onDocumentChangeTabSize);
this.session.removeEventListener("changeWrapLimit", this.$onDocumentChangeWrapLimit);
this.session.removeEventListener("changeWrapMode", this.$onDocumentChangeWrapMode);
this.session.removeEventListener("changeFrontMarker", this.$onChangeFrontMarker);
this.session.removeEventListener("changeBackMarker", this.$onChangeBackMarker);
this.session.removeEventListener("changeBreakpoint", this.$onDocumentChangeBreakpoint);
this.session.removeEventListener("changeAnnotation", this.$onDocumentChangeAnnotation);
var selection = this.session.getSelection();
selection.removeEventListener("changeCursor", this.$onCursorChange);
@ -146,11 +149,25 @@ var Editor =function(renderer, session) {
this.$onDocumentChangeTabSize = this.renderer.updateText.bind(this.renderer);
session.addEventListener("changeTabSize", this.$onDocumentChangeTabSize);
this.$onDocumentChangeWrapLimit = this.onDocumentChangeWrapLimit.bind(this);
session.addEventListener("changeWrapLimit", this.$onDocumentChangeWrapLimit);
this.$onDocumentChangeWrapMode = this.onDocumentChangeWrapMode.bind(this);
session.addEventListener("changeWrapMode", this.$onDocumentChangeWrapMode);
this.$onChangeFrontMarker = this.onChangeFrontMarker.bind(this);
this.session.addEventListener("changeFrontMarker", this.$onChangeFrontMarker);
this.$onChangeBackMarker = this.onChangeBackMarker.bind(this);
this.session.addEventListener("changeBackMarker", this.$onChangeBackMarker);
this.$onDocumentChangeBreakpoint = this.onDocumentChangeBreakpoint.bind(this);
this.session.addEventListener("changeBreakpoint", this.$onDocumentChangeBreakpoint);
this.$onDocumentChangeAnnotation = this.onDocumentChangeAnnotation.bind(this);
this.session.addEventListener("changeAnnotation", this.$onDocumentChangeAnnotation);
this.selection = session.getSelection();
this.$desiredColumn = 0;
this.$onCursorChange = this.onCursorChange.bind(this);
this.selection.addEventListener("changeCursor", this.$onCursorChange);
@ -164,9 +181,17 @@ var Editor =function(renderer, session) {
this.onCursorChange();
this.onSelectionChange();
this.onChangeFrontMarker();
this.onChangeBackMarker();
this.onDocumentChangeBreakpoint();
this.onDocumentChangeAnnotation();
this.renderer.scrollToRow(session.getScrollTopRow());
this.renderer.updateFull();
this._dispatchEvent("changeSession", {
session: session,
oldSession: oldSession
});
};
this.getSession = function() {
@ -185,10 +210,18 @@ var Editor =function(renderer, session) {
this.renderer.setTheme(theme);
};
this.setStyle = function(style) {
this.renderer.setStyle(style)
};
this.unsetStyle = function(style) {
this.renderer.unsetStyle(style)
}
this.$highlightBrackets = function() {
if (this.$bracketHighlight) {
this.renderer.removeMarker(this.$bracketHighlight);
this.$bracketHighlight = null;
if (this.session.$bracketHighlight) {
this.session.removeMarker(this.session.$bracketHighlight);
this.session.$bracketHighlight = null;
}
if (this.$highlightPending) {
@ -204,12 +237,19 @@ var Editor =function(renderer, session) {
var pos = self.session.findMatchingBracket(self.getCursorPosition());
if (pos) {
var range = new Range(pos.row, pos.column, pos.row, pos.column+1);
self.$bracketHighlight = self.renderer.addMarker(range, "ace_bracket");
self.session.$bracketHighlight = self.session.addMarker(range, "ace_bracket");
}
}, 10);
};
this.focus = function() {
// Safari need the timeout
// iOS and Firefox need it called immediately
// to be on the save side we do both
var _self = this;
setTimeout(function() {
_self.textInput.focus();
});
this.textInput.focus();
};
@ -220,21 +260,23 @@ var Editor =function(renderer, session) {
this.onFocus = function() {
this.renderer.showCursor();
this.renderer.visualizeFocus();
this._dispatchEvent("focus");
};
this.onBlur = function() {
this.renderer.hideCursor();
this.renderer.visualizeBlur();
this._dispatchEvent("blur");
};
this.onDocumentChange = function(e) {
var delta = e.data;
var range = delta.range;
this.bgTokenizer.start(range.start.row);
if (range.start.row == range.end.row)
if (range.start.row == range.end.row && delta.action != "insertLines" && delta.action != "removeLines")
var lastRow = range.end.row;
else
else
lastRow = Infinity;
this.renderer.updateLines(range.start.row, lastRow);
@ -248,47 +290,71 @@ var Editor =function(renderer, session) {
};
this.onCursorChange = function(e) {
this.$highlightBrackets();
this.renderer.updateCursor(this.getCursorPosition(), this.$overwrite);
if (!this.$blockScrolling && (!e || !e.blockScrolling)) {
if (!this.$blockScrolling) {
this.renderer.scrollCursorIntoView();
}
// move text input over the cursor
// this is required for iOS and IME
this.renderer.moveTextAreaToCursor(this.textInput.getElement());
this.$highlightBrackets();
this.$updateHighlightActiveLine();
};
this.$updateHighlightActiveLine = function() {
if (this.$highlightLineMarker) {
this.renderer.removeMarker(this.$highlightLineMarker);
var session = this.getSession();
if (session.$highlightLineMarker) {
session.removeMarker(session.$highlightLineMarker);
}
this.$highlightLineMarker = null;
session.$highlightLineMarker = null;
if (this.getHighlightActiveLine() && (this.getSelectionStyle() != "line" || !this.selection.isMultiLine())) {
var cursor = this.getCursorPosition();
var range = new Range(cursor.row, 0, cursor.row+1, 0);
this.$highlightLineMarker = this.renderer.addMarker(range, "ace_active_line", "line");
session.$highlightLineMarker = session.addMarker(range, "ace_active_line", "line");
}
};
this.onSelectionChange = function(e) {
if (this.$selectionMarker) {
this.renderer.removeMarker(this.$selectionMarker);
var session = this.getSession();
if (session.$selectionMarker) {
session.removeMarker(session.$selectionMarker);
}
this.$selectionMarker = null;
session.$selectionMarker = null;
if (!this.selection.isEmpty()) {
var range = this.selection.getRange();
var style = this.getSelectionStyle();
this.$selectionMarker = this.renderer.addMarker(range, "ace_selection", style);
session.$selectionMarker = session.addMarker(range, "ace_selection", style);
}
this.onCursorChange(e);
if (this.$highlightSelectedWord)
this.mode.highlightSelection(this);
};
this.onChangeFrontMarker = function() {
this.renderer.updateFrontMarkers();
};
this.onChangeBackMarker = function() {
this.renderer.updateBackMarkers();
};
this.onDocumentChangeBreakpoint = function() {
this.renderer.setBreakpoints(this.session.getBreakpoints());
};
this.onDocumentChangeAnnotation = function() {
this.renderer.setAnnotations(this.session.getAnnotations());
};
this.onDocumentModeChange = function() {
var mode = this.session.getMode();
if (this.mode == mode)
@ -308,94 +374,13 @@ var Editor =function(renderer, session) {
this.renderer.setTokenizer(this.bgTokenizer);
};
this.onMouseDown = function(e) {
var pageX = event.getDocumentX(e);
var pageY = event.getDocumentY(e);
var pos = this.renderer.screenToTextCoordinates(pageX, pageY);
pos.row = Math.max(0, Math.min(pos.row, this.session.getLength()-1));
if (event.getButton(e) != 0) {
if (this.selection.isEmpty()) {
this.moveCursorToPosition(pos);
}
return;
}
if (e.shiftKey)
this.selection.selectToPosition(pos)
else {
this.moveCursorToPosition(pos);
if (!this.$clickSelection)
this.selection.clearSelection(pos.row, pos.column);
}
this.renderer.scrollCursorIntoView();
var self = this;
var mousePageX, mousePageY;
var onMouseSelection = function(e) {
mousePageX = event.getDocumentX(e);
mousePageY = event.getDocumentY(e);
};
var onMouseSelectionEnd = function() {
clearInterval(timerId);
self.$clickSelection = null;
};
var onSelectionInterval = function() {
if (mousePageX === undefined || mousePageY === undefined)
return;
var cursor = self.renderer.screenToTextCoordinates(mousePageX, mousePageY);
cursor.row = Math.max(0, Math.min(cursor.row, self.session.getLength()-1));
if (self.$clickSelection) {
if (self.$clickSelection.contains(cursor.row, cursor.column)) {
self.selection.setSelectionRange(self.$clickSelection);
} else {
if (self.$clickSelection.compare(cursor.row, cursor.column) == -1) {
var anchor = self.$clickSelection.end;
} else {
var anchor = self.$clickSelection.start;
}
self.selection.setSelectionAnchor(anchor.row, anchor.column);
self.selection.selectToPosition(cursor);
}
}
else {
self.selection.selectToPosition(cursor);
}
self.renderer.scrollCursorIntoView();
};
event.capture(this.container, onMouseSelection, onMouseSelectionEnd);
var timerId = setInterval(onSelectionInterval, 20);
return event.preventDefault(e);
this.onDocumentChangeWrapLimit = function() {
this.renderer.updateCursor(this.getCursorPosition(), this.$overwrite);
this.renderer.updateFull();
};
this.onMouseDoubleClick = function(e) {
this.selection.selectWord();
this.$clickSelection = this.getSelectionRange();
this.$updateDesiredColumn();
};
this.onMouseTripleClick = function(e) {
this.selection.selectLine();
this.$clickSelection = this.getSelectionRange();
this.$updateDesiredColumn();
};
this.onMouseWheel = function(e) {
var speed = this.$scrollSpeed * 2;
this.renderer.scrollBy(e.wheelX * speed, e.wheelY * speed);
return event.preventDefault(e);
this.onDocumentChangeWrapMode = function() {
this.renderer.onResize(true);
};
this.getCopyText = function() {
@ -412,7 +397,7 @@ var Editor =function(renderer, session) {
return;
if (!this.selection.isEmpty()) {
this.moveCursorToPosition(this.session.remove(this.getSelectionRange()));
this.session.remove(this.getSelectionRange())
this.clearSelection();
}
};
@ -442,20 +427,18 @@ var Editor =function(renderer, session) {
var lineIndent = this.mode.getNextLineIndent(lineState, line.slice(0, cursor.column), this.session.getTabString());
var end = this.session.insert(cursor, text);
/* TODO: This shortcut is somehow broken
if (!shouldOutdent && line != this.session.getLine(row) && text != "\n") {
this.moveCursorToPosition(end);
this.renderer.scrollCursorIntoView();
return;
}
*/
var lineState = this.bgTokenizer.getState(cursor.row);
// multi line insert
if (cursor.row !== end.row) {
// TODO disabled multiline auto indent
// possibly doing the indent before inserting the text
// if (cursor.row !== end.row) {
if (this.session.getDocument().isNewLine(text)) {
this.moveCursorTo(cursor.row+1, 0);
var size = this.session.getTabSize(),
minIndent = Number.MAX_VALUE;
for (var row = cursor.row + 1; row <= end.row; ++row) {
var indent = 0;
@ -480,17 +463,14 @@ var Editor =function(renderer, session) {
outdent -= size;
else if (line.charAt(i) == ' ')
outdent -= 1;
this.session.replace(new Range(row, 0, row, line.length), line.substr(i));
this.session.remove(new Range(row, 0, row, i));
}
end.column += this.session.indentRows(cursor.row + 1, end.row, lineIndent);
this.session.indentRows(cursor.row + 1, end.row, lineIndent);
} else {
if (shouldOutdent) {
end.column += this.mode.autoOutdent(lineState, this.session, cursor.row);
this.mode.autoOutdent(lineState, this.session, cursor.row);
}
}
this.moveCursorToPosition(end);
this.renderer.scrollCursorIntoView();
};
}
this.onTextInput = function(text) {
@ -507,9 +487,9 @@ var Editor =function(renderer, session) {
this.$overwrite = overwrite;
this.$blockScrolling = true;
this.$blockScrolling += 1;
this.onCursorChange();
this.$blockScrolling = false;
this.$blockScrolling -= 1;
this._dispatchEvent("changeOverwrite", {data: overwrite});
};
@ -522,15 +502,13 @@ var Editor =function(renderer, session) {
this.setOverwrite(!this.$overwrite);
};
this.$scrollSpeed = 1;
this.setScrollSpeed = function(speed) {
this.$scrollSpeed = speed;
}
this.$mouseHandler.setScrollSpeed(speed);
};
this.getScrollSpeed = function() {
return this.$scrollSpeed;
}
return this.$mouseHandler.getScrollSpeed()
};
this.$selectionStyle = "line";
this.setSelectionStyle = function(style) {
@ -541,7 +519,6 @@ var Editor =function(renderer, session) {
this._dispatchEvent("changeSelectionStyle", {data: style});
};
this.getSelectionStyle = function() {
return this.$selectionStyle;
};
@ -558,6 +535,22 @@ var Editor =function(renderer, session) {
return this.$highlightActiveLine;
};
this.$highlightSelectedWord = true;
this.setHighlightSelectedWord = function(shouldHighlight) {
if (this.$highlightSelectedWord == shouldHighlight)
return;
this.$highlightSelectedWord = shouldHighlight;
if (shouldHighlight)
this.mode.highlightSelection(this);
else
this.mode.clearSelectionHighlight(this);
};
this.getHighlightSelectedWord = function() {
return this.$highlightSelectedWord;
};
this.setShowInvisibles = function(showInvisibles) {
if (this.getShowInvisibles() == showInvisibles)
return;
@ -601,7 +594,7 @@ var Editor =function(renderer, session) {
if (this.selection.isEmpty()) {
this.selection.selectRight();
}
this.moveCursorToPosition(this.session.remove(this.getSelectionRange()));
this.session.remove(this.getSelectionRange())
this.clearSelection();
};
@ -612,10 +605,93 @@ var Editor =function(renderer, session) {
if (this.selection.isEmpty())
this.selection.selectLeft();
this.moveCursorToPosition(this.session.remove(this.getSelectionRange()));
this.session.remove(this.getSelectionRange());
this.clearSelection();
};
this.removeWordRight = function() {
if (this.$readOnly)
return;
if (this.selection.isEmpty())
this.selection.selectWordRight();
this.session.remove(this.getSelectionRange());
this.clearSelection();
};
this.removeWordLeft = function() {
if (this.$readOnly)
return;
if (this.selection.isEmpty())
this.selection.selectWordLeft();
this.session.remove(this.getSelectionRange());
this.clearSelection();
};
this.removeToLineStart = function() {
if (this.$readOnly)
return;
if (this.selection.isEmpty())
this.selection.selectLineStart();
this.session.remove(this.getSelectionRange());
this.clearSelection();
};
this.removeToLineEnd = function() {
if (this.$readOnly)
return;
if (this.selection.isEmpty())
this.selection.selectLineEnd();
this.session.remove(this.getSelectionRange());
this.clearSelection();
};
this.splitLine = function() {
if (this.$readOnly)
return;
if (!this.selection.isEmpty()) {
this.session.remove(this.getSelectionRange());
this.clearSelection();
}
var cursor = this.getCursorPosition();
this.insert("\n");
this.moveCursorToPosition(cursor);
};
this.transposeLetters = function() {
if (this.$readOnly)
return;
if (!this.selection.isEmpty()) {
return;
}
var cursor = this.getCursorPosition();
var column = cursor.column;
if (column == 0)
return;
var line = this.session.getLine(cursor.row);
if (column < line.length) {
var swap = line.charAt(column) + line.charAt(column-1);
var range = new Range(cursor.row, column-1, cursor.row, column+1)
}
else {
var swap = line.charAt(column-1) + line.charAt(column-2);
var range = new Range(cursor.row, column-2, cursor.row, column)
}
this.session.replace(range, swap);
};
this.indent = function() {
if (this.$readOnly)
return;
@ -625,9 +701,7 @@ var Editor =function(renderer, session) {
if (range.start.row < range.end.row || range.start.column < range.end.column) {
var rows = this.$getSelectedRows();
var count = session.indentRows(rows.first, rows.last, "\t");
this.selection.shiftSelection(count);
session.indentRows(rows.first, rows.last, "\t");
} else {
var indentString;
@ -649,10 +723,7 @@ var Editor =function(renderer, session) {
return;
var selection = this.session.getSelection();
var range = this.session.outdentRows(selection.getRange());
selection.setSelectionRange(range, selection.isBackwards());
this.$updateDesiredColumn();
this.session.outdentRows(selection.getRange());
};
this.toggleCommentLines = function() {
@ -661,8 +732,7 @@ var Editor =function(renderer, session) {
var state = this.bgTokenizer.getState(this.getCursorPosition().row);
var rows = this.$getSelectedRows()
var addedColumns = this.mode.toggleCommentLines(state, this.session, rows.first, rows.last);
this.selection.shiftSelection(addedColumns);
this.mode.toggleCommentLines(state, this.session, rows.first, rows.last);
};
this.removeLines = function() {
@ -670,10 +740,7 @@ var Editor =function(renderer, session) {
return;
var rows = this.$getSelectedRows();
this.selection.setSelectionAnchor(rows.last+1, 0);
this.selection.selectTo(rows.first, 0);
this.session.remove(this.getSelectionRange());
this.session.remove(new Range(rows.first, 0, rows.last+1, 0));
this.clearSelection();
};
@ -738,7 +805,6 @@ var Editor =function(renderer, session) {
this.onCompositionStart = function(text) {
this.renderer.showComposition(this.getCursorPosition());
//this.onTextInput(text);
};
this.onCompositionUpdate = function(text) {
@ -747,7 +813,6 @@ var Editor =function(renderer, session) {
this.onCompositionEnd = function() {
this.renderer.hideComposition();
//this.removeLeft();
};
@ -763,142 +828,141 @@ var Editor =function(renderer, session) {
return (row >= this.getFirstVisibleRow() && row <= this.getLastVisibleRow());
};
this.getVisibleRowCount = function() {
return this.getLastVisibleRow() - this.getFirstVisibleRow() + 1;
this.$getVisibleRowCount = function() {
return this.renderer.getScrollBottomRow() - this.renderer.getScrollTopRow() + 1;
};
this.getPageDownRow = function() {
return this.renderer.getLastVisibleRow() - 1;
this.$getPageDownRow = function() {
return this.renderer.getScrollBottomRow();
};
this.getPageUpRow = function() {
var firstRow = this.renderer.getFirstVisibleRow();
var lastRow = this.renderer.getLastVisibleRow();
this.$getPageUpRow = function() {
var firstRow = this.renderer.getScrollTopRow();
var lastRow = this.renderer.getScrollBottomRow();
return firstRow - (lastRow - firstRow) + 1;
return firstRow - (lastRow - firstRow);
};
this.selectPageDown = function() {
var row = this.getPageDownRow() + Math.floor(this.getVisibleRowCount() / 2);
var row = this.$getPageDownRow() + Math.floor(this.$getVisibleRowCount() / 2);
this.scrollPageDown();
var selection = this.getSelection();
selection.$moveSelection(function() {
selection.moveCursorTo(row, selection.getSelectionLead().column);
});
var leadScreenPos = this.session.documentToScreenPosition(selection.getSelectionLead());
var dest = this.session.screenToDocumentPosition(row, leadScreenPos.column);
selection.selectTo(dest.row, dest.column);
};
this.selectPageUp = function() {
var visibleRows = this.getLastVisibleRow() - this.getFirstVisibleRow();
var row = this.getPageUpRow() + Math.round(visibleRows / 2);
var visibleRows = this.renderer.getScrollTopRow() - this.renderer.getScrollBottomRow();
var row = this.$getPageUpRow() + Math.round(visibleRows / 2);
this.scrollPageUp();
var selection = this.getSelection();
selection.$moveSelection(function() {
selection.moveCursorTo(row, selection.getSelectionLead().column);
});
var leadScreenPos = this.session.documentToScreenPosition(selection.getSelectionLead());
var dest = this.session.screenToDocumentPosition(row, leadScreenPos.column);
selection.selectTo(dest.row, dest.column);
};
this.gotoPageDown = function() {
var row = this.getPageDownRow(),
column = Math.min(this.getCursorPosition().column,
this.session.getLine(row).length);
var row = this.$getPageDownRow();
var column = this.getCursorPositionScreen().column;
this.scrollToRow(row);
this.getSelection().moveCursorTo(row, column);
this.getSelection().moveCursorToScreen(row, column);
};
this.gotoPageUp = function() {
var row = this.getPageUpRow(),
column = Math.min(this.getCursorPosition().column,
this.session.getLine(row).length);
var row = this.$getPageUpRow();
var column = this.getCursorPositionScreen().column;
this.scrollToRow(row);
this.getSelection().moveCursorTo(row, column);
this.getSelection().moveCursorToScreen(row, column);
};
this.scrollPageDown = function() {
this.scrollToRow(this.getPageDownRow());
this.scrollToRow(this.$getPageDownRow());
};
this.scrollPageUp = function() {
this.renderer.scrollToRow(this.getPageUpRow());
this.renderer.scrollToRow(this.$getPageUpRow());
};
this.scrollToRow = function(row) {
this.renderer.scrollToRow(row);
};
this.scrollToLine = function(line, center) {
this.renderer.scrollToLine(line, center);
};
this.centerSelection = function() {
var range = this.getSelectionRange();
var line = Math.floor(range.start.row + (range.end.row - range.start.row) / 2);
this.renderer.scrollToLine(line, true);
};
this.getCursorPosition = function() {
return this.selection.getCursor();
};
this.getCursorPositionScreen = function() {
return this.session.documentToScreenPosition(this.getCursorPosition());
}
this.getSelectionRange = function() {
return this.selection.getRange();
};
this.selectAll = function() {
this.$blockScrolling += 1;
this.selection.selectAll();
this.$blockScrolling -= 1;
};
this.clearSelection = function() {
this.selection.clearSelection();
this.$updateDesiredColumn();
};
this.moveCursorTo = function(row, column) {
this.selection.moveCursorTo(row, column);
this.$updateDesiredColumn();
};
this.moveCursorToPosition = function(pos) {
this.selection.moveCursorToPosition(pos);
this.$updateDesiredColumn();
};
this.gotoLine = function(lineNumber, row) {
this.selection.clearSelection();
this.$blockScrolling = true;
this.$blockScrolling += 1;
this.moveCursorTo(lineNumber-1, row || 0);
this.$blockScrolling = false;
this.$blockScrolling -= 1;
if (!this.isRowVisible(this.getCursorPosition().row)) {
this.scrollToRow(lineNumber - 1 - Math.floor(this.getVisibleRowCount() / 2));
this.scrollToLine(lineNumber, true);
}
},
this.navigateTo = function(row, column) {
this.clearSelection();
this.moveCursorTo(row, column);
this.$updateDesiredColumn(column);
};
this.navigateUp = function(times) {
this.selection.clearSelection();
this.selection.moveCursorBy(-(times || 1), 0);
if (this.$desiredColumn) {
var cursor = this.getCursorPosition();
var column = this.session.screenToDocumentColumn(cursor.row, this.$desiredColumn);
this.selection.moveCursorTo(cursor.row, column);
}
times = times || 1;
this.selection.moveCursorBy(-times, 0);
};
this.navigateDown = function(times) {
this.selection.clearSelection();
this.selection.moveCursorBy(times || 1, 0);
if (this.$desiredColumn) {
var cursor = this.getCursorPosition();
var column = this.session.screenToDocumentColumn(cursor.row, this.$desiredColumn);
this.selection.moveCursorTo(cursor.row, column);
}
};
this.$updateDesiredColumn = function() {
var cursor = this.getCursorPosition();
this.$desiredColumn = this.session.documentToScreenColumn(cursor.row, cursor.column);
times = times || 1;
this.selection.moveCursorBy(times, 0);
};
this.navigateLeft = function(times) {
@ -967,7 +1031,6 @@ var Editor =function(renderer, session) {
this.$tryReplace(range, replacement);
if (range !== null)
this.selection.setSelectionRange(range);
this.$updateDesiredColumn();
},
this.replaceAll = function(replacement, options) {
@ -979,14 +1042,16 @@ var Editor =function(renderer, session) {
if (!ranges.length)
return;
var selection = this.getSelectionRange();
this.clearSelection();
this.selection.moveCursorTo(0, 0);
this.$blockScrolling += 1;
for (var i = ranges.length - 1; i >= 0; --i)
this.$tryReplace(ranges[i], replacement);
if (ranges[0] !== null)
this.selection.setSelectionRange(ranges[0]);
this.$updateDesiredColumn();
this.selection.setSelectionRange(selection);
this.$blockScrolling -= 1;
},
this.$tryReplace = function(range, replacement) {
@ -1039,7 +1104,6 @@ var Editor =function(renderer, session) {
var range = this.$search.find(this.session);
if (range) {
this.gotoLine(range.end.row+1, range.end.column);
this.$updateDesiredColumn();
this.selection.setSelectionRange(range);
}
};

View file

@ -42,8 +42,8 @@ exports.bindings = {
"removeline": "Command-D",
"gotoline": "Command-L",
"togglecomment": "Command-7",
"findnext": "Command-K",
"findprevious": "Command-Shift-K",
"findnext": "Command-G",
"findprevious": "Command-Shift-G",
"find": "Command-F",
"replace": "Command-R",
"undo": "Command-Z",
@ -54,35 +54,44 @@ exports.bindings = {
"selecttostart": "Command-Shift-Up",
"gotostart": "Command-Home|Command-Up",
"selectup": "Shift-Up",
"golineup": "Up",
"golineup": "Up|Ctrl-P",
"copylinesdown": "Command-Option-Down",
"movelinesdown": "Option-Down",
"selecttoend": "Command-Shift-Down",
"gotoend": "Command-End|Command-Down",
"selectdown": "Shift-Down",
"golinedown": "Down",
"golinedown": "Down|Ctrl-N",
"selectwordleft": "Option-Shift-Left",
"gotowordleft": "Option-Left",
"selecttolinestart": "Command-Shift-Left",
"gotolinestart": "Command-Left|Home",
"gotolinestart": "Command-Left|Home|Ctrl-A",
"selectleft": "Shift-Left",
"gotoleft": "Left",
"gotoleft": "Left|Ctrl-B",
"selectwordright": "Option-Shift-Right",
"gotowordright": "Option-Right",
"selecttolineend": "Command-Shift-Right",
"gotolineend": "Command-Right|End",
"gotolineend": "Command-Right|End|Ctrl-E",
"selectright": "Shift-Right",
"gotoright": "Right",
"gotoright": "Right|Ctrl-F",
"selectpagedown": "Shift-PageDown",
"pagedown": "PageDown",
"gotopagedown": "Option-PageDown|Ctrl-V",
"selectpageup": "Shift-PageUp",
"pageup": "PageUp",
"gotopageup": "Option-PageUp",
"selectlinestart": "Shift-Home",
"selectlineend": "Shift-End",
"del": "Delete",
"backspace": "Ctrl-Backspace|Command-Backspace|Option-Backspace|Backspace",
"del": "Delete|Ctrl-D",
"backspace": "Ctrl-Backspace|Command-Backspace|Shift-Backspace|Backspace|Ctrl-H",
"removetolineend": "Ctrl-K",
"removetolinestart": "Option-Backspace",
"removewordleft": "Alt-Backspace|Ctrl-Alt-Backspace",
"removewordright": "Alt-Delete",
"outdent": "Shift-Tab",
"indent": "Tab"
"indent": "Tab",
"transposeletters": "Ctrl-T",
"splitline": "Ctrl-O",
"centerselection": "Ctrl-L"
};
});

View file

@ -74,13 +74,13 @@ exports.bindings = {
"selectright": "Shift-Right",
"gotoright": "Right",
"selectpagedown": "Shift-PageDown",
"pagedown": "PageDown",
"gotopagedown": "PageDown",
"selectpageup": "Shift-PageUp",
"pageup": "PageUp",
"gotopageup": "PageUp",
"selectlinestart": "Shift-Home",
"selectlineend": "Shift-End",
"del": "Delete",
"backspace": "Backspace",
"backspace": "Ctrl-Backspace|Command-Backspace|Option-Backspace|Shift-Backspace|Backspace",
"outdent": "Shift-Tab",
"indent": "Tab"
};

View file

@ -80,13 +80,13 @@ StateHandler.prototype = {
}
var keyArray = [];
if (hashId & 1) keyArray.push("Ctrl");
if (hashId & 8) keyArray.push("Command");
if (hashId & 2) keyArray.push("Option");
if (hashId & 4) keyArray.push("Shift");
if (hashId & 1) keyArray.push("ctrl");
if (hashId & 8) keyArray.push("command");
if (hashId & 2) keyArray.push("option");
if (hashId & 4) keyArray.push("shift");
if (key) keyArray.push(key);
var symbolicName = keyArray.join("-").toLowerCase();
var symbolicName = keyArray.join("-");
var bufferToUse = data.buffer + symbolicName;
// Don't add the symbolic name to the key buffer if the alt_ key is
@ -194,6 +194,13 @@ StateHandler.prototype = {
* This function is called by keyBinding.
*/
handleKeyboard: function(data, hashId, key) {
// If we pressed any command key but no other key, then ignore the input.
// Otherwise "shift-" is added to the buffer, and later on "shift-g"
// which results in "shift-shift-g" which doesn't make senese.
if (hashId != 0 && (key == "" || String.fromCharCode(0))) {
return null;
}
// Compute the current value of the keyboard input buffer.
var r = this.$composeBuffer(data, hashId, key);
var buffer = r.bufferToUse;

View file

@ -38,14 +38,12 @@
define(function(require, exports, module) {
var event = require("pilot/event");
var useragent = require("pilot/useragent");
var TextInput = function(parentNode, host) {
var text = document.createElement("textarea");
var style = text.style;
style.position = "absolute";
style.left = "-10000px";
style.top = "-10000px";
text.style.left = "-10000px";
parentNode.appendChild(text);
var PLACEHOLDER = String.fromCharCode(0);
@ -53,6 +51,7 @@ var TextInput = function(parentNode, host) {
var inCompostion = false;
var copied = false;
var tempStyle = '';
function sendText(valueToSend) {
if (!copied) {
@ -74,6 +73,7 @@ var TextInput = function(parentNode, host) {
}
var onTextInput = function(e) {
if (useragent.isIE && text.value.charCodeAt(0) > 128) return;
setTimeout(function() {
if (!inCompostion)
sendText();
@ -82,40 +82,67 @@ var TextInput = function(parentNode, host) {
var onCompositionStart = function(e) {
inCompostion = true;
sendText();
text.value = "";
if (!useragent.isIE) {
sendText();
text.value = "";
};
host.onCompositionStart();
setTimeout(onCompositionUpdate, 0);
if (!useragent.isGecko) setTimeout(onCompositionUpdate, 0);
};
var onCompositionUpdate = function() {
if (!inCompostion) return;
host.onCompositionUpdate(text.value);
};
var onCompositionEnd = function() {
inCompostion = false;
host.onCompositionEnd();
onTextInput();
setTimeout(function () {
sendText();
}, 0);
};
var onCopy = function() {
var onCopy = function(e) {
copied = true;
text.value = host.getCopyText();
var copyText = host.getCopyText();
if(copyText)
text.value = copyText;
else
e.preventDefault();
text.select();
copied = true;
setTimeout(sendText, 0);
setTimeout(function () {
sendText();
}, 0);
};
var onCut = function() {
var onCut = function(e) {
copied = true;
text.value = host.getCopyText();
host.onCut();
var copyText = host.getCopyText();
if(copyText) {
text.value = copyText;
host.onCut();
} else
e.preventDefault();
text.select();
setTimeout(sendText, 0);
setTimeout(function () {
sendText();
}, 0);
};
event.addCommandKeyListener(text, host.onCommandKey.bind(host));
event.addListener(text, "keypress", onTextInput);
if (useragent.isIE) {
var keytable = { 13:1, 27:1 };
event.addListener(text, "keyup", function (e) {
if (inCompostion && (!text.value || keytable[e.keyCode]))
setTimeout(onCompositionEnd, 0);
if ((text.value.charCodeAt(0)|0) < 129) {
return;
};
inCompostion ? onCompositionUpdate() : onCompositionStart();
});
};
event.addListener(text, "textInput", onTextInput);
event.addListener(text, "paste", function(e) {
// Some browsers support the event.clipboardData API. Use this to get
@ -130,13 +157,41 @@ var TextInput = function(parentNode, host) {
onTextInput();
}
});
event.addListener(text, "propertychange", onTextInput);
if (!useragent.isIE) {
event.addListener(text, "propertychange", onTextInput);
};
event.addListener(text, "copy", onCopy);
event.addListener(text, "cut", onCut);
if (useragent.isIE) {
event.addListener(text, "beforecopy", function(e) {
var copyText = host.getCopyText();
if(copyText)
clipboardData.setData("Text", copyText);
else
e.preventDefault();
});
event.addListener(parentNode, "keydown", function(e) {
if (e.ctrlKey && e.keyCode == 88) {
var copyText = host.getCopyText();
if (copyText) {
clipboardData.setData("Text", copyText);
host.onCut();
}
event.preventDefault(e)
}
});
}
else {
event.addListener(text, "copy", onCopy);
event.addListener(text, "cut", onCut);
}
event.addListener(text, "compositionstart", onCompositionStart);
event.addListener(text, "compositionupdate", onCompositionUpdate);
if (useragent.isGecko) {
event.addListener(text, "text", onCompositionUpdate);
};
if (useragent.isWebKit) {
event.addListener(text, "keyup", onCompositionUpdate);
};
event.addListener(text, "compositionend", onCompositionEnd);
event.addListener(text, "blur", function() {
@ -157,6 +212,32 @@ var TextInput = function(parentNode, host) {
this.blur = function() {
text.blur();
};
this.getElement = function() {
return text;
};
this.onContextMenu = function(mousePos, isEmpty){
if (mousePos) {
if(!tempStyle)
tempStyle = text.style.cssText;
text.style.cssText = 'position:fixed; z-index:1000;' +
'left:' + (mousePos.x - 2) + 'px; top:' + (mousePos.y - 2) + 'px;'
}
if (isEmpty)
text.value='';
}
this.onContextMenuClose = function(){
setTimeout(function () {
if (tempStyle) {
text.style.cssText = tempStyle;
tempStyle = '';
}
sendText();
}, 0);
}
};
exports.TextInput = TextInput;

View file

@ -20,6 +20,7 @@
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
* Julian Viereck <julian.viereck@gmail.com>
*
* 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
@ -57,15 +58,8 @@ var Cursor = function(parentEl) {
};
this.setCursor = function(position, overwrite) {
this.position = {
row : position.row,
column : this.session.documentToScreenColumn(position.row, position.column)
};
if (overwrite) {
dom.addCssClass(this.cursor, "ace_overwrite");
} else {
dom.removeCssClass(this.cursor, "ace_overwrite");
}
this.position = position;
this.overwrite = overwrite;
};
this.hideCursor = function() {
@ -100,7 +94,7 @@ var Cursor = function(parentEl) {
}, 1000);
};
this.getPixelPosition = function() {
this.getPixelPosition = function(onScreen) {
if (!this.config || !this.position) {
return {
left : 0,
@ -108,10 +102,12 @@ var Cursor = function(parentEl) {
};
}
var cursorLeft = Math.round(this.position.column * this.config.characterWidth);
var cursorTop = this.position.row * this.config.lineHeight;
var pos = this.session.documentToScreenPosition(this.position);
var cursorLeft = Math.round(pos.column * this.config.characterWidth);
var cursorTop = (pos.row - (onScreen ? this.config.firstRowScreen : 0)) *
this.config.lineHeight;
return {
return {
left : cursorLeft,
top : cursorTop
};
@ -123,23 +119,23 @@ var Cursor = function(parentEl) {
this.config = config;
var cursorLeft = Math.round(this.position.column * config.characterWidth);
var cursorTop = this.position.row * config.lineHeight;
this.pixelPos = this.getPixelPosition(true);
this.pixelPos = {
left : cursorLeft,
top : cursorTop
};
this.cursor.style.left = cursorLeft + "px";
this.cursor.style.top = (cursorTop - (config.firstRow * config.lineHeight))
+ "px";
this.cursor.style.left = this.pixelPos.left + "px";
this.cursor.style.top = this.pixelPos.top + "px";
this.cursor.style.width = config.characterWidth + "px";
this.cursor.style.height = config.lineHeight + "px";
if (this.isVisible) {
this.element.appendChild(this.cursor);
}
if (this.overwrite) {
dom.addCssClass(this.cursor, "ace_overwrite");
} else {
dom.removeCssClass(this.cursor, "ace_overwrite");
}
this.restartTimer();
};

View file

@ -20,6 +20,7 @@
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
* Julian Viereck <julian.viereck@gmail.com>
*
* 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
@ -45,35 +46,70 @@ var Gutter = function(parentEl) {
parentEl.appendChild(this.element);
this.$breakpoints = [];
this.$annotations = [];
this.$decorations = [];
};
(function() {
this.setSession = function(session) {
this.session = session;
};
this.addGutterDecoration = function(row, className){
if (!this.$decorations[row])
if (!this.$decorations[row])
this.$decorations[row] = "";
this.$decorations[row] += " ace_" + className;
}
this.removeGutterDecoration = function(row, className){
this.$decorations[row] =
this.$decorations[row].replace(" ace_" + className, "");
}
this.$decorations[row] = this.$decorations[row].replace(" ace_" + className, "");
};
this.setBreakpoints = function(rows) {
this.$breakpoints = rows.concat();
};
this.setAnnotations = function(annotations) {
// iterate over sparse array
this.$annotations = [];
for (var row in annotations) if (annotations.hasOwnProperty(row)) {
var rowAnnotations = annotations[row];
if (!rowAnnotations)
continue;
var rowInfo = this.$annotations[row] = {
text: []
};
for (var i=0; i<rowAnnotations.length; i++) {
var annotation = rowAnnotations[i];
rowInfo.text.push(annotation.text.replace(/"/g, "&quot;").replace(/'/g, "&rsquo;").replace(/</, "&lt;"));
var type = annotation.type;
if (type == "error")
rowInfo.className = "ace_error";
else if (type == "warning" && rowInfo.className != "ace_error")
rowInfo.className = "ace_warning";
else if (type == "info" && (!rowInfo.className))
rowInfo.className = "ace_info";
}
}
};
this.update = function(config) {
this.$config = config;
var html = [];
for ( var i = config.firstRow; i <= config.lastRow; i++) {
var annotation = this.$annotations[i] || {
className: "",
text: []
};
html.push("<div class='ace_gutter-cell",
this.$decorations[i] || "",
this.$breakpoints[i] ? " ace_breakpoint" : "",
"' style='height:", config.lineHeight, "px;'>", (i+1), "</div>");
this.$breakpoints[i] ? " ace_breakpoint " : " ",
annotation.className,
"' title='", annotation.text.join("\n"),
"' style='height:", this.session.getRowHeight(config, i), "px;'>", (i+1), "</div>");
html.push("</div>");
}
this.element = dom.setInnerHtml(this.element, html.join(""));

View file

@ -20,6 +20,7 @@
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
* Julian Viereck <julian.viereck@gmail.com>
*
* 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
@ -44,9 +45,6 @@ var Marker = function(parentEl) {
this.element = document.createElement("div");
this.element.className = "ace_layer ace_marker-layer";
parentEl.appendChild(this.element);
this.markers = {};
this.$markerId = 1;
};
(function() {
@ -54,23 +52,9 @@ var Marker = function(parentEl) {
this.setSession = function(session) {
this.session = session;
};
this.addMarker = function(range, clazz, type) {
var id = this.$markerId++;
this.markers[id] = {
range : range,
type : type || "line",
clazz : clazz
};
return id;
};
this.removeMarker = function(markerId) {
var marker = this.markers[markerId];
if (marker) {
delete (this.markers[markerId]);
}
this.setMarkers = function(markers) {
this.markers = markers;
};
this.update = function(config) {
@ -80,14 +64,21 @@ var Marker = function(parentEl) {
this.config = config;
var html = [];
var html = [];
for ( var key in this.markers) {
var marker = this.markers[key];
var range = marker.range.clipRows(config.firstRow, config.lastRow);
if (range.isEmpty()) continue;
if (range.isMultiLine()) {
range = range.toScreenRange(this.session);
if (marker.renderer) {
var top = this.$getTop(range.start.row, config);
var left = Math.round(range.start.column * config.characterWidth);
marker.renderer(html, range, left, top, config);
}
else if (range.isMultiLine()) {
if (marker.type == "text") {
this.drawTextMarker(html, range, marker.clazz, config);
} else {
@ -101,11 +92,16 @@ var Marker = function(parentEl) {
this.element = dom.setInnerHtml(this.element, html.join(""));
};
this.drawTextMarker = function(stringBuilder, range, clazz, layerConfig) {
this.$getTop = function(row, layerConfig) {
return (row - layerConfig.firstRowScreen) * layerConfig.lineHeight;
};
this.drawTextMarker = function(stringBuilder, range, clazz, layerConfig) {
// selection start
var row = range.start.row;
var lineRange = new Range(row, range.start.column, row, this.session.getLine(row).length);
var lineRange = new Range(row, range.start.column,
row, this.session.getScreenLastRowColumn(row));
this.drawSingleLineMarker(stringBuilder, lineRange, clazz, layerConfig, 1);
// selection end
@ -116,18 +112,16 @@ var Marker = function(parentEl) {
for (var row = range.start.row + 1; row < range.end.row; row++) {
lineRange.start.row = row;
lineRange.end.row = row;
lineRange.end.column = this.session.getLine(row).length; // account for endofline characters
lineRange.end.column = this.session.getScreenLastRowColumn(row);
this.drawSingleLineMarker(stringBuilder, lineRange, clazz, layerConfig, 1);
}
};
this.drawMultiLineMarker = function(stringBuilder, range, clazz, layerConfig) {
var range = range.toScreenRange(this.session);
// from selection start to the end of the line
var height = layerConfig.lineHeight;
var width = Math.round(layerConfig.width - (range.start.column * layerConfig.characterWidth));
var top = (range.start.row - layerConfig.firstRow) * layerConfig.lineHeight;
var top = this.$getTop(range.start.row, layerConfig);
var left = Math.round(range.start.column * layerConfig.characterWidth);
stringBuilder.push(
@ -139,7 +133,7 @@ var Marker = function(parentEl) {
);
// from start of the last line to the selection end
var top = (range.end.row - layerConfig.firstRow) * layerConfig.lineHeight;
var top = this.$getTop(range.end.row, layerConfig);
var width = Math.round(range.end.column * layerConfig.characterWidth);
stringBuilder.push(
@ -153,7 +147,7 @@ var Marker = function(parentEl) {
var height = (range.end.row - range.start.row - 1) * layerConfig.lineHeight;
if (height < 0)
return;
var top = (range.start.row + 1 - layerConfig.firstRow) * layerConfig.lineHeight;
var top = this.$getTop(range.start.row + 1, layerConfig);
stringBuilder.push(
"<div class='", clazz, "' style='",
@ -164,11 +158,9 @@ var Marker = function(parentEl) {
};
this.drawSingleLineMarker = function(stringBuilder, range, clazz, layerConfig, extraLength) {
var range = range.toScreenRange(this.session);
var height = layerConfig.lineHeight;
var width = Math.round((range.end.column + (extraLength || 0) - range.start.column) * layerConfig.characterWidth);
var top = (range.start.row - layerConfig.firstRow) * layerConfig.lineHeight;
var top = this.$getTop(range.start.row, layerConfig);
var left = Math.round(range.start.column * layerConfig.characterWidth);
stringBuilder.push(

View file

@ -20,6 +20,7 @@
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
* Julian Viereck <julian.viereck@gmail.com>
*
* 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
@ -96,15 +97,15 @@ var Text = function(parentEl) {
if (!this.$measureNode) {
var measureNode = this.$measureNode = document.createElement("div");
var style = measureNode.style;
style.width = style.height = "auto";
style.left = style.top = "-1000px";
style.left = style.top = (-n * 40) + "px";
style.visibility = "hidden";
style.position = "absolute";
style.overflow = "visible";
style.whiteSpace = "nowrap";
// in FF 3.6 monospace fonts can have a fixed sub pixel width.
// that's why we have to measure many characters
// Note: characterWidth can be a float!
@ -133,7 +134,7 @@ var Text = function(parentEl) {
this.setShowInvisibles = function(showInvisibles) {
if (this.showInvisibles == showInvisibles)
return false;
this.showInvisibles = showInvisibles;
return true;
};
@ -152,23 +153,31 @@ var Text = function(parentEl) {
}
};
this.updateLines = function(layerConfig, firstRow, lastRow) {
this.updateLines = function(config, firstRow, lastRow) {
this.$computeTabString();
this.config = layerConfig;
var first = Math.max(firstRow, layerConfig.firstRow);
var last = Math.min(lastRow, layerConfig.lastRow);
// Due to wrap line changes there can be new lines if e.g.
// the line to updated wrapped in the meantime.
if (this.config.lastRow != config.lastRow ||
this.config.firstRow != config.firstRow) {
this.scrollLines(config);
}
this.config = config;
var first = Math.max(firstRow, config.firstRow);
var last = Math.min(lastRow, config.lastRow);
var lineElements = this.element.childNodes;
var tokens = this.tokenizer.getTokens(first, last);
for (var i=first; i<=last; i++) {
var lineElement = lineElements[i - layerConfig.firstRow];
var lineElement = lineElements[i - config.firstRow];
if (!lineElement)
continue;
var html = [];
this.$renderLine(html, i, tokens[i-first].tokens);
dom.setInnerHtml(lineElement, html.join(""));
lineElement = dom.setInnerHtml(lineElement, html.join(""));
lineElement.style.height =
this.session.getRowHeight(config, i) + "px";
}
};
@ -200,7 +209,7 @@ var Text = function(parentEl) {
else
el.appendChild(fragment);
}
if (config.lastRow > oldConfig.lastRow) {
var fragment = this.$renderLinesFragment(config, oldConfig.lastRow + 1, config.lastRow);
el.appendChild(fragment);
@ -214,11 +223,12 @@ var Text = function(parentEl) {
var lineEl = document.createElement("div");
lineEl.className = "ace_line";
var style = lineEl.style;
style.height = this.$characterSize.height + "px";
style.height = this.session.getRowHeight(config, row) + "px";
style.width = config.width + "px";
var html = [];
this.$renderLine(html, row, tokens[row-firstRow].tokens);
if (tokens.length > row-firstRow)
this.$renderLine(html, row, tokens[row-firstRow].tokens);
// don't use setInnerHtml since we are working with an empty DIV
lineEl.innerHTML = html.join("");
fragment.appendChild(lineEl);
@ -231,14 +241,12 @@ var Text = function(parentEl) {
this.config = config;
var html = [];
var tokens = this.tokenizer.getTokens(config.firstRow, config.lastRow);
for (var i=config.firstRow; i<=config.lastRow; i++) {
html.push("<div class='ace_line' style='height:" + this.$characterSize.height + "px;", "width:",
config.width, "px'>");
this.$renderLine(html, i, tokens[i-config.firstRow].tokens), html.push("</div>");
}
var tokens = this.tokenizer.getTokens(config.firstRow, config.lastRow)
var fragment = this.$renderLinesFragment(config, config.firstRow, config.lastRow);
this.element = dom.setInnerHtml(this.element, html.join(""));
// Clear the current content of the element and add the rendered fragment.
this.element.innerHTML = "";
this.element.appendChild(fragment);
};
this.$textToken = {
@ -248,35 +256,81 @@ var Text = function(parentEl) {
};
this.$renderLine = function(stringBuilder, row, tokens) {
// if (this.showInvisibles) {
// var self = this;
// var spaceRe = /[\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]+/g;
// var spaceReplace = function(space) {
// var space = new Array(space.length+1).join(self.SPACE_CHAR);
// return "<span class='ace_invisible'>" + space + "</span>";
// };
// }
// else {
if (this.showInvisibles) {
var self = this;
var spaceRe = /( +)|([\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000])/g;
var spaceReplace = function(space) {
if (space.charCodeAt(0) == 32)
return new Array(space.length+1).join("&nbsp;");
else {
var space = new Array(space.length+1).join(self.SPACE_CHAR);
return "<span class='ace_invisible'>" + space + "</span>";
}
};
}
else {
var spaceRe = /[\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]/g;
var spaceReplace = "&nbsp;";
// }
}
for ( var i = 0; i < tokens.length; i++) {
var token = tokens[i];
var output = token.value
var _self = this;
var characterWidth = this.config.characterWidth;
function addToken(token, value) {
var output = value
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(spaceRe, spaceReplace)
.replace(/\t/g, this.$tabString);
.replace(/\t/g, _self.$tabString)
.replace(/[\u3040-\u309F]|[\u30A0-\u30FF]|[\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF]/g, function(c) {
return "<span class='ace_cjk' style='width:" + (characterWidth * 2) + "px'>" + c + "</span>"
});
if (!this.$textToken[token.type]) {
if (!_self.$textToken[token.type]) {
var classes = "ace_" + token.type.replace(/\./g, " ace_");
stringBuilder.push("<span class='", classes, "'>", output, "</span>");
}
else {
stringBuilder.push(output);
}
}
var splits = this.session.getRowSplitData(row);
var chars = 0, split = 0, splitChars;
if (!splits || splits.length == 0) {
splitChars = Number.MAX_VALUE;
} else {
splitChars = splits[0];
}
stringBuilder.push("<div style='height:",
this.config.lineHeight, "px",
"'>");
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i];
var value = token.value;
if (chars + value.length < splitChars) {
addToken(token, value);
chars += value.length;
} else {
while (chars + value.length >= splitChars) {
addToken(token, value.substring(0, splitChars - chars));
value = value.substring(splitChars - chars);
chars = splitChars;
stringBuilder.push("</div>",
"<div style='height:",
this.config.lineHeight, "px",
"'>");
split ++;
splitChars = splits[split] || Number.MAX_VALUE;
}
if (value.length != 0) {
chars += value.length;
addToken(token, value);
}
}
};
if (this.showInvisibles) {
@ -286,6 +340,7 @@ var Text = function(parentEl) {
stringBuilder.push("<span class='ace_invisible'>" + this.EOF_CHAR + "</span>");
}
}
stringBuilder.push("</div>");
};
}).call(Text.prototype);

128
lib/ace/mode/c_cpp.js Normal file
View file

@ -0,0 +1,128 @@
/* ***** 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>
* Gastón Kleiman <gaston.kleiman AT gmail DOT com>
*
* 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 TextMode = require("ace/mode/text").Mode;
var Tokenizer = require("ace/tokenizer").Tokenizer;
var c_cppHighlightRules = require("ace/mode/c_cpp_highlight_rules").c_cppHighlightRules;
var MatchingBraceOutdent = require("ace/mode/matching_brace_outdent").MatchingBraceOutdent;
var Range = require("ace/range").Range;
var Mode = function() {
this.$tokenizer = new Tokenizer(new c_cppHighlightRules().getRules());
this.$outdent = new MatchingBraceOutdent();
};
oop.inherits(Mode, TextMode);
(function() {
this.toggleCommentLines = function(state, doc, startRow, endRow) {
var outdent = true;
var outentedRows = [];
var re = /^(\s*)\/\//;
for (var i=startRow; i<= endRow; i++) {
if (!re.test(doc.getLine(i))) {
outdent = false;
break;
}
}
if (outdent) {
var deleteRange = new Range(0, 0, 0, 0);
for (var i=startRow; i<= endRow; i++)
{
var line = doc.getLine(i);
var m = line.match(re);
deleteRange.start.row = i;
deleteRange.end.row = i;
deleteRange.end.column = m[0].length;
doc.replace(deleteRange, m[1]);
}
}
else {
doc.indentRows(startRow, endRow, "//");
}
};
this.getNextLineIndent = function(state, line, tab) {
var indent = this.$getIndent(line);
var tokenizedLine = this.$tokenizer.getLineTokens(line, state);
var tokens = tokenizedLine.tokens;
var endState = tokenizedLine.state;
if (tokens.length && tokens[tokens.length-1].type == "comment") {
return indent;
}
if (state == "start") {
var match = line.match(/^.*[\{\(\[]\s*$/);
if (match) {
indent += tab;
}
} else if (state == "doc-start") {
if (endState == "start") {
return "";
}
var match = line.match(/^\s*(\/?)\*/);
if (match) {
if (match[1]) {
indent += " ";
}
indent += "* ";
}
}
return indent;
};
this.checkOutdent = function(state, line, input) {
return this.$outdent.checkOutdent(line, input);
};
this.autoOutdent = function(state, doc, row) {
this.$outdent.autoOutdent(doc, row);
};
}).call(Mode.prototype);
exports.Mode = Mode;
});

View file

@ -0,0 +1,172 @@
/* ***** 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>
* Gastón Kleiman <gaston.kleiman AT gmail DOT com>
*
* Based on Bespin's C/C++ Syntax Plugin by Marc McIntyre.
*
* 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 DocCommentHighlightRules = require("ace/mode/doc_comment_highlight_rules").DocCommentHighlightRules;
var TextHighlightRules = require("ace/mode/text_highlight_rules").TextHighlightRules;
var c_cppHighlightRules = function() {
var docComment = new DocCommentHighlightRules();
var keywords = lang.arrayToMap(
("and|double|not_eq|throw|and_eq|dynamic_cast|operator|true|" +
"asm|else|or|try|auto|enum|or_eq|typedef|bitand|explicit|private|" +
"typeid|bitor|extern|protected|typename|bool|false|public|union|" +
"break|float|register|unsigned|case|fro|reinterpret-cast|using|catch|" +
"friend|return|virtual|char|goto|short|void|class|if|signed|volatile|" +
"compl|inline|sizeof|wchar_t|const|int|static|while|const-cast|long|" +
"static_cast|xor|continue|mutable|struct|xor_eq|default|namespace|" +
"switch|delete|new|template|do|not|this|for").split("|")
);
var buildinConstants = lang.arrayToMap(
("NULL").split("|")
);
// regexp must not have capturing parentheses. Use (?:) instead.
// regexps are ordered -> the first match is used
this.$rules = {
"start" : [
{
token : "comment",
regex : "\\/\\/.*$"
},
docComment.getStartRule("doc-start"),
{
token : "comment", // multi line comment
regex : "\\/\\*",
next : "comment"
}, {
token : "string", // single line
regex : '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'
}, {
token : "string", // multi line string start
regex : '["].*\\\\$',
next : "qqstring"
}, {
token : "string", // single line
regex : "['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"
}, {
token : "string", // multi line string start
regex : "['].*\\\\$",
next : "qstring"
}, {
token : "constant.numeric", // hex
regex : "0[xX][0-9a-fA-F]+\\b"
}, {
token : "constant.numeric", // float
regex : "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"
}, {
token : "constant", // <CONSTANT>
regex : "<[a-zA-Z0-9.]+>"
}, {
token : "keyword", // pre-compiler directivs
regex : "(?:#include|#pragma|#line|#define|#undef|#ifdef|#else|#elif|#endif|#ifndef)"
}, {
token : function(value) {
if (value == "this")
return "variable.language";
else if (keywords.hasOwnProperty(value))
return "keyword";
else if (buildinConstants.hasOwnProperty(value))
return "constant.language";
else
return "identifier";
},
regex : "[a-zA-Z_$][a-zA-Z0-9_$]*\\b"
}, {
token : "keyword.operator",
regex : "!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|==|=|!=|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|new|delete|typeof|void)"
}, {
token : "lparen",
regex : "[[({]"
}, {
token : "rparen",
regex : "[\\])}]"
}, {
token : "text",
regex : "\\s+"
}
],
"comment" : [
{
token : "comment", // closing comment
regex : ".*?\\*\\/",
next : "start"
}, {
token : "comment", // comment spanning whole line
regex : ".+"
}
],
"qqstring" : [
{
token : "string",
regex : '(?:(?:\\\\.)|(?:[^"\\\\]))*?"',
next : "start"
}, {
token : "string",
regex : '.+'
}
],
"qstring" : [
{
token : "string",
regex : "(?:(?:\\\\.)|(?:[^'\\\\]))*?'",
next : "start"
}, {
token : "string",
regex : '.+'
}
]
};
this.addRules(docComment.getRules(), "doc-");
this.$rules["doc-start"][0].next = "start";
};
oop.inherits(c_cppHighlightRules, TextHighlightRules);
exports.c_cppHighlightRules = c_cppHighlightRules;
});

99
lib/ace/mode/coffee.js Normal file
View file

@ -0,0 +1,99 @@
/* ***** 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):
* Satoshi Murakami <murky.satyr AT gmail DOT com>
*
* 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 Tokenizer = require("ace/tokenizer").Tokenizer;
var Rules = require("ace/mode/coffee_highlight_rules").CoffeeHighlightRules;
var Outdent = require("ace/mode/matching_brace_outdent").MatchingBraceOutdent;
var Range = require("ace/range").Range;
var TextMode = require("ace/mode/text").Mode;
var oop = require("pilot/oop")
function CoffeeMode() {
this.$tokenizer = new Tokenizer(new Rules().getRules());
this.$outdent = new Outdent();
};
oop.inherits(CoffeeMode, TextMode);
var proto = CoffeeMode.prototype;
var indenter = /(?:[({[=:]|[-=]>|\b(?:else|switch|try|catch(?:\s*[$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*)?|finally))\s*$/;
var commentLine = /^(\s*)#/;
var hereComment = /^\s*###(?!#)/;
var indentation = /^\s*/;
proto.getNextLineIndent = function(state, line, tab) {
var indent = this.$getIndent(line);
var tokens = this.$tokenizer.getLineTokens(line, state).tokens;
if (!(tokens.length && tokens[tokens.length - 1].type === 'comment') &&
state === 'start' && indenter.test(line))
indent += tab;
return indent;
};
proto.toggleCommentLines = function(state, doc, startRow, endRow){
console.log("toggle");
var range = new Range(0, 0, 0, 0);
for (var i = startRow; i <= endRow; ++i) {
var line = doc.getLine(i);
if (hereComment.test(line))
continue;
if (commentLine.test(line))
line = line.replace(commentLine, '$1');
else
line = line.replace(indentation, '$&#');
range.end.row = range.start.row = i;
range.end.column = line.length + 1;
doc.replace(range, line);
}
};
proto.checkOutdent = function(state, line, input) {
return this.$outdent.checkOutdent(line, input);
};
proto.autoOutdent = function(state, doc, row) {
this.$outdent.autoOutdent(doc, row);
};
exports.Mode = CoffeeMode;
});

View file

@ -0,0 +1,182 @@
/* ***** 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):
* Satoshi Murakami <murky.satyr AT gmail DOT com>
*
* 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) {
require("pilot/oop").inherits(
CoffeeHighlightRules,
require("ace/mode/text_highlight_rules").TextHighlightRules);
function CoffeeHighlightRules() {
var identifier = "[$A-Za-z_\\x7f-\\uffff][$\\w\\x7f-\\uffff]*"
, keywordend = "(?![$\\w]|\\s*:)"
, stringfill = {token: "string", regex: ".+"}
;
this.$rules =
{ start:
[ { token: "identifier"
, regex: "(?:@|(?:\\.|::)\\s*)" + identifier
}
, { token: "keyword"
, regex: "(?:t(?:h(?:is|row|en)|ry|ypeof)|s(?:uper|witch)|return|b(?:reak|y)|c(?:ontinue|atch|lass)|i(?:n(?:stanceof)?|s(?:nt)?|f)|e(?:lse|xtends)|f(?:or (?:own)?|inally|unction)|wh(?:ile|en)|n(?:ew|ot?)|d(?:e(?:lete|bugger)|o)|loop|o(?:ff?|[rn])|un(?:less|til)|and|yes)" + keywordend
}
, { token: "constant.language"
, regex: "(?:true|false|null|undefined)" + keywordend
}
, { token: "invalid.illegal"
, regex: "(?:c(?:ase|onst)|default|function|v(?:ar|oid)|with|e(?:num|xport)|i(?:mplements|nterface)|let|p(?:ackage|r(?:ivate|otected)|ublic)|static|yield|__(?:hasProp|extends|slice|bind|indexOf))" + keywordend
}
, { token: "language.support.class"
, regex: "(?:Array|Boolean|Date|Function|Number|Object|R(?:e(?:gExp|ferenceError)|angeError)|S(?:tring|yntaxError)|E(?:rror|valError)|TypeError|URIError)" + keywordend
}
, { token: "language.support.function"
, regex: "(?:Math|JSON|is(?:NaN|Finite)|parse(?:Int|Float)|encodeURI(?:Component)?|decodeURI(?:Component)?)" + keywordend
}
, { token: "identifier"
, regex: identifier
}
, { token: "constant.numeric"
, regex: "(?:0x[\\da-fA-F]+|(?:\\d+(?:\\.\\d+)?|\\.\\d+)(?:[eE][+-]?\\d+)?)"
}
, { token: "string"
, regex: "'''"
, next : "qdoc"
}
, { token: "string"
, regex: '"""'
, next : "qqdoc"
}
, { token: "string"
, regex: "'"
, next : "qstring"
}
, { token: "string"
, regex: '"'
, next : "qqstring"
}
, { token: "string"
, regex: "`"
, next : "js"
}
, { token: "string.regex"
, regex: "///"
, next : "heregex"
}
, { token: "string.regex"
, regex: "/(?!\\s)[^[/\\n\\\\]*(?: (?:\\\\.|\\[[^\\]\\n\\\\]*(?:\\\\.[^\\]\\n\\\\]*)*\\])[^[/\\n\\\\]*)*/[imgy]{0,4}(?!\\w)"
}
, { token: "comment"
, regex: "###(?!#)"
, next : "comment"
}
, { token: "comment"
, regex: "#.*"
}
, { token: "lparen"
, regex: "[({[]"
}
, { token: "rparen"
, regex: "[\\]})]"
}
, { token: "keyword.operator"
, regex: "\\S+"
}
, { token: "text"
, regex: "\\s+"
}
]
, qdoc:
[ { token: "string"
, regex: ".*?'''"
, next : "start"
}
, stringfill
]
, qqdoc:
[ { token: "string"
, regex: '.*?"""'
, next : "start"
}
, stringfill
]
, qstring:
[ { token: "string"
, regex: "[^\\\\']*(?:\\\\.[^\\\\']*)*'"
, next : "start"
}
, stringfill
]
, qqstring:
[ { token: "string"
, regex: '[^\\\\"]*(?:\\\\.[^\\\\"]*)*"'
, next : "start"
}
, stringfill
]
, js:
[ { token: "string"
, regex: "[^\\\\`]*(?:\\\\.[^\\\\`]*)*`"
, next : "start"
}
, stringfill
]
, heregex:
[ { token: "string.regex"
, regex: '.*?///[imgy]{0,4}'
, next : "start"
}
, { token: "comment.regex"
, regex: "\\s+(?:#.*)?"
}
, { token: "string.regex"
, regex: "\\S+"
}
]
, comment:
[ { token: "comment"
, regex: '.*?###'
, next : "start"
}
, { token: "comment"
, regex: ".+"
}
]
};
}
exports.CoffeeHighlightRules = CoffeeHighlightRules;
});

View file

@ -73,7 +73,7 @@ oop.inherits(Mode, TextMode);
};
this.autoOutdent = function(state, doc, row) {
return this.$outdent.autoOutdent(doc, row);
this.$outdent.autoOutdent(doc, row);
};
}).call(Mode.prototype);

View file

@ -44,14 +44,14 @@ var TextHighlightRules = require("ace/mode/text_highlight_rules").TextHighlightR
var CssHighlightRules = function() {
var properties = lang.arrayToMap(
("azimuth|background-attachment|background-color|background-image|" +
("-moz-box-sizing|-webkit-box-sizing|azimuth|background-attachment|background-color|background-image|" +
"background-position|background-repeat|background|border-bottom-color|" +
"border-bottom-style|border-bottom-width|border-bottom|border-collapse|" +
"border-color|border-left-color|border-left-style|border-left-width|" +
"border-left|border-right-color|border-right-style|border-right-width|" +
"border-right|border-spacing|border-style|border-top-color|" +
"border-top-style|border-top-width|border-top|border-width|border|" +
"bottom|caption-side|clear|clip|color|content|counter-increment|" +
"bottom|box-sizing|caption-side|clear|clip|color|content|counter-increment|" +
"counter-reset|cue-after|cue-before|cue|cursor|direction|display|" +
"elevation|empty-cells|float|font-family|font-size-adjust|font-size|" +
"font-stretch|font-style|font-variant|font-weight|font|height|left|" +
@ -76,8 +76,8 @@ var CssHighlightRules = function() {
var constants = lang.arrayToMap(
("absolute|all-scroll|always|armenian|auto|baseline|below|bidi-override|" +
"block|bold|bolder|both|bottom|break-all|break-word|capitalize|center|" +
"char|circle|cjk-ideographic|col-resize|collapse|crosshair|dashed|" +
"block|bold|bolder|border-box|both|bottom|break-all|break-word|capitalize|center|" +
"char|circle|cjk-ideographic|col-resize|collapse|content-box|crosshair|dashed|" +
"decimal-leading-zero|decimal|default|disabled|disc|" +
"distribute-all-lines|distribute-letter|distribute-space|" +
"distribute|dotted|double|e-resize|ellipsis|fixed|georgian|groove|" +
@ -198,16 +198,16 @@ var CssHighlightRules = function() {
regex : "\}"
}, {
token : function(value) {
if (properties[value.toLowerCase()]) {
if (properties.hasOwnProperty(value.toLowerCase())) {
return "support.type";
}
else if (functions[value.toLowerCase()]) {
else if (functions.hasOwnProperty(value.toLowerCase())) {
return "support.function";
}
else if (constants[value.toLowerCase()]) {
else if (constants.hasOwnProperty(value.toLowerCase())) {
return "support.constant";
}
else if (colors[value.toLowerCase()]) {
else if (colors.hasOwnProperty(value.toLowerCase())) {
return "support.constant.color";
}
else {

View file

@ -55,7 +55,7 @@ oop.inherits(Mode, TextMode);
(function() {
this.toggleCommentLines = function(state, doc, startRow, endRow) {
return this.$delegate("toggleCommentLines", arguments, function() {
this.$delegate("toggleCommentLines", arguments, function() {
return 0;
});
};
@ -74,7 +74,7 @@ oop.inherits(Mode, TextMode);
};
this.autoOutdent = function(state, doc, row) {
return this.$delegate("autoOutdent", arguments);
this.$delegate("autoOutdent", arguments);
};
this.$delegate = function(method, args, defaultHandler) {

24
lib/ace/mode/java.js Normal file
View file

@ -0,0 +1,24 @@
define(function(require, exports, module) {
var oop = require("pilot/oop");
var JavaScriptMode = require("ace/mode/javascript").Mode;
var Tokenizer = require("ace/tokenizer").Tokenizer;
var JavaHighlightRules = require("ace/mode/java_highlight_rules").JavaHighlightRules;
var MatchingBraceOutdent = require("ace/mode/matching_brace_outdent").MatchingBraceOutdent;
var Mode = function() {
this.$tokenizer = new Tokenizer(new JavaHighlightRules().getRules());
this.$outdent = new MatchingBraceOutdent();
};
oop.inherits(Mode, JavaScriptMode);
(function() {
this.createWorker = function(session) {
return null;
};
}).call(Mode.prototype);
exports.Mode = Mode;
});

View file

@ -0,0 +1,134 @@
define(function(require, exports, module) {
var oop = require("pilot/oop");
var lang = require("pilot/lang");
var DocCommentHighlightRules = require("ace/mode/doc_comment_highlight_rules").DocCommentHighlightRules;
var TextHighlightRules = require("ace/mode/text_highlight_rules").TextHighlightRules;
var JavaHighlightRules = function() {
var docComment = new DocCommentHighlightRules();
// taken from http://download.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html
var keywords = lang.arrayToMap(
("abstract|continue|for|new|switch|" +
"assert|default|goto|package|synchronized" +
"boolean|do|if|private|this" +
"break|double|implements|protected|throw" +
"byte|else|import|public|throws" +
"case|enum|instanceof|return|transient" +
"catch|extends|int|short|try" +
"char|final|interface|static|void" +
"class|finally|long|strictfp|volatile|" +
"const|float|native|super|while").split("|")
);
var buildinConstants = lang.arrayToMap(
("null|Infinity|NaN|undefined").split("|")
);
// regexp must not have capturing parentheses. Use (?:) instead.
// regexps are ordered -> the first match is used
this.$rules = {
"start" : [
{
token : "comment",
regex : "\\/\\/.*$"
},
docComment.getStartRule("doc-start"),
{
token : "comment", // multi line comment
regex : "\\/\\*",
next : "comment"
}, {
token : "comment", // multi line comment
regex : "\\/\\*\\*",
next : "comment"
}, {
token : "string.regexp",
regex : "[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"
}, {
token : "string", // single line
regex : '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'
}, {
token : "string", // single line
regex : "['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"
}, {
token : "constant.numeric", // hex
regex : "0[xX][0-9a-fA-F]+\\b"
}, {
token : "constant.numeric", // float
regex : "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"
}, {
token : "constant.language.boolean",
regex : "(?:true|false)\\b"
}, {
token : function(value) {
if (value == "this")
return "variable.language";
else if (keywords.hasOwnProperty(value))
return "keyword";
else if (buildinConstants.hasOwnProperty(value))
return "constant.language";
else
return "identifier";
},
// TODO: Unicode escape sequences
// TODO: Unicode identifiers
regex : "[a-zA-Z_$][a-zA-Z0-9_$]*\\b"
}, {
token : "keyword.operator",
regex : "!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"
}, {
token : "lparen",
regex : "[[({]"
}, {
token : "rparen",
regex : "[\\])}]"
}, {
token : "text",
regex : "\\s+"
}
],
"comment" : [
{
token : "comment", // closing comment
regex : ".*?\\*\\/",
next : "start"
}, {
token : "comment", // comment spanning whole line
regex : ".+"
}
],
"qqstring" : [
{
token : "string",
regex : '(?:(?:\\\\.)|(?:[^"\\\\]))*?"',
next : "start"
}, {
token : "string",
regex : '.+'
}
],
"qstring" : [
{
token : "string",
regex : "(?:(?:\\\\.)|(?:[^'\\\\]))*?'",
next : "start"
}, {
token : "string",
regex : '.+'
}
]
};
this.addRules(docComment.getRules(), "doc-");
this.$rules["doc-start"][0].next = "start";
};
oop.inherits(JavaHighlightRules, TextHighlightRules);
exports.JavaHighlightRules = JavaHighlightRules;
});

View file

@ -43,6 +43,7 @@ var Tokenizer = require("ace/tokenizer").Tokenizer;
var JavaScriptHighlightRules = require("ace/mode/javascript_highlight_rules").JavaScriptHighlightRules;
var MatchingBraceOutdent = require("ace/mode/matching_brace_outdent").MatchingBraceOutdent;
var Range = require("ace/range").Range;
var WorkerClient = require("ace/worker/worker_client").WorkerClient;
var Mode = function() {
this.$tokenizer = new Tokenizer(new JavaScriptHighlightRules().getRules());
@ -68,16 +69,16 @@ oop.inherits(Mode, TextMode);
var deleteRange = new Range(0, 0, 0, 0);
for (var i=startRow; i<= endRow; i++)
{
var line = doc.getLine(i).replace(re, "$1");
var line = doc.getLine(i);
var m = line.match(re);
deleteRange.start.row = i;
deleteRange.end.row = i;
deleteRange.end.column = line.length + 2;
doc.replace(deleteRange, line);
deleteRange.end.column = m[0].length;
doc.replace(deleteRange, m[1]);
}
return -2;
}
else {
return doc.indentRows(startRow, endRow, "//");
doc.indentRows(startRow, endRow, "//");
}
};
@ -118,7 +119,48 @@ oop.inherits(Mode, TextMode);
};
this.autoOutdent = function(state, doc, row) {
return this.$outdent.autoOutdent(doc, row);
this.$outdent.autoOutdent(doc, row);
};
this.createWorker = function(session) {
var doc = session.getDocument();
var worker = new WorkerClient(["ace", "pilot"], "worker-javascript.js", "ace/mode/javascript_worker", "JavaScriptWorker");
worker.call("setValue", [doc.getValue()]);
doc.on("change", function(e) {
e.range = {
start: e.data.range.start,
end: e.data.range.end
};
worker.emit("change", e);
});
worker.on("jslint", function(results) {
var errors = [];
for (var i=0; i<results.data.length; i++) {
var error = results.data[i];
if (error)
errors.push({
row: error.line-1,
column: error.character-1,
text: error.reason,
type: "warning",
lint: error
})
}
session.setAnnotations(errors)
});
worker.on("narcissus", function(e) {
session.setAnnotations([e.data]);
});
worker.on("terminate", function() {
session.clearAnnotations();
});
return worker;
};
}).call(Mode.prototype);

View file

@ -42,7 +42,7 @@ var lang = require("pilot/lang");
var DocCommentHighlightRules = require("ace/mode/doc_comment_highlight_rules").DocCommentHighlightRules;
var TextHighlightRules = require("ace/mode/text_highlight_rules").TextHighlightRules;
JavaScriptHighlightRules = function() {
var JavaScriptHighlightRules = function() {
var docComment = new DocCommentHighlightRules();
@ -50,11 +50,11 @@ JavaScriptHighlightRules = function() {
("break|case|catch|continue|default|delete|do|else|finally|for|function|" +
"if|in|instanceof|new|return|switch|throw|try|typeof|var|while|with").split("|")
);
var buildinConstants = lang.arrayToMap(
("null|Infinity|NaN|undefined").split("|")
);
var futureReserved = lang.arrayToMap(
("class|enum|extends|super|const|export|import|implements|let|private|" +
"public|yield|interface|package|protected|static").split("|")
@ -104,11 +104,11 @@ JavaScriptHighlightRules = function() {
token : function(value) {
if (value == "this")
return "variable.language";
else if (keywords[value])
else if (keywords.hasOwnProperty(value))
return "keyword";
else if (buildinConstants[value])
else if (buildinConstants.hasOwnProperty(value))
return "constant.language";
else if (futureReserved[value])
else if (futureReserved.hasOwnProperty(value))
return "invalid.illegal";
else if (value == "debugger")
return "invalid.deprecated";

View file

@ -0,0 +1,49 @@
define(function(require, exports, module) {
var oop = require("pilot/oop");
var Mirror = require("ace/worker/mirror").Mirror;
//var lint = require("ace/worker/jslint").JSLINT;
var lint = require("ace/worker/jshint").JSHINT;
var JavaScriptWorker = exports.JavaScriptWorker = function(sender) {
Mirror.call(this, sender);
this.setTimeout(500);
};
oop.inherits(JavaScriptWorker, Mirror);
(function() {
this.onUpdate = function() {
var value = this.doc.getValue();
value = value.replace(/^#!.*\n/, "\n");
// var start = new Date();
var parser = require("ace/narcissus/jsparse");
try {
parser.parse(value);
} catch(e) {
// console.log("narcissus")
// console.log(e);
sender.emit("narcissus", {
row: e.lineno-1,
column: null, // TODO convert e.cursor
text: e.message,
type: "error"
});
return;
} finally {
// console.log("parse time: " + (new Date() - start));
}
// var start = new Date();
// console.log("jslint")
lint(value, {undef: false, onevar: false, passfail: false});
this.sender.emit("jslint", lint.errors);
// console.log("lint time: " + (new Date() - start));
}
}).call(JavaScriptWorker.prototype);
});

View file

@ -63,8 +63,6 @@ var MatchingBraceOutdent = function() {};
var indent = this.$getIndent(doc.getLine(openBracePos.row));
doc.replace(new Range(row, 0, row, column-1), indent);
return indent.length - (column-1);
};
this.$getIndent = function(line) {

116
lib/ace/mode/perl.js Normal file
View file

@ -0,0 +1,116 @@
/* ***** 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):
* Panagiotis Astithas <pastith AT gmail DOT com>
*
* 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 TextMode = require("ace/mode/text").Mode;
var Tokenizer = require("ace/tokenizer").Tokenizer;
var PerlHighlightRules = require("ace/mode/perl_highlight_rules").PerlHighlightRules;
var MatchingBraceOutdent = require("ace/mode/matching_brace_outdent").MatchingBraceOutdent;
var Range = require("ace/range").Range;
var Mode = function() {
this.$tokenizer = new Tokenizer(new PerlHighlightRules().getRules());
this.$outdent = new MatchingBraceOutdent();
};
oop.inherits(Mode, TextMode);
(function() {
this.toggleCommentLines = function(state, doc, startRow, endRow) {
var outdent = true;
var outentedRows = [];
var re = /^(\s*)#/;
for (var i=startRow; i<= endRow; i++) {
if (!re.test(doc.getLine(i))) {
outdent = false;
break;
}
}
if (outdent) {
var deleteRange = new Range(0, 0, 0, 0);
for (var i=startRow; i<= endRow; i++)
{
var line = doc.getLine(i);
var m = line.match(re);
deleteRange.start.row = i;
deleteRange.end.row = i;
deleteRange.end.column = m[0].length;
doc.replace(deleteRange, m[1]);
}
}
else {
doc.indentRows(startRow, endRow, "#");
}
};
this.getNextLineIndent = function(state, line, tab) {
var indent = this.$getIndent(line);
var tokenizedLine = this.$tokenizer.getLineTokens(line, state);
var tokens = tokenizedLine.tokens;
var endState = tokenizedLine.state;
if (tokens.length && tokens[tokens.length-1].type == "comment") {
return indent;
}
if (state == "start") {
var match = line.match(/^.*[\{\(\[\:]\s*$/);
if (match) {
indent += tab;
}
}
return indent;
};
this.checkOutdent = function(state, line, input) {
return this.$outdent.checkOutdent(line, input);
};
this.autoOutdent = function(state, doc, row) {
this.$outdent.autoOutdent(doc, row);
};
}).call(Mode.prototype);
exports.Mode = Mode;
});

View file

@ -0,0 +1,163 @@
/* ***** 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):
* Panagiotis Astithas <pastith AT gmail DOT com>
*
* 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 TextHighlightRules = require("ace/mode/text_highlight_rules").TextHighlightRules;
var PerlHighlightRules = function() {
var keywords = lang.arrayToMap(
("base|constant|continue|else|elsif|for|foreach|format|goto|if|last|local|my|next|" +
"no|package|parent|redo|require|scalar|sub|unless|until|while|use|vars").split("|")
);
var buildinConstants = lang.arrayToMap(
("ARGV|ENV|INC|SIG").split("|")
);
var builtinFunctions = lang.arrayToMap(
("getprotobynumber|getprotobyname|getservbyname|gethostbyaddr|" +
"gethostbyname|getservbyport|getnetbyaddr|getnetbyname|getsockname|" +
"getpeername|setpriority|getprotoent|setprotoent|getpriority|" +
"endprotoent|getservent|setservent|endservent|sethostent|socketpair|" +
"getsockopt|gethostent|endhostent|setsockopt|setnetent|quotemeta|" +
"localtime|prototype|getnetent|endnetent|rewinddir|wantarray|getpwuid|" +
"closedir|getlogin|readlink|endgrent|getgrgid|getgrnam|shmwrite|" +
"shutdown|readline|endpwent|setgrent|readpipe|formline|truncate|" +
"dbmclose|syswrite|setpwent|getpwnam|getgrent|getpwent|ucfirst|sysread|" +
"setpgrp|shmread|sysseek|sysopen|telldir|defined|opendir|connect|" +
"lcfirst|getppid|binmode|syscall|sprintf|getpgrp|readdir|seekdir|" +
"waitpid|reverse|unshift|symlink|dbmopen|semget|msgrcv|rename|listen|" +
"chroot|msgsnd|shmctl|accept|unpack|exists|fileno|shmget|system|" +
"unlink|printf|gmtime|msgctl|semctl|values|rindex|substr|splice|" +
"length|msgget|select|socket|return|caller|delete|alarm|ioctl|index|" +
"undef|lstat|times|srand|chown|fcntl|close|write|umask|rmdir|study|" +
"sleep|chomp|untie|print|utime|mkdir|atan2|split|crypt|flock|chmod|" +
"BEGIN|bless|chdir|semop|shift|reset|link|stat|chop|grep|fork|dump|" +
"join|open|tell|pipe|exit|glob|warn|each|bind|sort|pack|eval|push|" +
"keys|getc|kill|seek|sqrt|send|wait|rand|tied|read|time|exec|recv|" +
"eof|chr|int|ord|exp|pos|pop|sin|log|abs|oct|hex|tie|cos|vec|END|ref|" +
"map|die|uc|lc|do").split("|")
);
// regexp must not have capturing parentheses. Use (?:) instead.
// regexps are ordered -> the first match is used
this.$rules = {
"start" : [
{
token : "comment",
regex : "#.*$"
}, {
token : "string.regexp",
regex : "[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"
}, {
token : "string", // single line
regex : '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'
}, {
token : "string", // multi line string start
regex : '["].*\\\\$',
next : "qqstring"
}, {
token : "string", // single line
regex : "['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"
}, {
token : "string", // multi line string start
regex : "['].*\\\\$",
next : "qstring"
}, {
token : "constant.numeric", // hex
regex : "0x[0-9a-fA-F]+\\b"
}, {
token : "constant.numeric", // float
regex : "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"
}, {
token : function(value) {
if (keywords.hasOwnProperty(value))
return "keyword";
else if (buildinConstants.hasOwnProperty(value))
return "constant.language";
else if (builtinFunctions.hasOwnProperty(value))
return "support.function";
else
return "identifier";
},
regex : "[a-zA-Z_$][a-zA-Z0-9_$]*\\b"
}, {
token : "keyword.operator",
regex : "\\.\\.\\.|\\|\\|=|>>=|<<=|<=>|&&=|=>|!~|\\^=|&=|\\|=|\\.=|x=|%=|\\/=|\\*=|\\-=|\\+=|=~|\\*\\*|\\-\\-|\\.\\.|\\|\\||&&|\\+\\+|\\->|!=|==|>=|<=|>>|<<|,|=|\\?\\:|\\^|\\||x|%|\\/|\\*|<|&|\\\\|~|!|>|\\.|\\-|\\+|\\-C|\\-b|\\-S|\\-u|\\-t|\\-p|\\-l|\\-d|\\-f|\\-g|\\-s|\\-z|\\-k|\\-e|\\-O|\\-T|\\-B|\\-M|\\-A|\\-X|\\-W|\\-c|\\-R|\\-o|\\-x|\\-w|\\-r|\\b(?:and|cmp|eq|ge|gt|le|lt|ne|not|or|xor)"
}, {
token : "lparen",
regex : "[[({]"
}, {
token : "rparen",
regex : "[\\])}]"
}, {
token : "text",
regex : "\\s+"
}
],
"qqstring" : [
{
token : "string",
regex : '(?:(?:\\\\.)|(?:[^"\\\\]))*?"',
next : "start"
}, {
token : "string",
regex : '.+'
}
],
"qstring" : [
{
token : "string",
regex : "(?:(?:\\\\.)|(?:[^'\\\\]))*?'",
next : "start"
}, {
token : "string",
regex : '.+'
}
]
};
};
oop.inherits(PerlHighlightRules, TextHighlightRules);
exports.PerlHighlightRules = PerlHighlightRules;
});

View file

@ -68,16 +68,16 @@ oop.inherits(Mode, TextMode);
var deleteRange = new Range(0, 0, 0, 0);
for (var i=startRow; i<= endRow; i++)
{
var line = doc.getLine(i).replace(re, "$1");
var line = doc.getLine(i);
var m = line.match(re);
deleteRange.start.row = i;
deleteRange.end.row = i;
deleteRange.end.column = line.length + 2;
doc.replace(deleteRange, line);
deleteRange.end.column = m[0].length;
doc.replace(deleteRange, m[1]);
}
return -2;
}
else {
return doc.indentRows(startRow, endRow, "#");
doc.indentRows(startRow, endRow, "#");
}
};
@ -107,7 +107,7 @@ oop.inherits(Mode, TextMode);
};
this.autoOutdent = function(state, doc, row) {
return this.$outdent.autoOutdent(doc, row);
this.$outdent.autoOutdent(doc, row);
};
}).call(Mode.prototype);

View file

@ -33,7 +33,7 @@
* 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 *****
* ***** END LICENSE BLOCK *****
*/
define(function(require, exports, module) {
@ -43,7 +43,7 @@ var lang = require("pilot/lang");
var DocCommentHighlightRules = require("ace/mode/doc_comment_highlight_rules").DocCommentHighlightRules;
var TextHighlightRules = require("ace/mode/text_highlight_rules").TextHighlightRules;
PhpHighlightRules = function() {
var PhpHighlightRules = function() {
var docComment = new DocCommentHighlightRules();
@ -443,18 +443,18 @@ PhpHighlightRules = function() {
'include|include_once|global|goto|if|implements|interface|instanceof|namespace|new|old_function|or|' +
'private|protected|public|return|require|require_once|static|switch|throw|try|use|var|while|xor').split('|')
);
var builtinConstants = lang.arrayToMap(
('true|false|null|__FILE__|__LINE__|__METHOD__|__FUNCTION__|__CLASS__').split('|')
);
var builtinVariables = lang.arrayToMap(
('$_GLOBALS|$_SERVER|$_GET|$_POST|$_FILES|$_REQUEST|$_SESSION|$_ENV|$_COOKIE|$php_errormsg|$HTTP_RAW_POST_DATA|' +
'$http_response_header|$argc|$argv').split('|')
);
var futureReserved = lang.arrayToMap([]);
// regexp must not have capturing parentheses. Use (?:) instead.
// regexps are ordered -> the first match is used
@ -472,6 +472,10 @@ PhpHighlightRules = function() {
token : "comment",
regex : "\\/\\/.*$"
},
{
token : "comment",
regex : "#.*$"
},
docComment.getStartRule("doc-start"),
{
token : "comment", // multi line comment
@ -503,13 +507,13 @@ PhpHighlightRules = function() {
}, {
token : "constant.language", // constants
regex : "\\b(?:DEFAULT_INCLUDE_PATH|E_(?:ALL|CO(?:MPILE_(?:ERROR|WARNING)|RE_(?:ERROR|WARNING))|" +
"ERROR|NOTICE|PARSE|STRICT|USER_(?:ERROR|NOTICE|WARNING)|WARNING)|P(?:EAR_(?:EXTENSION_DIR|INSTALL_DIR)|" +
"HP_(?:BINDIR|CONFIG_FILE_(?:PATH|SCAN_DIR)|DATADIR|E(?:OL|XTENSION_DIR)|INT_(?:MAX|SIZE)|" +
"ERROR|NOTICE|PARSE|STRICT|USER_(?:ERROR|NOTICE|WARNING)|WARNING)|P(?:EAR_(?:EXTENSION_DIR|INSTALL_DIR)|" +
"HP_(?:BINDIR|CONFIG_FILE_(?:PATH|SCAN_DIR)|DATADIR|E(?:OL|XTENSION_DIR)|INT_(?:MAX|SIZE)|" +
"L(?:IBDIR|OCALSTATEDIR)|O(?:S|UTPUT_HANDLER_(?:CONT|END|START))|PREFIX|S(?:API|HLIB_SUFFIX|YSCONFDIR)|" +
"VERSION))|__COMPILER_HALT_OFFSET__)\\b"
}, {
token : "constant.language", // constants
regex : "\\b(?:A(?:B(?:DAY_(?:1|2|3|4|5|6|7)|MON_(?:1(?:0|1|2|)|2|3|4|5|6|7|8|9))|LT_DIGITS|M_STR|" +
regex : "\\b(?:A(?:B(?:DAY_(?:1|2|3|4|5|6|7)|MON_(?:1(?:0|1|2|)|2|3|4|5|6|7|8|9))|LT_DIGITS|M_STR|" +
"SSERT_(?:ACTIVE|BAIL|CALLBACK|QUIET_EVAL|WARNING))|C(?:ASE_(?:LOWER|UPPER)|HAR_MAX|" +
"O(?:DESET|NNECTION_(?:ABORTED|NORMAL|TIMEOUT)|UNT_(?:NORMAL|RECURSIVE))|" +
"R(?:EDITS_(?:ALL|DOCS|FULLPAGE|G(?:ENERAL|ROUP)|MODULES|QA|SAPI)|NCYSTR|" +
@ -527,15 +531,15 @@ PhpHighlightRules = function() {
"T(?:HOUS(?:ANDS_SEP|EP)|_FMT(?:_AMPM|))|YES(?:EXPR|STR)|STD(?:IN|OUT|ERR))\\b"
}, {
token : function(value) {
if (keywords[value])
if (keywords.hasOwnProperty(value))
return "keyword";
else if (builtinConstants[value])
else if (builtinConstants.hasOwnProperty(value))
return "constant.language";
else if (builtinVariables[value])
else if (builtinVariables.hasOwnProperty(value))
return "variable.language";
else if (futureReserved[value])
else if (futureReserved.hasOwnProperty(value))
return "invalid.illegal";
else if (builtinFunctions[value])
else if (builtinFunctions.hasOwnProperty(value))
return "support.function";
else if (value == "debugger")
return "invalid.deprecated";

View file

@ -20,6 +20,7 @@
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
* Colin Gourlay <colin DOT j DOT gourlay AT gmail DOT com>
*
* 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
@ -68,16 +69,16 @@ oop.inherits(Mode, TextMode);
var deleteRange = new Range(0, 0, 0, 0);
for (var i=startRow; i<= endRow; i++)
{
var line = doc.getLine(i).replace(re, "$1");
var line = doc.getLine(i);
var m = line.match(re);
deleteRange.start.row = i;
deleteRange.end.row = i;
deleteRange.end.column = line.length + 2;
doc.replace(deleteRange, line);
deleteRange.end.column = m[0].length;
doc.replace(deleteRange, m[1]);
}
return -2;
}
else {
return doc.indentRows(startRow, endRow, "#");
doc.indentRows(startRow, endRow, "#");
}
};
@ -107,7 +108,7 @@ oop.inherits(Mode, TextMode);
};
this.autoOutdent = function(state, doc, row) {
return this.$outdent.autoOutdent(doc, row);
this.$outdent.autoOutdent(doc, row);
};
}).call(Mode.prototype);

View file

@ -20,6 +20,7 @@
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
* Colin Gourlay <colin DOT j DOT gourlay AT gmail DOT com>
*
* 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
@ -44,7 +45,7 @@ var oop = require("pilot/oop");
var lang = require("pilot/lang");
var TextHighlightRules = require("ace/mode/text_highlight_rules").TextHighlightRules;
PythonHighlightRules = function() {
var PythonHighlightRules = function() {
var keywords = lang.arrayToMap(
("and|as|assert|break|class|continue|def|del|elif|else|except|exec|" +
@ -71,7 +72,7 @@ PythonHighlightRules = function() {
("").split("|")
);
var strPre = "(?:(?:[rubRUB])|(?:[ubUB][rR]))?";
var strPre = "(?:r|u|ur|R|U|UR|Ur|uR)?";
var decimalInteger = "(?:(?:[1-9]\\d*)|(?:0))";
var octInteger = "(?:0[oO]?[0-7]+)";
@ -92,24 +93,24 @@ PythonHighlightRules = function() {
regex : "#.*$"
}, {
token : "string", // """ string
regex : strPre + '"{3}(?:(?:.)|(?:^"{3}))*?"{3}'
regex : strPre + '"{3}(?:[^\\\\]|\\\\.)*?"{3}'
}, {
token : "string", // multi line """ string start
regex : strPre + '"{3}.*$',
next : "qqstring"
}, {
token : "string", // " string
regex : strPre + '"(?:(?:\\\\.)|(?:[^"\\\\]))*?"'
regex : strPre + '"(?:[^\\\\]|\\\\.)*?"'
}, {
token : "string", // ''' string
regex : strPre + "'{3}(?:(?:.)|(?:^'{3}))*?'{3}"
regex : strPre + "'{3}(?:[^\\\\]|\\\\.)*?'{3}"
}, {
token : "string", // multi line ''' string start
regex : strPre + "'{3}.*$",
next : "qstring"
}, {
token : "string", // ' string
regex : strPre + "'(?:(?:\\\\.)|(?:[^'\\\\]))*?'"
regex : strPre + "'(?:[^\\\\]|\\\\.)*?'"
}, {
token : "constant.numeric", // imaginary
regex : "(?:" + floatNumber + "|\\d+)[jJ]\\b"
@ -124,13 +125,13 @@ PythonHighlightRules = function() {
regex : integer + "\\b"
}, {
token : function(value) {
if (keywords[value])
if (keywords.hasOwnProperty(value))
return "keyword";
else if (builtinConstants[value])
else if (builtinConstants.hasOwnProperty(value))
return "constant.language";
else if (futureReserved[value])
else if (futureReserved.hasOwnProperty(value))
return "invalid.illegal";
else if (builtinFunctions[value])
else if (builtinFunctions.hasOwnProperty(value))
return "support.function";
else if (value == "debugger")
return "invalid.deprecated";
@ -152,8 +153,8 @@ PythonHighlightRules = function() {
regex : "\\s+"
} ],
"qqstring" : [ {
token : "string", // multi line """ string end
regex : '(?:^"{3})*?"{3}',
token : "string", // multi line """ string end
regex : '(?:[^\\\\]|\\\\.)*?"{3}',
next : "start"
}, {
token : "string",
@ -161,7 +162,7 @@ PythonHighlightRules = function() {
} ],
"qstring" : [ {
token : "string", // multi line ''' string end
regex : "(?:^'{3})*?'{3}",
regex : "(?:[^\\\\]|\\\\.)*?'{3}",
next : "start"
}, {
token : "string",

117
lib/ace/mode/ruby.js Normal file
View file

@ -0,0 +1,117 @@
/* ***** 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>
* Shlomo Zalman Heigh <shlomozalmanheigh AT gmail DOT com>
*
* 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 TextMode = require("ace/mode/text").Mode;
var Tokenizer = require("ace/tokenizer").Tokenizer;
var RubyHighlightRules = require("ace/mode/ruby_highlight_rules").RubyHighlightRules;
var MatchingBraceOutdent = require("ace/mode/matching_brace_outdent").MatchingBraceOutdent;
var Range = require("ace/range").Range;
var Mode = function() {
this.$tokenizer = new Tokenizer(new RubyHighlightRules().getRules());
this.$outdent = new MatchingBraceOutdent();
};
oop.inherits(Mode, TextMode);
(function() {
this.toggleCommentLines = function(state, doc, startRow, endRow) {
var outdent = true;
var outentedRows = [];
var re = /^(\s*)#/;
for (var i=startRow; i<= endRow; i++) {
if (!re.test(doc.getLine(i))) {
outdent = false;
break;
}
}
if (outdent) {
var deleteRange = new Range(0, 0, 0, 0);
for (var i=startRow; i<= endRow; i++)
{
var line = doc.getLine(i);
var m = line.match(re);
deleteRange.start.row = i;
deleteRange.end.row = i;
deleteRange.end.column = m[0].length;
doc.replace(deleteRange, m[1]);
}
}
else {
doc.indentRows(startRow, endRow, "#");
}
};
this.getNextLineIndent = function(state, line, tab) {
var indent = this.$getIndent(line);
var tokenizedLine = this.$tokenizer.getLineTokens(line, state);
var tokens = tokenizedLine.tokens;
var endState = tokenizedLine.state;
if (tokens.length && tokens[tokens.length-1].type == "comment") {
return indent;
}
if (state == "start") {
var match = line.match(/^.*[\{\(\[]\s*$/);
if (match) {
indent += tab;
}
}
return indent;
};
this.checkOutdent = function(state, line, input) {
return this.$outdent.checkOutdent(line, input);
};
this.autoOutdent = function(state, doc, row) {
this.$outdent.autoOutdent(doc, row);
};
}).call(Mode.prototype);
exports.Mode = Mode;
});

View file

@ -0,0 +1,148 @@
/* ***** 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>
* Shlomo Zalman Heigh <shlomozalmanheigh AT gmail DOT com>
*
* 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 TextHighlightRules = require("ace/mode/text_highlight_rules").TextHighlightRules;
var RubyHighlightRules = function() {
var builtinFunctions = lang.arrayToMap(
("abort|Array|at_exit|autoload|binding|block_given?|callcc|caller|catch|chomp|chomp!|chop|chop!|eval|exec|exit|exit!" +
"fail|Float|fork|format|gets|global_variables|gsub|gsub!|Integer|lambda|load|local_variables|loop|open|p|print|" +
"printf|proc|putc|puts|raise|rand|readline|readlines|require|scan|select|set_trace_func|sleep|split|sprintf|srand|" +
"String|syscall|system|sub|sub!|test|throw|trace_var|trap|untrace_var|" +
"atan2|cos|exp|frexp|ldexp|log|log10|sin|sqrt|tan").split("|")
);
var keywords = lang.arrayToMap(
("alias|and|BEGIN|begin|break|case|class|def|defined|do|else|elsif|END|end|ensure|__FILE__|finally|for|" +
"if|in|__LINE__|module|next|not|or|redo|rescue|retry|return|super|then|undef|unless|until|when|while|yield").split("|")
);
var buildinConstants = lang.arrayToMap(
("true|TRUE|false|FALSE|nil|NIL|ARGF|ARGV|DATA|ENV|RUBY_PLATFORM|RUBY_RELEASE_DATE|RUBY_VERSION|STDERR|STDIN|STDOUT|TOPLEVEL_BINDING").split("|")
);
var builtinVariables = lang.arrayToMap(
("\$DEBUG|\$defout|\$FILENAME|\$LOAD_PATH|\$SAFE|\$stdin|\$stdout|\$stderr|\$VERBOSE").split("|")
);
// regexp must not have capturing parentheses. Use (?:) instead.
// regexps are ordered -> the first match is used
this.$rules = {
"start" : [
{
token : "comment",
regex : "#.*$"
}, {
token : "comment", // multi line comment
regex : "^\=begin$",
next : "comment"
}, {
token : "string.regexp",
regex : "[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"
}, {
token : "string", // single line
regex : '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'
}, {
token : "string", // single line
regex : "['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"
}, {
token : "constant.numeric", // hex
regex : "0[xX][0-9a-fA-F]+\\b"
}, {
token : "constant.numeric", // float
regex : "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"
}, {
token : "constant.language.boolean",
regex : "(?:true|false)\\b"
}, {
token : function(value) {
if (value == "self")
return "variable.language";
else if (keywords.hasOwnProperty(value))
return "keyword";
else if (buildinConstants.hasOwnProperty(value))
return "constant.language";
else if (builtinVariables.hasOwnProperty(value))
return "variable.language";
else if (builtinFunctions.hasOwnProperty(value))
return "support.function";
else if (value == "debugger")
return "invalid.deprecated";
else
return "identifier";
},
// TODO: Unicode escape sequences
// TODO: Unicode identifiers
regex : "[a-zA-Z_$][a-zA-Z0-9_$]*\\b"
}, {
token : "keyword.operator",
regex : "!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"
}, {
token : "lparen",
regex : "[[({]"
}, {
token : "rparen",
regex : "[\\])}]"
}, {
token : "text",
regex : "\\s+"
}
],
"comment" : [
{
token : "comment", // closing comment
regex : "^\=end$",
next : "start"
}, {
token : "comment", // comment spanning whole line
regex : ".+"
}
]
};
};
oop.inherits(RubyHighlightRules, TextHighlightRules);
exports.RubyHighlightRules = RubyHighlightRules;
});

View file

@ -1,4 +1,5 @@
/* ***** BEGIN LICENSE BLOCK *****
/* vim:ts=4:sts=4:sw=4:
* ***** 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
@ -20,6 +21,7 @@
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
* Mihai Sucan <mihai DOT sucan AT gmail DOT com>
*
* 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
@ -51,7 +53,6 @@ var Mode = function() {
};
this.toggleCommentLines = function(state, doc, startRow, endRow) {
return 0;
};
this.getNextLineIndent = function(state, line, tab) {
@ -73,6 +74,72 @@ var Mode = function() {
return "";
};
this.createWorker = function(session) {
return null;
};
this.highlightSelection = function(editor) {
var session = editor.session;
if (!session.$selectionOccurrences)
session.$selectionOccurrences = [];
if (session.$selectionOccurrences.length)
this.clearSelectionHighlight(editor);
var selection = editor.getSelectionRange();
if (selection.isEmpty() || selection.isMultiLine())
return;
var startOuter = selection.start.column - 1;
var endOuter = selection.end.column + 1;
var line = session.getLine(selection.start.row);
var lineCols = line.length;
var needle = line.substring(Math.max(startOuter, 0),
Math.min(endOuter, lineCols));
// Make sure the outer characters are not part of the word.
if ((startOuter >= 0 && /^[\w\d]/.test(needle)) ||
(endOuter <= lineCols && /[\w\d]$/.test(needle)))
return;
needle = line.substring(selection.start.column, selection.end.column);
if (!/^[\w\d]+$/.test(needle))
return;
var cursor = editor.getCursorPosition();
var newOptions = {
wrap: true,
wholeWord: true,
caseSensitive: true,
needle: needle
};
var currentOptions = editor.$search.getOptions();
editor.$search.set(newOptions);
var ranges = editor.$search.findAll(session);
ranges.forEach(function(range) {
if (!range.contains(cursor.row, cursor.column)) {
var marker = session.addMarker(range, "ace_selected_word");
session.$selectionOccurrences.push(marker);
}
});
editor.$search.set(currentOptions);
};
this.clearSelectionHighlight = function(editor) {
if (!editor.session.$selectionOccurrences)
return;
editor.session.$selectionOccurrences.forEach(function(marker) {
editor.session.removeMarker(marker);
});
editor.session.$selectionOccurrences = [];
};
}).call(Mode.prototype);

View file

@ -44,6 +44,9 @@ var TextHighlightRules = function() {
this.$rules = {
"start" : [ {
token : "empty_line",
regex : '^$'
}, {
token : "text",
regex : ".+"
} ]

174
lib/ace/mouse_handler.js Normal file
View file

@ -0,0 +1,174 @@
/* ***** 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 event = require("pilot/event");
var MouseHandler = function(editor) {
this.editor = editor;
event.addListener(editor.container, "mousedown", function(e) {
editor.focus();
return event.preventDefault(e);
});
event.addListener(editor.container, "selectstart", function(e) {
return event.preventDefault(e);
});
var mouseTarget = editor.renderer.getMouseEventTarget();
event.addListener(mouseTarget, "mousedown", this.onMouseDown.bind(this));
event.addMultiMouseDownListener(mouseTarget, 0, 2, 500, this.onMouseDoubleClick.bind(this));
event.addMultiMouseDownListener(mouseTarget, 0, 3, 600, this.onMouseTripleClick.bind(this));
event.addMultiMouseDownListener(mouseTarget, 0, 4, 600, this.onMouseQuadClick.bind(this));
event.addMouseWheelListener(mouseTarget, this.onMouseWheel.bind(this));
};
(function() {
this.$scrollSpeed = 1;
this.setScrollSpeed = function(speed) {
this.$scrollSpeed = speed;
};
this.getScrollSpeed = function() {
return this.$scrollSpeed;
};
this.onMouseDown = function(e) {
var pageX = event.getDocumentX(e);
var pageY = event.getDocumentY(e);
var editor = this.editor;
var pos = editor.renderer.screenToTextCoordinates(pageX, pageY);
pos.row = Math.max(0, Math.min(pos.row, editor.session.getLength()-1));
var button = event.getButton(e)
if (button != 0) {
var isEmpty = editor.selection.isEmpty()
if (isEmpty) {
editor.moveCursorToPosition(pos);
}
if(button == 2) {
editor.textInput.onContextMenu({x: pageX, y: pageY}, isEmpty);
event.capture(editor.container, function(){}, editor.textInput.onContextMenuClose);
}
return;
}
if (e.shiftKey)
editor.selection.selectToPosition(pos)
else {
editor.moveCursorToPosition(pos);
if (!editor.$clickSelection)
editor.selection.clearSelection(pos.row, pos.column);
}
editor.renderer.scrollCursorIntoView();
var self = this;
var mousePageX, mousePageY;
var onMouseSelection = function(e) {
mousePageX = event.getDocumentX(e);
mousePageY = event.getDocumentY(e);
};
var onMouseSelectionEnd = function() {
clearInterval(timerId);
self.$clickSelection = null;
};
var onSelectionInterval = function() {
if (mousePageX === undefined || mousePageY === undefined)
return;
var cursor = editor.renderer.screenToTextCoordinates(mousePageX, mousePageY);
cursor.row = Math.max(0, Math.min(cursor.row, editor.session.getLength()-1));
if (self.$clickSelection) {
if (self.$clickSelection.contains(cursor.row, cursor.column)) {
editor.selection.setSelectionRange(self.$clickSelection);
} else {
if (self.$clickSelection.compare(cursor.row, cursor.column) == -1) {
var anchor = self.$clickSelection.end;
} else {
var anchor = self.$clickSelection.start;
}
editor.selection.setSelectionAnchor(anchor.row, anchor.column);
editor.selection.selectToPosition(cursor);
}
}
else {
editor.selection.selectToPosition(cursor);
}
editor.renderer.scrollCursorIntoView();
};
event.capture(editor.container, onMouseSelection, onMouseSelectionEnd);
var timerId = setInterval(onSelectionInterval, 20);
return event.preventDefault(e);
};
this.onMouseDoubleClick = function(e) {
this.editor.selection.selectWord();
this.$clickSelection = this.editor.getSelectionRange();
};
this.onMouseTripleClick = function(e) {
this.editor.selection.selectLine();
this.$clickSelection = this.editor.getSelectionRange();
};
this.onMouseQuadClick = function(e) {
this.editor.selectAll();
this.$clickSelection = this.editor.getSelectionRange();
};
this.onMouseWheel = function(e) {
var speed = this.$scrollSpeed * 2;
this.editor.renderer.scrollBy(e.wheelX * speed, e.wheelY * speed);
return event.preventDefault(e);
};
}).call(MouseHandler.prototype);
exports.MouseHandler = MouseHandler;
});

375
lib/ace/narcissus/jsdefs.js Normal file
View file

@ -0,0 +1,375 @@
/* vim: set sw=4 ts=4 et tw=78: */
/* ***** 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 the Narcissus JavaScript engine.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>.
* Portions created by the Initial Developer are Copyright (C) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Tom Austin <taustin@ucsc.edu>
* Brendan Eich <brendan@mozilla.org>
* Shu-Yu Guo <shu@rfrn.org>
* Dave Herman <dherman@mozilla.com>
* Dimitris Vardoulakis <dimvar@ccs.neu.edu>
* Patrick Walton <pcwalton@mozilla.com>
*
* 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 ***** */
/*
* Narcissus - JS implemented in JS.
*
* Well-known constants and lookup tables. Many consts are generated from the
* tokens table via eval to minimize redundancy, so consumers must be compiled
* separately to take advantage of the simple switch-case constant propagation
* done by SpiderMonkey.
*/
define(function(require, exports, module) {
exports.options = {
version: 185,
};
(function() {
exports.hostGlobal = this
})();
var tokens = [
// End of source.
"END",
// Operators and punctuators. Some pair-wise order matters, e.g. (+, -)
// and (UNARY_PLUS, UNARY_MINUS).
"\n", ";",
",",
"=",
"?", ":", "CONDITIONAL",
"||",
"&&",
"|",
"^",
"&",
"==", "!=", "===", "!==",
"<", "<=", ">=", ">",
"<<", ">>", ">>>",
"+", "-",
"*", "/", "%",
"!", "~", "UNARY_PLUS", "UNARY_MINUS",
"++", "--",
".",
"[", "]",
"{", "}",
"(", ")",
// Nonterminal tree node type codes.
"SCRIPT", "BLOCK", "LABEL", "FOR_IN", "CALL", "NEW_WITH_ARGS", "INDEX",
"ARRAY_INIT", "OBJECT_INIT", "PROPERTY_INIT", "GETTER", "SETTER",
"GROUP", "LIST", "LET_BLOCK", "ARRAY_COMP", "GENERATOR", "COMP_TAIL",
// Terminals.
"IDENTIFIER", "NUMBER", "STRING", "REGEXP",
// Keywords.
"break",
"case", "catch", "const", "continue",
"debugger", "default", "delete", "do",
"else",
"false", "finally", "for", "function",
"if", "in", "instanceof",
"let",
"new", "null",
"return",
"switch",
"this", "throw", "true", "try", "typeof",
"var", "void",
"yield",
"while", "with",
];
var statementStartTokens = [
"break",
"const", "continue",
"debugger", "do",
"for",
"if",
"return",
"switch",
"throw", "try",
"var",
"yield",
"while", "with",
];
// Operator and punctuator mapping from token to tree node type name.
// NB: because the lexer doesn't backtrack, all token prefixes must themselves
// be valid tokens (e.g. !== is acceptable because its prefixes are the valid
// tokens != and !).
var opTypeNames = {
'\n': "NEWLINE",
';': "SEMICOLON",
',': "COMMA",
'?': "HOOK",
':': "COLON",
'||': "OR",
'&&': "AND",
'|': "BITWISE_OR",
'^': "BITWISE_XOR",
'&': "BITWISE_AND",
'===': "STRICT_EQ",
'==': "EQ",
'=': "ASSIGN",
'!==': "STRICT_NE",
'!=': "NE",
'<<': "LSH",
'<=': "LE",
'<': "LT",
'>>>': "URSH",
'>>': "RSH",
'>=': "GE",
'>': "GT",
'++': "INCREMENT",
'--': "DECREMENT",
'+': "PLUS",
'-': "MINUS",
'*': "MUL",
'/': "DIV",
'%': "MOD",
'!': "NOT",
'~': "BITWISE_NOT",
'.': "DOT",
'[': "LEFT_BRACKET",
']': "RIGHT_BRACKET",
'{': "LEFT_CURLY",
'}': "RIGHT_CURLY",
'(': "LEFT_PAREN",
')': "RIGHT_PAREN"
};
// Hash of keyword identifier to tokens index. NB: we must null __proto__ to
// avoid toString, etc. namespace pollution.
var keywords = {__proto__: null};
// Define const END, etc., based on the token names. Also map name to index.
var tokenIds = {};
// Building up a string to be eval'd in different contexts.
var consts = "const ";
for (var i = 0, j = tokens.length; i < j; i++) {
if (i > 0)
consts += ", ";
var t = tokens[i];
var name;
if (/^[a-z]/.test(t)) {
name = t.toUpperCase();
keywords[t] = i;
} else {
name = (/^\W/.test(t) ? opTypeNames[t] : t);
}
consts += name + " = " + i;
tokenIds[name] = i;
tokens[t] = i;
}
consts += ";";
var isStatementStartCode = {__proto__: null};
for (i = 0, j = statementStartTokens.length; i < j; i++)
isStatementStartCode[keywords[statementStartTokens[i]]] = true;
// Map assignment operators to their indexes in the tokens array.
var assignOps = ['|', '^', '&', '<<', '>>', '>>>', '+', '-', '*', '/', '%'];
for (i = 0, j = assignOps.length; i < j; i++) {
t = assignOps[i];
assignOps[t] = tokens[t];
}
function defineGetter(obj, prop, fn, dontDelete, dontEnum) {
Object.defineProperty(obj, prop,
{ get: fn, configurable: !dontDelete, enumerable: !dontEnum });
}
function defineProperty(obj, prop, val, dontDelete, readOnly, dontEnum) {
Object.defineProperty(obj, prop,
{ value: val, writable: !readOnly, configurable: !dontDelete,
enumerable: !dontEnum });
}
// Returns true if fn is a native function. (Note: SpiderMonkey specific.)
function isNativeCode(fn) {
// Relies on the toString method to identify native code.
return ((typeof fn) === "function") && fn.toString().match(/\[native code\]/);
}
function getPropertyDescriptor(obj, name) {
while (obj) {
if (({}).hasOwnProperty.call(obj, name))
return Object.getOwnPropertyDescriptor(obj, name);
obj = Object.getPrototypeOf(obj);
}
}
function getOwnProperties(obj) {
var map = {};
for (var name in Object.getOwnPropertyNames(obj))
map[name] = Object.getOwnPropertyDescriptor(obj, name);
return map;
}
function makePassthruHandler(obj) {
// Handler copied from
// http://wiki.ecmascript.org/doku.php?id=harmony:proxies&s=proxy%20object#examplea_no-op_forwarding_proxy
return {
getOwnPropertyDescriptor: function(name) {
var desc = Object.getOwnPropertyDescriptor(obj, name);
// a trapping proxy's properties must always be configurable
desc.configurable = true;
return desc;
},
getPropertyDescriptor: function(name) {
var desc = getPropertyDescriptor(obj, name);
// a trapping proxy's properties must always be configurable
desc.configurable = true;
return desc;
},
getOwnPropertyNames: function() {
return Object.getOwnPropertyNames(obj);
},
defineProperty: function(name, desc) {
Object.defineProperty(obj, name, desc);
},
"delete": function(name) { return delete obj[name]; },
fix: function() {
if (Object.isFrozen(obj)) {
return getOwnProperties(obj);
}
// As long as obj is not frozen, the proxy won't allow itself to be fixed.
return undefined; // will cause a TypeError to be thrown
},
has: function(name) { return name in obj; },
hasOwn: function(name) { return ({}).hasOwnProperty.call(obj, name); },
get: function(receiver, name) { return obj[name]; },
// bad behavior when set fails in non-strict mode
set: function(receiver, name, val) { obj[name] = val; return true; },
enumerate: function() {
var result = [];
for (name in obj) { result.push(name); };
return result;
},
keys: function() { return Object.keys(obj); }
};
}
// default function used when looking for a property in the global object
function noPropFound() { return undefined; }
var hasOwnProperty = ({}).hasOwnProperty;
function StringMap() {
this.table = Object.create(null, {});
this.size = 0;
}
StringMap.prototype = {
has: function(x) { return hasOwnProperty.call(this.table, x); },
set: function(x, v) {
if (!hasOwnProperty.call(this.table, x))
this.size++;
this.table[x] = v;
},
get: function(x) { return this.table[x]; },
getDef: function(x, thunk) {
if (!hasOwnProperty.call(this.table, x)) {
this.size++;
this.table[x] = thunk();
}
return this.table[x];
},
forEach: function(f) {
var table = this.table;
for (var key in table)
f.call(this, key, table[key]);
},
toString: function() { return "[object StringMap]" }
};
// non-destructive stack
function Stack(elts) {
this.elts = elts || null;
}
Stack.prototype = {
push: function(x) {
return new Stack({ top: x, rest: this.elts });
},
top: function() {
if (!this.elts)
throw new Error("empty stack");
return this.elts.top;
},
isEmpty: function() {
return this.top === null;
},
find: function(test) {
for (var elts = this.elts; elts; elts = elts.rest) {
if (test(elts.top))
return elts.top;
}
return null;
},
has: function(x) {
return Boolean(this.find(function(elt) { return elt === x }));
},
forEach: function(f) {
for (var elts = this.elts; elts; elts = elts.rest) {
f(elts.top);
}
}
};
exports.tokens = tokens;
exports.opTypeNames = opTypeNames;
exports.keywords = keywords;
exports.isStatementStartCode = isStatementStartCode;
exports.tokenIds = tokenIds;
exports.consts = consts;
exports.assignOps = assignOps;
exports.defineGetter = defineGetter;
exports.defineProperty = defineProperty;
exports.isNativeCode = isNativeCode;
exports.makePassthruHandler = makePassthruHandler;
exports.noPropFound = noPropFound;
exports.StringMap = StringMap;
exports.Stack = Stack;
});

460
lib/ace/narcissus/jslex.js Normal file
View file

@ -0,0 +1,460 @@
/* vim: set sw=4 ts=4 et tw=78: */
/* ***** 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 the Narcissus JavaScript engine.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>.
* Portions created by the Initial Developer are Copyright (C) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Tom Austin <taustin@ucsc.edu>
* Brendan Eich <brendan@mozilla.org>
* Shu-Yu Guo <shu@rfrn.org>
* Dave Herman <dherman@mozilla.com>
* Dimitris Vardoulakis <dimvar@ccs.neu.edu>
* Patrick Walton <pcwalton@mozilla.com>
*
* 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 ***** */
/*
* Narcissus - JS implemented in JS.
*
* Lexical scanner.
*/
define(function(require, exports, module) {
var definitions = require("ace/narcissus/jsdefs");
// Set constants in the local scope.
eval(definitions.consts);
// Build up a trie of operator tokens.
var opTokens = {};
for (var op in definitions.opTypeNames) {
if (op === '\n' || op === '.')
continue;
var node = opTokens;
for (var i = 0; i < op.length; i++) {
var ch = op[i];
if (!(ch in node))
node[ch] = {};
node = node[ch];
node.op = op;
}
}
/*
* Tokenizer :: (source, filename, line number) -> Tokenizer
*/
function Tokenizer(s, f, l) {
this.cursor = 0;
this.source = String(s);
this.tokens = [];
this.tokenIndex = 0;
this.lookahead = 0;
this.scanNewlines = false;
this.unexpectedEOF = false;
this.filename = f || "";
this.lineno = l || 1;
}
Tokenizer.prototype = {
get done() {
// We need to set scanOperand to true here because the first thing
// might be a regexp.
return this.peek(true) === END;
},
get token() {
return this.tokens[this.tokenIndex];
},
match: function (tt, scanOperand) {
return this.get(scanOperand) === tt || this.unget();
},
mustMatch: function (tt) {
if (!this.match(tt)) {
throw this.newSyntaxError("Missing " +
definitions.tokens[tt].toLowerCase());
}
return this.token;
},
peek: function (scanOperand) {
var tt, next;
if (this.lookahead) {
next = this.tokens[(this.tokenIndex + this.lookahead) & 3];
tt = (this.scanNewlines && next.lineno !== this.lineno)
? NEWLINE
: next.type;
} else {
tt = this.get(scanOperand);
this.unget();
}
return tt;
},
peekOnSameLine: function (scanOperand) {
this.scanNewlines = true;
var tt = this.peek(scanOperand);
this.scanNewlines = false;
return tt;
},
// Eat comments and whitespace.
skip: function () {
var input = this.source;
for (;;) {
var ch = input[this.cursor++];
var next = input[this.cursor];
if (ch === '\n' && !this.scanNewlines) {
this.lineno++;
} else if (ch === '/' && next === '*') {
this.cursor++;
for (;;) {
ch = input[this.cursor++];
if (ch === undefined)
throw this.newSyntaxError("Unterminated comment");
if (ch === '*') {
next = input[this.cursor];
if (next === '/') {
this.cursor++;
break;
}
} else if (ch === '\n') {
this.lineno++;
}
}
} else if (ch === '/' && next === '/') {
this.cursor++;
for (;;) {
ch = input[this.cursor++];
if (ch === undefined)
return;
if (ch === '\n') {
this.lineno++;
break;
}
}
} else if (ch !== ' ' && ch !== '\t') {
this.cursor--;
return;
}
}
},
// Lex the exponential part of a number, if present. Return true iff an
// exponential part was found.
lexExponent: function() {
var input = this.source;
var next = input[this.cursor];
if (next === 'e' || next === 'E') {
this.cursor++;
ch = input[this.cursor++];
if (ch === '+' || ch === '-')
ch = input[this.cursor++];
if (ch < '0' || ch > '9')
throw this.newSyntaxError("Missing exponent");
do {
ch = input[this.cursor++];
} while (ch >= '0' && ch <= '9');
this.cursor--;
return true;
}
return false;
},
lexZeroNumber: function (ch) {
var token = this.token, input = this.source;
token.type = NUMBER;
ch = input[this.cursor++];
if (ch === '.') {
do {
ch = input[this.cursor++];
} while (ch >= '0' && ch <= '9');
this.cursor--;
this.lexExponent();
token.value = parseFloat(token.start, this.cursor);
} else if (ch === 'x' || ch === 'X') {
do {
ch = input[this.cursor++];
} while ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
(ch >= 'A' && ch <= 'F'));
this.cursor--;
token.value = parseInt(input.substring(token.start, this.cursor));
} else if (ch >= '0' && ch <= '7') {
do {
ch = input[this.cursor++];
} while (ch >= '0' && ch <= '7');
this.cursor--;
token.value = parseInt(input.substring(token.start, this.cursor));
} else {
this.cursor--;
this.lexExponent(); // 0E1, &c.
token.value = 0;
}
},
lexNumber: function (ch) {
var token = this.token, input = this.source;
token.type = NUMBER;
var floating = false;
do {
ch = input[this.cursor++];
if (ch === '.' && !floating) {
floating = true;
ch = input[this.cursor++];
}
} while (ch >= '0' && ch <= '9');
this.cursor--;
var exponent = this.lexExponent();
floating = floating || exponent;
var str = input.substring(token.start, this.cursor);
token.value = floating ? parseFloat(str) : parseInt(str);
},
lexDot: function (ch) {
var token = this.token, input = this.source;
var next = input[this.cursor];
if (next >= '0' && next <= '9') {
do {
ch = input[this.cursor++];
} while (ch >= '0' && ch <= '9');
this.cursor--;
this.lexExponent();
token.type = NUMBER;
token.value = parseFloat(token.start, this.cursor);
} else {
token.type = DOT;
token.assignOp = null;
token.value = '.';
}
},
lexString: function (ch) {
var token = this.token, input = this.source;
token.type = STRING;
var hasEscapes = false;
var delim = ch;
while ((ch = input[this.cursor++]) !== delim) {
if (this.cursor == input.length)
throw this.newSyntaxError("Unterminated string literal");
if (ch === '\\') {
hasEscapes = true;
if (++this.cursor == input.length)
throw this.newSyntaxError("Unterminated string literal");
}
}
token.value = hasEscapes
? eval(input.substring(token.start, this.cursor))
: input.substring(token.start + 1, this.cursor - 1);
},
lexRegExp: function (ch) {
var token = this.token, input = this.source;
token.type = REGEXP;
do {
ch = input[this.cursor++];
if (ch === '\\') {
this.cursor++;
} else if (ch === '[') {
do {
if (ch === undefined)
throw this.newSyntaxError("Unterminated character class");
if (ch === '\\')
this.cursor++;
ch = input[this.cursor++];
} while (ch !== ']');
} else if (ch === undefined) {
throw this.newSyntaxError("Unterminated regex");
}
} while (ch !== '/');
do {
ch = input[this.cursor++];
} while (ch >= 'a' && ch <= 'z');
this.cursor--;
token.value = eval(input.substring(token.start, this.cursor));
},
lexOp: function (ch) {
var token = this.token, input = this.source;
// A bit ugly, but it seems wasteful to write a trie lookup routine
// for only 3 characters...
var node = opTokens[ch];
var next = input[this.cursor];
if (next in node) {
node = node[next];
this.cursor++;
next = input[this.cursor];
if (next in node) {
node = node[next];
this.cursor++;
next = input[this.cursor];
}
}
var op = node.op;
if (definitions.assignOps[op] && input[this.cursor] === '=') {
this.cursor++;
token.type = ASSIGN;
token.assignOp = definitions.tokenIds[definitions.opTypeNames[op]];
op += '=';
} else {
token.type = definitions.tokenIds[definitions.opTypeNames[op]];
token.assignOp = null;
}
token.value = op;
},
// FIXME: Unicode escape sequences
// FIXME: Unicode identifiers
lexIdent: function (ch) {
var token = this.token, input = this.source;
do {
ch = input[this.cursor++];
} while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') || ch === '$' || ch === '_');
this.cursor--; // Put the non-word character back.
var id = input.substring(token.start, this.cursor);
token.type = definitions.keywords[id] || IDENTIFIER;
token.value = id;
},
/*
* Tokenizer.get :: void -> token type
*
* Consume input *only* if there is no lookahead.
* Dispatch to the appropriate lexing function depending on the input.
*/
get: function (scanOperand) {
var token;
while (this.lookahead) {
--this.lookahead;
this.tokenIndex = (this.tokenIndex + 1) & 3;
token = this.tokens[this.tokenIndex];
if (token.type !== NEWLINE || this.scanNewlines)
return token.type;
}
this.skip();
this.tokenIndex = (this.tokenIndex + 1) & 3;
token = this.tokens[this.tokenIndex];
if (!token)
this.tokens[this.tokenIndex] = token = {};
var input = this.source;
if (this.cursor === input.length)
return token.type = END;
token.start = this.cursor;
token.lineno = this.lineno;
var ch = input[this.cursor++];
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch === '$' || ch === '_') {
this.lexIdent(ch);
} else if (scanOperand && ch === '/') {
this.lexRegExp(ch);
} else if (ch in opTokens) {
this.lexOp(ch);
} else if (ch === '.') {
this.lexDot(ch);
} else if (ch >= '1' && ch <= '9') {
this.lexNumber(ch);
} else if (ch === '0') {
this.lexZeroNumber(ch);
} else if (ch === '"' || ch === "'") {
this.lexString(ch);
} else if (this.scanNewlines && ch === '\n') {
token.type = NEWLINE;
token.value = '\n';
this.lineno++;
} else {
throw this.newSyntaxError("Illegal token");
}
token.end = this.cursor;
return token.type;
},
/*
* Tokenizer.unget :: void -> undefined
*
* Match depends on unget returning undefined.
*/
unget: function () {
if (++this.lookahead === 4) throw "PANIC: too much lookahead!";
this.tokenIndex = (this.tokenIndex - 1) & 3;
},
newSyntaxError: function (m) {
var e = new SyntaxError(m, this.filename, this.lineno);
e.source = this.source;
e.lineno = this.lineno;
e.cursor = this.lookahead
? this.tokens[(this.tokenIndex + this.lookahead) & 3].start
: this.cursor;
return e;
},
};
exports.Tokenizer = Tokenizer;
});

1432
lib/ace/narcissus/jsparse.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -137,7 +137,7 @@ var Range = function(startRow, startColumn, endRow, endColumn) {
this.clone = function() {
return Range.fromPoints(this.start, this.end);
};
this.collapseRows = function() {
if (this.end.column == 0)
return new Range(this.start.row, 0, Math.max(this.start.row, this.end.row-1), 0)
@ -145,10 +145,14 @@ var Range = function(startRow, startColumn, endRow, endColumn) {
return new Range(this.start.row, 0, this.end.row, 0)
};
this.toScreenRange = function(doc) {
this.toScreenRange = function(session) {
var screenPosStart =
session.documentToScreenPosition(this.start);
var screenPosEnd =
session.documentToScreenPosition(this.end);
return new Range(
this.start.row, doc.documentToScreenColumn(this.start.row, this.start.column),
this.end.row, doc.documentToScreenColumn(this.end.row, this.end.column)
screenPosStart.row, screenPosStart.column,
screenPosEnd.row, screenPosEnd.column
);
};

View file

@ -52,7 +52,7 @@ var ScrollBar = function(parent) {
parent.appendChild(this.element);
this.width = dom.scrollbarWidth();
this.element.style.width = this.width;
this.element.style.width = this.width + "px";
event.addListener(this.element, "scroll", this.onScroll.bind(this));
};
@ -69,7 +69,7 @@ var ScrollBar = function(parent) {
};
this.setHeight = function(height) {
this.element.style.height = Math.max(0, height - this.width) + "px";
this.element.style.height = height + "px";
};
this.setInnerHeight = function(height) {

View file

@ -1,4 +1,5 @@
/* ***** BEGIN LICENSE BLOCK *****
/* vim:ts=4:sts=4:sw=4:
* ***** 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
@ -20,6 +21,7 @@
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
* Mihai Sucan <mihai DOT sucan AT gmail DOT com>
*
* 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
@ -219,12 +221,16 @@ Search.SELECTION = 2;
var lastRow = searchSelection ? range.end.row : session.getLength() - 1;
var wrap = this.$options.wrap;
var inWrap = false;
function getLine(row) {
var line = session.getLine(row);
if (searchSelection && row == range.end.row) {
line = line.substring(0, range.end.column);
}
if (inWrap && row == start.row) {
line = line.substring(0, start.column);
}
return line;
}
@ -236,6 +242,7 @@ Search.SELECTION = 2;
var startIndex = start.column;
var stop = false;
inWrap = false;
while (!callback(line, startIndex, row)) {
@ -250,6 +257,7 @@ Search.SELECTION = 2;
if (wrap) {
row = firstRow;
startIndex = firstColumn;
inWrap = true;
} else {
return;
}
@ -283,6 +291,7 @@ Search.SELECTION = 2;
var line = session.getLine(row).substring(0, start.column);
var startIndex = 0;
var stop = false;
var inWrap = false;
while (!callback(line, startIndex, row)) {
@ -295,6 +304,7 @@ Search.SELECTION = 2;
if (row < firstRow) {
if (wrap) {
row = lastRow;
inWrap = true;
} else {
return;
}
@ -310,6 +320,9 @@ Search.SELECTION = 2;
else if (row == lastRow)
line = line.substring(0, range.end.column);
}
if (inWrap && row == start.row)
startIndex = start.column;
}
}
};

View file

@ -20,6 +20,7 @@
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
* Julian Viereck <julian.viereck@gmail.com>
*
* 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
@ -42,14 +43,27 @@ var lang = require("pilot/lang");
var EventEmitter = require("pilot/event_emitter").EventEmitter;
var Range = require("ace/range").Range;
var Selection = function(doc) {
this.doc = doc;
var Selection = function(session) {
this.session = session;
this.doc = session.getDocument();
this.clearSelection();
this.selectionLead = {
row: 0,
column: 0
};
this.selectionLead = this.doc.createAnchor(0, 0);
this.selectionAnchor = this.doc.createAnchor(0, 0);
var _self = this;
this.selectionLead.on("change", function(e) {
_self._dispatchEvent("changeCursor");
if (!_self.$isEmpty)
_self._dispatchEvent("changeSelection");
if (e.old.row == e.value.row)
_self.$updateDesiredColumn();
});
this.selectionAnchor.on("change", function() {
if (!_self.$isEmpty)
_self._dispatchEvent("changeSelection");
});
};
(function() {
@ -57,9 +71,10 @@ var Selection = function(doc) {
oop.implement(this, EventEmitter);
this.isEmpty = function() {
return (!this.selectionAnchor ||
(this.selectionAnchor.row == this.selectionLead.row &&
this.selectionAnchor.column == this.selectionLead.column));
return (this.$isEmpty || (
this.selectionAnchor.row == this.selectionLead.row &&
this.selectionAnchor.column == this.selectionLead.column
));
};
this.isMultiLine = function() {
@ -71,37 +86,31 @@ var Selection = function(doc) {
};
this.getCursor = function() {
return this.selectionLead;
return this.selectionLead.getPosition();
};
this.setSelectionAnchor = function(row, column) {
var anchor = this.$clipPositionToDocument(row, column);
this.selectionAnchor.setPosition(row, column);
if (!this.selectionAnchor) {
this.selectionAnchor = anchor;
this._dispatchEvent("changeSelection", {});
if (this.$isEmpty) {
this.$isEmpty = false;
this._dispatchEvent("changeSelection");
}
else if (this.selectionAnchor.row !== anchor.row || this.selectionAnchor.column !== anchor.column) {
this.selectionAnchor = anchor;
this._dispatchEvent("changeSelection", {});
}
};
this.getSelectionAnchor = function() {
if (this.selectionAnchor) {
return this.$clone(this.selectionAnchor);
} else {
return this.$clone(this.selectionLead);
}
if (this.$isEmpty)
return this.getSelectionLead()
else
return this.selectionAnchor.getPosition();
};
this.getSelectionLead = function() {
return this.$clone(this.selectionLead);
return this.selectionLead.getPosition();
};
this.shiftSelection = function(columns) {
if (this.isEmpty()) {
if (this.$isEmpty) {
this.moveCursorTo(this.selectionLead.row, this.selectionLead.column + columns);
return;
};
@ -122,15 +131,18 @@ var Selection = function(doc) {
};
this.isBackwards = function() {
var anchor = this.selectionAnchor || this.selectionLead;
var anchor = this.selectionAnchor;
var lead = this.selectionLead;
return (anchor.row > lead.row || (anchor.row == lead.row && anchor.column > lead.column));
};
this.getRange = function() {
var anchor = this.selectionAnchor || this.selectionLead;
var anchor = this.selectionAnchor;
var lead = this.selectionLead;
if (this.isEmpty())
return Range.fromPoints(lead, lead);
if (this.isBackwards()) {
return Range.fromPoints(lead, anchor);
}
@ -140,55 +152,40 @@ var Selection = function(doc) {
};
this.clearSelection = function() {
if (this.selectionAnchor) {
this.selectionAnchor = null;
this._dispatchEvent("changeSelection", {});
if (!this.$isEmpty) {
this.$isEmpty = true;
this._dispatchEvent("changeSelection");
}
};
this.selectAll = function() {
var lastRow = this.doc.getLength() - 1;
this.setSelectionAnchor(lastRow, this.doc.getLine(lastRow).length);
if (!this.selectionAnchor) {
this.selectionAnchor = this.$clone(this.selectionLead);
}
var cursor = {row:0, column:0};
// only dispatch change if the cursor actually changed
if (cursor.row !== this.selectionLead.row || cursor.column !== this.selectionLead.column) {
this.selectionLead = cursor;
this._dispatchEvent("changeSelection", {blockScrolling: true});
}
this.moveCursorTo(0, 0);
};
this.setSelectionRange = function(range, reverse) {
if (reverse) {
this.setSelectionAnchor(range.end.row, range.end.column);
this.selectTo(range.start.row, range.start.column);
this.selectTo(range.start.row, range.start.column);
} else {
this.setSelectionAnchor(range.start.row, range.start.column);
this.selectTo(range.end.row, range.end.column);
}
this.$updateDesiredColumn();
};
this.$updateDesiredColumn = function() {
var cursor = this.getCursor();
this.$desiredColumn = this.session.documentToScreenColumn(cursor.row, cursor.column);
};
this.$moveSelection = function(mover) {
var changed = false;
var lead = this.selectionLead;
if (this.$isEmpty)
this.setSelectionAnchor(lead.row, lead.column);
if (!this.selectionAnchor) {
changed = true;
this.selectionAnchor = this.$clone(this.selectionLead);
}
var cursor = this.$clone(this.selectionLead);
mover.call(this);
if (cursor.row !== this.selectionLead.row || cursor.column !== this.selectionLead.column) {
changed = true;
}
if (changed)
this._dispatchEvent("changeSelection", {});
};
this.selectTo = function(row, column) {
@ -244,15 +241,9 @@ var Selection = function(doc) {
};
this.selectWord = function() {
var cursor = this.selectionLead;
var column = cursor.column;
var range = this.doc.getWordRange(cursor.row, column);
var cursor = this.getCursor();
var range = this.session.getWordRange(cursor.row, cursor.column);
this.setSelectionRange(range);
/*this.setSelectionAnchor(cursor.row, start);
this.$moveSelection(function() {
this.moveCursorTo(cursor.row, end);
});*/
};
this.selectLine = function() {
@ -271,18 +262,16 @@ var Selection = function(doc) {
};
this.moveCursorLeft = function() {
if (this.selectionLead.column == 0) {
// cursor is a line start
if (this.selectionLead.row > 0) {
this.moveCursorTo(this.selectionLead.row - 1, this.doc
.getLine(this.selectionLead.row - 1).length);
var cursor = this.selectionLead.getPosition();
if (cursor.column == 0) {
// cursor is a line (start
if (cursor.row > 0) {
this.moveCursorTo(cursor.row - 1, this.doc.getLine(cursor.row - 1).length);
}
}
else {
var doc = this.doc;
var tabSize = doc.getTabSize();
var cursor = this.selectionLead;
if (doc.isTabStop(cursor) && doc.getLine(cursor.row).slice(cursor.column-tabSize, cursor.column).split(" ").length-1 == tabSize)
var tabSize = this.session.getTabSize();
if (this.session.isTabStop(cursor) && this.doc.getLine(cursor.row).slice(cursor.column-tabSize, cursor.column).split(" ").length-1 == tabSize)
this.moveCursorBy(0, -tabSize);
else
this.moveCursorBy(0, -1);
@ -296,10 +285,9 @@ var Selection = function(doc) {
}
}
else {
var doc = this.doc;
var tabSize = doc.getTabSize();
var tabSize = this.session.getTabSize();
var cursor = this.selectionLead;
if (doc.isTabStop(cursor) && doc.getLine(cursor.row).slice(cursor.column, cursor.column+tabSize).split(" ").length-1 == tabSize)
if (this.session.isTabStop(cursor) && this.doc.getLine(cursor.row).slice(cursor.column, cursor.column+tabSize).split(" ").length-1 == tabSize)
this.moveCursorBy(0, tabSize);
else
this.moveCursorBy(0, 1);
@ -309,19 +297,26 @@ var Selection = function(doc) {
this.moveCursorLineStart = function() {
var row = this.selectionLead.row;
var column = this.selectionLead.column;
var beforeCursor = this.doc.getLine(row).slice(0, column);
var screenRow = this.session.documentToScreenRow(row, column);
var firstRowColumn = this.session.getScreenFirstRowColumn(screenRow);
var beforeCursor = this.doc.getLine(row).slice(firstRowColumn, column);
var leadingSpace = beforeCursor.match(/^\s*/);
if (leadingSpace[0].length == 0)
this.moveCursorTo(row, this.doc.getLine(row).match(/^\s*/)[0].length);
else if (leadingSpace[0].length >= column)
this.moveCursorTo(row, 0);
else
this.moveCursorTo(row, leadingSpace[0].length);
if (leadingSpace[0].length == 0) {
var lastRowColumn = this.session.getDocumentLastRowColumn(row, column);
leadingSpace = this.doc.getLine(row).
substring(firstRowColumn, lastRowColumn).
match(/^\s*/);
this.moveCursorTo(row, firstRowColumn + leadingSpace[0].length);
} else if (leadingSpace[0].length >= column) {
this.moveCursorTo(row, firstRowColumn);
} else {
this.moveCursorTo(row, firstRowColumn + leadingSpace[0].length);
}
};
this.moveCursorLineEnd = function() {
this.moveCursorTo(this.selectionLead.row,
this.doc.getLine(this.selectionLead.row).length);
var lead = this.selectionLead;
this.moveCursorTo(lead.row, this.session.getDocumentLastRowColumn(lead.row, lead.column));
};
this.moveCursorFileEnd = function() {
@ -341,20 +336,20 @@ var Selection = function(doc) {
var rightOfCursor = line.substring(column);
var match;
this.doc.nonTokenRe.lastIndex = 0;
this.doc.tokenRe.lastIndex = 0;
this.session.nonTokenRe.lastIndex = 0;
this.session.tokenRe.lastIndex = 0;
if (column == line.length) {
this.moveCursorRight();
return;
}
else if (match = this.doc.nonTokenRe.exec(rightOfCursor)) {
column += this.doc.nonTokenRe.lastIndex;
this.doc.nonTokenRe.lastIndex = 0;
else if (match = this.session.nonTokenRe.exec(rightOfCursor)) {
column += this.session.nonTokenRe.lastIndex;
this.session.nonTokenRe.lastIndex = 0;
}
else if (match = this.doc.tokenRe.exec(rightOfCursor)) {
column += this.doc.tokenRe.lastIndex;
this.doc.tokenRe.lastIndex = 0;
else if (match = this.session.tokenRe.exec(rightOfCursor)) {
column += this.session.tokenRe.lastIndex;
this.session.tokenRe.lastIndex = 0;
}
this.moveCursorTo(row, column);
@ -367,72 +362,58 @@ var Selection = function(doc) {
var leftOfCursor = lang.stringReverse(line.substring(0, column));
var match;
this.doc.nonTokenRe.lastIndex = 0;
this.doc.tokenRe.lastIndex = 0;
this.session.nonTokenRe.lastIndex = 0;
this.session.tokenRe.lastIndex = 0;
if (column == 0) {
this.moveCursorLeft();
return;
}
else if (match = this.doc.nonTokenRe.exec(leftOfCursor)) {
column -= this.doc.nonTokenRe.lastIndex;
this.doc.nonTokenRe.lastIndex = 0;
else if (match = this.session.nonTokenRe.exec(leftOfCursor)) {
column -= this.session.nonTokenRe.lastIndex;
this.session.nonTokenRe.lastIndex = 0;
}
else if (match = this.doc.tokenRe.exec(leftOfCursor)) {
column -= this.doc.tokenRe.lastIndex;
this.doc.tokenRe.lastIndex = 0;
else if (match = this.session.tokenRe.exec(leftOfCursor)) {
column -= this.session.tokenRe.lastIndex;
this.session.tokenRe.lastIndex = 0;
}
this.moveCursorTo(row, column);
};
this.moveCursorBy = function(rows, chars) {
this.moveCursorTo(this.selectionLead.row + rows, this.selectionLead.column + chars);
if (this.session.getUseWrapMode()) {
var screenPos = this.session.documentToScreenPosition(
this.selectionLead.row,
this.selectionLead.column
);
var screenCol = (chars == 0 && this.$desiredColumn) || screenPos.column;
var docPos = this.session.screenToDocumentPosition(screenPos.row + rows, screenCol);
this.moveCursorTo(docPos.row, docPos.column + chars, chars == 0);
} else {
var docColumn = (chars == 0 && this.$desiredColumn) || this.selectionLead.column;
this.moveCursorTo(this.selectionLead.row + rows, docColumn + chars, chars == 0);
}
};
this.moveCursorToPosition = function(position) {
this.moveCursorTo(position.row, position.column);
};
this.moveCursorTo = function(row, column) {
var cursor = this.$clipPositionToDocument(row, column);
// only dispatch change if the cursor actually changed
if (cursor.row !== this.selectionLead.row || cursor.column !== this.selectionLead.column) {
this.selectionLead = cursor;
this._dispatchEvent("changeCursor", { data: this.getCursor() });
}
this.moveCursorTo = function(row, column, preventUpdateDesiredColumn) {
this.selectionLead.setPosition(row, column);
if (!preventUpdateDesiredColumn)
this.$updateDesiredColumn(this.selectionLead.column);
};
this.moveCursorUp = function() {
this.moveCursorBy(-1, 0);
};
this.$clipPositionToDocument = function(row, column) {
var pos = {};
if (row >= this.doc.getLength()) {
pos.row = Math.max(0, this.doc.getLength() - 1);
pos.column = this.doc.getLine(pos.row).length;
this.moveCursorToScreen = function(row, column, preventUpdateDesiredColumn) {
if (this.session.getUseWrapMode()) {
var pos = this.session.screenToDocumentPosition(row, column);
row = pos.row;
column = pos.column;
}
else if (row < 0) {
pos.row = 0;
pos.column = 0;
}
else {
pos.row = row;
pos.column = Math.min(this.doc.getLine(pos.row).length,
Math.max(0, column));
}
return pos;
};
this.$clone = function(pos) {
return {
row: pos.row,
column: pos.column
};
this.moveCursorTo(row, column, preventUpdateDesiredColumn);
};
}).call(Selection.prototype);

View file

@ -0,0 +1,97 @@
/* vim:ts=4:sts=4:sw=4:
* ***** 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):
* Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)
*
* 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 types = require('pilot/types')
var SelectionType = require('pilot/types/basic').SelectionType
var env
var settingTypes = {
selectionStyle: new SelectionType({
data: [ 'line', 'text' ]
})
}
var settings = {
printMargin: {
description: 'Position of the print margin column.',
type: 'number',
defaultValue: 80,
onChange: function onChange(event) {
if (env.editor) env.editor.setPrintMarginColumn(event.value)
}
},
showIvisibles: {
description: 'Whether or not to show invisible characters.',
type: 'bool',
defaultValue: false,
onChange: function onChange(event) {
if (env.editor) env.editor.setShowInvisibles(event.value)
}
},
highlightActiveLine: {
description: 'Whether or not highlight active line.',
type: 'bool',
defaultValue: true,
onChange: function onChange(event) {
if (env.editor) env.editor.setHighlightActiveLine(event.value)
}
},
selectionStyle: {
description: 'Type of text selection.',
type: 'selectionStyle',
defaultValue: 'line',
onChange: function onChange(event) {
if (env.editor) env.editor.setSelectionStyle(event.value)
}
}
}
exports.startup = function startup(data, reason) {
env = data.env
types.registerTypes(settingTypes)
data.env.settings.addSettings(settings)
}
exports.shutdown = function shutdown(data, reason) {
data.env.settings.removeSettings(settings)
}
})

View file

@ -37,9 +37,11 @@
require("../../../support/paths");
require("./mockdom");
var async = require("asyncjs");
async.concat(
require("./anchor_test"),
require("./change_document_test"),
require("./document_test"),
require("./edit_session_test"),
@ -49,6 +51,7 @@ async.concat(
require("./search_test"),
require("./selection_test"),
require("./text_edit_test"),
require("./highlight_selected_word_test"),
require("./mode/css_test"),
require("./mode/css_tokenizer_test"),
require("./mode/html_test"),
@ -57,5 +60,5 @@ async.concat(
require("./mode/javascript_tokenizer_test"),
require("./mode/text_test"),
require("./mode/xml_test"),
require("./mode/xml_tokenizer_test")
require("./mode/xml_tokenizer_test")
).exec();

View file

@ -0,0 +1,76 @@
define(function(require, exports, module) {
require("pilot/fixoldbrowsers");
var async = require("asyncjs");
var dom = require("pilot/dom");
var passed = 0
var failed = 0
var log = document.getElementById("log")
async.concat(
require("./anchor_test"),
require("./change_document_test"),
require("./document_test"),
require("./edit_session_test"),
require("./event_emitter_test"),
require("./navigation_test"),
require("./range_test"),
require("./search_test"),
require("./selection_test"),
require("./text_edit_test"),
require("./virtual_renderer_test"),
require("./highlight_selected_word_test"),
require("./mode/css_test"),
require("./mode/css_tokenizer_test"),
require("./mode/html_test"),
require("./mode/html_tokenizer_test"),
require("./mode/javascript_test"),
require("./mode/javascript_tokenizer_test"),
require("./mode/text_test"),
require("./mode/xml_test"),
require("./mode/xml_tokenizer_test")
)
.run()
.each(function(test, next) {
var node = document.createElement("div");
node.className = test.passed ? "passed" : "failed";
var name = test.name
if (test.suiteName)
name = test.suiteName + ": " + test.name
var msg = "[" + test.count + "/" + test.index + "] " + name + " " + (test.passed ? "OK" : "FAIL")
if (!test.passed) {
if (test.err.stack)
var err = test.err.stack
else
var err = test.err
msg += "<pre class='error'>" + err + "</pre>";
}
node.innerHTML = msg;
log.appendChild(node);
next()
})
.each(function(test) {
if (test.passed)
passed += 1
else
failed += 1
})
.end(function() {
log.innerHTML += [
"<div class='summary'>",
"<br>",
"Summary: <br>",
"<br>",
"Total number of tests: " + (passed + failed) + "<br>",
(passed ? "Passed tests: " + passed + "<br>" : ""),
(failed ? "Failed tests: " + failed + "<br>" : "")
].join("")
})
});

183
lib/ace/test/anchor_test.js Normal file
View file

@ -0,0 +1,183 @@
/* ***** 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("ace/document").Document;
var Anchor = require("ace/anchor").Anchor;
var Range = require("ace/range").Range;
var assert = require("./assertions");
var async = require("asyncjs");
var Test = {
"test create anchor" : function() {
var doc = new Document("juhu");
var anchor = new Anchor(doc, 0, 0);
assert.position(anchor.getPosition(), 0, 0);
assert.equal(anchor.getDocument(), doc);
},
"test insert text in same row before cursor should move anchor column": function() {
var doc = new Document("juhu\nkinners");
var anchor = new Anchor(doc, 1, 4);
doc.insert({row: 1, column: 1}, "123");
assert.position(anchor.getPosition(), 1, 7);
},
"test insert lines before cursor should move anchor row": function() {
var doc = new Document("juhu\nkinners");
var anchor = new Anchor(doc, 1, 4);
doc.insertLines(1, ["123", "456"]);
assert.position(anchor.getPosition(), 3, 4);
},
"test insert new line before cursor should move anchor column": function() {
var doc = new Document("juhu\nkinners");
var anchor = new Anchor(doc, 1, 4);
doc.insertNewLine({row: 0, column: 0});
assert.position(anchor.getPosition(), 2, 4);
},
"test insert new line in anchor line before anchor should move anchor column and row": function() {
var doc = new Document("juhu\nkinners");
var anchor = new Anchor(doc, 1, 4);
doc.insertNewLine({row: 1, column: 2});
assert.position(anchor.getPosition(), 2, 2);
},
"test delete text in anchor line before anchor should move anchor column": function() {
var doc = new Document("juhu\nkinners");
var anchor = new Anchor(doc, 1, 4);
doc.remove(new Range(1, 1, 1, 3));
assert.position(anchor.getPosition(), 1, 2);
},
"test remove range which contains the anchor should move the anchor to the start of the range": function() {
var doc = new Document("juhu\nkinners");
var anchor = new Anchor(doc, 0, 3);
doc.remove(new Range(0, 1, 1, 3));
assert.position(anchor.getPosition(), 0, 1);
},
"test delete character before the anchor should have no effect": function() {
var doc = new Document("juhu\nkinners");
var anchor = new Anchor(doc, 1, 4);
doc.remove(new Range(1, 4, 1, 5));
assert.position(anchor.getPosition(), 1, 4);
},
"test delete lines in anchor line before anchor should move anchor row": function() {
var doc = new Document("juhu\n1\n2\nkinners");
var anchor = new Anchor(doc, 3, 4);
doc.removeLines(1, 2);
assert.position(anchor.getPosition(), 1, 4);
},
"test remove new line before the cursor": function() {
var doc = new Document("juhu\nkinners");
var anchor = new Anchor(doc, 1, 4);
doc.removeNewLine(0);
assert.position(anchor.getPosition(), 0, 8);
},
"test delete range which contains the anchor should move anchor to the end of the range": function() {
var doc = new Document("juhu\nkinners");
var anchor = new Anchor(doc, 1, 4);
doc.remove(new Range(0, 2, 1, 2));
assert.position(anchor.getPosition(), 0, 4);
},
"test delete line which contains the anchor should move anchor to the end of the range": function() {
var doc = new Document("juhu\nkinners\n123");
var anchor = new Anchor(doc, 1, 5);
doc.removeLines(1, 1);
assert.position(anchor.getPosition(), 1, 0);
},
"test remove after the anchor should have no effect": function() {
var doc = new Document("juhu\nkinners\n123");
var anchor = new Anchor(doc, 1, 2);
doc.remove(new Range(1, 4, 2, 2));
assert.position(anchor.getPosition(), 1, 2);
},
"test anchor changes triggered by document changes should emit change event": function(next) {
var doc = new Document("juhu\nkinners\n123");
var anchor = new Anchor(doc, 1, 5);
anchor.on("change", function(e) {
assert.position(anchor.getPosition(), 0, 0);
next();
});
doc.remove(new Range(0, 0, 2, 1));
},
"test only fire change event if position changes": function() {
var doc = new Document("juhu\nkinners\n123");
var anchor = new Anchor(doc, 1, 5);
anchor.on("change", function(e) {
assert.fail();
});
doc.remove(new Range(2, 0, 2, 1));
}
};
module.exports = require("asyncjs/test").testcase(Test);
});
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
}

View file

@ -0,0 +1,306 @@
define(function(require, exports, module) {
// http://wiki.commonjs.org/wiki/Unit_Testing/1.0
//
// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
//
// Originally from narwhal.js (http://narwhaljs.org)
// Copyright (c) 2009 Thomas Robinson <280north.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the 'Software'), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// UTILITY
var oop = require('pilot/oop');
var pSlice = Array.prototype.slice;
// 1. The assert module provides functions that throw
// AssertionError's when particular conditions are not met. The
// assert module must conform to the following interface.
var assert = exports;
// 2. The AssertionError is defined in assert.
// new assert.AssertionError({ message: message,
// actual: actual,
// expected: expected })
assert.AssertionError = function AssertionError(options) {
this.name = 'AssertionError';
this.message = options.message;
this.actual = options.actual;
this.expected = options.expected;
this.operator = options.operator;
var stackStartFunction = options.stackStartFunction || fail;
if (Error.captureStackTrace) {
Error.captureStackTrace(this, stackStartFunction);
}
};
oop.inherits(assert.AssertionError, Error);
assert.AssertionError.prototype.toString = function() {
if (this.message) {
return [this.name + ':', this.message].join(' ');
} else {
return [this.name + ':',
JSON.stringify(this.expected),
this.operator,
JSON.stringify(this.actual)].join(' ');
}
};
// assert.AssertionError instanceof Error
assert.AssertionError.__proto__ = Error.prototype;
// At present only the three keys mentioned above are used and
// understood by the spec. Implementations or sub modules can pass
// other keys to the AssertionError's constructor - they will be
// ignored.
// 3. All of the following functions must throw an AssertionError
// when a corresponding condition is not met, with a message that
// may be undefined if not provided. All assertion methods provide
// both the actual and expected values to the assertion error for
// display purposes.
function fail(actual, expected, message, operator, stackStartFunction) {
throw new assert.AssertionError({
message: message,
actual: actual,
expected: expected,
operator: operator,
stackStartFunction: stackStartFunction
});
}
// EXTENSION! allows for well behaved errors defined elsewhere.
assert.fail = fail;
// 4. Pure assertion tests whether a value is truthy, as determined
// by !!guard.
// assert.ok(guard, message_opt);
// This statement is equivalent to assert.equal(true, guard,
// message_opt);. To test strictly for the value true, use
// assert.strictEqual(true, guard, message_opt);.
assert.ok = function ok(value, message) {
if (!!!value) fail(value, true, message, '==', assert.ok);
};
// 5. The equality assertion tests shallow, coercive equality with
// ==.
// assert.equal(actual, expected, message_opt);
assert.equal = function equal(actual, expected, message) {
if (actual != expected) fail(actual, expected, message, '==', assert.equal);
};
// 6. The non-equality assertion tests for whether two objects are not equal
// with != assert.notEqual(actual, expected, message_opt);
assert.notEqual = function notEqual(actual, expected, message) {
if (actual == expected) {
fail(actual, expected, message, '!=', assert.notEqual);
}
};
// 7. The equivalence assertion tests a deep equality relation.
// assert.deepEqual(actual, expected, message_opt);
assert.deepEqual = function deepEqual(actual, expected, message) {
if (!_deepEqual(actual, expected)) {
fail(actual, expected, message, 'deepEqual', assert.deepEqual);
}
};
function _deepEqual(actual, expected) {
// 7.1. All identical values are equivalent, as determined by ===.
if (actual === expected) {
return true;
} else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
if (actual.length != expected.length) return false;
for (var i = 0; i < actual.length; i++) {
if (actual[i] !== expected[i]) return false;
}
return true;
// 7.2. If the expected value is a Date object, the actual value is
// equivalent if it is also a Date object that refers to the same time.
} else if (actual instanceof Date && expected instanceof Date) {
return actual.getTime() === expected.getTime();
// 7.3. Other pairs that do not both pass typeof value == 'object',
// equivalence is determined by ==.
} else if (typeof actual != 'object' && typeof expected != 'object') {
return actual == expected;
// 7.4. For all other Object pairs, including Array objects, equivalence is
// determined by having the same number of owned properties (as verified
// with Object.prototype.hasOwnProperty.call), the same set of keys
// (although not necessarily the same order), equivalent values for every
// corresponding key, and an identical 'prototype' property. Note: this
// accounts for both named and indexed properties on Arrays.
} else {
return objEquiv(actual, expected);
}
}
function isUndefinedOrNull(value) {
return value === null || value === undefined;
}
function isArguments(object) {
return Object.prototype.toString.call(object) == '[object Arguments]';
}
function objEquiv(a, b) {
if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
return false;
// an identical 'prototype' property.
if (a.prototype !== b.prototype) return false;
//~~~I've managed to break Object.keys through screwy arguments passing.
// Converting to array solves the problem.
if (isArguments(a)) {
if (!isArguments(b)) {
return false;
}
a = pSlice.call(a);
b = pSlice.call(b);
return _deepEqual(a, b);
}
try {
var ka = Object.keys(a),
kb = Object.keys(b),
key, i;
} catch (e) {//happens when one is a string literal and the other isn't
return false;
}
// having the same number of owned properties (keys incorporates
// hasOwnProperty)
if (ka.length != kb.length)
return false;
//the same set of keys (although not necessarily the same order),
ka.sort();
kb.sort();
//~~~cheap key test
for (i = ka.length - 1; i >= 0; i--) {
if (ka[i] != kb[i])
return false;
}
//equivalent values for every corresponding key, and
//~~~possibly expensive deep test
for (i = ka.length - 1; i >= 0; i--) {
key = ka[i];
if (!_deepEqual(a[key], b[key])) return false;
}
return true;
}
// 8. The non-equivalence assertion tests for any deep inequality.
// assert.notDeepEqual(actual, expected, message_opt);
assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
if (_deepEqual(actual, expected)) {
fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual);
}
};
// 9. The strict equality assertion tests strict equality, as determined by ===.
// assert.strictEqual(actual, expected, message_opt);
assert.strictEqual = function strictEqual(actual, expected, message) {
if (actual !== expected) {
fail(actual, expected, message, '===', assert.strictEqual);
}
};
// 10. The strict non-equality assertion tests for strict inequality, as
// determined by !==. assert.notStrictEqual(actual, expected, message_opt);
assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
if (actual === expected) {
fail(actual, expected, message, '!==', assert.notStrictEqual);
}
};
function expectedException(actual, expected) {
if (!actual || !expected) {
return false;
}
if (expected instanceof RegExp) {
return expected.test(actual);
} else if (actual instanceof expected) {
return true;
} else if (expected.call({}, actual) === true) {
return true;
}
return false;
}
function _throws(shouldThrow, block, expected, message) {
var actual;
if (typeof expected === 'string') {
message = expected;
expected = null;
}
try {
block();
} catch (e) {
actual = e;
}
message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
(message ? ' ' + message : '.');
if (shouldThrow && !actual) {
fail('Missing expected exception' + message);
}
if (!shouldThrow && expectedException(actual, expected)) {
fail('Got unwanted exception' + message);
}
if ((shouldThrow && actual && expected &&
!expectedException(actual, expected)) || (!shouldThrow && actual)) {
throw actual;
}
}
// 11. Expected to throw an error:
// assert.throws(block, Error_opt, message_opt);
assert.throws = function(block, /*optional*/error, /*optional*/message) {
_throws.apply(this, [true].concat(pSlice.call(arguments)));
};
// EXTENSION! This is annoying to write outside this module.
assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) {
_throws.apply(this, [false].concat(pSlice.call(arguments)));
};
assert.ifError = function(err) { if (err) {throw err;}};
});

View file

@ -0,0 +1,489 @@
/*!
* async.js
* Copyright(c) 2010 Fabian Jakobs <fabian.jakobs@web.de>
* MIT Licensed
*/
define(function(require, exports, module) {
var STOP = exports.STOP = {}
exports.Generator = function(source) {
if (typeof source == "function")
this.source = {
next: source
}
else
this.source = source
}
;(function() {
this.next = function(callback) {
this.source.next(callback)
}
this.map = function(mapper) {
if (!mapper)
return this
mapper = makeAsync(1, mapper)
var source = this.source
this.next = function(callback) {
source.next(function(err, value) {
if (err)
callback(err)
else {
mapper(value, function(err, value) {
if (err)
callback(err)
else
callback(null, value)
})
}
})
}
return new this.constructor(this)
}
this.filter = function(filter) {
if (!filter)
return this
filter = makeAsync(1, filter)
var source = this.source
this.next = function(callback) {
source.next(function handler(err, value) {
if (err)
callback(err)
else {
filter(value, function(err, takeIt) {
if (err)
callback(err)
else if (takeIt)
callback(null, value)
else
source.next(handler)
})
}
})
}
return new this.constructor(this)
}
this.slice = function(begin, end) {
var count = -1
if (!end || end < 0)
var end = Infinity
var source = this.source
this.next = function(callback) {
source.next(function handler(err, value) {
count++
if (err)
callback(err)
else if (count >= begin && count < end)
callback(null, value)
else if (count >= end)
callback(STOP)
else
source.next(handler)
})
}
return new this.constructor(this)
}
this.reduce = function(reduce, initialValue) {
reduce = makeAsync(3, reduce)
var index = 0
var done = false
var previousValue = initialValue
var source = this.source
this.next = function(callback) {
if (done)
return callback(STOP)
if (initialValue === undefined) {
source.next(function(err, currentValue) {
if (err)
return callback(err, previousValue)
previousValue = currentValue
reduceAll()
})
}
else
reduceAll()
function reduceAll() {
source.next(function handler(err, currentValue) {
if (err) {
done = true
if (err == STOP)
return callback(null, previousValue)
else
return(err)
}
reduce(previousValue, currentValue, index++, function(err, value) {
previousValue = value
source.next(handler)
})
})
}
}
return new this.constructor(this)
}
this.forEach =
this.each = function(fn) {
fn = makeAsync(1, fn)
var source = this.source
this.next = function(callback) {
source.next(function handler(err, value) {
if (err)
callback(err)
else {
fn(value, function(err) {
callback(err, value)
})
}
})
}
return new this.constructor(this)
}
this.some = function(condition) {
condition = makeAsync(1, condition)
var source = this.source
var done = false
this.next = function(callback) {
if (done)
return callback(STOP)
source.next(function handler(err, value) {
if (err)
return callback(err)
condition(value, function(err, result) {
if (err) {
done = true
if (err == STOP)
callback(null, false)
else
callback(err)
}
else if (result) {
done = true
callback(null, true)
}
else
source.next(handler)
})
})
}
return new this.constructor(this)
}
this.every = function(condition) {
condition = makeAsync(1, condition)
var source = this.source
var done = false
this.next = function(callback) {
if (done)
return callback(STOP)
source.next(function handler(err, value) {
if (err)
return callback(err)
condition(value, function(err, result) {
if (err) {
done = true
if (err == STOP)
callback(null, true)
else
callback(err)
}
else if (!result) {
done = true
callback(null, false)
}
else
source.next(handler)
})
})
}
return new this.constructor(this)
}
this.call = function(context) {
var source = this.source
return this.map(function(fn, next) {
fn = makeAsync(0, fn, context)
fn.call(context, function(err, value) {
next(err, value)
})
})
}
this.concat = function(generator) {
var generators = [this]
generators.push.apply(generators, arguments)
var index = 0
var source = generators[index++]
return new this.constructor(function(callback) {
source.next(function handler(err, value) {
if (err) {
if (err == STOP) {
source = generators[index++]
if (!source)
return callback(STOP)
else
return source.next(handler)
}
else
return callback(err)
}
else
return callback(null, value)
})
})
}
this.zip = function(generator) {
var generators = [this]
generators.push.apply(generators, arguments)
return new this.constructor(function(callback) {
exports.list(generators)
.map(function(gen, next) {
gen.next(next)
})
.toArray(callback)
})
}
this.sort = function(compare) {
var self = this
var arrGen
this.next = function(callback) {
if (arrGen)
return arrGen.next(callback)
self.toArray(function(err, arr) {
if (err)
callback(err)
else {
arrGen = exports.list(arr.sort(compare))
arrGen.next(callback)
}
})
}
return new this.constructor(this)
}
this.join = function(separator) {
return this.$arrayOp(Array.prototype.join, separator !== undefined ? [separator] : null)
}
this.reverse = function() {
return this.$arrayOp(Array.prototype.reverse)
}
this.$arrayOp = function(arrayMethod, args) {
var self = this
var i = 0
this.next = function(callback) {
if (i++ > 0)
return callback(STOP)
self.toArray(function(err, arr) {
if (err)
callback(err, "")
else {
if (args)
callback(null, arrayMethod.apply(arr, args))
else
callback(null, arrayMethod.call(arr))
}
})
}
return new this.constructor(this)
}
this.end = function(breakOnError, callback) {
if (!callback) {
callback = arguments[0]
breakOnError = true
}
var source = this.source
var last
var lastError
source.next(function handler(err, value) {
if (err) {
if (err == STOP)
callback && callback(lastError, last)
else if (!breakOnError) {
lastError = err
source.next(handler)
}
else
callback && callback(err, value)
}
else {
last = value
source.next(handler)
}
})
}
this.toArray = function(breakOnError, callback) {
if (!callback) {
callback = arguments[0]
breakOnError = true
}
var values = []
var errors = []
var source = this.source
source.next(function handler(err, value) {
if (err) {
if (err == STOP) {
if (breakOnError)
return callback(null, values)
else {
errors.length = values.length
return callback(errors, values)
}
}
else {
if (breakOnError)
return callback(err)
else
errors[values.length] = err
}
}
values.push(value)
source.next(handler)
})
}
}).call(exports.Generator.prototype)
var makeAsync = exports.makeAsync = function(args, fn, context) {
if (fn.length > args)
return fn
else {
return function() {
var value
var next = arguments[args]
try {
value = fn.apply(context || this, arguments)
} catch(e) {
return next(e)
}
next(null, value)
}
}
}
exports.list = function(arr, construct) {
var construct = construct || exports.Generator
var i = 0
var len = arr.length
return new construct(function(callback) {
if (i < len)
callback(null, arr[i++])
else
callback(STOP)
})
}
exports.values = function(map, construct) {
var values = []
for (var key in map)
values.push(map[key])
return exports.list(values, construct)
}
exports.keys = function(map, construct) {
var keys = []
for (var key in map)
keys.push(key)
return exports.list(keys, construct)
}
/**
* range([start,] stop[, step]) -> generator of integers
*
* Return a generator containing an arithmetic progression of integers.
* range(i, j) returns [i, i+1, i+2, ..., j-1] start (!) defaults to 0.
* When step is given, it specifies the increment (or decrement).
*/
exports.range = function(start, stop, step, construct) {
var construct = construct || exports.Generator
start = start || 0
step = step || 1
if (stop === undefined || stop === null)
stop = step > 0 ? Infinity : -Infinity
var value = start
return new construct(function(callback) {
if (step > 0 && value >= stop || step < 0 && value <= stop)
callback(STOP)
else {
var current = value
value += step
callback(null, current)
}
})
}
exports.concat = function(first, varargs) {
if (arguments.length > 1)
return first.concat.apply(first, Array.prototype.slice.call(arguments, 1))
else
return first
}
exports.zip = function(first, varargs) {
if (arguments.length > 1)
return first.zip.apply(first, Array.prototype.slice.call(arguments, 1))
else
return first.map(function(item, next) {
next(null, [item])
})
}
exports.plugin = function(members, constructors) {
if (members) {
for (var key in members) {
exports.Generator.prototype[key] = members[key]
}
}
if (constructors) {
for (var key in constructors) {
exports[key] = constructors[key]
}
}
}
})

View file

@ -0,0 +1,12 @@
/*!
* async.js
* Copyright(c) 2010 Fabian Jakobs <fabian.jakobs@web.de>
* MIT Licensed
*/
define(function(require, exports, module) {
module.exports = require("./async")
require("./utils")
})

View file

@ -0,0 +1,195 @@
/*!
* async.js
* Copyright(c) 2010 Fabian Jakobs <fabian.jakobs@web.de>
* MIT Licensed
*/
define(function(require, exports, module) {
var oop = require("pilot/oop")
var async = require("asyncjs/async")
require("asyncjs/utils")
exports.TestGenerator = function(source) {
async.Generator.call(this, source)
}
oop.inherits(exports.TestGenerator, async.Generator)
;(function() {
this.exec = function() {
this.run().report().summary(function(err, passed) {
console.log("DONE")
})
}
this.run = function() {
return this.setupTest()
.each(function(test, next) {
if (test.setUpSuite)
test.setUpSuite(next)
else
next()
})
.each(function(test, next) {
test.test(function(err, passed) {
test.err = err
test.passed = passed
next()
})
})
.each(function(test, next) {
if (test.tearDownSuite)
test.tearDownSuite(next)
else
next()
})
}
this.report = function() {
return this.each(function(test, next) {
var color = test.passed ? "\x1b[32m" : "\x1b[31m"
var name = test.name
if (test.suiteName)
name = test.suiteName + ": " + test.name
console.log(color + "[" + test.count + "/" + test.index + "] " + name + " " + (test.passed ? "OK" : "FAIL") + "\x1b[0m")
if (!test.passed)
if (test.err.stack)
console.log(test.err.stack)
else
console.log(test.err)
next()
})
}
this.summary = function(callback) {
var passed = 0
var failed = 0
this.each(function(test) {
if (test.passed)
passed += 1
else
failed += 1
}).end(function() {
console.log("")
console.log("Summary:")
console.log("")
console.log( "Total number of tests: " + (passed + failed))
passed && console.log("\x1b[32mPassed tests: " + passed + "\x1b[0m")
failed && console.log("\x1b[31mFailed tests: " + failed + "\x1b[0m")
console.log("")
callback(null, failed == 0)
})
}
this.setupTest = function() {
return this.each(function(test, next) {
var empty = function(next) { next() }
var context = test.context || this
if (test.setUp)
var setUp = async.makeAsync(0, test.setUp, context)
else
setUp = empty
tearDownCalled = false
if (test.tearDown)
var tearDownInner = async.makeAsync(0, test.tearDown, context)
else
tearDownInner = empty
function tearDown(next) {
tearDownCalled = true
tearDownInner.call(test.context, next)
}
var testFn = async.makeAsync(0, test.fn, context)
test.test = function(callback) {
var called
function errorListener(e) {
if (called)
return
called = true
//process.removeListener('uncaughtException', errorListener)
if (!tearDownCalled) {
async.list([tearDown])
.call()
.timeout(test.timeout)
.end(function() {
callback(e, false)
}) }
else
callback(e, false)
}
//process.addListener('uncaughtException', errorListener)
async.list([setUp, testFn, tearDown])
.delay(0)
.call(context)
.timeout(test.timeout)
.toArray(false, function(errors, values) {
if (called)
return
called = true
var err = errors[1]
//process.removeListener('uncaughtException', errorListener)
callback(err, !err)
})
}
next()
})
}
}).call(exports.TestGenerator.prototype)
exports.testcase = function(testcase, suiteName, timeout) {
var methods = []
for (var method in testcase)
methods.push(method)
var setUp = testcase.setUp || null
var tearDown = testcase.tearDown || null
var single
methods.forEach(function(name) {
if (name.charAt(0) == '>')
single = name
})
if (single)
methods = [single]
var testNames = methods.filter(function(method) {
return method.match(/^>?test/) && typeof(testcase[method]) == "function"
})
var count = testNames.length
var i=1
tests = testNames.map(function(name) {
return {
suiteName: suiteName || testcase.name || "",
name: name,
setUp: setUp,
tearDown: tearDown,
context: testcase,
timeout: timeout === undefined ? 3000 : timeout,
fn: testcase[name],
count: count,
index: i++
}
})
if (testcase.setUpSuite) {
tests[0].setUpSuite = async.makeAsync(0, testcase.setUpSuite, testcase)
}
if (testcase.tearDownSuite) {
tests[tests.length-1].tearDownSuite = async.makeAsync(0, testcase.tearDownSuite, testcase)
}
return async.list(tests, exports.TestGenerator)
}
})

View file

@ -0,0 +1,63 @@
/*!
* async.js
* Copyright(c) 2010 Fabian Jakobs <fabian.jakobs@web.de>
* MIT Licensed
*/
define(function(require, exports, module) {
var async = require("asyncjs/async")
async.plugin({
delay: function(delay) {
return this.each(function(item, next) {
setTimeout(next, delay)
})
},
timeout: function(timeout) {
timeout = timeout || 0
var source = this.source
this.next = function(callback) {
var called
var id = setTimeout(function() {
called = true
callback("Source did not respond after " + timeout + "ms!")
}, timeout)
source.next(function(err, value) {
if (called)
return
called = true
clearTimeout(id)
callback(err, value)
})
}
return new this.constructor(this)
},
get: function(key) {
return this.map(function(value, next) {
next(null, value[key])
})
},
inspect: function() {
return this.each(function(item, next) {
console.log(JSON.stringify(item))
next()
})
},
print: function() {
return this.each(function(item, next) {
console.log(item)
next()
})
}
})
})

View file

@ -37,8 +37,6 @@
define(function(require, exports, module) {
require("./mockdom");
var EditSession = require("../edit_session").EditSession,
Editor = require("../editor").Editor,
Text = require("../mode/text").Mode,
@ -47,10 +45,10 @@ var EditSession = require("../edit_session").EditSession,
assert = require("./assertions");
var Test = {
setUp : function(next) {
this.session1 = new EditSession(["abc", "def"]);
this.session2 = new EditSession(["ghi", "jkl"]);
this.session2 = new EditSession(["ghi", "jkl"]);
this.editor = new Editor(new MockRenderer());
next();
},
@ -158,7 +156,7 @@ var Test = {
module.exports = require("asyncjs/test").testcase(Test);
});
if (module === require.main) {
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
}

View file

@ -20,6 +20,7 @@
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
* Julian Viereck <julian.viereck@gmail.com>
*
* 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
@ -35,11 +36,13 @@
*
* ***** END LICENSE BLOCK ***** */
define(function(require, exports, module) {
var Document = require("../document").Document,
Range = require("../range").Range,
assert = require("./assertions"),
async = require("asyncjs");
var Test = {
"test: insert text in line" : function() {
@ -47,194 +50,194 @@ var Test = {
var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); });
doc.insert({row: 0, column: 1}, "juhu");
assert.equal(doc.getValue(), ["1juhu2", "34"].join("\n"));
var d = deltas.concat();
var d = deltas.concat();
doc.revertDeltas(d);
assert.equal(doc.getValue(), ["12", "34"].join("\n"));
doc.applyDeltas(d);
assert.equal(doc.getValue(), ["1juhu2", "34"].join("\n"));
},
"test: insert new line" : function() {
var doc = new Document(["12", "34"]);
var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); });
doc.insertNewLine({row: 0, column: 1});
assert.equal(doc.getValue(), ["1", "2", "34"].join("\n"));
var d = deltas.concat();
var d = deltas.concat();
doc.revertDeltas(d);
assert.equal(doc.getValue(), ["12", "34"].join("\n"));
doc.applyDeltas(d);
assert.equal(doc.getValue(), ["1", "2", "34"].join("\n"));
},
"test: insert lines at the beginning" : function() {
var doc = new Document(["12", "34"]);
var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); });
doc.insertLines(0, ["aa", "bb"]);
assert.equal(doc.getValue(), ["aa", "bb", "12", "34"].join("\n"));
var d = deltas.concat();
var d = deltas.concat();
doc.revertDeltas(d);
assert.equal(doc.getValue(), ["12", "34"].join("\n"));
doc.applyDeltas(d);
assert.equal(doc.getValue(), ["aa", "bb", "12", "34"].join("\n"));
},
"test: insert lines at the end" : function() {
var doc = new Document(["12", "34"]);
var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); });
doc.insertLines(2, ["aa", "bb"]);
assert.equal(doc.getValue(), ["12", "34", "aa", "bb"].join("\n"));
},
"test: insert lines in the middle" : function() {
var doc = new Document(["12", "34"]);
var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); });
doc.insertLines(1, ["aa", "bb"]);
assert.equal(doc.getValue(), ["12", "aa", "bb", "34"].join("\n"));
var d = deltas.concat();
var d = deltas.concat();
doc.revertDeltas(d);
assert.equal(doc.getValue(), ["12", "34"].join("\n"));
doc.applyDeltas(d);
assert.equal(doc.getValue(), ["12", "aa", "bb", "34"].join("\n"));
},
"test: insert multi line string at the start" : function() {
var doc = new Document(["12", "34"]);
var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); });
doc.insert({row: 0, column: 0}, "aa\nbb\ncc");
assert.equal(doc.getValue(), ["aa", "bb", "cc12", "34"].join("\n"));
var d = deltas.concat();
var d = deltas.concat();
doc.revertDeltas(d);
assert.equal(doc.getValue(), ["12", "34"].join("\n"));
doc.applyDeltas(d);
assert.equal(doc.getValue(), ["aa", "bb", "cc12", "34"].join("\n"));
},
"test: insert multi line string at the end" : function() {
var doc = new Document(["12", "34"]);
var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); });
doc.insert({row: 2, column: 0}, "aa\nbb\ncc");
assert.equal(doc.getValue(), ["12", "34aa", "bb", "cc"].join("\n"));
var d = deltas.concat();
var d = deltas.concat();
doc.revertDeltas(d);
assert.equal(doc.getValue(), ["12", "34"].join("\n"));
doc.applyDeltas(d);
assert.equal(doc.getValue(), ["12", "34aa", "bb", "cc"].join("\n"));
},
"test: insert multi line string in the middle" : function() {
var doc = new Document(["12", "34"]);
var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); });
doc.insert({row: 0, column: 1}, "aa\nbb\ncc");
assert.equal(doc.getValue(), ["1aa", "bb", "cc2", "34"].join("\n"));
var d = deltas.concat();
var d = deltas.concat();
doc.revertDeltas(d);
assert.equal(doc.getValue(), ["12", "34"].join("\n"));
doc.applyDeltas(d);
assert.equal(doc.getValue(), ["1aa", "bb", "cc2", "34"].join("\n"));
},
"test: delete in line" : function() {
var doc = new Document(["1234", "5678"]);
var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); });
doc.remove(new Range(0, 1, 0, 3));
assert.equal(doc.getValue(), ["14", "5678"].join("\n"));
var d = deltas.concat();
var d = deltas.concat();
doc.revertDeltas(d);
assert.equal(doc.getValue(), ["1234", "5678"].join("\n"));
doc.applyDeltas(d);
assert.equal(doc.getValue(), ["14", "5678"].join("\n"));
},
"test: delete new line" : function() {
var doc = new Document(["1234", "5678"]);
var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); });
doc.remove(new Range(0, 4, 1, 0));
assert.equal(doc.getValue(), ["12345678"].join("\n"));
var d = deltas.concat();
var d = deltas.concat();
doc.revertDeltas(d);
assert.equal(doc.getValue(), ["1234", "5678"].join("\n"));
doc.applyDeltas(d);
assert.equal(doc.getValue(), ["12345678"].join("\n"));
},
"test: delete multi line range line" : function() {
var doc = new Document(["1234", "5678", "abcd"]);
var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); });
doc.remove(new Range(0, 2, 2, 2));
assert.equal(doc.getValue(), ["12cd"].join("\n"));
var d = deltas.concat();
var d = deltas.concat();
doc.revertDeltas(d);
assert.equal(doc.getValue(), ["1234", "5678", "abcd"].join("\n"));
doc.applyDeltas(d);
assert.equal(doc.getValue(), ["12cd"].join("\n"));
},
"test: delete full lines" : function() {
var doc = new Document(["1234", "5678", "abcd"]);
var deltas = [];
doc.on("change", function(e) { deltas.push(e.data); });
doc.remove(new Range(1, 0, 3, 0));
assert.equal(doc.getValue(), ["1234", ""].join("\n"));
},
"test: remove lines should return the removed lines" : function() {
var doc = new Document(["1234", "5678", "abcd"]);
var removed = doc.removeLines(1, 2);
assert.equal(removed.join("\n"), ["5678", "abcd"].join("\n"));
},
@ -246,43 +249,64 @@ var Test = {
"test: should handle windows style new lines" : function() {
var doc = new Document(["1", "2", "3"].join("\r\n"));
doc.setNewLineMode("unix");
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"));
doc.setNewLineMode("windows");
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"));
doc.setNewLineMode("unix");
assert.equal(doc.getValue(), ["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.getValue(), ["1", "2", "3"].join("\n"));
var doc = new Document(["1", "2", "3"].join("\r\n"));
doc.setNewLineMode("auto");
assert.equal(doc.getValue(), ["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.getValue());
},
"test: set value": function() {
var doc = new Document("1");
assert.equal("1", doc.getValue());
doc.setValue(doc.getValue());
assert.equal("1", doc.getValue());
var doc = new Document("1\n2");
assert.equal("1\n2", doc.getValue());
doc.setValue(doc.getValue());
assert.equal("1\n2", doc.getValue());
},
"test: empty document has to contain one line": function() {
var doc = new Document("");
assert.equal(doc.$lines.length, 1);
}
};
module.exports = require("asyncjs/test").testcase(Test);
if (module === require.main) {
});
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
}
}

View file

@ -38,12 +38,13 @@
define(function(require, exports, module) {
var EditSession = require("ace/edit_session").EditSession,
Editor = require("../editor").Editor,
UndoManager = require("ace/undomanager").UndoManager,
MockRenderer = require("./mockrenderer"),
Range = require("ace/range").Range,
assert = require("./assertions"),
async = require("asyncjs");
var Test = {
"test: find matching opening bracket" : function() {
@ -81,16 +82,16 @@ var Test = {
"test: move lines down" : function() {
var session = new EditSession(["a1", "a2", "a3", "a4"]);
session.moveLinesDown(0, 1);
assert.equal(session.getValue(), ["a3", "a1", "a2", "a4"].join("\n"));
session.moveLinesDown(1, 2);
assert.equal(session.getValue(), ["a3", "a4", "a1", "a2"].join("\n"));
session.moveLinesDown(2, 3);
assert.equal(session.getValue(), ["a3", "a4", "a1", "a2"].join("\n"));
session.moveLinesDown(2, 2);
assert.equal(session.getValue(), ["a3", "a4", "a2", "a1"].join("\n"));
},
@ -100,13 +101,13 @@ var Test = {
session.moveLinesUp(2, 3);
assert.equal(session.getValue(), ["a1", "a3", "a4", "a2"].join("\n"));
session.moveLinesUp(1, 2);
assert.equal(session.getValue(), ["a3", "a4", "a1", "a2"].join("\n"));
session.moveLinesUp(0, 1);
assert.equal(session.getValue(), ["a3", "a4", "a1", "a2"].join("\n"));
session.moveLinesUp(2, 2);
assert.equal(session.getValue(), ["a3", "a1", "a4", "a2"].join("\n"));
},
@ -153,7 +154,7 @@ var Test = {
assert.equal(session.documentToScreenColumn(0, 13), 15);
},
"test: convert document to scrren coordinates with leading tabs": function() {
"test: convert document to screen coordinates with leading tabs": function() {
var session = new EditSession("\t\t123");
session.setTabSize(4);
@ -163,6 +164,27 @@ var Test = {
assert.equal(session.documentToScreenColumn(0, 3), 9);
},
"test: documentToScreen with soft wrap and multibyte characters": function() {
var tabSize = 4;
var wrapLimit = 12;
var session = new EditSession(["foo bar foo bar"]);
session.setUseWrapMode(true);
session.setWrapLimitRange(12, 12);
session.adjustWrapLimit(80);
assert.position(session.documentToScreenPosition(0, 11), 0, 11);
assert.position(session.documentToScreenPosition(0, 12), 1, 0);
session = new EditSession(["ぁぁa"]);
session.setUseWrapMode(true);
session.setWrapLimitRange(2, 2);
session.adjustWrapLimit(80);
assert.position(session.documentToScreenPosition(0, 1), 1, 0);
assert.position(session.documentToScreenPosition(0, 2), 2, 0);
assert.position(session.documentToScreenPosition(0, 4), 2, 1);
},
"test: convert screen to document coordinates" : function() {
var session = new EditSession("01234\t567890\t1234");
session.setTabSize(4);
@ -177,99 +199,153 @@ var Test = {
assert.equal(session.screenToDocumentColumn(0, 15), 12);
assert.equal(session.screenToDocumentColumn(0, 19), 13);
},
"test: insert text in multiple rows": function() {
var session = new EditSession(["12", "", "abcd"]);
"test: screenToDocument with soft wrap and multi byte characters": function() {
var tabSize = 4;
var wrapLimit = 12;
var session = new EditSession(["foo bar foo bar"]);
session.setUseWrapMode(true);
session.setWrapLimitRange(12, 12);
session.adjustWrapLimit(80);
assert.position(session.screenToDocumentPosition(1, 0), 0, 12);
assert.position(session.screenToDocumentPosition(0, 11), 0, 11);
// Check if the position is clamped the right way.
assert.position(session.screenToDocumentPosition(0, 12), 0, 11);
assert.position(session.screenToDocumentPosition(0, 20), 0, 11);
session = new EditSession(["ぁ a"]);
session.setUseWrapMode(true);
session.adjustWrapLimit(80);
var inserted = session.multiRowInsert([0, 1, 2], 2, "juhu 1");
assert.equal(inserted.rows, 0);
assert.equal(inserted.columns, 6);
assert.equal(session.getValue(), ["12juhu 1", " juhu 1", "abjuhu 1cd"].join("\n"));
assert.position(session.screenToDocumentPosition(0, 1), 0, 0);
assert.position(session.screenToDocumentPosition(0, 2), 0, 1);
assert.position(session.screenToDocumentPosition(0, 3), 0, 2);
assert.position(session.screenToDocumentPosition(0, 4), 0, 3);
assert.position(session.screenToDocumentPosition(0, 5), 0, 3);
},
"test: undo insert text in multiple rows": function() {
var session = new EditSession(["12", "", "abcd"]);
var undoManager = new UndoManager();
session.setUndoManager(undoManager);
session.multiRowInsert([0, 1, 2], 2, "juhu 1");
session.$informUndoManager.call();
assert.equal(session.getValue(), ["12juhu 1", " juhu 1", "abjuhu 1cd"].join("\n"));
"test: wrapLine split function" : function() {
var splits;
var computeWrapSplits = EditSession.prototype.$computeWrapSplits;
var c = 0;
undoManager.undo();
assert.equal(session.getValue(), ["12", "", "abcd"].join("\n"));
undoManager.redo();
assert.equal(session.getValue(), ["12juhu 1", " juhu 1", "abjuhu 1cd"].join("\n"));
},
"test: insert new line in multiple rows": function() {
var session = new EditSession(["12", "", "abcd"]);
var inserted = session.multiRowInsert([0, 1, 2], 2, "\n");
assert.equal(inserted.rows, 1);
assert.equal(session.getValue(), ["12\n", " \n", "ab\ncd"].join("\n"));
},
"test: insert multi line text in multiple rows": function() {
var session = new EditSession(["12", "", "abcd"]);
var inserted = session.multiRowInsert([0, 1, 2], 2, "juhu\n12");
assert.equal(inserted.rows, 1);
assert.equal(session.getValue(), ["12juhu\n12", " juhu\n12", "abjuhu\n12cd"].join("\n"));
},
"test: remove right in multiple rows" : function() {
var session = new EditSession(["12", "", "abcd"]);
function computeAndAssert(line, assertEqual, wrapLimit, tabSize) {
wrapLimit = wrapLimit || 12;
tabSize = tabSize || 4;
splits = computeWrapSplits.call(EditSession.prototype, line, wrapLimit, tabSize);
// console.log("String:", line, "Result:", splits, "Expected:", assertEqual);
assert.ok(splits.length == assertEqual.length);
for (var i = 0; i < splits.length; i++) {
assert.ok(splits[i] == assertEqual[i]);
}
}
session.multiRowRemove([0, 1, 2], new Range(0, 2, 0, 3));
assert.equal(session.getValue(), ["12", "", "abd"].join("\n"));
},
"test: undo remove right in multiple rows" : function() {
var session = new EditSession(["12", "", "abcd"]);
var undoManager = new UndoManager();
session.setUndoManager(undoManager);
// Basic splitting.
computeAndAssert("foo bar foo bar", [ 12 ]);
computeAndAssert("foo bar f bar", [ 12 ]);
computeAndAssert("foo bar f r", [ 14 ]);
computeAndAssert("foo bar foo bar foo bara foo", [12, 25]);
session.multiRowRemove([0, 1, 2], new Range(0, 1, 0, 3));
session.$informUndoManager.call();
assert.equal(session.getValue(), ["1", "", "ad"].join("\n"));
undoManager.undo();
assert.equal(session.getValue(), ["12", "", "abcd"].join("\n"));
undoManager.redo();
assert.equal(session.getValue(), ["1", "", "ad"].join("\n"));
// Don't split if there is only whitespaces/tabs at the end of the line.
computeAndAssert("foo foo foo \t \t", [ ]);
// If there is no space to split, force split.
computeAndAssert("foooooooooooooo", [ 12 ]);
computeAndAssert("fooooooooooooooooooooooooooo", [12, 24]);
computeAndAssert("foo bar fooooooooooobooooooo", [8, 20]);
// Basic splitting + tabs.
computeAndAssert("foo \t\tbar", [ 6 ]);
computeAndAssert("foo \t \tbar", [ 7 ]);
// Ignore spaces/tabs at beginning of split.
computeAndAssert("foo \t \t \t \t bar", [ 14 ]);
// Test wrapping for asian characters.
computeAndAssert("ぁぁ", [1], 2);
computeAndAssert(" ぁぁ", [1, 2], 2);
computeAndAssert(" ぁ\tぁ", [1, 3], 2);
computeAndAssert(" ぁぁ\tぁ", [1, 4], 4);
},
"test get longest line" : function() {
var session = new EditSession(["12"]);
session.setTabSize(4);
assert.equal(session.getWidth(), 2);
assert.equal(session.getScreenWidth(), 2);
session.doc.insertNewLine(0);
session.doc.insertLines(1, ["123"]);
assert.equal(session.getWidth(), 3);
assert.equal(session.getScreenWidth(), 3);
session.doc.insertNewLine(0);
session.doc.insertLines(1, ["\t\t"]);
assert.equal(session.getWidth(), 3);
assert.equal(session.getScreenWidth(), 8);
session.setTabSize(2);
assert.equal(session.getWidth(), 3);
assert.equal(session.getScreenWidth(), 4);
},
"test getDisplayString": function() {
var session = new EditSession(["12"]);
session.setTabSize(4);
assert.equal(session.$getDisplayTokens("\t").length, 4);
assert.equal(session.$getDisplayTokens("abc").length, 3);
assert.equal(session.$getDisplayTokens("abc\t").length, 7);
},
"test issue 83": function() {
var session = new EditSession("");
var editor = new Editor(new MockRenderer(), session);
var document = session.getDocument();
session.setUseWrapMode(true);
document.insertLines(0, ["a", "b"]);
document.insertLines(2, ["c", "d"]);
document.removeLines(1, 2);
},
"test wrapMode init has to create wrapData array": function() {
var session = new EditSession("foo bar\nfoo bar");
var editor = new Editor(new MockRenderer(), session);
var document = session.getDocument();
session.setUseWrapMode(true);
session.setWrapLimitRange(3, 3);
session.adjustWrapLimit(80);
// Test if wrapData is there and was computed.
assert.equal(session.$wrapData.length, 2);
assert.equal(session.$wrapData[0].length, 1);
assert.equal(session.$wrapData[1].length, 1);
},
"test first line blank with wrap": function() {
var session = new EditSession("\nfoo");
session.setUseWrapMode(true);
assert.equal(session.doc.getValue(), ["", "foo"].join("\n"));
},
"test first line blank with wrap 2" : function() {
var session = new EditSession("");
session.setUseWrapMode(true);
session.setValue("\nfoo");
assert.equal(session.doc.getValue(), ["", "foo"].join("\n"));
}
};
module.exports = require("asyncjs/test").testcase(Test);
});
if (module === require.main) {
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
}

View file

@ -35,10 +35,10 @@
*
* ***** END LICENSE BLOCK ***** */
require("../../../support/paths");
define(function(require, exports, module) {
var oop = require("pilot/oop");
EventEmitter = require("pilot/event_emitter").EventEmitter,
EventEmitter = require("pilot/event_emitter").EventEmitter,
assert = require("./assertions");
var Emitter = function() {};
@ -61,6 +61,4 @@ var Test = {
};
module.exports = require("asyncjs/test").testcase(Test)
if (module === require.main)
module.exports.exec()
});

View file

@ -0,0 +1,212 @@
/* vim:ts=4:sts=4:sw=4:
* ***** 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
* Mihai Sucan.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mihai Sucan <mihai.sucan@gmail.com>
*
* 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 EditSession = require("ace/edit_session").EditSession,
Editor = require("../editor").Editor,
MockRenderer = require("./mockrenderer"),
TextMode = require("ace/mode/text").Mode,
assert = require("./assertions"),
async = require("asyncjs");
var lipsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
"Mauris at arcu mi, eu lobortis mauris. Quisque ut libero eget " +
"diam congue vehicula. Quisque ut odio ut mi aliquam tincidunt. " +
"Duis lacinia aliquam lorem eget eleifend. Morbi eget felis mi. " +
"Duis quam ligula, consequat vitae convallis volutpat, blandit " +
"nec neque. Nulla facilisi. Etiam suscipit lorem ac justo " +
"sollicitudin tristique. Phasellus ut posuere nunc. Aliquam " +
"scelerisque mollis felis non gravida. Vestibulum lacus sem, " +
"posuere non bibendum id, luctus non dolor. Aenean id metus " +
"lorem, vel dapibus est. Donec gravida feugiat augue nec " +
"accumsan.Lorem ipsum dolor sit amet, consectetur adipiscing " +
"elit. Nulla vulputate, velit vitae tincidunt congue, nunc " +
"augue accumsan velit, eu consequat turpis lectus ac orci. " +
"Pellentesque ornare dolor feugiat dui auctor eu varius nulla " +
"fermentum. Sed aliquam odio at velit lacinia vel fermentum " +
"felis sodales. In dignissim magna eget nunc lobortis non " +
"fringilla nibh ullamcorper. Donec facilisis malesuada elit " +
"at egestas. Etiam bibendum, diam vitae tempor aliquet, dui " +
"libero vehicula odio, eget bibendum mauris velit eu lorem.\n" +
"consectetur";
var Test = {
setUp: function() {
this.session = new EditSession(lipsum);
this.editor = new Editor(new MockRenderer(), this.session);
this.selection = this.session.getSelection();
this.search = this.editor.$search;
},
"test: highlight selected words by default": function() {
assert.equal(this.editor.getHighlightSelectedWord(), true);
},
"test: highlight a word": function() {
this.editor.moveCursorTo(0, 9);
this.selection.selectWord();
var range = this.selection.getRange();
assert.equal(this.session.getTextRange(range), "ipsum");
assert.equal(this.session.$selectionOccurrences.length, 1);
},
"test: highlight a word and clear highlight": function() {
this.editor.moveCursorTo(0, 8);
this.selection.selectWord();
var range = this.selection.getRange();
assert.equal(this.session.getTextRange(range), "ipsum");
assert.equal(this.session.$selectionOccurrences.length, 1);
this.session.getMode().clearSelectionHighlight(this.editor);
assert.equal(this.session.$selectionOccurrences.length, 0);
},
"test: highlight another word": function() {
this.selection.moveCursorTo(0, 14);
this.selection.selectWord();
var range = this.selection.getRange();
assert.equal(this.session.getTextRange(range), "dolor");
assert.equal(this.session.$selectionOccurrences.length, 3);
},
"test: no selection, no highlight": function() {
this.selection.clearSelection();
assert.equal(this.session.$selectionOccurrences.length, 0);
},
"test: select a word, no highlight": function() {
this.editor.setHighlightSelectedWord(false);
this.selection.moveCursorTo(0, 14);
this.selection.selectWord();
var range = this.selection.getRange();
assert.equal(this.session.getTextRange(range), "dolor");
assert.equal(this.session.$selectionOccurrences.length, 0);
},
"test: select a word with no matches": function() {
this.editor.setHighlightSelectedWord(true);
var currentOptions = this.search.getOptions();
var newOptions = {
wrap: true,
wholeWord: true,
caseSensitive: true,
needle: "Mauris"
};
this.search.set(newOptions);
var match = this.search.find(this.session);
assert.notEqual(match, null, "found a match for 'Mauris'");
this.search.set(currentOptions);
this.selection.setSelectionRange(match);
assert.equal(this.session.getTextRange(match), "Mauris");
assert.equal(this.session.$selectionOccurrences.length, 0);
},
"test: partial word selection 1": function() {
this.selection.moveCursorTo(0, 14);
this.selection.selectWord();
this.selection.selectLeft();
var range = this.selection.getRange();
assert.equal(this.session.getTextRange(range), "dolo");
assert.equal(this.session.$selectionOccurrences.length, 0);
},
"test: partial word selection 2": function() {
this.selection.moveCursorTo(0, 13);
this.selection.selectWord();
this.selection.selectRight();
var range = this.selection.getRange();
assert.equal(this.session.getTextRange(range), "dolor ");
assert.equal(this.session.$selectionOccurrences.length, 0);
},
"test: partial word selection 3": function() {
this.selection.moveCursorTo(0, 14);
this.selection.selectWord();
this.selection.selectLeft();
this.selection.shiftSelection(1);
var range = this.selection.getRange();
assert.equal(this.session.getTextRange(range), "olor");
assert.equal(this.session.$selectionOccurrences.length, 0);
},
"test: select last word": function() {
this.selection.moveCursorTo(0, 1);
var currentOptions = this.search.getOptions();
var newOptions = {
wrap: true,
wholeWord: true,
caseSensitive: true,
backwards: true,
needle: "consectetur"
};
this.search.set(newOptions);
var match = this.search.find(this.session);
assert.notEqual(match, null, "found a match for 'consectetur'");
assert.position(match.start, 1, 0);
this.search.set(currentOptions);
this.selection.setSelectionRange(match);
assert.equal(this.session.getTextRange(match), "consectetur");
assert.equal(this.session.$selectionOccurrences.length, 2);
},
};
module.exports = require("asyncjs/test").testcase(Test);
});
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
}

View file

@ -50,6 +50,8 @@ MockRenderer = function(visibleRowCount) {
firstVisibleRow : 0,
lastVisibleRow : this.visibleRowCount
};
this.isMockRenderer = true;
};
@ -69,6 +71,13 @@ MockRenderer.prototype.getMouseEventTarget = function() {
return this.container;
};
MockRenderer.prototype.getTextAreaContainer = function() {
return this.container;
};
MockRenderer.prototype.moveTextAreaToCursor = function() {
};
MockRenderer.prototype.setSession = function(session) {
this.session = session;
};
@ -85,6 +94,19 @@ MockRenderer.prototype.updateCursor = function(position) {
this.cursor.column = position.column;
};
MockRenderer.prototype.scrollToLine = function(line, center) {
var lineHeight = { lineHeight: 16 };
var row = 0;
for (var l = 1; l < line; l++) {
row += this.session.getRowHeight(lineHeight, l-1) / lineHeight.lineHeight;
}
if (center) {
row -= this.visibleRowCount / 2;
}
this.scrollToRow(row);
};
MockRenderer.prototype.scrollCursorIntoView = function() {
if (this.cursor.row < this.layerConfig.firstVisibleRow) {
this.scrollToRow(this.cursor.row);
@ -111,16 +133,22 @@ MockRenderer.prototype.draw = function() {
MockRenderer.prototype.updateLines = function(startRow, endRow) {
};
MockRenderer.prototype.addMarker = function() {
MockRenderer.prototype.updateBackMarkers = function() {
};
MockRenderer.prototype.updateFrontMarkers = function() {
};
MockRenderer.prototype.setBreakpoints = function() {
};
MockRenderer.prototype.updateFull = function() {
MockRenderer.prototype.onResize = function() {
};
MockRenderer.prototype.updateText = function() {
MockRenderer.prototype.updateFull = function() {
};
MockRenderer.prototype.updateText = function() {
};
MockRenderer.prototype.showCursor = function() {
@ -129,5 +157,15 @@ MockRenderer.prototype.showCursor = function() {
MockRenderer.prototype.visualizeFocus = function() {
};
MockRenderer.prototype.setAnnotations = function() {
};
MockRenderer.prototype.textToScreenCoordinates = function() {
return {
pageX: 0,
pageY: 0
}
};
return MockRenderer;
});

View file

@ -73,7 +73,7 @@ var Test = {
module.exports = require("asyncjs/test").testcase(Test);
});
if (module === require.main) {
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
}

View file

@ -88,7 +88,7 @@ module.exports = require("asyncjs/test").testcase(Test, "css tokenizer");
});
if (module === require.main) {
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
}

View file

@ -65,7 +65,7 @@ var Test = {
module.exports = require("asyncjs/test").testcase(Test);
});
if (module === require.main) {
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
exports.exec();
}

View file

@ -68,7 +68,7 @@ var Test = {
module.exports = require("asyncjs/test").testcase(Test);
});
if (module === require.main) {
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
exports.exec();
}

View file

@ -147,7 +147,7 @@ var Test = {
module.exports = require("asyncjs/test").testcase(Test);
});
if (module === require.main) {
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
exports.exec();
}

View file

@ -104,7 +104,7 @@ var Test = {
module.exports = require("asyncjs/test").testcase(Test);
});
if (module === require.main) {
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
exports.exec();
}

View file

@ -62,7 +62,7 @@ var Test = {
module.exports = require("asyncjs/test").testcase(Test);
});
if (module === require.main) {
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
exports.exec();
}

View file

@ -73,7 +73,7 @@ var Test = {
module.exports = require("asyncjs/test").testcase(Test);
});
if (module === require.main) {
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
exports.exec();
}

View file

@ -63,7 +63,7 @@ var Test = {
module.exports = require("asyncjs/test").testcase(Test);
});
if (module === require.main) {
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
exports.exec();
}

View file

@ -37,10 +37,8 @@
define(function(require, exports, module) {
require("./mockdom");
var EditSession = require("ace/edit_session").EditSession,
Editor = require("../Editor").Editor,
Editor = require("../editor").Editor,
MockRenderer = require("./mockrenderer"),
assert = require("./assertions");
@ -153,7 +151,7 @@ var Test = {
module.exports = require("asyncjs/test").testcase(Test)
});
if (module === require.main) {
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
exports.exec();
}

View file

@ -132,39 +132,39 @@ var Test = {
var range = range.extend(2, 5);
assert.range(range, 2, 5, 2, 30);
var range = range.extend(2, 35);
assert.range(range, 2, 5, 2, 35);
var range = range.extend(2, 15);
assert.range(range, 2, 5, 2, 35);
var range = range.extend(1, 4);
assert.range(range, 1, 4, 2, 35);
var range = range.extend(6, 10);
assert.range(range, 1, 4, 6, 10);
},
"test: collapse rows" : function() {
var range = new Range(0, 2, 1, 2);
assert.range(range.collapseRows(), 0, 0, 1, 0);
assert.range(range.collapseRows(), 0, 0, 1, 0);
var range = new Range(2, 2, 3, 1);
assert.range(range.collapseRows(), 2, 0, 3, 0);
assert.range(range.collapseRows(), 2, 0, 3, 0);
var range = new Range(2, 2, 3, 0);
assert.range(range.collapseRows(), 2, 0, 2, 0);
assert.range(range.collapseRows(), 2, 0, 2, 0);
var range = new Range(2, 0, 2, 0);
assert.range(range.collapseRows(), 2, 0, 2, 0);
assert.range(range.collapseRows(), 2, 0, 2, 0);
}
};
module.exports = require("asyncjs/test").testcase(Test);
});
if (module === require.main) {
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
exports.exec();
}

View file

@ -20,6 +20,7 @@
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
* Mihai Sucan <mihai DOT sucan AT gmail DOT com>
*
* 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
@ -341,13 +342,58 @@ var Test = {
assert.equal(search.replace("ab12", "cd$1"), "cd12");
assert.equal(search.replace("ab12", "-$&-"), "-ab12-");
assert.equal(search.replace("ab12", "$$"), "$");
}
},
"test: find all matches in a line" : function() {
var session = new EditSession("foo bar foo baz foobar foo");
var search = new Search().set({
needle: "foo",
wrap: true,
wholeWord: true,
});
session.getSelection().moveCursorTo(0, 4);
var ranges = search.findAll(session);
assert.equal(ranges.length, 3);
assert.position(ranges[0].start, 0, 8);
assert.position(ranges[0].end, 0, 11);
assert.position(ranges[1].start, 0, 23);
assert.position(ranges[1].end, 0, 26);
assert.position(ranges[2].start, 0, 0);
assert.position(ranges[2].end, 0, 3);
},
"test: find all matches in a line backwards" : function() {
var session = new EditSession("foo bar foo baz foobar foo");
var search = new Search().set({
needle: "foo",
wrap: true,
wholeWord: true,
backwards: true,
});
session.getSelection().moveCursorTo(0, 13);
var ranges = search.findAll(session);
assert.equal(ranges.length, 3);
assert.position(ranges[0].start, 0, 8);
assert.position(ranges[0].end, 0, 11);
assert.position(ranges[1].start, 0, 0);
assert.position(ranges[1].end, 0, 3);
assert.position(ranges[2].start, 0, 23);
assert.position(ranges[2].end, 0, 26);
},
};
module.exports = require("asyncjs/test").testcase(Test)
});
if (module === require.main) {
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
exports.exec();
}

View file

@ -287,13 +287,49 @@ var Test = {
selection.moveCursorTo(0, 5);
assert.notOk(called);
},
"test: moveWordLeft should move past || and [": function() {
var session = new EditSession("||foo[");
var selection = session.getSelection();
// Move behind ||
selection.moveCursorWordRight();
assert.position(selection.getCursor(), 0, 2);
// Move beind foo
selection.moveCursorWordRight();
assert.position(selection.getCursor(), 0, 5);
// Move behind [
selection.moveCursorWordRight();
assert.position(selection.getCursor(), 0, 6);
},
"test: moveWordRight should move past || and [": function() {
var session = new EditSession("||foo[");
var selection = session.getSelection();
selection.moveCursorTo(0, 6);
// Move behind [
selection.moveCursorWordLeft();
assert.position(selection.getCursor(), 0, 5);
// Move beind foo
selection.moveCursorWordLeft();
assert.position(selection.getCursor(), 0, 2);
// Move behind ||
selection.moveCursorWordLeft();
assert.position(selection.getCursor(), 0, 0);
}
};
module.exports = require("asyncjs/test").testcase(Test);
});
if (module === require.main) {
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
}

54
lib/ace/test/tests.html Normal file
View file

@ -0,0 +1,54 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Ace Unit Tests</title>
<style type="text/css" media="screen">
#log .passed {
color: green;
}
#log .failed {
color: red;
}
#log pre.error {
color: black;
}
</style>
</head>
<body>
<div id="log"></div>
<script src="../../../demo/require.js" type="text/javascript" charset="utf-8"></script>
<script>
require({
paths: {
ace: "../lib/ace",
cockpit: "../support/cockpit/lib/cockpit",
pilot: "../support/pilot/lib/pilot"
},
packages : [{
name: "asyncjs",
location: "../lib/ace/test",
lib: "asyncjs",
main: "index"
}, {
name: "assert",
location: "../lib/ace/test",
lib: "asyncjs",
main: "assert"
}]
});
require(["ace/test/all_browser"], function(tests) {
});
</script>
</body>
</html>

View file

@ -36,7 +36,6 @@
* ***** END LICENSE BLOCK ***** */
define(function(require, exports, module) {
require("./mockdom");
var EditSession = require("ace/edit_session").EditSession,
Editor = require("../editor").Editor,
@ -126,19 +125,19 @@ var Test = {
"test: indent selected lines" : function() {
var session = new EditSession(["a12345", "b12345", "c12345"].join("\n"));
var editor = new Editor(new MockRenderer(), session);
editor.moveCursorTo(1, 0);
editor.getSelection().selectDown();
editor.indent();
assert.equal(["a12345", " b12345", "c12345"].join("\n"), session.toString());
},
"test: no auto indent if cursor is before the {" : function() {
var session = new EditSession("{", new JavaScriptMode());
var editor = new Editor(new MockRenderer(), session);
editor.moveCursorTo(0, 0);
editor.moveCursorTo(0, 0);
editor.onTextInput("\n");
assert.equal(["", "{"].join("\n"), session.toString());
},
@ -149,7 +148,7 @@ var Test = {
editor.moveCursorTo(0, 5);
editor.getSelection().selectDown();
editor.getSelection().selectDown();
editor.getSelection().selectDown();
editor.blockOutdent();
assert.equal(session.toString(), [" a12345", "b12345", " c12345"].join("\n"));
@ -208,21 +207,21 @@ var Test = {
assert.equal([" abc", "cde"].join("\n"), session.toString());
assert.range(editor.getSelectionRange(), 0, 0, 1, 1);
},
"test: toggle comment lines twice should return the original text" : function() {
var session = new EditSession([" abc", "cde", "fg"], new JavaScriptMode());
var editor = new Editor(new MockRenderer(), session);
editor.moveCursorTo(0, 0);
editor.getSelection().selectDown();
editor.getSelection().selectDown();
editor.toggleCommentLines();
editor.toggleCommentLines();
assert.equal([" abc", "cde", "fg"].join("\n"), session.toString());
},
"test: comment lines - if the selection end is at the line start it should stay there": function() {
//select down
@ -366,82 +365,132 @@ var Test = {
editor.onTextInput("\t");
assert.equal(session.toString(), "\t");
},
"test: undo/redo for delete line" : function() {
var session = new EditSession(["111", "222", "333"]);
var undoManager = new UndoManager();
session.setUndoManager(undoManager);
var initialText = session.toString();
var editor = new Editor(new MockRenderer(), session);
editor.removeLines();
var step1 = session.toString();
assert.equal(step1, "222\n333");
session.$informUndoManager.call();
editor.removeLines();
var step2 = session.toString();
assert.equal(step2, "333");
session.$informUndoManager.call();
editor.removeLines();
var step3 = session.toString();
assert.equal(step3, "");
session.$informUndoManager.call();
undoManager.undo();
session.$informUndoManager.call();
assert.equal(session.toString(), step2);
undoManager.undo();
session.$informUndoManager.call();
assert.equal(session.toString(), step1);
undoManager.undo();
session.$informUndoManager.call();
assert.equal(session.toString(), initialText);
undoManager.undo();
session.$informUndoManager.call();
assert.equal(session.toString(), initialText);
},
"test: remove left should remove character left of the cursor" : function() {
var session = new EditSession(["123", "456"]);
var editor = new Editor(new MockRenderer(), session);
editor.moveCursorTo(1, 1);
editor.removeLeft();
assert.equal(session.toString(), "123\n56");
},
"test: remove left should remove line break if cursor is at line start" : function() {
var session = new EditSession(["123", "456"]);
var editor = new Editor(new MockRenderer(), session);
editor.moveCursorTo(1, 0);
editor.removeLeft();
assert.equal(session.toString(), "123456");
},
"test: remove left should remove tabsize spaces if cursor is on a tab stop and preceeded by spaces" : function() {
var session = new EditSession(["123", " 456"]);
session.setUseSoftTabs(true);
session.setTabSize(4);
var editor = new Editor(new MockRenderer(), session);
editor.moveCursorTo(1, 8);
editor.removeLeft();
assert.equal(session.toString(), "123\n 456");
},
"test: transpose at line start should be a noop": function() {
var session = new EditSession(["123", "4567", "89"]);
var editor = new Editor(new MockRenderer(), session);
editor.moveCursorTo(1, 0);
editor.transposeLetters();
assert.equal(session.getValue(), ["123", "4567", "89"].join("\n"));
},
"test: transpose in line should swap the charaters before and after the cursor": function() {
var session = new EditSession(["123", "4567", "89"]);
var editor = new Editor(new MockRenderer(), session);
editor.moveCursorTo(1, 2);
editor.transposeLetters();
assert.equal(session.getValue(), ["123", "4657", "89"].join("\n"));
},
"test: transpose at line end should swap the last two characters": function() {
var session = new EditSession(["123", "4567", "89"]);
var editor = new Editor(new MockRenderer(), session);
editor.moveCursorTo(1, 4);
editor.transposeLetters();
assert.equal(session.getValue(), ["123", "4576", "89"].join("\n"));
},
"test: transpose with non empty selection should be a noop": function() {
var session = new EditSession(["123", "4567", "89"]);
var editor = new Editor(new MockRenderer(), session);
editor.moveCursorTo(1, 1);
editor.getSelection().selectRight();
editor.transposeLetters();
assert.equal(session.getValue(), ["123", "4567", "89"].join("\n"));
},
"test: transpose should move the cursor behind the last swapped character": function() {
var session = new EditSession(["123", "4567", "89"]);
var editor = new Editor(new MockRenderer(), session);
editor.moveCursorTo(1, 2);
editor.transposeLetters();
assert.position(editor.getCursorPosition(), 1, 3);
}
};
module.exports = require("asyncjs/test").testcase(Test);
});
if (module === require.main) {
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
exports.exec();
}

View file

@ -36,7 +36,6 @@
* ***** END LICENSE BLOCK ***** */
define(function(require, exports, module) {
require("./mockdom");
var EditSession = require("ace/edit_session").EditSession,
VirtualRenderer = require("../virtual_renderer").VirtualRenderer,
@ -75,7 +74,7 @@ var Test = {
module.exports = require("asyncjs/test").testcase(Test);
});
if (module === require.main) {
if (typeof module !== "undefined" && module === require.main) {
require("../../../support/paths");
exports.exec()
exports.exec();
}

View file

@ -192,4 +192,4 @@ define(function(require, exports, module) {
dom.importCssString(cssText);
exports.cssClass = "ace-clouds";
});
});

View file

@ -193,4 +193,4 @@ background-color:#E92E2E;\
dom.importCssString(cssText);
exports.cssClass = "ace-clouds-midnight";
});
});

View file

@ -194,4 +194,4 @@ color:#0088FF;\
dom.importCssString(cssText);
exports.cssClass = "ace-cobalt";
});
});

View file

@ -198,4 +198,4 @@ color:#5A525F;\
dom.importCssString(cssText);
exports.cssClass = "ace-dawn";
});
});

View file

@ -1,83 +0,0 @@
.ace-eclipse .ace_editor {
border: 2px solid rgb(159, 159, 159);
}
.ace-eclipse .ace_editor.ace_focus {
border: 2px solid #327fbd;
}
.ace-eclipse .ace_gutter {
width: 40px;
background: rgb(227, 227, 227);
border-right: 1px solid rgb(159, 159, 159);
color: rgb(136, 136, 136);
}
.ace-eclipse .ace_gutter-layer {
right: 10px;
text-align: right;
}
.ace-eclipse .ace_text-layer {
cursor: text;
}
.ace-eclipse .ace_cursor {
border-left: 1px solid black;
}
.ace-eclipse .ace_line .ace_keyword, .ace-eclipse .ace_line .ace_variable {
color: rgb(127, 0, 85);
}
.ace-eclipse .ace_line .ace_constant.ace_buildin {
color: rgb(88, 72, 246);
}
.ace-eclipse .ace_line .ace_constant.ace_library {
color: rgb(6, 150, 14);
}
.ace-eclipse .ace_line .ace_function {
color: rgb(60, 76, 114);
}
.ace-eclipse .ace_line .ace_string {
color: rgb(42, 0, 255);
}
.ace-eclipse .ace_line .ace_comment {
color: rgb(63, 127, 95);
}
.ace-eclipse .ace_line .ace_comment.ace_doc {
color: rgb(63, 95, 191);
}
.ace-eclipse .ace_line .ace_comment.ace_doc.ace_tag {
color: rgb(127, 159, 191);
}
.ace-eclipse .ace_line .ace_constant.ace_numeric {
}
.ace-eclipse .ace_line .ace_tag {
color: rgb(63, 127, 127);
}
.ace-eclipse .ace_line .ace_xml_pe {
color: rgb(104, 104, 91);
}
.ace-eclipse .ace_marker-layer .ace_selection {
background: rgb(181, 213, 255);
}
.ace-eclipse .ace_marker-layer .ace_bracket {
margin: -1px 0 0 -1px;
border: 1px solid rgb(192, 192, 192);
}
.ace-eclipse .ace_marker-layer .ace_active_line {
background: rgb(232, 242, 254);
}

View file

@ -38,7 +38,94 @@
define(function(require, exports, module) {
var dom = require("pilot/dom");
var cssText = require("text!ace/theme/eclipse.css");
var cssText = ".ace-eclipse .ace_editor {\
border: 2px solid rgb(159, 159, 159);\
}\
\
.ace-eclipse .ace_editor.ace_focus {\
border: 2px solid #327fbd;\
}\
\
.ace-eclipse .ace_gutter {\
width: 50px;\
background: rgb(227, 227, 227);\
border-right: 1px solid rgb(159, 159, 159); \
color: rgb(136, 136, 136);\
}\
\
.ace-eclipse .ace_gutter-layer {\
width: 100%;\
text-align: right;\
}\
\
.ace-eclipse .ace_gutter-layer .ace_gutter-cell {\
padding-right: 6px;\
}\
\
.ace-eclipse .ace_text-layer {\
cursor: text;\
}\
\
.ace-eclipse .ace_cursor {\
border-left: 1px solid black;\
}\
\
.ace-eclipse .ace_line .ace_keyword, .ace-eclipse .ace_line .ace_variable {\
color: rgb(127, 0, 85);\
}\
\
.ace-eclipse .ace_line .ace_constant.ace_buildin {\
color: rgb(88, 72, 246);\
}\
\
.ace-eclipse .ace_line .ace_constant.ace_library {\
color: rgb(6, 150, 14);\
}\
\
.ace-eclipse .ace_line .ace_function {\
color: rgb(60, 76, 114);\
}\
\
.ace-eclipse .ace_line .ace_string {\
color: rgb(42, 0, 255);\
}\
\
.ace-eclipse .ace_line .ace_comment {\
color: rgb(63, 127, 95);\
}\
\
.ace-eclipse .ace_line .ace_comment.ace_doc {\
color: rgb(63, 95, 191);\
}\
\
.ace-eclipse .ace_line .ace_comment.ace_doc.ace_tag {\
color: rgb(127, 159, 191);\
}\
\
.ace-eclipse .ace_line .ace_constant.ace_numeric {\
}\
\
.ace-eclipse .ace_line .ace_tag {\
color: rgb(63, 127, 127);\
}\
\
.ace-eclipse .ace_line .ace_xml_pe {\
color: rgb(104, 104, 91);\
}\
\
.ace-eclipse .ace_marker-layer .ace_selection {\
background: rgb(181, 213, 255);\
}\
\
.ace-eclipse .ace_marker-layer .ace_bracket {\
margin: -1px 0 0 -1px;\
border: 1px solid rgb(192, 192, 192);\
}\
\
.ace-eclipse .ace_marker-layer .ace_active_line {\
background: rgb(232, 242, 254);\
}";
// import CSS once
dom.importCssString(cssText);

View file

@ -146,7 +146,7 @@ background-color:#FF0000;\
}\
\
.ace-idle-fingers .ace_support {\
color:#bc9458;\
\
}\
\
.ace-idle-fingers .ace_support.ace_function {\
@ -167,7 +167,7 @@ background-color:#FF0000;\
\
.ace-idle-fingers .ace_comment {\
font-style:italic;\
color:#BC9458;\
color:#BC9458;\
}\
\
.ace-idle-fingers .ace_comment.ace_doc {\
@ -179,19 +179,24 @@ background-color:#FF0000;\
}\
\
.ace-idle-fingers .ace_variable {\
color:#b7dff8;\
\
}\
\
.ace-idle-fingers .ace_variable.ace_language {\
color:#b7dff8;\
\
}\
\
.ace-idle-fingers .ace_xml_pe {\
\
}\
\
.ace-idle-fingers .ace_collab.ace_user1 {\
color:#323232;\
background-color:#FFF980; \
}";
// import CSS once
dom.importCssString(cssText);
exports.cssClass = "ace-idle-fingers";
});
});

View file

@ -194,4 +194,4 @@ color:#706D5B;\
dom.importCssString(cssText);
exports.cssClass = "ace-kr-theme";
});
});

View file

@ -194,4 +194,4 @@ background-color:#151C19;\
dom.importCssString(cssText);
exports.cssClass = "ace-mono-industrial";
});
});

View file

@ -194,4 +194,4 @@ background-color:#AE81FF;\
dom.importCssString(cssText);
exports.cssClass = "ace-monokai";
});
});

View file

@ -38,7 +38,148 @@
define(function(require, exports, module) {
var dom = require("pilot/dom");
var cssText = require("text!ace/theme/tm.css");
var cssText = ".ace-tm .ace_editor {\
border: 2px solid rgb(159, 159, 159);\
}\
\
.ace-tm .ace_editor.ace_focus {\
border: 2px solid #327fbd;\
}\
\
.ace-tm .ace_gutter {\
width: 50px;\
background: #e8e8e8;\
color: #333;\
overflow : hidden;\
}\
\
.ace-tm .ace_gutter-layer {\
width: 100%;\
text-align: right;\
}\
\
.ace-tm .ace_gutter-layer .ace_gutter-cell {\
padding-right: 6px;\
}\
\
.ace-tm .ace_print_margin {\
width: 1px;\
background: #e8e8e8;\
}\
\
.ace-tm .ace_text-layer {\
cursor: text;\
}\
\
.ace-tm .ace_cursor {\
border-left: 2px solid black;\
}\
\
.ace-tm .ace_cursor.ace_overwrite {\
border-left: 0px;\
border-bottom: 1px solid black;\
}\
\
.ace-tm .ace_line .ace_invisible {\
color: rgb(191, 191, 191);\
}\
\
.ace-tm .ace_line .ace_keyword {\
color: blue;\
}\
\
.ace-tm .ace_line .ace_constant.ace_buildin {\
color: rgb(88, 72, 246);\
}\
\
.ace-tm .ace_line .ace_constant.ace_language {\
color: rgb(88, 92, 246);\
}\
\
.ace-tm .ace_line .ace_constant.ace_library {\
color: rgb(6, 150, 14);\
}\
\
.ace-tm .ace_line .ace_invalid {\
background-color: rgb(153, 0, 0);\
color: white;\
}\
\
.ace-tm .ace_line .ace_support.ace_function {\
color: rgb(60, 76, 114);\
}\
\
.ace-tm .ace_line .ace_support.ace_constant {\
color: rgb(6, 150, 14);\
}\
\
.ace-tm .ace_line .ace_support.ace_type,\
.ace-tm .ace_line .ace_support.ace_class {\
color: rgb(109, 121, 222);\
}\
\
.ace-tm .ace_line .ace_keyword.ace_operator {\
color: rgb(104, 118, 135);\
}\
\
.ace-tm .ace_line .ace_string {\
color: rgb(3, 106, 7);\
}\
\
.ace-tm .ace_line .ace_comment {\
color: rgb(76, 136, 107);\
}\
\
.ace-tm .ace_line .ace_comment.ace_doc {\
color: rgb(0, 102, 255);\
}\
\
.ace-tm .ace_line .ace_comment.ace_doc.ace_tag {\
color: rgb(128, 159, 191);\
}\
\
.ace-tm .ace_line .ace_constant.ace_numeric {\
color: rgb(0, 0, 205);\
}\
\
.ace-tm .ace_line .ace_variable {\
color: rgb(49, 132, 149);\
}\
\
.ace-tm .ace_line .ace_xml_pe {\
color: rgb(104, 104, 91);\
}\
\
.ace-tm .ace_marker-layer .ace_selection {\
background: rgb(181, 213, 255);\
}\
\
.ace-tm .ace_marker-layer .ace_step {\
background: rgb(252, 255, 0);\
}\
\
.ace-tm .ace_marker-layer .ace_stack {\
background: rgb(164, 229, 101);\
}\
\
.ace-tm .ace_marker-layer .ace_bracket {\
margin: -1px 0 0 -1px;\
border: 1px solid rgb(192, 192, 192);\
}\
\
.ace-tm .ace_marker-layer .ace_active_line {\
background: rgb(232, 242, 254);\
}\
\
.ace-tm .ace_marker-layer .ace_selected_word {\
background: rgb(250, 250, 255);\
border: 1px solid rgb(200, 200, 250);\
}\
\
.ace-tm .ace_string.ace_regex {\
color: rgb(255, 0, 0)\
}";
// import CSS once
dom.importCssString(cssText);

View file

@ -1,136 +0,0 @@
.ace-tm .ace_editor {
border: 2px solid rgb(159, 159, 159);
}
.ace-tm .ace_editor.ace_focus {
border: 2px solid #327fbd;
}
.ace-tm .ace_gutter {
width: 50px;
background: #e8e8e8;
color: #333;
overflow : hidden;
}
.ace-tm .ace_gutter-layer {
width: 100%;
text-align: right;
}
.ace-tm .ace_gutter-layer .ace_gutter-cell {
padding-right: 6px;
}
.ace-tm .ace_print_margin {
width: 1px;
background: #e8e8e8;
}
.ace-tm .ace_text-layer {
cursor: text;
}
.ace-tm .ace_cursor {
border-left: 2px solid black;
}
.ace-tm .ace_cursor.ace_overwrite {
border-left: 0px;
border-bottom: 1px solid black;
}
.ace-tm .ace_line .ace_invisible {
color: rgb(191, 191, 191);
}
.ace-tm .ace_line .ace_keyword {
color: blue;
}
.ace-tm .ace_line .ace_constant.ace_buildin {
color: rgb(88, 72, 246);
}
.ace-tm .ace_line .ace_constant.ace_language {
color: rgb(88, 92, 246);
}
.ace-tm .ace_line .ace_constant.ace_library {
color: rgb(6, 150, 14);
}
.ace-tm .ace_line .ace_invalid {
background-color: rgb(153, 0, 0);
color: white;
}
.ace-tm .ace_line .ace_support.ace_function {
color: rgb(60, 76, 114);
}
.ace-tm .ace_line .ace_support.ace_constant {
color: rgb(6, 150, 14);
}
.ace-tm .ace_line .ace_support.ace_type,
.ace-tm .ace_line .ace_support.ace_class {
color: rgb(109, 121, 222);
}
.ace-tm .ace_line .ace_keyword.ace_operator {
color: rgb(104, 118, 135);
}
.ace-tm .ace_line .ace_string {
color: rgb(3, 106, 7);
}
.ace-tm .ace_line .ace_comment {
color: rgb(76, 136, 107);
}
.ace-tm .ace_line .ace_comment.ace_doc {
color: rgb(0, 102, 255);
}
.ace-tm .ace_line .ace_comment.ace_doc.ace_tag {
color: rgb(128, 159, 191);
}
.ace-tm .ace_line .ace_constant.ace_numeric {
color: rgb(0, 0, 205);
}
.ace-tm .ace_line .ace_variable {
color: rgb(49, 132, 149);
}
.ace-tm .ace_line .ace_xml_pe {
color: rgb(104, 104, 91);
}
.ace-tm .ace_marker-layer .ace_selection {
background: rgb(181, 213, 255);
}
.ace-tm .ace_marker-layer .ace_step {
background: rgb(252, 255, 0);
}
.ace-tm .ace_marker-layer .ace_stack {
background: rgb(164, 229, 101);
}
.ace-tm .ace_marker-layer .ace_bracket {
margin: -1px 0 0 -1px;
border: 1px solid rgb(192, 192, 192);
}
.ace-tm .ace_marker-layer .ace_active_line {
background: rgb(232, 242, 254);
}
.ace-tm .ace_string.ace_regex {
color: rgb(255, 0, 0)
}

View file

@ -64,9 +64,8 @@ define(function(require, exports, module) {
}\
\
.ace-twilight .ace_print_margin {\
border-left: 1px solid #3C3C3C;\
width: 100%;\
background: #242424;\
width: 1px;\
background: #e8e8e8;\
}\
\
.ace-twilight .ace_scroller {\
@ -197,4 +196,4 @@ color:#5F5A60;\
dom.importCssString(cssText);
exports.cssClass = "ace-twilight";
});
});

View file

@ -74,9 +74,6 @@ var Tokenizer = function(rules) {
var type = "text";
var value = match[0];
if (re.lastIndex == lastIndex) { throw new Error("tokenizer error"); }
lastIndex = re.lastIndex;
for ( var i = 0; i < state.length; i++) {
if (match[i + 1]) {
if (typeof state[i].token == "function") {
@ -110,6 +107,12 @@ var Tokenizer = function(rules) {
} else {
token.value += value;
}
if (lastIndex == line.length) {
break;
}
lastIndex = re.lastIndex;
};
if (token.type) {

View file

@ -38,8 +38,7 @@
define(function(require, exports, module) {
var UndoManager = function() {
this.$undoStack = [];
this.$redoStack = [];
this.reset();
};
(function() {
@ -65,6 +64,11 @@ var UndoManager = function() {
this.$undoStack.push(deltas);
}
};
this.reset = function() {
this.$undoStack = [];
this.$redoStack = [];
};
}).call(UndoManager.prototype);

View file

@ -1,4 +1,5 @@
/* ***** BEGIN LICENSE BLOCK *****
/* vim:ts=4:sts=4:sw=4:
* ***** 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
@ -19,8 +20,9 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
* Fabian Jakobs <fabian@ajax.org>
* Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)
* Julian Viereck <julian.viereck@gmail.com>
*
* 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
@ -41,6 +43,7 @@ define(function(require, exports, module) {
var oop = require("pilot/oop");
var dom = require("pilot/dom");
var event = require("pilot/event");
var useragent = require("pilot/useragent");
var GutterLayer = require("ace/layer/gutter").Gutter;
var MarkerLayer = require("ace/layer/marker").Marker;
var TextLayer = require("ace/layer/text").Text;
@ -68,22 +71,26 @@ var VirtualRenderer = function(container, theme) {
this.container.appendChild(this.scroller);
this.content = document.createElement("div");
this.content.style.cssText = "position:absolute;box-sizing:border-box;" +
"-moz-box-sizing:border-box;-webkit-box-sizing:border-box";
this.content.className = "ace_content";
this.scroller.appendChild(this.content);
this.$gutterLayer = new GutterLayer(this.$gutter);
this.$markerLayer = new MarkerLayer(this.content);
this.$markerBack = new MarkerLayer(this.content);
var textLayer = this.$textLayer = new TextLayer(this.content);
this.canvas = textLayer.element;
this.$markerFront = new MarkerLayer(this.content);
this.characterWidth = textLayer.getCharacterWidth();
this.lineHeight = textLayer.getLineHeight();
this.$cursorLayer = new CursorLayer(this.content);
this.$cursorPadding = 8;
this.layers = [ this.$markerLayer, textLayer, this.$cursorLayer ];
// Indicates whether the horizontal scrollbar is visible
this.$horizScroll = true;
this.$horizScrollAlwaysVisible = true;
this.scrollBar = new ScrollBar(container);
this.scrollBar.addEventListener("scroll", this.onScroll.bind(this));
@ -95,12 +102,14 @@ var VirtualRenderer = function(container, theme) {
column : 0
};
var self = this;
var _self = this;
this.$textLayer.addEventListener("changeCharaterSize", function() {
self.characterWidth = textLayer.getCharacterWidth();
self.lineHeight = textLayer.getLineHeight();
_self.characterWidth = textLayer.getCharacterWidth();
_self.lineHeight = textLayer.getLineHeight();
_self.$updatePrintMargin();
_self.onResize(true);
self.$loop.schedule(self.CHANGE_FULL);
_self.$loop.schedule(_self.CHANGE_FULL);
});
event.addListener(this.$gutter, "click", this.$onGutterClick.bind(this));
event.addListener(this.$gutter, "dblclick", this.$onGutterClick.bind(this));
@ -115,12 +124,11 @@ var VirtualRenderer = function(container, theme) {
this.$loop = new RenderLoop(this.$renderChanges.bind(this));
this.$loop.schedule(this.CHANGE_FULL);
this.$updatePrintMargin();
this.setPadding(4);
this.$updatePrintMargin();
};
(function() {
this.showGutter = true;
this.CHANGE_CURSOR = 1;
@ -130,16 +138,19 @@ var VirtualRenderer = function(container, theme) {
this.CHANGE_LINES = 16;
this.CHANGE_TEXT = 32;
this.CHANGE_SIZE = 64;
this.CHANGE_FULL = 128;
this.CHANGE_MARKER_BACK = 128;
this.CHANGE_MARKER_FRONT = 256;
this.CHANGE_FULL = 512;
oop.implement(this, EventEmitter);
this.setSession = function(session) {
this.session = session;
this.$cursorLayer.setSession(session);
this.$markerLayer.setSession(session);
this.$markerBack.setSession(session);
this.$markerFront.setSession(session);
this.$gutterLayer.setSession(session);
this.$textLayer.setSession(session);
this.$loop.schedule(this.CHANGE_FULL);
};
@ -184,15 +195,15 @@ var VirtualRenderer = function(container, theme) {
/**
* Triggers resize of the editor
*/
this.onResize = function() {
this.onResize = function(force) {
var changes = this.CHANGE_SIZE;
var height = dom.getInnerHeight(this.container);
if (this.$size.height != height) {
if (force || this.$size.height != height) {
this.$size.height = height;
this.scroller.style.height = height + "px";
this.scrollBar.setHeight(height);
this.scrollBar.setHeight(this.scroller.clientHeight);
if (this.session) {
this.scrollToY(this.getScrollTop());
@ -201,12 +212,20 @@ var VirtualRenderer = function(container, theme) {
}
var width = dom.getInnerWidth(this.container);
if (this.$size.width != width) {
if (force || this.$size.width != width) {
this.$size.width = width;
var gutterWidth = this.showGutter ? this.$gutter.offsetWidth : 0;
this.scroller.style.left = gutterWidth + "px";
this.scroller.style.width = Math.max(0, width - gutterWidth - this.scrollBar.getWidth()) + "px";
if (this.session.getUseWrapMode()) {
var availableWidth = this.scroller.clientWidth - this.$padding * 2;
var limit = Math.floor(availableWidth / this.characterWidth) - 1;
if (this.session.adjustWrapLimit(limit) || force) {
changes = changes | this.CHANGE_FULL;
}
}
}
this.$size.scrollerWidth = this.scroller.clientWidth;
@ -260,9 +279,11 @@ var VirtualRenderer = function(container, theme) {
};
this.setShowGutter = function(show){
if(this.showGutter === show)
return;
this.$gutter.style.display = show ? "block" : "none";
this.showGutter = show;
this.onResize();
this.onResize(true);
}
this.$updatePrintMargin = function() {
@ -270,6 +291,7 @@ var VirtualRenderer = function(container, theme) {
if (!this.$showPrintMargin && !this.$printMarginEl)
return;
if (!this.$printMarginEl) {
containerEl = document.createElement("div");
containerEl.className = "ace_print_margin_layer";
@ -280,7 +302,7 @@ var VirtualRenderer = function(container, theme) {
}
var style = this.$printMarginEl.style;
style.left = (this.characterWidth * this.$printMarginColumn) + "px";
style.left = ((this.characterWidth * this.$printMarginColumn) + this.$padding * 2) + "px";
style.visibility = this.$showPrintMargin ? "visible" : "hidden";
};
@ -292,6 +314,26 @@ var VirtualRenderer = function(container, theme) {
return this.content;
};
this.getTextAreaContainer = function() {
return this.container;
};
this.moveTextAreaToCursor = function(textarea) {
// in IE the native cursor always shines through
if (useragent.isIE)
return;
var pos = this.$cursorLayer.getPixelPosition();
if (!pos)
return;
var bounds = this.content.getBoundingClientRect();
var offset = (this.layerConfig && this.layerConfig.offset) || 0;
textarea.style.left = (bounds.left + pos.left + this.$padding) + "px";
textarea.style.top = (bounds.top + pos.top - this.scrollTop + offset) + "px";
};
this.getFirstVisibleRow = function() {
return (this.layerConfig || {}).firstRow || 0;
};
@ -320,21 +362,30 @@ var VirtualRenderer = function(container, theme) {
this.$padding = padding;
this.content.style.padding = "0 " + padding + "px";
this.$loop.schedule(this.CHANGE_FULL);
this.$updatePrintMargin();
};
this.setHScrollBarAlwaysVisible = function(alwaysVisible) {
if (this.$horizScrollAlwaysVisible != alwaysVisible) {
this.$horizScrollAlwaysVisible = alwaysVisible;
if (!this.$horizScrollAlwaysVisible || !this.$horizScroll)
this.$loop.schedule(this.CHANGE_SCROLL);
}
}
this.onScroll = function(e) {
this.scrollToY(e.data);
};
this.$updateScrollBar = function() {
this.scrollBar.setInnerHeight(this.session.getLength() * this.lineHeight);
this.scrollBar.setInnerHeight(this.session.getScreenLength() * this.lineHeight);
this.scrollBar.setScrollTop(this.scrollTop);
};
this.$renderChanges = function(changes) {
if (!changes || !this.session || !this.$tokenizer)
return;
// text, scrolling and resize changes can cause the view port size to change
if (!this.layerConfig ||
changes & this.CHANGE_FULL ||
@ -349,9 +400,11 @@ var VirtualRenderer = function(container, theme) {
if (changes & this.CHANGE_FULL) {
this.$textLayer.update(this.layerConfig);
this.showGutter && this.$gutterLayer.update(this.layerConfig);
this.$markerLayer.update(this.layerConfig);
this.$markerBack.update(this.layerConfig);
this.$markerFront.update(this.layerConfig);
this.$cursorLayer.update(this.layerConfig);
this.$updateScrollBar();
this.scrollCursorIntoView();
return;
}
@ -362,7 +415,8 @@ var VirtualRenderer = function(container, theme) {
else
this.$textLayer.scrollLines(this.layerConfig);
this.showGutter && this.$gutterLayer.update(this.layerConfig);
this.$markerLayer.update(this.layerConfig);
this.$markerBack.update(this.layerConfig);
this.$markerFront.update(this.layerConfig);
this.$cursorLayer.update(this.layerConfig);
this.$updateScrollBar();
return;
@ -383,8 +437,12 @@ var VirtualRenderer = function(container, theme) {
if (changes & this.CHANGE_CURSOR)
this.$cursorLayer.update(this.layerConfig);
if (changes & this.CHANGE_MARKER) {
this.$markerLayer.update(this.layerConfig);
if (changes & (this.CHANGE_MARKER | this.CHANGE_MARKER_FRONT)) {
this.$markerFront.update(this.layerConfig);
}
if (changes & (this.CHANGE_MARKER | this.CHANGE_MARKER_BACK)) {
this.$markerBack.update(this.layerConfig);
}
if (changes & this.CHANGE_SIZE)
@ -392,20 +450,42 @@ var VirtualRenderer = function(container, theme) {
};
this.$computeLayerConfig = function() {
var session = this.session;
var offset = this.scrollTop % this.lineHeight;
var minHeight = this.$size.scrollerHeight + this.lineHeight;
var longestLine = this.$getLongestLine();
var widthChanged = !this.layerConfig ? true : (this.layerConfig.width != longestLine);
var lineCount = Math.ceil(minHeight / this.lineHeight);
var horizScroll = this.$horizScrollAlwaysVisible || this.$size.scrollerWidth - longestLine < 0;
var horizScrollChanged = this.$horizScroll !== horizScroll;
this.$horizScroll = horizScroll;
if (horizScrollChanged)
this.scroller.style.overflowX = horizScroll ? "scroll" : "hidden";
var lineCount = Math.ceil(minHeight / this.lineHeight) - 1;
var firstRow = Math.max(0, Math.round((this.scrollTop - offset) / this.lineHeight));
var lastRow = Math.max(0, Math.min(this.session.getLength(), firstRow + lineCount) - 1);
var lastRow = firstRow + lineCount;
// Map lines on the screen to lines in the document.
var firstRowScreen, firstRowHeight;
var lineHeight = { lineHeight: this.lineHeight };
firstRow = session.screenToDocumentRow(firstRow);
firstRowScreen = session.documentToScreenRow(firstRow);
firstRowHeight = session.getRowHeight(lineHeight, firstRow);
lastRow = Math.min(session.screenToDocumentRow(lastRow), session.getLength() - 1);
minHeight = this.$size.scrollerHeight + session.getRowHeight(lineHeight, lastRow)+
firstRowHeight;
offset = this.scrollTop - firstRowScreen * this.lineHeight;
var layerConfig = this.layerConfig = {
width : longestLine,
padding : this.$padding,
firstRow : firstRow,
firstRowScreen: firstRowScreen,
lastRow : lastRow,
lineHeight : this.lineHeight,
characterWidth : this.characterWidth,
@ -418,6 +498,11 @@ var VirtualRenderer = function(container, theme) {
this.content.style.marginTop = (-offset) + "px";
this.content.style.width = longestLine + "px";
this.content.style.height = minHeight + "px";
// Horizontal scrollbar visibility may have changed, which changes
// the client height of the scroller
if (horizScrollChanged)
this.onResize(true);
};
this.$updateLines = function() {
@ -446,22 +531,21 @@ var VirtualRenderer = function(container, theme) {
};
this.$getLongestLine = function() {
var charCount = this.session.getScreenWidth();
var charCount = this.session.getScreenWidth() + 1;
if (this.$textLayer.showInvisibles)
charCount += 1;
return Math.max(this.$size.scrollerWidth - this.$padding * 2, Math.round(charCount * this.characterWidth));
};
this.addMarker = function(range, clazz, type) {
var id = this.$markerLayer.addMarker(range, clazz, type);
this.$loop.schedule(this.CHANGE_MARKER);
return id;
this.updateFrontMarkers = function() {
this.$markerFront.setMarkers(this.session.getMarkers(true));
this.$loop.schedule(this.CHANGE_MARKER_FRONT);
};
this.removeMarker = function(markerId) {
this.$markerLayer.removeMarker(markerId);
this.$loop.schedule(this.CHANGE_MARKER);
this.updateBackMarkers = function() {
this.$markerBack.setMarkers(this.session.getMarkers());
this.$loop.schedule(this.CHANGE_MARKER_BACK);
};
this.addGutterDecoration = function(row, className){
@ -479,6 +563,11 @@ var VirtualRenderer = function(container, theme) {
this.$loop.schedule(this.CHANGE_GUTTER);
};
this.setAnnotations = function(annotations) {
this.$gutterLayer.setAnnotations(annotations);
this.$loop.schedule(this.CHANGE_GUTTER);
};
this.updateCursor = function(position, overwrite) {
this.$cursorLayer.setCursor(position, overwrite);
this.$loop.schedule(this.CHANGE_CURSOR);
@ -493,6 +582,10 @@ var VirtualRenderer = function(container, theme) {
};
this.scrollCursorIntoView = function() {
// the editor is not visible
if (this.$size.scrollerHeight === 0)
return;
var pos = this.$cursorLayer.getPixelPosition();
var left = pos.left + this.$padding;
@ -511,10 +604,12 @@ var VirtualRenderer = function(container, theme) {
this.scrollToX(left);
}
if (this.scroller.scrollLeft + this.$size.scrollerWidth < left
+ this.characterWidth) {
this.scrollToX(Math.round(left + this.characterWidth
- this.$size.scrollerWidth));
if (this.scroller.scrollLeft + this.$size.scrollerWidth < left + this.characterWidth) {
if (left + this.characterWidth > this.scroller.scrollWidth)
this.$renderChanges(this.CHANGE_SIZE);
this.scrollToX(Math.round(left + this.characterWidth - this.$size.scrollerWidth));
}
},
@ -530,12 +625,29 @@ var VirtualRenderer = function(container, theme) {
return this.scrollTop / this.lineHeight;
};
this.getScrollBottomRow = function() {
return Math.max(0, Math.floor((this.scrollTop + this.$size.scrollerHeight) / this.lineHeight) - 1);
}
this.scrollToRow = function(row) {
this.scrollToY(row * this.lineHeight);
};
this.scrollToLine = function(line, center) {
var lineHeight = { lineHeight: this.lineHeight };
var offset = 0;
for (var l = 1; l < line; l++) {
offset += this.session.getRowHeight(lineHeight, l-1);
}
if (center) {
offset -= this.$size.scrollerHeight / 2;
}
this.scrollToY(offset);
};
this.scrollToY = function(scrollTop) {
var maxHeight = this.session.getLength() * this.lineHeight - this.$size.scrollerHeight;
var maxHeight = this.session.getScreenLength() * this.lineHeight - this.$size.scrollerHeight;
var scrollTop = Math.max(0, Math.min(maxHeight, scrollTop));
if (this.scrollTop !== scrollTop) {
@ -559,22 +671,20 @@ var VirtualRenderer = function(container, theme) {
this.screenToTextCoordinates = function(pageX, pageY) {
var canvasPos = this.scroller.getBoundingClientRect();
var col = Math.round((pageX + this.scroller.scrollLeft - canvasPos.left - this.$padding)
var col = Math.round((pageX + this.scroller.scrollLeft - canvasPos.left - this.$padding - dom.getPageScrollLeft())
/ this.characterWidth);
var row = Math.floor((pageY + this.scrollTop - canvasPos.top)
var row = Math.floor((pageY + this.scrollTop - canvasPos.top - dom.getPageScrollTop())
/ this.lineHeight);
return {
row : row,
column : this.session.screenToDocumentColumn(Math.max(0, Math.min(row, this.session.getLength()-1)), col)
};
return this.session.screenToDocumentPosition(row, Math.max(col, 0));
};
this.textToScreenCoordinates = function(row, column) {
var canvasPos = this.scroller.getBoundingClientRect();
var pos = this.session.documentToScreenPosition(row, column);
var x = this.$padding + Math.round(this.session.documentToScreenColumn(row, column) * this.characterWidth);
var y = row * this.lineHeight;
var x = this.$padding + Math.round(pos.column * this.characterWidth);
var y = pos.row * this.lineHeight;
return {
pageX: canvasPos.left + x - this.getScrollLeft(),
@ -591,12 +701,36 @@ var VirtualRenderer = function(container, theme) {
};
this.showComposition = function(position) {
if (!this.$composition) {
this.$composition = document.createElement("div");
this.$composition.className = "ace_composition";
this.content.appendChild(this.$composition);
}
this.$composition.innerHTML = "&nbsp;";
var pos = this.$cursorLayer.getPixelPosition();
var style = this.$composition.style;
style.top = pos.top + "px";
style.left = (pos.left + this.$padding) + "px";
style.height = this.lineHeight + "px";
this.hideCursor();
};
this.setCompositionText = function(text) {
dom.setInnerText(this.$composition, text);
};
this.hideComposition = function() {
this.showCursor();
if (!this.$composition)
return;
var style = this.$composition.style;
style.top = "-10000px";
style.left = "-10000px";
};
this.setTheme = function(theme) {
@ -628,7 +762,19 @@ var VirtualRenderer = function(container, theme) {
}
};
// Methods allows to add / remove CSS classnames to the editor element.
// This feature can be used by plug-ins to provide a visual indication of
// a certain mode that editor is in.
this.setStyle = function setStyle(style) {
dom.addCssClass(this.container, style)
};
this.unsetStyle = function unsetStyle(style) {
dom.removeCssClass(this.container, style)
};
}).call(VirtualRenderer.prototype);
exports.VirtualRenderer = VirtualRenderer;
});
});

5857
lib/ace/worker/jshint.js Normal file

File diff suppressed because it is too large Load diff

5747
lib/ace/worker/jslint.js Normal file

File diff suppressed because it is too large Load diff

43
lib/ace/worker/mirror.js Normal file
View file

@ -0,0 +1,43 @@
define(function(require, exports, module) {
var Document = require("ace/document").Document;
var lang = require("pilot/lang");
var Mirror = exports.Mirror = function(sender) {
this.sender = sender;
var doc = this.doc = new Document("");
var deferredUpdate = this.deferredUpdate = lang.deferredCall(this.onUpdate.bind(this));
var _self = this;
sender.on("change", function(e) {
doc.applyDeltas([e.data]);
deferredUpdate.schedule(_self.$timeout);
});
};
(function() {
this.$timeout = 500;
this.setTimeout = function(timeout) {
this.$timeout = timeout;
};
this.setValue = function(value) {
this.doc.setValue(value);
this.deferredUpdate.schedule(this.$timeout);
};
this.getValue = function(callbackId) {
this.sender.callback(this.doc.getValue(), callbackId);
};
this.onUpdate = function() {
// abstract method
};
}).call(Mirror.prototype);
});

108
lib/ace/worker/worker.js Normal file
View file

@ -0,0 +1,108 @@
var console = {
log: function(msg) {
postMessage({type: "log", data: msg});
}
};
var window = {
console: console
};
var require = function(id) {
var module = require.modules[id];
if (module) {
if (!module.initialized) {
module.exports = module.factory().exports;
module.initialized = true;
}
return module.exports;
}
var chunks = id.split("/");
chunks[0] = require.tlns[chunks[0]] || chunks[0];
path = chunks.join("/") + ".js";
require.id = id;
// console.log("require " + path + " " + id)
importScripts(path);
return require(id);
};
require.modules = {};
require.tlns = {};
var define = function(id, factory) {
if (!factory) {
factory = id;
id = require.id;
}
if (id.indexOf("text!") === 0)
return;
require.modules[id] = {
factory: function() {
var module = {
exports: {}
};
var returnExports = factory(require, module.exports, module);
if (returnExports)
module.exports = exports;
return module;
}
};
};
function initBaseUrls(topLevelNamespaces) {
require.tlns = topLevelNamespaces;
}
function initSender() {
var EventEmitter = require("pilot/event_emitter").EventEmitter;
var oop = require("pilot/oop");
var Sender = function() {};
(function() {
oop.implement(this, EventEmitter);
this.callback = function(data, callbackId) {
postMessage({
type: "call",
id: callbackId,
data: data
});
};
this.emit = function(name, data) {
postMessage({
type: "event",
name: name,
data: data
});
};
}).call(Sender.prototype);
return new Sender();
}
var main;
var sender;
onmessage = function(e) {
var msg = e.data;
if (msg.command) {
main[msg.command].apply(main, msg.args);
}
else if (msg.init) {
initBaseUrls(msg.tlns);
require("pilot/fixoldbrowsers");
sender = initSender();
var clazz = require(msg.module)[msg.classname];
main = new clazz(sender);
}
else if (msg.event) {
sender._dispatchEvent(msg.event, msg.data);
}
};

View file

@ -0,0 +1,112 @@
/**
* Ajax.org Code Editor (ACE)
*
* @copyright 2010, Ajax.org Services B.V.
* @license LGPLv3 <http://www.gnu.org/licenses/lgpl-3.0.txt>
* @author Fabian Jakobs <fabian AT ajax DOT org>
*/
define(function(require, exports, module) {
var oop = require("pilot/oop");
var EventEmitter = require("pilot/event_emitter").EventEmitter;
var WorkerClient = function(topLevelNamespaces, packagedJs, module, classname) {
this.callbacks = [];
if (require.packaged) {
var base = this.$guessBasePath();
var worker = this.$worker = new Worker(base + packagedJs);
}
else {
var workerUrl = require.nameToUrl("ace/worker/worker", null, "_");
var worker = this.$worker = new Worker(workerUrl);
var tlns = {};
for (var i=0; i<topLevelNamespaces.length; i++) {
var ns = topLevelNamespaces[i];
tlns[ns] = require.nameToUrl(ns, null, "_").replace(/.js$/, "");
}
}
this.$worker.postMessage({
init : true,
tlns: tlns,
module: module,
classname: classname
});
this.callbackId = 1;
this.callbacks = {};
var _self = this;
this.$worker.onerror = function(e) {
console.log(e);
throw e;
};
this.$worker.onmessage = function(e) {
var msg = e.data;
switch(msg.type) {
case "log":
window.console && console.log && console.log(msg.data);
break;
case "event":
_self._dispatchEvent(msg.name, {data: msg.data});
break;
case "call":
var callback = _self.callbacks[msg.id];
if (callback) {
callback(msg.data);
delete _self.callbacks[msg.id];
}
break;
}
};
};
(function(){
oop.implement(this, EventEmitter);
this.$guessBasePath = function() {
var scripts = document.getElementsByTagName("script");
for (var i=0; i<scripts.length; i++) {
var m = scripts[i].src.
match(/^(.*\/)ace\.js$|^(.*\/)ace-uncompressed\.js$/);
if (m) {
return m[1] || m[2];
}
}
return "";
};
this.terminate = function() {
this._dispatchEvent("terminate", {});
this.$worker.terminate();
};
this.send = function(cmd, args) {
this.$worker.postMessage({command: cmd, args: args});
};
this.call = function(cmd, args, callback) {
if (callback) {
var id = this.callbackId++;
this.callbacks[id] = callback;
args.push(id);
}
this.send(cmd, args);
};
this.emit = function(event, data) {
this.$worker.postMessage({event: event, data: data});
};
}).call(WorkerClient.prototype);
exports.WorkerClient = WorkerClient;
});