diff --git a/lib/ace/keyboard/vim2.js b/lib/ace/keyboard/vim2.js index c0f780ac..4b709888 100644 --- a/lib/ace/keyboard/vim2.js +++ b/lib/ace/keyboard/vim2.js @@ -1161,14 +1161,19 @@ dom.importCssString(".normal-mode .ace_cursor{\ function defineOption(name, defaultValue, type) { if (defaultValue === undefined) { throw Error('defaultValue is required'); } if (!type) { type = 'string'; } - options[name] = { - type: type, - defaultValue: defaultValue - }; + var opt = name; + if (typeof name == "string") + opt = { + type: type, + defaultValue: defaultValue + }; + else + name = opt.name; + options[name] = opt; setOption(name, defaultValue); } - function setOption(name, value) { + function setOption(name, value, cm) { var option = options[name]; if (!option) { throw Error('Unknown option: ' + name); @@ -1181,7 +1186,8 @@ dom.importCssString(".normal-mode .ace_cursor{\ value = true; } } - option.value = option.type == 'boolean' ? !!value : value; + option.value = value; + if (option.set) option.set(value, cm); } function getOption(name) { @@ -2564,6 +2570,9 @@ dom.importCssString(".normal-mode .ace_cursor{\ } else if (character === 'w') { tmp = expandWordUnderCursor(cm, inclusive, true /** forward */, false /** bigWord */); + } else if (character === 'p') { + tmp = expandParagraphUnderCursor(cm, inclusive, true /** forward */, + false /** bigWord */); } else { // No text object defined for this, don't move. return null; @@ -4867,7 +4876,7 @@ dom.importCssString(".normal-mode .ace_cursor{\ showConfirm(cm, ' ' + optionName + '=' + oldValue); } } else { - setOption(optionName, value); + setOption(optionName, value, cm); } }, registers: function(cm,params) { @@ -5657,18 +5666,61 @@ dom.importCssString(".normal-mode .ace_cursor{\ if (name.length > 1) { name = '<' + name + '>'; } return name; } - var handleKey = CodeMirror.Vim.handleKey - CodeMirror.Vim.handleKey = function(cm, key, origin) { + var handleKey = Vim.handleKey + Vim.handleKey = function(cm, key, origin) { return cm.operation(function() { return handleKey(cm, key, origin); }, 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 == '' && !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; var getVim = Vim.maybeInitVimState_; exports.handler = { - cm: null, drawCursor: function(style, pixelPos, config, sel, session) { - var vim = this.cm.state.vim; + var vim = this.state.vim || {}; var w = config.characterWidth; var h = config.lineHeight; var top = pixelPos.top; @@ -5698,7 +5750,8 @@ dom.importCssString(".normal-mode .ace_cursor{\ var name = lookupKey(hashId, key, e || {}); if (vim.status == null) 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) vim.status += name; else if (vim.status == null) @@ -5713,25 +5766,27 @@ dom.importCssString(".normal-mode .ace_cursor{\ if (!editor.state) editor.state = {}; var cm = new CodeMirror(editor); editor.state.cm = cm; - editor.$vimModeHandler = Object.create(this); - editor.$vimModeHandler.cm = editor.state.cm; - var vim = CodeMirror.Vim.maybeInitVimState_(cm); + editor.$vimModeHandler = this; CodeMirror.keyMap.vim.attach(cm); - vim.status = null; + getVim(cm).status = null; cm.on('vim-command-done', function() { - vim.status = null; + if (cm.virtualSelectionMode()) return; + getVim(cm).status = null; cm.ace._signal("changeStatus"); + cm.ace.session.markUndoGroup(); }); cm.on("changeStatus", function() { cm.ace.renderer.updateCursor(); cm.ace._signal("changeStatus"); }); 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.ace.renderer.setStyle("normal-mode", !vim.insertMode); - editor.renderer.$cursorLayer.drawCursor = this.drawCursor.bind(editor.$vimModeHandler); + cm.ace.renderer.setStyle("normal-mode", !getVim(cm).insertMode); + editor.renderer.$cursorLayer.drawCursor = this.drawCursor.bind(cm); + renderVirtualNumbers.attach(editor); }, detach: function(editor) { var cm = editor.state.cm; @@ -5741,6 +5796,7 @@ dom.importCssString(".normal-mode .ace_cursor{\ editor.$vimModeHandler = null; editor.renderer.$cursorLayer.drawCursor = null; editor.renderer.setStyle("normal-mode", false); + renderVirtualNumbers.detach(editor); }, getStatusText: function(editor) { var cm = editor.state.cm; @@ -5760,4 +5816,68 @@ dom.importCssString(".normal-mode .ace_cursor{\ 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: '', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorAbove" } }, + { keys: '', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorBelow" } }, + { keys: '', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorAboveSkipCurrent" } }, + { keys: '', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorBelowSkipCurrent" } }, + { keys: '', type: 'action', action: 'aceCommand', actionArgs: { name: "selectMoreBefore" } }, + { keys: '', type: 'action', action: 'aceCommand', actionArgs: { name: "selectMoreAfter" } }, + { keys: '', type: 'action', action: 'aceCommand', actionArgs: { name: "selectNextBefore" } }, + { keys: '', 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)]); + } });