add alignCursors command (ctrl+alt+a)

This commit is contained in:
nightwing 2012-08-19 18:21:33 +04:00
commit 4e12d1c9b9
2 changed files with 139 additions and 19 deletions

View file

@ -74,11 +74,15 @@ exports.defaultCommands = [{
}, { }, {
name: "splitIntoLines", name: "splitIntoLines",
exec: function(editor) { editor.multiSelect.splitIntoLines(); }, exec: function(editor) { editor.multiSelect.splitIntoLines(); },
bindKey: {win: "Ctrl-Shift-L", mac: "Ctrl-Shift-L"}, bindKey: {win: "Ctrl-Alt-L", mac: "Ctrl-Alt-L"},
readonly: true readonly: true
}, {
name: "alignCursors",
exec: function(editor) { editor.alignCursors(); },
bindKey: {win: "Ctrl-Alt-A", mac: "Ctrl-Alt-A"}
}]; }];
// commands active in multiselect mode // commands active only in multiselect mode
exports.multiSelectCommands = [{ exports.multiSelectCommands = [{
name: "singleSelection", name: "singleSelection",
bindKey: "esc", bindKey: "esc",

View file

@ -3,7 +3,7 @@
* *
* Copyright (c) 2010, Ajax.org B.V. * Copyright (c) 2010, Ajax.org B.V.
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
@ -14,7 +14,7 @@
* * Neither the name of Ajax.org B.V. nor the * * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * 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 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@ -35,6 +35,7 @@ var Range = require("./range").Range;
var Selection = require("./selection").Selection; var Selection = require("./selection").Selection;
var onMouseDown = require("./mouse/multi_select_handler").onMouseDown; var onMouseDown = require("./mouse/multi_select_handler").onMouseDown;
var event = require("./lib/event"); var event = require("./lib/event");
var lang = require("./lib/lang");
var commands = require("./commands/multi_select_commands"); var commands = require("./commands/multi_select_commands");
exports.commands = commands.defaultCommands.concat(commands.multiSelectCommands); exports.commands = commands.defaultCommands.concat(commands.multiSelectCommands);
@ -66,7 +67,7 @@ var EditSession = require("./edit_session").EditSession;
this.rangeList = null; this.rangeList = null;
/** extension /** extension
* Selection.addRange(range, $blockChangeEvents) * Selection.addRange(range, $blockChangeEvents)
* - range (Range): The new range to add * - range (Range): The new range to add
* - $blockChangeEvents (Boolean): Whether or not to block changing events * - $blockChangeEvents (Boolean): Whether or not to block changing events
* *
@ -113,7 +114,7 @@ var EditSession = require("./edit_session").EditSession;
range && this.fromOrientedRange(range); range && this.fromOrientedRange(range);
}; };
/** extension /** extension
* Selection.substractPoint(pos) -> Range * Selection.substractPoint(pos) -> Range
* - pos (Range): The position to remove, as a `{row, column}` object * - pos (Range): The position to remove, as a `{row, column}` object
@ -129,7 +130,7 @@ var EditSession = require("./edit_session").EditSession;
}; };
/** extension /** extension
* Selection.mergeOverlappingRanges() * Selection.mergeOverlappingRanges()
* *
* Merges overlapping ranges ensuring consistency after changes * Merges overlapping ranges ensuring consistency after changes
**/ **/
@ -241,7 +242,7 @@ var EditSession = require("./edit_session").EditSession;
* - screenCursor (Cursor): The cursor to use * - screenCursor (Cursor): The cursor to use
* - screenAnchor (Anchor): The anchor to use * - screenAnchor (Anchor): The anchor to use
* - includeEmptyLines (Boolean): If true, this includes ranges inside the block which are empty due to clipping * - includeEmptyLines (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 * Gets list of ranges composing rectangular block on the screen
* *
**/ **/
@ -313,9 +314,9 @@ var EditSession = require("./edit_session").EditSession;
// extend Editor // extend Editor
var Editor = require("./editor").Editor; var Editor = require("./editor").Editor;
(function() { (function() {
/** extension /** extension
* Editor.updateSelectionMarkers() * Editor.updateSelectionMarkers()
* *
* Updates the cursor and marker layers. * Updates the cursor and marker layers.
**/ **/
@ -343,7 +344,7 @@ var Editor = require("./editor").Editor;
}; };
/** extension /** extension
* Editor.removeSelectionMarker(range) * Editor.removeSelectionMarker(range)
* - range (Range): The selection range added with [[Editor.addSelectionMarker `addSelectionMarker()`]]. * - range (Range): The selection range added with [[Editor.addSelectionMarker `addSelectionMarker()`]].
* *
* Removes the selection marker. * Removes the selection marker.
@ -431,7 +432,7 @@ var Editor = require("./editor").Editor;
}; };
/** extension /** extension
* Editor.forEachSelection(cmd, args) * Editor.forEachSelection(cmd, args)
* - cmd (String): The command to execute * - cmd (String): The command to execute
* - args (String): Any arguments for the command * - args (String): Any arguments for the command
* *
@ -496,7 +497,7 @@ var Editor = require("./editor").Editor;
// todo this should change when paste becomes a command // todo this should change when paste becomes a command
this.onPaste = function(text) { this.onPaste = function(text) {
if (this.$readOnly) if (this.$readOnly)
return; return;
this._emit("paste", text); this._emit("paste", text);
@ -551,7 +552,7 @@ var Editor = require("./editor").Editor;
// commands // commands
/** extension /** extension
* Editor.selectMoreLines(dir, skip) * Editor.selectMoreLines(dir, skip)
* - dir (Number): The direction of lines to select: -1 for up, 1 for down * - dir (Number): The direction of lines to select: -1 for up, 1 for down
* - skip (Boolean): If `true`, removes the active selection range * - skip (Boolean): If `true`, removes the active selection range
* *
@ -596,10 +597,10 @@ var Editor = require("./editor").Editor;
}; };
/** extension /** extension
* Editor.transposeSelections(dir) * Editor.transposeSelections(dir)
* - dir (Number): The direction to rotate selections * - dir (Number): The direction to rotate selections
* *
* Transposes the selected ranges. * Transposes the selected ranges.
**/ **/
this.transposeSelections = function(dir) { this.transposeSelections = function(dir) {
var session = this.session; var session = this.session;
@ -639,13 +640,13 @@ var Editor = require("./editor").Editor;
} }
/** extension /** extension
* Editor.selectMore(dir, skip) * Editor.selectMore(dir, skip)
* - dir (Number): The direction of lines to select: -1 for up, 1 for down * - dir (Number): The direction of lines to select: -1 for up, 1 for down
* - skip (Boolean): If `true`, removes the active selection range * - 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. * Finds the next occurence of text in an active selection and adds it to the selections.
**/ **/
this.selectMore = function (dir, skip) { this.selectMore = function(dir, skip) {
var session = this.session; var session = this.session;
var sel = session.multiSelect; var sel = session.multiSelect;
@ -665,6 +666,121 @@ var Editor = require("./editor").Editor;
if (skip) if (skip)
this.multiSelect.substractPoint(range.cursor); this.multiSelect.substractPoint(range.cursor);
}; };
/** extension
* Editor.alignCursors()
*
* aligns cursors or selected text
**/
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); }).call(Editor.prototype);
@ -710,7 +826,7 @@ exports.onSessionChange = function(e) {
} }
}; };
// MultiSelect(editor) // MultiSelect(editor)
// adds multiple selection support to the editor // adds multiple selection support to the editor
// (note: should be called only once for each editor instance) // (note: should be called only once for each editor instance)
function MultiSelect(editor) { function MultiSelect(editor) {