Merge branch 'master' of github.com:ajaxorg/ace

This commit is contained in:
Fabian Jakobs 2011-02-08 09:04:19 +01:00
commit 44c1196e08
30 changed files with 488 additions and 176 deletions

View file

@ -46,6 +46,11 @@ var Document = function(text) {
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);
}
@ -57,7 +62,7 @@ var Document = function(text) {
this.setValue = function(text) {
var len = this.getLength();
this.remove(new Range(0, 0, len, this.getLine(len-1).length));
this.remove(new Range(0, 0, len, this.getLine(len-1).length));
this.insert({row: 0, column:0}, text);
};
@ -262,16 +267,16 @@ var Document = function(text) {
var firstRow = range.start.row;
var lastRow = range.end.row;
if (range.isMultiLine()) {
if (range.isMultiLine()) {
var firstFullRow = range.start.column == 0 ? firstRow : firstRow + 1;
var lastFullRow = lastRow - 1;
if (range.end.column > 0)
this.removeInLine(lastRow, 0, range.end.column);
if (lastFullRow >= firstFullRow)
this.removeLines(firstFullRow, lastFullRow);
if (firstFullRow != firstRow) {
this.removeInLine(firstRow, range.start.column, this.$lines[firstRow].length);
this.removeNewLine(range.start.row);

View file

@ -295,7 +295,7 @@ var EditSession = function(text, mode) {
if (window.Worker)
this.$worker = mode.createWorker(this);
else
else
this.$worker = null;
this.$mode = mode;
@ -646,8 +646,18 @@ var EditSession = function(text, mode) {
this.setUseWrapMode = function(useWrapMode) {
if (useWrapMode != this.$useWrapMode) {
this.$useWrapMode = useWrapMode;
this.$updateWrapData(0, this.getLength() - 1);
this.$modified = true;
// If wrapMode is activaed, the wrapData array has to be initialized.
if (useWrapMode) {
var len = this.getLength();
this.$wrapMode = [];
for (i = 0; i < len; i++) {
this.$wrapData.push([]);
}
this.$updateWrapData(0, len - 1);
}
this._dispatchEvent("changeWrapMode");
}
};
@ -659,7 +669,9 @@ var EditSession = function(text, mode) {
this.setWrapLimit = function(wrapLimit) {
if (wrapLimit != this.$wrapLimit) {
this.$wrapLimit = wrapLimit;
this.$updateWrapData(0, this.getLength() - 1);
if (this.$useWrapMode) {
this.$updateWrapData(0, this.getLength() - 1);
}
this._dispatchEvent("changeWrapMode");
}
};
@ -673,20 +685,23 @@ var EditSession = function(text, mode) {
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;
lastRow = firstRow + (e.data.lines.length);
} else {
firstRow = lastRow - e.data.lines.length;
lastRow = firstRow;
}
len = e.data.lines.length;
} else {
len = lastRow - firstRow;
}
if (firstRow != lastRow) {
var len = lastRow - firstRow;
if (len != 0) {
if (action.indexOf("remove") != -1) {
this.$wrapData.splice(firstRow, len);
lastRow = firstRow;
@ -697,6 +712,10 @@ var EditSession = function(text, mode) {
}
}
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);
};
@ -706,9 +725,6 @@ var EditSession = function(text, mode) {
var wrapData = this.$wrapData;
var wrapLimit = this.$wrapLimit;
// Remove lines that are no longer there.
wrapData.splice(lines.length, wrapData.length - lines.length);
for (var row = firstRow; row <= lastRow; row++) {
wrapData[row] =
this.$computeWrapSplits(lines[row], wrapLimit, tabSize);
@ -1055,7 +1071,7 @@ var EditSession = function(text, mode) {
if (docRow > wrapData.length - 1) {
return [
this.getScreenLength(),
wrapData[wrapData.length - 1].length - 1
wrapData.length == 0 ? 0 : (wrapData[wrapData.length - 1].length - 1)
];
}

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

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

@ -124,7 +124,7 @@ oop.inherits(Mode, TextMode);
this.createWorker = function(session) {
var doc = session.getDocument();
var worker = new WorkerClient("../..", ["ace", "pilot"], "worker-javascript.js", "ace/mode/javascript_worker", "JavaScriptWorker");
var worker = new WorkerClient(["ace", "pilot"], "worker-javascript.js", "ace/mode/javascript_worker", "JavaScriptWorker");
worker.call("setValue", [doc.getValue()]);
doc.on("change", function(e) {

View file

@ -44,7 +44,6 @@ 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 WorkerClient = require("ace/worker/worker_client").WorkerClient;
var Mode = function() {
this.$tokenizer = new Tokenizer(new RubyHighlightRules().getRules());

View file

@ -279,19 +279,24 @@ var Test = {
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);
}
};

View file

@ -38,6 +38,7 @@
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,
@ -162,17 +163,17 @@ var Test = {
assert.equal(session.documentToScreenColumn(0, 2), 8);
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.setWrapLimit(12);
assert.position(session.documentToScreenPosition(0, 11), 0, 11);
assert.position(session.documentToScreenPosition(0, 12), 1, 0);
session = new EditSession(["ぁぁa"]);
session.setUseWrapMode(true);
session.setWrapLimit(2);
@ -195,20 +196,20 @@ var Test = {
assert.equal(session.screenToDocumentColumn(0, 15), 12);
assert.equal(session.screenToDocumentColumn(0, 19), 13);
},
"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.setWrapLimit(12);
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);
assert.position(session.screenToDocumentPosition(0, 1), 0, 0);
@ -217,12 +218,12 @@ var Test = {
assert.position(session.screenToDocumentPosition(0, 4), 0, 3);
assert.position(session.screenToDocumentPosition(0, 5), 0, 3);
},
"test: wrapLine split function" : function() {
var splits;
var computeWrapSplits = EditSession.prototype.$computeWrapSplits;
var c = 0;
function computeAndAssert(line, assertEqual, wrapLimit, tabSize) {
wrapLimit = wrapLimit || 12;
tabSize = tabSize || 4;
@ -233,28 +234,28 @@ var Test = {
assert.ok(splits[i] == assertEqual[i]);
}
}
// 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]);
// 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);
@ -357,6 +358,32 @@ var Test = {
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.setWrapLimit(3);
// 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);
}
};

View file

@ -19,7 +19,7 @@ var require = function(id) {
var chunks = id.split("/");
chunks[0] = require.tlns[chunks[0]] || chunks[0];
path = /*require.baseUrl + "/" +*/ chunks.join("/") + ".js";
path = chunks.join("/") + ".js";
require.id = id;
// console.log("require " + path + " " + id)
@ -29,7 +29,6 @@ var require = function(id) {
require.modules = {};
require.tlns = {};
require.baseUrl = "";
var define = function(id, factory) {
if (!factory) {
@ -52,8 +51,7 @@ var define = function(id, factory) {
};
};
function initBaseUrls(baseUrl, topLevelNamespaces) {
require.baseUrl = baseUrl;
function initBaseUrls(topLevelNamespaces) {
require.tlns = topLevelNamespaces;
}
@ -98,7 +96,7 @@ onmessage = function(e) {
main[msg.command].apply(main, msg.args);
}
else if (msg.init) {
initBaseUrls(msg.base, msg.tlns);
initBaseUrls(msg.tlns);
require("pilot/fixoldbrowsers");
sender = initSender();
var clazz = require(msg.module)[msg.classname];

View file

@ -11,12 +11,13 @@ define(function(require, exports, module) {
var oop = require("pilot/oop");
var EventEmitter = require("pilot/event_emitter").EventEmitter;
var WorkerClient = function(baseUrl, topLevelNamespaces, packagedJs, module, classname) {
var WorkerClient = function(topLevelNamespaces, packagedJs, module, classname) {
this.callbacks = [];
if (require.packaged) {
var worker = this.$worker = new Worker(packagedJs);
var base = this.$guessBasePath();
var worker = this.$worker = new Worker(base + packagedJs);
}
else {
var workerUrl = require.nameToUrl("ace/worker/worker", null, "_");
@ -25,14 +26,13 @@ var WorkerClient = function(baseUrl, topLevelNamespaces, packagedJs, module, cla
var tlns = {};
for (var i=0; i<topLevelNamespaces.length; i++) {
var ns = topLevelNamespaces[i];
tlns[ns] = require.nameToUrl(ns, null, "_").replace(/.js$/, "").replace(require.config.baseUrl, "");
tlns[ns] = require.nameToUrl(ns, null, "_").replace(/.js$/, "");
}
}
this.$worker.postMessage({
init : true,
tlns: tlns,
base: baseUrl,
module: module,
classname: classname
});
@ -71,6 +71,16 @@ var WorkerClient = function(baseUrl, topLevelNamespaces, packagedJs, module, cla
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$/);
if (m)
return m[1];
}
return "";
};
this.terminate = function() {
this._dispatchEvent("terminate", {});
this.$worker.terminate();