diff --git a/src/ace/Editor.js b/src/ace/Editor.js index 97a959db..088eb668 100644 --- a/src/ace/Editor.js +++ b/src/ace/Editor.js @@ -18,8 +18,8 @@ ace.Editor = function(renderer, doc) { var mouseTarget = renderer.getMouseEventTarget(); ace.addListener(mouseTarget, "mousedown", ace.bind(this.onMouseDown, this)); - ace.addListener(mouseTarget, "dblclick", ace.bind(this.onMouseDoubleClick, this)); - ace.addTripleClickListener(mouseTarget, ace.bind(this.onMouseTripleClick, this)); + ace.addMultiMouseDownListener(mouseTarget, 2, ace.bind(this.onMouseDoubleClick, this)); + ace.addMultiMouseDownListener(mouseTarget, 3, ace.bind(this.onMouseTripleClick, this)); ace.addMouseWheelListener(mouseTarget, ace.bind(this.onMouseWheel, this)); this.$selectionMarker = null; @@ -246,7 +246,9 @@ ace.Editor = function(renderer, doc) { pos.row = Math.max(0, Math.min(pos.row, this.doc.getLength()-1)); this.moveCursorToPosition(pos); - this.selection.setSelectionAnchor(pos.row, pos.column); + if (!this.$clickSelection) + this.selection.setSelectionAnchor(pos.row, pos.column); + this.renderer.scrollCursorIntoView(); var self = this; @@ -259,16 +261,33 @@ ace.Editor = function(renderer, doc) { var onMouseSelectionEnd = function() { clearInterval(timerId); + self.$clickSelection = null; }; var onSelectionInterval = function() { if (mousePageX === undefined || mousePageY === undefined) return; - selectionLead = self.renderer.screenToTextCoordinates(mousePageX, mousePageY); - selectionLead.row = Math.max(0, Math.min(selectionLead.row, self.doc.getLength()-1)); + var cursor = self.renderer.screenToTextCoordinates(mousePageX, mousePageY); + cursor.row = Math.max(0, Math.min(cursor.row, self.doc.getLength()-1)); + + if (self.$clickSelection) { + if (self.$clickSelection.contains(cursor.row, cursor.column)) { + self.selection.setSelectionRange(self.$clickSelection); + } else { + if (self.$clickSelection.compare(cursor.row, cursor.column) == -1) { + var anchor = self.$clickSelection.end; + } else { + var anchor = self.$clickSelection.start; + } + self.selection.setSelectionAnchor(anchor.row, anchor.column); + self.selection.selectToPosition(cursor); + } + } + else { + self.selection.selectToPosition(cursor); + } - self.selection.selectToPosition(selectionLead); self.renderer.scrollCursorIntoView(); }; @@ -280,11 +299,13 @@ ace.Editor = function(renderer, doc) { this.onMouseDoubleClick = function(e) { this.selection.selectWord(); + this.$clickSelection = this.getSelectionRange(); this.$updateDesiredColumn(); }; this.onMouseTripleClick = function(e) { this.selection.selectLine(); + this.$clickSelection = this.getSelectionRange(); this.$updateDesiredColumn(); }; diff --git a/src/ace/Range.js b/src/ace/Range.js index 43418cc9..84f6e6c0 100644 --- a/src/ace/Range.js +++ b/src/ace/Range.js @@ -14,35 +14,79 @@ ace.Range = function(startRow, startColumn, endRow, endColumn) { (function() { + this.toString = function() { + return ("Range: [" + this.start.row + "/" + this.start.column + + "] -> [" + this.end.row + "/" + this.end.column + "]"); + }; + + this.contains = function(row, column) { + return this.compare(row, column) == 0; + }; + + this.compare = function(row, column) { + if (!this.isMultiLine()) { + if (row === this.start.row) { + return column < this.start.column ? -1 : (column > this.end.column ? 1 : 0); + }; + } + + if (row < this.start.row) + return -1; + + if (row > this.end.row) + return 1; + + if (this.start.row === row) + return column >= this.start.column ? 0 : -1; + + if (this.end.row === row) + return column <= this.end.column ? 0 : 1; + + return 0; + }; + this.clipRows = function(firstRow, lastRow) { if (this.end.row > lastRow) { - this.end = { + var end = { row: lastRow+1, column: 0 }; } if (this.start.row > lastRow) { - this.start = { + var start = { row: lastRow+1, column: 0 }; } if (this.start.row < firstRow) { - this.start = { + var start = { row: firstRow, column: 0 }; } if (this.end.row < firstRow) { - this.end = { + var end = { row: firstRow, column: 0 }; } - return this; + return ace.Range.fromPoints(start || this.start, end || this.end); + }; + + this.extend = function(row, column) { + var cmp = this.compare(row, column); + + if (cmp == 0) + return this; + else if (cmp == -1) + var start = {row: row, column: column}; + else + var end = {row: row, column: column}; + + return ace.Range.fromPoints(start || this.start, end || this.end); }; this.isEmpty = function() { diff --git a/src/ace/Selection.js b/src/ace/Selection.js index be595f5e..f4ba4be7 100644 --- a/src/ace/Selection.js +++ b/src/ace/Selection.js @@ -33,8 +33,17 @@ ace.Selection = function(doc) { }; this.setSelectionAnchor = function(row, column) { - this.clearSelection(); - this.selectionAnchor = this.$clipPositionToDocument(row, column); + var anchor = this.$clipPositionToDocument(row, column); + + if (!this.selectionAnchor) { + this.selectionAnchor = anchor; + this.$dispatchEvent("changeSelection", {}); + } + else if (this.selectionAnchor.row !== anchor.row || this.selectionAnchor.column !== anchor.column) { + this.selectionAnchor = anchor; + this.$dispatchEvent("changeSelection", {}); + } + }; this.getSelectionAnchor = function() { diff --git a/src/ace/layer/Marker.js b/src/ace/layer/Marker.js index 9d4c76e3..dcac23bc 100644 --- a/src/ace/layer/Marker.js +++ b/src/ace/layer/Marker.js @@ -46,7 +46,7 @@ ace.layer.Marker = function(parentEl) { for ( var key in this.markers) { var marker = this.markers[key]; - var range = marker.range.clone().clipRows(config.firstRow, config.lastRow); + var range = marker.range.clipRows(config.firstRow, config.lastRow); if (range.isEmpty()) continue; if (range.isMultiLine()) { diff --git a/src/ace/lib/event.js b/src/ace/lib/event.js index a8cac740..3648b3aa 100644 --- a/src/ace/lib/event.js +++ b/src/ace/lib/event.js @@ -120,7 +120,7 @@ self.addListener(el, "mousewheel", listener); }; - this.addTripleClickListener = function(el, callback) { + this.addMultiMouseDownListener = function(el, count, callback) { var clicks = 0; var listener = function(e) { clicks += 1; @@ -130,7 +130,7 @@ }, 600); } - if (clicks == 3) { + if (clicks == count) { clicks = 0; callback(e); } diff --git a/src/test/ace/RangeTest.js b/src/test/ace/RangeTest.js index 39eb41fd..5fe98aae 100644 --- a/src/test/ace/RangeTest.js +++ b/src/test/ace/RangeTest.js @@ -19,24 +19,14 @@ RangeTest = new TestCase("RangeTest", { }, "test: clip to rows": function() { - var range = new ace.Range(0, 20, 100, 30); - range.clipRows(10, 30); - - assertPosition(10, 0, range.start); - assertPosition(31, 0, range.end); - - var range = new ace.Range(0, 20, 30, 10); - range.clipRows(10, 30); - - assertPosition(10, 0, range.start); - assertPosition(30, 10, range.end); + assertRange(10, 0, 31, 0, new ace.Range(0, 20, 100, 30).clipRows(10, 30)); + assertRange(10, 0, 30, 10, new ace.Range(0, 20, 30, 10).clipRows(10, 30)); var range = new ace.Range(0, 20, 3, 10); - range.clipRows(10, 30); + var range = range.clipRows(10, 30); assertTrue(range.isEmpty()); - assertPosition(10, 0, range.start); - assertPosition(10, 0, range.end); + assertRange(10, 0, 10, 0, range); }, "test: isEmpty": function() { @@ -67,5 +57,51 @@ RangeTest = new TestCase("RangeTest", { clone.end.column = 20; assertPosition(3, 4, range.end); + }, + + "test: contains for multi line ranges": function() { + var range = new ace.Range(1, 10, 5, 20); + + assertTrue(range.contains(1, 10)); + assertTrue(range.contains(2, 0)); + assertTrue(range.contains(3, 100)); + assertTrue(range.contains(5, 19)); + assertTrue(range.contains(5, 20)); + + assertFalse(range.contains(1, 9)); + assertFalse(range.contains(0, 0)); + assertFalse(range.contains(5, 21)); + }, + + "test: contains for single line ranges": function() { + var range = new ace.Range(1, 10, 1, 20); + + assertTrue(range.contains(1, 10)); + assertTrue(range.contains(1, 15)); + assertTrue(range.contains(1, 20)); + + assertFalse(range.contains(0, 9)); + assertFalse(range.contains(2, 9)); + assertFalse(range.contains(1, 9)); + assertFalse(range.contains(1, 21)); + }, + + "test: extend range": function() { + var range = new ace.Range(2, 10, 2, 30); + + var range = range.extend(2, 5); + assertRange(2, 5, 2, 30, range); + + var range = range.extend(2, 35); + assertRange(2, 5, 2, 35, range); + + var range = range.extend(2, 15); + assertRange(2, 5, 2, 35, range); + + var range = range.extend(1, 4); + assertRange(1, 4, 2, 35, range); + + var range = range.extend(6, 10); + assertRange(1, 4, 6, 10, range); } }); \ No newline at end of file diff --git a/src/test/ace/assertions.js b/src/test/ace/assertions.js index 025d19bc..3e754934 100644 --- a/src/test/ace/assertions.js +++ b/src/test/ace/assertions.js @@ -3,6 +3,11 @@ assertPosition = function(row, column, cursor) { assertEquals(column, cursor.column); }; +assertRange = function(startRow, startColumn, endRow, endColumn, range) { + assertPosition(startRow, startColumn, range.start); + assertPosition(endRow, endColumn, range.end); +}; + assertJsonEquals = function(expectedJson, foundJson) { assertEquals(JSON.stringify(expectedJson), JSON.stringify(foundJson)); }; \ No newline at end of file