add support for multiple selections

This commit is contained in:
nightwing 2014-11-04 17:43:57 +04:00
commit 068c950517

View file

@ -1161,14 +1161,19 @@ dom.importCssString(".normal-mode .ace_cursor{\
function defineOption(name, defaultValue, type) { function defineOption(name, defaultValue, type) {
if (defaultValue === undefined) { throw Error('defaultValue is required'); } if (defaultValue === undefined) { throw Error('defaultValue is required'); }
if (!type) { type = 'string'; } if (!type) { type = 'string'; }
options[name] = { var opt = name;
type: type, if (typeof name == "string")
defaultValue: defaultValue opt = {
}; type: type,
defaultValue: defaultValue
};
else
name = opt.name;
options[name] = opt;
setOption(name, defaultValue); setOption(name, defaultValue);
} }
function setOption(name, value) { function setOption(name, value, cm) {
var option = options[name]; var option = options[name];
if (!option) { if (!option) {
throw Error('Unknown option: ' + name); throw Error('Unknown option: ' + name);
@ -1181,7 +1186,8 @@ dom.importCssString(".normal-mode .ace_cursor{\
value = true; value = true;
} }
} }
option.value = option.type == 'boolean' ? !!value : value; option.value = value;
if (option.set) option.set(value, cm);
} }
function getOption(name) { function getOption(name) {
@ -2564,6 +2570,9 @@ dom.importCssString(".normal-mode .ace_cursor{\
} else if (character === 'w') { } else if (character === 'w') {
tmp = expandWordUnderCursor(cm, inclusive, true /** forward */, tmp = expandWordUnderCursor(cm, inclusive, true /** forward */,
false /** bigWord */); false /** bigWord */);
} else if (character === 'p') {
tmp = expandParagraphUnderCursor(cm, inclusive, true /** forward */,
false /** bigWord */);
} else { } else {
// No text object defined for this, don't move. // No text object defined for this, don't move.
return null; return null;
@ -4867,7 +4876,7 @@ dom.importCssString(".normal-mode .ace_cursor{\
showConfirm(cm, ' ' + optionName + '=' + oldValue); showConfirm(cm, ' ' + optionName + '=' + oldValue);
} }
} else { } else {
setOption(optionName, value); setOption(optionName, value, cm);
} }
}, },
registers: function(cm,params) { registers: function(cm,params) {
@ -5657,18 +5666,61 @@ dom.importCssString(".normal-mode .ace_cursor{\
if (name.length > 1) { name = '<' + name + '>'; } if (name.length > 1) { name = '<' + name + '>'; }
return name; return name;
} }
var handleKey = CodeMirror.Vim.handleKey var handleKey = Vim.handleKey
CodeMirror.Vim.handleKey = function(cm, key, origin) { Vim.handleKey = function(cm, key, origin) {
return cm.operation(function() { return cm.operation(function() {
return handleKey(cm, key, origin); return handleKey(cm, key, origin);
}, true); }, true);
}
function cloneVimState(state) {
var n = new state.constructor();
Object.keys(state).forEach(function(key) {
var o = state[key];
if (Array.isArray(o))
o = o.slice();
else if (o && typeof o == "object" && o.constructor != Object)
o = cloneVimState(o);
n[key] = o;
});
return n;
}
function multiSelectHandleKey(cm, key, origin) {
var isHandled = false;
var vim = Vim.maybeInitVimState_(cm);
var visualBlock = vim.visualBlock || vim.wasInVisualBlock;
if (vim.wasInVisualBlock && !cm.ace.inMultiSelectMode) {
vim.wasInVisualBlock = false;
} else if (cm.ace.inMultiSelectMode && vim.visualBlock) {
vim.wasInVisualBlock = true;
}
if (key == '<Esc>' && !vim.insertMode && !vim.visualMode && cm.ace.inMultiSelectMode) {
cm.ace.exitMultiSelectMode();
} else if (visualBlock || !cm.ace.inMultiSelectMode || cm.ace.inVirtualSelectionMode) {
isHandled = Vim.handleKey(cm, key, origin);
} else {
var old = cloneVimState(vim);
cm.operation(function() {
cm.ace.forEachSelection(function() {
var sel = cm.ace.selection;
cm.state.vim.lastHPos = sel.$desiredColumn == null ? sel.lead.column : sel.$desiredColumn;
isHandled = handleKey(cm, key, origin);
sel.$desiredColumn = cm.state.vim.lastHPos == -1 ? null : cm.state.vim.lastHPos;
if (cm.virtualSelectionMode()) {
cm.state.vim = cloneVimState(old);
}
});
if (cm.curOp.cursorActivity && !isHandled)
cm.curOp.cursorActivity = false;
}, true);
}
return isHandled;
}; };
exports.CodeMirror = CodeMirror; exports.CodeMirror = CodeMirror;
var getVim = Vim.maybeInitVimState_; var getVim = Vim.maybeInitVimState_;
exports.handler = { exports.handler = {
cm: null,
drawCursor: function(style, pixelPos, config, sel, session) { drawCursor: function(style, pixelPos, config, sel, session) {
var vim = this.cm.state.vim; var vim = this.state.vim || {};
var w = config.characterWidth; var w = config.characterWidth;
var h = config.lineHeight; var h = config.lineHeight;
var top = pixelPos.top; var top = pixelPos.top;
@ -5698,7 +5750,8 @@ dom.importCssString(".normal-mode .ace_cursor{\
var name = lookupKey(hashId, key, e || {}); var name = lookupKey(hashId, key, e || {});
if (vim.status == null) if (vim.status == null)
vim.status = ""; vim.status = "";
var isHandled = CodeMirror.Vim.handleKey(cm, name, 'user'); var isHandled = multiSelectHandleKey(cm, name, 'user');
vim = getVim(cm); // may be changed by multiSelectHandleKey
if (isHandled && vim.status != null) if (isHandled && vim.status != null)
vim.status += name; vim.status += name;
else if (vim.status == null) else if (vim.status == null)
@ -5713,25 +5766,27 @@ dom.importCssString(".normal-mode .ace_cursor{\
if (!editor.state) editor.state = {}; if (!editor.state) editor.state = {};
var cm = new CodeMirror(editor); var cm = new CodeMirror(editor);
editor.state.cm = cm; editor.state.cm = cm;
editor.$vimModeHandler = Object.create(this); editor.$vimModeHandler = this;
editor.$vimModeHandler.cm = editor.state.cm;
var vim = CodeMirror.Vim.maybeInitVimState_(cm);
CodeMirror.keyMap.vim.attach(cm); CodeMirror.keyMap.vim.attach(cm);
vim.status = null; getVim(cm).status = null;
cm.on('vim-command-done', function() { cm.on('vim-command-done', function() {
vim.status = null; if (cm.virtualSelectionMode()) return;
getVim(cm).status = null;
cm.ace._signal("changeStatus"); cm.ace._signal("changeStatus");
cm.ace.session.markUndoGroup();
}); });
cm.on("changeStatus", function() { cm.on("changeStatus", function() {
cm.ace.renderer.updateCursor(); cm.ace.renderer.updateCursor();
cm.ace._signal("changeStatus"); cm.ace._signal("changeStatus");
}); });
cm.on("vim-mode-change", function() { cm.on("vim-mode-change", function() {
cm.ace.renderer.setStyle("normal-mode", !vim.insertMode); if (cm.virtualSelectionMode()) return;
cm.ace.renderer.setStyle("normal-mode", !getVim(cm).insertMode);
cm._signal("changeStatus"); cm._signal("changeStatus");
}); });
cm.ace.renderer.setStyle("normal-mode", !vim.insertMode); cm.ace.renderer.setStyle("normal-mode", !getVim(cm).insertMode);
editor.renderer.$cursorLayer.drawCursor = this.drawCursor.bind(editor.$vimModeHandler); editor.renderer.$cursorLayer.drawCursor = this.drawCursor.bind(cm);
renderVirtualNumbers.attach(editor);
}, },
detach: function(editor) { detach: function(editor) {
var cm = editor.state.cm; var cm = editor.state.cm;
@ -5741,6 +5796,7 @@ dom.importCssString(".normal-mode .ace_cursor{\
editor.$vimModeHandler = null; editor.$vimModeHandler = null;
editor.renderer.$cursorLayer.drawCursor = null; editor.renderer.$cursorLayer.drawCursor = null;
editor.renderer.setStyle("normal-mode", false); editor.renderer.setStyle("normal-mode", false);
renderVirtualNumbers.detach(editor);
}, },
getStatusText: function(editor) { getStatusText: function(editor) {
var cm = editor.state.cm; var cm = editor.state.cm;
@ -5760,4 +5816,68 @@ dom.importCssString(".normal-mode .ace_cursor{\
return status; return status;
} }
} }
var renderVirtualNumbers = {
getText: function(session, row) {
return (Math.abs(session.selection.lead.row - row) || (row + 1 + (row < 9? "\xb7" : "" ))) + ""
},
getWidth: function(session, lastLineNumber, config) {
return session.getLength().toString().length * config.characterWidth;
},
update: function(e, editor) {
editor.renderer.$loop.schedule(editor.renderer.CHANGE_GUTTER)
},
attach: function(editor) {
editor.renderer.$gutterLayer.$renderer = this;
editor.on("changeSelection", this.update);
},
detach: function(editor) {
editor.renderer.$gutterLayer.$renderer = null;
editor.off("changeSelection", this.update);
}
};
Vim.defineOption({
name: "wrap",
set: function(value, cm) {
if (cm) {cm.ace.setOption("wrap", value)}
},
type: "boolean"
}, false);
defaultKeymap.push(
{ keys: 'zc', type: 'action', action: 'fold', actionArgs: { open: false } },
{ keys: 'zC', type: 'action', action: 'fold', actionArgs: { open: false, all: true } },
{ keys: 'zo', type: 'action', action: 'fold', actionArgs: { open: true, } },
{ keys: 'zO', type: 'action', action: 'fold', actionArgs: { open: true, all: true } },
{ keys: 'za', type: 'action', action: 'fold', actionArgs: { toggle: true } },
{ keys: 'zA', type: 'action', action: 'fold', actionArgs: { toggle: true, all: true } },
{ keys: 'zf', type: 'action', action: 'fold', actionArgs: { open: true, all: true } },
{ keys: 'zd', type: 'action', action: 'fold', actionArgs: { open: true, all: true } },
{ keys: '<C-A-j>', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorAbove" } },
{ keys: '<C-A-k>', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorBelow" } },
{ keys: '<C-A-S-j>', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorAboveSkipCurrent" } },
{ keys: '<C-A-S-k>', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorBelowSkipCurrent" } },
{ keys: '<C-A-h>', type: 'action', action: 'aceCommand', actionArgs: { name: "selectMoreBefore" } },
{ keys: '<C-A-l>', type: 'action', action: 'aceCommand', actionArgs: { name: "selectMoreAfter" } },
{ keys: '<C-A-S-h>', type: 'action', action: 'aceCommand', actionArgs: { name: "selectNextBefore" } },
{ keys: '<C-A-S-l>', type: 'action', action: 'aceCommand', actionArgs: { name: "selectNextAfter" } }
);
actions.aceCommand = function(cm, actionArgs, vim) {
cm.vimCmd = actionArgs;
if (cm.ace.inVirtualSelectionMode)
cm.ace.on("beforeEndOperation", delayedExecAceCommand);
else
delayedExecAceCommand(null, cm.ace)
};
function delayedExecAceCommand(op, ace) {
ace.off("beforeEndOperation", delayedExecAceCommand);
var cmd = ace.state.cm.vimCmd;
if (cmd) {
ace.execCommand(cmd.name, cmd.args);
}
ace.curOp = ace.prevOp;
}
actions.fold = function(cm, actionArgs, vim) {
cm.ace.execCommand(['toggleFoldWidget', 'toggleFoldWidget', 'foldOther', 'unfoldall'
][(actionArgs.all ? 2 : 0) + (actionArgs.open ? 1 : 0)]);
}
}); });