(function (){ 'use strict'; var defaultKeymap = [{ keys: [''] , type: 'keyToKey', toKeys: ['h'] } , { keys: [''] , type: 'keyToKey', toKeys: ['l'] } , { keys: [''] , type: 'keyToKey', toKeys: ['k'] } , { keys: [''] , type: 'keyToKey', toKeys: ['j'] } , { keys: [''] , type: 'keyToKey', toKeys: ['l'] } , { keys: [''] , type: 'keyToKey', toKeys: ['h'] } , { keys: [''] , type: 'keyToKey', toKeys: ['W'] } , { keys: [''] , type: 'keyToKey', toKeys: ['B'] } , { keys: [''] , type: 'keyToKey', toKeys: ['w'] } , { keys: [''] , type: 'keyToKey', toKeys: ['b'] } , { keys: [''] , type: 'keyToKey', toKeys: ['j'] } , { keys: [''] , type: 'keyToKey', toKeys: ['k'] } , { keys: ['C-['] , type: 'keyToKey', toKeys: [''] } , { keys: [''] , type: 'keyToKey', toKeys: [''] } , { keys: ['s'] , type: 'keyToKey', toKeys: ['c', 'l'] , context: 'normal'} , { keys: ['s'] , type: 'keyToKey', toKeys: ['x', 'i'] , context: 'visual'} , { keys: ['S'] , type: 'keyToKey', toKeys: ['c', 'c'] , context: 'normal'} , { keys: ['S'] , type: 'keyToKey', toKeys: ['d', 'c', 'c'] , context: 'visual'} , { keys: [''] , type: 'keyToKey', toKeys: ['0'] } , { keys: [''] , type: 'keyToKey', toKeys: ['$'] } , { keys: [''] , type: 'keyToKey', toKeys: [''] } , { keys: [''] , type: 'keyToKey', toKeys: [''] } , { keys: ['H'] , type: 'motion', motion: 'moveToTopLine', motionArgs: { linewise: true , toJumplist: true } } , { keys: ['M'] , type: 'motion', motion: 'moveToMiddleLine', motionArgs: { linewise: true , toJumplist: true } } , { keys: ['L'] , type: 'motion', motion: 'moveToBottomLine', motionArgs: { linewise: true , toJumplist: true } } , { keys: ['h'] , type: 'motion', motion: 'moveByCharacters', motionArgs: { forward: false } } , { keys: ['l'] , type: 'motion', motion: 'moveByCharacters', motionArgs: { forward: true } } , { keys: ['j'] , type: 'motion', motion: 'moveByLines', motionArgs: { forward: true , linewise: true } } , { keys: ['k'] , type: 'motion', motion: 'moveByLines', motionArgs: { forward: false , linewise: true } } , { keys: ['g', 'j'] , type: 'motion', motion: 'moveByDisplayLines', motionArgs: { forward: true } } , { keys: ['g', 'k'] , type: 'motion', motion: 'moveByDisplayLines', motionArgs: { forward: false } } , { keys: ['w'] , type: 'motion', motion: 'moveByWords', motionArgs: { forward: true , wordEnd: false } } , { keys: ['W'] , type: 'motion', motion: 'moveByWords', motionArgs: { forward: true , wordEnd: false , bigWord: true } } , { keys: ['e'] , type: 'motion', motion: 'moveByWords', motionArgs: { forward: true , wordEnd: true , inclusive: true } } , { keys: ['E'] , type: 'motion', motion: 'moveByWords', motionArgs: { forward: true , wordEnd: true , bigWord: true , inclusive: true } } , { keys: ['b'] , type: 'motion', motion: 'moveByWords', motionArgs: { forward: false , wordEnd: false } } , { keys: ['B'] , type: 'motion', motion: 'moveByWords', motionArgs: { forward: false , wordEnd: false , bigWord: true } } , { keys: ['g', 'e'] , type: 'motion', motion: 'moveByWords', motionArgs: { forward: false , wordEnd: true , inclusive: true } } , { keys: ['g', 'E'] , type: 'motion', motion: 'moveByWords', motionArgs: { forward: false , wordEnd: true , bigWord: true , inclusive: true } } , { keys: ['{'] , type: 'motion', motion: 'moveByParagraph', motionArgs: { forward: false , toJumplist: true } } , { keys: ['}'] , type: 'motion', motion: 'moveByParagraph', motionArgs: { forward: true , toJumplist: true } } , { keys: [''] , type: 'motion', motion: 'moveByPage', motionArgs: { forward: true } } , { keys: [''] , type: 'motion', motion: 'moveByPage', motionArgs: { forward: false } } , { keys: [''] , type: 'motion', motion: 'moveByScroll', motionArgs: { forward: true , explicitRepeat: true } } , { keys: [''] , type: 'motion', motion: 'moveByScroll', motionArgs: { forward: false , explicitRepeat: true } } , { keys: ['g', 'g'] , type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: false , explicitRepeat: true , linewise: true , toJumplist: true } } , { keys: ['G'] , type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: true , explicitRepeat: true , linewise: true , toJumplist: true } } , { keys: ['0'] , type: 'motion', motion: 'moveToStartOfLine'} , { keys: ['^'] , type: 'motion', motion: 'moveToFirstNonWhiteSpaceCharacter'} , { keys: ['+'] , type: 'motion', motion: 'moveByLines', motionArgs: { forward: true , toFirstChar: true } } , { keys: ['-'] , type: 'motion', motion: 'moveByLines', motionArgs: { forward: false , toFirstChar: true } } , { keys: ['_'] , type: 'motion', motion: 'moveByLines', motionArgs: { forward: true , toFirstChar: true , repeatOffset: -1} } , { keys: ['$'] , type: 'motion', motion: 'moveToEol', motionArgs: { inclusive: true } } , { keys: ['%'] , type: 'motion', motion: 'moveToMatchedSymbol', motionArgs: { inclusive: true , toJumplist: true } } , { keys: ['f', 'character'] , type: 'motion', motion: 'moveToCharacter', motionArgs: { forward: true , inclusive: true } } , { keys: ['F', 'character'] , type: 'motion', motion: 'moveToCharacter', motionArgs: { forward: false } } , { keys: ['t', 'character'] , type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: true , inclusive: true } } , { keys: ['T', 'character'] , type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: false } } , { keys: [';'] , type: 'motion', motion: 'repeatLastCharacterSearch', motionArgs: { forward: true } } , { keys: [','] , type: 'motion', motion: 'repeatLastCharacterSearch', motionArgs: { forward: false } } , { keys: ['\'', 'character'] , type: 'motion', motion: 'goToMark', motionArgs: { toJumplist: true } } , { keys: ['`', 'character'] , type: 'motion', motion: 'goToMark', motionArgs: { toJumplist: true } } , { keys: [']', '`'] , type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true } } , { keys: ['[', '`'] , type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false } } , { keys: [']', '\''] , type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true , linewise: true } } , { keys: ['[', '\''] , type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false , linewise: true } } , { keys: [']', 'character'] , type: 'motion', motion: 'moveToSymbol', motionArgs: { forward: true , toJumplist: true } } , { keys: ['[', 'character'] , type: 'motion', motion: 'moveToSymbol', motionArgs: { forward: false , toJumplist: true } } , { keys: ['|'] , type: 'motion', motion: 'moveToColumn', motionArgs: { } } , { keys: ['d'] , type: 'operator', operator: 'delete'} , { keys: ['y'] , type: 'operator', operator: 'yank'} , { keys: ['c'] , type: 'operator', operator: 'change'} , { keys: ['>'] , type: 'operator', operator: 'indent', operatorArgs: { indentRight: true } } , { keys: ['<'] , type: 'operator', operator: 'indent', operatorArgs: { indentRight: false } } , { keys: ['g', '~'] , type: 'operator', operator: 'swapcase'} , { keys: ['n'] , type: 'motion', motion: 'findNext', motionArgs: { forward: true , toJumplist: true } } , { keys: ['N'] , type: 'motion', motion: 'findNext', motionArgs: { forward: false , toJumplist: true } } , { keys: ['x'] , type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: true } , operatorMotionArgs: { visualLine: false } } , { keys: ['X'] , type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: false } , operatorMotionArgs: { visualLine: true } } , { keys: ['D'] , type: 'operatorMotion', operator: 'delete', motion: 'moveToEol', motionArgs: { inclusive: true } , operatorMotionArgs: { visualLine: true } } , { keys: ['Y'] , type: 'operatorMotion', operator: 'yank', motion: 'moveToEol', motionArgs: { inclusive: true } , operatorMotionArgs: { visualLine: true } } , { keys: ['C'] , type: 'operatorMotion', operator: 'change', motion: 'moveToEol', motionArgs: { inclusive: true } , operatorMotionArgs: { visualLine: true } } , { keys: ['~'] , type: 'operatorMotion', operator: 'swapcase', operatorArgs: { shouldMoveCursor: true } , motion: 'moveByCharacters', motionArgs: { forward: true } } , { keys: [''] , type: 'action', action: 'jumpListWalk', actionArgs: { forward: true } } , { keys: [''] , type: 'action', action: 'jumpListWalk', actionArgs: { forward: false } } , { keys: ['a'] , type: 'action', action: 'enterInsertMode', isEdit: true , actionArgs: { insertAt: 'charAfter'} } , { keys: ['A'] , type: 'action', action: 'enterInsertMode', isEdit: true , actionArgs: { insertAt: 'eol'} } , { keys: ['i'] , type: 'action', action: 'enterInsertMode', isEdit: true , actionArgs: { insertAt: 'inplace'} } , { keys: ['I'] , type: 'action', action: 'enterInsertMode', isEdit: true , actionArgs: { insertAt: 'firstNonBlank'} } , { keys: ['o'] , type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true , interlaceInsertRepeat: true , actionArgs: { after: true } } , { keys: ['O'] , type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true , interlaceInsertRepeat: true , actionArgs: { after: false } } , { keys: ['v'] , type: 'action', action: 'toggleVisualMode'} , { keys: ['V'] , type: 'action', action: 'toggleVisualMode', actionArgs: { linewise: true } } , { keys: ['J'] , type: 'action', action: 'joinLines', isEdit: true } , { keys: ['p'] , type: 'action', action: 'paste', isEdit: true , actionArgs: { after: true , isEdit: true } } , { keys: ['P'] , type: 'action', action: 'paste', isEdit: true , actionArgs: { after: false , isEdit: true } } , { keys: ['r', 'character'] , type: 'action', action: 'replace', isEdit: true } , { keys: ['@', 'character'] , type: 'action', action: 'replayMacro'} , { keys: ['q', 'character'] , type: 'action', action: 'enterMacroRecordMode'} , { keys: ['R'] , type: 'action', action: 'enterInsertMode', isEdit: true , actionArgs: { replace: true } } , { keys: ['u'] , type: 'action', action: 'undo'} , { keys: [''] , type: 'action', action: 'redo'} , { keys: ['m', 'character'] , type: 'action', action: 'setMark'} , { keys: ['"', 'character'] , type: 'action', action: 'setRegister'} , { keys: ['z', 'z'] , type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center'} } , { keys: ['z', '.'] , type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center'} , motion: 'moveToFirstNonWhiteSpaceCharacter'} , { keys: ['z', 't'] , type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top'} } , { keys: ['z', ''] , type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top'} , motion: 'moveToFirstNonWhiteSpaceCharacter'} , { keys: ['z', '-'] , type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom'} } , { keys: ['z', 'b'] , type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom'} , motion: 'moveToFirstNonWhiteSpaceCharacter'} , { keys: ['.'] , type: 'action', action: 'repeatLastEdit'} , { keys: [''] , type: 'action', action: 'incrementNumberToken', isEdit: true , actionArgs: { increase: true , backtrack: false } } , { keys: [''] , type: 'action', action: 'incrementNumberToken', isEdit: true , actionArgs: { increase: false , backtrack: false } } , { keys: ['a', 'character'] , type: 'motion', motion: 'textObjectManipulation'} , { keys: ['i', 'character'] , type: 'motion', motion: 'textObjectManipulation', motionArgs: { textObjectInner: true } } , { keys: ['/'] , type: 'search', searchArgs: { forward: true , querySrc: 'prompt', toJumplist: true } } , { keys: ['?'] , type: 'search', searchArgs: { forward: false , querySrc: 'prompt', toJumplist: true } } , { keys: ['*'] , type: 'search', searchArgs: { forward: true , querySrc: 'wordUnderCursor', toJumplist: true } } , { keys: ['#'] , type: 'search', searchArgs: { forward: false , querySrc: 'wordUnderCursor', toJumplist: true } } , { keys: [':'] , type: 'ex'} ] ; var Vim = function (){ CodeMirror.defineOption('vimMode', false , function (cm, val){ if (val) { cm.setOption('keyMap', 'vim'); CodeMirror.signal(cm, "vim-mode-change", { mode: "normal"} ); cm.on('beforeSelectionChange', beforeSelectionChange); maybeInitVimState(cm); } else if (cm.state.vim) { cm.setOption('keyMap', 'default'); cm.off('beforeSelectionChange', beforeSelectionChange); cm.state.vim = null ; } } ); function beforeSelectionChange(cm, cur){ var vim = cm.state.vim; if (vim.insertMode || vim.exMode) return ; var head = cur.head; if (head.ch && head.ch == _AN_Read_length('length', cm.doc.getLine(head.line))) { head.ch-- ; } } var numberRegex = /[\d]/; var wordRegexp = [(/\w/), (/[^\w\s]/)] , bigWordRegexp = [(/\S/)] ; function makeKeyRange(start, size){ var keys = [] ; for (var i = start; i < start + size; i++ ){ keys.push(String.fromCharCode(i)); } return keys; } var upperCaseAlphabet = makeKeyRange(65, 26); var lowerCaseAlphabet = makeKeyRange(97, 26); var numbers = makeKeyRange(48, 10); var specialSymbols = '~`!@#$%^&*()_-+=[{}]\\|/?.,<>:;"\''.split(''); var specialKeys = ['Left', 'Right', 'Up', 'Down', 'Space', 'Backspace', 'Esc', 'Home', 'End', 'PageUp', 'PageDown', 'Enter'] ; var validMarks = [] .concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['<', '>'] ); var validRegisters = [] .concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['-', '"'] ); function isLine(cm, line){ return line >= cm.firstLine() && line <= cm.lastLine(); } function isLowerCase(k){ return (/^[a-z]$/).test(k); } function isMatchableSymbol(k){ return '()[]{}'.indexOf(k) != -1; } function isNumber(k){ return numberRegex.test(k); } function isUpperCase(k){ return (/^[A-Z]$/).test(k); } function isWhiteSpaceString(k){ return (/^\s*$/).test(k); } function inArray(val, arr){ for (var i = 0; i < _AN_Read_length('length', arr); i++ ){ if (arr[i] == val) { return true ; } } return false ; } var createCircularJumpList = function (){ var size = 100; var pointer = -1; var head = 0; var tail = 0; var buffer = new Array(size); function add(cm, oldCur, newCur){ var current = pointer % size; var curMark = buffer[current]; function useNextSlot(cursor){ var next = ++pointer % size; var trashMark = buffer[next]; if (trashMark) { _AN_Call_clear('clear', trashMark); } buffer[next] = cm.setBookmark(cursor); } if (curMark) { var markPos = curMark.find(); if (markPos && !cursorEqual(markPos, oldCur)) { useNextSlot(oldCur); } } else { useNextSlot(oldCur); } useNextSlot(newCur); head = pointer; tail = pointer - size + 1; if (tail < 0) { tail = 0; } } function move(cm, offset){ pointer += offset; if (pointer > head) { pointer = head; } else if (pointer < tail) { pointer = tail; } var mark = buffer[(size + pointer) % size]; if (mark && !mark.find()) { var inc = offset > 0? 1: -1; var newCur; var oldCur = cm.getCursor(); do { pointer += inc; mark = buffer[(size + pointer) % size]; if (mark && (newCur = mark.find()) && !cursorEqual(oldCur, newCur)) { break ; } } while(pointer < head && pointer > tail)} return mark; } return { cachedCursor: undefined, add: add, move: move} ; } ; var createMacroState = function (){ return { macroKeyBuffer: [] , latestRegister: undefined, inReplay: false , lastInsertModeChanges: { changes: [] , expectCursorActivityForChange: false } , enteredMacroMode: undefined, isMacroPlaying: false , toggle: function (cm, registerName){ if (this.enteredMacroMode) { this.enteredMacroMode(); this.enteredMacroMode = undefined; } else { this.latestRegister = registerName; this.enteredMacroMode = cm.openDialog('(recording)[' + registerName + ']', null , { bottom: true } ); } } } ; } ; function maybeInitVimState(cm){ if (!cm.state.vim) { cm.state.vim = { inputState: new InputState(), lastEditInputState: undefined, lastEditActionCommand: undefined, lastHPos: -1, lastHSPos: -1, lastMotion: null , marks: { } , insertMode: false , insertModeRepeat: undefined, visualMode: false , visualLine: false } ; } return cm.state.vim; } var vimGlobalState; function resetVimGlobalState(){ vimGlobalState = { searchQuery: null , searchIsReversed: false , jumpList: createCircularJumpList(), macroModeState: createMacroState(), lastChararacterSearch: { increment: 0, forward: true , selectedCharacter: ''} , registerController: new RegisterController({ } )} ; } var vimApi = { buildKeyMap: function (){ } , getRegisterController: function (){ return vimGlobalState.registerController; } , resetVimGlobalState_: resetVimGlobalState, getVimGlobalState_: function (){ return vimGlobalState; } , maybeInitVimState_: maybeInitVimState, InsertModeKey: InsertModeKey, map: function (lhs, rhs){ exCommandDispatcher.map(lhs, rhs); } , defineEx: function (name, prefix, func){ if (name.indexOf(prefix) !== 0) { throw new Error('(Vim.defineEx) "' + prefix + '" is not a prefix of "' + name + '", command not registered') } exCommands[name] = func; exCommandDispatcher.commandMap_[prefix] = { name: name, shortName: prefix, type: 'api'} ; } , handleKey: function (cm, key){ var command; var vim = maybeInitVimState(cm); var macroModeState = vimGlobalState.macroModeState; if (macroModeState.enteredMacroMode) { if (key == 'q') { actions.exitMacroRecordMode(); vim.inputState = new InputState(); return ; } } if (key == '') { vim.inputState = new InputState(); if (vim.visualMode) { exitVisualMode(cm); } return ; } if (!vim.visualMode && !cursorEqual(cm.getCursor('head'), cm.getCursor('anchor'))) { vim.visualMode = true ; vim.visualLine = false ; CodeMirror.signal(cm, "vim-mode-change", { mode: "visual"} ); cm.on('mousedown', exitVisualMode); } if (key != '0' || (key == '0' && vim.inputState.getRepeat() === 0)) { command = commandDispatcher.matchCommand(key, defaultKeymap, vim); } if (!command) { if (isNumber(key)) { vim.inputState.pushRepeatDigit(key); } return ; } if (command.type == 'keyToKey') { for (var i = 0; i < _AN_Read_length('length', command.toKeys); i++ ){ this.handleKey(cm, command.toKeys[i]); } } else { if (macroModeState.enteredMacroMode) { logKey(macroModeState, key); } commandDispatcher.processCommand(cm, vim, command); } } , handleEx: function (cm, input){ exCommandDispatcher.processCommand(cm, input); } } ; function InputState(){ this.prefixRepeat = [] ; this.motionRepeat = [] ; this.operator = null ; this.operatorArgs = null ; this.motion = null ; this.motionArgs = null ; this.keyBuffer = [] ; this.registerName = null ; } InputState.prototype.pushRepeatDigit = function (n){ if (!this.operator) { this.prefixRepeat = this.prefixRepeat.concat(n); } else { this.motionRepeat = this.motionRepeat.concat(n); } } ; InputState.prototype.getRepeat = function (){ var repeat = 0; if (_AN_Read_length('length', this.prefixRepeat) > 0 || _AN_Read_length('length', this.motionRepeat) > 0) { repeat = 1; if (_AN_Read_length('length', this.prefixRepeat) > 0) { repeat *= parseInt(this.prefixRepeat.join(''), 10); } if (_AN_Read_length('length', this.motionRepeat) > 0) { repeat *= parseInt(this.motionRepeat.join(''), 10); } } return repeat; } ; function Register(text, linewise){ _AN_Call_clear('clear', this); if (text) { this.set(text, linewise); } } Register.prototype = { set: function (text, linewise){ _AN_Write_text('text', this, false , text); this.linewise = !!linewise; } , append: function (text, linewise){ if (linewise || this.linewise) { _AN_Write_text('text', this, true , '\n' + text); this.linewise = true ; } else { _AN_Write_text('text', this, true , text); } } , clear: function (){ _AN_Write_text('text', this, false , ''); this.linewise = false ; } , toString: function (){ return this.text; } } ; function RegisterController(registers){ this.registers = registers; this.unamedRegister = registers["\""] = new Register(); } RegisterController.prototype = { pushText: function (registerName, operator, text, linewise){ if (linewise && text.charAt(0) == '\n') { text = text.slice(1) + '\n'; } if (linewise && text.charAt(_AN_Read_length('length', text) - 1) !== '\n') { text += '\n'; } var register = this.isValidRegister(registerName)? this.getRegister(registerName): null ; if (!register) { switch (operator){ case 'yank': this.registers["0"] = new Register(text, linewise); break ; case 'delete': case 'change': if (text.indexOf('\n') == -1) { this.registers["-"] = new Register(text, linewise); } else { this.shiftNumericRegisters_(); this.registers["1"] = new Register(text, linewise); } break ; } this.unamedRegister.set(text, linewise); return ; } var append = isUpperCase(registerName); if (append) { register.append(text, linewise); this.unamedRegister.append(text, linewise); } else { register.set(text, linewise); this.unamedRegister.set(text, linewise); } } , setRegisterText: function (name, text, linewise){ this.getRegister(name).set(text, linewise); } , getRegister: function (name){ if (!this.isValidRegister(name)) { return this.unamedRegister; } name = name.toLowerCase(); if (!this.registers[name]) { this.registers[name] = new Register(); } return this.registers[name]; } , isValidRegister: function (name){ return name && inArray(name, validRegisters); } , shiftNumericRegisters_: function (){ for (var i = 9; i >= 2; i-- ){ this.registers[i] = this.getRegister('' + (i - 1)); } } } ; var commandDispatcher = { matchCommand: function (key, keyMap, vim){ var inputState = vim.inputState; var keys = inputState.keyBuffer.concat(key); var matchedCommands = [] ; var selectedCharacter; for (var i = 0; i < _AN_Read_length('length', keyMap); i++ ){ var command = keyMap[i]; if (matchKeysPartial(keys, command.keys)) { if (inputState.operator && command.type == 'action') { continue ; } if (command.keys[_AN_Read_length('length', keys) - 1] == 'character') { selectedCharacter = keys[_AN_Read_length('length', keys) - 1]; if (_AN_Read_length('length', selectedCharacter) > 1) { switch (selectedCharacter){ case '': selectedCharacter = '\n'; break ; case '': selectedCharacter = ' '; break ; default : { continue ; } } } } matchedCommands.push(command); } } function getFullyMatchedCommandOrNull(command){ if (_AN_Read_length('length', keys) < _AN_Read_length('length', command.keys)) { inputState.keyBuffer.push(key); return null ; } else { if (command.keys[_AN_Read_length('length', keys) - 1] == 'character') { inputState.selectedCharacter = selectedCharacter; } inputState.keyBuffer = [] ; return command; } } if (!_AN_Read_length('length', matchedCommands)) { inputState.keyBuffer = [] ; return null ; } else if (_AN_Read_length('length', matchedCommands) == 1) { return getFullyMatchedCommandOrNull(matchedCommands[0]); } else { var context = vim.visualMode? 'visual': 'normal'; var bestMatch = matchedCommands[0]; for (var i = 0; i < _AN_Read_length('length', matchedCommands); i++ ){ if (matchedCommands[i].context == context) { bestMatch = matchedCommands[i]; break ; } } return getFullyMatchedCommandOrNull(bestMatch); } } , processCommand: function (cm, vim, command){ vim.inputState.repeatOverride = command.repeatOverride; switch (command.type){ case 'motion': this.processMotion(cm, vim, command); break ; case 'operator': this.processOperator(cm, vim, command); break ; case 'operatorMotion': this.processOperatorMotion(cm, vim, command); break ; case 'action': this.processAction(cm, vim, command); break ; case 'search': this.processSearch(cm, vim, command); break ; case 'ex': case 'keyToEx': this.processEx(cm, vim, command); break ; default : { break ; } } } , processMotion: function (cm, vim, command){ vim.inputState.motion = command.motion; vim.inputState.motionArgs = copyArgs(command.motionArgs); this.evalInput(cm, vim); } , processOperator: function (cm, vim, command){ var inputState = vim.inputState; if (inputState.operator) { if (inputState.operator == command.operator) { inputState.motion = 'expandToLine'; inputState.motionArgs = { linewise: true } ; this.evalInput(cm, vim); return ; } else { vim.inputState = new InputState(); } } inputState.operator = command.operator; inputState.operatorArgs = copyArgs(command.operatorArgs); if (vim.visualMode) { this.evalInput(cm, vim); } } , processOperatorMotion: function (cm, vim, command){ var visualMode = vim.visualMode; var operatorMotionArgs = copyArgs(command.operatorMotionArgs); if (operatorMotionArgs) { if (visualMode && operatorMotionArgs.visualLine) { vim.visualLine = true ; } } this.processOperator(cm, vim, command); if (!visualMode) { this.processMotion(cm, vim, command); } } , processAction: function (cm, vim, command){ var inputState = vim.inputState; var repeat = inputState.getRepeat(); var repeatIsExplicit = !!repeat; var actionArgs = copyArgs(command.actionArgs) || { } ; if (inputState.selectedCharacter) { actionArgs.selectedCharacter = inputState.selectedCharacter; } if (command.operator) { this.processOperator(cm, vim, command); } if (command.motion) { this.processMotion(cm, vim, command); } if (command.motion || command.operator) { this.evalInput(cm, vim); } actionArgs.repeat = repeat || 1; actionArgs.repeatIsExplicit = repeatIsExplicit; actionArgs.registerName = inputState.registerName; vim.inputState = new InputState(); vim.lastMotion = null ; if (command.isEdit) { this.recordLastEdit(vim, inputState, command); } actions[_AN_Read_action('action', command)](cm, actionArgs, vim); } , processSearch: function (cm, vim, command){ if (!cm.getSearchCursor) { return ; } var forward = command.searchArgs.forward; getSearchState(cm).setReversed(!forward); var promptPrefix = (forward)? '/': '?'; var originalQuery = getSearchState(cm).getQuery(); var originalScrollPos = cm.getScrollInfo(); function handleQuery(query, ignoreCase, smartCase){ try { updateSearchQuery(cm, query, ignoreCase, smartCase); } catch (e) { showConfirm(cm, 'Invalid regex: ' + query); return ; } commandDispatcher.processMotion(cm, vim, { type: 'motion', motion: 'findNext', motionArgs: { forward: true , toJumplist: command.searchArgs.toJumplist} } ); } function onPromptClose(query){ cm.scrollTo(originalScrollPos.left, originalScrollPos.top); handleQuery(query, true , true ); } function onPromptKeyUp(_e, query){ var parsedQuery; try { parsedQuery = updateSearchQuery(cm, query, true , true ); } catch (e) { } if (parsedQuery) { cm.scrollIntoView(findNext(cm, !forward, parsedQuery), 30); } else { clearSearchHighlight(cm); cm.scrollTo(originalScrollPos.left, originalScrollPos.top); } } function onPromptKeyDown(e, _query, close){ var keyName = CodeMirror.keyName(e); if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[') { updateSearchQuery(cm, originalQuery); clearSearchHighlight(cm); cm.scrollTo(originalScrollPos.left, originalScrollPos.top); CodeMirror.e_stop(e); close(); cm.focus(); } } switch (command.searchArgs.querySrc){ case 'prompt': showPrompt(cm, { onClose: onPromptClose, prefix: promptPrefix, desc: searchPromptDesc, onKeyUp: onPromptKeyUp, onKeyDown: onPromptKeyDown} ); break ; case 'wordUnderCursor': var word = expandWordUnderCursor(cm, false , true , false , true ); var isKeyword = true ; if (!word) { word = expandWordUnderCursor(cm, false , true , false , false ); isKeyword = false ; } if (!word) { return ; } var query = cm.getLine(word.start.line).substring(word.start.ch, word.end.ch); if (isKeyword) { query = '\\b' + query + '\\b'; } else { query = escapeRegex(query); } vimGlobalState.jumpList.cachedCursor = cm.getCursor(); cm.setCursor(word.start); handleQuery(query, true , false ); break ; } } , processEx: function (cm, vim, command){ function onPromptClose(input){ exCommandDispatcher.processCommand(cm, input); } function onPromptKeyDown(e, _input, close){ var keyName = CodeMirror.keyName(e); if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[') { CodeMirror.e_stop(e); close(); cm.focus(); } } if (command.type == 'keyToEx') { exCommandDispatcher.processCommand(cm, command.exArgs.input); } else { if (vim.visualMode) { showPrompt(cm, { onClose: onPromptClose, prefix: ':', value: '\'<,\'>', onKeyDown: onPromptKeyDown} ); } else { showPrompt(cm, { onClose: onPromptClose, prefix: ':', onKeyDown: onPromptKeyDown} ); } } } , evalInput: function (cm, vim){ var inputState = vim.inputState; var motion = inputState.motion; var motionArgs = inputState.motionArgs || { } ; var operator = inputState.operator; var operatorArgs = inputState.operatorArgs || { } ; var registerName = inputState.registerName; var selectionEnd = cm.getCursor('head'); var selectionStart = cm.getCursor('anchor'); var curStart = copyCursor(selectionEnd); var curOriginal = copyCursor(curStart); var curEnd; var repeat; if (operator) { this.recordLastEdit(vim, inputState); } if (inputState.repeatOverride !== undefined) { repeat = inputState.repeatOverride; } else { repeat = inputState.getRepeat(); } if (repeat > 0 && motionArgs.explicitRepeat) { motionArgs.repeatIsExplicit = true ; } else if (motionArgs.noRepeat || (!motionArgs.explicitRepeat && repeat === 0)) { repeat = 1; motionArgs.repeatIsExplicit = false ; } if (inputState.selectedCharacter) { motionArgs.selectedCharacter = operatorArgs.selectedCharacter = inputState.selectedCharacter; } motionArgs.repeat = repeat; vim.inputState = new InputState(); if (motion) { var motionResult = motions[motion](cm, motionArgs, vim); vim.lastMotion = motions[motion]; if (!motionResult) { return ; } if (motionArgs.toJumplist) { var jumpList = vimGlobalState.jumpList; var cachedCursor = jumpList.cachedCursor; if (cachedCursor) { recordJumpPosition(cm, cachedCursor, motionResult); delete jumpList.cachedCursor; } else { recordJumpPosition(cm, curOriginal, motionResult); } } if (motionResult instanceof Array) { curStart = motionResult[0]; curEnd = motionResult[1]; } else { curEnd = motionResult; } if (!curEnd) { curEnd = { ch: curStart.ch, line: curStart.line} ; } if (vim.visualMode) { if (cursorIsBefore(selectionStart, selectionEnd) && (cursorEqual(selectionStart, curEnd) || cursorIsBefore(curEnd, selectionStart))) { selectionStart.ch += 1; } else if (cursorIsBefore(selectionEnd, selectionStart) && (cursorEqual(selectionStart, curEnd) || cursorIsBefore(selectionStart, curEnd))) { selectionStart.ch -= 1; } selectionEnd = curEnd; if (vim.visualLine) { if (cursorIsBefore(selectionStart, selectionEnd)) { selectionStart.ch = 0; var lastLine = cm.lastLine(); if (selectionEnd.line > lastLine) { selectionEnd.line = lastLine; } selectionEnd.ch = lineLength(cm, selectionEnd.line); } else { selectionEnd.ch = 0; selectionStart.ch = lineLength(cm, selectionStart.line); } } cm.setSelection(selectionStart, selectionEnd); updateMark(cm, vim, '<', cursorIsBefore(selectionStart, selectionEnd)? selectionStart: selectionEnd); updateMark(cm, vim, '>', cursorIsBefore(selectionStart, selectionEnd)? selectionEnd: selectionStart); } else if (!operator) { curEnd = clipCursorToContent(cm, curEnd); cm.setCursor(curEnd.line, curEnd.ch); } } if (operator) { var inverted = false ; vim.lastMotion = null ; operatorArgs.repeat = repeat; if (vim.visualMode) { curStart = selectionStart; curEnd = selectionEnd; motionArgs.inclusive = true ; } if (cursorIsBefore(curEnd, curStart)) { var tmp = curStart; curStart = curEnd; curEnd = tmp; inverted = true ; } if (motionArgs.inclusive && !(vim.visualMode && inverted)) { curEnd.ch++ ; } var linewise = motionArgs.linewise || (vim.visualMode && vim.visualLine); if (linewise) { expandSelectionToLine(cm, curStart, curEnd); } else if (motionArgs.forward) { clipToLine(cm, curStart, curEnd); } operatorArgs.registerName = registerName; operatorArgs.linewise = linewise; operators[operator](cm, operatorArgs, vim, curStart, curEnd, curOriginal); if (vim.visualMode) { exitVisualMode(cm); } } } , recordLastEdit: function (vim, inputState, actionCommand){ var macroModeState = vimGlobalState.macroModeState; if (macroModeState.inReplay) { return ; } vim.lastEditInputState = inputState; vim.lastEditActionCommand = actionCommand; macroModeState.lastInsertModeChanges.changes = [] ; macroModeState.lastInsertModeChanges.expectCursorActivityForChange = false ; } } ; var motions = { moveToTopLine: function (cm, motionArgs){ var line = getUserVisibleLines(cm).top + motionArgs.repeat - 1; return { line: line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(line))} ; } , moveToMiddleLine: function (cm){ var range = getUserVisibleLines(cm); var line = Math.floor((range.top + range.bottom) * 0.5); return { line: line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(line))} ; } , moveToBottomLine: function (cm, motionArgs){ var line = getUserVisibleLines(cm).bottom - motionArgs.repeat + 1; return { line: line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(line))} ; } , expandToLine: function (cm, motionArgs){ var cur = cm.getCursor(); return { line: cur.line + motionArgs.repeat - 1, ch: Infinity} ; } , findNext: function (cm, motionArgs){ var state = getSearchState(cm); var query = state.getQuery(); if (!query) { return ; } var prev = !motionArgs.forward; prev = (state.isReversed())? !prev: prev; highlightSearchMatches(cm, query); return findNext(cm, prev, query, motionArgs.repeat); } , goToMark: function (_cm, motionArgs, vim){ var mark = vim.marks[motionArgs.selectedCharacter]; if (mark) { return mark.find(); } return null ; } , jumpToMark: function (cm, motionArgs, vim){ var best = cm.getCursor(); for (var i = 0; i < motionArgs.repeat; i++ ){ var cursor = best; for (var key in vim.marks){ if (!isLowerCase(key)) { continue ; } var mark = vim.marks[key].find(); var isWrongDirection = (motionArgs.forward)? cursorIsBefore(mark, cursor): cursorIsBefore(cursor, mark); if (isWrongDirection) { continue ; } if (motionArgs.linewise && (mark.line == cursor.line)) { continue ; } var equal = cursorEqual(cursor, best); var between = (motionArgs.forward)? cusrorIsBetween(cursor, mark, best): cusrorIsBetween(best, mark, cursor); if (equal || between) { best = mark; } } } if (motionArgs.linewise) { best.ch = findFirstNonWhiteSpaceCharacter(cm.getLine(best.line)); } return best; } , moveByCharacters: function (cm, motionArgs){ var cur = cm.getCursor(); var repeat = motionArgs.repeat; var ch = motionArgs.forward? cur.ch + repeat: cur.ch - repeat; return { line: cur.line, ch: ch} ; } , moveByLines: function (cm, motionArgs, vim){ var cur = cm.getCursor(); var endCh = cur.ch; switch (vim.lastMotion){ case this.moveByLines: case this.moveByDisplayLines: case this.moveByScroll: case this.moveToColumn: case this.moveToEol: endCh = vim.lastHPos; break ; default : { vim.lastHPos = endCh; } } var repeat = motionArgs.repeat + (motionArgs.repeatOffset || 0); var line = motionArgs.forward? cur.line + repeat: cur.line - repeat; var first = cm.firstLine(); var last = cm.lastLine(); if ((line < first && cur.line == first) || (line > last && cur.line == last)) { return ; } if (motionArgs.toFirstChar) { endCh = findFirstNonWhiteSpaceCharacter(cm.getLine(line)); vim.lastHPos = endCh; } vim.lastHSPos = cm.charCoords({ line: line, ch: endCh} , 'div').left; return { line: line, ch: endCh} ; } , moveByDisplayLines: function (cm, motionArgs, vim){ var cur = cm.getCursor(); switch (vim.lastMotion){ case this.moveByDisplayLines: case this.moveByScroll: case this.moveByLines: case this.moveToColumn: case this.moveToEol: break ; default : { vim.lastHSPos = cm.charCoords(cur, 'div').left; } } var repeat = motionArgs.repeat; var res = cm.findPosV(cur, (motionArgs.forward? repeat: - repeat), 'line', vim.lastHSPos); if (res.hitSide) { if (motionArgs.forward) { var lastCharCoords = cm.charCoords(res, 'div'); var goalCoords = { top: lastCharCoords.top + 8, left: vim.lastHSPos} ; var res = cm.coordsChar(goalCoords, 'div'); } else { var resCoords = cm.charCoords({ line: cm.firstLine(), ch: 0} , 'div'); resCoords.left = vim.lastHSPos; res = cm.coordsChar(resCoords, 'div'); } } vim.lastHPos = res.ch; return res; } , moveByPage: function (cm, motionArgs){ var curStart = cm.getCursor(); var repeat = motionArgs.repeat; cm.moveV((motionArgs.forward? repeat: - repeat), 'page'); var curEnd = cm.getCursor(); cm.setCursor(curStart); return curEnd; } , moveByParagraph: function (cm, motionArgs){ var line = cm.getCursor().line; var repeat = motionArgs.repeat; var inc = motionArgs.forward? 1: -1; for (var i = 0; i < repeat; i++ ){ if ((!motionArgs.forward && line === cm.firstLine()) || (motionArgs.forward && line == cm.lastLine())) { break ; } line += inc; while (line !== cm.firstLine() && line != cm.lastLine() && cm.getLine(line)){ line += inc; } } return { line: line, ch: 0} ; } , moveByScroll: function (cm, motionArgs, vim){ var scrollbox = cm.getScrollInfo(); var curEnd = null ; var repeat = motionArgs.repeat; if (!repeat) { repeat = scrollbox.clientHeight / (2 * cm.defaultTextHeight()); } var orig = cm.charCoords(cm.getCursor(), 'local'); motionArgs.repeat = repeat; var curEnd = motions.moveByDisplayLines(cm, motionArgs, vim); if (!curEnd) { return null ; } var dest = cm.charCoords(curEnd, 'local'); cm.scrollTo(null , scrollbox.top + dest.top - orig.top); return curEnd; } , moveByWords: function (cm, motionArgs){ return moveToWord(cm, motionArgs.repeat, !!motionArgs.forward, !!motionArgs.wordEnd, !!motionArgs.bigWord); } , moveTillCharacter: function (cm, motionArgs){ var repeat = motionArgs.repeat; var curEnd = moveToCharacter(cm, repeat, motionArgs.forward, motionArgs.selectedCharacter); var increment = motionArgs.forward? -1: 1; recordLastCharacterSearch(increment, motionArgs); if (!curEnd) return cm.getCursor(); curEnd.ch += increment; return curEnd; } , moveToCharacter: function (cm, motionArgs){ var repeat = motionArgs.repeat; recordLastCharacterSearch(0, motionArgs); return moveToCharacter(cm, repeat, motionArgs.forward, motionArgs.selectedCharacter) || cm.getCursor(); } , moveToSymbol: function (cm, motionArgs){ var repeat = motionArgs.repeat; return findSymbol(cm, repeat, motionArgs.forward, motionArgs.selectedCharacter) || cm.getCursor(); } , moveToColumn: function (cm, motionArgs, vim){ var repeat = motionArgs.repeat; vim.lastHPos = repeat - 1; vim.lastHSPos = cm.charCoords(cm.getCursor(), 'div').left; return moveToColumn(cm, repeat); } , moveToEol: function (cm, motionArgs, vim){ var cur = cm.getCursor(); vim.lastHPos = Infinity; var retval = { line: cur.line + motionArgs.repeat - 1, ch: Infinity} ; var end = cm.clipPos(retval); end.ch-- ; vim.lastHSPos = cm.charCoords(end, 'div').left; return retval; } , moveToFirstNonWhiteSpaceCharacter: function (cm){ var cursor = cm.getCursor(); return { line: cursor.line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(cursor.line))} ; } , moveToMatchedSymbol: function (cm){ var cursor = cm.getCursor(); var line = cursor.line; var ch = cursor.ch; var lineText = cm.getLine(line); var symbol; var startContext = cm.getTokenAt(cursor).type; var startCtxLevel = getContextLevel(startContext); do { symbol = lineText.charAt(ch++ ); if (symbol && isMatchableSymbol(symbol)) { var endContext = cm.getTokenAt({ line: line, ch: ch} ).type; var endCtxLevel = getContextLevel(endContext); if (startCtxLevel >= endCtxLevel) { break ; } } } while(symbol)if (symbol) { return findMatchedSymbol(cm, { line: line, ch: ch - 1} , symbol); } else { return cursor; } } , moveToStartOfLine: function (cm){ var cursor = cm.getCursor(); return { line: cursor.line, ch: 0} ; } , moveToLineOrEdgeOfDocument: function (cm, motionArgs){ var lineNum = motionArgs.forward? cm.lastLine(): cm.firstLine(); if (motionArgs.repeatIsExplicit) { lineNum = motionArgs.repeat - cm.getOption('firstLineNumber'); } return { line: lineNum, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum))} ; } , textObjectManipulation: function (cm, motionArgs){ var character = motionArgs.selectedCharacter; var inclusive = !motionArgs.textObjectInner; if (!textObjects[character]) { return null ; } var tmp = textObjects[character](cm, inclusive); var start = tmp.start; var end = tmp.end; return [start, end] ; } , repeatLastCharacterSearch: function (cm, motionArgs){ var lastSearch = vimGlobalState.lastChararacterSearch; var repeat = motionArgs.repeat; var forward = motionArgs.forward === lastSearch.forward; var increment = (lastSearch.increment? 1: 0) * (forward? -1: 1); cm.moveH(- increment, 'char'); motionArgs.inclusive = forward? true : false ; var curEnd = moveToCharacter(cm, repeat, forward, lastSearch.selectedCharacter); if (!curEnd) { cm.moveH(increment, 'char'); return cm.getCursor(); } curEnd.ch += increment; return curEnd; } } ; var operators = { change: function (cm, operatorArgs, _vim, curStart, curEnd){ vimGlobalState.registerController.pushText(operatorArgs.registerName, 'change', cm.getRange(curStart, curEnd), operatorArgs.linewise); if (operatorArgs.linewise) { var replacement = curEnd.line > cm.lastLine()? '': '\n'; cm.replaceRange(replacement, curStart, curEnd); cm.indentLine(curStart.line, 'smart'); curStart.ch = null ; } else { var text = cm.getRange(curStart, curEnd); if (!isWhiteSpaceString(text)) { var match = (/\s+$/).exec(text); if (match) { curEnd = offsetCursor(curEnd, 0, - _AN_Read_length('length', match[0])); } } cm.replaceRange('', curStart, curEnd); } actions.enterInsertMode(cm, { } , cm.state.vim); cm.setCursor(curStart); } , 'delete': function (cm, operatorArgs, _vim, curStart, curEnd){ if (operatorArgs.linewise && curEnd.line > cm.lastLine() && curStart.line > cm.firstLine()) { curStart.line-- ; curStart.ch = lineLength(cm, curStart.line); } vimGlobalState.registerController.pushText(operatorArgs.registerName, 'delete', cm.getRange(curStart, curEnd), operatorArgs.linewise); cm.replaceRange('', curStart, curEnd); if (operatorArgs.linewise) { cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm)); } else { cm.setCursor(curStart); } } , indent: function (cm, operatorArgs, vim, curStart, curEnd){ var startLine = curStart.line; var endLine = curEnd.line; var repeat = (vim.visualMode)? operatorArgs.repeat: 1; if (operatorArgs.linewise) { endLine-- ; } for (var i = startLine; i <= endLine; i++ ){ for (var j = 0; j < repeat; j++ ){ cm.indentLine(i, operatorArgs.indentRight); } } cm.setCursor(curStart); cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm)); } , swapcase: function (cm, operatorArgs, _vim, curStart, curEnd, curOriginal){ var toSwap = cm.getRange(curStart, curEnd); var swapped = ''; for (var i = 0; i < _AN_Read_length('length', toSwap); i++ ){ var character = toSwap.charAt(i); swapped += isUpperCase(character)? character.toLowerCase(): character.toUpperCase(); } cm.replaceRange(swapped, curStart, curEnd); if (!operatorArgs.shouldMoveCursor) { cm.setCursor(curOriginal); } } , yank: function (cm, operatorArgs, _vim, curStart, curEnd, curOriginal){ vimGlobalState.registerController.pushText(operatorArgs.registerName, 'yank', cm.getRange(curStart, curEnd), operatorArgs.linewise); cm.setCursor(curOriginal); } } ; var actions = { jumpListWalk: function (cm, actionArgs, vim){ if (vim.visualMode) { return ; } var repeat = actionArgs.repeat; var forward = actionArgs.forward; var jumpList = vimGlobalState.jumpList; var mark = jumpList.move(cm, forward? repeat: - repeat); var markPos = mark? mark.find(): undefined; markPos = markPos? markPos: cm.getCursor(); cm.setCursor(markPos); } , scrollToCursor: function (cm, actionArgs){ var lineNum = cm.getCursor().line; var charCoords = cm.charCoords({ line: lineNum, ch: 0} , 'local'); var height = cm.getScrollInfo().clientHeight; var y = charCoords.top; var lineHeight = charCoords.bottom - y; switch (actionArgs.position){ case 'center': y = y - (height / 2) + lineHeight; break ; case 'bottom': y = y - height + lineHeight * 1.4; break ; case 'top': y = y + lineHeight * 0.4; break ; } cm.scrollTo(null , y); } , replayMacro: function (cm, actionArgs){ var registerName = actionArgs.selectedCharacter; var repeat = actionArgs.repeat; var macroModeState = vimGlobalState.macroModeState; if (registerName == '@') { registerName = macroModeState.latestRegister; } var keyBuffer = parseRegisterToKeyBuffer(macroModeState, registerName); while (repeat-- ){ executeMacroKeyBuffer(cm, macroModeState, keyBuffer); } } , exitMacroRecordMode: function (){ var macroModeState = vimGlobalState.macroModeState; macroModeState.toggle(); parseKeyBufferToRegister(macroModeState.latestRegister, macroModeState.macroKeyBuffer); } , enterMacroRecordMode: function (cm, actionArgs){ var macroModeState = vimGlobalState.macroModeState; var registerName = actionArgs.selectedCharacter; macroModeState.toggle(cm, registerName); emptyMacroKeyBuffer(macroModeState); } , enterInsertMode: function (cm, actionArgs, vim){ if (cm.getOption('readOnly')) { return ; } vim.insertMode = true ; vim.insertModeRepeat = actionArgs && actionArgs.repeat || 1; var insertAt = (actionArgs)? actionArgs.insertAt: null ; if (insertAt == 'eol') { var cursor = cm.getCursor(); cursor = { line: cursor.line, ch: lineLength(cm, cursor.line)} ; cm.setCursor(cursor); } else if (insertAt == 'charAfter') { cm.setCursor(offsetCursor(cm.getCursor(), 0, 1)); } else if (insertAt == 'firstNonBlank') { cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm)); } cm.setOption('keyMap', 'vim-insert'); if (actionArgs && actionArgs.replace) { cm.toggleOverwrite(true ); cm.setOption('keyMap', 'vim-replace'); CodeMirror.signal(cm, "vim-mode-change", { mode: "replace"} ); } else { cm.setOption('keyMap', 'vim-insert'); CodeMirror.signal(cm, "vim-mode-change", { mode: "insert"} ); } if (!vimGlobalState.macroModeState.inReplay) { cm.on('change', onChange); cm.on('cursorActivity', onCursorActivity); CodeMirror.on(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown); } } , toggleVisualMode: function (cm, actionArgs, vim){ var repeat = actionArgs.repeat; var curStart = cm.getCursor(); var curEnd; if (!vim.visualMode) { cm.on('mousedown', exitVisualMode); vim.visualMode = true ; vim.visualLine = !!actionArgs.linewise; if (vim.visualLine) { curStart.ch = 0; curEnd = clipCursorToContent(cm, { line: curStart.line + repeat - 1, ch: lineLength(cm, curStart.line)} , true ); } else { curEnd = clipCursorToContent(cm, { line: curStart.line, ch: curStart.ch + repeat} , true ); } if (!actionArgs.repeatIsExplicit && !vim.visualLine) { cm.setCursor(curEnd); cm.setSelection(curEnd, curStart); } else { cm.setSelection(curStart, curEnd); } CodeMirror.signal(cm, "vim-mode-change", { mode: "visual", subMode: vim.visualLine? "linewise": ""} ); } else { curStart = cm.getCursor('anchor'); curEnd = cm.getCursor('head'); if (!vim.visualLine && actionArgs.linewise) { vim.visualLine = true ; curStart.ch = cursorIsBefore(curStart, curEnd)? 0: lineLength(cm, curStart.line); curEnd.ch = cursorIsBefore(curStart, curEnd)? lineLength(cm, curEnd.line): 0; cm.setSelection(curStart, curEnd); CodeMirror.signal(cm, "vim-mode-change", { mode: "visual", subMode: "linewise"} ); } else if (vim.visualLine && !actionArgs.linewise) { vim.visualLine = false ; CodeMirror.signal(cm, "vim-mode-change", { mode: "visual"} ); } else { exitVisualMode(cm); } } updateMark(cm, vim, '<', cursorIsBefore(curStart, curEnd)? curStart: curEnd); updateMark(cm, vim, '>', cursorIsBefore(curStart, curEnd)? curEnd: curStart); } , joinLines: function (cm, actionArgs, vim){ var curStart, curEnd; if (vim.visualMode) { curStart = cm.getCursor('anchor'); curEnd = cm.getCursor('head'); curEnd.ch = lineLength(cm, curEnd.line) - 1; } else { var repeat = Math.max(actionArgs.repeat, 2); curStart = cm.getCursor(); curEnd = clipCursorToContent(cm, { line: curStart.line + repeat - 1, ch: Infinity} ); } var finalCh = 0; cm.operation(function (){ for (var i = curStart.line; i < curEnd.line; i++ ){ finalCh = lineLength(cm, curStart.line); var tmp = { line: curStart.line + 1, ch: lineLength(cm, curStart.line + 1)} ; var text = cm.getRange(curStart, tmp); text = _AN_Call_replace('replace', text, /\n\s*/g, ' '); cm.replaceRange(text, curStart, tmp); } var curFinalPos = { line: curStart.line, ch: finalCh} ; cm.setCursor(curFinalPos); } ); } , newLineAndEnterInsertMode: function (cm, actionArgs, vim){ vim.insertMode = true ; var insertAt = cm.getCursor(); if (insertAt.line === cm.firstLine() && !actionArgs.after) { cm.replaceRange('\n', { line: cm.firstLine(), ch: 0} ); cm.setCursor(cm.firstLine(), 0); } else { insertAt.line = (actionArgs.after)? insertAt.line: insertAt.line - 1; insertAt.ch = lineLength(cm, insertAt.line); cm.setCursor(insertAt); var newlineFn = CodeMirror.commands.newlineAndIndentContinueComment || CodeMirror.commands.newlineAndIndent; newlineFn(cm); } this.enterInsertMode(cm, { repeat: actionArgs.repeat} , vim); } , paste: function (cm, actionArgs){ var cur = cm.getCursor(); var register = vimGlobalState.registerController.getRegister(actionArgs.registerName); if (!register.text) { return ; } for (var text = '', i = 0; i < actionArgs.repeat; i++ ){ text += register.text; } var linewise = register.linewise; if (linewise) { if (actionArgs.after) { text = '\n' + text.slice(0, _AN_Read_length('length', text) - 1); cur.ch = lineLength(cm, cur.line); } else { cur.ch = 0; } } else { cur.ch += actionArgs.after? 1: 0; } cm.replaceRange(text, cur); var curPosFinal; var idx; if (linewise && actionArgs.after) { curPosFinal = { line: cur.line + 1, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line + 1))} ; } else if (linewise && !actionArgs.after) { curPosFinal = { line: cur.line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line))} ; } else if (!linewise && actionArgs.after) { idx = cm.indexFromPos(cur); curPosFinal = cm.posFromIndex(idx + _AN_Read_length('length', text) - 1); } else { idx = cm.indexFromPos(cur); curPosFinal = cm.posFromIndex(idx + _AN_Read_length('length', text)); } cm.setCursor(curPosFinal); } , undo: function (cm, actionArgs){ cm.operation(function (){ repeatFn(cm, CodeMirror.commands.undo, actionArgs.repeat)(); cm.setCursor(cm.getCursor('anchor')); } ); } , redo: function (cm, actionArgs){ repeatFn(cm, CodeMirror.commands.redo, actionArgs.repeat)(); } , setRegister: function (_cm, actionArgs, vim){ vim.inputState.registerName = actionArgs.selectedCharacter; } , setMark: function (cm, actionArgs, vim){ var markName = actionArgs.selectedCharacter; updateMark(cm, vim, markName, cm.getCursor()); } , replace: function (cm, actionArgs, vim){ var replaceWith = actionArgs.selectedCharacter; var curStart = cm.getCursor(); var replaceTo; var curEnd; if (vim.visualMode) { curStart = cm.getCursor('start'); curEnd = cm.getCursor('end'); curEnd = cm.clipPos({ line: curEnd.line, ch: curEnd.ch + 1} ); } else { var line = cm.getLine(curStart.line); replaceTo = curStart.ch + actionArgs.repeat; if (replaceTo > _AN_Read_length('length', line)) { replaceTo = _AN_Read_length('length', line); } curEnd = { line: curStart.line, ch: replaceTo} ; } if (replaceWith == '\n') { if (!vim.visualMode) cm.replaceRange('', curStart, curEnd); (CodeMirror.commands.newlineAndIndentContinueComment || CodeMirror.commands.newlineAndIndent)(cm); } else { var replaceWithStr = cm.getRange(curStart, curEnd); replaceWithStr = _AN_Call_replace('replace', replaceWithStr, /[^\n]/g, replaceWith); cm.replaceRange(replaceWithStr, curStart, curEnd); if (vim.visualMode) { cm.setCursor(curStart); exitVisualMode(cm); } else { cm.setCursor(offsetCursor(curEnd, 0, -1)); } } } , incrementNumberToken: function (cm, actionArgs){ var cur = cm.getCursor(); var lineStr = cm.getLine(cur.line); var re = /-?\d+/g; var match; var start; var end; var numberStr; var token; while ((match = re.exec(lineStr)) !== null ){ token = match[0]; start = match.index; end = start + _AN_Read_length('length', token); if (cur.ch < end) break ; } if (!actionArgs.backtrack && (end <= cur.ch)) return ; if (token) { var increment = actionArgs.increase? 1: -1; var number = parseInt(token) + (increment * actionArgs.repeat); var from = { ch: start, line: cur.line} ; var to = { ch: end, line: cur.line} ; numberStr = number.toString(); cm.replaceRange(numberStr, from, to); } else { return ; } cm.setCursor({ line: cur.line, ch: start + _AN_Read_length('length', numberStr) - 1} ); } , repeatLastEdit: function (cm, actionArgs, vim){ var lastEditInputState = vim.lastEditInputState; if (!lastEditInputState) { return ; } var repeat = actionArgs.repeat; if (repeat && actionArgs.repeatIsExplicit) { vim.lastEditInputState.repeatOverride = repeat; } else { repeat = vim.lastEditInputState.repeatOverride || repeat; } repeatLastEdit(cm, vim, repeat, false ); } } ; var textObjects = { 'w': function (cm, inclusive){ return expandWordUnderCursor(cm, inclusive, true , false ); } , 'W': function (cm, inclusive){ return expandWordUnderCursor(cm, inclusive, true , true ); } , '{': function (cm, inclusive){ return selectCompanionObject(cm, '}', inclusive); } , '(': function (cm, inclusive){ return selectCompanionObject(cm, ')', inclusive); } , '[': function (cm, inclusive){ return selectCompanionObject(cm, ']', inclusive); } , '\'': function (cm, inclusive){ return findBeginningAndEnd(cm, "'", inclusive); } , '"': function (cm, inclusive){ return findBeginningAndEnd(cm, '"', inclusive); } } ; function clipCursorToContent(cm, cur, includeLineBreak){ var line = Math.min(Math.max(cm.firstLine(), cur.line), cm.lastLine()); var maxCh = lineLength(cm, line) - 1; maxCh = (includeLineBreak)? maxCh + 1: maxCh; var ch = Math.min(Math.max(0, cur.ch), maxCh); return { line: line, ch: ch} ; } function copyArgs(args){ var ret = { } ; for (var prop in args){ if (args.hasOwnProperty(prop)) { ret[prop] = args[prop]; } } return ret; } function offsetCursor(cur, offsetLine, offsetCh){ return { line: cur.line + offsetLine, ch: cur.ch + offsetCh} ; } function matchKeysPartial(pressed, mapped){ for (var i = 0; i < _AN_Read_length('length', pressed); i++ ){ if (pressed[i] != mapped[i] && mapped[i] != 'character') { return false ; } } return true ; } function repeatFn(cm, fn, repeat){ return function (){ for (var i = 0; i < repeat; i++ ){ fn(cm); } } ; } function copyCursor(cur){ return { line: cur.line, ch: cur.ch} ; } function cursorEqual(cur1, cur2){ return cur1.ch == cur2.ch && cur1.line == cur2.line; } function cursorIsBefore(cur1, cur2){ if (cur1.line < cur2.line) { return true ; } if (cur1.line == cur2.line && cur1.ch < cur2.ch) { return true ; } return false ; } function cusrorIsBetween(cur1, cur2, cur3){ var cur1before2 = cursorIsBefore(cur1, cur2); var cur2before3 = cursorIsBefore(cur2, cur3); return cur1before2 && cur2before3; } function lineLength(cm, lineNum){ return _AN_Read_length('length', cm.getLine(lineNum)); } function reverse(s){ return s.split('').reverse().join(''); } function trim(s){ if (s.trim) { return s.trim(); } return _AN_Call_replace('replace', s, /^\s+|\s+$/g, ''); } function escapeRegex(s){ return _AN_Call_replace('replace', s, /([.?*+$\[\]\/\\(){}|\-])/g, '\\$1'); } function exitVisualMode(cm){ cm.off('mousedown', exitVisualMode); var vim = cm.state.vim; vim.visualMode = false ; vim.visualLine = false ; var selectionStart = cm.getCursor('anchor'); var selectionEnd = cm.getCursor('head'); if (!cursorEqual(selectionStart, selectionEnd)) { cm.setCursor(clipCursorToContent(cm, selectionEnd)); } CodeMirror.signal(cm, "vim-mode-change", { mode: "normal"} ); } function clipToLine(cm, curStart, curEnd){ var selection = cm.getRange(curStart, curEnd); if (/\n\s*$/.test(selection)) { var lines = selection.split('\n'); lines.pop(); var line; for (var line = lines.pop(); _AN_Read_length('length', lines) > 0 && line && isWhiteSpaceString(line); line = lines.pop()){ curEnd.line-- ; curEnd.ch = 0; } if (line) { curEnd.line-- ; curEnd.ch = lineLength(cm, curEnd.line); } else { curEnd.ch = 0; } } } function expandSelectionToLine(_cm, curStart, curEnd){ curStart.ch = 0; curEnd.ch = 0; curEnd.line++ ; } function findFirstNonWhiteSpaceCharacter(text){ if (!text) { return 0; } var firstNonWS = text.search(/\S/); return firstNonWS == -1? _AN_Read_length('length', text): firstNonWS; } function expandWordUnderCursor(cm, inclusive, _forward, bigWord, noSymbol){ var cur = cm.getCursor(); var line = cm.getLine(cur.line); var idx = cur.ch; var textAfterIdx = line.substring(idx); var firstMatchedChar; if (noSymbol) { firstMatchedChar = textAfterIdx.search(/\w/); } else { firstMatchedChar = textAfterIdx.search(/\S/); } if (firstMatchedChar == -1) { return null ; } idx += firstMatchedChar; textAfterIdx = line.substring(idx); var textBeforeIdx = line.substring(0, idx); var matchRegex; if (bigWord) { matchRegex = /^\S+/; } else { if ((/\w/).test(line.charAt(idx))) { matchRegex = /^\w+/; } else { matchRegex = /^[^\w\s]+/; } } var wordAfterRegex = matchRegex.exec(textAfterIdx); var wordStart = idx; var wordEnd = idx + _AN_Read_length('length', wordAfterRegex[0]); var revTextBeforeIdx = reverse(textBeforeIdx); var wordBeforeRegex = matchRegex.exec(revTextBeforeIdx); if (wordBeforeRegex) { wordStart -= _AN_Read_length('length', wordBeforeRegex[0]); } if (inclusive) { var textAfterWordEnd = line.substring(wordEnd); var whitespacesAfterWord = _AN_Read_length('length', textAfterWordEnd.match(/^\s*/)[0]); if (whitespacesAfterWord > 0) { wordEnd += whitespacesAfterWord; } else { var revTrim = _AN_Read_length('length', revTextBeforeIdx) - wordStart; var textBeforeWordStart = revTextBeforeIdx.substring(revTrim); var whitespacesBeforeWord = _AN_Read_length('length', textBeforeWordStart.match(/^\s*/)[0]); wordStart -= whitespacesBeforeWord; } } return { start: { line: cur.line, ch: wordStart} , end: { line: cur.line, ch: wordEnd} } ; } function recordJumpPosition(cm, oldCur, newCur){ if (!cursorEqual(oldCur, newCur)) { vimGlobalState.jumpList.add(cm, oldCur, newCur); } } function recordLastCharacterSearch(increment, args){ vimGlobalState.lastChararacterSearch.increment = increment; vimGlobalState.lastChararacterSearch.forward = args.forward; vimGlobalState.lastChararacterSearch.selectedCharacter = args.selectedCharacter; } var symbolToMode = { '(': 'bracket', ')': 'bracket', '{': 'bracket', '}': 'bracket', '[': 'section', ']': 'section', '*': 'comment', '/': 'comment', 'm': 'method', 'M': 'method', '#': 'preprocess'} ; var findSymbolModes = { bracket: { isComplete: function (state){ if (state.nextCh === state.symb) { state.depth++ ; if (state.depth >= 1) return true ; } else if (state.nextCh === state.reverseSymb) { state.depth-- ; } return false ; } } , section: { init: function (state){ state.curMoveThrough = true ; state.symb = (state.forward? ']': '[') === state.symb? '{': '}'; } , isComplete: function (state){ return state.index === 0 && state.nextCh === state.symb; } } , comment: { isComplete: function (state){ var found = state.lastCh === '*' && state.nextCh === '/'; state.lastCh = state.nextCh; return found; } } , method: { init: function (state){ state.symb = (state.symb === 'm'? '{': '}'); state.reverseSymb = state.symb === '{'? '}': '{'; } , isComplete: function (state){ if (state.nextCh === state.symb) return true ; return false ; } } , preprocess: { init: function (state){ state.index = 0; } , isComplete: function (state){ if (state.nextCh === '#') { var token = state.lineText.match(/#(\w+)/)[1]; if (token === 'endif') { if (state.forward && state.depth === 0) { return true ; } state.depth++ ; } else if (token === 'if') { if (!state.forward && state.depth === 0) { return true ; } state.depth-- ; } if (token === 'else' && state.depth === 0) return true ; } return false ; } } } ; function findSymbol(cm, repeat, forward, symb){ var cur = cm.getCursor(); var increment = forward? 1: -1; var endLine = forward? cm.lineCount(): -1; var curCh = cur.ch; var line = cur.line; var lineText = cm.getLine(line); var state = { lineText: lineText, nextCh: lineText.charAt(curCh), lastCh: null , index: curCh, symb: symb, reverseSymb: (forward? { ')': '(', '}': '{'} : { '(': ')', '{': '}'} )[symb], forward: forward, depth: 0, curMoveThrough: false } ; var mode = symbolToMode[symb]; if (!mode) return cur; var init = findSymbolModes[mode].init; var isComplete = findSymbolModes[mode].isComplete; if (init) init(state); while (line !== endLine && repeat){ state.index += increment; state.nextCh = state.lineText.charAt(state.index); if (!state.nextCh) { line += increment; state.lineText = cm.getLine(line) || ''; if (increment > 0) { state.index = 0; } else { var lineLen = _AN_Read_length('length', state.lineText); state.index = (lineLen > 0)? (lineLen - 1): 0; } state.nextCh = state.lineText.charAt(state.index); } if (isComplete(state)) { cur.line = line; cur.ch = state.index; repeat-- ; } } if (state.nextCh || state.curMoveThrough) { return { line: line, ch: state.index} ; } return cur; } function findWord(cm, cur, forward, bigWord, emptyLineIsWord){ var lineNum = cur.line; var pos = cur.ch; var line = cm.getLine(lineNum); var dir = forward? 1: -1; var regexps = bigWord? bigWordRegexp: wordRegexp; if (emptyLineIsWord && line == '') { lineNum += dir; line = cm.getLine(lineNum); if (!isLine(cm, lineNum)) { return null ; } pos = (forward)? 0: _AN_Read_length('length', line); } while (true ){ if (emptyLineIsWord && line == '') { return { from: 0, to: 0, line: lineNum} ; } var stop = (dir > 0)? _AN_Read_length('length', line): -1; var wordStart = stop, wordEnd = stop; while (pos != stop){ var foundWord = false ; for (var i = 0; i < _AN_Read_length('length', regexps) && !foundWord; ++i){ if (regexps[i].test(line.charAt(pos))) { wordStart = pos; while (pos != stop && regexps[i].test(line.charAt(pos))){ pos += dir; } wordEnd = pos; foundWord = wordStart != wordEnd; if (wordStart == cur.ch && lineNum == cur.line && wordEnd == wordStart + dir) { continue ; } else { return { from: Math.min(wordStart, wordEnd + 1), to: Math.max(wordStart, wordEnd), line: lineNum} ; } } } if (!foundWord) { pos += dir; } } lineNum += dir; if (!isLine(cm, lineNum)) { return null ; } line = cm.getLine(lineNum); pos = (dir > 0)? 0: _AN_Read_length('length', line); } throw new Error('The impossible happened.') } function moveToWord(cm, repeat, forward, wordEnd, bigWord){ var cur = cm.getCursor(); var curStart = copyCursor(cur); var words = [] ; if (forward && !wordEnd || !forward && wordEnd) { repeat++ ; } var emptyLineIsWord = !(forward && wordEnd); for (var i = 0; i < repeat; i++ ){ var word = findWord(cm, cur, forward, bigWord, emptyLineIsWord); if (!word) { var eodCh = lineLength(cm, cm.lastLine()); words.push(forward? { line: cm.lastLine(), from: eodCh, to: eodCh} : { line: 0, from: 0, to: 0} ); break ; } words.push(word); cur = { line: word.line, ch: forward? (word.to - 1): word.from} ; } var shortCircuit = _AN_Read_length('length', words) != repeat; var firstWord = words[0]; var lastWord = words.pop(); if (forward && !wordEnd) { if (!shortCircuit && (firstWord.from != curStart.ch || firstWord.line != curStart.line)) { lastWord = words.pop(); } return { line: lastWord.line, ch: lastWord.from} ; } else if (forward && wordEnd) { return { line: lastWord.line, ch: lastWord.to - 1} ; } else if (!forward && wordEnd) { if (!shortCircuit && (firstWord.to != curStart.ch || firstWord.line != curStart.line)) { lastWord = words.pop(); } return { line: lastWord.line, ch: lastWord.to} ; } else { return { line: lastWord.line, ch: lastWord.from} ; } } function moveToCharacter(cm, repeat, forward, character){ var cur = cm.getCursor(); var start = cur.ch; var idx; for (var i = 0; i < repeat; i++ ){ var line = cm.getLine(cur.line); idx = charIdxInLine(start, line, character, forward, true ); if (idx == -1) { return null ; } start = idx; } return { line: cm.getCursor().line, ch: idx} ; } function moveToColumn(cm, repeat){ var line = cm.getCursor().line; return clipCursorToContent(cm, { line: line, ch: repeat - 1} ); } function updateMark(cm, vim, markName, pos){ if (!inArray(markName, validMarks)) { return ; } if (vim.marks[markName]) { _AN_Call_clear('clear', vim.marks[markName]); } vim.marks[markName] = cm.setBookmark(pos); } function charIdxInLine(start, line, character, forward, includeChar){ var idx; if (forward) { idx = line.indexOf(character, start + 1); if (idx != -1 && !includeChar) { idx -= 1; } } else { idx = line.lastIndexOf(character, start - 1); if (idx != -1 && !includeChar) { idx += 1; } } return idx; } function getContextLevel(ctx){ return (ctx === 'string' || ctx === 'comment')? 1: 0; } function findMatchedSymbol(cm, cur, symb){ var line = cur.line; var ch = cur.ch; symb = symb? symb: cm.getLine(line).charAt(ch); var symbContext = cm.getTokenAt({ line: line, ch: ch + 1} ).type; var symbCtxLevel = getContextLevel(symbContext); var reverseSymb = ({ '(': ')', ')': '(', '[': ']', ']': '[', '{': '}', '}': '{'} )[symb]; if (!reverseSymb) { return cur; } var increment = ({ '(': 1, '{': 1, '[': 1} )[symb] || -1; var endLine = increment === 1? cm.lineCount(): -1; var depth = 1, nextCh = symb, index = ch, lineText = cm.getLine(line); while (line !== endLine && depth > 0){ index += increment; nextCh = lineText.charAt(index); if (!nextCh) { line += increment; lineText = cm.getLine(line) || ''; if (increment > 0) { index = 0; } else { var lineLen = _AN_Read_length('length', lineText); index = (lineLen > 0)? (lineLen - 1): 0; } nextCh = lineText.charAt(index); } var revSymbContext = cm.getTokenAt({ line: line, ch: index + 1} ).type; var revSymbCtxLevel = getContextLevel(revSymbContext); if (symbCtxLevel >= revSymbCtxLevel) { if (nextCh === symb) { depth++ ; } else if (nextCh === reverseSymb) { depth-- ; } } } if (nextCh) { return { line: line, ch: index} ; } return cur; } function selectCompanionObject(cm, revSymb, inclusive){ var cur = cm.getCursor(); var end = findMatchedSymbol(cm, cur, revSymb); var start = findMatchedSymbol(cm, end); start.ch += inclusive? 1: 0; end.ch += inclusive? 0: 1; return { start: start, end: end} ; } function findBeginningAndEnd(cm, symb, inclusive){ var cur = cm.getCursor(); var line = cm.getLine(cur.line); var chars = line.split(''); var start, end, i, len; var firstIndex = chars.indexOf(symb); if (cur.ch < firstIndex) { cur.ch = firstIndex; } else if (firstIndex < cur.ch && chars[cur.ch] == symb) { end = cur.ch; --cur.ch; } if (chars[cur.ch] == symb && !end) { start = cur.ch + 1; } else { for (i = cur.ch; i > -1 && !start; i-- ){ if (chars[i] == symb) { start = i + 1; } } } if (start && !end) { for (i = start, len = _AN_Read_length('length', chars); i < len && !end; i++ ){ if (chars[i] == symb) { end = i; } } } if (!start || !end) { return { start: cur, end: cur} ; } if (inclusive) { --start; ++end; } return { start: { line: cur.line, ch: start} , end: { line: cur.line, ch: end} } ; } function SearchState(){ } SearchState.prototype = { getQuery: function (){ return vimGlobalState.query; } , setQuery: function (query){ vimGlobalState.query = query; } , getOverlay: function (){ return this.searchOverlay; } , setOverlay: function (overlay){ this.searchOverlay = overlay; } , isReversed: function (){ return vimGlobalState.isReversed; } , setReversed: function (reversed){ vimGlobalState.isReversed = reversed; } } ; function getSearchState(cm){ var vim = cm.state.vim; return vim.searchState_ || (vim.searchState_ = new SearchState()); } function dialog(cm, template, shortText, onClose, options){ if (cm.openDialog) { cm.openDialog(template, onClose, { bottom: true , value: options.value, onKeyDown: options.onKeyDown, onKeyUp: options.onKeyUp} ); } else { onClose(prompt(shortText, '')); } } function findUnescapedSlashes(str){ var escapeNextChar = false ; var slashes = [] ; for (var i = 0; i < _AN_Read_length('length', str); i++ ){ var c = str.charAt(i); if (!escapeNextChar && c == '/') { slashes.push(i); } escapeNextChar = (c == '\\'); } return slashes; } function parseQuery(query, ignoreCase, smartCase){ if (query instanceof RegExp) { return query; } var slashes = findUnescapedSlashes(query); var regexPart; var forceIgnoreCase; if (!_AN_Read_length('length', slashes)) { regexPart = query; } else { regexPart = query.substring(0, slashes[0]); var flagsPart = query.substring(slashes[0]); forceIgnoreCase = (flagsPart.indexOf('i') != -1); } if (!regexPart) { return null ; } if (smartCase) { ignoreCase = (/^[^A-Z]*$/).test(regexPart); } var regexp = new RegExp(regexPart, (ignoreCase || forceIgnoreCase)? 'i': undefined); return regexp; } function showConfirm(cm, text){ if (cm.openNotification) { cm.openNotification('' + text + '', { bottom: true , duration: 5000} ); } else { alert(text); } } function makePrompt(prefix, desc){ var raw = ''; if (prefix) { raw += '' + prefix + ''; } raw += ' ' + ''; if (desc) { raw += ''; raw += desc; raw += ''; } return raw; } var searchPromptDesc = '(Javascript regexp)'; function showPrompt(cm, options){ var shortText = (options.prefix || '') + ' ' + (options.desc || ''); var prompt = makePrompt(options.prefix, options.desc); dialog(cm, prompt, shortText, options.onClose, options); } function regexEqual(r1, r2){ if (r1 instanceof RegExp && r2 instanceof RegExp) { var props = ['global', 'multiline', 'ignoreCase', 'source'] ; for (var i = 0; i < _AN_Read_length('length', props); i++ ){ var prop = props[i]; if (r1[prop] !== r2[prop]) { return false ; } } return true ; } return false ; } function updateSearchQuery(cm, rawQuery, ignoreCase, smartCase){ if (!rawQuery) { return ; } var state = getSearchState(cm); var query = parseQuery(rawQuery, !!ignoreCase, !!smartCase); if (!query) { return ; } highlightSearchMatches(cm, query); if (regexEqual(query, state.getQuery())) { return query; } state.setQuery(query); return query; } function searchOverlay(query){ if (query.source.charAt(0) == '^') { var matchSol = true ; } return { token: function (stream){ if (matchSol && !stream.sol()) { stream.skipToEnd(); return ; } var match = stream.match(query, false ); if (match) { if (_AN_Read_length('length', match[0]) == 0) { stream.next(); return 'searching'; } if (!stream.sol()) { stream.backUp(1); if (!query.exec(stream.next() + match[0])) { stream.next(); return null ; } } stream.match(query); return 'searching'; } while (!stream.eol()){ stream.next(); if (stream.match(query, false )) break ; } } , query: query} ; } function highlightSearchMatches(cm, query){ var overlay = getSearchState(cm).getOverlay(); if (!overlay || query != overlay.query) { if (overlay) { cm.removeOverlay(overlay); } overlay = searchOverlay(query); cm.addOverlay(overlay); getSearchState(cm).setOverlay(overlay); } } function findNext(cm, prev, query, repeat){ if (repeat === undefined) { repeat = 1; } return cm.operation(function (){ var pos = cm.getCursor(); var cursor = cm.getSearchCursor(query, pos); for (var i = 0; i < repeat; i++ ){ var found = cursor.find(prev); if (i == 0 && found && cursorEqual(cursor.from(), pos)) { found = cursor.find(prev); } if (!found) { cursor = cm.getSearchCursor(query, (prev)? { line: cm.lastLine()} : { line: cm.firstLine(), ch: 0} ); if (!cursor.find(prev)) { return ; } } } return cursor.from(); } ); } function clearSearchHighlight(cm){ cm.removeOverlay(getSearchState(cm).getOverlay()); getSearchState(cm).setOverlay(null ); } function isInRange(pos, start, end){ if (typeof pos != 'number') { pos = pos.line; } if (start instanceof Array) { return inArray(pos, start); } else { if (end) { return (pos >= start && pos <= end); } else { return pos == start; } } } function getUserVisibleLines(cm){ var scrollInfo = cm.getScrollInfo(); var occludeToleranceTop = 6; var occludeToleranceBottom = 10; var from = cm.coordsChar({ left: 0, top: occludeToleranceTop + scrollInfo.top} , 'local'); var bottomY = scrollInfo.clientHeight - occludeToleranceBottom + scrollInfo.top; var to = cm.coordsChar({ left: 0, top: bottomY} , 'local'); return { top: from.line, bottom: to.line} ; } var defaultExCommandMap = [{ name: 'map', type: 'builtIn'} , { name: 'write', shortName: 'w', type: 'builtIn'} , { name: 'undo', shortName: 'u', type: 'builtIn'} , { name: 'redo', shortName: 'red', type: 'builtIn'} , { name: 'sort', shortName: 'sor', type: 'builtIn'} , { name: 'substitute', shortName: 's', type: 'builtIn'} , { name: 'nohlsearch', shortName: 'noh', type: 'builtIn'} , { name: 'delmarks', shortName: 'delm', type: 'builtin'} ] ; Vim.ExCommandDispatcher = function (){ this.buildCommandMap_(); } ; Vim.ExCommandDispatcher.prototype = { processCommand: function (cm, input){ var vim = cm.state.vim; if (vim.visualMode) { exitVisualMode(cm); } var inputStream = new CodeMirror.StringStream(input); var params = { } ; params.input = input; try { this.parseInput_(cm, inputStream, params); } catch (e) { showConfirm(cm, e); return ; } var commandName; if (!params.commandName) { if (params.line !== undefined) { commandName = 'move'; } } else { var command = this.matchCommand_(params.commandName); if (command) { commandName = command.name; this.parseCommandArgs_(inputStream, params, command); if (command.type == 'exToKey') { for (var i = 0; i < _AN_Read_length('length', command.toKeys); i++ ){ CodeMirror.Vim.handleKey(cm, command.toKeys[i]); } return ; } else if (command.type == 'exToEx') { this.processCommand(cm, command.toInput); return ; } } } if (!commandName) { showConfirm(cm, 'Not an editor command ":' + input + '"'); return ; } try { exCommands[commandName](cm, params); } catch (e) { showConfirm(cm, e); throw e } } , parseInput_: function (cm, inputStream, result){ inputStream.eatWhile(':'); if (inputStream.eat('%')) { result.line = cm.firstLine(); result.lineEnd = cm.lastLine(); } else { result.line = this.parseLineSpec_(cm, inputStream); if (result.line !== undefined && inputStream.eat(',')) { result.lineEnd = this.parseLineSpec_(cm, inputStream); } } var commandMatch = inputStream.match(/^(\w+)/); if (commandMatch) { result.commandName = commandMatch[1]; } else { result.commandName = inputStream.match(/.*/)[0]; } return result; } , parseLineSpec_: function (cm, inputStream){ var numberMatch = inputStream.match(/^(\d+)/); if (numberMatch) { return parseInt(numberMatch[1], 10) - 1; } switch (inputStream.next()){ case '.': return cm.getCursor().line; case '$': return cm.lastLine(); case '\'': var mark = cm.state.vim.marks[inputStream.next()]; if (mark && mark.find()) { return mark.find().line; } throw new Error('Mark not set') default : { inputStream.backUp(1); return undefined; } } } , parseCommandArgs_: function (inputStream, params, command){ if (inputStream.eol()) { return ; } params.argString = inputStream.match(/.*/)[0]; var delim = command.argDelimiter || /\s+/; var args = trim(params.argString).split(delim); if (_AN_Read_length('length', args) && args[0]) { params.args = args; } } , matchCommand_: function (commandName){ for (var i = _AN_Read_length('length', commandName); i > 0; i-- ){ var prefix = commandName.substring(0, i); if (this.commandMap_[prefix]) { var command = this.commandMap_[prefix]; if (command.name.indexOf(commandName) === 0) { return command; } } } return null ; } , buildCommandMap_: function (){ this.commandMap_ = { } ; for (var i = 0; i < _AN_Read_length('length', defaultExCommandMap); i++ ){ var command = defaultExCommandMap[i]; var key = command.shortName || command.name; this.commandMap_[key] = command; } } , map: function (lhs, rhs){ if (lhs != ':' && lhs.charAt(0) == ':') { var commandName = lhs.substring(1); if (rhs != ':' && rhs.charAt(0) == ':') { this.commandMap_[commandName] = { name: commandName, type: 'exToEx', toInput: rhs.substring(1)} ; } else { this.commandMap_[commandName] = { name: commandName, type: 'exToKey', toKeys: parseKeyString(rhs)} ; } } else { if (rhs != ':' && rhs.charAt(0) == ':') { defaultKeymap.unshift({ keys: parseKeyString(lhs), type: 'keyToEx', exArgs: { input: rhs.substring(1)} } ); } else { defaultKeymap.unshift({ keys: parseKeyString(lhs), type: 'keyToKey', toKeys: parseKeyString(rhs)} ); } } } } ; function parseKeyString(str){ var key, match; var keys = [] ; while (str){ match = (/<\w+-.+?>|<\w+>|./).exec(str); if (match === null ) break ; key = match[0]; str = str.substring(match.index + _AN_Read_length('length', key)); keys.push(key); } return keys; } var exCommands = { map: function (cm, params){ var mapArgs = params.args; if (!mapArgs || _AN_Read_length('length', mapArgs) < 2) { if (cm) { showConfirm(cm, 'Invalid mapping: ' + params.input); } return ; } exCommandDispatcher.map(mapArgs[0], mapArgs[1], cm); } , move: function (cm, params){ commandDispatcher.processCommand(cm, cm.state.vim, { type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: false , explicitRepeat: true , linewise: true } , repeatOverride: params.line + 1} ); } , sort: function (cm, params){ var reverse, ignoreCase, unique, number; function parseArgs(){ if (params.argString) { var args = new CodeMirror.StringStream(params.argString); if (args.eat('!')) { reverse = true ; } if (args.eol()) { return ; } if (!args.eatSpace()) { return 'Invalid arguments'; } var opts = args.match(/[a-z]+/); if (opts) { opts = opts[0]; ignoreCase = opts.indexOf('i') != -1; unique = opts.indexOf('u') != -1; var decimal = opts.indexOf('d') != -1 && 1; var hex = opts.indexOf('x') != -1 && 1; var octal = opts.indexOf('o') != -1 && 1; if (decimal + hex + octal > 1) { return 'Invalid arguments'; } number = decimal && 'decimal' || hex && 'hex' || octal && 'octal'; } if (args.eatSpace() && args.match(/\/.*\//)) { 'patterns not supported'; } } } var err = parseArgs(); if (err) { showConfirm(cm, err + ': ' + params.argString); return ; } var lineStart = params.line || cm.firstLine(); var lineEnd = params.lineEnd || params.line || cm.lastLine(); if (lineStart == lineEnd) { return ; } var curStart = { line: lineStart, ch: 0} ; var curEnd = { line: lineEnd, ch: lineLength(cm, lineEnd)} ; var text = cm.getRange(curStart, curEnd).split('\n'); var numberRegex = (number == 'decimal')? /(-?)([\d]+)/: (number == 'hex')? /(-?)(?:0x)?([0-9a-f]+)/i: (number == 'octal')? /([0-7]+)/: null ; var radix = (number == 'decimal')? 10: (number == 'hex')? 16: (number == 'octal')? 8: null ; var numPart = [] , textPart = [] ; if (number) { for (var i = 0; i < _AN_Read_length('length', text); i++ ){ if (numberRegex.exec(text[i])) { numPart.push(text[i]); } else { textPart.push(text[i]); } } } else { textPart = text; } function compareFn(a, b){ if (reverse) { var tmp; tmp = a; a = b; b = tmp; } if (ignoreCase) { a = a.toLowerCase(); b = b.toLowerCase(); } var anum = number && numberRegex.exec(a); var bnum = number && numberRegex.exec(b); if (!anum) { return a < b? -1: 1; } anum = parseInt((anum[1] + anum[2]).toLowerCase(), radix); bnum = parseInt((bnum[1] + bnum[2]).toLowerCase(), radix); return anum - bnum; } numPart.sort(compareFn); textPart.sort(compareFn); text = (!reverse)? textPart.concat(numPart): numPart.concat(textPart); if (unique) { var textOld = text; var lastLine; text = [] ; for (var i = 0; i < _AN_Read_length('length', textOld); i++ ){ if (textOld[i] != lastLine) { text.push(textOld[i]); } lastLine = textOld[i]; } } cm.replaceRange(text.join('\n'), curStart, curEnd); } , substitute: function (cm, params){ if (!cm.getSearchCursor) { throw new Error('Search feature not available. Requires searchcursor.js or ' + 'any other getSearchCursor implementation.') } var argString = params.argString; var slashes = findUnescapedSlashes(argString); if (slashes[0] !== 0) { showConfirm(cm, 'Substitutions should be of the form ' + ':s/pattern/replace/'); return ; } var regexPart = argString.substring(slashes[0] + 1, slashes[1]); var replacePart = ''; var flagsPart; var count; var confirm = false ; if (slashes[1]) { replacePart = argString.substring(slashes[1] + 1, slashes[2]); } if (slashes[2]) { var trailing = argString.substring(slashes[2] + 1).split(' '); flagsPart = trailing[0]; count = parseInt(trailing[1]); } if (flagsPart) { if (flagsPart.indexOf('c') != -1) { confirm = true ; _AN_Call_replace('replace', flagsPart, 'c', ''); } regexPart = regexPart + '/' + flagsPart; } if (regexPart) { try { updateSearchQuery(cm, regexPart, true , true ); } catch (e) { showConfirm(cm, 'Invalid regex: ' + regexPart); return ; } } var state = getSearchState(cm); var query = state.getQuery(); var lineStart = (params.line !== undefined)? params.line: cm.getCursor().line; var lineEnd = params.lineEnd || lineStart; if (count) { lineStart = lineEnd; lineEnd = lineStart + count - 1; } var startPos = clipCursorToContent(cm, { line: lineStart, ch: 0} ); var cursor = cm.getSearchCursor(query, startPos); doReplace(cm, confirm, lineStart, lineEnd, cursor, query, replacePart); } , redo: CodeMirror.commands.redo, undo: CodeMirror.commands.undo, write: function (cm){ if (CodeMirror.commands.save) { CodeMirror.commands.save(cm); } else { cm.save(); } } , nohlsearch: function (cm){ clearSearchHighlight(cm); } , delmarks: function (cm, params){ if (!params.argString || !trim(params.argString)) { showConfirm(cm, 'Argument required'); return ; } var state = cm.state.vim; var stream = new CodeMirror.StringStream(trim(params.argString)); while (!stream.eol()){ stream.eatSpace(); var count = stream.pos; if (!stream.match(/[a-zA-Z]/, false )) { showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count)); return ; } var sym = stream.next(); if (stream.match('-', true )) { if (!stream.match(/[a-zA-Z]/, false )) { showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count)); return ; } var startMark = sym; var finishMark = stream.next(); if (isLowerCase(startMark) && isLowerCase(finishMark) || isUpperCase(startMark) && isUpperCase(finishMark)) { var start = startMark.charCodeAt(0); var finish = finishMark.charCodeAt(0); if (start >= finish) { showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count)); return ; } for (var j = 0; j <= finish - start; j++ ){ var mark = String.fromCharCode(start + j); delete state.marks[mark]; } } else { showConfirm(cm, 'Invalid argument: ' + startMark + '-'); return ; } } else { delete state.marks[sym]; } } } } ; var exCommandDispatcher = new Vim.ExCommandDispatcher(); function doReplace(cm, confirm, lineStart, lineEnd, searchCursor, query, replaceWith){ cm.state.vim.exMode = true ; var done = false ; var lastPos = searchCursor.from(); function replaceAll(){ cm.operation(function (){ while (!done){ replace(); next(); } stop(); } ); } function replace(){ var text = cm.getRange(searchCursor.from(), searchCursor.to()); var newText = _AN_Call_replace('replace', text, query, replaceWith); _AN_Call_replace('replace', searchCursor, newText); } function next(){ var found = searchCursor.findNext(); if (!found) { done = true ; } else if (isInRange(searchCursor.from(), lineStart, lineEnd)) { cm.scrollIntoView(searchCursor.from(), 30); cm.setSelection(searchCursor.from(), searchCursor.to()); lastPos = searchCursor.from(); done = false ; } else { done = true ; } } function stop(close){ if (close) { close(); } cm.focus(); if (lastPos) { cm.setCursor(lastPos); var vim = cm.state.vim; vim.exMode = false ; vim.lastHPos = vim.lastHSPos = lastPos.ch; } } function onPromptKeyDown(e, _value, close){ CodeMirror.e_stop(e); var keyName = CodeMirror.keyName(e); switch (keyName){ case 'Y': replace(); next(); break ; case 'N': next(); break ; case 'A': cm.operation(replaceAll); break ; case 'L': replace(); case 'Q': case 'Esc': case 'Ctrl-C': case 'Ctrl-[': stop(close); break ; } if (done) { stop(close); } } next(); if (done) { showConfirm(cm, 'No matches for ' + query.source); return ; } if (!confirm) { replaceAll(); return ; } showPrompt(cm, { prefix: 'replace with ' + replaceWith + ' (y/n/a/q/l)', onKeyDown: onPromptKeyDown} ); } function buildVimKeyMap(){ function cmKeyToVimKey(key, modifier){ var vimKey = key; if (isUpperCase(vimKey)) { if (modifier == 'Shift') { modifier = null ; } else { vimKey = vimKey.toLowerCase(); } } if (modifier) { vimKey = modifier.charAt(0) + '-' + vimKey; } var specialKey = ({ Enter: 'CR', Backspace: 'BS', Delete: 'Del'} )[vimKey]; vimKey = specialKey? specialKey: vimKey; vimKey = _AN_Read_length('length', vimKey) > 1? '<' + vimKey + '>': vimKey; return vimKey; } function keyMapper(vimKey){ return function (cm){ CodeMirror.Vim.handleKey(cm, vimKey); } ; } var cmToVimKeymap = { 'nofallthrough': true , 'disableInput': true , 'style': 'fat-cursor'} ; function bindKeys(keys, modifier){ for (var i = 0; i < _AN_Read_length('length', keys); i++ ){ var key = keys[i]; if (!modifier && inArray(key, specialSymbols)) { key = "'" + key + "'"; } var vimKey = cmKeyToVimKey(keys[i], modifier); var cmKey = modifier? modifier + '-' + key: key; cmToVimKeymap[cmKey] = keyMapper(vimKey); } } bindKeys(upperCaseAlphabet); bindKeys(upperCaseAlphabet, 'Shift'); bindKeys(upperCaseAlphabet, 'Ctrl'); bindKeys(specialSymbols); bindKeys(specialSymbols, 'Ctrl'); bindKeys(numbers); bindKeys(numbers, 'Ctrl'); bindKeys(specialKeys); bindKeys(specialKeys, 'Ctrl'); return cmToVimKeymap; } CodeMirror.keyMap.vim = buildVimKeyMap(); function exitInsertMode(cm){ var vim = cm.state.vim; var inReplay = vimGlobalState.macroModeState.inReplay; if (!inReplay) { cm.off('change', onChange); cm.off('cursorActivity', onCursorActivity); CodeMirror.off(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown); } if (!inReplay && vim.insertModeRepeat > 1) { repeatLastEdit(cm, vim, vim.insertModeRepeat - 1, true ); vim.lastEditInputState.repeatOverride = vim.insertModeRepeat; } delete vim.insertModeRepeat; cm.setCursor(cm.getCursor().line, cm.getCursor().ch - 1, true ); vim.insertMode = false ; cm.setOption('keyMap', 'vim'); cm.toggleOverwrite(false ); CodeMirror.signal(cm, "vim-mode-change", { mode: "normal"} ); } CodeMirror.keyMap["vim-insert"] = { 'Esc': exitInsertMode, 'Ctrl-[': exitInsertMode, 'Ctrl-C': exitInsertMode, 'Ctrl-N': 'autocomplete', 'Ctrl-P': 'autocomplete', 'Enter': function (cm){ var fn = CodeMirror.commands.newlineAndIndentContinueComment || CodeMirror.commands.newlineAndIndent; fn(cm); } , fallthrough: ['default'] } ; CodeMirror.keyMap["vim-replace"] = { 'Backspace': 'goCharLeft', fallthrough: ['vim-insert'] } ; function parseRegisterToKeyBuffer(macroModeState, registerName){ var match, key; var register = vimGlobalState.registerController.getRegister(registerName); var text = register.toString(); var macroKeyBuffer = macroModeState.macroKeyBuffer; emptyMacroKeyBuffer(macroModeState); do { match = (/<\w+-.+?>|<\w+>|./).exec(text); if (match === null ) break ; key = match[0]; text = text.substring(match.index + _AN_Read_length('length', key)); macroKeyBuffer.push(key); } while(text)return macroKeyBuffer; } function parseKeyBufferToRegister(registerName, keyBuffer){ var text = keyBuffer.join(''); vimGlobalState.registerController.setRegisterText(registerName, text); } function emptyMacroKeyBuffer(macroModeState){ if (macroModeState.isMacroPlaying) return ; var macroKeyBuffer = macroModeState.macroKeyBuffer; macroKeyBuffer.length = 0; } function executeMacroKeyBuffer(cm, macroModeState, keyBuffer){ macroModeState.isMacroPlaying = true ; for (var i = 0, len = _AN_Read_length('length', keyBuffer); i < len; i++ ){ CodeMirror.Vim.handleKey(cm, keyBuffer[i]); } ; macroModeState.isMacroPlaying = false ; } function logKey(macroModeState, key){ if (macroModeState.isMacroPlaying) return ; var macroKeyBuffer = macroModeState.macroKeyBuffer; macroKeyBuffer.push(key); } function onChange(_cm, changeObj){ var macroModeState = vimGlobalState.macroModeState; var lastChange = macroModeState.lastInsertModeChanges; while (changeObj){ lastChange.expectCursorActivityForChange = true ; if (changeObj.origin == '+input' || changeObj.origin == 'paste' || changeObj.origin === undefined) { var text = changeObj.text.join('\n'); lastChange.changes.push(text); } changeObj = changeObj.next; } } function onCursorActivity(){ var macroModeState = vimGlobalState.macroModeState; var lastChange = macroModeState.lastInsertModeChanges; if (lastChange.expectCursorActivityForChange) { lastChange.expectCursorActivityForChange = false ; } else { lastChange.changes = [] ; } } function InsertModeKey(keyName){ this.keyName = keyName; } function onKeyEventTargetKeyDown(e){ var macroModeState = vimGlobalState.macroModeState; var lastChange = macroModeState.lastInsertModeChanges; var keyName = CodeMirror.keyName(e); function onKeyFound(){ lastChange.changes.push(new InsertModeKey(keyName)); return true ; } if (keyName.indexOf('Delete') != -1 || keyName.indexOf('Backspace') != -1) { CodeMirror.lookupKey(keyName, ['vim-insert'] , onKeyFound); } } function repeatLastEdit(cm, vim, repeat, repeatForInsert){ var macroModeState = vimGlobalState.macroModeState; macroModeState.inReplay = true ; var isAction = !!vim.lastEditActionCommand; var cachedInputState = vim.inputState; function repeatCommand(){ if (isAction) { commandDispatcher.processAction(cm, vim, vim.lastEditActionCommand); } else { commandDispatcher.evalInput(cm, vim); } } function repeatInsert(repeat){ if (_AN_Read_length('length', macroModeState.lastInsertModeChanges.changes) > 0) { repeat = !vim.lastEditActionCommand? 1: repeat; repeatLastInsertModeChanges(cm, repeat, macroModeState); } } vim.inputState = vim.lastEditInputState; if (isAction && vim.lastEditActionCommand.interlaceInsertRepeat) { for (var i = 0; i < repeat; i++ ){ repeatCommand(); repeatInsert(1); } } else { if (!repeatForInsert) { repeatCommand(); } repeatInsert(repeat); } vim.inputState = cachedInputState; if (vim.insertMode && !repeatForInsert) { exitInsertMode(cm); } macroModeState.inReplay = false ; } ; function repeatLastInsertModeChanges(cm, repeat, macroModeState){ var lastChange = macroModeState.lastInsertModeChanges; function keyHandler(binding){ if (typeof binding == 'string') { CodeMirror.commands[binding](cm); } else { binding(cm); } return true ; } for (var i = 0; i < repeat; i++ ){ for (var j = 0; j < _AN_Read_length('length', lastChange.changes); j++ ){ var change = lastChange.changes[j]; if (change instanceof InsertModeKey) { CodeMirror.lookupKey(change.keyName, ['vim-insert'] , keyHandler); } else { var cur = cm.getCursor(); cm.replaceRange(change, cur, cur); } } } } resetVimGlobalState(); return vimApi; } ; CodeMirror.Vim = Vim(); } )();