diff --git a/demo/editor.html b/demo/editor.html
index c0bdfb1d..385a2b56 100644
--- a/demo/editor.html
+++ b/demo/editor.html
@@ -9,11 +9,24 @@
@@ -39,21 +52,51 @@
+
+
+ |
+
+
+ |
+
+
+
diff --git a/jsTestDriver.conf b/jsTestDriver.conf
index 0a1df7c2..48f4c221 100644
--- a/jsTestDriver.conf
+++ b/jsTestDriver.conf
@@ -6,4 +6,6 @@ load:
- src/mode/Text.js
- src/mode/*.js
- src/*.js
- - test/*.js
\ No newline at end of file
+
+ - test/*.js
+ - test/mode/*.js
\ No newline at end of file
diff --git a/src/BackgroundTokenizer.js b/src/BackgroundTokenizer.js
index 4698b31c..161b8b5d 100644
--- a/src/BackgroundTokenizer.js
+++ b/src/BackgroundTokenizer.js
@@ -47,6 +47,13 @@ ace.BackgroundTokenizer = function(tokenizer, onUpdate, onComplete) {
};
};
+ace.BackgroundTokenizer.prototype.setTokenizer = function(tokenizer) {
+ this.tokenizer = tokenizer;
+ this.lines = [];
+
+ this.start(0);
+};
+
ace.BackgroundTokenizer.prototype.setLines = function(textLines) {
this.textLines = textLines;
this.lines = [];
diff --git a/src/Editor.js b/src/Editor.js
index af71ed83..6efd21db 100644
--- a/src/Editor.js
+++ b/src/Editor.js
@@ -4,8 +4,8 @@ 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.setDocument(doc || new ace.TextDocument(""));
this.textInput = new ace.TextInput(container, this);
new ace.KeyBinding(container, this);
@@ -39,20 +39,23 @@ ace.Editor.prototype.setDocument = function(doc) {
this.doc = doc;
doc.addChangeListener(ace.bind(this.onDocumentChange, this));
this.renderer.setDocument(doc);
+
+ this.bgTokenizer.setLines(this.doc.lines);
};
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;
+ var tokenizer = mode.getTokenizer();
- this.tokenizer = new ace.BackgroundTokenizer(mode.getTokenizer(), ace.bind(this.onTokenizerUpdate, this));
+ if (!this.bgTokenizer) {
+ var onUpdate = ace.bind(this.onTokenizerUpdate, this);
+ this.bgTokenizer = new ace.BackgroundTokenizer(tokenizer, onUpdate);
+ } else {
+ this.bgTokenizer.setTokenizer(tokenizer);
+ }
- this.tokenizer.setLines(this.doc.lines);
- this.renderer.setTokenizer(this.tokenizer);
+ this.renderer.setTokenizer(this.bgTokenizer);
};
ace.Editor.prototype.resize = function()
@@ -109,7 +112,7 @@ ace.Editor.prototype.onBlur = function() {
};
ace.Editor.prototype.onDocumentChange = function(startRow, endRow) {
- this.tokenizer.start(startRow);
+ this.bgTokenizer.start(startRow);
this.renderer.updateLines(startRow, endRow);
};
@@ -259,35 +262,30 @@ ace.Editor.prototype.removeLine = function() {
};
ace.Editor.prototype.blockIndent = function(indentString) {
- if (!this.hasSelection()) {
- return;
- };
-
- var range = this.getSelectionRange();
+ if (!this.hasSelection()) return;
var indentString = indentString || " ";
- this.doc.indentRows(range, indentString);
+ var addedColumns = this.doc.indentRows(this.getSelectionRange(), indentString);
- this.setSelectionAnchor(range.start.row, range.start.column + indentString.length);
- this._moveSelection(function() {
- this.moveCursorTo(range.end.row, range.end.column + indentString.length);
- });
+ this.shiftSelection(addedColumns);
};
ace.Editor.prototype.blockOutdent = function(indentString) {
- if (!this.hasSelection()) {
- return;
- };
-
- var range = this.getSelectionRange();
+ if (!this.hasSelection()) return;
var indentString = indentString || " ";
- var removedColumns = this.doc.outdentRows(range, indentString);
+ var addedColumns = this.doc.outdentRows(this.getSelectionRange(), indentString);
- this.setSelectionAnchor(range.start.row, range.start.column - removedColumns);
- this._moveSelection(function() {
- this.moveCursorTo(range.end.row, range.end.column - removedColumns);
- });
+ this.shiftSelection(addedColumns);
+};
+
+ace.Editor.prototype.toggleCommentLines = function() {
+ if (!this.hasSelection()) return;
+
+ var selection = this.getSelectionRange();
+ var addedColumns = this.mode.toggleCommentLines(this.doc, selection);
+
+ this.shiftSelection(addedColumns);
};
ace.Editor.prototype.onCompositionStart = function() {
@@ -591,6 +589,40 @@ ace.Editor.prototype.setSelectionAnchor = function(row, column) {
this.selectionLead = null;
};
+ace.Editor.prototype.getSelectionAnchor = function() {
+ if (this.selectionAnchor) {
+ return {
+ row: this.selectionAnchor.row,
+ column: this.selectionAnchor.column
+ };
+ } else {
+ return null;
+ }
+};
+
+ace.Editor.prototype.getSelectionLead = function() {
+ if (this.selectionLead) {
+ return {
+ row: this.selectionLead.row,
+ column: this.selectionLead.column
+ };
+ } else {
+ return null;
+ }
+};
+
+ace.Editor.prototype.shiftSelection = function(columns) {
+ if (!this.hasSelection()) return;
+
+ var anchor = this.getSelectionAnchor();
+ var lead = this.getSelectionLead();
+
+ this.setSelectionAnchor(anchor.row, anchor.column + columns);
+ this._moveSelection(function() {
+ this.moveCursorTo(lead.row, lead.column + columns);
+ });
+};
+
ace.Editor.prototype.getSelectionRange = function() {
var anchor = this.selectionAnchor;
var lead = this.selectionLead;
diff --git a/src/KeyBinding.js b/src/KeyBinding.js
index f7dd006c..f53dfddd 100644
--- a/src/KeyBinding.js
+++ b/src/KeyBinding.js
@@ -16,7 +16,8 @@ var keys = {
TAB : 9,
A : 65,
D: 68,
- L: 76
+ L: 76,
+ "7": 55
};
ace.KeyBinding = function(element, host) {
@@ -48,6 +49,13 @@ ace.KeyBinding = function(element, host) {
}
break;
+ case keys["7"]:
+ if (e.metaKey) {
+ host.toggleCommentLines();
+ return ace.stopEvent(e);
+ };
+ break;
+
case keys.UP:
if (e.metaKey && e.shiftKey) {
host.selectFileStart();
diff --git a/src/TextDocument.js b/src/TextDocument.js
index f8d26198..5934b835 100644
--- a/src/TextDocument.js
+++ b/src/TextDocument.js
@@ -89,6 +89,10 @@ ace.TextDocument.prototype.getTextRange = function(range) {
}
};
+ace.TextDocument.prototype.getLines = function(firstRow, lastRow) {
+ return this.lines.slice(firstRow, lastRow+1);
+};
+
ace.TextDocument.prototype.findMatchingBracket = function(position) {
if (position.column == 0) return null;
@@ -279,6 +283,7 @@ ace.TextDocument.prototype.indentRows = function(range, indentString) {
this.lines[i] = indentString + this.getLine(i);
}
this.fireChangeEvent(range.start.row, range.end.row);
+ return indentString.length;
};
ace.TextDocument.prototype.outdentRows = function(range, indentString) {
@@ -295,5 +300,5 @@ ace.TextDocument.prototype.outdentRows = function(range, indentString) {
}
this.fireChangeEvent(range.start.row, range.end.row);
- return outdentLength;
+ return -outdentLength;
};
\ No newline at end of file
diff --git a/src/mode/JavaScript.js b/src/mode/JavaScript.js
index 0f6003a6..4dbab043 100644
--- a/src/mode/JavaScript.js
+++ b/src/mode/JavaScript.js
@@ -5,3 +5,10 @@ ace.mode.JavaScript = function() {
};
ace.inherits(ace.mode.JavaScript, ace.mode.Text);
+ace.mode.JavaScript.prototype.toggleCommentLines = function(doc, range) {
+ var addedRows = doc.outdentRows(range, "//");
+ if (addedRows == 0) {
+ var addedRows = doc.indentRows(range, "//");
+ };
+ return addedRows;
+};
\ No newline at end of file
diff --git a/src/mode/Text.js b/src/mode/Text.js
index c48f4cf1..5b35cfbc 100644
--- a/src/mode/Text.js
+++ b/src/mode/Text.js
@@ -1,9 +1,19 @@
ace.provide("ace.mode.Text");
ace.mode.Text = function() {
- this.$tokenizer = new ace.Tokenizer({});
+ var rules = {
+ "start" : [ {
+ token : "text",
+ regex : ".+"
+ } ]
+ };
+ this.$tokenizer = new ace.Tokenizer(rules);
};
ace.mode.Text.prototype.getTokenizer = function() {
return this.$tokenizer;
+};
+
+ace.mode.Text.prototype.toggleCommentLines = function(doc, range) {
+ return 0;
};
\ No newline at end of file
diff --git a/src/mode/Xml.js b/src/mode/Xml.js
index 23ccc4b1..dbef442c 100644
--- a/src/mode/Xml.js
+++ b/src/mode/Xml.js
@@ -1,6 +1,6 @@
ace.provide("ace.mode.Xml");
ace.mode.Xml = function() {
- this.$tokenizer = new Tokenizer(new ace.mode.XmlHighlightRules().getRules());
+ this.$tokenizer = new ace.Tokenizer(new ace.mode.XmlHighlightRules().getRules());
};
ace.inherits(ace.mode.Xml, ace.mode.Text);
diff --git a/test/TextEditTest.js b/test/TextEditTest.js
index 55f0ea4e..d2ab4796 100644
--- a/test/TextEditTest.js
+++ b/test/TextEditTest.js
@@ -73,5 +73,39 @@ var TextEditTest = TestCase("TextEditTest",
var selection = editor.getSelectionRange();
assertPosition(0, 1, selection.start);
assertPosition(2, 1, selection.end);
+ },
+
+ "test: comment lines should perserve selection" : function() {
+ var doc = new ace.TextDocument([" abc", "cde"].join("\n"));
+ var editor = new ace.Editor(new MockRenderer(), doc, new ace.mode.JavaScript());
+
+ editor.moveCursorTo(0, 2);
+ editor.selectDown();
+
+ editor.toggleCommentLines();
+
+ assertEquals(["// abc", "//cde"].join("\n"), doc.toString());
+
+ var selection = editor.getSelectionRange();
+ assertPosition(0, 4, selection.start);
+ assertPosition(1, 4, selection.end);
+ },
+
+ "test: uncomment lines should perserve selection" : function() {
+ var doc = new ace.TextDocument(["// abc", "//cde"].join("\n"));
+ var editor = new ace.Editor(new MockRenderer(), doc, new ace.mode.JavaScript());
+
+ editor.moveCursorTo(0, 1);
+ editor.selectDown();
+ editor.selectRight();
+ editor.selectRight();
+
+ editor.toggleCommentLines();
+
+ assertEquals([" abc", "cde"].join("\n"), doc.toString());
+
+ var selection = editor.getSelectionRange();
+ assertPosition(0, 0, selection.start);
+ assertPosition(1, 1, selection.end);
}
});
\ No newline at end of file
diff --git a/test/mode/JavaScriptTest.js b/test/mode/JavaScriptTest.js
new file mode 100644
index 00000000..2a0ce050
--- /dev/null
+++ b/test/mode/JavaScriptTest.js
@@ -0,0 +1,51 @@
+var JavaScriptTest = new TestCase("mode.JavaScriptTest", {
+
+ setUp : function() {
+ this.mode = new ace.mode.JavaScript();
+ },
+
+ "test: getTokenizer() (smoke test)" : function() {
+ var tokenizer = this.mode.getTokenizer();
+
+ assertTrue(tokenizer instanceof ace.Tokenizer);
+
+ var tokens = tokenizer.getLineTokens("'juhu'", "start").tokens;
+ assertEquals("string", tokens[0].type);
+ },
+
+ "test: toggle comment lines should prepend '//' to each line" : function() {
+ var doc = new ace.TextDocument([" abc", "cde", "fg"].join("\n"));
+
+ var range = {
+ start: {row: 0, column: 3},
+ end: {row: 1, column: 1}
+ };
+
+ var comment = this.mode.toggleCommentLines(doc, range);
+ assertEquals(["// abc", "//cde", "fg"].join("\n"), doc.toString());
+ },
+
+ "test: toggle comment on commented lines should remove leading '//' chars" : function() {
+ var doc = new ace.TextDocument(["// abc", "//cde", "fg"].join("\n"));
+
+ var range = {
+ start: {row: 0, column: 3},
+ end: {row: 1, column: 1}
+ };
+
+ var comment = this.mode.toggleCommentLines(doc, range);
+ assertEquals([" abc", "cde", "fg"].join("\n"), doc.toString());
+ },
+
+ "test: toggle comment on multiple lines with one commented line prepend '//' to each line" : function() {
+ var doc = new ace.TextDocument(["// abc", "//cde", "fg"].join("\n"));
+
+ var range = {
+ start: {row: 0, column: 3},
+ end: {row: 2, column: 1}
+ };
+
+ var comment = this.mode.toggleCommentLines(doc, range);
+ assertEquals(["//// abc", "////cde", "//fg"].join("\n"), doc.toString());
+ }
+});
\ No newline at end of file
diff --git a/test/mode/XmlTest.js b/test/mode/XmlTest.js
new file mode 100644
index 00000000..80e6e733
--- /dev/null
+++ b/test/mode/XmlTest.js
@@ -0,0 +1,27 @@
+var XmlTest = new TestCase("mode.XmlTest", {
+
+ setUp : function() {
+ this.mode = new ace.mode.Xml();
+ },
+
+ "test: getTokenizer() (smoke test)" : function() {
+ var tokenizer = this.mode.getTokenizer();
+
+ assertTrue(tokenizer instanceof ace.Tokenizer);
+
+ var tokens = tokenizer.getLineTokens("", "start").tokens;
+ assertEquals("keyword", tokens[1].type);
+ },
+
+ "test: toggle comment lines should not do anything" : function() {
+ var doc = new ace.TextDocument([" abc", "cde", "fg"].join("\n"));
+
+ var range = {
+ start: {row: 0, column: 3},
+ end: {row: 1, column: 1}
+ };
+
+ var comment = this.mode.toggleCommentLines(doc, range);
+ assertEquals([" abc", "cde", "fg"].join("\n"), doc.toString());
+ }
+});
\ No newline at end of file