(function (mod){ if (typeof exports == "object" && typeof module == "object") mod(require("../lib/codemirror"), require("../addon/search/searchcursor"), require("../addon/dialog/dialog"), require("../addon/edit/matchbrackets.js")); else if (typeof define == "function" && define.amd) define(["../lib/codemirror", "../addon/search/searchcursor", "../addon/dialog/dialog", "../addon/edit/matchbrackets"] , mod); else mod(CodeMirror); } )(function (CodeMirror){ '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', context: 'normal'} , { keys: '', type: 'keyToKey', toKeys: 'W'} , { keys: '', type: 'keyToKey', toKeys: 'B', context: 'normal'} , { keys: '', type: 'keyToKey', toKeys: 'w'} , { keys: '', type: 'keyToKey', toKeys: 'b', context: 'normal'} , { keys: '', type: 'keyToKey', toKeys: 'j'} , { keys: '', type: 'keyToKey', toKeys: 'k'} , { keys: '', type: 'keyToKey', toKeys: ''} , { keys: '', type: 'keyToKey', toKeys: ''} , { keys: '', type: 'keyToKey', toKeys: '', context: 'insert'} , { keys: '', type: 'keyToKey', toKeys: '', context: 'insert'} , { keys: 's', type: 'keyToKey', toKeys: 'cl', context: 'normal'} , { keys: 's', type: 'keyToKey', toKeys: 'c', context: 'visual'} , { keys: 'S', type: 'keyToKey', toKeys: 'cc', context: 'normal'} , { keys: 'S', type: 'keyToKey', toKeys: 'VdO', context: 'visual'} , { keys: '', type: 'keyToKey', toKeys: '0'} , { keys: '', type: 'keyToKey', toKeys: '$'} , { keys: '', type: 'keyToKey', toKeys: ''} , { keys: '', type: 'keyToKey', toKeys: ''} , { keys: '', type: 'keyToKey', toKeys: 'j^', context: 'normal'} , { 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: 'gj', type: 'motion', motion: 'moveByDisplayLines', motionArgs: { forward: true } } , { keys: 'gk', 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: 'ge', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false , wordEnd: true , inclusive: true } } , { keys: 'gE', 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: 'gg', 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', type: 'motion', motion: 'moveToCharacter', motionArgs: { forward: true , inclusive: true } } , { keys: 'F', type: 'motion', motion: 'moveToCharacter', motionArgs: { forward: false } } , { keys: 't', type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: true , inclusive: true } } , { keys: 'T', type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: false } } , { keys: ';', type: 'motion', motion: 'repeatLastCharacterSearch', motionArgs: { forward: true } } , { keys: ',', type: 'motion', motion: 'repeatLastCharacterSearch', motionArgs: { forward: false } } , { keys: '\'', type: 'motion', motion: 'goToMark', motionArgs: { toJumplist: true , linewise: true } } , { keys: '`', 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: ']p', type: 'action', action: 'paste', isEdit: true , actionArgs: { after: true , isEdit: true , matchIndent: true } } , { keys: '[p', type: 'action', action: 'paste', isEdit: true , actionArgs: { after: false , isEdit: true , matchIndent: true } } , { keys: ']', type: 'motion', motion: 'moveToSymbol', motionArgs: { forward: true , toJumplist: true } } , { keys: '[', type: 'motion', motion: 'moveToSymbol', motionArgs: { forward: false , toJumplist: true } } , { keys: '|', type: 'motion', motion: 'moveToColumn'} , { keys: 'o', type: 'motion', motion: 'moveToOtherHighlightedEnd', context: 'visual'} , { keys: 'O', type: 'motion', motion: 'moveToOtherHighlightedEnd', motionArgs: { sameLine: true } , context: 'visual'} , { 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: 'changeCase'} , { keys: 'gu', type: 'operator', operator: 'changeCase', operatorArgs: { toLower: true } , isEdit: true } , { keys: 'gU', type: 'operator', operator: 'changeCase', operatorArgs: { toLower: false } , isEdit: true } , { 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 } , context: 'normal'} , { keys: 'D', type: 'operator', operator: 'delete', operatorArgs: { linewise: true } , context: 'visual'} , { keys: 'Y', type: 'operatorMotion', operator: 'yank', motion: 'moveToEol', motionArgs: { inclusive: true } , context: 'normal'} , { keys: 'Y', type: 'operator', operator: 'yank', operatorArgs: { linewise: true } , context: 'visual'} , { keys: 'C', type: 'operatorMotion', operator: 'change', motion: 'moveToEol', motionArgs: { inclusive: true } , context: 'normal'} , { keys: 'C', type: 'operator', operator: 'change', operatorArgs: { linewise: true } , context: 'visual'} , { keys: '~', type: 'operatorMotion', operator: 'changeCase', motion: 'moveByCharacters', motionArgs: { forward: true } , operatorArgs: { shouldMoveCursor: true } , context: 'normal'} , { keys: '~', type: 'operator', operator: 'changeCase', context: 'visual'} , { keys: '', type: 'operatorMotion', operator: 'delete', motion: 'moveByWords', motionArgs: { forward: false , wordEnd: false } , context: 'insert'} , { keys: '', type: 'action', action: 'jumpListWalk', actionArgs: { forward: true } } , { keys: '', type: 'action', action: 'jumpListWalk', actionArgs: { forward: false } } , { keys: '', type: 'action', action: 'scroll', actionArgs: { forward: true , linewise: true } } , { keys: '', type: 'action', action: 'scroll', actionArgs: { forward: false , linewise: true } } , { keys: 'a', type: 'action', action: 'enterInsertMode', isEdit: true , actionArgs: { insertAt: 'charAfter'} , context: 'normal'} , { keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true , actionArgs: { insertAt: 'eol'} , context: 'normal'} , { keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true , actionArgs: { insertAt: 'endOfSelectedArea'} , context: 'visual'} , { keys: 'i', type: 'action', action: 'enterInsertMode', isEdit: true , actionArgs: { insertAt: 'inplace'} , context: 'normal'} , { keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true , actionArgs: { insertAt: 'firstNonBlank'} , context: 'normal'} , { keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true , actionArgs: { insertAt: 'startOfSelectedArea'} , context: 'visual'} , { keys: 'o', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true , interlaceInsertRepeat: true , actionArgs: { after: true } , context: 'normal'} , { keys: 'O', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true , interlaceInsertRepeat: true , actionArgs: { after: false } , context: 'normal'} , { keys: 'v', type: 'action', action: 'toggleVisualMode'} , { keys: 'V', type: 'action', action: 'toggleVisualMode', actionArgs: { linewise: true } } , { keys: '', type: 'action', action: 'toggleVisualMode', actionArgs: { blockwise: true } } , { keys: '', type: 'action', action: 'toggleVisualMode', actionArgs: { blockwise: true } } , { keys: 'gv', type: 'action', action: 'reselectLastSelection'} , { 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', type: 'action', action: 'replace', isEdit: true } , { keys: '@', type: 'action', action: 'replayMacro'} , { keys: 'q', type: 'action', action: 'enterMacroRecordMode'} , { keys: 'R', type: 'action', action: 'enterInsertMode', isEdit: true , actionArgs: { replace: true } } , { keys: 'u', type: 'action', action: 'undo', context: 'normal'} , { keys: 'u', type: 'operator', operator: 'changeCase', operatorArgs: { toLower: true } , context: 'visual', isEdit: true } , { keys: 'U', type: 'operator', operator: 'changeCase', operatorArgs: { toLower: false } , context: 'visual', isEdit: true } , { keys: '', type: 'action', action: 'redo'} , { keys: 'm', type: 'action', action: 'setMark'} , { keys: '"', type: 'action', action: 'setRegister'} , { keys: 'zz', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center'} } , { keys: 'z.', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center'} , motion: 'moveToFirstNonWhiteSpaceCharacter'} , { keys: 'zt', 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: 'zb', 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', type: 'motion', motion: 'textObjectManipulation'} , { keys: 'i', 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', wholeWordOnly: true , toJumplist: true } } , { keys: '#', type: 'search', searchArgs: { forward: false , querySrc: 'wordUnderCursor', wholeWordOnly: true , toJumplist: true } } , { keys: 'g*', type: 'search', searchArgs: { forward: true , querySrc: 'wordUnderCursor', toJumplist: true } } , { keys: 'g#', type: 'search', searchArgs: { forward: false , querySrc: 'wordUnderCursor', toJumplist: true } } , { keys: ':', type: 'ex'} ] ; var defaultExCommandMap = [{ name: 'colorscheme', shortName: 'colo'} , { name: 'map'} , { name: 'imap', shortName: 'im'} , { name: 'nmap', shortName: 'nm'} , { name: 'vmap', shortName: 'vm'} , { name: 'unmap'} , { name: 'write', shortName: 'w'} , { name: 'undo', shortName: 'u'} , { name: 'redo', shortName: 'red'} , { name: 'set', shortName: 'se'} , { name: 'set', shortName: 'se'} , { name: 'setlocal', shortName: 'setl'} , { name: 'setglobal', shortName: 'setg'} , { name: 'sort', shortName: 'sor'} , { name: 'substitute', shortName: 's', possiblyAsync: true } , { name: 'nohlsearch', shortName: 'noh'} , { name: 'delmarks', shortName: 'delm'} , { name: 'registers', shortName: 'reg', excludeFromCommandHistory: true } , { name: 'global', shortName: 'g'} ] ; var Pos = CodeMirror.Pos; var Vim = function (){ function enterVimMode(cm){ cm.setOption('disableInput', true ); cm.setOption('showCursorWhenSelecting', false ); CodeMirror.signal(cm, "vim-mode-change", { mode: "normal"} ); cm.on('cursorActivity', onCursorActivity); maybeInitVimState(cm); CodeMirror.on(cm.getInputField(), 'paste', getOnPasteFn(cm)); } function leaveVimMode(cm){ cm.setOption('disableInput', false ); cm.off('cursorActivity', onCursorActivity); CodeMirror.off(cm.getInputField(), 'paste', getOnPasteFn(cm)); cm.state.vim = null ; } function detachVimMap(cm, next){ if (this == CodeMirror.keyMap.vim) CodeMirror.rmClass(cm.getWrapperElement(), "cm-fat-cursor"); if (!next || next.attach != attachVimMap) leaveVimMode(cm, false ); } function attachVimMap(cm, prev){ if (this == CodeMirror.keyMap.vim) CodeMirror.addClass(cm.getWrapperElement(), "cm-fat-cursor"); if (!prev || prev.attach != attachVimMap) enterVimMode(cm); } CodeMirror.defineOption('vimMode', false , function (cm, val, prev){ if (val && cm.getOption("keyMap") != "vim") cm.setOption("keyMap", "vim"); else if (!val && prev != CodeMirror.Init && /^vim/.test(cm.getOption("keyMap"))) cm.setOption("keyMap", "default"); } ); function cmKey(key, cm){ if (!cm) { return undefined; } var vimKey = cmKeyToVimKey(key); if (!vimKey) { return false ; } var cmd = CodeMirror.Vim.findKey(cm, vimKey); if (typeof cmd == 'function') { CodeMirror.signal(cm, 'vim-keypress', vimKey); } return cmd; } var modifiers = { 'Shift': 'S', 'Ctrl': 'C', 'Alt': 'A', 'Cmd': 'D', 'Mod': 'A'} ; var specialKeys = { Enter: 'CR', Backspace: 'BS', Delete: 'Del'} ; function cmKeyToVimKey(key){ if (key.charAt(0) == '\'') { return key.charAt(1); } var pieces = key.split(/-(?!$)/); var lastPiece = pieces[_AN_Read_length('length', pieces) - 1]; if (_AN_Read_length('length', pieces) == 1 && _AN_Read_length('length', pieces[0]) == 1) { return false ; } else if (_AN_Read_length('length', pieces) == 2 && pieces[0] == 'Shift' && _AN_Read_length('length', lastPiece) == 1) { return false ; } var hasCharacter = false ; for (var i = 0; i < _AN_Read_length('length', pieces); i++ ){ var piece = pieces[i]; if (piece in modifiers) { pieces[i] = modifiers[piece]; } else { hasCharacter = true ; } if (piece in specialKeys) { pieces[i] = specialKeys[piece]; } } if (!hasCharacter) { return false ; } if (isUpperCase(lastPiece)) { pieces[_AN_Read_length('length', pieces) - 1] = lastPiece.toLowerCase(); } return '<' + pieces.join('-') + '>'; } function getOnPasteFn(cm){ var vim = cm.state.vim; if (!vim.onPasteFn) { vim.onPasteFn = function (){ if (!vim.insertMode) { cm.setCursor(offsetCursor(cm.getCursor(), 0, 1)); actions.enterInsertMode(cm, { } , vim); } } ; } return vim.onPasteFn; } var numberRegex = /[\d]/; var wordCharTest = [CodeMirror.isWordChar, function (ch){ return ch && !CodeMirror.isWordChar(ch) && !/\s/.test(ch); } ] , bigWordCharTest = [function (ch){ return /\S/.test(ch); } ] ; 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 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 options = { } ; function defineOption(name, defaultValue, type, aliases, callback){ if (defaultValue === undefined && !callback) { throw Error('defaultValue is required unless callback is provided') } if (!type) { type = 'string'; } options[name] = { type: type, defaultValue: defaultValue, callback: callback} ; if (aliases) { for (var i = 0; i < _AN_Read_length('length', aliases); i++ ){ options[aliases[i]] = options[name]; } } if (defaultValue) { setOption(name, defaultValue); } } function setOption(name, value, cm, cfg){ var option = options[name]; cfg = cfg || { } ; var scope = cfg.scope; if (!option) { throw Error('Unknown option: ' + name) } if (option.type == 'boolean') { if (value && value !== true ) { throw Error('Invalid argument: ' + name + '=' + value) } else if (value !== false ) { value = true ; } } if (option.callback) { if (scope !== 'local') { option.callback(value, undefined); } if (scope !== 'global' && cm) { option.callback(value, cm); } } else { if (scope !== 'local') { option.value = option.type == 'boolean'? !!value: value; } if (scope !== 'global' && cm) { cm.state.vim.options[name] = { value: value} ; } } } function getOption(name, cm, cfg){ var option = options[name]; cfg = cfg || { } ; var scope = cfg.scope; if (!option) { throw Error('Unknown option: ' + name) } if (option.callback) { var local = cm && option.callback(undefined, cm); if (scope !== 'global' && local !== undefined) { return local; } if (scope !== 'local') { return option.callback(); } return ; } else { var local = (scope !== 'global') && (cm && cm.state.vim.options[name]); return (local || (scope !== 'local') && option || { } ).value; } } defineOption('filetype', undefined, 'string', ['ft'] , function (name, cm){ if (cm === undefined) { return ; } if (name === undefined) { var mode = cm.getOption('mode'); return mode == 'null'? '': mode; } else { var mode = name == ''? 'null': name; cm.setOption('mode', mode); } } ); 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 createInsertModeChanges = function (c){ if (c) { return { changes: c.changes, expectCursorActivityForChange: c.expectCursorActivityForChange} ; } return { changes: [] , expectCursorActivityForChange: false } ; } ; function MacroModeState(){ this.latestRegister = undefined; this.isPlaying = false ; this.isRecording = false ; this.replaySearchQueries = [] ; this.onRecordingDone = undefined; this.lastInsertModeChanges = createInsertModeChanges(); } MacroModeState.prototype = { exitMacroRecordMode: function (){ var macroModeState = vimGlobalState.macroModeState; if (macroModeState.onRecordingDone) { macroModeState.onRecordingDone(); } macroModeState.onRecordingDone = undefined; macroModeState.isRecording = false ; } , enterMacroRecordMode: function (cm, registerName){ var register = vimGlobalState.registerController.getRegister(registerName); if (register) { _AN_Call_clear('clear', register); this.latestRegister = registerName; if (cm.openDialog) { this.onRecordingDone = cm.openDialog('(recording)[' + registerName + ']', null , { bottom: true } ); } this.isRecording = 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: { } , fakeCursor: null , insertMode: false , insertModeRepeat: undefined, visualMode: false , visualLine: false , visualBlock: false , lastSelection: null , lastPastedText: null , sel: { } , options: { } } ; } return cm.state.vim; } var vimGlobalState; function resetVimGlobalState(){ vimGlobalState = { searchQuery: null , searchIsReversed: false , lastSubstituteReplacePart: undefined, jumpList: createCircularJumpList(), macroModeState: new MacroModeState(), lastChararacterSearch: { increment: 0, forward: true , selectedCharacter: ''} , registerController: new RegisterController({ } ), searchHistoryController: new HistoryController({ } ), exCommandHistoryController: new HistoryController({ } )} ; for (var optionName in options){ var option = options[optionName]; option.value = option.defaultValue; } } var lastInsertModeKeyTimer; var vimApi = { buildKeyMap: function (){ } , getRegisterController: function (){ return vimGlobalState.registerController; } , resetVimGlobalState_: resetVimGlobalState, getVimGlobalState_: function (){ return vimGlobalState; } , maybeInitVimState_: maybeInitVimState, suppressErrorLogging: false , InsertModeKey: InsertModeKey, map: function (lhs, rhs, ctx){ exCommandDispatcher.map(lhs, rhs, ctx); } , unmap: function (lhs, ctx){ exCommandDispatcher.unmap(lhs, ctx); } , setOption: setOption, getOption: getOption, defineOption: defineOption, defineEx: function (name, prefix, func){ if (!prefix) { prefix = name; } else 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, origin){ var command = this.findKey(cm, key, origin); if (typeof command === 'function') { return command(); } } , findKey: function (cm, key, origin){ var vim = maybeInitVimState(cm); function handleMacroRecording(){ var macroModeState = vimGlobalState.macroModeState; if (macroModeState.isRecording) { if (key == 'q') { macroModeState.exitMacroRecordMode(); clearInputState(cm); return true ; } if (origin != 'mapping') { logKey(macroModeState, key); } } } function handleEsc(){ if (key == '') { clearInputState(cm); if (vim.visualMode) { exitVisualMode(cm); } else if (vim.insertMode) { exitInsertMode(cm); } return true ; } } function doKeyToKey(keys){ var match; while (keys){ match = (/<\w+-.+?>|<\w+>|./).exec(keys); key = match[0]; keys = keys.substring(match.index + _AN_Read_length('length', key)); CodeMirror.Vim.handleKey(cm, key, 'mapping'); } } function handleKeyInsertMode(){ if (handleEsc()) { return true ; } var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key; var keysAreChars = _AN_Read_length('length', key) == 1; var match = commandDispatcher.matchCommand(keys, defaultKeymap, vim.inputState, 'insert'); while (_AN_Read_length('length', keys) > 1 && match.type != 'full'){ var keys = vim.inputState.keyBuffer = keys.slice(1); var thisMatch = commandDispatcher.matchCommand(keys, defaultKeymap, vim.inputState, 'insert'); if (thisMatch.type != 'none') { match = thisMatch; } } if (match.type == 'none') { clearInputState(cm); return false ; } else if (match.type == 'partial') { if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); } lastInsertModeKeyTimer = _AN_Call_settimeout('setTimeout', window, function (){ if (vim.insertMode && vim.inputState.keyBuffer) { clearInputState(cm); } } , getOption('insertModeEscKeysTimeout')); return !keysAreChars; } if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); } if (keysAreChars) { var here = cm.getCursor(); cm.replaceRange('', offsetCursor(here, 0, - (_AN_Read_length('length', keys) - 1)), here, '+input'); } clearInputState(cm); return match.command; } function handleKeyNonInsertMode(){ if (handleMacroRecording() || handleEsc()) { return true ; } ; var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key; if (/^[1-9]\d*$/.test(keys)) { return true ; } var keysMatcher = /^(\d*)(.*)$/.exec(keys); if (!keysMatcher) { clearInputState(cm); return false ; } var context = vim.visualMode? 'visual': 'normal'; var match = commandDispatcher.matchCommand(keysMatcher[2] || keysMatcher[1], defaultKeymap, vim.inputState, context); if (match.type == 'none') { clearInputState(cm); return false ; } else if (match.type == 'partial') { return true ; } vim.inputState.keyBuffer = ''; var keysMatcher = /^(\d*)(.*)$/.exec(keys); if (keysMatcher[1] && keysMatcher[1] != '0') { vim.inputState.pushRepeatDigit(keysMatcher[1]); } return match.command; } var command; if (vim.insertMode) { command = handleKeyInsertMode(); } else { command = handleKeyNonInsertMode(); } if (command === false ) { return undefined; } else if (command === true ) { return function (){ } ; } else { return function (){ return cm.operation(function (){ cm.curOp.isVimOp = true ; try { if (command.type == 'keyToKey') { doKeyToKey(command.toKeys); } else { commandDispatcher.processCommand(cm, vim, command); } } catch (e) { cm.state.vim = undefined; maybeInitVimState(cm); if (!CodeMirror.Vim.suppressErrorLogging) { console.log(e); } throw e } return true ; } ); } ; } } , handleEx: function (cm, input){ exCommandDispatcher.processCommand(cm, input); } , defineMotion: defineMotion, defineAction: defineAction, defineOperator: defineOperator, mapCommand: mapCommand, _mapCommand: _mapCommand, defineRegister: defineRegister, exitVisualMode: exitVisualMode, exitInsertMode: exitInsertMode} ; 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 clearInputState(cm, reason){ cm.state.vim.inputState = new InputState(); CodeMirror.signal(cm, 'vim-command-done', reason); } function Register(text, linewise, blockwise){ _AN_Call_clear('clear', this); this.keyBuffer = [text || ''] ; this.insertModeChanges = [] ; this.searchQueries = [] ; this.linewise = !!linewise; this.blockwise = !!blockwise; } Register.prototype = { setText: function (text, linewise, blockwise){ this.keyBuffer = [text || ''] ; this.linewise = !!linewise; this.blockwise = !!blockwise; } , pushText: function (text, linewise){ if (linewise) { if (!this.linewise) { this.keyBuffer.push('\n'); } this.linewise = true ; } this.keyBuffer.push(text); } , pushInsertModeChanges: function (changes){ this.insertModeChanges.push(createInsertModeChanges(changes)); } , pushSearchQuery: function (query){ this.searchQueries.push(query); } , clear: function (){ this.keyBuffer = [] ; this.insertModeChanges = [] ; this.searchQueries = [] ; this.linewise = false ; } , toString: function (){ return this.keyBuffer.join(''); } } ; function defineRegister(name, register){ var registers = vimGlobalState.registerController.registers[name]; if (!name || _AN_Read_length('length', name) != 1) { throw Error('Register name must be 1 character') } if (registers[name]) { throw Error('Register already defined ' + name) } registers[name] = register; validRegisters.push(name); } function RegisterController(registers){ this.registers = registers; this.unnamedRegister = registers["\""] = new Register(); registers["."] = new Register(); registers[":"] = new Register(); registers["/"] = new Register(); } RegisterController.prototype = { pushText: function (registerName, operator, text, linewise, blockwise){ 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, blockwise); 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.unnamedRegister.setText(text, linewise, blockwise); return ; } var append = isUpperCase(registerName); if (append) { register.pushText(text, linewise); } else { register.setText(text, linewise, blockwise); } this.unnamedRegister.setText(register.toString(), linewise); } , getRegister: function (name){ if (!this.isValidRegister(name)) { return this.unnamedRegister; } 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)); } } } ; function HistoryController(){ this.historyBuffer = [] ; this.iterator; this.initialPrefix = null ; } HistoryController.prototype = { nextMatch: function (input, up){ var historyBuffer = this.historyBuffer; var dir = up? -1: 1; if (this.initialPrefix === null ) this.initialPrefix = input; for (var i = this.iterator + dir; up? i >= 0: i < _AN_Read_length('length', historyBuffer); i += dir){ var element = historyBuffer[i]; for (var j = 0; j <= _AN_Read_length('length', element); j++ ){ if (this.initialPrefix == element.substring(0, j)) { this.iterator = i; return element; } } } if (i >= _AN_Read_length('length', historyBuffer)) { this.iterator = _AN_Read_length('length', historyBuffer); return this.initialPrefix; } if (i < 0) return input; } , pushInput: function (input){ var index = this.historyBuffer.indexOf(input); if (index > -1) this.historyBuffer.splice(index, 1); if (input.length) this.historyBuffer.push(input); } , reset: function (){ this.initialPrefix = null ; this.iterator = _AN_Read_length('length', this.historyBuffer); } } ; var commandDispatcher = { matchCommand: function (keys, keyMap, inputState, context){ var matches = commandMatches(keys, keyMap, context, inputState); if (!matches.full && !matches.partial) { return { type: 'none'} ; } else if (!matches.full && matches.partial) { return { type: 'partial'} ; } var bestMatch; for (var i = 0; i < _AN_Read_length('length', matches.full); i++ ){ var match = matches.full[i]; if (!bestMatch) { bestMatch = match; } } if (bestMatch.keys.slice(-11) == '') { inputState.selectedCharacter = lastChar(keys); } return { type: 'full', command: 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 { clearInputState(cm); } } 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; clearInputState(cm); 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; var wholeWordOnly = command.searchArgs.wholeWordOnly; getSearchState(cm).setReversed(!forward); var promptPrefix = (forward)? '/': '?'; var originalQuery = getSearchState(cm).getQuery(); var originalScrollPos = cm.getScrollInfo(); function handleQuery(query, ignoreCase, smartCase){ vimGlobalState.searchHistoryController.pushInput(query); vimGlobalState.searchHistoryController.reset(); try { updateSearchQuery(cm, query, ignoreCase, smartCase); } catch (e) { showConfirm(cm, 'Invalid regex: ' + query); clearInputState(cm); 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 ); var macroModeState = vimGlobalState.macroModeState; if (macroModeState.isRecording) { logSearchQuery(macroModeState, query); } } function onPromptKeyUp(e, query, close){ var keyName = CodeMirror.keyName(e), up; if (keyName == 'Up' || keyName == 'Down') { up = keyName == 'Up'? true : false ; query = vimGlobalState.searchHistoryController.nextMatch(query, up) || ''; close(query); } else { if (keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift') vimGlobalState.searchHistoryController.reset(); } 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-[' || (keyName == 'Backspace' && query == '')) { vimGlobalState.searchHistoryController.pushInput(query); vimGlobalState.searchHistoryController.reset(); updateSearchQuery(cm, originalQuery); clearSearchHighlight(cm); cm.scrollTo(originalScrollPos.left, originalScrollPos.top); CodeMirror.e_stop(e); clearInputState(cm); close(); cm.focus(); } else if (keyName == 'Ctrl-U') { CodeMirror.e_stop(e); close(''); } } switch (command.searchArgs.querySrc){ case 'prompt': var macroModeState = vimGlobalState.macroModeState; if (macroModeState.isPlaying) { var query = macroModeState.replaySearchQueries.shift(); handleQuery(query, true , false ); } else { 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 && wholeWordOnly) { 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){ vimGlobalState.exCommandHistoryController.pushInput(input); vimGlobalState.exCommandHistoryController.reset(); exCommandDispatcher.processCommand(cm, input); } function onPromptKeyDown(e, input, close){ var keyName = CodeMirror.keyName(e), up; if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[' || (keyName == 'Backspace' && input == '')) { vimGlobalState.exCommandHistoryController.pushInput(input); vimGlobalState.exCommandHistoryController.reset(); CodeMirror.e_stop(e); clearInputState(cm); close(); cm.focus(); } if (keyName == 'Up' || keyName == 'Down') { up = keyName == 'Up'? true : false ; input = vimGlobalState.exCommandHistoryController.nextMatch(input, up) || ''; close(input); } else if (keyName == 'Ctrl-U') { CodeMirror.e_stop(e); close(''); } else { if (keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift') vimGlobalState.exCommandHistoryController.reset(); } } 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 sel = vim.sel; var origHead = copyCursor(vim.visualMode? clipCursorToContent(cm, sel.head): cm.getCursor('head')); var origAnchor = copyCursor(vim.visualMode? clipCursorToContent(cm, sel.anchor): cm.getCursor('anchor')); var oldHead = copyCursor(origHead); var oldAnchor = copyCursor(origAnchor); var newHead, newAnchor; 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; clearInputState(cm); if (motion) { var motionResult = motions[motion](cm, origHead, 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, origHead, motionResult); } } if (motionResult instanceof Array) { newAnchor = motionResult[0]; newHead = motionResult[1]; } else { newHead = motionResult; } if (!newHead) { newHead = copyCursor(origHead); } if (vim.visualMode) { if (!(vim.visualBlock && newHead.ch === Infinity)) { newHead = clipCursorToContent(cm, newHead, vim.visualBlock); } if (newAnchor) { newAnchor = clipCursorToContent(cm, newAnchor, true ); } newAnchor = newAnchor || oldAnchor; sel.anchor = newAnchor; sel.head = newHead; updateCmSelection(cm); updateMark(cm, vim, '<', cursorIsBefore(newAnchor, newHead)? newAnchor: newHead); updateMark(cm, vim, '>', cursorIsBefore(newAnchor, newHead)? newHead: newAnchor); } else if (!operator) { newHead = clipCursorToContent(cm, newHead); cm.setCursor(newHead.line, newHead.ch); } } if (operator) { if (operatorArgs.lastSel) { newAnchor = oldAnchor; var lastSel = operatorArgs.lastSel; var lineOffset = Math.abs(lastSel.head.line - lastSel.anchor.line); var chOffset = Math.abs(lastSel.head.ch - lastSel.anchor.ch); if (lastSel.visualLine) { newHead = Pos(oldAnchor.line + lineOffset, oldAnchor.ch); } else if (lastSel.visualBlock) { newHead = Pos(oldAnchor.line + lineOffset, oldAnchor.ch + chOffset); } else if (lastSel.head.line == lastSel.anchor.line) { newHead = Pos(oldAnchor.line, oldAnchor.ch + chOffset); } else { newHead = Pos(oldAnchor.line + lineOffset, oldAnchor.ch); } vim.visualMode = true ; vim.visualLine = lastSel.visualLine; vim.visualBlock = lastSel.visualBlock; sel = vim.sel = { anchor: newAnchor, head: newHead} ; updateCmSelection(cm); } else if (vim.visualMode) { operatorArgs.lastSel = { anchor: copyCursor(sel.anchor), head: copyCursor(sel.head), visualBlock: vim.visualBlock, visualLine: vim.visualLine} ; } var curStart, curEnd, linewise, mode; var cmSel; if (vim.visualMode) { curStart = cursorMin(sel.head, sel.anchor); curEnd = cursorMax(sel.head, sel.anchor); linewise = vim.visualLine || operatorArgs.linewise; mode = vim.visualBlock? 'block': linewise? 'line': 'char'; cmSel = makeCmSelection(cm, { anchor: curStart, head: curEnd} , mode); if (linewise) { var ranges = cmSel.ranges; if (mode == 'block') { for (var i = 0; i < _AN_Read_length('length', ranges); i++ ){ ranges[i].head.ch = lineLength(cm, ranges[i].head.line); } } else if (mode == 'line') { ranges[0].head = Pos(ranges[0].head.line + 1, 0); } } } else { curStart = copyCursor(newAnchor || oldAnchor); curEnd = copyCursor(newHead || oldHead); if (cursorIsBefore(curEnd, curStart)) { var tmp = curStart; curStart = curEnd; curEnd = tmp; } linewise = motionArgs.linewise || operatorArgs.linewise; if (linewise) { expandSelectionToLine(cm, curStart, curEnd); } else if (motionArgs.forward) { clipToLine(cm, curStart, curEnd); } mode = 'char'; var exclusive = !motionArgs.inclusive || linewise; cmSel = makeCmSelection(cm, { anchor: curStart, head: curEnd} , mode, exclusive); } cm.setSelections(cmSel.ranges, cmSel.primary); vim.lastMotion = null ; operatorArgs.repeat = repeat; operatorArgs.registerName = registerName; operatorArgs.linewise = linewise; var operatorMoveTo = operators[operator](cm, operatorArgs, cmSel.ranges, oldAnchor, newHead); if (vim.visualMode) { exitVisualMode(cm, operatorMoveTo != null ); } if (operatorMoveTo) { cm.setCursor(operatorMoveTo); } } } , recordLastEdit: function (vim, inputState, actionCommand){ var macroModeState = vimGlobalState.macroModeState; if (macroModeState.isPlaying) { return ; } vim.lastEditInputState = inputState; vim.lastEditActionCommand = actionCommand; macroModeState.lastInsertModeChanges.changes = [] ; macroModeState.lastInsertModeChanges.expectCursorActivityForChange = false ; } } ; var motions = { moveToTopLine: function (cm, _head, motionArgs){ var line = getUserVisibleLines(cm).top + motionArgs.repeat - 1; return Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line))); } , moveToMiddleLine: function (cm){ var range = getUserVisibleLines(cm); var line = Math.floor((range.top + range.bottom) * 0.5); return Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line))); } , moveToBottomLine: function (cm, _head, motionArgs){ var line = getUserVisibleLines(cm).bottom - motionArgs.repeat + 1; return Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line))); } , expandToLine: function (_cm, head, motionArgs){ var cur = head; return Pos(cur.line + motionArgs.repeat - 1, Infinity); } , findNext: function (cm, _head, 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, _head, motionArgs, vim){ var mark = vim.marks[motionArgs.selectedCharacter]; if (mark) { var pos = mark.find(); return motionArgs.linewise? { line: pos.line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(pos.line))} : pos; } return null ; } , moveToOtherHighlightedEnd: function (cm, _head, motionArgs, vim){ if (vim.visualBlock && motionArgs.sameLine) { var sel = vim.sel; return [clipCursorToContent(cm, Pos(sel.anchor.line, sel.head.ch)), clipCursorToContent(cm, Pos(sel.head.line, sel.anchor.ch))] ; } else { return ([vim.sel.head, vim.sel.anchor] ); } } , jumpToMark: function (cm, head, motionArgs, vim){ var best = head; 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)? cursorIsBetween(cursor, mark, best): cursorIsBetween(best, mark, cursor); if (equal || between) { best = mark; } } } if (motionArgs.linewise) { best = Pos(best.line, findFirstNonWhiteSpaceCharacter(cm.getLine(best.line))); } return best; } , moveByCharacters: function (_cm, head, motionArgs){ var cur = head; var repeat = motionArgs.repeat; var ch = motionArgs.forward? cur.ch + repeat: cur.ch - repeat; return Pos(cur.line, ch); } , moveByLines: function (cm, head, motionArgs, vim){ var cur = head; 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) { return this.moveToStartOfLine(cm, head, motionArgs, vim); } else if (line > last && cur.line == last) { return this.moveToEol(cm, head, motionArgs, vim); } if (motionArgs.toFirstChar) { endCh = findFirstNonWhiteSpaceCharacter(cm.getLine(line)); vim.lastHPos = endCh; } vim.lastHSPos = cm.charCoords(Pos(line, endCh), 'div').left; return Pos(line, endCh); } , moveByDisplayLines: function (cm, head, motionArgs, vim){ var cur = head; 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(Pos(cm.firstLine(), 0), 'div'); resCoords.left = vim.lastHSPos; res = cm.coordsChar(resCoords, 'div'); } } vim.lastHPos = res.ch; return res; } , moveByPage: function (cm, head, motionArgs){ var curStart = head; var repeat = motionArgs.repeat; return cm.findPosV(curStart, (motionArgs.forward? repeat: - repeat), 'page'); } , moveByParagraph: function (cm, head, motionArgs){ var dir = motionArgs.forward? 1: -1; return findParagraph(cm, head, motionArgs.repeat, dir); } , moveByScroll: function (cm, head, 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(head, 'local'); motionArgs.repeat = repeat; var curEnd = motions.moveByDisplayLines(cm, head, 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, head, motionArgs){ return moveToWord(cm, head, motionArgs.repeat, !!motionArgs.forward, !!motionArgs.wordEnd, !!motionArgs.bigWord); } , moveTillCharacter: function (cm, _head, 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 null ; curEnd.ch += increment; return curEnd; } , moveToCharacter: function (cm, head, motionArgs){ var repeat = motionArgs.repeat; recordLastCharacterSearch(0, motionArgs); return moveToCharacter(cm, repeat, motionArgs.forward, motionArgs.selectedCharacter) || head; } , moveToSymbol: function (cm, head, motionArgs){ var repeat = motionArgs.repeat; return findSymbol(cm, repeat, motionArgs.forward, motionArgs.selectedCharacter) || head; } , moveToColumn: function (cm, head, motionArgs, vim){ var repeat = motionArgs.repeat; vim.lastHPos = repeat - 1; vim.lastHSPos = cm.charCoords(head, 'div').left; return moveToColumn(cm, repeat); } , moveToEol: function (cm, head, motionArgs, vim){ var cur = head; vim.lastHPos = Infinity; var retval = Pos(cur.line + motionArgs.repeat - 1, Infinity); var end = cm.clipPos(retval); end.ch-- ; vim.lastHSPos = cm.charCoords(end, 'div').left; return retval; } , moveToFirstNonWhiteSpaceCharacter: function (cm, head){ var cursor = head; return Pos(cursor.line, findFirstNonWhiteSpaceCharacter(cm.getLine(cursor.line))); } , moveToMatchedSymbol: function (cm, head){ var cursor = head; var line = cursor.line; var ch = cursor.ch; var lineText = cm.getLine(line); var symbol; do { symbol = lineText.charAt(ch++ ); if (symbol && isMatchableSymbol(symbol)) { var style = cm.getTokenTypeAt(Pos(line, ch)); if (style !== "string" && style !== "comment") { break ; } } } while(symbol)if (symbol) { var matched = cm.findMatchingBracket(Pos(line, ch)); return matched.to; } else { return cursor; } } , moveToStartOfLine: function (_cm, head){ return Pos(head.line, 0); } , moveToLineOrEdgeOfDocument: function (cm, _head, motionArgs){ var lineNum = motionArgs.forward? cm.lastLine(): cm.firstLine(); if (motionArgs.repeatIsExplicit) { lineNum = motionArgs.repeat - cm.getOption('firstLineNumber'); } return Pos(lineNum, findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum))); } , textObjectManipulation: function (cm, head, motionArgs, vim){ var mirroredPairs = { '(': ')', ')': '(', '{': '}', '}': '{', '[': ']', ']': '['} ; var selfPaired = { '\'': true , '"': true } ; var character = motionArgs.selectedCharacter; if (character == 'b') { character = '('; } else if (character == 'B') { character = '{'; } var inclusive = !motionArgs.textObjectInner; var tmp; if (mirroredPairs[character]) { tmp = selectCompanionObject(cm, head, character, inclusive); } else if (selfPaired[character]) { tmp = findBeginningAndEnd(cm, head, character, inclusive); } else if (character === 'W') { tmp = expandWordUnderCursor(cm, inclusive, true , true ); } else if (character === 'w') { tmp = expandWordUnderCursor(cm, inclusive, true , false ); } else if (character === 'p') { tmp = findParagraph(cm, head, motionArgs.repeat, 0, inclusive); motionArgs.linewise = true ; if (vim.visualMode) { if (!vim.visualLine) { vim.visualLine = true ; } } else { var operatorArgs = vim.inputState.operatorArgs; if (operatorArgs) { operatorArgs.linewise = true ; } tmp.end.line-- ; } } else { return null ; } if (!cm.state.vim.visualMode) { return [tmp.start, tmp.end] ; } else { return expandSelection(cm, tmp.start, tmp.end); } } , repeatLastCharacterSearch: function (cm, head, 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 head; } curEnd.ch += increment; return curEnd; } } ; function defineMotion(name, fn){ motions[name] = fn; } function fillArray(val, times){ var arr = [] ; for (var i = 0; i < times; i++ ){ arr.push(val); } return arr; } var operators = { change: function (cm, args, ranges){ var finalHead, text; var vim = cm.state.vim; vimGlobalState.macroModeState.lastInsertModeChanges.inVisualBlock = vim.visualBlock; if (!vim.visualMode) { var anchor = ranges[0].anchor, head = ranges[0].head; text = cm.getRange(anchor, head); var lastState = vim.lastEditInputState || { } ; if (lastState.motion == "moveByWords" && !isWhiteSpaceString(text)) { var match = (/\s+$/).exec(text); if (match && lastState.motionArgs && lastState.motionArgs.forward) { head = offsetCursor(head, 0, - _AN_Read_length("length", match[0])); text = text.slice(0, - _AN_Read_length("length", match[0])); } } var prevLineEnd = new Pos(anchor.line - 1, Number.MAX_VALUE); var wasLastLine = cm.firstLine() == cm.lastLine(); if (head.line > cm.lastLine() && args.linewise && !wasLastLine) { cm.replaceRange('', prevLineEnd, head); } else { cm.replaceRange('', anchor, head); } if (args.linewise) { if (!wasLastLine) { cm.setCursor(prevLineEnd); CodeMirror.commands.newlineAndIndent(cm); } anchor.ch = Number.MAX_VALUE; } finalHead = anchor; } else { text = cm.getSelection(); var replacement = fillArray('', _AN_Read_length('length', ranges)); cm.replaceSelections(replacement); finalHead = cursorMin(ranges[0].head, ranges[0].anchor); } vimGlobalState.registerController.pushText(args.registerName, 'change', text, args.linewise, _AN_Read_length('length', ranges) > 1); actions.enterInsertMode(cm, { head: finalHead} , cm.state.vim); } , 'delete': function (cm, args, ranges){ var finalHead, text; var vim = cm.state.vim; if (!vim.visualBlock) { var anchor = ranges[0].anchor, head = ranges[0].head; if (args.linewise && head.line != cm.firstLine() && anchor.line == cm.lastLine() && anchor.line == head.line - 1) { if (anchor.line == cm.firstLine()) { anchor.ch = 0; } else { anchor = Pos(anchor.line - 1, lineLength(cm, anchor.line - 1)); } } text = cm.getRange(anchor, head); cm.replaceRange('', anchor, head); finalHead = anchor; if (args.linewise) { finalHead = motions.moveToFirstNonWhiteSpaceCharacter(cm, anchor); } } else { text = cm.getSelection(); var replacement = fillArray('', _AN_Read_length('length', ranges)); cm.replaceSelections(replacement); finalHead = ranges[0].anchor; } vimGlobalState.registerController.pushText(args.registerName, 'delete', text, args.linewise, vim.visualBlock); return clipCursorToContent(cm, finalHead); } , indent: function (cm, args, ranges){ var vim = cm.state.vim; var startLine = ranges[0].anchor.line; var endLine = vim.visualBlock? ranges[_AN_Read_length('length', ranges) - 1].anchor.line: ranges[0].head.line; var repeat = (vim.visualMode)? args.repeat: 1; if (args.linewise) { endLine-- ; } for (var i = startLine; i <= endLine; i++ ){ for (var j = 0; j < repeat; j++ ){ cm.indentLine(i, args.indentRight); } } return motions.moveToFirstNonWhiteSpaceCharacter(cm, ranges[0].anchor); } , changeCase: function (cm, args, ranges, oldAnchor, newHead){ var selections = cm.getSelections(); var swapped = [] ; var toLower = args.toLower; for (var j = 0; j < _AN_Read_length('length', selections); j++ ){ var toSwap = selections[j]; var text = ''; if (toLower === true ) { text = toSwap.toLowerCase(); } else if (toLower === false ) { text = toSwap.toUpperCase(); } else { for (var i = 0; i < _AN_Read_length('length', toSwap); i++ ){ var character = toSwap.charAt(i); text += isUpperCase(character)? character.toLowerCase(): character.toUpperCase(); } } swapped.push(text); } cm.replaceSelections(swapped); if (args.shouldMoveCursor) { return newHead; } else if (!cm.state.vim.visualMode && args.linewise && ranges[0].anchor.line + 1 == ranges[0].head.line) { return motions.moveToFirstNonWhiteSpaceCharacter(cm, oldAnchor); } else if (args.linewise) { return oldAnchor; } else { return cursorMin(ranges[0].anchor, ranges[0].head); } } , yank: function (cm, args, ranges, oldAnchor){ var vim = cm.state.vim; var text = cm.getSelection(); var endPos = vim.visualMode? cursorMin(vim.sel.anchor, vim.sel.head, ranges[0].head, ranges[0].anchor): oldAnchor; vimGlobalState.registerController.pushText(args.registerName, 'yank', text, args.linewise, vim.visualBlock); return endPos; } } ; function defineOperator(name, fn){ operators[name] = fn; } 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); } , scroll: function (cm, actionArgs, vim){ if (vim.visualMode) { return ; } var repeat = actionArgs.repeat || 1; var lineHeight = cm.defaultTextHeight(); var top = cm.getScrollInfo().top; var delta = lineHeight * repeat; var newPos = actionArgs.forward? top + delta: top - delta; var cursor = copyCursor(cm.getCursor()); var cursorCoords = cm.charCoords(cursor, 'local'); if (actionArgs.forward) { if (newPos > cursorCoords.top) { cursor.line += (newPos - cursorCoords.top) / lineHeight; cursor.line = Math.ceil(cursor.line); cm.setCursor(cursor); cursorCoords = cm.charCoords(cursor, 'local'); cm.scrollTo(null , cursorCoords.top); } else { cm.scrollTo(null , newPos); } } else { var newBottom = newPos + cm.getScrollInfo().clientHeight; if (newBottom < cursorCoords.bottom) { cursor.line -= (cursorCoords.bottom - newBottom) / lineHeight; cursor.line = Math.floor(cursor.line); cm.setCursor(cursor); cursorCoords = cm.charCoords(cursor, 'local'); cm.scrollTo(null , cursorCoords.bottom - cm.getScrollInfo().clientHeight); } else { cm.scrollTo(null , newPos); } } } , scrollToCursor: function (cm, actionArgs){ var lineNum = cm.getCursor().line; var charCoords = cm.charCoords(Pos(lineNum, 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; break ; } cm.scrollTo(null , y); } , replayMacro: function (cm, actionArgs, vim){ var registerName = actionArgs.selectedCharacter; var repeat = actionArgs.repeat; var macroModeState = vimGlobalState.macroModeState; if (registerName == '@') { registerName = macroModeState.latestRegister; } while (repeat-- ){ executeMacroRegister(cm, vim, macroModeState, registerName); } } , enterMacroRecordMode: function (cm, actionArgs){ var macroModeState = vimGlobalState.macroModeState; var registerName = actionArgs.selectedCharacter; macroModeState.enterMacroRecordMode(cm, registerName); } , 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 ; var sel = vim.sel; var head = actionArgs.head || cm.getCursor('head'); var height = _AN_Read_length('length', cm.listSelections()); if (insertAt == 'eol') { head = Pos(head.line, lineLength(cm, head.line)); } else if (insertAt == 'charAfter') { head = offsetCursor(head, 0, 1); } else if (insertAt == 'firstNonBlank') { head = motions.moveToFirstNonWhiteSpaceCharacter(cm, head); } else if (insertAt == 'startOfSelectedArea') { if (!vim.visualBlock) { if (sel.head.line < sel.anchor.line) { head = sel.head; } else { head = Pos(sel.anchor.line, 0); } } else { head = Pos(Math.min(sel.head.line, sel.anchor.line), Math.min(sel.head.ch, sel.anchor.ch)); height = Math.abs(sel.head.line - sel.anchor.line) + 1; } } else if (insertAt == 'endOfSelectedArea') { if (!vim.visualBlock) { if (sel.head.line >= sel.anchor.line) { head = offsetCursor(sel.head, 0, 1); } else { head = Pos(sel.anchor.line, 0); } } else { head = Pos(Math.min(sel.head.line, sel.anchor.line), Math.max(sel.head.ch + 1, sel.anchor.ch)); height = Math.abs(sel.head.line - sel.anchor.line) + 1; } } else if (insertAt == 'inplace') { if (vim.visualMode) { return ; } } cm.setOption('keyMap', 'vim-insert'); cm.setOption('disableInput', false ); 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.isPlaying) { cm.on('change', onChange); CodeMirror.on(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown); } if (vim.visualMode) { exitVisualMode(cm); } selectForInsert(cm, head, height); } , toggleVisualMode: function (cm, actionArgs, vim){ var repeat = actionArgs.repeat; var anchor = cm.getCursor(); var head; if (!vim.visualMode) { vim.visualMode = true ; vim.visualLine = !!actionArgs.linewise; vim.visualBlock = !!actionArgs.blockwise; head = clipCursorToContent(cm, Pos(anchor.line, anchor.ch + repeat - 1), true ); vim.sel = { anchor: anchor, head: head} ; CodeMirror.signal(cm, "vim-mode-change", { mode: "visual", subMode: vim.visualLine? "linewise": vim.visualBlock? "blockwise": ""} ); updateCmSelection(cm); updateMark(cm, vim, '<', cursorMin(anchor, head)); updateMark(cm, vim, '>', cursorMax(anchor, head)); } else if (vim.visualLine ^ actionArgs.linewise || vim.visualBlock ^ actionArgs.blockwise) { vim.visualLine = !!actionArgs.linewise; vim.visualBlock = !!actionArgs.blockwise; CodeMirror.signal(cm, "vim-mode-change", { mode: "visual", subMode: vim.visualLine? "linewise": vim.visualBlock? "blockwise": ""} ); updateCmSelection(cm); } else { exitVisualMode(cm); } } , reselectLastSelection: function (cm, _actionArgs, vim){ var lastSelection = vim.lastSelection; if (vim.visualMode) { updateLastSelection(cm, vim); } if (lastSelection) { var anchor = lastSelection.anchorMark.find(); var head = lastSelection.headMark.find(); if (!anchor || !head) { return ; } vim.sel = { anchor: anchor, head: head} ; vim.visualMode = true ; vim.visualLine = lastSelection.visualLine; vim.visualBlock = lastSelection.visualBlock; updateCmSelection(cm); updateMark(cm, vim, '<', cursorMin(anchor, head)); updateMark(cm, vim, '>', cursorMax(anchor, head)); CodeMirror.signal(cm, 'vim-mode-change', { mode: 'visual', subMode: vim.visualLine? 'linewise': vim.visualBlock? 'blockwise': ''} ); } } , joinLines: function (cm, actionArgs, vim){ var curStart, curEnd; if (vim.visualMode) { curStart = cm.getCursor('anchor'); curEnd = cm.getCursor('head'); if (cursorIsBefore(curEnd, curStart)) { var tmp = curEnd; curEnd = curStart; curStart = tmp; } curEnd.ch = lineLength(cm, curEnd.line) - 1; } else { var repeat = Math.max(actionArgs.repeat, 2); curStart = cm.getCursor(); curEnd = clipCursorToContent(cm, Pos(curStart.line + repeat - 1, Infinity)); } var finalCh = 0; for (var i = curStart.line; i < curEnd.line; i++ ){ finalCh = lineLength(cm, curStart.line); var tmp = Pos(curStart.line + 1, 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 = Pos(curStart.line, finalCh); if (vim.visualMode) { exitVisualMode(cm, false ); } cm.setCursor(curFinalPos); } , newLineAndEnterInsertMode: function (cm, actionArgs, vim){ vim.insertMode = true ; var insertAt = copyCursor(cm.getCursor()); if (insertAt.line === cm.firstLine() && !actionArgs.after) { cm.replaceRange('\n', Pos(cm.firstLine(), 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, vim){ var cur = copyCursor(cm.getCursor()); var register = vimGlobalState.registerController.getRegister(actionArgs.registerName); var text = register.toString(); if (!text) { return ; } if (actionArgs.matchIndent) { var tabSize = cm.getOption("tabSize"); var whitespaceLength = function (str){ var tabs = (_AN_Read_length("length", str.split("\t")) - 1); var spaces = (_AN_Read_length("length", str.split(" ")) - 1); return tabs * tabSize + spaces * 1; } ; var currentLine = cm.getLine(cm.getCursor().line); var indent = whitespaceLength(currentLine.match(/^\s*/)[0]); var chompedText = _AN_Call_replace("replace", text, /\n$/, ''); var wasChomped = text !== chompedText; var firstIndent = whitespaceLength(text.match(/^\s*/)[0]); var text = _AN_Call_replace('replace', chompedText, /^\s*/gm, function (wspace){ var newIndent = indent + (whitespaceLength(wspace) - firstIndent); if (newIndent < 0) { return ""; } else if (cm.getOption("indentWithTabs")) { var quotient = Math.floor(newIndent / tabSize); return Array(quotient + 1).join('\t'); } else { return Array(newIndent + 1).join(' '); } } ); text += wasChomped? "\n": ""; } if (actionArgs.repeat > 1) { var text = Array(actionArgs.repeat + 1).join(text); } var linewise = register.linewise; var blockwise = register.blockwise; if (linewise) { if (vim.visualMode) { text = vim.visualLine? text.slice(0, -1): '\n' + text.slice(0, _AN_Read_length('length', text) - 1) + '\n'; } else 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 { if (blockwise) { text = text.split('\n'); for (var i = 0; i < _AN_Read_length('length', text); i++ ){ text[i] = (text[i] == '')? ' ': text[i]; } } cur.ch += actionArgs.after? 1: 0; } var curPosFinal; var idx; if (vim.visualMode) { vim.lastPastedText = text; var lastSelectionCurEnd; var selectedArea = getSelectedAreaRange(cm, vim); var selectionStart = selectedArea[0]; var selectionEnd = selectedArea[1]; var selectedText = cm.getSelection(); var selections = cm.listSelections(); var emptyStrings = new Array((_AN_Read_length('length', selections))).join('1').split('1'); if (vim.lastSelection) { lastSelectionCurEnd = vim.lastSelection.headMark.find(); } vimGlobalState.registerController.unnamedRegister.setText(selectedText); if (blockwise) { cm.replaceSelections(emptyStrings); selectionEnd = Pos(selectionStart.line + _AN_Read_length('length', text) - 1, selectionStart.ch); cm.setCursor(selectionStart); selectBlock(cm, selectionEnd); cm.replaceSelections(text); curPosFinal = selectionStart; } else if (vim.visualBlock) { cm.replaceSelections(emptyStrings); cm.setCursor(selectionStart); cm.replaceRange(text, selectionStart, selectionStart); curPosFinal = selectionStart; } else { cm.replaceRange(text, selectionStart, selectionEnd); curPosFinal = cm.posFromIndex(cm.indexFromPos(selectionStart) + _AN_Read_length('length', text) - 1); } if (lastSelectionCurEnd) { vim.lastSelection.headMark = cm.setBookmark(lastSelectionCurEnd); } if (linewise) { curPosFinal.ch = 0; } } else { if (blockwise) { cm.setCursor(cur); for (var i = 0; i < _AN_Read_length('length', text); i++ ){ var line = cur.line + i; if (line > cm.lastLine()) { cm.replaceRange('\n', Pos(line, 0)); } var lastCh = lineLength(cm, line); if (lastCh < cur.ch) { extendLineToColumn(cm, line, cur.ch); } } cm.setCursor(cur); selectBlock(cm, Pos(cur.line + _AN_Read_length('length', text) - 1, cur.ch)); cm.replaceSelections(text); curPosFinal = cur; } else { cm.replaceRange(text, cur); if (linewise && actionArgs.after) { curPosFinal = Pos(cur.line + 1, findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line + 1))); } else if (linewise && !actionArgs.after) { curPosFinal = Pos(cur.line, 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)); } } } if (vim.visualMode) { exitVisualMode(cm, false ); } 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; var selections = cm.listSelections(); if (vim.visualMode) { curStart = cm.getCursor('start'); curEnd = cm.getCursor('end'); } 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 = Pos(curStart.line, 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); if (vim.visualBlock) { var spaces = new Array(cm.getOption("tabSize") + 1).join(' '); replaceWithStr = cm.getSelection(); replaceWithStr = _AN_Call_replace('replace', _AN_Call_replace('replace', replaceWithStr, /\t/g, spaces), /[^\n]/g, replaceWith).split('\n'); cm.replaceSelections(replaceWithStr); } else { cm.replaceRange(replaceWithStr, curStart, curEnd); } if (vim.visualMode) { curStart = cursorIsBefore(selections[0].anchor, selections[0].head)? selections[0].anchor: selections[0].head; cm.setCursor(curStart); exitVisualMode(cm, false ); } 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 = Pos(cur.line, start); var to = Pos(cur.line, end); numberStr = number.toString(); cm.replaceRange(numberStr, from, to); } else { return ; } cm.setCursor(Pos(cur.line, 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 ); } , exitInsertMode: exitInsertMode} ; function defineAction(name, fn){ actions[name] = fn; } 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 Pos(line, 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){ if (typeof offsetLine === 'object') { offsetCh = offsetLine.ch; offsetLine = offsetLine.line; } return Pos(cur.line + offsetLine, cur.ch + offsetCh); } function getOffset(anchor, head){ return { line: head.line - anchor.line, ch: head.line - anchor.line} ; } function commandMatches(keys, keyMap, context, inputState){ var match, partial = [] , full = [] ; for (var i = 0; i < _AN_Read_length('length', keyMap); i++ ){ var command = keyMap[i]; if (context == 'insert' && command.context != 'insert' || command.context && command.context != context || inputState.operator && command.type == 'action' || !(match = commandMatch(keys, command.keys))) { continue ; } if (match == 'partial') { partial.push(command); } if (match == 'full') { full.push(command); } } return { partial: _AN_Read_length('length', partial) && partial, full: _AN_Read_length('length', full) && full} ; } function commandMatch(pressed, mapped){ if (mapped.slice(-11) == '') { var prefixLen = _AN_Read_length('length', mapped) - 11; var pressedPrefix = pressed.slice(0, prefixLen); var mappedPrefix = mapped.slice(0, prefixLen); return pressedPrefix == mappedPrefix && _AN_Read_length('length', pressed) > prefixLen? 'full': mappedPrefix.indexOf(pressedPrefix) == 0? 'partial': false ; } else { return pressed == mapped? 'full': mapped.indexOf(pressed) == 0? 'partial': false ; } } function lastChar(keys){ var match = /^.*(<[\w\-]+>)$/.exec(keys); var selectedCharacter = match? match[1]: keys.slice(-1); if (_AN_Read_length('length', selectedCharacter) > 1) { switch (selectedCharacter){ case '': selectedCharacter = '\n'; break ; case '': selectedCharacter = ' '; break ; default : { break ; } } } return selectedCharacter; } function repeatFn(cm, fn, repeat){ return function (){ for (var i = 0; i < repeat; i++ ){ fn(cm); } } ; } function copyCursor(cur){ return Pos(cur.line, 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 cursorMin(cur1, cur2){ if (_AN_Read_length('length', arguments) > 2) { cur2 = cursorMin.apply(undefined, Array.prototype.slice.call(arguments, 1)); } return cursorIsBefore(cur1, cur2)? cur1: cur2; } function cursorMax(cur1, cur2){ if (_AN_Read_length('length', arguments) > 2) { cur2 = cursorMax.apply(undefined, Array.prototype.slice.call(arguments, 1)); } return cursorIsBefore(cur1, cur2)? cur2: cur1; } function cursorIsBetween(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 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 extendLineToColumn(cm, lineNum, column){ var endCh = lineLength(cm, lineNum); var spaces = new Array(column - endCh + 1).join(' '); cm.setCursor(Pos(lineNum, endCh)); cm.replaceRange(spaces, cm.getCursor()); } function selectBlock(cm, selectionEnd){ var selections = [] , ranges = cm.listSelections(); var head = copyCursor(cm.clipPos(selectionEnd)); var isClipped = !cursorEqual(selectionEnd, head); var curHead = cm.getCursor('head'); var primIndex = getIndex(ranges, curHead); var wasClipped = cursorEqual(ranges[primIndex].head, ranges[primIndex].anchor); var max = _AN_Read_length('length', ranges) - 1; var index = max - primIndex > primIndex? max: 0; var base = ranges[index].anchor; var firstLine = Math.min(base.line, head.line); var lastLine = Math.max(base.line, head.line); var baseCh = base.ch, headCh = head.ch; var dir = ranges[index].head.ch - baseCh; var newDir = headCh - baseCh; if (dir > 0 && newDir <= 0) { baseCh++ ; if (!isClipped) { headCh-- ; } } else if (dir < 0 && newDir >= 0) { baseCh-- ; if (!wasClipped) { headCh++ ; } } else if (dir < 0 && newDir == -1) { baseCh-- ; headCh++ ; } for (var line = firstLine; line <= lastLine; line++ ){ var range = { anchor: new Pos(line, baseCh), head: new Pos(line, headCh)} ; selections.push(range); } primIndex = head.line == lastLine? _AN_Read_length('length', selections) - 1: 0; cm.setSelections(selections); selectionEnd.ch = headCh; base.ch = baseCh; return base; } function selectForInsert(cm, head, height){ var sel = [] ; for (var i = 0; i < height; i++ ){ var lineHead = offsetCursor(head, i, 0); sel.push({ anchor: lineHead, head: lineHead} ); } cm.setSelections(sel, 0); } function getIndex(ranges, cursor, end){ for (var i = 0; i < _AN_Read_length('length', ranges); i++ ){ var atAnchor = end != 'head' && cursorEqual(ranges[i].anchor, cursor); var atHead = end != 'anchor' && cursorEqual(ranges[i].head, cursor); if (atAnchor || atHead) { return i; } } return -1; } function getSelectedAreaRange(cm, vim){ var lastSelection = vim.lastSelection; var getCurrentSelectedAreaRange = function (){ var selections = cm.listSelections(); var start = selections[0]; var end = selections[_AN_Read_length('length', selections) - 1]; var selectionStart = cursorIsBefore(start.anchor, start.head)? start.anchor: start.head; var selectionEnd = cursorIsBefore(end.anchor, end.head)? end.head: end.anchor; return [selectionStart, selectionEnd] ; } ; var getLastSelectedAreaRange = function (){ var selectionStart = cm.getCursor(); var selectionEnd = cm.getCursor(); var block = lastSelection.visualBlock; if (block) { var width = block.width; var height = block.height; selectionEnd = Pos(selectionStart.line + height, selectionStart.ch + width); var selections = [] ; for (var i = selectionStart.line; i < selectionEnd.line; i++ ){ var anchor = Pos(i, selectionStart.ch); var head = Pos(i, selectionEnd.ch); var range = { anchor: anchor, head: head} ; selections.push(range); } cm.setSelections(selections); } else { var start = lastSelection.anchorMark.find(); var end = lastSelection.headMark.find(); var line = end.line - start.line; var ch = end.ch - start.ch; selectionEnd = { line: selectionEnd.line + line, ch: line? selectionEnd.ch: ch + selectionEnd.ch} ; if (lastSelection.visualLine) { selectionStart = Pos(selectionStart.line, 0); selectionEnd = Pos(selectionEnd.line, lineLength(cm, selectionEnd.line)); } cm.setSelection(selectionStart, selectionEnd); } return [selectionStart, selectionEnd] ; } ; if (!vim.visualMode) { return getLastSelectedAreaRange(); } else { return getCurrentSelectedAreaRange(); } } function updateLastSelection(cm, vim){ var anchor = vim.sel.anchor; var head = vim.sel.head; if (vim.lastPastedText) { head = cm.posFromIndex(cm.indexFromPos(anchor) + _AN_Read_length('length', vim.lastPastedText)); vim.lastPastedText = null ; } vim.lastSelection = { 'anchorMark': cm.setBookmark(anchor), 'headMark': cm.setBookmark(head), 'anchor': copyCursor(anchor), 'head': copyCursor(head), 'visualMode': vim.visualMode, 'visualLine': vim.visualLine, 'visualBlock': vim.visualBlock} ; } function expandSelection(cm, start, end){ var sel = cm.state.vim.sel; var head = sel.head; var anchor = sel.anchor; var tmp; if (cursorIsBefore(end, start)) { tmp = end; end = start; start = tmp; } if (cursorIsBefore(head, anchor)) { head = cursorMin(start, head); anchor = cursorMax(anchor, end); } else { anchor = cursorMin(start, anchor); head = cursorMax(head, end); head = offsetCursor(head, 0, -1); if (head.ch == -1 && head.line != cm.firstLine()) { head = Pos(head.line - 1, lineLength(cm, head.line - 1)); } } return [anchor, head] ; } function updateCmSelection(cm, sel, mode){ var vim = cm.state.vim; sel = sel || vim.sel; var mode = mode || vim.visualLine? 'line': vim.visualBlock? 'block': 'char'; var cmSel = makeCmSelection(cm, sel, mode); cm.setSelections(cmSel.ranges, cmSel.primary); updateFakeCursor(cm); } function makeCmSelection(cm, sel, mode, exclusive){ var head = copyCursor(sel.head); var anchor = copyCursor(sel.anchor); if (mode == 'char') { var headOffset = !exclusive && !cursorIsBefore(sel.head, sel.anchor)? 1: 0; var anchorOffset = cursorIsBefore(sel.head, sel.anchor)? 1: 0; head = offsetCursor(sel.head, 0, headOffset); anchor = offsetCursor(sel.anchor, 0, anchorOffset); return { ranges: [{ anchor: anchor, head: head} ] , primary: 0} ; } else if (mode == 'line') { if (!cursorIsBefore(sel.head, sel.anchor)) { anchor.ch = 0; var lastLine = cm.lastLine(); if (head.line > lastLine) { head.line = lastLine; } head.ch = lineLength(cm, head.line); } else { head.ch = 0; anchor.ch = lineLength(cm, anchor.line); } return { ranges: [{ anchor: anchor, head: head} ] , primary: 0} ; } else if (mode == 'block') { var top = Math.min(anchor.line, head.line), left = Math.min(anchor.ch, head.ch), bottom = Math.max(anchor.line, head.line), right = Math.max(anchor.ch, head.ch) + 1; var height = bottom - top + 1; var primary = head.line == top? 0: height - 1; var ranges = [] ; for (var i = 0; i < height; i++ ){ ranges.push({ anchor: Pos(top + i, left), head: Pos(top + i, right)} ); } return { ranges: ranges, primary: primary} ; } } function getHead(cm){ var cur = cm.getCursor('head'); if (_AN_Read_length('length', cm.getSelection()) == 1) { cur = cursorMin(cur, cm.getCursor('anchor')); } return cur; } function exitVisualMode(cm, moveHead){ var vim = cm.state.vim; if (moveHead !== false ) { cm.setCursor(clipCursorToContent(cm, vim.sel.head)); } updateLastSelection(cm, vim); vim.visualMode = false ; vim.visualLine = false ; vim.visualBlock = false ; CodeMirror.signal(cm, "vim-mode-change", { mode: "normal"} ); if (vim.fakeCursor) { _AN_Call_clear("clear", vim.fakeCursor); } } 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 = getHead(cm); var line = cm.getLine(cur.line); var idx = cur.ch; var test = noSymbol? wordCharTest[0]: bigWordCharTest[0]; while (!test(line.charAt(idx))){ idx++ ; if (idx >= _AN_Read_length('length', line)) { return null ; } } if (bigWord) { test = bigWordCharTest[0]; } else { test = wordCharTest[0]; if (!test(line.charAt(idx))) { test = wordCharTest[1]; } } var end = idx, start = idx; while (test(line.charAt(end)) && end < _AN_Read_length('length', line)){ end++ ; } while (test(line.charAt(start)) && start >= 0){ start-- ; } start++ ; if (inclusive) { var wordEnd = end; while (/\s/.test(line.charAt(end)) && end < _AN_Read_length('length', line)){ end++ ; } if (wordEnd == end) { var wordStart = start; while (/\s/.test(line.charAt(start - 1)) && start > 0){ start-- ; } if (!start) { start = wordStart; } } } return { start: Pos(cur.line, start), end: Pos(cur.line, end)} ; } 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 = copyCursor(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 Pos(line, 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 charTests = bigWord? bigWordCharTest: wordCharTest; 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', charTests) && !foundWord; ++i){ if (charTests[i](line.charAt(pos))) { wordStart = pos; while (pos != stop && charTests[i](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, cur, repeat, forward, wordEnd, bigWord){ 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 = Pos(word.line, 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 Pos(lastWord.line, lastWord.from); } else if (forward && wordEnd) { return Pos(lastWord.line, lastWord.to - 1); } else if (!forward && wordEnd) { if (!shortCircuit && (firstWord.to != curStart.ch || firstWord.line != curStart.line)) { lastWord = words.pop(); } return Pos(lastWord.line, lastWord.to); } else { return Pos(lastWord.line, 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 Pos(cm.getCursor().line, idx); } function moveToColumn(cm, repeat){ var line = cm.getCursor().line; return clipCursorToContent(cm, Pos(line, 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 findParagraph(cm, head, repeat, dir, inclusive){ var line = head.line; var min = cm.firstLine(); var max = cm.lastLine(); var start, end, i = line; function isEmpty(i){ return !cm.getLine(i); } function isBoundary(i, dir, any){ if (any) { return isEmpty(i) != isEmpty(i + dir); } return !isEmpty(i) && isEmpty(i + dir); } if (dir) { while (min <= i && i <= max && repeat > 0){ if (isBoundary(i, dir)) { repeat-- ; } i += dir; } return new Pos(i, 0); } var vim = cm.state.vim; if (vim.visualLine && isBoundary(line, 1, true )) { var anchor = vim.sel.anchor; if (isBoundary(anchor.line, -1, true )) { if (!inclusive || anchor.line != line) { line += 1; } } } var startState = isEmpty(line); for (i = line; i <= max && repeat; i++ ){ if (isBoundary(i, 1, true )) { if (!inclusive || isEmpty(i) != startState) { repeat-- ; } } } end = new Pos(i, 0); if (i > max && !startState) { startState = true ; } else { inclusive = false ; } for (i = line; i > min; i-- ){ if (!inclusive || isEmpty(i) == startState || i == line) { if (isBoundary(i, -1, true )) { break ; } } } start = new Pos(i, 0); return { start: start, end: end} ; } function selectCompanionObject(cm, head, symb, inclusive){ var cur = head, start, end; var bracketRegexp = ({ '(': /[()]/, ')': /[()]/, '[': /[[\]]/, ']': /[[\]]/, '{': /[{}]/, '}': /[{}]/} )[symb]; var openSym = ({ '(': '(', ')': '(', '[': '[', ']': '[', '{': '{', '}': '{'} )[symb]; var curChar = cm.getLine(cur.line).charAt(cur.ch); var offset = curChar === openSym? 1: 0; start = cm.scanForBracket(Pos(cur.line, cur.ch + offset), -1, null , { 'bracketRegex': bracketRegexp} ); end = cm.scanForBracket(Pos(cur.line, cur.ch + offset), 1, null , { 'bracketRegex': bracketRegexp} ); if (!start || !end) { return { start: cur, end: cur} ; } start = start.pos; end = end.pos; if ((start.line == end.line && start.ch > end.ch) || (start.line > end.line)) { var tmp = start; start = end; end = tmp; } if (inclusive) { end.ch += 1; } else { start.ch += 1; } return { start: start, end: end} ; } function findBeginningAndEnd(cm, head, symb, inclusive){ var cur = copyCursor(head); 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: Pos(cur.line, start), end: Pos(cur.line, end)} ; } defineOption('pcre', true , 'boolean'); 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; } , getScrollbarAnnotate: function (){ return this.annotate; } , setScrollbarAnnotate: function (annotate){ this.annotate = annotate; } } ; 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, selectValueOnOpen: false } ); } else { onClose(prompt(shortText, '')); } } function splitBySlash(argString){ var slashes = findUnescapedSlashes(argString) || [] ; if (!_AN_Read_length('length', slashes)) return [] ; var tokens = [] ; if (slashes[0] !== 0) return ; for (var i = 0; i < _AN_Read_length('length', slashes); i++ ){ if (typeof slashes[i] == 'number') tokens.push(argString.substring(slashes[i] + 1, slashes[i + 1])); } return tokens; } 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 = !escapeNextChar && (c == '\\'); } return slashes; } function translateRegex(str){ var specials = '|(){'; var unescape = '}'; var escapeNextChar = false ; var out = [] ; for (var i = -1; i < _AN_Read_length('length', str); i++ ){ var c = str.charAt(i) || ''; var n = str.charAt(i + 1) || ''; var specialComesNext = (n && specials.indexOf(n) != -1); if (escapeNextChar) { if (c !== '\\' || !specialComesNext) { out.push(c); } escapeNextChar = false ; } else { if (c === '\\') { escapeNextChar = true ; if (n && unescape.indexOf(n) != -1) { specialComesNext = true ; } if (!specialComesNext || n === '\\') { out.push(c); } } else { out.push(c); if (specialComesNext && n !== '\\') { out.push('\\'); } } } } return out.join(''); } var charUnescapes = { '\\n': '\n', '\\r': '\r', '\\t': '\t'} ; function translateRegexReplace(str){ var escapeNextChar = false ; var out = [] ; for (var i = -1; i < _AN_Read_length('length', str); i++ ){ var c = str.charAt(i) || ''; var n = str.charAt(i + 1) || ''; if (charUnescapes[c + n]) { out.push(charUnescapes[c + n]); i++ ; } else if (escapeNextChar) { out.push(c); escapeNextChar = false ; } else { if (c === '\\') { escapeNextChar = true ; if ((isNumber(n) || n === '$')) { out.push('$'); } else if (n !== '/' && n !== '\\') { out.push('\\'); } } else { if (c === '$') { out.push('$'); } out.push(c); if (n === '/') { out.push('\\'); } } } } return out.join(''); } var unescapes = { '\\/': '/', '\\\\': '\\', '\\n': '\n', '\\r': '\r', '\\t': '\t'} ; function unescapeRegexReplace(str){ var stream = new CodeMirror.StringStream(str); var output = [] ; while (!stream.eol()){ while (stream.peek() && stream.peek() != '\\'){ output.push(stream.next()); } var matched = false ; for (var matcher in unescapes){ if (stream.match(matcher, true )) { matched = true ; output.push(unescapes[matcher]); break ; } } if (!matched) { output.push(stream.next()); } } return output.join(''); } function parseQuery(query, ignoreCase, smartCase){ var lastSearchRegister = vimGlobalState.registerController.getRegister('/'); lastSearchRegister.setText(query); 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 (!getOption('pcre')) { regexPart = translateRegex(regexPart); } 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 searchState = getSearchState(cm); var overlay = searchState.getOverlay(); if (!overlay || query != overlay.query) { if (overlay) { cm.removeOverlay(overlay); } overlay = searchOverlay(query); cm.addOverlay(overlay); if (cm.showMatchesOnScrollbar) { if (searchState.getScrollbarAnnotate()) { _AN_Call_clear('clear', searchState.getScrollbarAnnotate()); } searchState.setScrollbarAnnotate(cm.showMatchesOnScrollbar(query)); } searchState.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)? Pos(cm.lastLine()): Pos(cm.firstLine(), 0)); if (!cursor.find(prev)) { return ; } } } return cursor.from(); } ); } function clearSearchHighlight(cm){ var state = getSearchState(cm); cm.removeOverlay(getSearchState(cm).getOverlay()); state.setOverlay(null ); if (state.getScrollbarAnnotate()) { _AN_Call_clear('clear', state.getScrollbarAnnotate()); state.setScrollbarAnnotate(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 ExCommandDispatcher = function (){ this.buildCommandMap_(); } ; ExCommandDispatcher.prototype = { processCommand: function (cm, input, opt_params){ var that = this; cm.operation(function (){ cm.curOp.isVimOp = true ; that._processCommand(cm, input, opt_params); } ); } , _processCommand: function (cm, input, opt_params){ var vim = cm.state.vim; var commandHistoryRegister = vimGlobalState.registerController.getRegister(':'); var previousCommand = commandHistoryRegister.toString(); if (vim.visualMode) { exitVisualMode(cm); } var inputStream = new CodeMirror.StringStream(input); commandHistoryRegister.setText(input); var params = opt_params || { } ; params.input = input; try { this.parseInput_(cm, inputStream, params); } catch (e) { showConfirm(cm, e); throw e } var command; var commandName; if (!params.commandName) { if (params.line !== undefined) { commandName = 'move'; } } else { command = this.matchCommand_(params.commandName); if (command) { commandName = command.name; if (command.excludeFromCommandHistory) { commandHistoryRegister.setText(previousCommand); } 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], 'mapping'); } 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); if ((!command || !command.possiblyAsync) && params.callback) { params.callback(); } } 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, ctx){ if (lhs != ':' && lhs.charAt(0) == ':') { if (ctx) { throw Error('Mode not supported for ex mappings') } var commandName = lhs.substring(1); if (rhs != ':' && rhs.charAt(0) == ':') { this.commandMap_[commandName] = { name: commandName, type: 'exToEx', toInput: rhs.substring(1), user: true } ; } else { this.commandMap_[commandName] = { name: commandName, type: 'exToKey', toKeys: rhs, user: true } ; } } else { if (rhs != ':' && rhs.charAt(0) == ':') { var mapping = { keys: lhs, type: 'keyToEx', exArgs: { input: rhs.substring(1)} , user: true } ; if (ctx) { mapping.context = ctx; } defaultKeymap.unshift(mapping); } else { var mapping = { keys: lhs, type: 'keyToKey', toKeys: rhs, user: true } ; if (ctx) { mapping.context = ctx; } defaultKeymap.unshift(mapping); } } } , unmap: function (lhs, ctx){ if (lhs != ':' && lhs.charAt(0) == ':') { if (ctx) { throw Error('Mode not supported for ex mappings') } var commandName = lhs.substring(1); if (this.commandMap_[commandName] && this.commandMap_[commandName].user) { delete this.commandMap_[commandName]; return ; } } else { var keys = lhs; for (var i = 0; i < _AN_Read_length('length', defaultKeymap); i++ ){ if (keys == defaultKeymap[i].keys && defaultKeymap[i].context === ctx && defaultKeymap[i].user) { defaultKeymap.splice(i, 1); return ; } } } throw Error('No such mapping.') } } ; var exCommands = { colorscheme: function (cm, params){ if (!params.args || _AN_Read_length('length', params.args) < 1) { showConfirm(cm, cm.getOption('theme')); return ; } cm.setOption('theme', params.args[0]); } , map: function (cm, params, ctx){ 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], ctx); } , imap: function (cm, params){ this.map(cm, params, 'insert'); } , nmap: function (cm, params){ this.map(cm, params, 'normal'); } , vmap: function (cm, params){ this.map(cm, params, 'visual'); } , unmap: function (cm, params, ctx){ var mapArgs = params.args; if (!mapArgs || _AN_Read_length('length', mapArgs) < 1) { if (cm) { showConfirm(cm, 'No such mapping: ' + params.input); } return ; } exCommandDispatcher.unmap(mapArgs[0], ctx); } , 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} ); } , set: function (cm, params){ var setArgs = params.args; var setCfg = params.setCfg || { } ; if (!setArgs || _AN_Read_length('length', setArgs) < 1) { if (cm) { showConfirm(cm, 'Invalid mapping: ' + params.input); } return ; } var expr = setArgs[0].split('='); var optionName = expr[0]; var value = expr[1]; var forceGet = false ; if (optionName.charAt(_AN_Read_length('length', optionName) - 1) == '?') { if (value) { throw Error('Trailing characters: ' + params.argString) } optionName = optionName.substring(0, _AN_Read_length('length', optionName) - 1); forceGet = true ; } if (value === undefined && optionName.substring(0, 2) == 'no') { optionName = optionName.substring(2); value = false ; } var optionIsBoolean = options[optionName] && options[optionName].type == 'boolean'; if (optionIsBoolean && value == undefined) { value = true ; } if (!optionIsBoolean && value === undefined || forceGet) { var oldValue = getOption(optionName, cm, setCfg); if (oldValue === true || oldValue === false ) { showConfirm(cm, ' ' + (oldValue? '': 'no') + optionName); } else { showConfirm(cm, ' ' + optionName + '=' + oldValue); } } else { setOption(optionName, value, cm, setCfg); } } , setlocal: function (cm, params){ params.setCfg = { scope: 'local'} ; this.set(cm, params); } , setglobal: function (cm, params){ params.setCfg = { scope: 'global'} ; this.set(cm, params); } , registers: function (cm, params){ var regArgs = params.args; var registers = vimGlobalState.registerController.registers; var regInfo = '----------Registers----------

'; if (!regArgs) { for (var registerName in registers){ var text = registers[registerName].toString(); if (text.length) { regInfo += '"' + registerName + ' ' + text + '
'; } } } else { var registerName; regArgs = regArgs.join(''); for (var i = 0; i < _AN_Read_length('length', regArgs); i++ ){ registerName = regArgs.charAt(i); if (!vimGlobalState.registerController.isValidRegister(registerName)) { continue ; } var register = registers[registerName] || new Register(); regInfo += '"' + registerName + ' ' + register.toString() + '
'; } } showConfirm(cm, regInfo); } , 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.match(/\/.*\//)) { return '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 = Pos(lineStart, 0); var curEnd = Pos(lineEnd, 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); } , global: function (cm, params){ var argString = params.argString; if (!argString) { showConfirm(cm, 'Regular Expression missing from global'); return ; } var lineStart = (params.line !== undefined)? params.line: cm.firstLine(); var lineEnd = params.lineEnd || params.line || cm.lastLine(); var tokens = splitBySlash(argString); var regexPart = argString, cmd; if (tokens.length) { regexPart = tokens[0]; cmd = tokens.slice(1, _AN_Read_length('length', tokens)).join('/'); } if (regexPart) { try { updateSearchQuery(cm, regexPart, true , true ); } catch (e) { showConfirm(cm, 'Invalid regex: ' + regexPart); return ; } } var query = getSearchState(cm).getQuery(); var matchedLines = [] , content = ''; for (var i = lineStart; i <= lineEnd; i++ ){ var matched = query.test(cm.getLine(i)); if (matched) { matchedLines.push(i + 1); content += cm.getLine(i) + '
'; } } if (!cmd) { showConfirm(cm, content); return ; } var index = 0; var nextCommand = function (){ if (index < _AN_Read_length('length', matchedLines)) { var command = matchedLines[index] + cmd; exCommandDispatcher.processCommand(cm, command, { callback: nextCommand} ); } index++ ; } ; nextCommand(); } , 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 tokens = argString? splitBySlash(argString): [] ; var regexPart, replacePart = '', trailing, flagsPart, count; var confirm = false ; var global = false ; if (tokens.length) { regexPart = tokens[0]; replacePart = tokens[1]; if (replacePart !== undefined) { if (getOption('pcre')) { replacePart = unescapeRegexReplace(replacePart); } else { replacePart = translateRegexReplace(replacePart); } vimGlobalState.lastSubstituteReplacePart = replacePart; } trailing = tokens[2]? tokens[2].split(' '): [] ; } else { if (argString && _AN_Read_length('length', argString)) { showConfirm(cm, 'Substitutions should be of the form ' + ':s/pattern/replace/'); return ; } } if (trailing) { flagsPart = trailing[0]; count = parseInt(trailing[1]); if (flagsPart) { if (flagsPart.indexOf('c') != -1) { confirm = true ; _AN_Call_replace('replace', flagsPart, 'c', ''); } if (flagsPart.indexOf('g') != -1) { global = true ; _AN_Call_replace('replace', flagsPart, 'g', ''); } regexPart = regexPart + '/' + flagsPart; } } if (regexPart) { try { updateSearchQuery(cm, regexPart, true , true ); } catch (e) { showConfirm(cm, 'Invalid regex: ' + regexPart); return ; } } replacePart = replacePart || vimGlobalState.lastSubstituteReplacePart; if (replacePart === undefined) { showConfirm(cm, 'No previous substitute regular expression'); 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 (lineStart == cm.firstLine() && lineEnd == cm.lastLine()) { lineEnd = Infinity; } if (count) { lineStart = lineEnd; lineEnd = lineStart + count - 1; } var startPos = clipCursorToContent(cm, Pos(lineStart, 0)); var cursor = cm.getSearchCursor(query, startPos); doReplace(cm, confirm, global, lineStart, lineEnd, cursor, query, replacePart, params.callback); } , 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 ExCommandDispatcher(); function doReplace(cm, confirm, global, lineStart, lineEnd, searchCursor, query, replaceWith, callback){ 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(){ while (searchCursor.findNext() && isInRange(searchCursor.from(), lineStart, lineEnd)){ if (!global && lastPos && searchCursor.from().line == lastPos.line) { continue ; } cm.scrollIntoView(searchCursor.from(), 30); cm.setSelection(searchCursor.from(), searchCursor.to()); lastPos = searchCursor.from(); done = false ; return ; } 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; } if (callback) { callback(); } } 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': var savedCallback = callback; callback = undefined; cm.operation(replaceAll); callback = savedCallback; break ; case 'L': replace(); case 'Q': case 'Esc': case 'Ctrl-C': case 'Ctrl-[': stop(close); break ; } if (done) { stop(close); } return true ; } next(); if (done) { showConfirm(cm, 'No matches for ' + query.source); return ; } if (!confirm) { replaceAll(); if (callback) { callback(); } ; return ; } showPrompt(cm, { prefix: 'replace with ' + replaceWith + ' (y/n/a/q/l)', onKeyDown: onPromptKeyDown} ); } CodeMirror.keyMap.vim = { attach: attachVimMap, detach: detachVimMap, call: cmKey} ; function exitInsertMode(cm){ var vim = cm.state.vim; var macroModeState = vimGlobalState.macroModeState; var insertModeChangeRegister = vimGlobalState.registerController.getRegister('.'); var isPlaying = macroModeState.isPlaying; var lastChange = macroModeState.lastInsertModeChanges; var text = [] ; if (!isPlaying) { var selLength = lastChange.inVisualBlock? vim.lastSelection.visualBlock.height: 1; var changes = lastChange.changes; var text = [] ; var i = 0; while (i < _AN_Read_length('length', changes)){ text.push(changes[i]); if (changes[i] instanceof InsertModeKey) { i++ ; } else { i += selLength; } } lastChange.changes = text; cm.off('change', onChange); CodeMirror.off(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown); } if (!isPlaying && vim.insertModeRepeat > 1) { repeatLastEdit(cm, vim, vim.insertModeRepeat - 1, true ); vim.lastEditInputState.repeatOverride = vim.insertModeRepeat; } delete vim.insertModeRepeat; vim.insertMode = false ; cm.setCursor(cm.getCursor().line, cm.getCursor().ch - 1); cm.setOption('keyMap', 'vim'); cm.setOption('disableInput', true ); cm.toggleOverwrite(false ); insertModeChangeRegister.setText(lastChange.changes.join('')); CodeMirror.signal(cm, "vim-mode-change", { mode: "normal"} ); if (macroModeState.isRecording) { logInsertModeChange(macroModeState); } } function _mapCommand(command){ defaultKeymap.unshift(command); } function mapCommand(keys, type, name, args, extra){ var command = { keys: keys, type: type} ; command[type] = name; command[type + "Args"] = args; for (var key in extra)command[key] = extra[key]; _mapCommand(command); } defineOption('insertModeEscKeysTimeout', 200, 'number'); CodeMirror.keyMap["vim-insert"] = { 'Ctrl-N': 'autocomplete', 'Ctrl-P': 'autocomplete', 'Enter': function (cm){ var fn = CodeMirror.commands.newlineAndIndentContinueComment || CodeMirror.commands.newlineAndIndent; fn(cm); } , fallthrough: ['default'] , attach: attachVimMap, detach: detachVimMap, call: cmKey} ; CodeMirror.keyMap["vim-replace"] = { 'Backspace': 'goCharLeft', fallthrough: ['vim-insert'] , attach: attachVimMap, detach: detachVimMap, call: cmKey} ; function executeMacroRegister(cm, vim, macroModeState, registerName){ var register = vimGlobalState.registerController.getRegister(registerName); if (registerName == ':') { if (register.keyBuffer[0]) { exCommandDispatcher.processCommand(cm, register.keyBuffer[0]); } macroModeState.isPlaying = false ; return ; } var keyBuffer = register.keyBuffer; var imc = 0; macroModeState.isPlaying = true ; macroModeState.replaySearchQueries = register.searchQueries.slice(0); for (var i = 0; i < _AN_Read_length('length', keyBuffer); i++ ){ var text = keyBuffer[i]; var match, key; while (text){ match = (/<\w+-.+?>|<\w+>|./).exec(text); key = match[0]; text = text.substring(match.index + _AN_Read_length('length', key)); CodeMirror.Vim.handleKey(cm, key, 'macro'); if (vim.insertMode) { var changes = register.insertModeChanges[imc++ ].changes; vimGlobalState.macroModeState.lastInsertModeChanges.changes = changes; repeatInsertModeChanges(cm, changes, 1); exitInsertMode(cm); } } } ; macroModeState.isPlaying = false ; } function logKey(macroModeState, key){ if (macroModeState.isPlaying) { return ; } var registerName = macroModeState.latestRegister; var register = vimGlobalState.registerController.getRegister(registerName); if (register) { register.pushText(key); } } function logInsertModeChange(macroModeState){ if (macroModeState.isPlaying) { return ; } var registerName = macroModeState.latestRegister; var register = vimGlobalState.registerController.getRegister(registerName); if (register && register.pushInsertModeChanges) { register.pushInsertModeChanges(macroModeState.lastInsertModeChanges); } } function logSearchQuery(macroModeState, query){ if (macroModeState.isPlaying) { return ; } var registerName = macroModeState.latestRegister; var register = vimGlobalState.registerController.getRegister(registerName); if (register && register.pushSearchQuery) { register.pushSearchQuery(query); } } function onChange(_cm, changeObj){ var macroModeState = vimGlobalState.macroModeState; var lastChange = macroModeState.lastInsertModeChanges; if (!macroModeState.isPlaying) { 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(cm){ var vim = cm.state.vim; if (vim.insertMode) { var macroModeState = vimGlobalState.macroModeState; if (macroModeState.isPlaying) { return ; } var lastChange = macroModeState.lastInsertModeChanges; if (lastChange.expectCursorActivityForChange) { lastChange.expectCursorActivityForChange = false ; } else { lastChange.changes = [] ; } } else if (!cm.curOp.isVimOp) { handleExternalSelection(cm, vim); } if (vim.visualMode) { updateFakeCursor(cm); } } function updateFakeCursor(cm){ var vim = cm.state.vim; var from = clipCursorToContent(cm, copyCursor(vim.sel.head)); var to = offsetCursor(from, 0, 1); if (vim.fakeCursor) { _AN_Call_clear('clear', vim.fakeCursor); } vim.fakeCursor = cm.markText(from, to, { className: 'cm-animate-fat-cursor'} ); } function handleExternalSelection(cm, vim){ var anchor = cm.getCursor('anchor'); var head = cm.getCursor('head'); if (vim.visualMode && !cm.somethingSelected()) { exitVisualMode(cm, false ); } else if (!vim.visualMode && !vim.insertMode && cm.somethingSelected()) { vim.visualMode = true ; vim.visualLine = false ; CodeMirror.signal(cm, "vim-mode-change", { mode: "visual"} ); } if (vim.visualMode) { var headOffset = !cursorIsBefore(head, anchor)? -1: 0; var anchorOffset = cursorIsBefore(head, anchor)? -1: 0; head = offsetCursor(head, 0, headOffset); anchor = offsetCursor(anchor, 0, anchorOffset); vim.sel = { anchor: anchor, head: head} ; updateMark(cm, vim, '<', cursorMin(head, anchor)); updateMark(cm, vim, '>', cursorMax(head, anchor)); } else if (!vim.insertMode) { vim.lastHPos = cm.getCursor().ch; } } function InsertModeKey(keyName){ this.keyName = keyName; } function onKeyEventTargetKeyDown(e){ var macroModeState = vimGlobalState.macroModeState; var lastChange = macroModeState.lastInsertModeChanges; var keyName = CodeMirror.keyName(e); if (!keyName) { return ; } 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.isPlaying = 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; var changeObject = macroModeState.lastInsertModeChanges; repeatInsertModeChanges(cm, changeObject.changes, repeat); } } 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.isPlaying = false ; } ; function repeatInsertModeChanges(cm, changes, repeat){ function keyHandler(binding){ if (typeof binding == 'string') { CodeMirror.commands[binding](cm); } else { binding(cm); } return true ; } var head = cm.getCursor('head'); var inVisualBlock = vimGlobalState.macroModeState.lastInsertModeChanges.inVisualBlock; if (inVisualBlock) { var vim = cm.state.vim; var lastSel = vim.lastSelection; var offset = getOffset(lastSel.anchor, lastSel.head); selectForInsert(cm, head, offset.line + 1); repeat = _AN_Read_length('length', cm.listSelections()); cm.setCursor(head); } for (var i = 0; i < repeat; i++ ){ if (inVisualBlock) { cm.setCursor(offsetCursor(head, i, 0)); } for (var j = 0; j < _AN_Read_length('length', changes); j++ ){ var change = changes[j]; if (change instanceof InsertModeKey) { CodeMirror.lookupKey(change.keyName, 'vim-insert', keyHandler); } else { var cur = cm.getCursor(); cm.replaceRange(change, cur, cur); } } } if (inVisualBlock) { cm.setCursor(offsetCursor(head, 0, 1)); } } resetVimGlobalState(); return vimApi; } ; CodeMirror.Vim = Vim(); } );