lib/gollum/frontend/public/gollum/livepreview/js/ace/lib/ace/multi_select.js in gollum-2.4.4 vs lib/gollum/frontend/public/gollum/livepreview/js/ace/lib/ace/multi_select.js in gollum-2.4.5
- old
+ new
@@ -1,50 +1,43 @@
-/* vim:ts=4:sts=4:sw=4:
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
*
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
*
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Ajax.org B.V. nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
*
- * The Original Code is Ajax.org Code Editor (ACE).
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * The Initial Developer of the Original Code is
- * Ajax.org B.V.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Harutyun Amirjanyan <amirjanyan AT gmail DOT com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
* ***** END LICENSE BLOCK ***** */
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_handler").onMouseDown;
var event = require("./lib/event");
+var lang = require("./lib/lang");
var commands = require("./commands/multi_select_commands");
exports.commands = commands.defaultCommands.concat(commands.multiSelectCommands);
// Todo: session.find or editor.findVolatile that returns range
var Search = require("./search").Search;
@@ -71,16 +64,15 @@
this.ranges = null;
// automatically sorted list of ranges
this.rangeList = null;
- /** extension
- * Selection.addRange(range, $blockChangeEvents)
- * - range (Range): The new range to add
- * - $blockChangeEvents (Boolean): Whether or not to block changing events
- *
+ /**
* Adds a range to a selection by entering multiselect mode, if necessary.
+ * @param {Range} range The new range to add
+ * @param {Boolean} $blockChangeEvents Whether or not to block changing events
+ * @method Selection.addRange
**/
this.addRange = function(range, $blockChangeEvents) {
if (!range)
return;
@@ -111,37 +103,39 @@
}
return $blockChangeEvents || this.fromOrientedRange(range);
};
+ /**
+ * @method Selection.toSingleRange
+ **/
+
this.toSingleRange = function(range) {
range = range || this.ranges[0];
var removed = this.rangeList.removeAll();
if (removed.length)
this.$onRemoveRange(removed);
range && this.fromOrientedRange(range);
};
-
- /** extension
- * Selection.substractPoint(pos) -> Range
- * - pos (Range): The position to remove, as a `{row, column}` object
- *
+
+ /**
* Removes a Range containing pos (if it exists).
+ * @param {Range} pos The position to remove, as a `{row, column}` object
+ * @method Selection.substractPoint
**/
this.substractPoint = function(pos) {
var removed = this.rangeList.substractPoint(pos);
if (removed) {
this.$onRemoveRange(removed);
return removed[0];
}
};
- /** extension
- * Selection.mergeOverlappingRanges()
- *
+ /**
* Merges overlapping ranges ensuring consistency after changes
+ * @method Selection.mergeOverlappingRanges
**/
this.mergeOverlappingRanges = function() {
var removed = this.rangeList.merge();
if (removed.length)
this.$onRemoveRange(removed);
@@ -190,28 +184,47 @@
this.rangeList = new RangeList();
this.ranges = [];
this.rangeCount = 0;
};
+ /**
+ * Returns a concatenation of all the ranges.
+ * @returns Array
+ * @method Selection.getAllRanges
+ **/
this.getAllRanges = function() {
return this.rangeList.ranges.concat();
};
+ /**
+ * Splits all the ranges into lines.
+ * @method Selection.splitIntoLines
+ **/
+
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 range = this.getRange();
+ var isBackwards = this.isBackwards();
var startRow = range.start.row;
var endRow = range.end.row;
- if (startRow == endRow)
+ if (startRow == endRow) {
+ if (isBackwards)
+ var start = range.end, end = range.start;
+ else
+ var start = range.start, end = range.end;
+
+ this.addRange(Range.fromPoints(end, end));
+ this.addRange(Range.fromPoints(start, start));
return;
+ }
var rectSel = [];
var r = this.getLineRange(startRow, true);
r.start.column = range.start.column;
rectSel.push(r);
@@ -225,10 +238,13 @@
rectSel.forEach(this.addRange, this);
}
};
+ /**
+ * @method Selection.toggleBlockSelection
+ **/
this.toggleBlockSelection = 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);
@@ -242,19 +258,20 @@
var rectSel = this.rectangularRangeBlock(cursor, anchor);
rectSel.forEach(this.addRange, this);
}
};
- /** extension
- * Selection.rectangularRangeBlock(screenCursor, screenAnchor, includeEmptyLines) -> Range
- * - screenCursor (Cursor): The cursor to use
- * - screenAnchor (Anchor): The anchor to use
- * - includeEmptyLins (Boolean): If true, this includes ranges inside the block which are empty due to clipping
- *
+ /**
+ *
* Gets list of ranges composing rectangular block on the screen
- *
- */
+ *
+ * @param {Cursor} screenCursor The cursor to use
+ * @param {Anchor} screenAnchor The anchor to use
+ * @param {Boolean} includeEmptyLines If true, this includes ranges inside the block which are empty due to clipping
+ * @returns Range
+ * @method Selection.rectangularRangeBlock
+ **/
this.rectangularRangeBlock = function(screenCursor, screenAnchor, includeEmptyLines) {
var rectSel = [];
var xBackwards = screenCursor.column < screenAnchor.column;
if (xBackwards) {
@@ -319,26 +336,27 @@
}).call(Selection.prototype);
// extend Editor
var Editor = require("./editor").Editor;
(function() {
-
- /** extension
- * Editor.updateSelectionMarkers()
- *
+
+ /**
+ *
* Updates the cursor and marker layers.
+ * @method Editor.updateSelectionMarkers
+ *
**/
this.updateSelectionMarkers = function() {
this.renderer.updateCursor();
this.renderer.updateBackMarkers();
};
- /** extension
- * Editor.addSelectionMarker(orientedRange) -> Range
- * - orientedRange (Range): A range containing a cursor
- *
+ /**
* Adds the selection and cursor.
+ * @param {Range} orientedRange A range containing a cursor
+ * @returns Range
+ * @method Editor.addSelectionMarker
**/
this.addSelectionMarker = function(orientedRange) {
if (!orientedRange.cursor)
orientedRange.cursor = orientedRange.end;
@@ -348,15 +366,14 @@
this.session.$selectionMarkers.push(orientedRange);
this.session.selectionMarkerCount = this.session.$selectionMarkers.length;
return orientedRange;
};
- /** extension
- * Editor.removeSelectionMarker(range)
- * - range (Range): The selection range added with [[Editor.addSelectionMarker `addSelectionMarker()`]].
- *
+ /**
* Removes the selection marker.
+ * @param {Range} The selection range added with [[Editor.addSelectionMarker `addSelectionMarker()`]].
+ * @method Editor.removeSelectionMarker
**/
this.removeSelectionMarker = function(range) {
if (!range.marker)
return;
this.session.removeMarker(range.marker);
@@ -395,11 +412,11 @@
this.$onMultiSelect = function(e) {
if (this.inMultiSelectMode)
return;
this.inMultiSelectMode = true;
- this.setStyle("multiselect");
+ this.setStyle("ace_multiselect");
this.keyBinding.addKeyboardHandler(commands.keyboardHandler);
this.commands.on("exec", this.$onMultiSelectExec);
this.renderer.updateCursor();
this.renderer.updateBackMarkers();
@@ -408,11 +425,11 @@
this.$onSingleSelect = function(e) {
if (this.session.multiSelect.inVirtualMode)
return;
this.inMultiSelectMode = false;
- this.unsetStyle("multiselect");
+ this.unsetStyle("ace_multiselect");
this.keyBinding.removeKeyboardHandler(commands.keyboardHandler);
this.commands.removeEventListener("exec", this.$onMultiSelectExec);
this.renderer.updateCursor();
this.renderer.updateBackMarkers();
@@ -436,17 +453,16 @@
command.multiSelectAction(editor, e.args || {});
}
e.preventDefault();
};
- /** extension
- * Editor.forEachSelection(cmd, args)
- * - cmd (String): The command to execute
- * - args (String): Any arguments for the command
- *
+ /**
* Executes a command for each selection range.
- **/
+ * @param {String} cmd The command to execute
+ * @param {String} args Any arguments for the command
+ * @method Editor.forEachSelection
+ **/
this.forEachSelection = function(cmd, args) {
if (this.inVirtualSelectionMode)
return;
var session = this.session;
@@ -473,14 +489,13 @@
this.onCursorChange();
this.onSelectionChange();
};
- /** extension
- * Editor.exitMultiSelectMode() -> Void
- *
+ /**
* Removes all the selections except the last added one.
+ * @method Editor.exitMultiSelectMode
**/
this.exitMultiSelectMode = function() {
if (this.inVirtualSelectionMode)
return;
this.multiSelect.toSingleRange();
@@ -500,11 +515,15 @@
}
return text;
};
+ // todo this should change when paste becomes a command
this.onPaste = function(text) {
+ if (this.$readOnly)
+ return;
+
this._emit("paste", text);
if (!this.inMultiSelectMode)
return this.insert(text);
var lines = text.split(/\r\n|\r|\n/);
@@ -520,17 +539,17 @@
this.session.insert(range.start, lines[i]);
}
};
- /** extension
- * Editor.findAll(needle, options, additive) -> Number
- * - needle (String): The text to find
- * - options (Object): The search options
- * - additive (Boolean): keeps
- *
+ /**
* Finds and selects all the occurences of `needle`.
+ * @param {String} The text to find
+ * @param {Object} The search options
+ * @param {Boolean} keeps
+ * @returns Number
+ * @method Editor.findAll
**/
this.findAll = function(needle, options, additive) {
options = options || {};
options.needle = needle || options.needle;
this.$search.set(options);
@@ -551,18 +570,18 @@
this.$blockScrolling -= 1;
return ranges.length;
};
- // commands
- /** extension
- * Editor.selectMoreLines(dir, skip)
- * - dir (Number): The direction of lines to select: -1 for up, 1 for down
- * - skip (Boolean): If `true`, removes the active selection range
- *
+ /**
* Adds a cursor above or below the active cursor.
- **/
+ *
+ * @param {Number} dir The direction of lines to select: -1 for up, 1 for down
+ * @param {Boolean} skip If `true`, removes the active selection range
+ *
+ * @method Editor.selectMoreLines
+ */
this.selectMoreLines = function(dir, skip) {
var range = this.selection.toOrientedRange();
var isBackwards = range.cursor == range.end;
var screenLead = this.session.documentToScreenPosition(range.cursor);
@@ -597,15 +616,14 @@
this.selection.addRange(newRange);
if (toRemove)
this.selection.substractPoint(toRemove);
};
- /** extension
- * Editor.transposeSelections(dir)
- * - dir (Number): The direction to rotate selections
- *
- * Transposes the selected ranges.
+ /**
+ * Transposes the selected ranges.
+ * @param dir {Number} The direction to rotate selections
+ * @method Editor.transposeSelections
**/
this.transposeSelections = function(dir) {
var session = this.session;
var sel = session.multiSelect;
var all = sel.ranges;
@@ -638,20 +656,19 @@
var tmp = range.clone();
session.replace(range, words[i]);
range.start.row = tmp.start.row;
range.start.column = tmp.start.column;
}
- }
+ };
- /** extension
- * Editor.selectMore(dir, skip)
- * - dir (Number): The direction of lines to select: -1 for up, 1 for down
- * - skip (Boolean): If `true`, removes the active selection range
- *
+ /**
* Finds the next occurence of text in an active selection and adds it to the selections.
+ * @param {Number} dir The direction of lines to select: -1 for up, 1 for down
+ * @param {Boolean} skip If `true`, removes the active selection range
+ * @method Editor.selectMore
**/
- this.selectMore = function (dir, skip) {
+ this.selectMore = function(dir, skip) {
var session = this.session;
var sel = session.multiSelect;
var range = sel.toOrientedRange();
if (range.isEmpty()) {
@@ -667,10 +684,124 @@
this.multiSelect.addRange(newRange);
}
if (skip)
this.multiSelect.substractPoint(range.cursor);
};
+
+ /**
+ * Aligns the cursors or selected text.
+ * @method Editor.alignCursors
+ **/
+ this.alignCursors = function() {
+ var session = this.session;
+ var sel = session.multiSelect;
+ var ranges = sel.ranges;
+
+ if (!ranges.length) {
+ var range = this.selection.getRange();
+ var fr = range.start.row, lr = range.end.row;
+ var lines = this.session.doc.removeLines(fr, lr);
+ lines = this.$reAlignText(lines);
+ this.session.doc.insertLines(fr, lines);
+ range.start.column = 0;
+ range.end.column = lines[lines.length - 1].length;
+ this.selection.setRange(range);
+ } else {
+ // filter out ranges on same row
+ var row = -1;
+ var sameRowRanges = ranges.filter(function(r) {
+ if (r.cursor.row == row)
+ return true;
+ row = r.cursor.row;
+ });
+ sel.$onRemoveRange(sameRowRanges);
+
+ var maxCol = 0;
+ var minSpace = Infinity;
+ var spaceOffsets = ranges.map(function(r) {
+ var p = r.cursor;
+ var line = session.getLine(p.row);
+ var spaceOffset = line.substr(p.column).search(/\S/g);
+ if (spaceOffset == -1)
+ spaceOffset = 0;
+
+ if (p.column > maxCol)
+ maxCol = p.column;
+ if (spaceOffset < minSpace)
+ minSpace = spaceOffset;
+ return spaceOffset;
+ });
+ ranges.forEach(function(r, i) {
+ var p = r.cursor;
+ var l = maxCol - p.column;
+ var d = spaceOffsets[i] - minSpace;
+ if (l > d)
+ session.insert(p, lang.stringRepeat(" ", l - d));
+ else
+ session.remove(new Range(p.row, p.column, p.row, p.column - l + d));
+
+ r.start.column = r.end.column = maxCol;
+ r.start.row = r.end.row = p.row;
+ r.cursor = r.end;
+ });
+ sel.fromOrientedRange(ranges[0]);
+ this.renderer.updateCursor();
+ this.renderer.updateBackMarkers();
+ }
+ };
+
+ this.$reAlignText = function(lines) {
+ var isLeftAligned = true, isRightAligned = true;
+ var startW, textW, endW;
+
+ return lines.map(function(line) {
+ var m = line.match(/(\s*)(.*?)(\s*)([=:].*)/);
+ if (!m)
+ return [line];
+
+ if (startW == null) {
+ startW = m[1].length;
+ textW = m[2].length;
+ endW = m[3].length;
+ return m;
+ }
+
+ if (startW + textW + endW != m[1].length + m[2].length + m[3].length)
+ isRightAligned = false;
+ if (startW != m[1].length)
+ isLeftAligned = false;
+
+ if (startW > m[1].length)
+ startW = m[1].length;
+ if (textW < m[2].length)
+ textW = m[2].length;
+ if (endW > m[3].length)
+ endW = m[3].length;
+
+ return m;
+ }).map(isLeftAligned ? isRightAligned ? alignRight : alignLeft : unAlign);
+
+ function strRepeat(n, ch) {
+ return Array(n + 1).join(ch)
+ }
+
+ function alignLeft(m) {
+ return !m[2] ? m[0] : strRepeat(startW, " ") + m[2]
+ + strRepeat(textW - m[2].length + endW, " ")
+ + m[4].replace(/^([=:])\s+/, "$1 ")
+ }
+ function alignRight(m) {
+ return !m[2] ? m[0] : strRepeat(startW + textW - m[2].length, " ") + m[2]
+ + strRepeat(endW, " ")
+ + m[4].replace(/^([=:])\s+/, "$1 ")
+ }
+ function unAlign(m) {
+ return !m[2] ? m[0] : strRepeat(startW, " ") + m[2]
+ + strRepeat(endW, " ")
+ + m[4].replace(/^([=:])\s+/, "$1 ")
+ }
+ }
}).call(Editor.prototype);
function isSamePoint(p1, p2) {
return p1.row == p2.row && p1.column == p2.column;
@@ -712,10 +843,10 @@
else
this.$onSingleSelect();
}
};
-// MultiSelect(editor)
+// MultiSelect(editor)
// adds multiple selection support to the editor
// (note: should be called only once for each editor instance)
function MultiSelect(editor) {
editor.$onAddRange = editor.$onAddRange.bind(editor);
editor.$onRemoveRange = editor.$onRemoveRange.bind(editor);