diff --git a/src/Editor.js b/src/Editor.js index 5468832c..615ebb51 100644 --- a/src/Editor.js +++ b/src/Editor.js @@ -155,7 +155,15 @@ var KeyBinding = function(element, host) { return ace.stopEvent(e); case keys.TAB: - host.onTextInput(" "); + if (host.hasMultiLineSelection()) { + if (e.shiftKey) { + host.blockOutdent(); + } else { + host.blockIndent(); + } + } else { + host.onTextInput(" "); + } return ace.stopEvent(e); } }); @@ -366,6 +374,38 @@ ace.Editor.prototype.removeLine = function() { } }; +ace.Editor.prototype.blockIndent = function(indentString) { + if (!this.hasSelection()) { + return; + }; + + var range = this.getSelectionRange(); + + var indentString = indentString || " "; + this.doc.indentRows(range, indentString); + + this.setSelectionAnchor(range.start.row, range.start.column + indentString.length); + this._moveSelection(function() { + this.moveCursorTo(range.end.row, range.end.column + indentString.length); + }); +}; + +ace.Editor.prototype.blockOutdent = function(indentString) { + if (!this.hasSelection()) { + return; + }; + + var range = this.getSelectionRange(); + + var indentString = indentString || " "; + var removedColumns = this.doc.outdentRows(range, indentString); + + this.setSelectionAnchor(range.start.row, range.start.column - removedColumns); + this._moveSelection(function() { + this.moveCursorTo(range.end.row, range.end.column - removedColumns); + }); +}; + ace.Editor.prototype.onCompositionStart = function() { this.renderer.showComposition(this.cursor); this.onTextInput(" "); @@ -625,6 +665,15 @@ ace.Editor.prototype.hasSelection = function() { return !!this.selectionLead; }; +ace.Editor.prototype.hasMultiLineSelection = function() { + if (!this.hasSelection()) { + return false; + } + + var range = this.getSelectionRange(); + return (range.start.row !== range.end.row); +}; + ace.Editor.prototype.setSelectionAnchor = function(row, column) { this.clearSelection(); diff --git a/src/TextDocument.js b/src/TextDocument.js index 85932671..d6b66e75 100644 --- a/src/TextDocument.js +++ b/src/TextDocument.js @@ -181,4 +181,28 @@ ace.TextDocument.prototype.replace = function(range, text) { : undefined); return end; +}; + +ace.TextDocument.prototype.indentRows = function(range, indentString) { + for (var i=range.start.row; i<= range.end.row; i++) { + this.lines[i] = indentString + this.getLine(i); + } + this.fireChangeEvent(range.start.row, range.end.row); +}; + +ace.TextDocument.prototype.outdentRows = function(range, indentString) { + outdentLength = indentString.length; + + for (var i=range.start.row; i<= range.end.row; i++) { + if (this.getLine(i).substr(0, outdentLength) !== indentString) { + return 0; + } + } + + for (var i=range.start.row; i<= range.end.row; i++) { + this.lines[i] = this.getLine(i).substring(outdentLength); + } + + this.fireChangeEvent(range.start.row, range.end.row); + return outdentLength; }; \ No newline at end of file diff --git a/test/TextEditTest.js b/test/TextEditTest.js index 0a1e5b82..2273995b 100644 --- a/test/TextEditTest.js +++ b/test/TextEditTest.js @@ -30,5 +30,48 @@ var TextEditTest = TestCase("TextEditTest", assertEquals("a\nb", doc.toString()); assertPosition(1, 0, editor.getCursorPosition()); + }, + + "test: indent block" : function() { + var doc = new ace.TextDocument(["a12345", "b12345", "c12345"].join("\n")); + var editor = new ace.Editor(doc, new MockRenderer()); + + editor.moveCursorTo(1, 3); + editor.selectDown(); + + editor.blockIndent(" "); + + assertEquals(["a12345", " b12345", " c12345"].join("\n"), + doc.toString()); + + var selection = editor.getSelectionRange(); + assertPosition(1, 7, selection.start); + assertPosition(2, 7, selection.end); + }, + + "test: outdent block" : function() { + var doc = new ace.TextDocument([" a12345", " b12345", " c12345"].join("\n")); + var editor = new ace.Editor(doc, new MockRenderer()); + + editor.moveCursorTo(0, 3); + editor.selectDown(); + editor.selectDown(); + + editor.blockOutdent(" "); + assertEquals([" a12345", "b12345", " c12345"].join("\n"), + doc.toString()); + + var selection = editor.getSelectionRange(); + assertPosition(0, 1, selection.start); + assertPosition(2, 1, selection.end); + + + editor.blockOutdent(" "); + assertEquals([" a12345", "b12345", " c12345"].join("\n"), + doc.toString()); + + var selection = editor.getSelectionRange(); + assertPosition(0, 1, selection.start); + assertPosition(2, 1, selection.end); } }); \ No newline at end of file