Merge pull request #881 from ajaxorg/align_cursors
add command to align cursors
This commit is contained in:
commit
7966efc0df
2 changed files with 149 additions and 20 deletions
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
**/
|
**/
|
||||||
|
|
@ -198,10 +199,19 @@ var EditSession = require("./edit_session").EditSession;
|
||||||
this.setSelectionRange(range, lastRange.cursor == lastRange.start);
|
this.setSelectionRange(range, lastRange.cursor == lastRange.start);
|
||||||
} else {
|
} else {
|
||||||
var range = this.getRange();
|
var range = this.getRange();
|
||||||
|
var isBackwards = this.isBackwards();
|
||||||
var startRow = range.start.row;
|
var startRow = range.start.row;
|
||||||
var endRow = range.end.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;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var rectSel = [];
|
var rectSel = [];
|
||||||
var r = this.getLineRange(startRow, true);
|
var r = this.getLineRange(startRow, true);
|
||||||
|
|
@ -241,7 +251,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 +323,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 +353,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 +441,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 +506,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 +561,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 +606,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 +649,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 +675,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 +835,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) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue