From 7529f90080fe6930d0ae7f51db19be1fcbe3ef3d Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Thu, 15 Apr 2010 13:58:49 +0200 Subject: [PATCH] refactor selection support into a separate class Merge branch 'master' into HEAD Conflicts: src/Editor.js src/KeyBinding.js src/Selection.js --- demo/editor.html | 2 + src/Editor.js | 598 +++++++++++---------------------------- src/KeyBinding.js | 83 +++--- src/MEventEmitter.js | 27 ++ src/Selection.js | 359 +++++++++++++++++++++++ src/TextDocument.js | 5 + src/TextLayer.js | 6 +- src/ace.js | 22 ++ test/EventEmitterTest.js | 20 ++ test/NavigationTest.js | 187 +----------- test/SelectionTest.js | 177 ++++++++++++ test/TextEditTest.js | 81 ++++-- 12 files changed, 887 insertions(+), 680 deletions(-) create mode 100644 src/MEventEmitter.js create mode 100644 src/Selection.js create mode 100644 test/EventEmitterTest.js create mode 100644 test/SelectionTest.js diff --git a/demo/editor.html b/demo/editor.html index 0fce1956..fee837f5 100644 --- a/demo/editor.html +++ b/demo/editor.html @@ -38,6 +38,8 @@ + + diff --git a/src/Editor.js b/src/Editor.js index 30d92c25..6779f8a3 100644 --- a/src/Editor.js +++ b/src/Editor.js @@ -15,19 +15,15 @@ ace.Editor = function(renderer, doc, mode) { ace.addListener(container, "dblclick", ace .bind(this.onMouseDoubleClick, this)); ace.addMouseWheelListener(container, ace.bind(this.onMouseWheel, this)); - ace.addTripleClickListener(container, ace.bind(this.selectLine, - this)); + ace.addTripleClickListener(container, ace.bind(this.selection.selectLine, + this.selection)); - this.cursor = { - row : 0, - column : 0 - }; - - this.selectionAnchor = null; - this.selectionLead = null; - this.selection = null; + this.selectionMarker = null; + this._blockScrolling = false; this.renderer.draw(); + this.onCursorChange(); + this.onSelectionChange(); }; ace.Editor.prototype.setDocument = function(doc) { @@ -37,12 +33,25 @@ ace.Editor.prototype.setDocument = function(doc) { } this.doc = doc; + doc.addChangeListener(ace.bind(this.onDocumentChange, this)); this.renderer.setDocument(doc); + this.selection = doc.getSelection(); + + var onCursorChange = ace.bind(this.onCursorChange, this); + this.selection.addEventListener("changeCursor", onCursorChange); + + var onSelectionChange = ace.bind(this.onSelectionChange, this); + this.selection.addEventListener("changeSelection", onSelectionChange); + this.bgTokenizer.setLines(this.doc.lines); }; +ace.Editor.prototype.getSelection = function() { + return this.selection; +}; + ace.Editor.prototype.setMode = function(mode) { if (this.mode == mode) return; @@ -59,17 +68,13 @@ ace.Editor.prototype.setMode = function(mode) { this.renderer.setTokenizer(this.bgTokenizer); }; + ace.Editor.prototype.resize = function() { this.renderer.scrollToY(this.renderer.getScrollTop()); this.renderer.draw(); }; -ace.Editor.prototype.updateCursor = function() { - this.renderer.updateCursor(this.cursor); - this._highlightBrackets(); -}; - ace.Editor.prototype._highlightBrackets = function() { if (this._bracketHighlight) { @@ -87,7 +92,7 @@ ace.Editor.prototype._highlightBrackets = function() { setTimeout(function() { self._highlightPending = false; - var pos = self.doc.findMatchingBracket(self.cursor); + var pos = self.doc.findMatchingBracket(self.getCursorPosition()); if (pos) { range = { start: pos, @@ -121,12 +126,35 @@ ace.Editor.prototype.onTokenizerUpdate = function(startRow, endRow) { this.renderer.updateLines(startRow, endRow); }; +ace.Editor.prototype.onCursorChange = function() { + this._highlightBrackets(); + this.renderer.updateCursor(this.getCursorPosition()); + + if (!this._blockScrolling) { + this.renderer.scrollCursorIntoView(); + } +}; + +ace.Editor.prototype.onSelectionChange = function() { + if (this.selectionMarker) { + this.renderer.removeMarker(this.selectionMarker); + } + this.selectionMarker = null; + + if (!this.selection.isEmpty()) { + var range = this.selection.getRange(); + this.selectionMarker = this.renderer.addMarker(range, "selection", "text"); + } + + this.onCursorChange(); +}; + ace.Editor.prototype.onMouseDown = function(e) { this.textInput.focus(); var pos = this.renderer.screenToTextCoordinates(e.pageX, e.pageY); this.moveCursorToPosition(pos); - this.setSelectionAnchor(pos.row, pos.column); + this.selection.setSelectionAnchor(pos.row, pos.column); this.renderer.scrollCursorIntoView(); var _self = this; @@ -148,7 +176,7 @@ ace.Editor.prototype.onMouseDown = function(e) { selectionLead = _self.renderer.screenToTextCoordinates(mousePageX, mousePageY); - _self._moveSelection(function() { + _self.selection._moveSelection(function() { _self.moveCursorToPosition(selectionLead); }); _self.renderer.scrollCursorIntoView(); @@ -164,8 +192,10 @@ ace.Editor.prototype.tokenRe = /^[\w\d]+/g; ace.Editor.prototype.nonTokenRe = /^[^\w\d]+/g; ace.Editor.prototype.onMouseDoubleClick = function(e) { - var line = this.doc.getLine(this.cursor.row); - var column = this.cursor.column; + var cursor = this.selection.getCursor(); + + var line = this.doc.getLine(cursor.row); + var column = cursor.column; var inToken = false; if (column > 0) { @@ -192,9 +222,10 @@ ace.Editor.prototype.onMouseDoubleClick = function(e) { end++; } - this.setSelectionAnchor(this.cursor.row, start); - this._moveSelection(function() { - this.moveCursorTo(this.cursor.row, end); + var selection = this.selection; + selection.setSelectionAnchor(cursor.row, start); + selection._moveSelection(function() { + selection.moveCursorTo(cursor.row, end); }); }; @@ -205,7 +236,7 @@ ace.Editor.prototype.onMouseWheel = function(e) { }; ace.Editor.prototype.getCopyText = function() { - if (this.hasSelection()) { + if (!this.selection.isEmpty()) { return this.doc.getTextRange(this.getSelectionRange()); } else { @@ -214,23 +245,29 @@ ace.Editor.prototype.getCopyText = function() { }; ace.Editor.prototype.onCut = function() { - if (this.hasSelection()) { + if (!this.selection.isEmpty()) { this.moveCursorToPosition(this.doc.remove(this.getSelectionRange())); this.clearSelection(); } }; ace.Editor.prototype.onTextInput = function(text) { - if (this.hasSelection()) { + var cursor = this.getCursorPosition(); + + if (this.getUseSoftTabs()) { + text = text.replace(/\t/g, this.getTabString()); + } + + if (!this.selection.isEmpty()) { var end = this.doc.replace(this.getSelectionRange(), text); this.clearSelection(); } else { - var end = this.doc.insert(this.cursor, text); + var end = this.doc.insert(cursor, text); } // multi line insert - var row = this.cursor.row; + var row = cursor.row; if (row !== end.row) { var line = this.doc.getLine(row); var lineState = this.bgTokenizer.getState(row); @@ -253,37 +290,59 @@ ace.Editor.prototype.onTextInput = function(text) { ace.Editor.prototype.getTabString = function() { - return " "; + if (this.getUseSoftTabs()) { + return new Array(this.getTabSize()+1).join(" "); + } + return "\t"; +}; + +ace.Editor.prototype._useSoftTabs = true; +ace.Editor.prototype.setUseSoftTabs = function(useSoftTabs) { + if (this._useSoftTabs === useSoftTabs) return; + + this._useSoftTabs = useSoftTabs; +}; + +ace.Editor.prototype.getUseSoftTabs = function() { + return this._useSoftTabs; +}; + +ace.Editor.prototype._tabSize = 4; +ace.Editor.prototype.setTabSize = function(tabSize) { + if (this._tabSize === tabSize) return; + + this._tabSize = tabSize; + this.renderer.draw(); +}; + +ace.Editor.prototype.getTabSize = function() { + return this._tabSize; }; ace.Editor.prototype.removeRight = function() { - if (!this.hasSelection()) { - this.selectRight(); + if (this.selection.isEmpty()) { + this.selection.selectRight(); } this.moveCursorToPosition(this.doc.remove(this.getSelectionRange())); this.clearSelection(); - - this.renderer.scrollCursorIntoView(); }; ace.Editor.prototype.removeLeft = function() { - if (!this.hasSelection()) { - this.selectLeft(); + if (this.selection.isEmpty()) { + this.selection.selectLeft(); } this.moveCursorToPosition(this.doc.remove(this.getSelectionRange())); this.clearSelection(); - - this.renderer.scrollCursorIntoView(); -}, +}; ace.Editor.prototype.removeLine = function() { - this.selectLine(); + this.selection.selectLine(); this.moveCursorToPosition(this.doc.remove(this.getSelectionRange())); this.clearSelection(); - if (this.cursor.row == this.doc.getLength() - 1) { + if (this.getCursorPosition().row == this.doc.getLength() - 1) { this.removeLeft(); - this.moveCursorLineStart(); + this.selection.moveCursorLineStart(); } }; @@ -291,23 +350,23 @@ ace.Editor.prototype.blockIndent = function(indentString) { var indentString = indentString || this.getTabString(); var addedColumns = this.doc.indentRows(this.getSelectionRange(), indentString); - this.shiftSelection(addedColumns); + this.selection.shiftSelection(addedColumns); }; ace.Editor.prototype.blockOutdent = function(indentString) { var indentString = indentString || this.getTabString(); var addedColumns = this.doc.outdentRows(this.getSelectionRange(), indentString); - this.shiftSelection(addedColumns); + this.selection.shiftSelection(addedColumns); }; ace.Editor.prototype.toggleCommentLines = function() { - if (!this.hasSelection()) return; + if (this.selection.isEmpty()) return; - var selection = this.getSelectionRange(); - var addedColumns = this.mode.toggleCommentLines(this.doc, selection); + var range = this.getSelectionRange(); + var addedColumns = this.mode.toggleCommentLines(this.doc, range); - this.shiftSelection(addedColumns); + this.selection.shiftSelection(addedColumns); }; ace.Editor.prototype.moveLinesDown = function() { @@ -332,15 +391,16 @@ ace.Editor.prototype._moveLines = function(mover) { var linesMoved = mover.call(this, firstRow, lastRow); - this.setSelectionAnchor(lastRow+linesMoved+1, 0); - this._moveSelection(function() { - this.moveCursorTo(firstRow+linesMoved, 0); + var selection = this.selection; + selection.setSelectionAnchor(lastRow+linesMoved+1, 0); + selection._moveSelection(function() { + selection.moveCursorTo(firstRow+linesMoved, 0); }); }; ace.Editor.prototype.onCompositionStart = function() { - this.renderer.showComposition(this.cursor); + this.renderer.showComposition(this.getCursorPosition()); this.onTextInput(" "); }; @@ -353,6 +413,7 @@ ace.Editor.prototype.onCompositionEnd = function() { this.removeLeft(); }; + ace.Editor.prototype.getFirstVisibleRow = function() { return this.renderer.getFirstVisibleRow(); }; @@ -380,6 +441,7 @@ ace.Editor.prototype.getPageUpRow = function() { return firstRow - (lastRow - firstRow) + 1; }; + ace.Editor.prototype.scrollPageDown = function() { this.scrollToRow(this.getPageDownRow()); }; @@ -392,423 +454,101 @@ ace.Editor.prototype.scrollToRow = function(row) { this.renderer.scrollToRow(row); }; -ace.Editor.prototype.navigateTo = function(row, column) { - this.clearSelection(); - this.moveCursorTo(row, column); - this.renderer.scrollCursorIntoView(); + +ace.Editor.prototype.getCursorPosition = function() { + return this.selection.getCursor(); }; -ace.Editor.prototype.navigateUp = function() { - this.clearSelection(); - this.moveCursorUp(); - this.renderer.scrollCursorIntoView(); +ace.Editor.prototype.getSelectionRange = function() { + return this.selection.getRange(); }; -ace.Editor.prototype.navigateDown = function() { - this.clearSelection(); - this.moveCursorDown(); - this.renderer.scrollCursorIntoView(); -}; - -ace.Editor.prototype.navigateLeft = function() { - if (this.hasSelection()) { - var selectionStart = this.getSelectionRange().start; - this.moveCursorToPosition(selectionStart); - } - else { - this.moveCursorLeft(); - } - this.clearSelection(); - - this.renderer.scrollCursorIntoView(); -}; - -ace.Editor.prototype.navigateRight = function() { - if (this.hasSelection()) { - var selectionEnd = this.getSelectionRange().end; - this.moveCursorToPosition(selectionEnd); - } - else { - this.moveCursorRight(); - } - this.clearSelection(); - - this.renderer.scrollCursorIntoView(); -}, - -ace.Editor.prototype.navigateLineStart = function() { - this.clearSelection(); - this.moveCursorLineStart(); - this.renderer.scrollCursorIntoView(); -}; - -ace.Editor.prototype.navigateLineEnd = function() { - this.clearSelection(); - this.moveCursorLineEnd(); - this.renderer.scrollCursorIntoView(); -}; - -ace.Editor.prototype.navigateFileEnd = function() { - this.clearSelection(); - this.moveCursorFileEnd(); - this.renderer.scrollCursorIntoView(); -}, - -ace.Editor.prototype.navigateFileStart = function() { - this.clearSelection(); - this.moveCursorFileStart(); - this.renderer.scrollCursorIntoView(); -}, - -ace.Editor.prototype.navigateWordRight = function() { - this.clearSelection(); - this.moveCursorWordRight(); - this.renderer.scrollCursorIntoView(); -}, - -ace.Editor.prototype.navigateWordLeft = function() { - this.clearSelection(); - this.moveCursorWordLeft(); - this.renderer.scrollCursorIntoView(); -}, - -ace.Editor.prototype.moveCursorUp = function() { - this.moveCursorBy(-1, 0); -}; - -ace.Editor.prototype.moveCursorDown = function() { - this.moveCursorBy(1, 0); -}; - -ace.Editor.prototype.moveCursorLeft = function() { - if (this.cursor.column == 0) { - if (this.cursor.row > 0) { - this.moveCursorTo(this.cursor.row - 1, this.doc - .getLine(this.cursor.row - 1).length); - } - } - else { - this.moveCursorBy(0, -1); - } -}; - -ace.Editor.prototype.moveCursorRight = function() { - if (this.cursor.column == this.doc.getLine(this.cursor.row).length) { - if (this.cursor.row < this.doc.getLength() - 1) { - this.moveCursorTo(this.cursor.row + 1, 0); - } - } - else { - this.moveCursorBy(0, 1); - } -}; - -ace.Editor.prototype.moveCursorLineStart = function() { - this.moveCursorTo(this.cursor.row, 0); -}; - -ace.Editor.prototype.moveCursorLineEnd = function() { - this.moveCursorTo(this.cursor.row, - this.doc.getLine(this.cursor.row).length); -}; - -ace.Editor.prototype.moveCursorFileEnd = function() { - var row = this.doc.getLength() - 1; - var column = this.doc.getLine(row).length; - this.moveCursorTo(row, column); -}; - -ace.Editor.prototype.moveCursorFileStart = function() { - this.moveCursorTo(0, 0); -}; - -ace.Editor.prototype.moveCursorWordRight = function() { - var row = this.cursor.row; - var column = this.cursor.column; - var line = this.doc.getLine(row); - var rightOfCursor = line.substring(column); - - var match; - this.nonTokenRe.lastIndex = 0; - this.tokenRe.lastIndex = 0; - - if (column == line.length) { - this.moveCursorRight(); - return; - } - else if (match = this.nonTokenRe.exec(rightOfCursor)) { - column += this.nonTokenRe.lastIndex; - this.nonTokenRe.lastIndex = 0; - } - else if (match = this.tokenRe.exec(rightOfCursor)) { - column += this.tokenRe.lastIndex; - this.tokenRe.lastIndex = 0; - } - - this.moveCursorTo(row, column); -}; - -ace.Editor.prototype.moveCursorWordLeft = function() { - var row = this.cursor.row; - var column = this.cursor.column; - var line = this.doc.getLine(row); - var leftOfCursor = ace.stringReverse(line.substring(0, column)); - - var match; - this.nonTokenRe.lastIndex = 0; - this.tokenRe.lastIndex = 0; - - if (column == 0) { - this.moveCursorLeft(); - return; - } - else if (match = this.nonTokenRe.exec(leftOfCursor)) { - column -= this.nonTokenRe.lastIndex; - this.nonTokenRe.lastIndex = 0; - } - else if (match = this.tokenRe.exec(leftOfCursor)) { - column -= this.tokenRe.lastIndex; - this.tokenRe.lastIndex = 0; - } - - this.moveCursorTo(row, column); -}; - -ace.Editor.prototype.moveCursorBy = function(rows, chars) { - this.moveCursorTo(this.cursor.row + rows, this.cursor.column + chars); -}; - - -ace.Editor.prototype.moveCursorToPosition = function(position) { - this.moveCursorTo(position.row, position.column); -}; - -ace.Editor.prototype._clipPositionToDocument = function(row, column) { - var pos = {}; - - if (row >= this.doc.getLength()) { - pos.row = this.doc.getLength() - 1; - pos.column = this.doc.getLine(pos.row).length; - } - else if (row < 0) { - pos.row = 0; - pos.column = 0; - } - else { - pos.row = row; - pos.column = Math.min(this.doc.getLine(pos.row).length, - Math.max(0, column)); - } - return pos; +ace.Editor.prototype.clearSelection = function() { + this.selection.clearSelection(); }; ace.Editor.prototype.moveCursorTo = function(row, column) { - this.cursor = this._clipPositionToDocument(row, column); - this.updateCursor(); + this.selection.moveCursorTo(row, column); }; +ace.Editor.prototype.moveCursorToPosition = function(pos) { + this.selection.moveCursorToPosition(pos); +}; + + ace.Editor.prototype.gotoLine = function(lineNumber) { + this._blockScrolling = true; this.moveCursorTo(lineNumber, 0); - if (!this.isRowVisible(this.cursor.row)) { + this._blockScrolling = false; + + if (!this.isRowVisible(this.getCursorPosition().row)) { this.scrollToRow(lineNumber - Math.floor(this.getVisibleRowCount() / 2)); } }, -ace.Editor.prototype.getCursorPosition = function() { - return { - row : this.cursor.row, - column : this.cursor.column - }; -}; - -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) { +ace.Editor.prototype.navigateTo = function(row, column) { this.clearSelection(); - - this.selectionAnchor = this._clipPositionToDocument(row, column); - this.selectionLead = null; + this.moveCursorTo(row, column); }; -ace.Editor.prototype.getSelectionAnchor = function() { - if (this.selectionAnchor) { - return { - row: this.selectionAnchor.row, - column: this.selectionAnchor.column - }; - } else { - return { - row: this.cursor.row, - column: this.cursor.column - }; - } +ace.Editor.prototype.navigateUp = function() { + this.clearSelection(); + this.selection.moveCursorUp(); }; -ace.Editor.prototype.getSelectionLead = function() { - if (this.selectionLead) { - return { - row: this.selectionLead.row, - column: this.selectionLead.column - }; - } else { - return { - row: this.cursor.row, - column: this.cursor.column - }; - } +ace.Editor.prototype.navigateDown = function() { + this.clearSelection(); + this.selection.moveCursorDown(); }; -ace.Editor.prototype.shiftSelection = function(columns) { - if (!this.hasSelection()) { - this.moveCursorTo(this.cursor.row, this.cursor.column + columns); - 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 || this.cursor; - var lead = this.selectionLead || this.cursor; - - if (anchor.row > lead.row - || (anchor.row == lead.row && anchor.column > lead.column)) { - return { - start : lead, - end : anchor - }; +ace.Editor.prototype.navigateLeft = function() { + if (!this.selection.isEmpty()) { + var selectionStart = this.getSelectionRange().start; + this.moveCursorToPosition(selectionStart); } else { - return { - start : anchor, - end : lead - }; + this.selection.moveCursorLeft(); } + this.clearSelection(); }; -ace.Editor.prototype.clearSelection = function() { - this.selectionLead = null; - this.selectionAnchor = null; - - if (this.selection) { - this.renderer.removeMarker(this.selection); - this.selection = null; +ace.Editor.prototype.navigateRight = function() { + if (!this.selection.isEmpty()) { + var selectionEnd = this.getSelectionRange().end; + this.moveCursorToPosition(selectionEnd); } -}; - -ace.Editor.prototype.selectAll = function() { - var lastRow = this.doc.getLength() - 1; - this.setSelectionAnchor(lastRow, this.doc.getLine(lastRow).length); - - this._moveSelection(function() { - this.moveCursorTo(0, 0); - }); -}; - -ace.Editor.prototype._moveSelection = function(mover) { - if (!this.selectionAnchor) { - this.selectionAnchor = { - row : this.cursor.row, - column : this.cursor.column - }; + else { + this.selection.moveCursorRight(); } - - mover.call(this); - - this.selectionLead = { - row : this.cursor.row, - column : this.cursor.column - }; - - if (this.selection) { - this.renderer.removeMarker(this.selection); - } - this.selection = this.renderer.addMarker(this.getSelectionRange(), - "selection", "text"); - this.renderer.scrollCursorIntoView(); + this.clearSelection(); }; -ace.Editor.prototype.selectUp = function() { - this._moveSelection(this.moveCursorUp); +ace.Editor.prototype.navigateLineStart = function() { + this.clearSelection(); + this.selection.moveCursorLineStart(); }; -ace.Editor.prototype.selectDown = function() { - this._moveSelection(this.moveCursorDown); +ace.Editor.prototype.navigateLineEnd = function() { + this.clearSelection(); + this.selection.moveCursorLineEnd(); }; -ace.Editor.prototype.selectRight = function() { - this._moveSelection(this.moveCursorRight); +ace.Editor.prototype.navigateFileEnd = function() { + this.clearSelection(); + this.selection.moveCursorFileEnd(); }; -ace.Editor.prototype.selectLeft = function() { - this._moveSelection(this.moveCursorLeft); +ace.Editor.prototype.navigateFileStart = function() { + this.clearSelection(); + this.selection.moveCursorFileStart(); }; -ace.Editor.prototype.selectLineStart = function() { - this._moveSelection(this.moveCursorLineStart); +ace.Editor.prototype.navigateWordRight = function() { + this.clearSelection(); + this.selection.moveCursorWordRight(); }; -ace.Editor.prototype.selectLineEnd = function() { - this._moveSelection(this.moveCursorLineEnd); -}; - -ace.Editor.prototype.selectPageDown = function() { - var row = this.getPageDownRow() + Math.floor(this.getVisibleRowCount() / 2); - - this.scrollPageDown(); - - this._moveSelection(function() { - this.moveCursorTo(row, this.cursor.column); - }); -}; - -ace.Editor.prototype.selectPageUp = function() { - var visibleRows = this.getLastVisibleRow() - this.getFirstVisibleRow(); - var row = this.getPageUpRow() + Math.round(visibleRows / 2); - - this.scrollPageUp(); - - this._moveSelection(function() { - this.moveCursorTo(row, this.cursor.column); - }); -}; - -ace.Editor.prototype.selectFileEnd = function() { - this._moveSelection(this.moveCursorFileEnd); -}; - -ace.Editor.prototype.selectFileStart = function() { - this._moveSelection(this.moveCursorFileStart); -}; - -ace.Editor.prototype.selectWordRight = function() { - this._moveSelection(this.moveCursorWordRight); -}; - -ace.Editor.prototype.selectWordLeft = function() { - this._moveSelection(this.moveCursorWordLeft); -}; - -ace.Editor.prototype.selectLine = function() { - this.setSelectionAnchor(this.cursor.row, 0); - this._moveSelection(function() { - this.moveCursorTo(this.cursor.row + 1, 0); - }); +ace.Editor.prototype.navigateWordLeft = function() { + this.clearSelection(); + this.selection.moveCursorWordLeft(); }; \ No newline at end of file diff --git a/src/KeyBinding.js b/src/KeyBinding.js index fdd692d0..48417b97 100644 --- a/src/KeyBinding.js +++ b/src/KeyBinding.js @@ -20,21 +20,22 @@ var keys = { "7": 55 }; -ace.KeyBinding = function(element, host) { +ace.KeyBinding = function(element, editor) { ace.addListener(element, "keydown", function(e) { var key = e.keyCode; + var selection = editor.getSelection(); switch (key) { case keys.A: if (e.metaKey) { - host.selectAll(); + selection.selectAll(); return ace.stopEvent(e); } break; case keys.D: if (e.metaKey) { - host.removeLine(); + editor.removeLine(); return ace.stopEvent(e); } break; @@ -43,7 +44,7 @@ ace.KeyBinding = function(element, host) { if (e.metaKey) { var line = parseInt(prompt("Enter line number:")); if (!isNaN(line)) { - host.gotoLine(line); + editor.gotoLine(line); return ace.stopEvent(e); } } @@ -51,140 +52,140 @@ ace.KeyBinding = function(element, host) { case keys["7"]: if (e.metaKey) { - host.toggleCommentLines(); + editor.toggleCommentLines(); return ace.stopEvent(e); }; break; case keys.UP: if (e.altKey) { - host.moveLinesUp(); + editor.moveLinesUp(); } else if (e.metaKey && e.shiftKey) { - host.selectFileStart(); + selection.selectFileStart(); } else if (e.metaKey) { - host.navigateFileStart(); + editor.navigateFileStart(); } else if (e.shiftKey) { - host.selectUp(); + selection.selectUp(); } else { - host.navigateUp(); + editor.navigateUp(); } return ace.stopEvent(e); case keys.DOWN: if (e.altKey) { - host.moveLinesDown(); + editor.moveLinesDown(); } else if (e.metaKey && e.shiftKey) { - host.selectFileEnd(); + selection.selectFileEnd(); } else if (e.metaKey) { - host.navigateFileEnd(); + editor.navigateFileEnd(); } else if (e.shiftKey) { - host.selectDown(); + selection.selectDown(); } else { - host.navigateDown(); + editor.navigateDown(); } return ace.stopEvent(e); case keys.LEFT: if (e.altKey && e.shiftKey) { - host.selectWordLeft(); + selection.selectWordLeft(); } else if (e.altKey) { - host.navigateWordLeft(); + editor.navigateWordLeft(); } else if (e.metaKey && e.shiftKey) { - host.selectLineStart(); + selection.selectLineStart(); } else if (e.metaKey) { - host.navigateLineStart(); + editor.navigateLineStart(); } else if (e.shiftKey) { - host.selectLeft(); + selection.selectLeft(); } else { - host.navigateLeft(); + editor.navigateLeft(); } return ace.stopEvent(e); case keys.RIGHT: if (e.altKey && e.shiftKey) { - host.selectWordRight(); + selection.selectWordRight(); } else if (e.altKey) { - host.navigateWordRight(); + editor.navigateWordRight(); } else if (e.metaKey && e.shiftKey) { - host.selectLineEnd(); + selection.selectLineEnd(); } else if (e.metaKey) { - host.navigateLineEnd(); + editor.navigateLineEnd(); } else if (e.shiftKey) { - host.selectRight(); + selection.selectRight(); } else { - host.navigateRight(); + editor.navigateRight(); } return ace.stopEvent(e); case keys.PAGEDOWN: if (e.shiftKey) { - host.selectPageDown(); + selection.selectPageDown(); } else { - host.scrollPageDown(); + editor.scrollPageDown(); } return ace.stopEvent(e); case keys.PAGEUP: if (e.shiftKey) { - host.selectPageUp(); + selection.selectPageUp(); } else { - host.scrollPageUp(); + editor.scrollPageUp(); } return ace.stopEvent(e); case keys.POS1: if (e.shiftKey) { - host.selectLineStart(); + selection.selectLineStart(); } else { - host.navigateLineStart(); + editor.navigateLineStart(); } return ace.stopEvent(e); case keys.END: if (e.shiftKey) { - host.selectLineEnd(); + selection.selectLineEnd(); } else { - host.navigateLineEnd(); + editor.navigateLineEnd(); } return ace.stopEvent(e); case keys.DELETE: - host.removeRight(); + editor.removeRight(); return ace.stopEvent(e); case keys.BACKSPACE: - host.removeLeft(); + editor.removeLeft(); return ace.stopEvent(e); case keys.TAB: if (e.shiftKey) { - host.blockOutdent(); - } else if (host.hasMultiLineSelection()) { - host.blockIndent(); + editor.blockOutdent(); + } else if (selection.isMultiLineSelection()) { + editor.blockIndent(); } else { - host.onTextInput(host.getTabString()); + editor.onTextInput("\t"); } return ace.stopEvent(e); } diff --git a/src/MEventEmitter.js b/src/MEventEmitter.js new file mode 100644 index 00000000..6ae1983f --- /dev/null +++ b/src/MEventEmitter.js @@ -0,0 +1,27 @@ +ace.provide("ace.MEventEmitter"); + +ace.MEventEmitter.$initEvents = function() { + this._eventRegistry = {}; +}; + +ace.MEventEmitter.$dispatchEvent = function(eventName, e) { + var listeners = this._eventRegistry[eventName]; + if (!listeners) return; + + var e = e || {}; + e.type = eventName; + + for (var i=0; i lead.row + || (anchor.row == lead.row && anchor.column > lead.column)) { + return { + start : lead, + end : anchor + }; + } + else { + return { + start : anchor, + end : lead + }; + } +}; + +ace.Selection.prototype.clearSelection = function() { + this.selectionLead = null; + this.selectionAnchor = null; + this.updateSelection(); +}; + + +ace.Selection.prototype.selectAll = function() { + var lastRow = this.doc.getLength() - 1; + this.setSelectionAnchor(lastRow, this.doc.getLine(lastRow).length); + + this._moveSelection(function() { + this.moveCursorTo(0, 0); + }); +}; + +ace.Selection.prototype._moveSelection = function(mover) { + if (!this.selectionAnchor) { + this.selectionAnchor = { + row : this.cursor.row, + column : this.cursor.column + }; + } + + mover.call(this); + + this.selectionLead = { + row : this.cursor.row, + column : this.cursor.column + }; + + this.updateSelection(); +}; + +ace.Selection.prototype.selectUp = function() { + this._moveSelection(this.moveCursorUp); +}; + +ace.Selection.prototype.selectDown = function() { + this._moveSelection(this.moveCursorDown); +}; + +ace.Selection.prototype.selectRight = function() { + this._moveSelection(this.moveCursorRight); +}; + +ace.Selection.prototype.selectLeft = function() { + this._moveSelection(this.moveCursorLeft); +}; + +ace.Selection.prototype.selectLineStart = function() { + this._moveSelection(this.moveCursorLineStart); +}; + +ace.Selection.prototype.selectLineEnd = function() { + this._moveSelection(this.moveCursorLineEnd); +}; + +ace.Selection.prototype.selectPageDown = function() { + var row = this.getPageDownRow() + Math.floor(this.getVisibleRowCount() / 2); + + this.scrollPageDown(); + + this._moveSelection(function() { + this.moveCursorTo(row, this.cursor.column); + }); +}; + +ace.Selection.prototype.selectPageUp = function() { + var visibleRows = this.getLastVisibleRow() - this.getFirstVisibleRow(); + var row = this.getPageUpRow() + Math.round(visibleRows / 2); + + this.scrollPageUp(); + + this._moveSelection(function() { + this.moveCursorTo(row, this.cursor.column); + }); +}; + +ace.Selection.prototype.selectFileEnd = function() { + this._moveSelection(this.moveCursorFileEnd); +}; + +ace.Selection.prototype.selectFileStart = function() { + this._moveSelection(this.moveCursorFileStart); +}; + +ace.Selection.prototype.tokenRe = /^[\w\d]+/g; +ace.Selection.prototype.nonTokenRe = /^[^\w\d]+/g; + +ace.Selection.prototype.selectWordRight = function() { + this._moveSelection(this.moveCursorWordRight); +}; + +ace.Selection.prototype.selectWordLeft = function() { + this._moveSelection(this.moveCursorWordLeft); +}; + +ace.Selection.prototype.selectLine = function() { + this.setSelectionAnchor(this.cursor.row, 0); + this._moveSelection(function() { + this.moveCursorTo(this.cursor.row + 1, 0); + }); +}; + +ace.Selection.prototype.moveCursorUp = function() { + this.moveCursorBy(-1, 0); +}; + +ace.Selection.prototype.moveCursorDown = function() { + this.moveCursorBy(1, 0); +}; + +ace.Selection.prototype.moveCursorLeft = function() { + if (this.cursor.column == 0) { + if (this.cursor.row > 0) { + this.moveCursorTo(this.cursor.row - 1, this.doc + .getLine(this.cursor.row - 1).length); + } + } + else { + this.moveCursorBy(0, -1); + } +}; + +ace.Selection.prototype.moveCursorRight = function() { + if (this.cursor.column == this.doc.getLine(this.cursor.row).length) { + if (this.cursor.row < this.doc.getLength() - 1) { + this.moveCursorTo(this.cursor.row + 1, 0); + } + } + else { + this.moveCursorBy(0, 1); + } +}; + +ace.Selection.prototype.moveCursorLineStart = function() { + this.moveCursorTo(this.cursor.row, 0); +}; + +ace.Selection.prototype.moveCursorLineEnd = function() { + this.moveCursorTo(this.cursor.row, + this.doc.getLine(this.cursor.row).length); +}; + +ace.Selection.prototype.moveCursorFileEnd = function() { + var row = this.doc.getLength() - 1; + var column = this.doc.getLine(row).length; + this.moveCursorTo(row, column); +}; + +ace.Selection.prototype.moveCursorFileStart = function() { + this.moveCursorTo(0, 0); +}; + +ace.Selection.prototype.moveCursorWordRight = function() { + var row = this.cursor.row; + var column = this.cursor.column; + var line = this.doc.getLine(row); + var rightOfCursor = line.substring(column); + + var match; + this.nonTokenRe.lastIndex = 0; + this.tokenRe.lastIndex = 0; + + if (column == line.length) { + this.moveCursorRight(); + return; + } + else if (match = this.nonTokenRe.exec(rightOfCursor)) { + column += this.nonTokenRe.lastIndex; + this.nonTokenRe.lastIndex = 0; + } + else if (match = this.tokenRe.exec(rightOfCursor)) { + column += this.tokenRe.lastIndex; + this.tokenRe.lastIndex = 0; + } + + this.moveCursorTo(row, column); +}; + +ace.Selection.prototype.moveCursorWordLeft = function() { + var row = this.cursor.row; + var column = this.cursor.column; + var line = this.doc.getLine(row); + var leftOfCursor = ace.stringReverse(line.substring(0, column)); + + var match; + this.nonTokenRe.lastIndex = 0; + this.tokenRe.lastIndex = 0; + + if (column == 0) { + this.moveCursorLeft(); + return; + } + else if (match = this.nonTokenRe.exec(leftOfCursor)) { + column -= this.nonTokenRe.lastIndex; + this.nonTokenRe.lastIndex = 0; + } + else if (match = this.tokenRe.exec(leftOfCursor)) { + column -= this.tokenRe.lastIndex; + this.tokenRe.lastIndex = 0; + } + + this.moveCursorTo(row, column); +}; + +ace.Selection.prototype.moveCursorBy = function(rows, chars) { + this.moveCursorTo(this.cursor.row + rows, this.cursor.column + chars); +}; + + +ace.Selection.prototype.moveCursorToPosition = function(position) { + this.moveCursorTo(position.row, position.column); +}; + +ace.Selection.prototype.moveCursorTo = function(row, column) { + this.cursor = this._clipPositionToDocument(row, column); + this.updateCursor(); +}; + +ace.Selection.prototype.moveCursorUp = function() { + this.moveCursorBy(-1, 0); +}; + +ace.Selection.prototype._clipPositionToDocument = function(row, column) { + var pos = {}; + + if (row >= this.doc.getLength()) { + pos.row = this.doc.getLength() - 1; + pos.column = this.doc.getLine(pos.row).length; + } + else if (row < 0) { + pos.row = 0; + pos.column = 0; + } + else { + pos.row = row; + pos.column = Math.min(this.doc.getLine(pos.row).length, + Math.max(0, column)); + } + return pos; +}; + +ace.Selection.prototype._clone = function(pos) { + return { + row: pos.row, + column: pos.column + }; +}; \ No newline at end of file diff --git a/src/TextDocument.js b/src/TextDocument.js index 20349760..53c73d2e 100644 --- a/src/TextDocument.js +++ b/src/TextDocument.js @@ -3,6 +3,7 @@ ace.provide("ace.TextDocument"); ace.TextDocument = function(text) { this.lines = this._split(text); this.modified = true; + this.selection = new ace.Selection(this); this.listeners = []; }; @@ -15,6 +16,10 @@ ace.TextDocument.prototype.toString = function() { return this.lines.join("\n"); }; +ace.TextDocument.prototype.getSelection = function() { + return this.selection; +}; + ace.TextDocument.prototype.addChangeListener = function(listener) { this.listeners.push(listener); }; diff --git a/src/TextLayer.js b/src/TextLayer.js index d91ba5ed..d3cc4ed7 100644 --- a/src/TextLayer.js +++ b/src/TextLayer.js @@ -53,8 +53,7 @@ ace.TextLayer.prototype.updateLines = function(layerConfig, firstRow, lastRow) { var lineElement = lineElements[i - layerConfig.firstRow]; lineElement.innerHTML = html.join(""); - } - ; + }; }; ace.TextLayer.prototype.update = function(config) { @@ -84,6 +83,5 @@ ace.TextLayer.prototype.renderLine = function(stringBuilder, row) { else { stringBuilder.push(output); } - } - ; + }; }; \ No newline at end of file diff --git a/src/ace.js b/src/ace.js index 4a938586..a49b2805 100644 --- a/src/ace.js +++ b/src/ace.js @@ -21,6 +21,12 @@ ace.inherits = function(ctor, superCtor) { ctor.prototype.constructor = ctor; }; +ace.mixin = function(obj, mixin) { + for (var key in mixin) { + obj[key] = mixin[key]; + } +}; + ace.addListener = function(elem, type, callback) { if (elem.addEventListener) { return elem.addEventListener(type, callback, false); @@ -111,6 +117,22 @@ ace.stringReverse = function(string) { return string.split("").reverse().join(""); }; +if (Array.prototype.indexOf) { + ace.arrayIndexOf = function(array, searchElement) { + return array.indexOf(searchElement); + }; +} +else { + ace.arrayIndexOf = function(array, searchElement) { + for (var i=0; i= cursor.row); }, - "test: navigate to start of file should place the cursor on the first row and column" : function() { - 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 doc = this.createTextDocument(200, 10); var editor = new ace.Editor(new MockRenderer(), doc); @@ -43,190 +27,37 @@ var NavigationTest = TestCase("NavigationTest", assertEquals(0, editor.getFirstVisibleRow()); }, - "test: move selection lead to end of file" : function() { - var doc = this.createTextDocument(200, 10); - var editor = new ace.Editor(new MockRenderer(), doc); - - editor.moveCursorTo(100, 5); - editor.selectFileEnd(); - - var selection = editor.getSelectionRange(); - - assertPosition(100, 5, selection.start); - assertPosition(199, 10, selection.end); - }, - - "test: move selection lead to start of file" : function() { - var doc = this.createTextDocument(200, 10); - var editor = new ace.Editor(new MockRenderer(), doc); - - editor.moveCursorTo(100, 5); - editor.selectFileStart(); - - var selection = editor.getSelectionRange(); - - assertPosition(0, 0, selection.start); - assertPosition(100, 5, selection.end); - }, - - "test: navigate word right" : function() { - var doc = new ace.TextDocument( ["ab", - " Juhu Kinners (abc, 12)", " cde"].join("\n")); - var editor = new ace.Editor(new MockRenderer(), doc); - - editor.navigateDown(); - assertPosition(1, 0, editor.getCursorPosition()); - - editor.navigateWordRight(); - assertPosition(1, 1, editor.getCursorPosition()); - - editor.navigateWordRight(); - assertPosition(1, 5, editor.getCursorPosition()); - - editor.navigateWordRight(); - assertPosition(1, 6, editor.getCursorPosition()); - - editor.navigateWordRight(); - assertPosition(1, 13, editor.getCursorPosition()); - - editor.navigateWordRight(); - assertPosition(1, 15, editor.getCursorPosition()); - - editor.navigateWordRight(); - assertPosition(1, 18, editor.getCursorPosition()); - - editor.navigateWordRight(); - assertPosition(1, 20, editor.getCursorPosition()); - - editor.navigateWordRight(); - assertPosition(1, 22, editor.getCursorPosition()); - - editor.navigateWordRight(); - assertPosition(1, 23, editor.getCursorPosition()); - - // wrap line - editor.navigateWordRight(); - assertPosition(2, 0, editor.getCursorPosition()); - }, - - "test: select word right if cursor in word" : function() { - var doc = new ace.TextDocument("Juhu Kinners"); - var editor = new ace.Editor(new MockRenderer(), doc); - - editor.moveCursorTo(0, 2); - - editor.navigateWordRight(); - assertPosition(0, 4, editor.getCursorPosition()); - }, - - "test: navigate word left" : function() { - var doc = new ace.TextDocument( ["ab", - " Juhu Kinners (abc, 12)", " cde"].join("\n")); - var editor = new ace.Editor(new MockRenderer(), doc); - - editor.navigateDown(); - editor.navigateLineEnd(); - assertPosition(1, 23, editor.getCursorPosition()); - - editor.navigateWordLeft(); - assertPosition(1, 22, editor.getCursorPosition()); - - editor.navigateWordLeft(); - assertPosition(1, 20, editor.getCursorPosition()); - - editor.navigateWordLeft(); - assertPosition(1, 18, editor.getCursorPosition()); - - editor.navigateWordLeft(); - assertPosition(1, 15, editor.getCursorPosition()); - - editor.navigateWordLeft(); - assertPosition(1, 13, editor.getCursorPosition()); - - editor.navigateWordLeft(); - assertPosition(1, 6, editor.getCursorPosition()); - - editor.navigateWordLeft(); - assertPosition(1, 5, editor.getCursorPosition()); - - editor.navigateWordLeft(); - assertPosition(1, 1, editor.getCursorPosition()); - - editor.navigateWordLeft(); - assertPosition(1, 0, editor.getCursorPosition()); - - // wrap line - editor.navigateWordLeft(); - assertPosition(0, 2, editor.getCursorPosition()); - }, - - "test: select word left if cursor in word" : function() { - var doc = new ace.TextDocument("Juhu Kinners"); - var editor = new ace.Editor(new MockRenderer(), doc); - - editor.moveCursorTo(0, 8); - - editor.navigateWordLeft(); - assertPosition(0, 5, editor.getCursorPosition()); - }, - - "test: select word right and select" : function() { - var doc = new ace.TextDocument("Juhu Kinners"); - var editor = new ace.Editor(new MockRenderer(), doc); - - editor.moveCursorTo(0, 0); - editor.selectWordRight(); - - var selection = editor.getSelectionRange(); - - assertPosition(0, 0, selection.start); - assertPosition(0, 4, selection.end); - }, - - "test: select word left and select" : function() { - var doc = new ace.TextDocument("Juhu Kinners"); - var editor = new ace.Editor(new MockRenderer(), doc); - - editor.moveCursorTo(0, 3); - editor.selectWordLeft(); - - var selection = editor.getSelectionRange(); - - assertPosition(0, 0, selection.start); - assertPosition(0, 3, selection.end); - }, - "test: goto hidden line should scroll the line into the middle of the viewport" : function() { var editor = new ace.Editor(new MockRenderer(), this.createTextDocument(200, 5)); editor.navigateTo(0, 0); editor.gotoLine(100); - assertPosition(100, 0, editor.getCursorPosition()); + assertPosition(100, 0, editor.getSelection().getCursor()); assertEquals(90, editor.getFirstVisibleRow()); editor.navigateTo(100, 0); editor.gotoLine(10); - assertPosition(10, 0, editor.getCursorPosition()); + assertPosition(10, 0, editor.getSelection().getCursor()); assertEquals(0, editor.getFirstVisibleRow()); editor.navigateTo(100, 0); editor.gotoLine(5); - assertPosition(5, 0, editor.getCursorPosition()); + assertPosition(5, 0, editor.getSelection().getCursor()); assertEquals(0, editor.getFirstVisibleRow()); editor.navigateTo(100, 0); editor.gotoLine(0); - assertPosition(0, 0, editor.getCursorPosition()); + assertPosition(0, 0, editor.getSelection().getCursor()); assertEquals(0, editor.getFirstVisibleRow()); editor.navigateTo(0, 0); editor.gotoLine(190); - assertPosition(190, 0, editor.getCursorPosition()); + assertPosition(190, 0, editor.getSelection().getCursor()); assertEquals(180, editor.getFirstVisibleRow()); editor.navigateTo(0, 0); editor.gotoLine(195); - assertPosition(195, 0, editor.getCursorPosition()); + assertPosition(195, 0, editor.getSelection().getCursor()); assertEquals(180, editor.getFirstVisibleRow()); }, @@ -235,12 +66,12 @@ var NavigationTest = TestCase("NavigationTest", editor.navigateTo(0, 0); editor.gotoLine(11); - assertPosition(11, 0, editor.getCursorPosition()); + assertPosition(11, 0, editor.getSelection().getCursor()); assertEquals(0, editor.getFirstVisibleRow()); editor.navigateTo(30, 0); editor.gotoLine(32); - assertPosition(32, 0, editor.getCursorPosition()); + assertPosition(32, 0, editor.getSelection().getCursor()); assertEquals(30, editor.getFirstVisibleRow()); } }); \ No newline at end of file diff --git a/test/SelectionTest.js b/test/SelectionTest.js new file mode 100644 index 00000000..a42278a6 --- /dev/null +++ b/test/SelectionTest.js @@ -0,0 +1,177 @@ +var SelectionTest = TestCase("SelectionTest", +{ + createTextDocument : function(rows, cols) { + var line = new Array(cols + 1).join("a"); + var text = new Array(rows).join(line + "\n") + line; + return new ace.TextDocument(text); + }, + + "test: move cursor to end of file should place the cursor on last row and column" : function() { + var doc = this.createTextDocument(200, 10); + var selection = doc.getSelection(); + + selection.moveCursorFileEnd(); + assertPosition(199, 10, selection.getCursor()); + }, + + "test: moveCursor to start of file should place the cursor on the first row and column" : function() { + var doc = this.createTextDocument(200, 10); + var selection = doc.getSelection(); + + selection.moveCursorFileStart(); + assertPosition(0, 0, selection.getCursor()); + }, + + "test: move selection lead to end of file" : function() { + var doc = this.createTextDocument(200, 10); + var selection = doc.getSelection(); + + selection.moveCursorTo(100, 5); + selection.selectFileEnd(); + + var range = selection.getRange(); + + assertPosition(100, 5, range.start); + assertPosition(199, 10, range.end); + }, + + "test: move selection lead to start of file" : function() { + var doc = this.createTextDocument(200, 10); + var selection = doc.getSelection(); + + selection.moveCursorTo(100, 5); + selection.selectFileStart(); + + var range = selection.getRange(); + + assertPosition(0, 0, range.start); + assertPosition(100, 5, range.end); + }, + + "test: move cursor word right" : function() { + var doc = new ace.TextDocument( ["ab", + " Juhu Kinners (abc, 12)", " cde"].join("\n")); + var selection = doc.getSelection(); + + selection.moveCursorDown(); + assertPosition(1, 0, selection.getCursor()); + + selection.moveCursorWordRight(); + assertPosition(1, 1, selection.getCursor()); + + selection.moveCursorWordRight(); + assertPosition(1, 5, selection.getCursor()); + + selection.moveCursorWordRight(); + assertPosition(1, 6, selection.getCursor()); + + selection.moveCursorWordRight(); + assertPosition(1, 13, selection.getCursor()); + + selection.moveCursorWordRight(); + assertPosition(1, 15, selection.getCursor()); + + selection.moveCursorWordRight(); + assertPosition(1, 18, selection.getCursor()); + + selection.moveCursorWordRight(); + assertPosition(1, 20, selection.getCursor()); + + selection.moveCursorWordRight(); + assertPosition(1, 22, selection.getCursor()); + + selection.moveCursorWordRight(); + assertPosition(1, 23, selection.getCursor()); + + // wrap line + selection.moveCursorWordRight(); + assertPosition(2, 0, selection.getCursor()); + }, + + "test: select word right if cursor in word" : function() { + var doc = new ace.TextDocument("Juhu Kinners"); + var selection = doc.getSelection(); + + selection.moveCursorTo(0, 2); + selection.moveCursorWordRight(); + + assertPosition(0, 4, selection.getCursor()); + }, + + "test: moveCursor word left" : function() { + var doc = new ace.TextDocument( ["ab", + " Juhu Kinners (abc, 12)", " cde"].join("\n")); + var selection = doc.getSelection(); + + selection.moveCursorDown(); + selection.moveCursorLineEnd(); + assertPosition(1, 23, selection.getCursor()); + + selection.moveCursorWordLeft(); + assertPosition(1, 22, selection.getCursor()); + + selection.moveCursorWordLeft(); + assertPosition(1, 20, selection.getCursor()); + + selection.moveCursorWordLeft(); + assertPosition(1, 18, selection.getCursor()); + + selection.moveCursorWordLeft(); + assertPosition(1, 15, selection.getCursor()); + + selection.moveCursorWordLeft(); + assertPosition(1, 13, selection.getCursor()); + + selection.moveCursorWordLeft(); + assertPosition(1, 6, selection.getCursor()); + + selection.moveCursorWordLeft(); + assertPosition(1, 5, selection.getCursor()); + + selection.moveCursorWordLeft(); + assertPosition(1, 1, selection.getCursor()); + + selection.moveCursorWordLeft(); + assertPosition(1, 0, selection.getCursor()); + + // wrap line + selection.moveCursorWordLeft(); + assertPosition(0, 2, selection.getCursor()); + }, + + "test: select word left if cursor in word" : function() { + var doc = new ace.TextDocument("Juhu Kinners"); + var selection = doc.getSelection(); + + selection.moveCursorTo(0, 8); + + selection.moveCursorWordLeft(); + assertPosition(0, 5, selection.getCursor()); + }, + + "test: select word right and select" : function() { + var doc = new ace.TextDocument("Juhu Kinners"); + var selection = doc.getSelection(); + + selection.moveCursorTo(0, 0); + selection.selectWordRight(); + + var range = selection.getRange(); + + assertPosition(0, 0, range.start); + assertPosition(0, 4, range.end); + }, + + "test: select word left and select" : function() { + var doc = new ace.TextDocument("Juhu Kinners"); + var selection = doc.getSelection(); + + selection.moveCursorTo(0, 3); + selection.selectWordLeft(); + + var range = selection.getRange(); + + assertPosition(0, 0, range.start); + assertPosition(0, 3, range.end); + } +}); \ No newline at end of file diff --git a/test/TextEditTest.js b/test/TextEditTest.js index 35aa7a8a..32af630a 100644 --- a/test/TextEditTest.js +++ b/test/TextEditTest.js @@ -37,7 +37,7 @@ var TextEditTest = TestCase("TextEditTest", var editor = new ace.Editor(new MockRenderer(), doc); editor.moveCursorTo(1, 3); - editor.selectDown(); + editor.getSelection().selectDown(); editor.blockIndent(" "); @@ -46,9 +46,9 @@ var TextEditTest = TestCase("TextEditTest", assertPosition(2, 7, editor.getCursorPosition()); - var selection = editor.getSelectionRange(); - assertPosition(1, 7, selection.start); - assertPosition(2, 7, selection.end); + var range = editor.getSelectionRange(); + assertPosition(1, 7, range.start); + assertPosition(2, 7, range.end); }, "test: outdent block" : function() { @@ -56,8 +56,8 @@ var TextEditTest = TestCase("TextEditTest", var editor = new ace.Editor(new MockRenderer(), doc); editor.moveCursorTo(0, 3); - editor.selectDown(); - editor.selectDown(); + editor.getSelection().selectDown(); + editor.getSelection().selectDown(); editor.blockOutdent(" "); assertEquals([" a12345", "b12345", " c12345"].join("\n"), @@ -65,18 +65,18 @@ var TextEditTest = TestCase("TextEditTest", assertPosition(2, 1, editor.getCursorPosition()); - var selection = editor.getSelectionRange(); - assertPosition(0, 1, selection.start); - assertPosition(2, 1, selection.end); + var range = editor.getSelectionRange(); + assertPosition(0, 1, range.start); + assertPosition(2, 1, range.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); + var range = editor.getSelectionRange(); + assertPosition(0, 1, range.start); + assertPosition(2, 1, range.end); }, "test: outent without a selection should update cursor" : function() { @@ -95,7 +95,7 @@ var TextEditTest = TestCase("TextEditTest", var editor = new ace.Editor(new MockRenderer(), doc, new ace.mode.JavaScript()); editor.moveCursorTo(0, 2); - editor.selectDown(); + editor.getSelection().selectDown(); editor.toggleCommentLines(); @@ -111,9 +111,9 @@ var TextEditTest = TestCase("TextEditTest", var editor = new ace.Editor(new MockRenderer(), doc, new ace.mode.JavaScript()); editor.moveCursorTo(0, 1); - editor.selectDown(); - editor.selectRight(); - editor.selectRight(); + editor.getSelection().selectDown(); + editor.getSelection().selectRight(); + editor.getSelection().selectRight(); editor.toggleCommentLines(); @@ -129,26 +129,26 @@ var TextEditTest = TestCase("TextEditTest", var editor = new ace.Editor(new MockRenderer(), doc); editor.moveCursorTo(0, 1); - editor.selectDown(); + editor.getSelection().selectDown(); editor.moveLinesDown(); assertEquals(["33", "11", "22", "44"].join("\n"), doc.toString()); assertPosition(1, 0, editor.getCursorPosition()); - assertPosition(3, 0, editor.getSelectionAnchor()); - assertPosition(1, 0, editor.getSelectionLead()); + assertPosition(3, 0, editor.getSelection().getSelectionAnchor()); + assertPosition(1, 0, editor.getSelection().getSelectionLead()); editor.moveLinesDown(); assertEquals(["33", "44", "11", "22"].join("\n"), doc.toString()); assertPosition(2, 0, editor.getCursorPosition()); - assertPosition(3, 2, editor.getSelectionAnchor()); - assertPosition(2, 0, editor.getSelectionLead()); + assertPosition(3, 2, editor.getSelection().getSelectionAnchor()); + assertPosition(2, 0, editor.getSelection().getSelectionLead()); // moving again should have no effect editor.moveLinesDown(); assertEquals(["33", "44", "11", "22"].join("\n"), doc.toString()); assertPosition(2, 0, editor.getCursorPosition()); - assertPosition(3, 2, editor.getSelectionAnchor()); - assertPosition(2, 0, editor.getSelectionLead()); + assertPosition(3, 2, editor.getSelection().getSelectionAnchor()); + assertPosition(2, 0, editor.getSelection().getSelectionLead()); }, "test: move lines up should select moved lines" : function() { @@ -156,19 +156,19 @@ var TextEditTest = TestCase("TextEditTest", var editor = new ace.Editor(new MockRenderer(), doc); editor.moveCursorTo(2, 1); - editor.selectDown(); + editor.getSelection().selectDown(); editor.moveLinesUp(); assertEquals(["11", "33", "44", "22"].join("\n"), doc.toString()); assertPosition(1, 0, editor.getCursorPosition()); - assertPosition(3, 0, editor.getSelectionAnchor()); - assertPosition(1, 0, editor.getSelectionLead()); + assertPosition(3, 0, editor.getSelection().getSelectionAnchor()); + assertPosition(1, 0, editor.getSelection().getSelectionLead()); editor.moveLinesUp(); assertEquals(["33", "44", "11", "22"].join("\n"), doc.toString()); assertPosition(0, 0, editor.getCursorPosition()); - assertPosition(2, 0, editor.getSelectionAnchor()); - assertPosition(0, 0, editor.getSelectionLead()); + assertPosition(2, 0, editor.getSelection().getSelectionAnchor()); + assertPosition(0, 0, editor.getSelection().getSelectionLead()); }, "test: move line without active selection should move cursor to start of the moved line" : function() @@ -188,5 +188,30 @@ var TextEditTest = TestCase("TextEditTest", editor.moveLinesUp(); assertEquals(["11", "22", "33", "44"].join("\n"), doc.toString()); assertPosition(1, 0, editor.getCursorPosition()); + }, + + "test: input a tab with soft tab should convert it to spaces" : function() { + var doc = new ace.TextDocument(""); + var editor = new ace.Editor(new MockRenderer(), doc); + + editor.setTabSize(2); + editor.setUseSoftTabs(true); + + editor.onTextInput("\t"); + assertEquals(" ", doc.toString()); + + editor.setTabSize(5); + editor.onTextInput("\t"); + assertEquals(" ", doc.toString()); + }, + + "test: input tab without soft tabs should keep the tab character" : function() { + var doc = new ace.TextDocument(""); + var editor = new ace.Editor(new MockRenderer(), doc); + + editor.setUseSoftTabs(false); + + editor.onTextInput("\t"); + assertEquals("\t", doc.toString()); } }); \ No newline at end of file