reorganize language mode support

This commit is contained in:
Fabian Jakobs 2010-04-14 11:39:42 +02:00
commit 29435328f3
13 changed files with 287 additions and 233 deletions

View file

@ -20,9 +20,12 @@
<link rel="stylesheet" href="../css/editor.css" type="text/css" charset="utf-8">
<script src="../src/ace.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/mode/Text.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/mode/JavaScript.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/mode/JavaScriptHighlightRules.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/mode/Xml.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/mode/XmlHighlightRules.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/TextDocument.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/JavaScript.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/XML.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/Tokenizer.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/BackgroundTokenizer.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/CursorLayer.js" type="text/javascript" charset="utf-8"></script>
@ -42,7 +45,11 @@
<script type="text/javascript" charset="utf-8">
var container = document.getElementById("container");
var editor = new ace.Editor(new ace.TextDocument("Juhu Kinners"), new ace.VirtualRenderer(container));
var editor = new ace.Editor(
new ace.VirtualRenderer(container),
new ace.TextDocument("Juhu Kinners"),
new ace.mode.JavaScript()
);
window.onresize = function() {
editor.resize();

View file

@ -2,5 +2,8 @@
server: http://localhost:4224
load:
- src/ace.js
- src/mode/Text.js
- src/mode/*.js
- src/*.js
- test/*.js

View file

@ -1,9 +1,12 @@
ace.provide("ace.Editor");
ace.Editor = function(doc, renderer) {
ace.Editor = function(renderer, doc, mode) {
var container = renderer.getContainerElement();
this.renderer = renderer;
this.setDocument(doc || new ace.TextDocument(""));
this.setMode(mode || new ace.mode.Text());
this.textInput = new ace.TextInput(container, this);
new ace.KeyBinding(container, this);
@ -15,16 +18,6 @@ ace.Editor = function(doc, renderer) {
ace.addTripleClickListener(container, ace.bind(this.selectLine,
this));
this.doc = doc;
doc.addChangeListener(ace.bind(this.onDocumentChange, this));
renderer.setDocument(doc);
this.tokenizer = new ace.BackgroundTokenizer(new ace.Tokenizer(
ace.JavaScript.RULES), ace.bind(this.onTokenizerUpdate, this));
this.tokenizer.setLines(doc.lines);
renderer.setTokenizer(this.tokenizer);
this.cursor = {
row : 0,
column : 0
@ -37,6 +30,31 @@ ace.Editor = function(doc, renderer) {
this.renderer.draw();
};
ace.Editor.prototype.setDocument = function(doc) {
// TODO: document change is not yet supported
if (this.doc) {
throw new Error("TODO: document change is not yet supported");
}
this.doc = doc;
doc.addChangeListener(ace.bind(this.onDocumentChange, this));
this.renderer.setDocument(doc);
};
ace.Editor.prototype.setMode = function(mode) {
// TODO: mode change is not yet supported
if (this.mode) {
throw new Error("TODO: mode change is not yet supported");
}
this.mode = mode;
this.tokenizer = new ace.BackgroundTokenizer(mode.getTokenizer(), ace.bind(this.onTokenizerUpdate, this));
this.tokenizer.setLines(this.doc.lines);
this.renderer.setTokenizer(this.tokenizer);
};
ace.Editor.prototype.resize = function()
{
this.renderer.scrollToY(this.renderer.getScrollTop());

View file

@ -1,111 +0,0 @@
ace.provide("ace.JavaScript");
(function() {
var keywords = {
"break" : 1,
"case" : 1,
"catch" : 1,
"continue" : 1,
"default" : 1,
"delete" : 1,
"do" : 1,
"else" : 1,
"finally" : 1,
"for" : 1,
"function" : 1,
"if" : 1,
"in" : 1,
"instanceof" : 1,
"new" : 1,
"return" : 1,
"switch" : 1,
"throw" : 1,
"try" : 1,
"typeof" : 1,
"var" : 1,
"while" : 1,
"with" : 1
};
// regexp must not have capturing parentheses
// regexps are ordered -> the first match is used
ace.JavaScript.RULES = {
start : [ {
token : "comment",
regex : "\\/\\/.*$"
}, {
token : "comment", // multi line comment in one line
regex : "\\/\\*.*?\\*\\/"
}, {
token : "comment", // multi line comment start
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 : "number", // hex
regex : "0[xX][0-9a-fA-F]+\\b"
}, {
token : "number", // float
regex : "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"
}, {
token : function(value) {
if (keywords[value]) {
return "keyword";
}
else {
return "identifier";
}
},
regex : "[a-zA-Z_][a-zA-Z0-9_]*\\b"
}, {
token : function(value) {
// return parens[value];
return "text";
},
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 : '.+'
} ]
};
})();

View file

@ -1,72 +0,0 @@
ace.provide("ace.XML");
(function() {
// regexp must not have capturing parentheses
// regexps are ordered -> the first match is used
ace.XML.RULES = {
start : [ {
token : "text",
regex : "<\\!\\[CDATA\\[",
next : "cdata"
}, {
token : "xml_pe",
regex : "<\\?.*?\\?>"
}, {
token : "comment",
regex : "<\\!--",
next : "comment"
}, {
token : "text", // opening tag
regex : "<",
next : "tag"
}, {
token : "text",
regex : "\\s+"
}, {
token : "text",
regex : ".+"
} ],
tag : [ {
token : "text",
regex : ">",
next : "start"
}, {
token : "keyword",
regex : "[-_a-zA-Z0-9:]+"
}, {
token : "text",
regex : "\\s+"
}, {
token : "string",
regex : '".*?"'
}, {
token : "string",
regex : "'.*?'"
} ],
cdata : [ {
token : "text",
regex : "\\]\\]>",
next : "start"
}, {
token : "text",
regex : "\\s+"
}, {
token : "text",
regex : ".+"
} ],
comment : [ {
token : "comment",
regex : ".*?-->",
next : "start"
}, {
token : "comment",
regex : ".+"
} ]
};
})();

View file

@ -13,6 +13,14 @@ ace.provide = function(namespace) {
}
};
ace.inherits = function(ctor, superCtor) {
var tempCtor = function() {};
tempCtor.prototype = superCtor.prototype;
ctor.super_ = superCtor.prototype;
ctor.prototype = new tempCtor();
ctor.prototype.constructor = ctor;
};
ace.addListener = function(elem, type, callback) {
if (elem.addEventListener) {
return elem.addEventListener(type, callback, false);
@ -64,14 +72,6 @@ ace.preventDefault = function(e) {
e.returnValue = false;
};
ace.inherits = function(ctor, superCtor) {
var tempCtor = function() {};
tempCtor.prototype = superCtor.prototype;
ctor.super_ = superCtor.prototype;
ctor.prototype = new tempCtor();
ctor.prototype.constructor = ctor;
};
ace.getInnerWidth = function(element) {
return (parseInt(ace.computedStyle(element, "paddingLeft"))
+ parseInt(ace.computedStyle(element, "paddingRight")) + element.clientWidth);

7
src/mode/JavaScript.js Normal file
View file

@ -0,0 +1,7 @@
ace.provide("ace.mode.JavaScript");
ace.mode.JavaScript = function() {
this.$tokenizer = new ace.Tokenizer(new ace.mode.JavaScriptHighlightRules().getRules());
};
ace.inherits(ace.mode.JavaScript, ace.mode.Text);

View file

@ -0,0 +1,114 @@
ace.provide("ace.mode.JavaScriptHighlightRules");
ace.mode.JavaScriptHighlightRules = function() {
var keywords = {
"break" : 1,
"case" : 1,
"catch" : 1,
"continue" : 1,
"default" : 1,
"delete" : 1,
"do" : 1,
"else" : 1,
"finally" : 1,
"for" : 1,
"function" : 1,
"if" : 1,
"in" : 1,
"instanceof" : 1,
"new" : 1,
"return" : 1,
"switch" : 1,
"throw" : 1,
"try" : 1,
"typeof" : 1,
"var" : 1,
"while" : 1,
"with" : 1
};
// 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 in one line
regex : "\\/\\*.*?\\*\\/"
}, {
token : "comment", // multi line comment start
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 : "number", // hex
regex : "0[xX][0-9a-fA-F]+\\b"
}, {
token : "number", // float
regex : "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"
}, {
token : function(value) {
if (keywords[value]) {
return "keyword";
}
else {
return "identifier";
}
},
regex : "[a-zA-Z_][a-zA-Z0-9_]*\\b"
}, {
token : function(value) {
// return parens[value];
return "text";
},
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 : '.+'
} ]
};
};
ace.mode.JavaScriptHighlightRules.prototype.getRules = function() {
return this._rules;
};

9
src/mode/Text.js Normal file
View file

@ -0,0 +1,9 @@
ace.provide("ace.mode.Text");
ace.mode.Text = function() {
this.$tokenizer = new ace.Tokenizer({});
};
ace.mode.Text.prototype.getTokenizer = function() {
return this.$tokenizer;
};

6
src/mode/Xml.js Normal file
View file

@ -0,0 +1,6 @@
ace.provide("ace.mode.Xml");
ace.mode.Xml = function() {
this.$tokenizer = new Tokenizer(new ace.mode.XmlHighlightRules().getRules());
};
ace.inherits(ace.mode.Xml, ace.mode.Text);

View file

@ -0,0 +1,75 @@
ace.provide("ace.mode.XmlHighlightRules");
ace.mode.XmlHighlightRules = function() {
// regexp must not have capturing parentheses
// regexps are ordered -> the first match is used
this._rules = {
start : [ {
token : "text",
regex : "<\\!\\[CDATA\\[",
next : "cdata"
}, {
token : "xml_pe",
regex : "<\\?.*?\\?>"
}, {
token : "comment",
regex : "<\\!--",
next : "comment"
}, {
token : "text", // opening tag
regex : "<",
next : "tag"
}, {
token : "text",
regex : "\\s+"
}, {
token : "text",
regex : ".+"
} ],
tag : [ {
token : "text",
regex : ">",
next : "start"
}, {
token : "keyword",
regex : "[-_a-zA-Z0-9:]+"
}, {
token : "text",
regex : "\\s+"
}, {
token : "string",
regex : '".*?"'
}, {
token : "string",
regex : "'.*?'"
} ],
cdata : [ {
token : "text",
regex : "\\]\\]>",
next : "start"
}, {
token : "text",
regex : "\\s+"
}, {
token : "text",
regex : ".+"
} ],
comment : [ {
token : "comment",
regex : ".*?-->",
next : "start"
}, {
token : "comment",
regex : ".+"
} ]
};
};
ace.mode.XmlHighlightRules.prototype.getRules = function() {
return this._rules;
};

View file

@ -7,16 +7,16 @@ var NavigationTest = TestCase("NavigationTest",
},
"test: navigate to end of file should place the cursor on last row and column" : function() {
var editor = new ace.Editor(this.createTextDocument(200, 10),
new MockRenderer());
var doc = this.createTextDocument(200, 10);
var editor = new ace.Editor(new MockRenderer(), doc);
editor.navigateFileEnd();
assertPosition(199, 10, editor.getCursorPosition());
},
"test: navigate to end of file should scroll the last line into view" : function() {
var editor = new ace.Editor(this.createTextDocument(200, 10),
new MockRenderer());
var doc = this.createTextDocument(200, 10);
var editor = new ace.Editor(new MockRenderer(), doc);
editor.navigateFileEnd();
var cursor = editor.getCursorPosition();
@ -26,16 +26,16 @@ var NavigationTest = TestCase("NavigationTest",
},
"test: navigate to start of file should place the cursor on the first row and column" : function() {
var editor = new ace.Editor(this.createTextDocument(200, 10),
new MockRenderer());
var doc = this.createTextDocument(200, 10);
var editor = new ace.Editor(new MockRenderer(), doc);
editor.navigateFileStart();
assertPosition(0, 0, editor.getCursorPosition());
},
"test: navigate to start of file should scroll the first row into view" : function() {
var editor = new ace.Editor(this.createTextDocument(200, 10),
new MockRenderer());
var doc = this.createTextDocument(200, 10);
var editor = new ace.Editor(new MockRenderer(), doc);
editor.scrollToRow(editor.getLastVisibleRow() + 20);
editor.navigateFileStart();
@ -44,8 +44,8 @@ var NavigationTest = TestCase("NavigationTest",
},
"test: move selection lead to end of file" : function() {
var editor = new ace.Editor(this.createTextDocument(200, 10),
new MockRenderer());
var doc = this.createTextDocument(200, 10);
var editor = new ace.Editor(new MockRenderer(), doc);
editor.moveCursorTo(100, 5);
editor.selectFileEnd();
@ -57,8 +57,8 @@ var NavigationTest = TestCase("NavigationTest",
},
"test: move selection lead to start of file" : function() {
var editor = new ace.Editor(this.createTextDocument(200, 10),
new MockRenderer());
var doc = this.createTextDocument(200, 10);
var editor = new ace.Editor(new MockRenderer(), doc);
editor.moveCursorTo(100, 5);
editor.selectFileStart();
@ -72,7 +72,7 @@ var NavigationTest = TestCase("NavigationTest",
"test: navigate word right" : function() {
var doc = new ace.TextDocument( ["ab",
" Juhu Kinners (abc, 12)", " cde"].join("\n"));
var editor = new ace.Editor(doc, new MockRenderer());
var editor = new ace.Editor(new MockRenderer(), doc);
editor.navigateDown();
assertPosition(1, 0, editor.getCursorPosition());
@ -111,7 +111,7 @@ var NavigationTest = TestCase("NavigationTest",
"test: select word right if cursor in word" : function() {
var doc = new ace.TextDocument("Juhu Kinners");
var editor = new ace.Editor(doc, new MockRenderer());
var editor = new ace.Editor(new MockRenderer(), doc);
editor.moveCursorTo(0, 2);
@ -122,7 +122,7 @@ var NavigationTest = TestCase("NavigationTest",
"test: navigate word left" : function() {
var doc = new ace.TextDocument( ["ab",
" Juhu Kinners (abc, 12)", " cde"].join("\n"));
var editor = new ace.Editor(doc, new MockRenderer());
var editor = new ace.Editor(new MockRenderer(), doc);
editor.navigateDown();
editor.navigateLineEnd();
@ -162,7 +162,7 @@ var NavigationTest = TestCase("NavigationTest",
"test: select word left if cursor in word" : function() {
var doc = new ace.TextDocument("Juhu Kinners");
var editor = new ace.Editor(doc, new MockRenderer());
var editor = new ace.Editor(new MockRenderer(), doc);
editor.moveCursorTo(0, 8);
@ -172,7 +172,7 @@ var NavigationTest = TestCase("NavigationTest",
"test: select word right and select" : function() {
var doc = new ace.TextDocument("Juhu Kinners");
var editor = new ace.Editor(doc, new MockRenderer());
var editor = new ace.Editor(new MockRenderer(), doc);
editor.moveCursorTo(0, 0);
editor.selectWordRight();
@ -185,7 +185,7 @@ var NavigationTest = TestCase("NavigationTest",
"test: select word left and select" : function() {
var doc = new ace.TextDocument("Juhu Kinners");
var editor = new ace.Editor(doc, new MockRenderer());
var editor = new ace.Editor(new MockRenderer(), doc);
editor.moveCursorTo(0, 3);
editor.selectWordLeft();
@ -197,8 +197,7 @@ var NavigationTest = TestCase("NavigationTest",
},
"test: goto hidden line should scroll the line into the middle of the viewport" : function() {
var editor = new ace.Editor(this.createTextDocument(200, 5),
new MockRenderer());
var editor = new ace.Editor(new MockRenderer(), this.createTextDocument(200, 5));
editor.navigateTo(0, 0);
editor.gotoLine(100);
@ -232,8 +231,7 @@ var NavigationTest = TestCase("NavigationTest",
},
"test: goto visible line should only move the cursor and not scroll": function() {
var editor = new ace.Editor(this.createTextDocument(200, 5),
new MockRenderer());
var editor = new ace.Editor(new MockRenderer(), this.createTextDocument(200, 5));
editor.navigateTo(0, 0);
editor.gotoLine(11);
@ -245,4 +243,4 @@ var NavigationTest = TestCase("NavigationTest",
assertPosition(32, 0, editor.getCursorPosition());
assertEquals(30, editor.getFirstVisibleRow());
}
});
});

View file

@ -2,7 +2,7 @@ var TextEditTest = TestCase("TextEditTest",
{
"test: delete line from the middle" : function() {
var doc = new ace.TextDocument(["a", "b", "c", "d"].join("\n"));
var editor = new ace.Editor(doc, new MockRenderer());
var editor = new ace.Editor(new MockRenderer(), doc);
editor.moveCursorTo(1, 1);
editor.removeLine();
@ -13,7 +13,7 @@ var TextEditTest = TestCase("TextEditTest",
"test: delete first line" : function() {
var doc = new ace.TextDocument(["a", "b", "c"].join("\n"));
var editor = new ace.Editor(doc, new MockRenderer());
var editor = new ace.Editor(new MockRenderer(), doc);
editor.removeLine();
@ -23,7 +23,7 @@ var TextEditTest = TestCase("TextEditTest",
"test: delete last" : function() {
var doc = new ace.TextDocument(["a", "b", "c"].join("\n"));
var editor = new ace.Editor(doc, new MockRenderer());
var editor = new ace.Editor(new MockRenderer(), doc);
editor.moveCursorTo(2, 1);
editor.removeLine();
@ -34,7 +34,7 @@ var TextEditTest = TestCase("TextEditTest",
"test: indent block" : function() {
var doc = new ace.TextDocument(["a12345", "b12345", "c12345"].join("\n"));
var editor = new ace.Editor(doc, new MockRenderer());
var editor = new ace.Editor(new MockRenderer(), doc);
editor.moveCursorTo(1, 3);
editor.selectDown();
@ -51,7 +51,7 @@ var TextEditTest = TestCase("TextEditTest",
"test: outdent block" : function() {
var doc = new ace.TextDocument([" a12345", " b12345", " c12345"].join("\n"));
var editor = new ace.Editor(doc, new MockRenderer());
var editor = new ace.Editor(new MockRenderer(), doc);
editor.moveCursorTo(0, 3);
editor.selectDown();