From e9fe18f65767c1ffc64a252a4b94f3daf1e579be Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Wed, 14 Apr 2010 19:15:52 +0200 Subject: [PATCH] first support for auto indent --- src/BackgroundTokenizer.js | 16 ++++++--- src/Editor.js | 52 ++++++++++++++++++++++------ src/KeyBinding.js | 12 +++---- src/mode/JavaScript.js | 21 +++++++++++ src/mode/JavaScriptHighlightRules.js | 4 +-- src/mode/Text.js | 4 +++ test/TextEditTest.js | 15 ++++++++ test/mode/JavaScriptTest.js | 30 ++++++++++++++++ 8 files changed, 129 insertions(+), 25 deletions(-) diff --git a/src/BackgroundTokenizer.js b/src/BackgroundTokenizer.js index 161b8b5d..f2481584 100644 --- a/src/BackgroundTokenizer.js +++ b/src/BackgroundTokenizer.js @@ -77,14 +77,20 @@ ace.BackgroundTokenizer.prototype.stop = function() { }; ace.BackgroundTokenizer.prototype.getTokens = function(row) { - if (this.lines[row]) { - return this.lines[row].tokens; - } - else { + return this._tokenizeRow(row).tokens; +}; + +ace.BackgroundTokenizer.prototype.getState = function(row) { + return this._tokenizeRow(row).state; +}; + +ace.BackgroundTokenizer.prototype._tokenizeRow = function(row) { + if (!this.lines[row]) { var state = "start"; if (row > 0 && this.lines[row - 1]) { state = this.lines[row - 1].state; } - return this.tokenizer.getLineTokens(this.textLines[row] || "", state).tokens; + this.lines[row] = this.tokenizer.getLineTokens(this.textLines[row] || "", state); } + return this.lines[row]; }; \ No newline at end of file diff --git a/src/Editor.js b/src/Editor.js index c43e326c..e999c5fc 100644 --- a/src/Editor.js +++ b/src/Editor.js @@ -221,15 +221,40 @@ ace.Editor.prototype.onCut = function() { ace.Editor.prototype.onTextInput = function(text) { if (this.hasSelection()) { - this.moveCursorToPosition(this.doc.replace(this.getSelectionRange(), text)); + var end = this.doc.replace(this.getSelectionRange(), text); this.clearSelection(); } else { - this.moveCursorToPosition(this.doc.insert(this.cursor, text)); + var end = this.doc.insert(this.cursor, text); } + + // multi line insert + var row = this.cursor.row; + if (row !== end.row) { + var line = this.doc.getLine(row); + var lineState = this.bgTokenizer.getState(row); + var indent = this.mode.getNextLineIndent(line, lineState, this.getTabString()); + if (indent) { + var indentRange = { + start: { + row: row+1, + column: 0 + }, + end : end + }; + end.column += this.doc.indentRows(indentRange, indent); + } + } + + this.moveCursorToPosition(end); this.renderer.scrollCursorIntoView(); }; + +ace.Editor.prototype.getTabString = function() { + return " "; +}; + ace.Editor.prototype.removeRight = function() { if (!this.hasSelection()) { this.selectRight(); @@ -262,18 +287,14 @@ ace.Editor.prototype.removeLine = function() { }; ace.Editor.prototype.blockIndent = function(indentString) { - if (!this.hasSelection()) return; - - var indentString = indentString || " "; + var indentString = indentString || this.getTabString(); var addedColumns = this.doc.indentRows(this.getSelectionRange(), indentString); this.shiftSelection(addedColumns); }; ace.Editor.prototype.blockOutdent = function(indentString) { - if (!this.hasSelection()) return; - - var indentString = indentString || " "; + var indentString = indentString || this.getTabString(); var addedColumns = this.doc.outdentRows(this.getSelectionRange(), indentString); this.shiftSelection(addedColumns); @@ -626,7 +647,10 @@ ace.Editor.prototype.getSelectionAnchor = function() { column: this.selectionAnchor.column }; } else { - return null; + return { + row: this.cursor.row, + column: this.cursor.column + }; } }; @@ -637,12 +661,18 @@ ace.Editor.prototype.getSelectionLead = function() { column: this.selectionLead.column }; } else { - return null; + return { + row: this.cursor.row, + column: this.cursor.column + }; } }; ace.Editor.prototype.shiftSelection = function(columns) { - if (!this.hasSelection()) return; + if (!this.hasSelection()) { + this.moveCursorTo(this.cursor.row, this.cursor.column + columns); + return; + }; var anchor = this.getSelectionAnchor(); var lead = this.getSelectionLead(); diff --git a/src/KeyBinding.js b/src/KeyBinding.js index 7d1a5262..fdd692d0 100644 --- a/src/KeyBinding.js +++ b/src/KeyBinding.js @@ -179,14 +179,12 @@ ace.KeyBinding = function(element, host) { return ace.stopEvent(e); case keys.TAB: - if (host.hasMultiLineSelection()) { - if (e.shiftKey) { - host.blockOutdent(); - } else { - host.blockIndent(); - } + if (e.shiftKey) { + host.blockOutdent(); + } else if (host.hasMultiLineSelection()) { + host.blockIndent(); } else { - host.onTextInput(" "); + host.onTextInput(host.getTabString()); } return ace.stopEvent(e); } diff --git a/src/mode/JavaScript.js b/src/mode/JavaScript.js index 4dbab043..65800a53 100644 --- a/src/mode/JavaScript.js +++ b/src/mode/JavaScript.js @@ -11,4 +11,25 @@ ace.mode.JavaScript.prototype.toggleCommentLines = function(doc, range) { var addedRows = doc.indentRows(range, "//"); }; return addedRows; +}; + +ace.mode.JavaScript.prototype.increaseIndentPatterns = { + "start" : /^(\s*).*[\{\(\[]\s*$/ +}; + +ace.mode.JavaScript.prototype.getNextLineIndent = function(line, state, tab) { + var re = this.increaseIndentPatterns[state]; + if (!re) return + + var match = line.match(re); + if (match) { + return (match[1] || "") + tab; + } + + var match = line.match(/^(\s+).*$/); + if (match) { + return match[1]; + } + + return ""; }; \ No newline at end of file diff --git a/src/mode/JavaScriptHighlightRules.js b/src/mode/JavaScriptHighlightRules.js index 6c366559..767a436c 100644 --- a/src/mode/JavaScriptHighlightRules.js +++ b/src/mode/JavaScriptHighlightRules.js @@ -76,8 +76,8 @@ ace.mode.JavaScriptHighlightRules = function() { token : function(value) { // return parens[value]; return "text"; - }, - regex : "[\\[\\]\\(\\)\\{\\}]" + }, + regex : "[\\[\\]\\(\\)\\{\\}]" }, { token : "text", regex : "\\s+" diff --git a/src/mode/Text.js b/src/mode/Text.js index 5b35cfbc..d60719a2 100644 --- a/src/mode/Text.js +++ b/src/mode/Text.js @@ -16,4 +16,8 @@ ace.mode.Text.prototype.getTokenizer = function() { ace.mode.Text.prototype.toggleCommentLines = function(doc, range) { return 0; +}; + +ace.mode.Text.prototype.getNextLineIndent = function(line, state, tab) { + return ""; }; \ No newline at end of file diff --git a/test/TextEditTest.js b/test/TextEditTest.js index 50fbb046..35aa7a8a 100644 --- a/test/TextEditTest.js +++ b/test/TextEditTest.js @@ -44,6 +44,8 @@ var TextEditTest = TestCase("TextEditTest", assertEquals(["a12345", " b12345", " c12345"].join("\n"), doc.toString()); + assertPosition(2, 7, editor.getCursorPosition()); + var selection = editor.getSelectionRange(); assertPosition(1, 7, selection.start); assertPosition(2, 7, selection.end); @@ -61,6 +63,8 @@ var TextEditTest = TestCase("TextEditTest", assertEquals([" a12345", "b12345", " c12345"].join("\n"), doc.toString()); + assertPosition(2, 1, editor.getCursorPosition()); + var selection = editor.getSelectionRange(); assertPosition(0, 1, selection.start); assertPosition(2, 1, selection.end); @@ -75,6 +79,17 @@ var TextEditTest = TestCase("TextEditTest", assertPosition(2, 1, selection.end); }, + "test: outent without a selection should update cursor" : function() { + var doc = new ace.TextDocument(" 12"); + var editor = new ace.Editor(new MockRenderer(), doc); + + editor.moveCursorTo(0, 3); + editor.blockOutdent(" "); + + assertEquals(" 12", doc.toString()); + assertPosition(0, 1, editor.getCursorPosition()); + }, + "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()); diff --git a/test/mode/JavaScriptTest.js b/test/mode/JavaScriptTest.js index 2a0ce050..3e6183d4 100644 --- a/test/mode/JavaScriptTest.js +++ b/test/mode/JavaScriptTest.js @@ -47,5 +47,35 @@ var JavaScriptTest = new TestCase("mode.JavaScriptTest", { var comment = this.mode.toggleCommentLines(doc, range); assertEquals(["//// abc", "////cde", "//fg"].join("\n"), doc.toString()); + }, + + "test: auto indent after opening brace" : function() { + var doc = new ace.TextDocument(["if () {"].join("\n")); + var editor = new ace.Editor(new MockRenderer(), doc, this.mode); + + editor.navigateLineEnd(); + editor.onTextInput("\n"); + + assertEquals(["if () {", editor.getTabString()].join("\n"), doc.toString()); + }, + + "test: no auto indent after opening brace in multi line comment" : function() { + var doc = new ace.TextDocument(["/*if () {"].join("\n")); + var editor = new ace.Editor(new MockRenderer(), doc, this.mode); + + editor.navigateLineEnd(); + editor.onTextInput("\n"); + + assertEquals(["/*if () {", ""].join("\n"), doc.toString()); + }, + + "test: no auto indent should add to existing indent" : function() { + var doc = new ace.TextDocument([" if () {"].join("\n")); + var editor = new ace.Editor(new MockRenderer(), doc, this.mode); + + editor.navigateLineEnd(); + editor.onTextInput("\n"); + + assertEquals([" if () {", " " + editor.getTabString()].join("\n"), doc.toString()); } }); \ No newline at end of file