From 346ae096b3cda42c9c848c31c9c828e555e10e2d Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 3 Apr 2012 00:06:07 +0400 Subject: [PATCH] overuse of events is bad:( --- lib/ace/commands/multi_select_commands.js | 60 +- ...use_handler.js => multi_select_handler.js} | 131 +-- lib/ace/multi_select.js | 785 +++++++++--------- lib/ace/range_list.js | 101 ++- lib/ace/range_list_test.js | 7 +- 5 files changed, 581 insertions(+), 503 deletions(-) rename lib/ace/mouse/{multi_select_mouse_handler.js => multi_select_handler.js} (56%) diff --git a/lib/ace/commands/multi_select_commands.js b/lib/ace/commands/multi_select_commands.js index 3ec6778b..f2627a8f 100644 --- a/lib/ace/commands/multi_select_commands.js +++ b/lib/ace/commands/multi_select_commands.js @@ -40,23 +40,23 @@ define(function(require, exports, module) { // add multiSelectAction annotations to default commands require("./default_commands").commands.forEach(function(command) { - var single = RegExp(["selectall"].join("|"), ""); - var mapOver = RegExp(["backspace", "del", - "golinedown", "golineup", "gotoend", "gotoleft", "gotolineend", "gotolinestart", - "gotoright", "gotostart", "gotowordleft", "gotowordright", - "indent", "insertstring", "inserttext", "jumptomatching", "outdent", - "removetolineend", "removetolinestart", "removewordleft", "removewordright", - "selectdown", "selectleft", "selectlineend", "selectlinestart", "selectright", - "selecttoend", "selecttolineend", "selecttolinestart", "selecttostart", - "selectup", "selectwordleft", "selectwordright", - "splitline", "tolowercase", "touppercase"].join("|"), ""); - - if (single.test(command.name)) - command.multiSelectAction = "single"; - else if (mapOver.test(command.name)) - command.multiSelectAction = "forEach"; - else if (command.name == "transposeletters") - command.multiSelectAction = function(editor) {editor.transposeSelections(1); } + var single = RegExp(["selectall"].join("|"), ""); + var mapOver = RegExp(["backspace", "del", + "golinedown", "golineup", "gotoend", "gotoleft", "gotolineend", "gotolinestart", + "gotoright", "gotostart", "gotowordleft", "gotowordright", + "indent", "insertstring", "inserttext", "jumptomatching", "outdent", + "removetolineend", "removetolinestart", "removewordleft", "removewordright", + "selectdown", "selectleft", "selectlineend", "selectlinestart", "selectright", + "selecttoend", "selecttolineend", "selecttolinestart", "selecttostart", + "selectup", "selectwordleft", "selectwordright", + "splitline", "tolowercase", "touppercase"].join("|"), ""); + + if (single.test(command.name)) + command.multiSelectAction = "single"; + else if (mapOver.test(command.name)) + command.multiSelectAction = "forEach"; + else if (command.name == "transposeletters") + command.multiSelectAction = function(editor) {editor.transposeSelections(1); } }); // commands to enter multiselect mode @@ -64,37 +64,47 @@ exports.defaultCommands = [{ name: "addCursorAbove", exec: function(editor) { editor.selectMoreLines(-1); }, bindKey: {win: "Ctrl-Alt-Up", mac: "Ctrl-Alt-Up"}, - readonly: true + readonly: true }, { name: "addCursorBelow", exec: function(editor) { editor.selectMoreLines(1); }, bindKey: {win: "Ctrl-Alt-Down", mac: "Ctrl-Alt-Down"}, - readonly: true + readonly: true +}, { + name: "addCursorAboveSkipCurrent", + exec: function(editor) { editor.selectMoreLines(-1, true); }, + bindKey: {win: "Ctrl-Alt-Shift-Up", mac: "Ctrl-Alt-Shift-Up"}, + readonly: true +}, { + name: "addCursorBelowSkipCurrent", + exec: function(editor) { editor.selectMoreLines(1, true); }, + bindKey: {win: "Ctrl-Alt-Shift-Down", mac: "Ctrl-Alt-Shift-Down"}, + readonly: true }, { name: "selectMoreBefore", exec: function(editor) { editor.selectMore(-1); }, bindKey: {win: "Ctrl-Alt-Left", mac: "Ctrl-Alt-Left"}, - readonly: true + readonly: true }, { name: "selectMoreAfter", exec: function(editor) { editor.selectMore(1); }, bindKey: {win: "Ctrl-Alt-Right", mac: "Ctrl-Alt-Right"}, - readonly: true + readonly: true }, { name: "selectNextBefore", exec: function(editor) { editor.selectMore(-1, true); }, bindKey: {win: "Ctrl-Alt-Shift-Left", mac: "Ctrl-Alt-Shift-Left"}, - readonly: true + readonly: true }, { name: "selectNextAfter", exec: function(editor) { editor.selectMore(1, true); }, bindKey: {win: "Ctrl-Alt-Shift-Right", mac: "Ctrl-Alt-Shift-Right"}, - readonly: true + readonly: true }, { name: "splitIntoLines", exec: function(editor) { editor.multiSelect.splitIntoLines(); }, bindKey: {win: "Ctrl-Shift-L", mac: "Ctrl-Shift-L"}, - readonly: true + readonly: true }, ]; // commands active in multiselect mode @@ -102,7 +112,7 @@ exports.multiEditCommands = [{ name: "singleSelection", bindKey: "esc", exec: function(editor) { editor.exitMultiSelectMode(); }, - readonly: true + readonly: true }]; var HashHandler = require("../keyboard/hash_handler").HashHandler; diff --git a/lib/ace/mouse/multi_select_mouse_handler.js b/lib/ace/mouse/multi_select_handler.js similarity index 56% rename from lib/ace/mouse/multi_select_mouse_handler.js rename to lib/ace/mouse/multi_select_handler.js index 5d8dcfd5..318d78ae 100644 --- a/lib/ace/mouse/multi_select_mouse_handler.js +++ b/lib/ace/mouse/multi_select_handler.js @@ -50,11 +50,11 @@ function onMouseDown(e) { var ev = e.domEvent; var alt = ev.altKey; var shift = ev.shiftKey; - var ctrl = ev.ctrlKey; + var ctrl = e.getAccelKey(); var button = e.getButton(); if (!ctrl && !alt) { - if (e.editor.selection.rangeCount > 1) { + if (e.editor.inMultiSelectMode) { if (button == 0) { e.editor.exitMultiSelectMode(); } else if (button == 2) { @@ -70,12 +70,71 @@ function onMouseDown(e) { var editor = e.editor; var selection = editor.selection; - var isMultiSelect = selection.rangeCount > 1 + var isMultiSelect = editor.inMultiSelectMode; var pos = e.getDocumentPosition(); - var rangeList = selection.rangeList; var cursor = selection.getCursor() var inSelection = e.inSelection() || (selection.isEmpty() && isSamePoint(pos, cursor)); + + var mouseX = e.pageX, mouseY = e.pageY; + var clickX = mouseX, clickY = mouseY; + var onMouseSelection = function(e) { + mouseX = event.getDocumentX(e); + mouseY = event.getDocumentY(e); + }; + + var blockSelect = function() { + var newCursor = editor.renderer.pixelToScreenCoordinates(mouseX, mouseY); + var cursor = session.screenToDocumentPosition(newCursor.row, newCursor.column); + + if (isSamePoint(screenCursor, newCursor) + && isSamePoint(cursor, selection.selectionLead)) + return; + screenCursor = newCursor; + + editor.selection.moveCursorToPosition(cursor); + editor.selection.clearSelection(); + editor.renderer.scrollCursorIntoView(); + + editor.removeSelectionMarkers(rectSel); + rectSel = selection.rectangularRangeBlock(screenCursor, screenAnchor); + rectSel.forEach(editor.addSelectionMarker, editor); + editor.updateSelectionMarkers() + }; + + var normalSelect = function() { + var clickSelection + if (_self.$clickSelection) { + if (_self.$clickSelection.contains(cursor.row, cursor.column)) { + editor.selection.setSelectionRange(_self.$clickSelection); + } + else { + if (_self.$clickSelection.compare(cursor.row, cursor.column) == -1) { + anchor = _self.$clickSelection.end; + } + else { + anchor = _self.$clickSelection.start; + } + editor.selection.setSelectionAnchor(anchor.row, anchor.column); + editor.selection.selectToPosition(cursor); + } + } + else { + editor.selection.selectToPosition(cursor); + } + + editor.renderer.scrollCursorIntoView(); + }; + + var session = editor.session; + var anchor = selection.getCursor(); + var screenAnchor = editor.renderer.pixelToScreenCoordinates(mouseX, mouseY); + var clippedAnchor = session.documentToScreenPosition(anchor); + var screenCursor = screenAnchor; + + + onSelectionInterval = blockSelect; + if (ctrl && !shift && !alt && button == 0) { if (!isMultiSelect && inSelection) return // dragging @@ -86,23 +145,23 @@ function onMouseDown(e) { if (inSelection) selection.clearSelection(); - var helper = selection.toOrientedRange(); - editor.addSelectionMarker(helper); + var helper = selection.toOrientedRange(); + editor.addSelectionMarker(helper); - var oldRange = rangeList.rangeAtPoint(pos); + var oldRange = selection.rangeList.rangeAtPoint(pos); event.capture(editor.container, function(){}, function() { editor.removeSelectionMarkers([helper]); var tmpSel = selection.toOrientedRange(); if (oldRange && tmpSel.isEmpty() && isSamePoint(oldRange.cursor, tmpSel.cursor)) { - if (selection.rangeCount > 1) { - range = editor.selection.rangeList.substractPoint(tmpSel.cursor); - var range = editor.selection.rangeList.all[0]; - if (range) - editor.selection.addRange(range); - return; - } + if (selection.rangeCount > 1) { + editor.selection.substractPoint(tmpSel.cursor); + var range = editor.selection.ranges[0]; + if (range) + editor.selection.addRange(range); + return; + } } selection.addRange(tmpSel); @@ -111,20 +170,18 @@ function onMouseDown(e) { //e.stop() } else if (!shift && alt && button == 0) { e.stop() - var mouseX = e.pageX, mouseY = e.pageY; - var onMouseSelection = function(e) { - mouseX = event.getDocumentX(e); - mouseY = event.getDocumentY(e); - }; - if (isMultiSelect && !ctrl) { - selection.single(); + + if (isMultiSelect && !ctrl) { + selection.toSingleRange(); + } else if (!isMultiSelect && ctrl) { + selection.addRange(); } - selection.moveCursorToPosition(pos); - selection.clearSelection(); + + selection.moveCursorToPosition(pos); + selection.clearSelection(); var rectSel = []; - var session = editor.session; var onMouseSelectionEnd = function(e) { clearInterval(timerId); @@ -133,34 +190,12 @@ function onMouseDown(e) { selection.addRange(rectSel[i]) }; - var anchor = selection.getCursor(); - var screenAnchor = editor.renderer.pixelToScreenCoordinates(mouseX, mouseY); - var clippedAnchor = session.documentToScreenPosition(anchor); - var screenCursor = screenAnchor; + - var onSelectionInterval = function() { - var newCursor = editor.renderer.pixelToScreenCoordinates(mouseX, mouseY); - var cursor = session.screenToDocumentPosition(newCursor.row, newCursor.column); - - if (isSamePoint(screenCursor, newCursor) - && isSamePoint(cursor, selection.selectionLead)) - return; - screenCursor = newCursor; - editor.selection.moveCursorToPosition(cursor); - editor.selection.clearSelection(); - editor.renderer.scrollCursorIntoView(); - - editor.removeSelectionMarkers(rectSel); - rectSel = selection.rectangularRangeBlock(screenCursor, screenAnchor); - rectSel.forEach(editor.addSelectionMarker, editor); - - editor.renderer.updateCursor(); - editor.renderer.updateBackMarkers(); - }; event.capture(editor.container, onMouseSelection, onMouseSelectionEnd); - var timerId = setInterval(onSelectionInterval, 20); + var timerId = setInterval(function() {onSelectionInterval()}, 20); return e.preventDefault(); } diff --git a/lib/ace/multi_select.js b/lib/ace/multi_select.js index 4890e79c..12d4569b 100644 --- a/lib/ace/multi_select.js +++ b/lib/ace/multi_select.js @@ -41,7 +41,7 @@ define(function(require, exports, module) { var RangeList = require("./range_list").RangeList; var Range = require("./range").Range; var Selection = require("./selection").Selection; -var onMouseDown = require("./mouse/multi_select_mouse_handler").onMouseDown; +var onMouseDown = require("./mouse/multi_select_handler").onMouseDown; exports.commands = require("./commands/multi_select_commands"); // Todo @@ -49,292 +49,330 @@ var Search = require("ace/search").Search var search = new Search function find(session, needle, dir) { - search.$options.wrap = true; - search.$options.needle = needle; - search.$options.backwards = dir == -1; - return search.find(session) + search.$options.wrap = true; + search.$options.needle = needle; + search.$options.backwards = dir == -1; + return search.find(session) } // extend EditSession var EditSession = require("./edit_session").EditSession; ;(function() { - this.getSelectionMarkers = function() { - return this.$selectionMarkers; - }; + this.getSelectionMarkers = function() { + return this.$selectionMarkers; + }; }).call(EditSession.prototype); // extend Selection ;(function() { - this.addRange = function(range) { - if (!range.cursor) - range.cursor = range.end; - - if (this.rangeCount == 0) { - var oldRange = this.toOrientedRange(); - this.rangeList.add(oldRange); - this._emit("addRange", {range: oldRange}); - } + // list of ranges in reverse addition order + this.ranges = null; + // automatically sorted list of ranges + this.rangeList = null; - this.rangeList.add(range); - this.rangeCount = this.rangeList.ranges.length; - - if (this.rangeCount > 1 && !this.inMultiSelectMode) { - this._emit("multiSelect"); - this.inMultiSelectMode = true; - this.$undoSelect = false; - this.rangeList.attach(this.session); - } + /** + * add a range to selection entering multiselect mode if necessary + */ + this.addRange = function(range) { + if (!this.inMultiSelectMode && this.rangeCount == 0) { + var oldRange = this.toOrientedRange(); + if (!range || !range.isEqual(oldRange)) { + this.rangeList.add(oldRange); + this.$onAddRange(oldRange); + } + } - this.fromOrientedRange(range); - if (this.rangeCount >= 1) - this._emit("addRange", {range: range}); - }; + if (!range) + return; - this.single = function(range) { - range = range || this.rangeList.all[0]; - this.rangeList.removeAll(); - range && this.fromOrientedRange(range); - }; + if (!range.cursor) + range.cursor = range.end; - this.$onRemoveRange = function(e) { - this.rangeCount = this.rangeList.ranges.length; - this._emit("removeRange", e); + var removed = this.rangeList.add(range); - if (this.rangeCount <= 1 && this.inMultiSelectMode) { - this.inMultiSelectMode = false; - this._emit("singleSelect"); - this.$undoSelect = true; - this.rangeList.detach(this.session); + this.fromOrientedRange(range); + this.$onAddRange(range); - if (this.rangeCount == 1) - this.single(); - } - }; + if (removed.length) + this.$onRemoveRange(removed); - // adds multicursor support to selection - this.$initRangeList = function() { - if (this.rangeList) - return; + if (this.rangeCount > 0 && !this.inMultiSelectMode) { + this._emit("multiSelect"); + this.inMultiSelectMode = true; + this.session.$undoSelect = false; + this.rangeList.attach(this.session); + } + }; - var rangeList = new RangeList; - // list of ranges in reverse addition order - // rangeList.all[0] is the same as selection.getRange - rangeList.all = []; - rangeList.on("add", function(e) { - rangeList.all.unshift(e.range) - }) - rangeList.on("remove", function(e) { - var ranges = e.ranges - for (var i = ranges.length; i--; ) { - var index = rangeList.all.indexOf(ranges[i]); - rangeList.all.splice(index, 1); - } - }); + this.toSingleRange = function(range) { + range = range || this.ranges[0]; + var removed = this.rangeList.removeAll(); + if (removed.length) + this.$onRemoveRange(removed); - this.rangeList = rangeList; - this.cursor = this.selectionLead; + range && this.fromOrientedRange(range); + }; - this.rangeCount = 1; - this.rangeList.on("remove", this.$onRemoveRange.bind(this)); - }; - this.getAllRanges = function() { - return this.rangeList.ranges.concat() - }; + this.substractPoint = function(pos) { + var removed = this.rangeList.substractPoint(pos); + if (removed) { + this.$onRemoveRange(removed); + return removed[0] + } + }; - this.splitIntoLines = function () { - if (this.rangeCount > 1) { - var ranges = this.rangeList.ranges; - var lastRange = ranges[ranges.length - 1] - var range = Range.fromPoints(ranges[0].start, lastRange.end) + this.mergeOverlappingRanges = function() { + var removed = this.rangeList.merge(); + var lastRange = this.ranges[0]; + if (removed.length) + this.$onRemoveRange(removed); - this.single() - this.setSelectionRange(range, lastRange.cursor == lastRange.start) - } else { - var cursor = this.session.documentToScreenPosition(this.selectionLead); - var anchor = this.session.documentToScreenPosition(this.selectionAnchor); - - var rectSel = this.rectangularRangeBlock(cursor, anchor); - rectSel.forEach(this.addRange, this); - } - }; - - this.rectangularRangeBlock = function(screenCursor, screenAnchor, includeEmptyLines) { - var rectSel = []; + if (lastRange) + this.fromOrientedRange(lastRange); + }; - var xBackwards = screenCursor.column < screenAnchor.column; - if (xBackwards) { - var startColumn = screenCursor.column; - var endColumn = screenAnchor.column; - } else { - var startColumn = screenAnchor.column; - var endColumn = screenCursor.column; - } - - var yBackwards = screenCursor.row < screenAnchor.row; - if (yBackwards) { - var startRow = screenCursor.row; - var endRow = screenAnchor.row; - } else { - var startRow = screenAnchor.row; - var endRow = screenCursor.row; - } - - if (startColumn < 0) - startColumn = 0; - if (startRow < 0) - startRow = 0; + this.$onAddRange = function(range) { + this.rangeCount = this.rangeList.ranges.length; + this.ranges.unshift(range); + this._emit("addRange", {range: range}); + }; - if (startRow == endRow) - includeEmptyLines = true; - - for (var row = startRow; row <= endRow; row++) { - var range = Range.fromPoints( - this.session.screenToDocumentPosition(row, startColumn), - this.session.screenToDocumentPosition(row, endColumn) - ); - if (range.isEmpty()) { - if (docEnd && isSamePoint(range.end, docEnd)) - break; - var docEnd = range.end; - } - range.cursor = xBackwards ? range.start : range.end; - rectSel.push(range); - } - if (yBackwards) - rectSel.reverse(); - - if (!includeEmptyLines) { - var end = rectSel.length - 1; - while (rectSel[end].isEmpty() && end > 0) - end--; - if (end > 0) { - var start = 0; - while (rectSel[start].isEmpty()) - start++; - } - for (var i = end; i >= start; i--) { - if (rectSel[i].isEmpty()) - rectSel.splice(i, 1); - } - } - - return rectSel; - }; + this.$onRemoveRange = function(removed) { + this.rangeCount = this.rangeList.ranges.length; + if (this.rangeCount == 1 && this.inMultiSelectMode) { + removed.push(this.rangeList.ranges.pop()); + this.rangeCount = 0; + } + + for (var i = removed.length; i--; ) { + var index = this.ranges.indexOf(removed[i]); + this.ranges.splice(index, 1); + } + + console.log(this.ranges+'') + this._emit("removeRange", {ranges: removed}); + + if (this.rangeCount == 0 && this.inMultiSelectMode) { + this.inMultiSelectMode = false; + this._emit("singleSelect"); + this.session.$undoSelect = true; + this.rangeList.detach(this.session); + } + }; + + // adds multicursor support to selection + this.$initRangeList = function() { + if (this.rangeList) + return; + + this.rangeList = new RangeList; + this.ranges = []; + this.rangeCount = 0; + }; + + this.getAllRanges = function() { + return this.rangeList.ranges.concat() + }; + + this.splitIntoLines = function () { + if (this.rangeCount > 1) { + var ranges = this.rangeList.ranges; + var lastRange = ranges[ranges.length - 1] + var range = Range.fromPoints(ranges[0].start, lastRange.end) + + this.toSingleRange() + this.setSelectionRange(range, lastRange.cursor == lastRange.start) + } else { + var cursor = this.session.documentToScreenPosition(this.selectionLead); + var anchor = this.session.documentToScreenPosition(this.selectionAnchor); + + var rectSel = this.rectangularRangeBlock(cursor, anchor); + rectSel.forEach(this.addRange, this); + } + }; + + /** + * gets list of ranges composing rectangular block on the screen + * @includeEmptyLines if true includes ranges inside the block which + * are empty becuase of the clipping + */ + this.rectangularRangeBlock = function(screenCursor, screenAnchor, includeEmptyLines) { + var rectSel = []; + + var xBackwards = screenCursor.column < screenAnchor.column; + if (xBackwards) { + var startColumn = screenCursor.column; + var endColumn = screenAnchor.column; + } else { + var startColumn = screenAnchor.column; + var endColumn = screenCursor.column; + } + + var yBackwards = screenCursor.row < screenAnchor.row; + if (yBackwards) { + var startRow = screenCursor.row; + var endRow = screenAnchor.row; + } else { + var startRow = screenAnchor.row; + var endRow = screenCursor.row; + } + + if (startColumn < 0) + startColumn = 0; + if (startRow < 0) + startRow = 0; + + if (startRow == endRow) + includeEmptyLines = true; + + for (var row = startRow; row <= endRow; row++) { + var range = Range.fromPoints( + this.session.screenToDocumentPosition(row, startColumn), + this.session.screenToDocumentPosition(row, endColumn) + ); + if (range.isEmpty()) { + if (docEnd && isSamePoint(range.end, docEnd)) + break; + var docEnd = range.end; + } + range.cursor = xBackwards ? range.start : range.end; + rectSel.push(range); + } + if (yBackwards) + rectSel.reverse(); + + if (!includeEmptyLines) { + var end = rectSel.length - 1; + while (rectSel[end].isEmpty() && end > 0) + end--; + if (end > 0) { + var start = 0; + while (rectSel[start].isEmpty()) + start++; + } + for (var i = end; i >= start; i--) { + if (rectSel[i].isEmpty()) + rectSel.splice(i, 1); + } + } + + return rectSel; + }; }).call(Selection.prototype); // extend Editor var Editor = require("./editor").Editor; ;(function() { - this.addSelectionMarker = function(orientedRange) { - if (!orientedRange.cursor) - orientedRange.cursor = orientedRange.end; + this.updateSelectionMarkers = function(orientedRange) { + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + + this.addSelectionMarker = function(orientedRange) { + if (!orientedRange.cursor) + orientedRange.cursor = orientedRange.end; - var style = this.getSelectionStyle(); - orientedRange.marker = this.session.addMarker(orientedRange, "ace_selection", style); + var style = this.getSelectionStyle(); + orientedRange.marker = this.session.addMarker(orientedRange, "ace_selection", style); - this.session.$selectionMarkers.push(orientedRange); - this.session.selectionMarkerCount = this.session.$selectionMarkers.length; - return orientedRange - }; + this.session.$selectionMarkers.push(orientedRange); + this.session.selectionMarkerCount = this.session.$selectionMarkers.length; + return orientedRange; + }; - this.removeSelectionMarkers = function(ranges) { - for (var i = ranges.length; i--; ) { - var range = ranges[i]; - if (!range.marker) - continue; - this.session.removeMarker(range.marker); - var index = this.session.$selectionMarkers.indexOf(range); - if (index != -1) - this.session.$selectionMarkers.splice(index, 1) - } - this.session.selectionMarkerCount = this.session.$selectionMarkers.length; - }; + this.removeSelectionMarkers = function(ranges) { + for (var i = ranges.length; i--; ) { + var range = ranges[i]; + if (!range.marker) + continue; + this.session.removeMarker(range.marker); + var index = this.session.$selectionMarkers.indexOf(range); + if (index != -1) + this.session.$selectionMarkers.splice(index, 1) + } + this.session.selectionMarkerCount = this.session.$selectionMarkers.length; + }; - this.$onAddRange = function(e) { - this.addSelectionMarker(e.range); - this.renderer.updateCursor(); - this.renderer.updateBackMarkers(); - }; - this.$onRemoveRange = function(e) { - this.removeSelectionMarkers(e.ranges); - this.renderer.updateCursor(); - this.renderer.updateBackMarkers(); - }; - this.$onMultiSelect = function(e) { - if (this.inMultiSelectMode) - return; - this.inMultiSelectMode = true; + this.$onAddRange = function(e) { + this.addSelectionMarker(e.range); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + this.$onRemoveRange = function(e) { + this.removeSelectionMarkers(e.ranges); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + this.$onMultiSelect = function(e) { + if (this.inMultiSelectMode) + return; + this.inMultiSelectMode = true; - this.setStyle("multiselect"); - this.keyBinding.addKeyboardHandler(exports.commands.keyboardHandler); - // FixMe - this.commands.__SingleSelectionExec = this.commands.exec; - this.commands.exec = exports.exec; - this.renderer.updateCursor(); - this.renderer.updateBackMarkers(); - }; + this.setStyle("multiselect"); + this.keyBinding.addKeyboardHandler(exports.commands.keyboardHandler); + // FixMe + this.commands.__SingleSelectionExec = this.commands.exec; + this.commands.exec = exports.exec; + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; - this.$onSingleSelect = function(e) { - if (this.session.multiSelect.inVirtualMode) - return; - this.inMultiSelectMode = false; + this.$onSingleSelect = function(e) { + if (this.session.multiSelect.inVirtualMode) + return; + this.inMultiSelectMode = false; - this.unsetStyle("multiselect"); - this.keyBinding.removeKeyboardHandler(exports.commands.keyboardHandler); + this.unsetStyle("multiselect"); + this.keyBinding.removeKeyboardHandler(exports.commands.keyboardHandler); - this.commands.exec = this.commands.__SingleSelectionExec; - this.renderer.updateCursor(); - this.renderer.updateBackMarkers(); - }; + this.commands.exec = this.commands.__SingleSelectionExec; + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; - this.forEachSelection = function(cmd, args) { - if (this.inVirtualSelectionMode) - return; - var session = this.session - var selection = this.selection - var rangeList = selection.rangeList + this.forEachSelection = function(cmd, args) { + if (this.inVirtualSelectionMode) + return; + var session = this.session + var selection = this.selection + var rangeList = selection.rangeList - var reg = selection._eventRegistry; - selection._eventRegistry = {}; + var reg = selection._eventRegistry; + selection._eventRegistry = {}; - var tmpSel = new Selection(session); - this.inVirtualSelectionMode = true; - for (var i = rangeList.ranges.length; i--;) { - tmpSel.fromOrientedRange(rangeList.ranges[i]); - this.selection = session.selection = tmpSel; - cmd.exec(this, args || {}); - tmpSel.toOrientedRange(rangeList.ranges[i]); - } - tmpSel.detach(); + var tmpSel = new Selection(session); + this.inVirtualSelectionMode = true; + for (var i = rangeList.ranges.length; i--;) { + tmpSel.fromOrientedRange(rangeList.ranges[i]); + this.selection = session.selection = tmpSel; + cmd.exec(this, args || {}); + tmpSel.toOrientedRange(rangeList.ranges[i]); + } + tmpSel.detach(); - this.selection = session.selection = selection; - this.inVirtualSelectionMode = false; - selection._eventRegistry = reg; - rangeList.merge(); + this.selection = session.selection = selection; + this.inVirtualSelectionMode = false; + selection._eventRegistry = reg; + selection.mergeOverlappingRanges(); - var lastRange = selection.rangeList.all[0] - lastRange && selection.fromOrientedRange(lastRange); + this.onCursorChange(); + this.onSelectionChange(); + }; - selection._emit("changeSelection") - selection._emit("changeCursor") - this.renderer.updateCursor(); - this.renderer.updateBackMarkers(); - }; + this.exitMultiSelectMode = function() { + if (this.inVirtualSelectionMode) + return; + this.multiSelect.toSingleRange(); + }; - this.exitMultiSelectMode = function() { - if (this.inVirtualSelectionMode) - return; - this.multiSelect.single(); - }; - - // todo route copy/cut/paste through commandmanager - this.getCopyText = function() { + // todo route copy/cut/paste through commandmanager + this.getCopyText = function() { var text = ""; - if (this.inMultiSelectMode) { - var ranges = this.multiSelect.rangeList.ranges; - for (var i = 0; i < ranges.length; i++) { - text += this.session.getTextRange(ranges[i]); - } + if (this.inMultiSelectMode) { + var ranges = this.multiSelect.rangeList.ranges; + for (var i = 0; i < ranges.length; i++) { + text += this.session.getTextRange(ranges[i]); + } } else if (!this.selection.isEmpty()) text = this.session.getTextRange(this.getSelectionRange()); @@ -342,120 +380,121 @@ var Editor = require("./editor").Editor; return text; }; - this.onCut = function() { + this.onCut = function() { var cmd = { - name: "cut", - exec: function(editor) { - var range = editor.getSelectionRange(); - editor._emit("cut", range); + name: "cut", + exec: function(editor) { + var range = editor.getSelectionRange(); + editor._emit("cut", range); - if (!editor.selection.isEmpty()) { - editor.session.remove(range); - editor.clearSelection(); - } - }, - readonly: true, - multiSelectAction: "forEach" - } - this.commands.exec(cmd, this) + if (!editor.selection.isEmpty()) { + editor.session.remove(range); + editor.clearSelection(); + } + }, + readonly: true, + multiSelectAction: "forEach" + } + this.commands.exec(cmd, this) }; - // commands + // commands - this.selectMoreLines = function(dir, skip) { - var range = this.selection.toOrientedRange(); - var isBackwards = range.cursor == range.end; + this.selectMoreLines = function(dir, skip) { + var range = this.selection.toOrientedRange(); + var isBackwards = range.cursor == range.end; - var screenLead = this.session.documentToScreenPosition(range.cursor); - if (this.selection.$desiredColumn) - screenLead.column = this.selection.$desiredColumn; + var screenLead = this.session.documentToScreenPosition(range.cursor); + if (this.selection.$desiredColumn) + screenLead.column = this.selection.$desiredColumn; - var lead = this.session.screenToDocumentPosition(screenLead.row + dir, screenLead.column); + var lead = this.session.screenToDocumentPosition(screenLead.row + dir, screenLead.column); - if (!range.isEmpty()) { - var screenAnchor = this.session.documentToScreenPosition(isBackwards ? range.end : range.start); - var anchor = this.session.screenToDocumentPosition(screenAnchor.row + dir, screenAnchor.column); - } else { - var anchor = lead; - } + if (!range.isEmpty()) { + var screenAnchor = this.session.documentToScreenPosition(isBackwards ? range.end : range.start); + var anchor = this.session.screenToDocumentPosition(screenAnchor.row + dir, screenAnchor.column); + } else { + var anchor = lead; + } - if (isBackwards) { - var newRange = Range.fromPoints(lead, anchor); - newRange.cursor = newRange.start; - } else { - var newRange = Range.fromPoints(anchor, lead); - newRange.cursor = newRange.end; - } + if (isBackwards) { + var newRange = Range.fromPoints(lead, anchor); + newRange.cursor = newRange.start; + } else { + var newRange = Range.fromPoints(anchor, lead); + newRange.cursor = newRange.end; + } - newRange.desiredColumn = screenLead.column; - if (!this.selection.inMultiSelectMode) { - this.selection.addRange(range); - } else { - var allRanges = this.selection.rangeList.ranges; - // remove range if at end - if (skip || range.isEequal(allRanges[dir == 1 ? 0 : allRanges.length - 1])) - var toRemove = range.cursor; - } + newRange.desiredColumn = screenLead.column; + if (!this.selection.inMultiSelectMode) { + this.selection.addRange(range); + } else { + var allRanges = this.selection.rangeList.ranges; + // remove range if at end + // var atEdge = range.isEqual(allRanges[dir == 1 ? 0 : allRanges.length - 1]); + if (skip) + var toRemove = range.cursor; + } - this.selection.addRange(newRange); - if (toRemove) - this.selection.rangeList.substractPoint(toRemove); - } + this.selection.addRange(newRange); + if (toRemove) + this.selection.substractPoint(toRemove); + } - this.transposeSelections = function(dir) { - var session = this.session; - var sel = session.multiSelect; - var all = sel.rangeList.all; + this.transposeSelections = function(dir) { + var session = this.session; + var sel = session.multiSelect; + var all = sel.ranges; - var words = []; - for (var i = all.length; i--; ) { - var range = all[i] - if (range.isEmpty()) { - var tmp = session.getWordRange(range.start.row, range.start.column) - range.start.row = tmp.start.row; - range.start.column = tmp.start.column; - range.end.row = tmp.end.row; - range.end.column = tmp.end.column; - } + var words = []; + for (var i = all.length; i--; ) { + var range = all[i] + if (range.isEmpty()) { + var tmp = session.getWordRange(range.start.row, range.start.column) + range.start.row = tmp.start.row; + range.start.column = tmp.start.column; + range.end.row = tmp.end.row; + range.end.column = tmp.end.column; + } - words.unshift(this.session.getTextRange(range)); - } - if (dir < 0) - words.unshift(words.pop()); - else - words.push(words.shift()); + words.unshift(this.session.getTextRange(range)); + } + if (dir < 0) + words.unshift(words.pop()); + else + words.push(words.shift()); - for (var i = all.length; i--; ) { - var range = all[i]; - var tmp = range.clone(); - session.replace(range, words[i]); - range.start.row = tmp.start.row; - range.start.column = tmp.start.column; - } - } + for (var i = all.length; i--; ) { + var range = all[i]; + var tmp = range.clone(); + session.replace(range, words[i]); + range.start.row = tmp.start.row; + range.start.column = tmp.start.column; + } + } - this.selectMore = function (dir, skip) { - var session = this.session; - var sel = session.multiSelect; - var all = sel.rangeList.all; + this.selectMore = function (dir, skip) { + var session = this.session; + var sel = session.multiSelect; + var all = sel.ranges; - var range = sel.toOrientedRange(); - if (range.isEmpty()) { - var range = session.getWordRange(range.start.row, range.start.column) - range.cursor = range.end; - this.multiSelect.addRange(range); - } - var needle = session.getTextRange(range); + var range = sel.toOrientedRange(); + if (range.isEmpty()) { + var range = session.getWordRange(range.start.row, range.start.column) + range.cursor = range.end; + this.multiSelect.addRange(range); + } + var needle = session.getTextRange(range); - var newRange = find(session, needle, dir); - if (newRange) { - newRange.cursor = dir == -1 ? newRange.start : newRange.end; - this.multiSelect.addRange(newRange); - } - if (skip) - this.multiSelect.rangeList.substractPoint(range.cursor); - } + var newRange = find(session, needle, dir); + if (newRange) { + newRange.cursor = dir == -1 ? newRange.start : newRange.end; + this.multiSelect.addRange(newRange); + } + if (skip) + this.multiSelect.substractPoint(range.cursor); + } }).call(Editor.prototype); @@ -476,11 +515,11 @@ exports.exec = function(command, editor, args) { } else if (command.multiSelectAction == "forEach") { editor.forEachSelection(command, args); } else if (command.multiSelectAction == "single") { - editor.exitMultiSelectMode(); + editor.exitMultiSelectMode(); command.exec(editor, args || {}); - } else { + } else { command.multiSelectAction(editor, args || {}); - } + } return true; }; @@ -494,45 +533,47 @@ function isSamePoint(p1, p2) { // patch // adds multicursor support to a session exports.onSessionChange = function(e) { - var session = e.session; - if (!session.multiSelect) { - session.$selectionMarkers = []; - session.selection.$initRangeList(); - session.multiSelect = session.selection; - } + var session = e.session; + if (!session.multiSelect) { + session.$selectionMarkers = []; + session.selection.$initRangeList(); + session.multiSelect = session.selection; + } this.multiSelect = session.multiSelect; - var oldSession = e.oldSession; - if (oldSession) { - // todo use events - if (oldSession.multiSelect && oldSession.multiSelect.editor == this) - oldSession.multiSelect.editor = null; + var oldSession = e.oldSession; + if (oldSession) { + // todo use events + if (oldSession.multiSelect && oldSession.multiSelect.editor == this) + oldSession.multiSelect.editor = null; - session.multiSelect.removeEventListener("addRange", this.$onAddRange); - session.multiSelect.removeEventListener("removeRange", this.$onRemoveRange); - session.multiSelect.removeEventListener("multiSelect", this.$onMultiSelect); - session.multiSelect.removeEventListener("singleSelect", this.$onSingleSelect); - } + session.multiSelect.removeEventListener("addRange", this.$onAddRange); + session.multiSelect.removeEventListener("removeRange", this.$onRemoveRange); + session.multiSelect.removeEventListener("multiSelect", this.$onMultiSelect); + session.multiSelect.removeEventListener("singleSelect", this.$onSingleSelect); + } - session.multiSelect.on("addRange", this.$onAddRange); - session.multiSelect.on("removeRange", this.$onRemoveRange); - session.multiSelect.on("multiSelect", this.$onMultiSelect); - session.multiSelect.on("singleSelect", this.$onSingleSelect); - - if (this.inMultiSelectMode != session.selection.inMultiSelectMode) { - if (session.selection.inMultiSelectMode) - this.$onMultiSelect(); - else - this.$onSingleSelect(); - } + session.multiSelect.on("addRange", this.$onAddRange); + session.multiSelect.on("removeRange", this.$onRemoveRange); + session.multiSelect.on("multiSelect", this.$onMultiSelect); + session.multiSelect.on("singleSelect", this.$onSingleSelect); + + // this.$onSelectionChange = this.onSelectionChange.bind(this); + + if (this.inMultiSelectMode != session.selection.inMultiSelectMode) { + if (session.selection.inMultiSelectMode) + this.$onMultiSelect(); + else + this.$onSingleSelect(); + } } // adds multicursor support to editor instance function MultiSelect(editor) { - editor.$onAddRange = editor.$onAddRange.bind(editor); - editor.$onRemoveRange = editor.$onRemoveRange.bind(editor); - editor.$onMultiSelect = editor.$onMultiSelect.bind(editor); - editor.$onSingleSelect = editor.$onSingleSelect.bind(editor); + editor.$onAddRange = editor.$onAddRange.bind(editor); + editor.$onRemoveRange = editor.$onRemoveRange.bind(editor); + editor.$onMultiSelect = editor.$onMultiSelect.bind(editor); + editor.$onSingleSelect = editor.$onSingleSelect.bind(editor); exports.onSessionChange.call(editor, editor); editor.on("changeSession", exports.onSessionChange.bind(editor)); diff --git a/lib/ace/range_list.js b/lib/ace/range_list.js index f30b1756..c3dc2dd0 100644 --- a/lib/ace/range_list.js +++ b/lib/ace/range_list.js @@ -46,8 +46,6 @@ var RangeList = function(startRow, startColumn, endRow, endColumn) { }; (function() { - oop.implement(this, EventEmitter); - this.comparePoints = function(p1, p2) { return p1.row - p2.row || p1.column - p2.column }; @@ -84,25 +82,22 @@ var RangeList = function(startRow, startColumn, endRow, endColumn) { else endIndex++; - var removed = this.ranges.splice(startIndex, endIndex - startIndex, range); - if (removed.length) - this._emit("remove", {ranges: removed}); - this._emit("add", {range: range}); - return startIndex; + return this.ranges.splice(startIndex, endIndex - startIndex, range); }; this.addList = function(list) { - list.forEach(this.add, this); + var removed = []; + for (var i = list.length; i--; ) { + removed.push.call(removed, this.add(list[i])) + } + return removed; }; this.substractPoint = function(pos) { var i = this.pointIndex(pos); - if (i >= 0) { - var removed = this.ranges.splice(i, 1); - this._emit("remove", {ranges: removed}); - return removed[0] - } + if (i >= 0) + return this.ranges.splice(i, 1); }; // merge overlapping ranges @@ -131,8 +126,7 @@ var RangeList = function(startRow, startColumn, endRow, endColumn) { i--; } - if (removed.length) - this._emit("remove", {ranges: removed}); + return removed; }; this.contains = function(row, column) { @@ -170,8 +164,7 @@ var RangeList = function(startRow, startColumn, endRow, endColumn) { }; this.removeAll = function() { - if (this.ranges.length) - this._emit("remove", {ranges: this.ranges.splice(0, this.ranges.length)}); + return this.ranges.splice(0, this.ranges.length); }; this.attach = function(session) { @@ -193,45 +186,45 @@ var RangeList = function(startRow, startColumn, endRow, endColumn) { this.$onChange = function(e) { var changeRange = e.data.range; - if (e.data.action[0] == "i"){ - var start = changeRange.start; - var end = changeRange.end; - } else { - var end = changeRange.start; - var start = changeRange.end; - } - var startRow = start.row; - var endRow = end.row; - var lineDif = endRow - startRow; - - var colDiff = -start.column + end.column; - - var ranges = this.ranges; + if (e.data.action[0] == "i"){ + var start = changeRange.start; + var end = changeRange.end; + } else { + var end = changeRange.start; + var start = changeRange.end; + } + var startRow = start.row; + var endRow = end.row; + var lineDif = endRow - startRow; - for (var i=0, n = ranges.length; i < n; i++) { - var r = ranges[i]; - if (r.end.row < startRow) - continue; - if (r.start.row > startRow) - break; + var colDiff = -start.column + end.column; - if (r.start.row == startRow && r.start.column >= start.column ) { - r.start.column += colDiff; - r.start.row += lineDif; - } - if (r.end.row == startRow && r.end.column >= start.column) { - r.end.column += colDiff; - r.end.row += lineDif; - } - } - - if (lineDif != 0 && i < n) { - for (; i < n; i++) { - var r = ranges[i]; - r.start.row += lineDif; - r.end.row += lineDif; - } - } + var ranges = this.ranges; + + for (var i=0, n = ranges.length; i < n; i++) { + var r = ranges[i]; + if (r.end.row < startRow) + continue; + if (r.start.row > startRow) + break; + + if (r.start.row == startRow && r.start.column >= start.column ) { + r.start.column += colDiff; + r.start.row += lineDif; + } + if (r.end.row == startRow && r.end.column >= start.column) { + r.end.column += colDiff; + r.end.row += lineDif; + } + } + + if (lineDif != 0 && i < n) { + for (; i < n; i++) { + var r = ranges[i]; + r.start.row += lineDif; + r.end.row += lineDif; + } + } }; }).call(RangeList.prototype); diff --git a/lib/ace/range_list_test.js b/lib/ace/range_list_test.js index a73202f5..6c397369 100644 --- a/lib/ace/range_list_test.js +++ b/lib/ace/range_list_test.js @@ -124,25 +124,24 @@ module.exports = { new Range(8,8,9,9) ]); var removed = []; - rangeList.on('remove', function(e) {removed = e.ranges}); assert.equal(rangeList.ranges.length, 4); rangeList.ranges[1].end.row = 7; - rangeList.merge(); + removed = rangeList.merge(); assert.equal(removed.length, 1); assert.range(rangeList.ranges[1], 4,2,7,7); assert.equal(rangeList.ranges.length, 3); rangeList.ranges[0].end.row = 10; - rangeList.merge(); + removed = rangeList.merge(); assert.range(rangeList.ranges[0], 1,2,10,4); assert.equal(removed.length, 2); assert.equal(rangeList.ranges.length, 1); rangeList.ranges.push(new Range(10,10,10,10)); rangeList.ranges.push(new Range(10,10,10,10)); - rangeList.merge(); + removed = rangeList.merge(); assert.equal(rangeList.ranges.length, 2); },