import { cursorCharBackward, cursorCharLeft, cursorLineBoundaryBackward, cursorLineBoundaryForward, indentLess, indentMore, indentSelection, insertNewlineAndIndent, redo, undo } from "./chunk-TRAAS4SM.js"; import { StringStream, ensureSyntaxTree, foldCode, indentUnit, matchBrackets } from "./chunk-D63OSQ34.js"; import { RegExpCursor, SearchQuery, setSearchQuery } from "./chunk-JOEDLCIZ.js"; import "./chunk-B45MRPPJ.js"; import { Decoration, Direction, EditorView, ViewPlugin, runScopeHandlers, showPanel } from "./chunk-LORPBXGU.js"; import { EditorSelection, MapMode, Prec, RangeSetBuilder, StateEffect, StateField } from "./chunk-MKFMOIK6.js"; import "./chunk-BSVZPYOD.js"; // node_modules/@replit/codemirror-vim/dist/index.js function initVim(CodeMirror2) { var Pos2 = CodeMirror2.Pos; function transformCursor(cm, range) { var vim2 = cm.state.vim; if (!vim2 || vim2.insertMode) return range.head; var head = vim2.sel.head; if (!head) return range.head; if (vim2.visualBlock) { if (range.head.line != head.line) { return; } } if (range.from() == range.anchor && !range.empty()) { if (range.head.line == head.line && range.head.ch != head.ch) return new Pos2(range.head.line, range.head.ch - 1); } return range.head; } function updateSelectionForSurrogateCharacters(cm, curStart, curEnd) { if (curStart.line === curEnd.line && curStart.ch >= curEnd.ch - 1) { var text = cm.getLine(curStart.line); var charCode = text.charCodeAt(curStart.ch); if (55296 <= charCode && charCode <= 55551) { curEnd.ch += 1; } } return { start: curStart, end: curEnd }; } var defaultKeymap = [ // Key to key mapping. This goes first to make it possible to override // existing mappings. { keys: "", type: "keyToKey", toKeys: "h" }, { keys: "", type: "keyToKey", toKeys: "l" }, { keys: "", type: "keyToKey", toKeys: "k" }, { keys: "", type: "keyToKey", toKeys: "j" }, { keys: "g", type: "keyToKey", toKeys: "gk" }, { keys: "g", type: "keyToKey", toKeys: "gj" }, { keys: "", type: "keyToKey", toKeys: "l" }, { keys: "", type: "keyToKey", toKeys: "h", context: "normal" }, { keys: "", type: "keyToKey", toKeys: "x", 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: "", type: "keyToKey", toKeys: "" }, // ipad keyboard sends C-Esc instead of C-[ { 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: "", type: "keyToKey", toKeys: "i", context: "normal" }, { keys: "", type: "action", action: "toggleOverwrite", context: "insert" }, // Motions { 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: "moveBySentence", motionArgs: { forward: false } }, { keys: ")", type: "motion", motion: "moveBySentence", motionArgs: { forward: 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: "g$", type: "motion", motion: "moveToEndOfDisplayLine" }, { keys: "g^", type: "motion", motion: "moveToStartOfDisplayLine" }, { keys: "g0", type: "motion", motion: "moveToStartOfDisplayLine" }, { 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 } }, // the next two aren't motions but must come before more general motion declarations { 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" }, // Operators { keys: "d", type: "operator", operator: "delete" }, { keys: "y", type: "operator", operator: "yank" }, { keys: "c", type: "operator", operator: "change" }, { keys: "=", type: "operator", operator: "indentAuto" }, { 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: "gn", type: "motion", motion: "findAndSelectNextInclusive", motionArgs: { forward: true } }, { keys: "gN", type: "motion", motion: "findAndSelectNextInclusive", motionArgs: { forward: false } }, // Operator-Motion dual commands { 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: "expandToLine", motionArgs: { linewise: 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: "moveToStartOfLine", context: "insert" }, { keys: "", type: "operatorMotion", operator: "delete", motion: "moveByWords", motionArgs: { forward: false, wordEnd: false }, context: "insert" }, //ignore C-w in normal mode { keys: "", type: "idle", context: "normal" }, // Actions { 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: "gi", type: "action", action: "enterInsertMode", isEdit: true, actionArgs: { insertAt: "lastEdit" }, context: "normal" }, { keys: "I", type: "action", action: "enterInsertMode", isEdit: true, actionArgs: { insertAt: "firstNonBlank" }, context: "normal" }, { keys: "gI", type: "action", action: "enterInsertMode", isEdit: true, actionArgs: { insertAt: "bol" }, 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: "gJ", type: "action", action: "joinLines", actionArgs: { keepSpaces: true }, 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" }, // Handle Replace-mode as a special case of insert mode. { keys: "R", type: "action", action: "enterInsertMode", isEdit: true, actionArgs: { replace: true }, context: "normal" }, { keys: "R", type: "operator", operator: "change", operatorArgs: { linewise: true, fullLine: true }, context: "visual", exitVisualBlock: 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: "", type: "action", action: "insertRegister", context: "insert", isEdit: true }, { keys: "", type: "action", action: "oneNormalCommand", context: "insert" }, { 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: "zb", type: "action", action: "scrollToCursor", actionArgs: { position: "bottom" } }, { keys: "z-", 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: "", type: "action", action: "indent", actionArgs: { indentRight: true }, context: "insert" }, { keys: "", type: "action", action: "indent", actionArgs: { indentRight: false }, context: "insert" }, // Text object motions { keys: "a", type: "motion", motion: "textObjectManipulation" }, { keys: "i", type: "motion", motion: "textObjectManipulation", motionArgs: { textObjectInner: true } }, // Search { 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 } }, // Ex command { keys: ":", type: "ex" } ]; var defaultKeymapLength = defaultKeymap.length; var defaultExCommandMap = [ { name: "colorscheme", shortName: "colo" }, { name: "map" }, { name: "imap", shortName: "im" }, { name: "nmap", shortName: "nm" }, { name: "vmap", shortName: "vm" }, { name: "omap", shortName: "om" }, { name: "noremap", shortName: "no" }, { name: "nnoremap", shortName: "nn" }, { name: "vnoremap", shortName: "vn" }, { name: "inoremap", shortName: "ino" }, { name: "onoremap", shortName: "ono" }, { name: "unmap" }, { name: "mapclear", shortName: "mapc" }, { name: "nmapclear", shortName: "nmapc" }, { name: "vmapclear", shortName: "vmapc" }, { name: "imapclear", shortName: "imapc" }, { name: "omapclear", shortName: "omapc" }, { name: "write", shortName: "w" }, { name: "undo", shortName: "u" }, { name: "redo", shortName: "red" }, { 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: "yank", shortName: "y" }, { name: "delmarks", shortName: "delm" }, { name: "registers", shortName: "reg", excludeFromCommandHistory: true }, { name: "vglobal", shortName: "v" }, { name: "delete", shortName: "d" }, { name: "join", shortName: "j" }, { name: "normal", shortName: "norm" }, { name: "global", shortName: "g" } ]; function enterVimMode(cm) { cm.setOption("disableInput", true); cm.setOption("showCursorWhenSelecting", false); CodeMirror2.signal(cm, "vim-mode-change", { mode: "normal" }); cm.on("cursorActivity", onCursorActivity); maybeInitVimState(cm); CodeMirror2.on(cm.getInputField(), "paste", getOnPasteFn(cm)); } function leaveVimMode(cm) { cm.setOption("disableInput", false); cm.off("cursorActivity", onCursorActivity); CodeMirror2.off(cm.getInputField(), "paste", getOnPasteFn(cm)); cm.state.vim = null; if (highlightTimeout) clearTimeout(highlightTimeout); } function detachVimMap(cm, next) { if (this == CodeMirror2.keyMap.vim) { cm.options.$customCursor = null; CodeMirror2.rmClass(cm.getWrapperElement(), "cm-fat-cursor"); } if (!next || next.attach != attachVimMap) leaveVimMode(cm); } function attachVimMap(cm, prev) { if (this == CodeMirror2.keyMap.vim) { if (cm.curOp) cm.curOp.selectionChanged = true; cm.options.$customCursor = transformCursor; CodeMirror2.addClass(cm.getWrapperElement(), "cm-fat-cursor"); } if (!prev || prev.attach != attachVimMap) enterVimMode(cm); } CodeMirror2.defineOption("vimMode", false, function(cm, val, prev) { if (val && cm.getOption("keyMap") != "vim") cm.setOption("keyMap", "vim"); else if (!val && prev != CodeMirror2.Init && /^vim/.test(cm.getOption("keyMap"))) cm.setOption("keyMap", "default"); }); function cmKey(key, cm) { if (!cm) { return void 0; } if (this[key]) { return this[key]; } var vimKey2 = cmKeyToVimKey(key); if (!vimKey2) { return false; } var cmd = vimApi.findKey(cm, vimKey2); if (typeof cmd == "function") { CodeMirror2.signal(cm, "vim-keypress", vimKey2); } return cmd; } var modifiers = { Shift: "S", Ctrl: "C", Alt: "A", Cmd: "D", Mod: "A", CapsLock: "" }; var specialKeys = { Enter: "CR", Backspace: "BS", Delete: "Del", Insert: "Ins" }; var vimToCmKeyMap = {}; "Left|Right|Up|Down|End|Home".split("|").concat(Object.keys(specialKeys)).forEach(function(x) { vimToCmKeyMap[(specialKeys[x] || "").toLowerCase()] = vimToCmKeyMap[x.toLowerCase()] = x; }); function cmKeyToVimKey(key) { if (key.charAt(0) == "'") { return key.charAt(1); } var pieces = key.split(/-(?!$)/); var lastPiece = pieces[pieces.length - 1]; if (pieces.length == 1 && pieces[0].length == 1) { return false; } else if (pieces.length == 2 && pieces[0] == "Shift" && lastPiece.length == 1) { return false; } var hasCharacter = false; for (var i = 0; i < pieces.length; 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[pieces.length - 1] = lastPiece.toLowerCase(); } return "<" + pieces.join("-") + ">"; } function getOnPasteFn(cm) { var vim2 = cm.state.vim; if (!vim2.onPasteFn) { vim2.onPasteFn = function() { if (!vim2.insertMode) { cm.setCursor(offsetCursor(cm.getCursor(), 0, 1)); actions.enterInsertMode(cm, {}, vim2); } }; } return vim2.onPasteFn; } var numberRegex = /[\d]/; var wordCharTest = [CodeMirror2.isWordChar, function(ch) { return ch && !CodeMirror2.isWordChar(ch) && !/\s/.test(ch); }], bigWordCharTest = [function(ch) { return /\S/.test(ch); }]; function makeKeyRange(start, size) { var keys2 = []; for (var i = start; i < start + size; i++) { keys2.push(String.fromCharCode(i)); } return keys2; } 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, ["-", '"', ".", ":", "_", "/", "+"]); var upperCaseChars; try { upperCaseChars = new RegExp("^[\\p{Lu}]$", "u"); } catch (_) { upperCaseChars = /^[A-Z]$/; } 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 upperCaseChars.test(k); } function isWhiteSpaceString(k) { return /^\s*$/.test(k); } function isEndOfSentenceSymbol(k) { return ".?!".indexOf(k) != -1; } function inArray(val, arr) { for (var i = 0; i < arr.length; i++) { if (arr[i] == val) { return true; } } return false; } var options = {}; function defineOption(name, defaultValue, type, aliases, callback) { if (defaultValue === void 0 && !callback) { throw Error("defaultValue is required unless callback is provided"); } if (!type) { type = "string"; } options[name] = { type, defaultValue, callback }; if (aliases) { for (var i = 0; i < aliases.length; 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) { return new Error("Unknown option: " + name); } if (option.type == "boolean") { if (value && value !== true) { return new Error("Invalid argument: " + name + "=" + value); } else if (value !== false) { value = true; } } if (option.callback) { if (scope !== "local") { option.callback(value, void 0); } 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 }; } } } function getOption(name, cm, cfg) { var option = options[name]; cfg = cfg || {}; var scope = cfg.scope; if (!option) { return new Error("Unknown option: " + name); } if (option.callback) { var local = cm && option.callback(void 0, cm); if (scope !== "global" && local !== void 0) { 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", void 0, "string", ["ft"], function(name, cm) { if (cm === void 0) { return; } if (name === void 0) { 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) { trashMark.clear(); } 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; } function find(cm, offset) { var oldPointer = pointer; var mark = move(cm, offset); pointer = oldPointer; return mark && mark.find(); } return { cachedCursor: void 0, //used for # and * jumps add, find, move }; }; var createInsertModeChanges = function(c) { if (c) { return { changes: c.changes, expectCursorActivityForChange: c.expectCursorActivityForChange }; } return { // Change list changes: [], // Set to true on change, false on cursorActivity. expectCursorActivityForChange: false }; }; function MacroModeState() { this.latestRegister = void 0; this.isPlaying = false; this.isRecording = false; this.replaySearchQueries = []; this.onRecordingDone = void 0; this.lastInsertModeChanges = createInsertModeChanges(); } MacroModeState.prototype = { exitMacroRecordMode: function() { var macroModeState = vimGlobalState.macroModeState; if (macroModeState.onRecordingDone) { macroModeState.onRecordingDone(); } macroModeState.onRecordingDone = void 0; macroModeState.isRecording = false; }, enterMacroRecordMode: function(cm, registerName) { var register = vimGlobalState.registerController.getRegister(registerName); if (register) { register.clear(); this.latestRegister = registerName; if (cm.openDialog) { var template = dom("span", { class: "cm-vim-message" }, "recording @" + registerName); this.onRecordingDone = cm.openDialog(template, null, { bottom: true }); } this.isRecording = true; } } }; function maybeInitVimState(cm) { if (!cm.state.vim) { cm.state.vim = { inputState: new InputState(), // Vim's input state that triggered the last edit, used to repeat // motions and operators with '.'. lastEditInputState: void 0, // Vim's action command before the last edit, used to repeat actions // with '.' and insert mode repeat. lastEditActionCommand: void 0, // When using jk for navigation, if you move from a longer line to a // shorter line, the cursor may clip to the end of the shorter line. // If j is pressed again and cursor goes to the next line, the // cursor should go back to its horizontal position on the longer // line if it can. This is to keep track of the horizontal position. lastHPos: -1, // Doing the same with screen-position for gj/gk lastHSPos: -1, // The last motion command run. Cleared if a non-motion command gets // executed in between. lastMotion: null, marks: {}, insertMode: false, insertModeReturn: false, // Repeat count for changes made in insert mode, triggered by key // sequences like 3,i. Only exists when insertMode is true. insertModeRepeat: void 0, visualMode: false, // If we are in visual line mode. No effect if visualMode is false. visualLine: false, visualBlock: false, lastSelection: null, lastPastedText: null, sel: {}, // Buffer-local/window-local values of vim options. options: {} }; } return cm.state.vim; } var vimGlobalState; function resetVimGlobalState() { vimGlobalState = { // The current search query. searchQuery: null, // Whether we are searching backwards. searchIsReversed: false, // Replace part of the last substituted pattern lastSubstituteReplacePart: void 0, jumpList: createCircularJumpList(), macroModeState: new MacroModeState(), // Recording latest f, t, F or T motion command. lastCharacterSearch: { increment: 0, forward: true, selectedCharacter: "" }, registerController: new RegisterController({}), // search history buffer searchHistoryController: new HistoryController(), // ex Command history buffer exCommandHistoryController: new HistoryController() }; for (var optionName in options) { var option = options[optionName]; option.value = option.defaultValue; } } var lastInsertModeKeyTimer; var vimApi = { enterVimMode, leaveVimMode, buildKeyMap: function() { }, // Testing hook, though it might be useful to expose the register // controller anyway. getRegisterController: function() { return vimGlobalState.registerController; }, // Testing hook. resetVimGlobalState_: resetVimGlobalState, // Testing hook. getVimGlobalState_: function() { return vimGlobalState; }, // Testing hook. maybeInitVimState_: maybeInitVimState, suppressErrorLogging: false, InsertModeKey, map: function(lhs, rhs, ctx) { exCommandDispatcher.map(lhs, rhs, ctx); }, unmap: function(lhs, ctx) { return exCommandDispatcher.unmap(lhs, ctx); }, // Non-recursive map function. // NOTE: This will not create mappings to key maps that aren't present // in the default key map. See TODO at bottom of function. noremap: function(lhs, rhs, ctx) { exCommandDispatcher.map(lhs, rhs, ctx, true); }, // Remove all user-defined mappings for the provided context. mapclear: function(ctx) { var actualLength = defaultKeymap.length, origLength = defaultKeymapLength; var userKeymap = defaultKeymap.slice(0, actualLength - origLength); defaultKeymap = defaultKeymap.slice(actualLength - origLength); if (ctx) { for (var i = userKeymap.length - 1; i >= 0; i--) { var mapping = userKeymap[i]; if (ctx !== mapping.context) { if (mapping.context) { this._mapCommand(mapping); } else { var contexts = ["normal", "insert", "visual"]; for (var j in contexts) { if (contexts[j] !== ctx) { var newMapping = {}; for (var key in mapping) { newMapping[key] = mapping[key]; } newMapping.context = contexts[j]; this._mapCommand(newMapping); } } } } } } }, // TODO: Expose setOption and getOption as instance methods. Need to decide how to namespace // them, or somehow make them work with the existing CodeMirror setOption/getOption API. setOption, getOption, 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, shortName: prefix, type: "api" }; }, handleKey: function(cm, key, origin) { var command = this.findKey(cm, key, origin); if (typeof command === "function") { return command(); } }, multiSelectHandleKey, /** * This is the outermost function called by CodeMirror, after keys have * been mapped to their Vim equivalents. * * Finds a command based on the key (and cached keys if there is a * multi-key sequence). Returns `undefined` if no key is matched, a noop * function if a partial match is found (multi-key), and a function to * execute the bound command if a a key is matched. The function always * returns true. */ findKey: function(cm, key, origin) { var vim2 = 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 == "") { if (vim2.visualMode) { exitVisualMode(cm); } else if (vim2.insertMode) { exitInsertMode(cm); } else { return; } clearInputState(cm); return true; } } function handleKeyInsertMode() { if (handleEsc()) { return true; } vim2.inputState.keyBuffer.push(key); var keys2 = vim2.inputState.keyBuffer.join(""); var keysAreChars = key.length == 1; var match = commandDispatcher.matchCommand(keys2, defaultKeymap, vim2.inputState, "insert"); var changeQueue = vim2.inputState.changeQueue; if (match.type == "none") { clearInputState(cm); return false; } else if (match.type == "partial") { if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); } lastInsertModeKeyTimer = keysAreChars && window.setTimeout( function() { if (vim2.insertMode && vim2.inputState.keyBuffer.length) { clearInputState(cm); } }, getOption("insertModeEscKeysTimeout") ); if (keysAreChars) { var selections = cm.listSelections(); if (!changeQueue || changeQueue.removed.length != selections.length) changeQueue = vim2.inputState.changeQueue = new ChangeQueue(); changeQueue.inserted += key; for (var i = 0; i < selections.length; i++) { var from = cursorMin(selections[i].anchor, selections[i].head); var to = cursorMax(selections[i].anchor, selections[i].head); var text = cm.getRange(from, cm.state.overwrite ? offsetCursor(to, 0, 1) : to); changeQueue.removed[i] = (changeQueue.removed[i] || "") + text; } } return !keysAreChars; } if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); } if (match.command && changeQueue) { var selections = cm.listSelections(); for (var i = 0; i < selections.length; i++) { var here = selections[i].head; cm.replaceRange( changeQueue.removed[i] || "", offsetCursor(here, 0, -changeQueue.inserted.length), here, "+input" ); } vimGlobalState.macroModeState.lastInsertModeChanges.changes.pop(); } if (!match.command) clearInputState(cm); return match.command; } function handleKeyNonInsertMode() { if (handleMacroRecording() || handleEsc()) { return true; } vim2.inputState.keyBuffer.push(key); var keys2 = vim2.inputState.keyBuffer.join(""); if (/^[1-9]\d*$/.test(keys2)) { return true; } var keysMatcher = /^(\d*)(.*)$/.exec(keys2); if (!keysMatcher) { clearInputState(cm); return false; } var context = vim2.visualMode ? "visual" : "normal"; var mainKey = keysMatcher[2] || keysMatcher[1]; if (vim2.inputState.operatorShortcut && vim2.inputState.operatorShortcut.slice(-1) == mainKey) { mainKey = vim2.inputState.operatorShortcut; } var match = commandDispatcher.matchCommand(mainKey, defaultKeymap, vim2.inputState, context); if (match.type == "none") { clearInputState(cm); return false; } else if (match.type == "partial") { return true; } else if (match.type == "clear") { clearInputState(cm); return true; } vim2.inputState.keyBuffer.length = 0; keysMatcher = /^(\d*)(.*)$/.exec(keys2); if (keysMatcher[1] && keysMatcher[1] != "0") { vim2.inputState.pushRepeatDigit(keysMatcher[1]); } return match.command; } var command; if (vim2.insertMode) { command = handleKeyInsertMode(); } else { command = handleKeyNonInsertMode(); } if (command === false) { return !vim2.insertMode && key.length === 1 ? function() { return true; } : void 0; } else if (command === true) { return function() { return true; }; } else { return function() { return cm.operation(function() { cm.curOp.isVimOp = true; try { if (command.type == "keyToKey") { doKeyToKey(cm, command.toKeys, command); } else { commandDispatcher.processCommand(cm, vim2, command); } } catch (e) { cm.state.vim = void 0; maybeInitVimState(cm); if (!vimApi.suppressErrorLogging) { console["log"](e); } throw e; } return true; }); }; } }, handleEx: function(cm, input) { exCommandDispatcher.processCommand(cm, input); }, defineMotion, defineAction, defineOperator, mapCommand, _mapCommand, defineRegister, exitVisualMode, exitInsertMode }; var keyToKeyStack = []; var noremap = false; function doKeyToKey(cm, keys2, fromKey) { if (fromKey) { if (keyToKeyStack.indexOf(fromKey) != -1) return; keyToKeyStack.push(fromKey); noremap = fromKey.noremap; } try { var vim2 = maybeInitVimState(cm); var keyRe = /<(?:[CSMA]-)*\w+>|./gi; var match; while (match = keyRe.exec(keys2)) { var key = match[0]; var wasInsert = vim2.insertMode; var result = vimApi.handleKey(cm, key, "mapping"); if (!result && wasInsert && vim2.insertMode) { if (key[0] == "<") { var lowerKey = key.toLowerCase().slice(1, -1); var parts = lowerKey.split("-"); var lowerKey = parts.pop(); if (lowerKey == "lt") key = "<"; else if (lowerKey == "space") key = " "; else if (lowerKey == "cr") key = "\n"; else if (vimToCmKeyMap.hasOwnProperty(lowerKey)) { key = vimToCmKeyMap[lowerKey]; sendCmKey(cm, key); continue; } else { key = key[0]; keyRe.lastIndex = match.index + 1; } } cm.replaceSelection(key); } } } finally { noremap = false; keyToKeyStack.length = 0; } } function InputState() { this.prefixRepeat = []; this.motionRepeat = []; this.operator = null; this.operatorArgs = null; this.motion = null; this.motionArgs = null; this.keyBuffer = []; this.registerName = null; this.changeQueue = 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 (this.prefixRepeat.length > 0 || this.motionRepeat.length > 0) { repeat = 1; if (this.prefixRepeat.length > 0) { repeat *= parseInt(this.prefixRepeat.join(""), 10); } if (this.motionRepeat.length > 0) { repeat *= parseInt(this.motionRepeat.join(""), 10); } } return repeat; }; function clearInputState(cm, reason) { cm.state.vim.inputState = new InputState(); CodeMirror2.signal(cm, "vim-command-done", reason); } function ChangeQueue() { this.removed = []; this.inserted = ""; } function Register(text, linewise, blockwise) { this.clear(); 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; if (!name || name.length != 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(); registers["+"] = new Register(); } RegisterController.prototype = { pushText: function(registerName, operator, text, linewise, blockwise) { if (registerName === "_") return; if (linewise && text.charAt(text.length - 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); } if (registerName === "+") { navigator.clipboard.writeText(text); } this.unnamedRegister.setText(register.toString(), linewise); }, // Gets the register named @name. If one of @name doesn't already exist, // create it. If @name is invalid, return the unnamedRegister. 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 = 0; this.initialPrefix = null; } HistoryController.prototype = { // the input argument here acts a user entered prefix for a small time // until we start autocompletion in which case it is the autocompleted. 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 < historyBuffer.length; i += dir) { var element = historyBuffer[i]; for (var j = 0; j <= element.length; j++) { if (this.initialPrefix == element.substring(0, j)) { this.iterator = i; return element; } } } if (i >= historyBuffer.length) { this.iterator = historyBuffer.length; 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 = this.historyBuffer.length; } }; var commandDispatcher = { matchCommand: function(keys2, keyMap, inputState, context) { var matches = commandMatches(keys2, 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 < matches.full.length; i++) { var match = matches.full[i]; if (!bestMatch) { bestMatch = match; } } if (bestMatch.keys.slice(-11) == "") { var character = lastChar(keys2); if (!character || character.length > 1) return { type: "clear" }; inputState.selectedCharacter = character; } return { type: "full", command: bestMatch }; }, processCommand: function(cm, vim2, command) { vim2.inputState.repeatOverride = command.repeatOverride; switch (command.type) { case "motion": this.processMotion(cm, vim2, command); break; case "operator": this.processOperator(cm, vim2, command); break; case "operatorMotion": this.processOperatorMotion(cm, vim2, command); break; case "action": this.processAction(cm, vim2, command); break; case "search": this.processSearch(cm, vim2, command); break; case "ex": case "keyToEx": this.processEx(cm, vim2, command); break; } }, processMotion: function(cm, vim2, command) { vim2.inputState.motion = command.motion; vim2.inputState.motionArgs = copyArgs(command.motionArgs); this.evalInput(cm, vim2); }, processOperator: function(cm, vim2, command) { var inputState = vim2.inputState; if (inputState.operator) { if (inputState.operator == command.operator) { inputState.motion = "expandToLine"; inputState.motionArgs = { linewise: true }; this.evalInput(cm, vim2); return; } else { clearInputState(cm); } } inputState.operator = command.operator; inputState.operatorArgs = copyArgs(command.operatorArgs); if (command.keys.length > 1) { inputState.operatorShortcut = command.keys; } if (command.exitVisualBlock) { vim2.visualBlock = false; updateCmSelection(cm); } if (vim2.visualMode) { this.evalInput(cm, vim2); } }, processOperatorMotion: function(cm, vim2, command) { var visualMode = vim2.visualMode; var operatorMotionArgs = copyArgs(command.operatorMotionArgs); if (operatorMotionArgs) { if (visualMode && operatorMotionArgs.visualLine) { vim2.visualLine = true; } } this.processOperator(cm, vim2, command); if (!visualMode) { this.processMotion(cm, vim2, command); } }, processAction: function(cm, vim2, command) { var inputState = vim2.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, vim2, command); } if (command.motion) { this.processMotion(cm, vim2, command); } if (command.motion || command.operator) { this.evalInput(cm, vim2); } actionArgs.repeat = repeat || 1; actionArgs.repeatIsExplicit = repeatIsExplicit; actionArgs.registerName = inputState.registerName; clearInputState(cm); vim2.lastMotion = null; if (command.isEdit) { this.recordLastEdit(vim2, inputState, command); } actions[command.action](cm, actionArgs, vim2); }, processSearch: function(cm, vim2, 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(query2, ignoreCase, smartCase) { vimGlobalState.searchHistoryController.pushInput(query2); vimGlobalState.searchHistoryController.reset(); try { updateSearchQuery(cm, query2, ignoreCase, smartCase); } catch (e) { showConfirm(cm, "Invalid regex: " + query2); clearInputState(cm); return; } commandDispatcher.processMotion(cm, vim2, { type: "motion", motion: "findNext", motionArgs: { forward: true, toJumplist: command.searchArgs.toJumplist } }); } function onPromptClose(query2) { cm.scrollTo(originalScrollPos.left, originalScrollPos.top); handleQuery( query2, true, true /** smartCase */ ); var macroModeState2 = vimGlobalState.macroModeState; if (macroModeState2.isRecording) { logSearchQuery(macroModeState2, query2); } } function onPromptKeyUp(e, query2, close) { var keyName = CodeMirror2.keyName(e), up, offset; if (keyName == "Up" || keyName == "Down") { up = keyName == "Up" ? true : false; offset = e.target ? e.target.selectionEnd : 0; query2 = vimGlobalState.searchHistoryController.nextMatch(query2, up) || ""; close(query2); if (offset && e.target) e.target.selectionEnd = e.target.selectionStart = Math.min(offset, e.target.value.length); } else { if (keyName != "Left" && keyName != "Right" && keyName != "Ctrl" && keyName != "Alt" && keyName != "Shift") vimGlobalState.searchHistoryController.reset(); } var parsedQuery; try { parsedQuery = updateSearchQuery( cm, query2, true, true /** smartCase */ ); } catch (e2) { } if (parsedQuery) { cm.scrollIntoView(findNext(cm, !forward, parsedQuery), 30); } else { clearSearchHighlight(cm); cm.scrollTo(originalScrollPos.left, originalScrollPos.top); } } function onPromptKeyDown(e, query2, close) { var keyName = CodeMirror2.keyName(e); if (keyName == "Esc" || keyName == "Ctrl-C" || keyName == "Ctrl-[" || keyName == "Backspace" && query2 == "") { vimGlobalState.searchHistoryController.pushInput(query2); vimGlobalState.searchHistoryController.reset(); updateSearchQuery(cm, originalQuery); clearSearchHighlight(cm); cm.scrollTo(originalScrollPos.left, originalScrollPos.top); CodeMirror2.e_stop(e); clearInputState(cm); close(); cm.focus(); } else if (keyName == "Up" || keyName == "Down") { CodeMirror2.e_stop(e); } else if (keyName == "Ctrl-U") { CodeMirror2.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 /** smartCase */ ); } else { showPrompt(cm, { onClose: onPromptClose, prefix: promptPrefix, desc: "(JavaScript regexp)", onKeyUp: onPromptKeyUp, onKeyDown: onPromptKeyDown }); } break; case "wordUnderCursor": var word = expandWordUnderCursor( cm, false, false, false, true /** noSymbol */ ); var isKeyword = true; if (!word) { word = expandWordUnderCursor( cm, false, false, false, false /** noSymbol */ ); 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 /** smartCase */ ); break; } }, processEx: function(cm, vim2, command) { function onPromptClose(input) { vimGlobalState.exCommandHistoryController.pushInput(input); vimGlobalState.exCommandHistoryController.reset(); exCommandDispatcher.processCommand(cm, input); if (cm.state.vim) clearInputState(cm); } function onPromptKeyDown(e, input, close) { var keyName = CodeMirror2.keyName(e), up, offset; if (keyName == "Esc" || keyName == "Ctrl-C" || keyName == "Ctrl-[" || keyName == "Backspace" && input == "") { vimGlobalState.exCommandHistoryController.pushInput(input); vimGlobalState.exCommandHistoryController.reset(); CodeMirror2.e_stop(e); clearInputState(cm); close(); cm.focus(); } if (keyName == "Up" || keyName == "Down") { CodeMirror2.e_stop(e); up = keyName == "Up" ? true : false; offset = e.target ? e.target.selectionEnd : 0; input = vimGlobalState.exCommandHistoryController.nextMatch(input, up) || ""; close(input); if (offset && e.target) e.target.selectionEnd = e.target.selectionStart = Math.min(offset, e.target.value.length); } else if (keyName == "Ctrl-U") { CodeMirror2.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 (vim2.visualMode) { showPrompt(cm, { onClose: onPromptClose, prefix: ":", value: "'<,'>", onKeyDown: onPromptKeyDown, selectValueOnOpen: false }); } else { showPrompt(cm, { onClose: onPromptClose, prefix: ":", onKeyDown: onPromptKeyDown }); } } }, evalInput: function(cm, vim2) { var inputState = vim2.inputState; var motion = inputState.motion; var motionArgs = inputState.motionArgs || {}; var operator = inputState.operator; var operatorArgs = inputState.operatorArgs || {}; var registerName = inputState.registerName; var sel = vim2.sel; var origHead = copyCursor(vim2.visualMode ? clipCursorToContent(cm, sel.head) : cm.getCursor("head")); var origAnchor = copyCursor(vim2.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(vim2, inputState); } if (inputState.repeatOverride !== void 0) { 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, vim2, inputState); vim2.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 (vim2.visualMode) { if (!(vim2.visualBlock && newHead.ch === Infinity)) { newHead = clipCursorToContent(cm, newHead, oldHead); } if (newAnchor) { newAnchor = clipCursorToContent(cm, newAnchor); } newAnchor = newAnchor || oldAnchor; sel.anchor = newAnchor; sel.head = newHead; updateCmSelection(cm); updateMark( cm, vim2, "<", cursorIsBefore(newAnchor, newHead) ? newAnchor : newHead ); updateMark( cm, vim2, ">", cursorIsBefore(newAnchor, newHead) ? newHead : newAnchor ); } else if (!operator) { newHead = clipCursorToContent(cm, newHead, oldHead); 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 = new Pos2(oldAnchor.line + lineOffset, oldAnchor.ch); } else if (lastSel.visualBlock) { newHead = new Pos2(oldAnchor.line + lineOffset, oldAnchor.ch + chOffset); } else if (lastSel.head.line == lastSel.anchor.line) { newHead = new Pos2(oldAnchor.line, oldAnchor.ch + chOffset); } else { newHead = new Pos2(oldAnchor.line + lineOffset, oldAnchor.ch); } vim2.visualMode = true; vim2.visualLine = lastSel.visualLine; vim2.visualBlock = lastSel.visualBlock; sel = vim2.sel = { anchor: newAnchor, head: newHead }; updateCmSelection(cm); } else if (vim2.visualMode) { operatorArgs.lastSel = { anchor: copyCursor(sel.anchor), head: copyCursor(sel.head), visualBlock: vim2.visualBlock, visualLine: vim2.visualLine }; } var curStart, curEnd, linewise, mode; var cmSel; if (vim2.visualMode) { curStart = cursorMin(sel.head, sel.anchor); curEnd = cursorMax(sel.head, sel.anchor); linewise = vim2.visualLine || operatorArgs.linewise; mode = vim2.visualBlock ? "block" : linewise ? "line" : "char"; var newPositions = updateSelectionForSurrogateCharacters(cm, curStart, curEnd); cmSel = makeCmSelection(cm, { anchor: newPositions.start, head: newPositions.end }, mode); if (linewise) { var ranges = cmSel.ranges; if (mode == "block") { for (var i = 0; i < ranges.length; i++) { ranges[i].head.ch = lineLength(cm, ranges[i].head.line); } } else if (mode == "line") { ranges[0].head = new Pos2(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; var newPositions = updateSelectionForSurrogateCharacters(cm, curStart, curEnd); cmSel = makeCmSelection(cm, { anchor: newPositions.start, head: newPositions.end }, mode, exclusive); } cm.setSelections(cmSel.ranges, cmSel.primary); vim2.lastMotion = null; operatorArgs.repeat = repeat; operatorArgs.registerName = registerName; operatorArgs.linewise = linewise; var operatorMoveTo = operators[operator]( cm, operatorArgs, cmSel.ranges, oldAnchor, newHead ); if (vim2.visualMode) { exitVisualMode(cm, operatorMoveTo != null); } if (operatorMoveTo) { cm.setCursor(operatorMoveTo); } } }, recordLastEdit: function(vim2, inputState, actionCommand) { var macroModeState = vimGlobalState.macroModeState; if (macroModeState.isPlaying) { return; } vim2.lastEditInputState = inputState; vim2.lastEditActionCommand = actionCommand; macroModeState.lastInsertModeChanges.changes = []; macroModeState.lastInsertModeChanges.expectCursorActivityForChange = false; macroModeState.lastInsertModeChanges.visualBlock = vim2.visualBlock ? vim2.sel.head.line - vim2.sel.anchor.line : 0; } }; var motions = { moveToTopLine: function(cm, _head, motionArgs) { var line = getUserVisibleLines(cm).top + motionArgs.repeat - 1; return new Pos2(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line))); }, moveToMiddleLine: function(cm) { var range = getUserVisibleLines(cm); var line = Math.floor((range.top + range.bottom) * 0.5); return new Pos2(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line))); }, moveToBottomLine: function(cm, _head, motionArgs) { var line = getUserVisibleLines(cm).bottom - motionArgs.repeat + 1; return new Pos2(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line))); }, expandToLine: function(_cm, head, motionArgs) { var cur = head; return new Pos2(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); }, /** * Find and select the next occurrence of the search query. If the cursor is currently * within a match, then find and select the current match. Otherwise, find the next occurrence in the * appropriate direction. * * This differs from `findNext` in the following ways: * * 1. Instead of only returning the "from", this returns a "from", "to" range. * 2. If the cursor is currently inside a search match, this selects the current match * instead of the next match. * 3. If there is no associated operator, this will turn on visual mode. */ findAndSelectNextInclusive: function(cm, _head, motionArgs, vim2, prevInputState) { var state = getSearchState(cm); var query = state.getQuery(); if (!query) { return; } var prev = !motionArgs.forward; prev = state.isReversed() ? !prev : prev; var next = findNextFromAndToInclusive(cm, prev, query, motionArgs.repeat, vim2); if (!next) { return; } if (prevInputState.operator) { return next; } var from = next[0]; var to = new Pos2(next[1].line, next[1].ch - 1); if (vim2.visualMode) { if (vim2.visualLine || vim2.visualBlock) { vim2.visualLine = false; vim2.visualBlock = false; CodeMirror2.signal(cm, "vim-mode-change", { mode: "visual", subMode: "" }); } var anchor = vim2.sel.anchor; if (anchor) { if (state.isReversed()) { if (motionArgs.forward) { return [anchor, from]; } return [anchor, to]; } else { if (motionArgs.forward) { return [anchor, to]; } return [anchor, from]; } } } else { vim2.visualMode = true; vim2.visualLine = false; vim2.visualBlock = false; CodeMirror2.signal(cm, "vim-mode-change", { mode: "visual", subMode: "" }); } return prev ? [to, from] : [from, to]; }, goToMark: function(cm, _head, motionArgs, vim2) { var pos = getMarkPos(cm, vim2, motionArgs.selectedCharacter); if (pos) { return motionArgs.linewise ? { line: pos.line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(pos.line)) } : pos; } return null; }, moveToOtherHighlightedEnd: function(cm, _head, motionArgs, vim2) { if (vim2.visualBlock && motionArgs.sameLine) { var sel = vim2.sel; return [ clipCursorToContent(cm, new Pos2(sel.anchor.line, sel.head.ch)), clipCursorToContent(cm, new Pos2(sel.head.line, sel.anchor.ch)) ]; } else { return [vim2.sel.head, vim2.sel.anchor]; } }, jumpToMark: function(cm, head, motionArgs, vim2) { var best = head; for (var i = 0; i < motionArgs.repeat; i++) { var cursor = best; for (var key in vim2.marks) { if (!isLowerCase(key)) { continue; } var mark = vim2.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 = new Pos2(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 new Pos2(cur.line, ch); }, moveByLines: function(cm, head, motionArgs, vim2) { var cur = head; var endCh = cur.ch; switch (vim2.lastMotion) { case this.moveByLines: case this.moveByDisplayLines: case this.moveByScroll: case this.moveToColumn: case this.moveToEol: endCh = vim2.lastHPos; break; default: vim2.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(); var posV = cm.findPosV(cur, motionArgs.forward ? repeat : -repeat, "line", vim2.lastHSPos); var hasMarkedText = motionArgs.forward ? posV.line > line : posV.line < line; if (hasMarkedText) { line = posV.line; endCh = posV.ch; } if (line < first && cur.line == first) { return this.moveToStartOfLine(cm, head, motionArgs, vim2); } else if (line > last && cur.line == last) { return moveToEol(cm, head, motionArgs, vim2, true); } if (motionArgs.toFirstChar) { endCh = findFirstNonWhiteSpaceCharacter(cm.getLine(line)); vim2.lastHPos = endCh; } vim2.lastHSPos = cm.charCoords(new Pos2(line, endCh), "div").left; return new Pos2(line, endCh); }, moveByDisplayLines: function(cm, head, motionArgs, vim2) { var cur = head; switch (vim2.lastMotion) { case this.moveByDisplayLines: case this.moveByScroll: case this.moveByLines: case this.moveToColumn: case this.moveToEol: break; default: vim2.lastHSPos = cm.charCoords(cur, "div").left; } var repeat = motionArgs.repeat; var res = cm.findPosV(cur, motionArgs.forward ? repeat : -repeat, "line", vim2.lastHSPos); if (res.hitSide) { if (motionArgs.forward) { var lastCharCoords = cm.charCoords(res, "div"); var goalCoords = { top: lastCharCoords.top + 8, left: vim2.lastHSPos }; var res = cm.coordsChar(goalCoords, "div"); } else { var resCoords = cm.charCoords(new Pos2(cm.firstLine(), 0), "div"); resCoords.left = vim2.lastHSPos; res = cm.coordsChar(resCoords, "div"); } } vim2.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); }, moveBySentence: function(cm, head, motionArgs) { var dir = motionArgs.forward ? 1 : -1; return findSentence(cm, head, motionArgs.repeat, dir); }, moveByScroll: function(cm, head, motionArgs, vim2) { 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; curEnd = motions.moveByDisplayLines(cm, head, motionArgs, vim2); 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, head ); 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 ) || head; }, moveToSymbol: function(cm, head, motionArgs) { var repeat = motionArgs.repeat; return findSymbol( cm, repeat, motionArgs.forward, motionArgs.selectedCharacter ) || head; }, moveToColumn: function(cm, head, motionArgs, vim2) { var repeat = motionArgs.repeat; vim2.lastHPos = repeat - 1; vim2.lastHSPos = cm.charCoords(head, "div").left; return moveToColumn(cm, repeat); }, moveToEol: function(cm, head, motionArgs, vim2) { return moveToEol(cm, head, motionArgs, vim2, false); }, moveToFirstNonWhiteSpaceCharacter: function(cm, head) { var cursor = head; return new Pos2( 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; for (; ch < lineText.length; ch++) { symbol = lineText.charAt(ch); if (symbol && isMatchableSymbol(symbol)) { var style = cm.getTokenTypeAt(new Pos2(line, ch + 1)); if (style !== "string" && style !== "comment") { break; } } } if (ch < lineText.length) { var re = ch === "<" || ch === ">" ? /[(){}[\]<>]/ : /[(){}[\]]/; var matched = cm.findMatchingBracket(new Pos2(line, ch), { bracketRegex: re }); return matched.to; } else { return cursor; } }, moveToStartOfLine: function(_cm, head) { return new Pos2(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 new Pos2( lineNum, findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum)) ); }, moveToStartOfDisplayLine: function(cm) { cm.execCommand("goLineLeft"); return cm.getCursor(); }, moveToEndOfDisplayLine: function(cm) { cm.execCommand("goLineRight"); var head = cm.getCursor(); if (head.sticky == "before") head.ch--; return head; }, textObjectManipulation: function(cm, head, motionArgs, vim2) { var mirroredPairs = { "(": ")", ")": "(", "{": "}", "}": "{", "[": "]", "]": "[", "<": ">", ">": "<" }; var selfPaired = { "'": true, '"': true, "`": true }; var character = motionArgs.selectedCharacter; if (character == "b") { character = "("; } else if (character == "B") { character = "{"; } var inclusive = !motionArgs.textObjectInner; var tmp, move; if (mirroredPairs[character]) { move = true; tmp = selectCompanionObject(cm, head, character, inclusive); if (!tmp) { var sc = cm.getSearchCursor(new RegExp("\\" + character, "g"), head); if (sc.find()) { tmp = selectCompanionObject(cm, sc.from(), character, inclusive); } } } else if (selfPaired[character]) { move = true; tmp = findBeginningAndEnd(cm, head, character, inclusive); } else if (character === "W") { tmp = expandWordUnderCursor( cm, inclusive, !inclusive, true /** bigWord */ ); } else if (character === "w") { tmp = expandWordUnderCursor( cm, inclusive, !inclusive, false /** bigWord */ ); } else if (character === "p") { tmp = findParagraph(cm, head, motionArgs.repeat, 0, inclusive); motionArgs.linewise = true; if (vim2.visualMode) { if (!vim2.visualLine) { vim2.visualLine = true; } } else { var operatorArgs = vim2.inputState.operatorArgs; if (operatorArgs) { operatorArgs.linewise = true; } tmp.end.line--; } } else if (character === "t") { tmp = expandTagUnderCursor(cm, head, inclusive); } else if (character === "s") { var content = cm.getLine(head.line); if (head.ch > 0 && isEndOfSentenceSymbol(content[head.ch])) { head.ch -= 1; } var end = getSentence(cm, head, motionArgs.repeat, 1, inclusive); var start = getSentence(cm, head, motionArgs.repeat, -1, inclusive); if (isWhiteSpaceString(cm.getLine(start.line)[start.ch]) && isWhiteSpaceString(cm.getLine(end.line)[end.ch - 1])) { start = { line: start.line, ch: start.ch + 1 }; } tmp = { start, end }; } if (!tmp) { return null; } if (!cm.state.vim.visualMode) { return [tmp.start, tmp.end]; } else { return expandSelection(cm, tmp.start, tmp.end, move); } }, repeatLastCharacterSearch: function(cm, head, motionArgs) { var lastSearch = vimGlobalState.lastCharacterSearch; 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 vim2 = cm.state.vim; var anchor = ranges[0].anchor, head = ranges[0].head; if (!vim2.visualMode) { text = cm.getRange(anchor, head); var lastState = vim2.lastEditInputState || {}; if (lastState.motion == "moveByWords" && !isWhiteSpaceString(text)) { var match = /\s+$/.exec(text); if (match && lastState.motionArgs && lastState.motionArgs.forward) { head = offsetCursor(head, 0, -match[0].length); text = text.slice(0, -match[0].length); } } var prevLineEnd = new Pos2(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); CodeMirror2.commands.newlineAndIndent(cm); } anchor.ch = Number.MAX_VALUE; } finalHead = anchor; } else if (args.fullLine) { head.ch = Number.MAX_VALUE; head.line--; cm.setSelection(anchor, head); text = cm.getSelection(); cm.replaceSelection(""); finalHead = anchor; } else { text = cm.getSelection(); var replacement = fillArray("", ranges.length); cm.replaceSelections(replacement); finalHead = cursorMin(ranges[0].head, ranges[0].anchor); } vimGlobalState.registerController.pushText( args.registerName, "change", text, args.linewise, ranges.length > 1 ); actions.enterInsertMode(cm, { head: finalHead }, cm.state.vim); }, // delete is a javascript keyword. "delete": function(cm, args, ranges) { var finalHead, text; var vim2 = cm.state.vim; if (!vim2.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 = new Pos2(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("", ranges.length); cm.replaceSelections(replacement); finalHead = cursorMin(ranges[0].head, ranges[0].anchor); } vimGlobalState.registerController.pushText( args.registerName, "delete", text, args.linewise, vim2.visualBlock ); return clipCursorToContent(cm, finalHead); }, indent: function(cm, args, ranges) { var vim2 = cm.state.vim; if (cm.indentMore) { var repeat = vim2.visualMode ? args.repeat : 1; for (var j = 0; j < repeat; j++) { if (args.indentRight) cm.indentMore(); else cm.indentLess(); } } else { var startLine = ranges[0].anchor.line; var endLine = vim2.visualBlock ? ranges[ranges.length - 1].anchor.line : ranges[0].head.line; var repeat = vim2.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); }, indentAuto: function(cm, _args, ranges) { cm.execCommand("indentAuto"); 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 < selections.length; 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 < toSwap.length; 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 vim2 = cm.state.vim; var text = cm.getSelection(); var endPos = vim2.visualMode ? cursorMin(vim2.sel.anchor, vim2.sel.head, ranges[0].head, ranges[0].anchor) : oldAnchor; vimGlobalState.registerController.pushText( args.registerName, "yank", text, args.linewise, vim2.visualBlock ); return endPos; } }; function defineOperator(name, fn) { operators[name] = fn; } var actions = { jumpListWalk: function(cm, actionArgs, vim2) { if (vim2.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() : void 0; markPos = markPos ? markPos : cm.getCursor(); cm.setCursor(markPos); }, scroll: function(cm, actionArgs, vim2) { if (vim2.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(new Pos2(lineNum, 0), "local"); var height = cm.getScrollInfo().clientHeight; var y = charCoords.top; switch (actionArgs.position) { case "center": y = charCoords.bottom - height / 2; break; case "bottom": var lineLastCharPos = new Pos2(lineNum, cm.getLine(lineNum).length - 1); var lineLastCharCoords = cm.charCoords(lineLastCharPos, "local"); var lineHeight = lineLastCharCoords.bottom - y; y = y - height + lineHeight; break; } cm.scrollTo(null, y); }, replayMacro: function(cm, actionArgs, vim2) { var registerName = actionArgs.selectedCharacter; var repeat = actionArgs.repeat; var macroModeState = vimGlobalState.macroModeState; if (registerName == "@") { registerName = macroModeState.latestRegister; } else { macroModeState.latestRegister = registerName; } while (repeat--) { executeMacroRegister(cm, vim2, macroModeState, registerName); } }, enterMacroRecordMode: function(cm, actionArgs) { var macroModeState = vimGlobalState.macroModeState; var registerName = actionArgs.selectedCharacter; if (vimGlobalState.registerController.isValidRegister(registerName)) { macroModeState.enterMacroRecordMode(cm, registerName); } }, toggleOverwrite: function(cm) { if (!cm.state.overwrite) { cm.toggleOverwrite(true); cm.setOption("keyMap", "vim-replace"); CodeMirror2.signal(cm, "vim-mode-change", { mode: "replace" }); } else { cm.toggleOverwrite(false); cm.setOption("keyMap", "vim-insert"); CodeMirror2.signal(cm, "vim-mode-change", { mode: "insert" }); } }, enterInsertMode: function(cm, actionArgs, vim2) { if (cm.getOption("readOnly")) { return; } vim2.insertMode = true; vim2.insertModeRepeat = actionArgs && actionArgs.repeat || 1; var insertAt = actionArgs ? actionArgs.insertAt : null; var sel = vim2.sel; var head = actionArgs.head || cm.getCursor("head"); var height = cm.listSelections().length; if (insertAt == "eol") { head = new Pos2(head.line, lineLength(cm, head.line)); } else if (insertAt == "bol") { head = new Pos2(head.line, 0); } else if (insertAt == "charAfter") { var newPosition = updateSelectionForSurrogateCharacters(cm, head, offsetCursor(head, 0, 1)); head = newPosition.end; } else if (insertAt == "firstNonBlank") { var newPosition = updateSelectionForSurrogateCharacters(cm, head, motions.moveToFirstNonWhiteSpaceCharacter(cm, head)); head = newPosition.end; } else if (insertAt == "startOfSelectedArea") { if (!vim2.visualMode) return; if (!vim2.visualBlock) { if (sel.head.line < sel.anchor.line) { head = sel.head; } else { head = new Pos2(sel.anchor.line, 0); } } else { head = new Pos2( 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 (!vim2.visualMode) return; if (!vim2.visualBlock) { if (sel.head.line >= sel.anchor.line) { head = offsetCursor(sel.head, 0, 1); } else { head = new Pos2(sel.anchor.line, 0); } } else { head = new Pos2( Math.min(sel.head.line, sel.anchor.line), Math.max(sel.head.ch, sel.anchor.ch) + 1 ); height = Math.abs(sel.head.line - sel.anchor.line) + 1; } } else if (insertAt == "inplace") { if (vim2.visualMode) { return; } } else if (insertAt == "lastEdit") { head = getLastEditPos(cm) || head; } cm.setOption("disableInput", false); if (actionArgs && actionArgs.replace) { cm.toggleOverwrite(true); cm.setOption("keyMap", "vim-replace"); CodeMirror2.signal(cm, "vim-mode-change", { mode: "replace" }); } else { cm.toggleOverwrite(false); cm.setOption("keyMap", "vim-insert"); CodeMirror2.signal(cm, "vim-mode-change", { mode: "insert" }); } if (!vimGlobalState.macroModeState.isPlaying) { cm.on("change", onChange); if (vim2.insertEnd) vim2.insertEnd.clear(); vim2.insertEnd = cm.setBookmark(head, { insertLeft: true }); CodeMirror2.on(cm.getInputField(), "keydown", onKeyEventTargetKeyDown); } if (vim2.visualMode) { exitVisualMode(cm); } selectForInsert(cm, head, height); }, toggleVisualMode: function(cm, actionArgs, vim2) { var repeat = actionArgs.repeat; var anchor = cm.getCursor(); var head; if (!vim2.visualMode) { vim2.visualMode = true; vim2.visualLine = !!actionArgs.linewise; vim2.visualBlock = !!actionArgs.blockwise; head = clipCursorToContent( cm, new Pos2(anchor.line, anchor.ch + repeat - 1) ); var newPosition = updateSelectionForSurrogateCharacters(cm, anchor, head); vim2.sel = { anchor: newPosition.start, head: newPosition.end }; CodeMirror2.signal(cm, "vim-mode-change", { mode: "visual", subMode: vim2.visualLine ? "linewise" : vim2.visualBlock ? "blockwise" : "" }); updateCmSelection(cm); updateMark(cm, vim2, "<", cursorMin(anchor, head)); updateMark(cm, vim2, ">", cursorMax(anchor, head)); } else if (vim2.visualLine ^ actionArgs.linewise || vim2.visualBlock ^ actionArgs.blockwise) { vim2.visualLine = !!actionArgs.linewise; vim2.visualBlock = !!actionArgs.blockwise; CodeMirror2.signal(cm, "vim-mode-change", { mode: "visual", subMode: vim2.visualLine ? "linewise" : vim2.visualBlock ? "blockwise" : "" }); updateCmSelection(cm); } else { exitVisualMode(cm); } }, reselectLastSelection: function(cm, _actionArgs, vim2) { var lastSelection = vim2.lastSelection; if (vim2.visualMode) { updateLastSelection(cm, vim2); } if (lastSelection) { var anchor = lastSelection.anchorMark.find(); var head = lastSelection.headMark.find(); if (!anchor || !head) { return; } vim2.sel = { anchor, head }; vim2.visualMode = true; vim2.visualLine = lastSelection.visualLine; vim2.visualBlock = lastSelection.visualBlock; updateCmSelection(cm); updateMark(cm, vim2, "<", cursorMin(anchor, head)); updateMark(cm, vim2, ">", cursorMax(anchor, head)); CodeMirror2.signal(cm, "vim-mode-change", { mode: "visual", subMode: vim2.visualLine ? "linewise" : vim2.visualBlock ? "blockwise" : "" }); } }, joinLines: function(cm, actionArgs, vim2) { var curStart, curEnd; if (vim2.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, new Pos2( curStart.line + repeat - 1, Infinity )); } var finalCh = 0; for (var i = curStart.line; i < curEnd.line; i++) { finalCh = lineLength(cm, curStart.line); var text = ""; var nextStartCh = 0; if (!actionArgs.keepSpaces) { var nextLine = cm.getLine(curStart.line + 1); nextStartCh = nextLine.search(/\S/); if (nextStartCh == -1) { nextStartCh = nextLine.length; } else { text = " "; } } cm.replaceRange( text, new Pos2(curStart.line, finalCh), new Pos2(curStart.line + 1, nextStartCh) ); } var curFinalPos = clipCursorToContent(cm, new Pos2(curStart.line, finalCh)); if (vim2.visualMode) { exitVisualMode(cm, false); } cm.setCursor(curFinalPos); }, newLineAndEnterInsertMode: function(cm, actionArgs, vim2) { vim2.insertMode = true; var insertAt = copyCursor(cm.getCursor()); if (insertAt.line === cm.firstLine() && !actionArgs.after) { cm.replaceRange("\n", new Pos2(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 = CodeMirror2.commands.newlineAndIndentContinueComment || CodeMirror2.commands.newlineAndIndent; newlineFn(cm); } this.enterInsertMode(cm, { repeat: actionArgs.repeat }, vim2); }, paste: function(cm, actionArgs, vim2) { var register = vimGlobalState.registerController.getRegister( actionArgs.registerName ); if (actionArgs.registerName === "+") { navigator.clipboard.readText().then((value) => { this.continuePaste(cm, actionArgs, vim2, value, register); }); } else { var text = register.toString(); this.continuePaste(cm, actionArgs, vim2, text, register); } }, continuePaste: function(cm, actionArgs, vim2, text, register) { var cur = copyCursor(cm.getCursor()); if (!text) { return; } if (actionArgs.matchIndent) { var tabSize = cm.getOption("tabSize"); var whitespaceLength = function(str) { var tabs = str.split(" ").length - 1; var spaces = str.split(" ").length - 1; return tabs * tabSize + spaces * 1; }; var currentLine = cm.getLine(cm.getCursor().line); var indent = whitespaceLength(currentLine.match(/^\s*/)[0]); var chompedText = text.replace(/\n$/, ""); var wasChomped = text !== chompedText; var firstIndent = whitespaceLength(text.match(/^\s*/)[0]); var text = chompedText.replace(/^\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(" "); } 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 (blockwise) { text = text.split("\n"); if (linewise) { text.pop(); } for (var i = 0; i < text.length; i++) { text[i] = text[i] == "" ? " " : text[i]; } cur.ch += actionArgs.after ? 1 : 0; cur.ch = Math.min(lineLength(cm, cur.line), cur.ch); } else if (linewise) { if (vim2.visualMode) { text = vim2.visualLine ? text.slice(0, -1) : "\n" + text.slice(0, text.length - 1) + "\n"; } else if (actionArgs.after) { text = "\n" + text.slice(0, text.length - 1); cur.ch = lineLength(cm, cur.line); } else { cur.ch = 0; } } else { cur.ch += actionArgs.after ? 1 : 0; } var curPosFinal; if (vim2.visualMode) { vim2.lastPastedText = text; var lastSelectionCurEnd; var selectedArea = getSelectedAreaRange(cm, vim2); var selectionStart = selectedArea[0]; var selectionEnd = selectedArea[1]; var selectedText = cm.getSelection(); var selections = cm.listSelections(); var emptyStrings = new Array(selections.length).join("1").split("1"); if (vim2.lastSelection) { lastSelectionCurEnd = vim2.lastSelection.headMark.find(); } vimGlobalState.registerController.unnamedRegister.setText(selectedText); if (blockwise) { cm.replaceSelections(emptyStrings); selectionEnd = new Pos2(selectionStart.line + text.length - 1, selectionStart.ch); cm.setCursor(selectionStart); selectBlock(cm, selectionEnd); cm.replaceSelections(text); curPosFinal = selectionStart; } else if (vim2.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) + text.length - 1); } if (lastSelectionCurEnd) { vim2.lastSelection.headMark = cm.setBookmark(lastSelectionCurEnd); } if (linewise) { curPosFinal.ch = 0; } } else { if (blockwise) { cm.setCursor(cur); for (var i = 0; i < text.length; i++) { var line = cur.line + i; if (line > cm.lastLine()) { cm.replaceRange("\n", new Pos2(line, 0)); } var lastCh = lineLength(cm, line); if (lastCh < cur.ch) { extendLineToColumn(cm, line, cur.ch); } } cm.setCursor(cur); selectBlock(cm, new Pos2(cur.line + text.length - 1, cur.ch)); cm.replaceSelections(text); curPosFinal = cur; } else { cm.replaceRange(text, cur); if (linewise) { var line = actionArgs.after ? cur.line + 1 : cur.line; curPosFinal = new Pos2(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line))); } else { curPosFinal = copyCursor(cur); if (!/\n/.test(text)) { curPosFinal.ch += text.length - (actionArgs.after ? 1 : 0); } } } } if (vim2.visualMode) { exitVisualMode(cm, false); } cm.setCursor(curPosFinal); }, undo: function(cm, actionArgs) { cm.operation(function() { repeatFn(cm, CodeMirror2.commands.undo, actionArgs.repeat)(); cm.setCursor(clipCursorToContent(cm, cm.getCursor("start"))); }); }, redo: function(cm, actionArgs) { repeatFn(cm, CodeMirror2.commands.redo, actionArgs.repeat)(); }, setRegister: function(_cm, actionArgs, vim2) { vim2.inputState.registerName = actionArgs.selectedCharacter; }, insertRegister: function(cm, actionArgs, vim2) { var registerName = actionArgs.selectedCharacter; var register = vimGlobalState.registerController.getRegister(registerName); var text = register && register.toString(); if (text) { cm.replaceSelection(text); } }, oneNormalCommand: function(cm, actionArgs, vim2) { exitInsertMode(cm, true); vim2.insertModeReturn = true; CodeMirror2.on(cm, "vim-command-done", function handler() { if (vim2.visualMode) return; if (vim2.insertModeReturn) { vim2.insertModeReturn = false; if (!vim2.insertMode) { actions.enterInsertMode(cm, {}, vim2); } } CodeMirror2.off(cm, "vim-command-done", handler); }); }, setMark: function(cm, actionArgs, vim2) { var markName = actionArgs.selectedCharacter; updateMark(cm, vim2, markName, cm.getCursor()); }, replace: function(cm, actionArgs, vim2) { var replaceWith = actionArgs.selectedCharacter; var curStart = cm.getCursor(); var replaceTo; var curEnd; var selections = cm.listSelections(); if (vim2.visualMode) { curStart = cm.getCursor("start"); curEnd = cm.getCursor("end"); } else { var line = cm.getLine(curStart.line); replaceTo = curStart.ch + actionArgs.repeat; if (replaceTo > line.length) { replaceTo = line.length; } curEnd = new Pos2(curStart.line, replaceTo); } var newPositions = updateSelectionForSurrogateCharacters(cm, curStart, curEnd); curStart = newPositions.start; curEnd = newPositions.end; if (replaceWith == "\n") { if (!vim2.visualMode) cm.replaceRange("", curStart, curEnd); (CodeMirror2.commands.newlineAndIndentContinueComment || CodeMirror2.commands.newlineAndIndent)(cm); } else { var replaceWithStr = cm.getRange(curStart, curEnd); replaceWithStr = replaceWithStr.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, replaceWith); replaceWithStr = replaceWithStr.replace(/[^\n]/g, replaceWith); if (vim2.visualBlock) { var spaces = new Array(cm.getOption("tabSize") + 1).join(" "); replaceWithStr = cm.getSelection(); replaceWithStr = replaceWithStr.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, replaceWith); replaceWithStr = replaceWithStr.replace(/\t/g, spaces).replace(/[^\n]/g, replaceWith).split("\n"); cm.replaceSelections(replaceWithStr); } else { cm.replaceRange(replaceWithStr, curStart, curEnd); } if (vim2.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 = /(-?)(?:(0x)([\da-f]+)|(0b|0|)(\d+))/gi; var match; var start; var end; var numberStr; while ((match = re.exec(lineStr)) !== null) { start = match.index; end = start + match[0].length; if (cur.ch < end) break; } if (!actionArgs.backtrack && end <= cur.ch) return; if (match) { var baseStr = match[2] || match[4]; var digits = match[3] || match[5]; var increment = actionArgs.increase ? 1 : -1; var base = { "0b": 2, "0": 8, "": 10, "0x": 16 }[baseStr.toLowerCase()]; var number = parseInt(match[1] + digits, base) + increment * actionArgs.repeat; numberStr = number.toString(base); var zeroPadding = baseStr ? new Array(digits.length - numberStr.length + 1 + match[1].length).join("0") : ""; if (numberStr.charAt(0) === "-") { numberStr = "-" + baseStr + zeroPadding + numberStr.substr(1); } else { numberStr = baseStr + zeroPadding + numberStr; } var from = new Pos2(cur.line, start); var to = new Pos2(cur.line, end); cm.replaceRange(numberStr, from, to); } else { return; } cm.setCursor(new Pos2(cur.line, start + numberStr.length - 1)); }, repeatLastEdit: function(cm, actionArgs, vim2) { var lastEditInputState = vim2.lastEditInputState; if (!lastEditInputState) { return; } var repeat = actionArgs.repeat; if (repeat && actionArgs.repeatIsExplicit) { vim2.lastEditInputState.repeatOverride = repeat; } else { repeat = vim2.lastEditInputState.repeatOverride || repeat; } repeatLastEdit( cm, vim2, repeat, false /** repeatForInsert */ ); }, indent: function(cm, actionArgs) { cm.indentLine(cm.getCursor().line, actionArgs.indentRight); }, exitInsertMode }; function defineAction(name, fn) { actions[name] = fn; } function clipCursorToContent(cm, cur, oldCur) { var vim2 = cm.state.vim; var includeLineBreak = vim2.insertMode || vim2.visualMode; var line = Math.min(Math.max(cm.firstLine(), cur.line), cm.lastLine()); var text = cm.getLine(line); var maxCh = text.length - 1 + Number(!!includeLineBreak); var ch = Math.min(Math.max(0, cur.ch), maxCh); var charCode = text.charCodeAt(ch); if (56320 <= charCode && charCode <= 57343) { var direction = 1; if (oldCur && oldCur.line == line && oldCur.ch > ch) { direction = -1; } ch += direction; if (ch > maxCh) ch -= 2; } return new Pos2(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 new Pos2(cur.line + offsetLine, cur.ch + offsetCh); } function commandMatches(keys2, keyMap, context, inputState) { var operatorPending = inputState.operator; var match, partial = [], full = []; var startIndex = noremap ? keyMap.length - defaultKeymapLength : 0; for (var i = startIndex; i < keyMap.length; i++) { var command = keyMap[i]; if (context == "insert" && command.context != "insert" || (command.context == "operatorPending" ? !operatorPending : command.context && command.context != context) || inputState.operator && command.type == "action" || !(match = commandMatch(keys2, command.keys))) { continue; } if (match == "partial") { partial.push(command); } if (match == "full") { full.push(command); } } return { partial: partial.length && partial, full: full.length && full }; } function commandMatch(pressed, mapped) { if (mapped.slice(-11) == "") { var prefixLen = mapped.length - 11; var pressedPrefix = pressed.slice(0, prefixLen); var mappedPrefix = mapped.slice(0, prefixLen); return pressedPrefix == mappedPrefix && pressed.length > prefixLen ? "full" : mappedPrefix.indexOf(pressedPrefix) == 0 ? "partial" : false; } else { return pressed == mapped ? "full" : mapped.indexOf(pressed) == 0 ? "partial" : false; } } function lastChar(keys2) { var match = /^.*(<[^>]+>)$/.exec(keys2); var selectedCharacter = match ? match[1] : keys2.slice(-1); if (selectedCharacter.length > 1) { switch (selectedCharacter) { case "": selectedCharacter = "\n"; break; case "": selectedCharacter = " "; break; default: selectedCharacter = ""; break; } } return selectedCharacter; } function repeatFn(cm, fn, repeat) { return function() { for (var i = 0; i < repeat; i++) { fn(cm); } }; } function copyCursor(cur) { return new Pos2(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 (arguments.length > 2) { cur2 = cursorMin.apply(void 0, Array.prototype.slice.call(arguments, 1)); } return cursorIsBefore(cur1, cur2) ? cur1 : cur2; } function cursorMax(cur1, cur2) { if (arguments.length > 2) { cur2 = cursorMax.apply(void 0, 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 cm.getLine(lineNum).length; } function trim(s) { if (s.trim) { return s.trim(); } return s.replace(/^\s+|\s+$/g, ""); } function escapeRegex(s) { return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, "\\$1"); } function extendLineToColumn(cm, lineNum, column) { var endCh = lineLength(cm, lineNum); var spaces = new Array(column - endCh + 1).join(" "); cm.setCursor(new Pos2(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 = ranges.length - 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 Pos2(line, baseCh), head: new Pos2(line, headCh) }; selections.push(range); } 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 < ranges.length; 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, vim2) { var lastSelection = vim2.lastSelection; var getCurrentSelectedAreaRange = function() { var selections = cm.listSelections(); var start = selections[0]; var end = selections[selections.length - 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 = new Pos2(selectionStart.line + height, selectionStart.ch + width); var selections = []; for (var i = selectionStart.line; i < selectionEnd.line; i++) { var anchor = new Pos2(i, selectionStart.ch); var head = new Pos2(i, selectionEnd.ch); var range = { anchor, 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 = new Pos2(selectionStart.line, 0); selectionEnd = new Pos2(selectionEnd.line, lineLength(cm, selectionEnd.line)); } cm.setSelection(selectionStart, selectionEnd); } return [selectionStart, selectionEnd]; }; if (!vim2.visualMode) { return getLastSelectedAreaRange(); } else { return getCurrentSelectedAreaRange(); } } function updateLastSelection(cm, vim2) { var anchor = vim2.sel.anchor; var head = vim2.sel.head; if (vim2.lastPastedText) { head = cm.posFromIndex(cm.indexFromPos(anchor) + vim2.lastPastedText.length); vim2.lastPastedText = null; } vim2.lastSelection = { "anchorMark": cm.setBookmark(anchor), "headMark": cm.setBookmark(head), "anchor": copyCursor(anchor), "head": copyCursor(head), "visualMode": vim2.visualMode, "visualLine": vim2.visualLine, "visualBlock": vim2.visualBlock }; } function expandSelection(cm, start, end, move) { var sel = cm.state.vim.sel; var head = move ? start : sel.head; var anchor = move ? start : 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 = new Pos2(head.line - 1, lineLength(cm, head.line - 1)); } } return [anchor, head]; } function updateCmSelection(cm, sel, mode) { var vim2 = cm.state.vim; sel = sel || vim2.sel; var mode = mode || vim2.visualLine ? "line" : vim2.visualBlock ? "block" : "char"; var cmSel = makeCmSelection(cm, sel, mode); cm.setSelections(cmSel.ranges, cmSel.primary); } 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, 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, head }], primary: 0 }; } else if (mode == "block") { var top = Math.min(anchor.line, head.line), fromCh = anchor.ch, bottom = Math.max(anchor.line, head.line), toCh = head.ch; if (fromCh < toCh) { toCh += 1; } else { fromCh += 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: new Pos2(top + i, fromCh), head: new Pos2(top + i, toCh) }); } return { ranges, primary }; } } function getHead(cm) { var cur = cm.getCursor("head"); if (cm.getSelection().length == 1) { cur = cursorMin(cur, cm.getCursor("anchor")); } return cur; } function exitVisualMode(cm, moveHead) { var vim2 = cm.state.vim; if (moveHead !== false) { cm.setCursor(clipCursorToContent(cm, vim2.sel.head)); } updateLastSelection(cm, vim2); vim2.visualMode = false; vim2.visualLine = false; vim2.visualBlock = false; if (!vim2.insertMode) CodeMirror2.signal(cm, "vim-mode-change", { mode: "normal" }); } function clipToLine(cm, curStart, curEnd) { var selection = cm.getRange(curStart, curEnd); if (/\n\s*$/.test(selection)) { var lines = selection.split("\n"); lines.pop(); var line; for (var line = lines.pop(); lines.length > 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 ? text.length : firstNonWS; } function expandWordUnderCursor(cm, inclusive, innerWord, bigWord, noSymbol) { var cur = getHead(cm); var line = cm.getLine(cur.line); var idx = cur.ch; var test = noSymbol ? wordCharTest[0] : bigWordCharTest[0]; if (innerWord && /\s/.test(line.charAt(idx))) { test = function(ch) { return /\s/.test(ch); }; } else { while (!test(line.charAt(idx))) { idx++; if (idx >= line.length) { 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 < line.length) { end++; } while (test(line.charAt(start)) && start >= 0) { start--; } start++; if (inclusive) { var wordEnd = end; while (/\s/.test(line.charAt(end)) && end < line.length) { end++; } if (wordEnd == end) { var wordStart = start; while (/\s/.test(line.charAt(start - 1)) && start > 0) { start--; } if (!start) { start = wordStart; } } } return { start: new Pos2(cur.line, start), end: new Pos2(cur.line, end) }; } function expandTagUnderCursor(cm, head, inclusive) { var cur = head; if (!CodeMirror2.findMatchingTag || !CodeMirror2.findEnclosingTag) { return { start: cur, end: cur }; } var tags = CodeMirror2.findMatchingTag(cm, head) || CodeMirror2.findEnclosingTag(cm, head); if (!tags || !tags.open || !tags.close) { return { start: cur, end: cur }; } if (inclusive) { return { start: tags.open.from, end: tags.close.to }; } return { start: tags.open.to, end: tags.close.from }; } function recordJumpPosition(cm, oldCur, newCur) { if (!cursorEqual(oldCur, newCur)) { vimGlobalState.jumpList.add(cm, oldCur, newCur); } } function recordLastCharacterSearch(increment, args) { vimGlobalState.lastCharacterSearch.increment = increment; vimGlobalState.lastCharacterSearch.forward = args.forward; vimGlobalState.lastCharacterSearch.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; } }, // TODO: The original Vim implementation only operates on level 1 and 2. // The current implementation doesn't check for code block level and // therefore it operates on any levels. 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, nextCh: lineText.charAt(curCh), lastCh: null, index: curCh, symb, reverseSymb: (forward ? { ")": "(", "}": "{" } : { "(": ")", "{": "}" })[symb], 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 = state.lineText.length; 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 new Pos2(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 : line.length; } while (true) { if (emptyLineIsWord && line == "") { return { from: 0, to: 0, line: lineNum }; } var stop = dir > 0 ? line.length : -1; var wordStart = stop, wordEnd = stop; while (pos != stop) { var foundWord = false; for (var i = 0; i < charTests.length && !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 : line.length; } } 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 = new Pos2(word.line, forward ? word.to - 1 : word.from); } var shortCircuit = words.length != 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 new Pos2(lastWord.line, lastWord.from); } else if (forward && wordEnd) { return new Pos2(lastWord.line, lastWord.to - 1); } else if (!forward && wordEnd) { if (!shortCircuit && (firstWord.to != curStart.ch || firstWord.line != curStart.line)) { lastWord = words.pop(); } return new Pos2(lastWord.line, lastWord.to); } else { return new Pos2(lastWord.line, lastWord.from); } } function moveToEol(cm, head, motionArgs, vim2, keepHPos) { var cur = head; var retval = new Pos2(cur.line + motionArgs.repeat - 1, Infinity); var end = cm.clipPos(retval); end.ch--; if (!keepHPos) { vim2.lastHPos = Infinity; vim2.lastHSPos = cm.charCoords(end, "div").left; } return retval; } function moveToCharacter(cm, repeat, forward, character, head) { var cur = head || 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 new Pos2(cm.getCursor().line, idx); } function moveToColumn(cm, repeat) { var line = cm.getCursor().line; return clipCursorToContent(cm, new Pos2(line, repeat - 1)); } function updateMark(cm, vim2, markName, pos) { if (!inArray(markName, validMarks)) { return; } if (vim2.marks[markName]) { vim2.marks[markName].clear(); } vim2.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(i2) { return !cm.getLine(i2); } function isBoundary(i2, dir2, any) { if (any) { return isEmpty(i2) != isEmpty(i2 + dir2); } return !isEmpty(i2) && isEmpty(i2 + dir2); } if (dir) { while (min <= i && i <= max && repeat > 0) { if (isBoundary(i, dir)) { repeat--; } i += dir; } return new Pos2(i, 0); } var vim2 = cm.state.vim; if (vim2.visualLine && isBoundary(line, 1, true)) { var anchor = vim2.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 Pos2(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 Pos2(i, 0); return { start, end }; } function getSentence(cm, cur, repeat, dir, inclusive) { function nextChar(curr) { if (curr.pos + curr.dir < 0 || curr.pos + curr.dir >= curr.line.length) { curr.line = null; } else { curr.pos += curr.dir; } } function forward(cm2, ln, pos, dir2) { var line = cm2.getLine(ln); var curr = { line, ln, pos, dir: dir2 }; if (curr.line === "") { return { ln: curr.ln, pos: curr.pos }; } var lastSentencePos = curr.pos; nextChar(curr); while (curr.line !== null) { lastSentencePos = curr.pos; if (isEndOfSentenceSymbol(curr.line[curr.pos])) { if (!inclusive) { return { ln: curr.ln, pos: curr.pos + 1 }; } else { nextChar(curr); while (curr.line !== null) { if (isWhiteSpaceString(curr.line[curr.pos])) { lastSentencePos = curr.pos; nextChar(curr); } else { break; } } return { ln: curr.ln, pos: lastSentencePos + 1 }; } } nextChar(curr); } return { ln: curr.ln, pos: lastSentencePos + 1 }; } function reverse(cm2, ln, pos, dir2) { var line = cm2.getLine(ln); var curr = { line, ln, pos, dir: dir2 }; if (curr.line === "") { return { ln: curr.ln, pos: curr.pos }; } var lastSentencePos = curr.pos; nextChar(curr); while (curr.line !== null) { if (!isWhiteSpaceString(curr.line[curr.pos]) && !isEndOfSentenceSymbol(curr.line[curr.pos])) { lastSentencePos = curr.pos; } else if (isEndOfSentenceSymbol(curr.line[curr.pos])) { if (!inclusive) { return { ln: curr.ln, pos: lastSentencePos }; } else { if (isWhiteSpaceString(curr.line[curr.pos + 1])) { return { ln: curr.ln, pos: curr.pos + 1 }; } else { return { ln: curr.ln, pos: lastSentencePos }; } } } nextChar(curr); } curr.line = line; if (inclusive && isWhiteSpaceString(curr.line[curr.pos])) { return { ln: curr.ln, pos: curr.pos }; } else { return { ln: curr.ln, pos: lastSentencePos }; } } var curr_index = { ln: cur.line, pos: cur.ch }; while (repeat > 0) { if (dir < 0) { curr_index = reverse(cm, curr_index.ln, curr_index.pos, dir); } else { curr_index = forward(cm, curr_index.ln, curr_index.pos, dir); } repeat--; } return new Pos2(curr_index.ln, curr_index.pos); } function findSentence(cm, cur, repeat, dir) { function nextChar(cm2, idx) { if (idx.pos + idx.dir < 0 || idx.pos + idx.dir >= idx.line.length) { idx.ln += idx.dir; if (!isLine(cm2, idx.ln)) { idx.line = null; idx.ln = null; idx.pos = null; return; } idx.line = cm2.getLine(idx.ln); idx.pos = idx.dir > 0 ? 0 : idx.line.length - 1; } else { idx.pos += idx.dir; } } function forward(cm2, ln, pos, dir2) { var line = cm2.getLine(ln); var stop = line === ""; var curr = { line, ln, pos, dir: dir2 }; var last_valid = { ln: curr.ln, pos: curr.pos }; var skip_empty_lines = curr.line === ""; nextChar(cm2, curr); while (curr.line !== null) { last_valid.ln = curr.ln; last_valid.pos = curr.pos; if (curr.line === "" && !skip_empty_lines) { return { ln: curr.ln, pos: curr.pos }; } else if (stop && curr.line !== "" && !isWhiteSpaceString(curr.line[curr.pos])) { return { ln: curr.ln, pos: curr.pos }; } else if (isEndOfSentenceSymbol(curr.line[curr.pos]) && !stop && (curr.pos === curr.line.length - 1 || isWhiteSpaceString(curr.line[curr.pos + 1]))) { stop = true; } nextChar(cm2, curr); } var line = cm2.getLine(last_valid.ln); last_valid.pos = 0; for (var i = line.length - 1; i >= 0; --i) { if (!isWhiteSpaceString(line[i])) { last_valid.pos = i; break; } } return last_valid; } function reverse(cm2, ln, pos, dir2) { var line = cm2.getLine(ln); var curr = { line, ln, pos, dir: dir2 }; var last_valid = { ln: curr.ln, pos: null }; var skip_empty_lines = curr.line === ""; nextChar(cm2, curr); while (curr.line !== null) { if (curr.line === "" && !skip_empty_lines) { if (last_valid.pos !== null) { return last_valid; } else { return { ln: curr.ln, pos: curr.pos }; } } else if (isEndOfSentenceSymbol(curr.line[curr.pos]) && last_valid.pos !== null && !(curr.ln === last_valid.ln && curr.pos + 1 === last_valid.pos)) { return last_valid; } else if (curr.line !== "" && !isWhiteSpaceString(curr.line[curr.pos])) { skip_empty_lines = false; last_valid = { ln: curr.ln, pos: curr.pos }; } nextChar(cm2, curr); } var line = cm2.getLine(last_valid.ln); last_valid.pos = 0; for (var i = 0; i < line.length; ++i) { if (!isWhiteSpaceString(line[i])) { last_valid.pos = i; break; } } return last_valid; } var curr_index = { ln: cur.line, pos: cur.ch }; while (repeat > 0) { if (dir < 0) { curr_index = reverse(cm, curr_index.ln, curr_index.pos, dir); } else { curr_index = forward(cm, curr_index.ln, curr_index.pos, dir); } repeat--; } return new Pos2(curr_index.ln, curr_index.pos); } 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(new Pos2(cur.line, cur.ch + offset), -1, void 0, { "bracketRegex": bracketRegexp }); end = cm.scanForBracket(new Pos2(cur.line, cur.ch + offset), 1, void 0, { "bracketRegex": bracketRegexp }); if (!start || !end) return null; 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, 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) { var stringAfter = /string/.test(cm.getTokenTypeAt(offsetCursor(head, 0, 1))); var stringBefore = /string/.test(cm.getTokenTypeAt(head)); var isStringStart = stringAfter && !stringBefore; if (!isStringStart) { 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 = chars.length; i < len && !end; i++) { if (chars[i] == symb) { end = i; } } } if (!start || !end) { return { start: cur, end: cur }; } if (inclusive) { --start; ++end; } return { start: new Pos2(cur.line, start), end: new Pos2(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 vim2 = cm.state.vim; return vim2.searchState_ || (vim2.searchState_ = new SearchState()); } function splitBySlash(argString) { return splitBySeparator(argString, "/"); } function findUnescapedSlashes(argString) { return findUnescapedSeparators(argString, "/"); } function splitBySeparator(argString, separator) { var slashes = findUnescapedSeparators(argString, separator) || []; if (!slashes.length) return []; var tokens = []; if (slashes[0] !== 0) return; for (var i = 0; i < slashes.length; i++) { if (typeof slashes[i] == "number") tokens.push(argString.substring(slashes[i] + 1, slashes[i + 1])); } return tokens; } function findUnescapedSeparators(str, separator) { if (!separator) separator = "/"; var escapeNextChar = false; var slashes = []; for (var i = 0; i < str.length; i++) { var c = str.charAt(i); if (!escapeNextChar && c == separator) { 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 < str.length; 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": " " }; function translateRegexReplace(str) { var escapeNextChar = false; var out = []; for (var i = -1; i < str.length; 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": " ", "\\&": "&" }; function unescapeRegexReplace(str) { var stream = new CodeMirror2.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 (!slashes.length) { 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 ? "im" : "m" ); return regexp; } function dom(n) { if (typeof n === "string") n = document.createElement(n); for (var a, i = 1; i < arguments.length; i++) { if (!(a = arguments[i])) continue; if (typeof a !== "object") a = document.createTextNode(a); if (a.nodeType) n.appendChild(a); else for (var key in a) { if (!Object.prototype.hasOwnProperty.call(a, key)) continue; if (key[0] === "$") n.style[key.slice(1)] = a[key]; else n.setAttribute(key, a[key]); } } return n; } function showConfirm(cm, template) { var pre = dom("div", { $color: "red", $whiteSpace: "pre", class: "cm-vim-message" }, template); if (cm.openNotification) { cm.openNotification(pre, { bottom: true, duration: 5e3 }); } else { alert(pre.innerText); } } function makePrompt(prefix, desc) { return dom( "div", { $display: "flex" }, dom( "span", { $fontFamily: "monospace", $whiteSpace: "pre", $flex: 1 }, prefix, dom("input", { type: "text", autocorrect: "off", autocapitalize: "off", spellcheck: "false", $width: "100%" }) ), desc && dom("span", { $color: "#888" }, desc) ); } function showPrompt(cm, options2) { var template = makePrompt(options2.prefix, options2.desc); if (cm.openDialog) { cm.openDialog(template, options2.onClose, { onKeyDown: options2.onKeyDown, onKeyUp: options2.onKeyUp, bottom: true, selectValueOnOpen: false, value: options2.value }); } else { var shortText = ""; if (typeof options2.prefix != "string" && options2.prefix) shortText += options2.prefix.textContent; if (options2.desc) shortText += " " + options2.desc; options2.onClose(prompt(shortText, "")); } } function regexEqual(r1, r2) { if (r1 instanceof RegExp && r2 instanceof RegExp) { var props = ["global", "multiline", "ignoreCase", "source"]; for (var i = 0; i < props.length; 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 (match[0].length == 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 }; } var highlightTimeout = 0; function highlightSearchMatches(cm, query) { clearTimeout(highlightTimeout); highlightTimeout = setTimeout(function() { if (!cm.state.vim) return; 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()) { searchState.getScrollbarAnnotate().clear(); } searchState.setScrollbarAnnotate(cm.showMatchesOnScrollbar(query)); } searchState.setOverlay(overlay); } }, 50); } function findNext(cm, prev, query, repeat) { if (repeat === void 0) { 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)) { var lastEndPos = prev ? cursor.from() : cursor.to(); found = cursor.find(prev); if (found && !found[0] && cursorEqual(cursor.from(), lastEndPos)) { if (cm.getLine(lastEndPos.line).length == lastEndPos.ch) found = cursor.find(prev); } } if (!found) { cursor = cm.getSearchCursor( query, prev ? new Pos2(cm.lastLine()) : new Pos2(cm.firstLine(), 0) ); if (!cursor.find(prev)) { return; } } } return cursor.from(); }); } function findNextFromAndToInclusive(cm, prev, query, repeat, vim2) { if (repeat === void 0) { repeat = 1; } return cm.operation(function() { var pos = cm.getCursor(); var cursor = cm.getSearchCursor(query, pos); var found = cursor.find(!prev); if (!vim2.visualMode && found && cursorEqual(cursor.from(), pos)) { cursor.find(!prev); } for (var i = 0; i < repeat; i++) { found = cursor.find(prev); if (!found) { cursor = cm.getSearchCursor( query, prev ? new Pos2(cm.lastLine()) : new Pos2(cm.firstLine(), 0) ); if (!cursor.find(prev)) { return; } } } return [cursor.from(), cursor.to()]; }); } function clearSearchHighlight(cm) { var state = getSearchState(cm); cm.removeOverlay(getSearchState(cm).getOverlay()); state.setOverlay(null); if (state.getScrollbarAnnotate()) { state.getScrollbarAnnotate().clear(); 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 (typeof end == "number") { 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 }; } function getMarkPos(cm, vim2, markName) { if (markName == "'" || markName == "`") { return vimGlobalState.jumpList.find(cm, -1) || new Pos2(0, 0); } else if (markName == ".") { return getLastEditPos(cm); } var mark = vim2.marks[markName]; return mark && mark.find(); } function getLastEditPos(cm) { if (cm.getLastEditEnd) { return cm.getLastEditEnd(); } var done = cm.doc.history.done; for (var i = done.length; i--; ) { if (done[i].changes) { return copyCursor(done[i].changes[0].to); } } } 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 vim2 = cm.state.vim; var commandHistoryRegister = vimGlobalState.registerController.getRegister(":"); var previousCommand = commandHistoryRegister.toString(); var inputStream = new CodeMirror2.StringStream(input); commandHistoryRegister.setText(input); var params = opt_params || {}; params.input = input; try { this.parseInput_(cm, inputStream, params); } catch (e) { showConfirm(cm, e.toString()); throw e; } if (vim2.visualMode) { exitVisualMode(cm); } var command; var commandName; if (!params.commandName) { if (params.line !== void 0) { 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 < command.toKeys.length; i++) { vimApi.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.toString()); throw e; } }, parseInput_: function(cm, inputStream, result) { var _a, _b; inputStream.eatWhile(":"); if (inputStream.eat("%")) { result.line = cm.firstLine(); result.lineEnd = cm.lastLine(); } else { result.line = this.parseLineSpec_(cm, inputStream); if (result.line !== void 0 && inputStream.eat(",")) { result.lineEnd = this.parseLineSpec_(cm, inputStream); } } if (result.line == void 0) { if (cm.state.vim.visualMode) { result.selectionLine = (_a = getMarkPos(cm, cm.state.vim, "<")) == null ? void 0 : _a.line; result.selectionLineEnd = (_b = getMarkPos(cm, cm.state.vim, ">")) == null ? void 0 : _b.line; } else { result.selectionLine = cm.getCursor().line; } } else { result.selectionLine = result.line; result.selectionLineEnd = result.lineEnd; } var commandMatch2 = inputStream.match(/^(\w+|!!|@@|[!#&*<=>@~])/); if (commandMatch2) { result.commandName = commandMatch2[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 this.parseLineSpecOffset_(inputStream, cm.getCursor().line); case "$": return this.parseLineSpecOffset_(inputStream, cm.lastLine()); case "'": var markName = inputStream.next(); var markPos = getMarkPos(cm, cm.state.vim, markName); if (!markPos) throw new Error("Mark not set"); return this.parseLineSpecOffset_(inputStream, markPos.line); case "-": case "+": inputStream.backUp(1); return this.parseLineSpecOffset_(inputStream, cm.getCursor().line); default: inputStream.backUp(1); return void 0; } }, parseLineSpecOffset_: function(inputStream, line) { var offsetMatch = inputStream.match(/^([+-])?(\d+)/); if (offsetMatch) { var offset = parseInt(offsetMatch[2], 10); if (offsetMatch[1] == "-") { line -= offset; } else { line += offset; } } return line; }, 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 (args.length && args[0]) { params.args = args; } }, matchCommand_: function(commandName) { for (var i = commandName.length; 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 < defaultExCommandMap.length; i++) { var command = defaultExCommandMap[i]; var key = command.shortName || command.name; this.commandMap_[key] = command; } }, map: function(lhs, rhs, ctx, noremap2) { 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) } }; if (ctx) { mapping.context = ctx; } defaultKeymap.unshift(mapping); } else { var mapping = { keys: lhs, type: "keyToKey", toKeys: rhs, noremap: noremap2 }; 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 true; } } else { var keys2 = lhs; for (var i = 0; i < defaultKeymap.length; i++) { if (keys2 == defaultKeymap[i].keys && defaultKeymap[i].context === ctx) { defaultKeymap.splice(i, 1); return true; } } } } }; var exCommands = { colorscheme: function(cm, params) { if (!params.args || params.args.length < 1) { showConfirm(cm, cm.getOption("theme")); return; } cm.setOption("theme", params.args[0]); }, map: function(cm, params, ctx, defaultOnly) { var mapArgs = params.args; if (!mapArgs || mapArgs.length < 2) { if (cm) { showConfirm(cm, "Invalid mapping: " + params.input); } return; } exCommandDispatcher.map(mapArgs[0], mapArgs[1], ctx, defaultOnly); }, 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"); }, omap: function(cm, params) { this.map(cm, params, "operatorPending"); }, noremap: function(cm, params) { this.map(cm, params, void 0, true); }, inoremap: function(cm, params) { this.map(cm, params, "insert", true); }, nnoremap: function(cm, params) { this.map(cm, params, "normal", true); }, vnoremap: function(cm, params) { this.map(cm, params, "visual", true); }, onoremap: function(cm, params) { this.map(cm, params, "operatorPending", true); }, unmap: function(cm, params, ctx) { var mapArgs = params.args; if (!mapArgs || mapArgs.length < 1 || !exCommandDispatcher.unmap(mapArgs[0], ctx)) { if (cm) { showConfirm(cm, "No such mapping: " + params.input); } } }, mapclear: function(cm, params) { vimApi.mapclear(); }, imapclear: function(cm, params) { vimApi.mapclear("insert"); }, nmapclear: function(cm, params) { vimApi.mapclear("normal"); }, vmapclear: function(cm, params) { vimApi.mapclear("visual"); }, omapclear: function(cm, params) { vimApi.mapclear("operatorPending"); }, 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 || setArgs.length < 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(optionName.length - 1) == "?") { if (value) { throw Error("Trailing characters: " + params.argString); } optionName = optionName.substring(0, optionName.length - 1); forceGet = true; } if (value === void 0 && optionName.substring(0, 2) == "no") { optionName = optionName.substring(2); value = false; } var optionIsBoolean = options[optionName] && options[optionName].type == "boolean"; if (optionIsBoolean && value == void 0) { value = true; } if (!optionIsBoolean && value === void 0 || forceGet) { var oldValue = getOption(optionName, cm, setCfg); if (oldValue instanceof Error) { showConfirm(cm, oldValue.message); } else if (oldValue === true || oldValue === false) { showConfirm(cm, " " + (oldValue ? "" : "no") + optionName); } else { showConfirm(cm, " " + optionName + "=" + oldValue); } } else { var setOptionReturn = setOption(optionName, value, cm, setCfg); if (setOptionReturn instanceof Error) { showConfirm(cm, setOptionReturn.message); } } }, 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----------\n\n"; if (!regArgs) { for (var registerName in registers) { var text = registers[registerName].toString(); if (text.length) { regInfo += '"' + registerName + " " + text + "\n"; } } } else { var registerName; regArgs = regArgs.join(""); for (var i = 0; i < regArgs.length; i++) { registerName = regArgs.charAt(i); if (!vimGlobalState.registerController.isValidRegister(registerName)) { continue; } var register = registers[registerName] || new Register(); regInfo += '"' + registerName + " " + register.toString() + "\n"; } } showConfirm(cm, regInfo); }, sort: function(cm, params) { var reverse, ignoreCase, unique, number, pattern; function parseArgs() { if (params.argString) { var args = new CodeMirror2.StringStream(params.argString); if (args.eat("!")) { reverse = true; } if (args.eol()) { return; } if (!args.eatSpace()) { return "Invalid arguments"; } var opts = args.match(/([dinuox]+)?\s*(\/.+\/)?\s*/); if (!opts && !args.eol()) { return "Invalid arguments"; } if (opts[1]) { ignoreCase = opts[1].indexOf("i") != -1; unique = opts[1].indexOf("u") != -1; var decimal = opts[1].indexOf("d") != -1 || opts[1].indexOf("n") != -1 && 1; var hex = opts[1].indexOf("x") != -1 && 1; var octal = opts[1].indexOf("o") != -1 && 1; if (decimal + hex + octal > 1) { return "Invalid arguments"; } number = decimal && "decimal" || hex && "hex" || octal && "octal"; } if (opts[2]) { pattern = new RegExp(opts[2].substr(1, opts[2].length - 2), ignoreCase ? "i" : ""); } } } 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 = new Pos2(lineStart, 0); var curEnd = new Pos2(lineEnd, lineLength(cm, lineEnd)); var text = cm.getRange(curStart, curEnd).split("\n"); var numberRegex2 = pattern ? pattern : 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 || pattern) { for (var i = 0; i < text.length; i++) { var matchPart = pattern ? text[i].match(pattern) : null; if (matchPart && matchPart[0] != "") { numPart.push(matchPart); } else if (!pattern && numberRegex2.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 && numberRegex2.exec(a); var bnum = number && numberRegex2.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; } function comparePatternFn(a, b) { if (reverse) { var tmp; tmp = a; a = b; b = tmp; } if (ignoreCase) { a[0] = a[0].toLowerCase(); b[0] = b[0].toLowerCase(); } return a[0] < b[0] ? -1 : 1; } numPart.sort(pattern ? comparePatternFn : compareFn); if (pattern) { for (var i = 0; i < numPart.length; i++) { numPart[i] = numPart[i].input; } } else if (!number) { textPart.sort(compareFn); } text = !reverse ? textPart.concat(numPart) : numPart.concat(textPart); if (unique) { var textOld = text; var lastLine; text = []; for (var i = 0; i < textOld.length; i++) { if (textOld[i] != lastLine) { text.push(textOld[i]); } lastLine = textOld[i]; } } cm.replaceRange(text.join("\n"), curStart, curEnd); }, vglobal: function(cm, params) { this.global(cm, params); }, normal: function(cm, params) { var argString = params.argString && params.argString.trimStart(); if (!argString) { showConfirm(cm, "Argument is required."); return; } var line = params.line; if (typeof line == "number") { var lineEnd = isNaN(params.lineEnd) ? line : params.lineEnd; for (var i = line; i <= lineEnd; i++) { cm.setCursor(i, 0); doKeyToKey(cm, params.argString.trimStart()); if (cm.state.vim.insertMode) { exitInsertMode(cm, true); } } } else { doKeyToKey(cm, params.argString.trimStart()); if (cm.state.vim.insertMode) { exitInsertMode(cm, true); } } }, global: function(cm, params) { var argString = params.argString; if (!argString) { showConfirm(cm, "Regular Expression missing from global"); return; } var inverted = params.commandName[0] === "v"; if (argString[0] === "!" && params.commandName[0] === "g") { inverted = true; argString = argString.slice(1); } var lineStart = params.line !== void 0 ? 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, tokens.length).join("/"); } if (regexPart) { try { updateSearchQuery( cm, regexPart, true, true /** smartCase */ ); } catch (e) { showConfirm(cm, "Invalid regex: " + regexPart); return; } } var query = getSearchState(cm).getQuery(); var matchedLines = []; for (var i = lineStart; i <= lineEnd; i++) { var line = cm.getLine(i); var matched = query.test(line); if (matched !== inverted) { matchedLines.push(cmd ? cm.getLineHandle(i) : line); } } if (!cmd) { showConfirm(cm, matchedLines.join("\n")); return; } var index = 0; var nextCommand = function() { if (index < matchedLines.length) { var lineHandle = matchedLines[index++]; var lineNum = cm.getLineNumber(lineHandle); if (lineNum == null) { nextCommand(); return; } var command = lineNum + 1 + cmd; exCommandDispatcher.processCommand(cm, command, { callback: nextCommand }); } else if (cm.releaseLineHandles) { cm.releaseLineHandles(); } }; 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 ? splitBySeparator(argString, argString[0]) : []; var regexPart, replacePart = "", trailing, flagsPart, count; var confirm = false; var global = false; if (tokens.length) { regexPart = tokens[0]; if (getOption("pcre") && regexPart !== "") { regexPart = new RegExp(regexPart).source; } replacePart = tokens[1]; if (replacePart !== void 0) { if (getOption("pcre")) { replacePart = unescapeRegexReplace(replacePart.replace(/([^\\])&/g, "$1$$&")); } else { replacePart = translateRegexReplace(replacePart); } vimGlobalState.lastSubstituteReplacePart = replacePart; } trailing = tokens[2] ? tokens[2].split(" ") : []; } else { if (argString && argString.length) { 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; } if (flagsPart.indexOf("g") != -1) { global = true; } if (getOption("pcre")) { regexPart = regexPart + "/" + flagsPart; } else { regexPart = regexPart.replace(/\//g, "\\/") + "/" + flagsPart; } } } if (regexPart) { try { updateSearchQuery( cm, regexPart, true, true /** smartCase */ ); } catch (e) { showConfirm(cm, "Invalid regex: " + regexPart); return; } } replacePart = replacePart || vimGlobalState.lastSubstituteReplacePart; if (replacePart === void 0) { showConfirm(cm, "No previous substitute regular expression"); return; } var state = getSearchState(cm); var query = state.getQuery(); var lineStart = params.line !== void 0 ? 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, new Pos2(lineStart, 0)); var cursor = cm.getSearchCursor(query, startPos); doReplace(cm, confirm, global, lineStart, lineEnd, cursor, query, replacePart, params.callback); }, redo: CodeMirror2.commands.redo, undo: CodeMirror2.commands.undo, write: function(cm) { if (CodeMirror2.commands.save) { CodeMirror2.commands.save(cm); } else if (cm.save) { cm.save(); } }, nohlsearch: function(cm) { clearSearchHighlight(cm); }, yank: function(cm) { var cur = copyCursor(cm.getCursor()); var line = cur.line; var lineText = cm.getLine(line); vimGlobalState.registerController.pushText( "0", "yank", lineText, true, true ); }, delete: function(cm, params) { var line = params.selectionLine; var lineEnd = isNaN(params.selectionLineEnd) ? line : params.selectionLineEnd; operators.delete(cm, { linewise: true }, [ { anchor: new Pos2(line, 0), head: new Pos2(lineEnd + 1, 0) } ]); }, join: function(cm, params) { var line = params.selectionLine; var lineEnd = isNaN(params.selectionLineEnd) ? line : params.selectionLineEnd; cm.setCursor(new Pos2(line, 0)); actions.joinLines(cm, { repeat: lineEnd - line }, cm.state.vim); }, delmarks: function(cm, params) { if (!params.argString || !trim(params.argString)) { showConfirm(cm, "Argument required"); return; } var state = cm.state.vim; var stream = new CodeMirror2.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, modifiedLineNumber, joined; function replaceAll() { cm.operation(function() { while (!done) { replace(); next(); } stop(); }); } function replace() { var text = cm.getRange(searchCursor.from(), searchCursor.to()); var newText = text.replace(query, replaceWith); var unmodifiedLineNumber = searchCursor.to().line; searchCursor.replace(newText); modifiedLineNumber = searchCursor.to().line; lineEnd += modifiedLineNumber - unmodifiedLineNumber; joined = modifiedLineNumber < unmodifiedLineNumber; } function findNextValidMatch() { var lastMatchTo = lastPos && copyCursor(searchCursor.to()); var match = searchCursor.findNext(); if (match && !match[0] && lastMatchTo && cursorEqual(searchCursor.from(), lastMatchTo)) { match = searchCursor.findNext(); } return match; } function next() { while (findNextValidMatch() && isInRange(searchCursor.from(), lineStart, lineEnd)) { if (!global && searchCursor.from().line == modifiedLineNumber && !joined) { 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 vim2 = cm.state.vim; vim2.exMode = false; vim2.lastHPos = vim2.lastHSPos = lastPos.ch; } if (callback) { callback(); } } function onPromptKeyDown(e, _value, close) { CodeMirror2.e_stop(e); var keyName = CodeMirror2.keyName(e); switch (keyName) { case "Y": replace(); next(); break; case "N": next(); break; case "A": var savedCallback = callback; callback = void 0; 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: dom("span", "replace with ", dom("strong", replaceWith), " (y/n/a/q/l)"), onKeyDown: onPromptKeyDown }); } CodeMirror2.keyMap.vim = { attach: attachVimMap, detach: detachVimMap, call: cmKey }; function exitInsertMode(cm, keepCursor) { var vim2 = cm.state.vim; var macroModeState = vimGlobalState.macroModeState; var insertModeChangeRegister = vimGlobalState.registerController.getRegister("."); var isPlaying = macroModeState.isPlaying; var lastChange = macroModeState.lastInsertModeChanges; if (!isPlaying) { cm.off("change", onChange); if (vim2.insertEnd) vim2.insertEnd.clear(); vim2.insertEnd = null; CodeMirror2.off(cm.getInputField(), "keydown", onKeyEventTargetKeyDown); } if (!isPlaying && vim2.insertModeRepeat > 1) { repeatLastEdit( cm, vim2, vim2.insertModeRepeat - 1, true /** repeatForInsert */ ); vim2.lastEditInputState.repeatOverride = vim2.insertModeRepeat; } delete vim2.insertModeRepeat; vim2.insertMode = false; if (!keepCursor) { 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("")); CodeMirror2.signal(cm, "vim-mode-change", { mode: "normal" }); if (macroModeState.isRecording) { logInsertModeChange(macroModeState); } } function _mapCommand(command) { defaultKeymap.unshift(command); } function mapCommand(keys2, type, name, args, extra) { var command = { keys: keys2, type }; command[type] = name; command[type + "Args"] = args; for (var key in extra) command[key] = extra[key]; _mapCommand(command); } defineOption("insertModeEscKeysTimeout", 200, "number"); CodeMirror2.keyMap["vim-insert"] = { // TODO: override navigation keys so that Esc will cancel automatic // indentation from o, O, i_ fallthrough: ["default"], attach: attachVimMap, detach: detachVimMap, call: cmKey }; CodeMirror2.keyMap["vim-replace"] = { "Backspace": "goCharLeft", fallthrough: ["vim-insert"], attach: attachVimMap, detach: detachVimMap }; function executeMacroRegister(cm, vim2, 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 < keyBuffer.length; i++) { var text = keyBuffer[i]; var match, key; while (text) { match = /<\w+-.+?>|<\w+>|./.exec(text); key = match[0]; text = text.substring(match.index + key.length); vimApi.handleKey(cm, key, "macro"); if (vim2.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) { var vim2 = cm.state.vim; while (changeObj) { lastChange.expectCursorActivityForChange = true; if (lastChange.ignoreCount > 1) { lastChange.ignoreCount--; } else if (changeObj.origin == "+input" || changeObj.origin == "paste" || changeObj.origin === void 0) { var selectionCount = cm.listSelections().length; if (selectionCount > 1) lastChange.ignoreCount = selectionCount; var text = changeObj.text.join("\n"); if (lastChange.maybeReset) { lastChange.changes = []; lastChange.maybeReset = false; } if (text) { if (cm.state.overwrite && !/\n/.test(text)) { lastChange.changes.push([text]); } else { if (text.length > 1) { var insertEnd = vim2 && vim2.insertEnd && vim2.insertEnd.find(); var cursor = cm.getCursor(); if (insertEnd && insertEnd.line == cursor.line) { var offset = insertEnd.ch - cursor.ch; if (offset > 0 && offset < text.length) { lastChange.changes.push([text, offset]); text = ""; } } } if (text) lastChange.changes.push(text); } } } changeObj = changeObj.next; } } } function onCursorActivity(cm) { var vim2 = cm.state.vim; if (vim2.insertMode) { var macroModeState = vimGlobalState.macroModeState; if (macroModeState.isPlaying) { return; } var lastChange = macroModeState.lastInsertModeChanges; if (lastChange.expectCursorActivityForChange) { lastChange.expectCursorActivityForChange = false; } else { lastChange.maybeReset = true; if (vim2.insertEnd) vim2.insertEnd.clear(); vim2.insertEnd = cm.setBookmark(cm.getCursor(), { insertLeft: true }); } } else if (!cm.curOp.isVimOp) { handleExternalSelection(cm, vim2); } } function handleExternalSelection(cm, vim2) { var anchor = cm.getCursor("anchor"); var head = cm.getCursor("head"); if (vim2.visualMode && !cm.somethingSelected()) { exitVisualMode(cm, false); } else if (!vim2.visualMode && !vim2.insertMode && cm.somethingSelected()) { vim2.visualMode = true; vim2.visualLine = false; CodeMirror2.signal(cm, "vim-mode-change", { mode: "visual" }); } if (vim2.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); vim2.sel = { anchor, head }; updateMark(cm, vim2, "<", cursorMin(head, anchor)); updateMark(cm, vim2, ">", cursorMax(head, anchor)); } else if (!vim2.insertMode) { vim2.lastHPos = cm.getCursor().ch; } } function InsertModeKey(keyName) { this.keyName = keyName; } function onKeyEventTargetKeyDown(e) { var macroModeState = vimGlobalState.macroModeState; var lastChange = macroModeState.lastInsertModeChanges; var keyName = CodeMirror2.keyName(e); if (!keyName) { return; } function onKeyFound() { if (lastChange.maybeReset) { lastChange.changes = []; lastChange.maybeReset = false; } lastChange.changes.push(new InsertModeKey(keyName)); return true; } if (keyName.indexOf("Delete") != -1 || keyName.indexOf("Backspace") != -1) { CodeMirror2.lookupKey(keyName, "vim-insert", onKeyFound); } } function repeatLastEdit(cm, vim2, repeat, repeatForInsert) { var macroModeState = vimGlobalState.macroModeState; macroModeState.isPlaying = true; var isAction = !!vim2.lastEditActionCommand; var cachedInputState = vim2.inputState; function repeatCommand() { if (isAction) { commandDispatcher.processAction(cm, vim2, vim2.lastEditActionCommand); } else { commandDispatcher.evalInput(cm, vim2); } } function repeatInsert(repeat2) { if (macroModeState.lastInsertModeChanges.changes.length > 0) { repeat2 = !vim2.lastEditActionCommand ? 1 : repeat2; var changeObject = macroModeState.lastInsertModeChanges; repeatInsertModeChanges(cm, changeObject.changes, repeat2); } } vim2.inputState = vim2.lastEditInputState; if (isAction && vim2.lastEditActionCommand.interlaceInsertRepeat) { for (var i = 0; i < repeat; i++) { repeatCommand(); repeatInsert(1); } } else { if (!repeatForInsert) { repeatCommand(); } repeatInsert(repeat); } vim2.inputState = cachedInputState; if (vim2.insertMode && !repeatForInsert) { exitInsertMode(cm); } macroModeState.isPlaying = false; } function sendCmKey(cm, key) { CodeMirror2.lookupKey(key, "vim-insert", function keyHandler(binding) { if (typeof binding == "string") { CodeMirror2.commands[binding](cm); } else { binding(cm); } return true; }); } function repeatInsertModeChanges(cm, changes, repeat) { var head = cm.getCursor("head"); var visualBlock = vimGlobalState.macroModeState.lastInsertModeChanges.visualBlock; if (visualBlock) { selectForInsert(cm, head, visualBlock + 1); repeat = cm.listSelections().length; cm.setCursor(head); } for (var i = 0; i < repeat; i++) { if (visualBlock) { cm.setCursor(offsetCursor(head, i, 0)); } for (var j = 0; j < changes.length; j++) { var change = changes[j]; if (change instanceof InsertModeKey) { sendCmKey(cm, change.keyName); } else if (typeof change == "string") { cm.replaceSelection(change); } else { var start = cm.getCursor(); var end = offsetCursor(start, 0, change[0].length - (change[1] || 0)); cm.replaceRange(change[0], start, change[1] ? start : end); cm.setCursor(end); } } } if (visualBlock) { cm.setCursor(offsetCursor(head, 0, 1)); } } function cloneVimState(state) { var n = new state.constructor(); Object.keys(state).forEach(function(key) { if (key == "insertEnd") return; var o = state[key]; if (Array.isArray(o)) o = o.slice(); else if (o && typeof o == "object" && o.constructor != Object) o = cloneVimState(o); n[key] = o; }); if (state.sel) { n.sel = { head: state.sel.head && copyCursor(state.sel.head), anchor: state.sel.anchor && copyCursor(state.sel.anchor) }; } return n; } function multiSelectHandleKey(cm, key, origin) { var isHandled = false; var vim2 = vimApi.maybeInitVimState_(cm); var visualBlock = vim2.visualBlock || vim2.wasInVisualBlock; var wasMultiselect = cm.isInMultiSelectMode(); if (vim2.wasInVisualBlock && !wasMultiselect) { vim2.wasInVisualBlock = false; } else if (wasMultiselect && vim2.visualBlock) { vim2.wasInVisualBlock = true; } if (key == "" && !vim2.insertMode && !vim2.visualMode && wasMultiselect && vim2.status == "") { clearInputState(cm); } else if (visualBlock || !wasMultiselect || cm.inVirtualSelectionMode) { isHandled = vimApi.handleKey(cm, key, origin); } else { var old = cloneVimState(vim2); var changeQueueList = vim2.inputState.changeQueueList || []; cm.operation(function() { cm.curOp.isVimOp = true; var index = 0; cm.forEachSelection(function() { cm.state.vim.inputState.changeQueue = changeQueueList[index]; var head = cm.getCursor("head"); var anchor = cm.getCursor("anchor"); var headOffset = !cursorIsBefore(head, anchor) ? -1 : 0; var anchorOffset = cursorIsBefore(head, anchor) ? -1 : 0; head = offsetCursor(head, 0, headOffset); anchor = offsetCursor(anchor, 0, anchorOffset); cm.state.vim.sel.head = head; cm.state.vim.sel.anchor = anchor; isHandled = vimApi.handleKey(cm, key, origin); if (cm.virtualSelection) { changeQueueList[index] = cm.state.vim.inputState.changeQueue; cm.state.vim = cloneVimState(old); } index++; }); if (cm.curOp.cursorActivity && !isHandled) cm.curOp.cursorActivity = false; cm.state.vim = vim2; vim2.inputState.changeQueueList = changeQueueList; vim2.inputState.changeQueue = null; }, true); } if (isHandled && !vim2.visualMode && !vim2.insert && vim2.visualMode != cm.somethingSelected()) { handleExternalSelection(cm, vim2); } return isHandled; } resetVimGlobalState(); return vimApi; } function indexFromPos(doc, pos) { var ch = pos.ch; var lineNumber = pos.line + 1; if (lineNumber < 1) { lineNumber = 1; ch = 0; } if (lineNumber > doc.lines) { lineNumber = doc.lines; ch = Number.MAX_VALUE; } var line = doc.line(lineNumber); return Math.min(line.from + Math.max(0, ch), line.to); } function posFromIndex(doc, offset) { let line = doc.lineAt(offset); return { line: line.number - 1, ch: offset - line.from }; } var Pos = class { constructor(line, ch) { this.line = line; this.ch = ch; } }; function on(emitter, type, f) { if (emitter.addEventListener) { emitter.addEventListener(type, f, false); } else { var map = emitter._handlers || (emitter._handlers = {}); map[type] = (map[type] || []).concat(f); } } function off(emitter, type, f) { if (emitter.removeEventListener) { emitter.removeEventListener(type, f, false); } else { var map = emitter._handlers, arr = map && map[type]; if (arr) { var index = arr.indexOf(f); if (index > -1) { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } } } } function signal(emitter, type, ...args) { var _a; var handlers = (_a = emitter._handlers) === null || _a === void 0 ? void 0 : _a[type]; if (!handlers) return; for (var i = 0; i < handlers.length; ++i) { handlers[i](...args); } } function signalTo(handlers, ...args) { if (!handlers) return; for (var i = 0; i < handlers.length; ++i) { handlers[i](...args); } } var specialKey = { Return: "CR", Backspace: "BS", "Delete": "Del", Escape: "Esc", Insert: "Ins", ArrowLeft: "Left", ArrowRight: "Right", ArrowUp: "Up", ArrowDown: "Down", Enter: "CR", " ": "Space" }; var ignoredKeys = { Shift: 1, Alt: 1, Command: 1, Control: 1, CapsLock: 1, AltGraph: 1, Dead: 1, Unidentified: 1 }; var wordChar; try { wordChar = new RegExp("[\\w\\p{Alphabetic}\\p{Number}_]", "u"); } catch (_) { wordChar = /[\w]/; } function dispatchChange(cm, transaction) { var view = cm.cm6; var type = "input.type.compose"; if (cm.curOp) { if (!cm.curOp.lastChange) type = "input.type.compose.start"; } if (transaction.annotations) { try { transaction.annotations.some(function(note) { if (note.value == "input") note.value = type; }); } catch (e) { console.error(e); } } else { transaction.userEvent = type; } return view.dispatch(transaction); } function runHistoryCommand(cm, revert) { var _a; if (cm.curOp) { cm.curOp.$changeStart = void 0; } (revert ? undo : redo)(cm.cm6); let changeStartIndex = (_a = cm.curOp) === null || _a === void 0 ? void 0 : _a.$changeStart; if (changeStartIndex != null) { cm.cm6.dispatch({ selection: { anchor: changeStartIndex } }); } } var keys = {}; "Left|Right|Up|Down|Backspace|Delete".split("|").forEach((key) => { keys[key] = (cm) => runScopeHandlers(cm.cm6, { key }, "editor"); }); var CodeMirror = class _CodeMirror { constructor(cm6) { this.state = {}; this.marks = /* @__PURE__ */ Object.create(null); this.$mid = 0; this.options = {}; this._handlers = {}; this.$lastChangeEndOffset = 0; this.virtualSelection = null; this.cm6 = cm6; this.onChange = this.onChange.bind(this); this.onSelectionChange = this.onSelectionChange.bind(this); } // -------------------------- openDialog(template, callback, options) { return openDialog(this, template, callback, options); } openNotification(template, options) { return openNotification(this, template, options); } on(type, f) { on(this, type, f); } off(type, f) { off(this, type, f); } signal(type, e, handlers) { signal(this, type, e, handlers); } indexFromPos(pos) { return indexFromPos(this.cm6.state.doc, pos); } posFromIndex(offset) { return posFromIndex(this.cm6.state.doc, offset); } foldCode(pos) { let view = this.cm6; let ranges = view.state.selection.ranges; let doc = this.cm6.state.doc; let index = indexFromPos(doc, pos); let tmpRanges = EditorSelection.create([EditorSelection.range(index, index)], 0).ranges; view.state.selection.ranges = tmpRanges; foldCode(view); view.state.selection.ranges = ranges; } firstLine() { return 0; } lastLine() { return this.cm6.state.doc.lines - 1; } lineCount() { return this.cm6.state.doc.lines; } setCursor(line, ch) { if (typeof line === "object") { ch = line.ch; line = line.line; } var offset = indexFromPos(this.cm6.state.doc, { line, ch }); this.cm6.dispatch({ selection: { anchor: offset } }, { scrollIntoView: !this.curOp }); if (this.curOp && !this.curOp.isVimOp) this.onBeforeEndOperation(); } getCursor(p) { var sel = this.cm6.state.selection.main; var offset = p == "head" || !p ? sel.head : p == "anchor" ? sel.anchor : p == "start" ? sel.from : p == "end" ? sel.to : null; if (offset == null) throw new Error("Invalid cursor type"); return this.posFromIndex(offset); } listSelections() { var doc = this.cm6.state.doc; return this.cm6.state.selection.ranges.map((r) => { return { anchor: posFromIndex(doc, r.anchor), head: posFromIndex(doc, r.head) }; }); } setSelections(p, primIndex) { var doc = this.cm6.state.doc; var ranges = p.map((x) => { return EditorSelection.range(indexFromPos(doc, x.anchor), indexFromPos(doc, x.head)); }); this.cm6.dispatch({ selection: EditorSelection.create(ranges, primIndex) }); } setSelection(anchor, head, options) { var doc = this.cm6.state.doc; var ranges = [EditorSelection.range(indexFromPos(doc, anchor), indexFromPos(doc, head))]; this.cm6.dispatch({ selection: EditorSelection.create(ranges, 0) }); if (options && options.origin == "*mouse") { this.onBeforeEndOperation(); } } getLine(row) { var doc = this.cm6.state.doc; if (row < 0 || row >= doc.lines) return ""; return this.cm6.state.doc.line(row + 1).text; } getLineHandle(row) { if (!this.$lineHandleChanges) this.$lineHandleChanges = []; return { row, index: this.indexFromPos(new Pos(row, 0)) }; } getLineNumber(handle) { var updates = this.$lineHandleChanges; if (!updates) return null; var offset = handle.index; for (var i = 0; i < updates.length; i++) { offset = updates[i].changes.mapPos(offset, 1, MapMode.TrackAfter); if (offset == null) return null; } var pos = this.posFromIndex(offset); return pos.ch == 0 ? pos.line : null; } releaseLineHandles() { this.$lineHandleChanges = void 0; } getRange(s, e) { var doc = this.cm6.state.doc; return this.cm6.state.sliceDoc(indexFromPos(doc, s), indexFromPos(doc, e)); } replaceRange(text, s, e) { if (!e) e = s; var doc = this.cm6.state.doc; var from = indexFromPos(doc, s); var to = indexFromPos(doc, e); dispatchChange(this, { changes: { from, to, insert: text } }); } replaceSelection(text) { dispatchChange(this, this.cm6.state.replaceSelection(text)); } replaceSelections(replacements) { var ranges = this.cm6.state.selection.ranges; var changes = ranges.map((r, i) => { return { from: r.from, to: r.to, insert: replacements[i] || "" }; }); dispatchChange(this, { changes }); } getSelection() { return this.getSelections().join("\n"); } getSelections() { var cm = this.cm6; return cm.state.selection.ranges.map((r) => cm.state.sliceDoc(r.from, r.to)); } somethingSelected() { return this.cm6.state.selection.ranges.some((r) => !r.empty); } getInputField() { return this.cm6.contentDOM; } clipPos(p) { var doc = this.cm6.state.doc; var ch = p.ch; var lineNumber = p.line + 1; if (lineNumber < 1) { lineNumber = 1; ch = 0; } if (lineNumber > doc.lines) { lineNumber = doc.lines; ch = Number.MAX_VALUE; } var line = doc.line(lineNumber); ch = Math.min(Math.max(0, ch), line.to - line.from); return new Pos(lineNumber - 1, ch); } getValue() { return this.cm6.state.doc.toString(); } setValue(text) { var cm = this.cm6; return cm.dispatch({ changes: { from: 0, to: cm.state.doc.length, insert: text }, selection: EditorSelection.range(0, 0) }); } focus() { return this.cm6.focus(); } blur() { return this.cm6.contentDOM.blur(); } defaultTextHeight() { return this.cm6.defaultLineHeight; } findMatchingBracket(pos) { var state = this.cm6.state; var offset = indexFromPos(state.doc, pos); var m = matchBrackets(state, offset + 1, -1); if (m && m.end) { return { to: posFromIndex(state.doc, m.end.from) }; } m = matchBrackets(state, offset, 1); if (m && m.end) { return { to: posFromIndex(state.doc, m.end.from) }; } return { to: void 0 }; } scanForBracket(pos, dir, style, config) { return scanForBracket(this, pos, dir, style, config); } indentLine(line, more) { if (more) this.indentMore(); else this.indentLess(); } indentMore() { indentMore(this.cm6); } indentLess() { indentLess(this.cm6); } execCommand(name) { if (name == "indentAuto") _CodeMirror.commands.indentAuto(this); else if (name == "goLineLeft") cursorLineBoundaryBackward(this.cm6); else if (name == "goLineRight") { cursorLineBoundaryForward(this.cm6); cursorCharBackward(this.cm6); } else console.log(name + " is not implemented"); } setBookmark(cursor, options) { var assoc = (options === null || options === void 0 ? void 0 : options.insertLeft) ? 1 : -1; var offset = this.indexFromPos(cursor); var bm = new Marker(this, offset, assoc); return bm; } addOverlay({ query }) { let cm6Query = new SearchQuery({ regexp: true, search: query.source, caseSensitive: !/i/.test(query.flags) }); if (cm6Query.valid) { cm6Query.forVim = true; this.cm6Query = cm6Query; let effect = setSearchQuery.of(cm6Query); this.cm6.dispatch({ effects: effect }); return cm6Query; } } removeOverlay(overlay) { if (!this.cm6Query) return; this.cm6Query.forVim = false; let effect = setSearchQuery.of(this.cm6Query); this.cm6.dispatch({ effects: effect }); } getSearchCursor(query, pos) { var cm = this; var last = null; var lastCM5Result = null; if (pos.ch == void 0) pos.ch = Number.MAX_VALUE; var firstOffset = indexFromPos(cm.cm6.state.doc, pos); var source = query.source.replace(/(\\.|{(?:\d+(?:,\d*)?|,\d+)})|[{}]/g, function(a, b) { if (!b) return "\\" + a; return b; }); function rCursor(doc, from = 0, to = doc.length) { return new RegExpCursor(doc, source, { ignoreCase: query.ignoreCase }, from, to); } function nextMatch(from) { var doc = cm.cm6.state.doc; if (from > doc.length) return null; let res = rCursor(doc, from).next(); return res.done ? null : res.value; } var ChunkSize = 1e4; function prevMatchInRange(from, to) { var doc = cm.cm6.state.doc; for (let size = 1; ; size++) { let start = Math.max(from, to - size * ChunkSize); let cursor = rCursor(doc, start, to), range = null; while (!cursor.next().done) range = cursor.value; if (range && (start == from || range.from > start + 10)) return range; if (start == from) return null; } } return { findNext: function() { return this.find(false); }, findPrevious: function() { return this.find(true); }, find: function(back) { var doc = cm.cm6.state.doc; if (back) { let endAt = last ? last.from == last.to ? last.to - 1 : last.from : firstOffset; last = prevMatchInRange(0, endAt); } else { let startFrom = last ? last.from == last.to ? last.to + 1 : last.to : firstOffset; last = nextMatch(startFrom); } lastCM5Result = last && { from: posFromIndex(doc, last.from), to: posFromIndex(doc, last.to), match: last.match }; return last && last.match; }, from: function() { return lastCM5Result === null || lastCM5Result === void 0 ? void 0 : lastCM5Result.from; }, to: function() { return lastCM5Result === null || lastCM5Result === void 0 ? void 0 : lastCM5Result.to; }, replace: function(text) { if (last) { dispatchChange(cm, { changes: { from: last.from, to: last.to, insert: text } }); last.to = last.from + text.length; if (lastCM5Result) { lastCM5Result.to = posFromIndex(cm.cm6.state.doc, last.to); } } } }; } findPosV(start, amount, unit, goalColumn) { let { cm6 } = this; const doc = cm6.state.doc; let pixels = unit == "page" ? cm6.dom.clientHeight : 0; const startOffset = indexFromPos(doc, start); let range = EditorSelection.range(startOffset, startOffset, goalColumn); let count = Math.round(Math.abs(amount)); for (let i = 0; i < count; i++) { if (unit == "page") { range = cm6.moveVertically(range, amount > 0, pixels); } else if (unit == "line") { range = cm6.moveVertically(range, amount > 0); } } let pos = posFromIndex(doc, range.head); if (amount < 0 && range.head == 0 && goalColumn != 0 && start.line == 0 && start.ch != 0 || amount > 0 && range.head == doc.length && pos.ch != goalColumn && start.line == pos.line) { pos.hitSide = true; } return pos; } charCoords(pos, mode) { var rect = this.cm6.contentDOM.getBoundingClientRect(); var offset = indexFromPos(this.cm6.state.doc, pos); var coords = this.cm6.coordsAtPos(offset); var d = -rect.top; return { left: ((coords === null || coords === void 0 ? void 0 : coords.left) || 0) - rect.left, top: ((coords === null || coords === void 0 ? void 0 : coords.top) || 0) + d, bottom: ((coords === null || coords === void 0 ? void 0 : coords.bottom) || 0) + d }; } coordsChar(coords, mode) { var rect = this.cm6.contentDOM.getBoundingClientRect(); var offset = this.cm6.posAtCoords({ x: coords.left + rect.left, y: coords.top + rect.top }) || 0; return posFromIndex(this.cm6.state.doc, offset); } getScrollInfo() { var scroller = this.cm6.scrollDOM; return { left: scroller.scrollLeft, top: scroller.scrollTop, height: scroller.scrollHeight, width: scroller.scrollWidth, clientHeight: scroller.clientHeight, clientWidth: scroller.clientWidth }; } scrollTo(x, y) { if (x != null) this.cm6.scrollDOM.scrollLeft = x; if (y != null) this.cm6.scrollDOM.scrollTop = y; } scrollIntoView(pos, margin) { if (pos) { var offset = this.indexFromPos(pos); this.cm6.dispatch({ effects: EditorView.scrollIntoView(offset) }); } else { this.cm6.dispatch({ scrollIntoView: true, userEvent: "scroll" }); } } getWrapperElement() { return this.cm6.dom; } // for tests getMode() { return { name: this.getOption("mode") }; } setSize(w, h) { this.cm6.dom.style.width = w + 4 + "px"; this.cm6.dom.style.height = h + "px"; this.refresh(); } refresh() { this.cm6.measure(); } // event listeners destroy() { this.removeOverlay(); } getLastEditEnd() { return this.posFromIndex(this.$lastChangeEndOffset); } onChange(update) { if (this.$lineHandleChanges) { this.$lineHandleChanges.push(update); } for (let i in this.marks) { let m = this.marks[i]; m.update(update.changes); } if (this.virtualSelection) { this.virtualSelection.ranges = this.virtualSelection.ranges.map((range) => range.map(update.changes)); } var curOp = this.curOp = this.curOp || {}; update.changes.iterChanges((fromA, toA, fromB, toB, text) => { if (curOp.$changeStart == null || curOp.$changeStart > fromB) curOp.$changeStart = fromB; this.$lastChangeEndOffset = toB; var change = { text: text.toJSON() }; if (!curOp.lastChange) { curOp.lastChange = curOp.change = change; } else { curOp.lastChange.next = curOp.lastChange = change; } }, true); if (!curOp.changeHandlers) curOp.changeHandlers = this._handlers["change"] && this._handlers["change"].slice(); } onSelectionChange() { var curOp = this.curOp = this.curOp || {}; if (!curOp.cursorActivityHandlers) curOp.cursorActivityHandlers = this._handlers["cursorActivity"] && this._handlers["cursorActivity"].slice(); this.curOp.cursorActivity = true; } operation(fn) { if (!this.curOp) this.curOp = { $d: 0 }; this.curOp.$d++; try { var result = fn(); } finally { if (this.curOp) { this.curOp.$d--; if (!this.curOp.$d) this.onBeforeEndOperation(); } } return result; } onBeforeEndOperation() { var op = this.curOp; var scrollIntoView = false; if (op) { if (op.change) { signalTo(op.changeHandlers, this, op.change); } if (op && op.cursorActivity) { signalTo(op.cursorActivityHandlers, this, null); if (op.isVimOp) scrollIntoView = true; } this.curOp = null; } if (scrollIntoView) this.scrollIntoView(); } moveH(increment, unit) { if (unit == "char") { var cur = this.getCursor(); this.setCursor(cur.line, cur.ch + increment); } } setOption(name, val) { switch (name) { case "keyMap": this.state.keyMap = val; break; } } getOption(name) { switch (name) { case "firstLineNumber": return 1; case "tabSize": return this.cm6.state.tabSize || 4; case "readonly": return this.cm6.state.readOnly; case "indentWithTabs": return this.cm6.state.facet(indentUnit) == " "; case "indentUnit": return this.cm6.state.facet(indentUnit).length || 2; case "keyMap": return this.state.keyMap || "vim"; } } toggleOverwrite(on2) { this.state.overwrite = on2; } getTokenTypeAt(pos) { var _a; var offset = this.indexFromPos(pos); var tree = ensureSyntaxTree(this.cm6.state, offset); var node = tree === null || tree === void 0 ? void 0 : tree.resolve(offset); var type = ((_a = node === null || node === void 0 ? void 0 : node.type) === null || _a === void 0 ? void 0 : _a.name) || ""; if (/comment/i.test(type)) return "comment"; if (/string/i.test(type)) return "string"; return ""; } overWriteSelection(text) { var doc = this.cm6.state.doc; var sel = this.cm6.state.selection; var ranges = sel.ranges.map((x) => { if (x.empty) { var ch = x.to < doc.length ? doc.sliceString(x.from, x.to + 1) : ""; if (ch && !/\n/.test(ch)) return EditorSelection.range(x.from, x.to + 1); } return x; }); this.cm6.dispatch({ selection: EditorSelection.create(ranges, sel.mainIndex) }); this.replaceSelection(text); } /*** multiselect ****/ isInMultiSelectMode() { return this.cm6.state.selection.ranges.length > 1; } virtualSelectionMode() { return !!this.virtualSelection; } forEachSelection(command) { var selection = this.cm6.state.selection; this.virtualSelection = EditorSelection.create(selection.ranges, selection.mainIndex); for (var i = 0; i < this.virtualSelection.ranges.length; i++) { var range = this.virtualSelection.ranges[i]; if (!range) continue; this.cm6.dispatch({ selection: EditorSelection.create([range]) }); command(); this.virtualSelection.ranges[i] = this.cm6.state.selection.ranges[0]; } this.cm6.dispatch({ selection: this.virtualSelection }); this.virtualSelection = null; } }; CodeMirror.isMac = typeof navigator != "undefined" && /Mac/.test(navigator.platform); CodeMirror.Pos = Pos; CodeMirror.StringStream = StringStream; CodeMirror.commands = { cursorCharLeft: function(cm) { cursorCharLeft(cm.cm6); }, redo: function(cm) { runHistoryCommand(cm, false); }, undo: function(cm) { runHistoryCommand(cm, true); }, newlineAndIndent: function(cm) { insertNewlineAndIndent({ state: cm.cm6.state, dispatch: (tr) => { return dispatchChange(cm, tr); } }); }, indentAuto: function(cm) { indentSelection(cm.cm6); } }; CodeMirror.defineOption = function(name, val, setter) { }; CodeMirror.isWordChar = function(ch) { return wordChar.test(ch); }; CodeMirror.keys = keys; CodeMirror.keyMap = {}; CodeMirror.addClass = function() { }; CodeMirror.rmClass = function() { }; CodeMirror.e_preventDefault = function(e) { e.preventDefault(); }; CodeMirror.e_stop = function(e) { var _a, _b; (_a = e === null || e === void 0 ? void 0 : e.stopPropagation) === null || _a === void 0 ? void 0 : _a.call(e); (_b = e === null || e === void 0 ? void 0 : e.preventDefault) === null || _b === void 0 ? void 0 : _b.call(e); }; CodeMirror.keyName = function(e) { var key = e.key; if (ignoredKeys[key]) return; if (key == "Escape") key = "Esc"; if (key == " ") key = "Space"; if (key.length > 1) { key = key.replace(/Numpad|Arrow/, ""); } if (key.length == 1) key = key.toUpperCase(); var name = ""; if (e.ctrlKey) { name += "Ctrl-"; } if (e.altKey) { name += "Alt-"; } if ((name || key.length > 1) && e.shiftKey) { name += "Shift-"; } name += key; return name; }; CodeMirror.vimKey = function vimKey(e) { var key = e.key; if (ignoredKeys[key]) return; if (key.length > 1 && key[0] == "n") { key = key.replace("Numpad", ""); } key = specialKey[key] || key; var name = ""; if (e.ctrlKey) { name += "C-"; } if (e.altKey) { name += "A-"; } if (e.metaKey) { name += "M-"; } if (CodeMirror.isMac && e.altKey && !e.metaKey && !e.ctrlKey) { name = name.slice(2); } if ((name || key.length > 1) && e.shiftKey) { name += "S-"; } name += key; if (name.length > 1) { name = "<" + name + ">"; } return name; }; CodeMirror.lookupKey = function lookupKey(key, map, handle) { var result = CodeMirror.keys[key]; if (result) handle(result); }; CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal; CodeMirror.findMatchingTag = findMatchingTag; CodeMirror.findEnclosingTag = findEnclosingTag; function dialogDiv(cm, template, bottom) { var dialog = document.createElement("div"); dialog.appendChild(template); return dialog; } function closeNotification(cm, newVal) { if (cm.state.currentNotificationClose) cm.state.currentNotificationClose(); cm.state.currentNotificationClose = newVal; } function openNotification(cm, template, options) { closeNotification(cm, close); var dialog = dialogDiv(cm, template, options && options.bottom); var closed = false; var doneTimer; var duration = options && typeof options.duration !== "undefined" ? options.duration : 5e3; function close() { if (closed) return; closed = true; clearTimeout(doneTimer); dialog.remove(); hideDialog(cm, dialog); } dialog.onclick = function(e) { e.preventDefault(); close(); }; showDialog(cm, dialog); if (duration) doneTimer = setTimeout(close, duration); return close; } function showDialog(cm, dialog) { var oldDialog = cm.state.dialog; cm.state.dialog = dialog; if (dialog && oldDialog !== dialog) { if (oldDialog && oldDialog.contains(document.activeElement)) cm.focus(); if (oldDialog && oldDialog.parentElement) { oldDialog.parentElement.replaceChild(dialog, oldDialog); } else if (oldDialog) { oldDialog.remove(); } CodeMirror.signal(cm, "dialog"); } } function hideDialog(cm, dialog) { if (cm.state.dialog == dialog) { cm.state.dialog = null; CodeMirror.signal(cm, "dialog"); } } function openDialog(me, template, callback, options) { if (!options) options = {}; closeNotification(me, void 0); var dialog = dialogDiv(me, template, options.bottom); var closed = false; showDialog(me, dialog); function close(newVal) { if (typeof newVal == "string") { inp.value = newVal; } else { if (closed) return; closed = true; hideDialog(me, dialog); if (!me.state.dialog) me.focus(); if (options.onClose) options.onClose(dialog); } } var inp = dialog.getElementsByTagName("input")[0]; if (inp) { if (options.value) { inp.value = options.value; if (options.selectValueOnOpen !== false) inp.select(); } if (options.onInput) CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close); }); if (options.onKeyUp) CodeMirror.on(inp, "keyup", function(e) { options.onKeyUp(e, inp.value, close); }); CodeMirror.on(inp, "keydown", function(e) { if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; } if (e.keyCode == 13) callback(inp.value); if (e.keyCode == 27 || options.closeOnEnter !== false && e.keyCode == 13) { inp.blur(); CodeMirror.e_stop(e); close(); } }); if (options.closeOnBlur !== false) CodeMirror.on(inp, "blur", function() { setTimeout(function() { if (document.activeElement === inp) return; close(); }); }); inp.focus(); } return close; } var matching = { "(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<" }; function bracketRegex(config) { return config && config.bracketRegex || /[(){}[\]]/; } function scanForBracket(cm, where, dir, style, config) { var maxScanLen = config && config.maxScanLineLength || 1e4; var maxScanLines = config && config.maxScanLines || 1e3; var stack = []; var re = bracketRegex(config); var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) : Math.max(cm.firstLine() - 1, where.line - maxScanLines); for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) { var line = cm.getLine(lineNo); if (!line) continue; var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1; if (line.length > maxScanLen) continue; if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0); for (; pos != end; pos += dir) { var ch = line.charAt(pos); if (re.test(ch)) { var match = matching[ch]; if (match && match.charAt(1) == ">" == dir > 0) stack.push(ch); else if (!stack.length) return { pos: new Pos(lineNo, pos), ch }; else stack.pop(); } } } return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null; } function findMatchingTag(cm, pos) { } function findEnclosingTag(cm, pos) { var _a, _b; var state = cm.cm6.state; var offset = cm.indexFromPos(pos); if (offset < state.doc.length) { var text = state.sliceDoc(offset, offset + 1); if (text == "<") offset++; } var tree = ensureSyntaxTree(state, offset); var node = (tree === null || tree === void 0 ? void 0 : tree.resolve(offset)) || null; while (node) { if (((_a = node.firstChild) === null || _a === void 0 ? void 0 : _a.type.name) == "OpenTag" && ((_b = node.lastChild) === null || _b === void 0 ? void 0 : _b.type.name) == "CloseTag") { return { open: convertRange(state.doc, node.firstChild), close: convertRange(state.doc, node.lastChild) }; } node = node.parent; } } function convertRange(doc, cm6Range) { return { from: posFromIndex(doc, cm6Range.from), to: posFromIndex(doc, cm6Range.to) }; } var Marker = class { constructor(cm, offset, assoc) { this.cm = cm; this.id = cm.$mid++; this.offset = offset; this.assoc = assoc; cm.marks[this.id] = this; } clear() { delete this.cm.marks[this.id]; } find() { if (this.offset == null) return null; return this.cm.posFromIndex(this.offset); } update(change) { if (this.offset != null) this.offset = change.mapPos(this.offset, this.assoc, MapMode.TrackDel); } }; var Piece = class { constructor(left, top, height, fontFamily, fontSize, fontWeight, color, className, letter, partial) { this.left = left; this.top = top; this.height = height; this.fontFamily = fontFamily; this.fontSize = fontSize; this.fontWeight = fontWeight; this.color = color; this.className = className; this.letter = letter; this.partial = partial; } draw() { let elt = document.createElement("div"); elt.className = this.className; this.adjust(elt); return elt; } adjust(elt) { elt.style.left = this.left + "px"; elt.style.top = this.top + "px"; elt.style.height = this.height + "px"; elt.style.lineHeight = this.height + "px"; elt.style.fontFamily = this.fontFamily; elt.style.fontSize = this.fontSize; elt.style.fontWeight = this.fontWeight; elt.style.color = this.partial ? "transparent" : this.color; elt.className = this.className; elt.textContent = this.letter; } eq(p) { return this.left == p.left && this.top == p.top && this.height == p.height && this.fontFamily == p.fontFamily && this.fontSize == p.fontSize && this.fontWeight == p.fontWeight && this.color == p.color && this.className == p.className && this.letter == p.letter; } }; var BlockCursorPlugin = class { constructor(view, cm) { this.view = view; this.rangePieces = []; this.cursors = []; this.cm = cm; this.measureReq = { read: this.readPos.bind(this), write: this.drawSel.bind(this) }; this.cursorLayer = view.scrollDOM.appendChild(document.createElement("div")); this.cursorLayer.className = "cm-cursorLayer cm-vimCursorLayer"; this.cursorLayer.setAttribute("aria-hidden", "true"); view.requestMeasure(this.measureReq); this.setBlinkRate(); } setBlinkRate() { this.cursorLayer.style.animationDuration = "1200ms"; } update(update) { if (update.selectionSet || update.geometryChanged || update.viewportChanged) { this.view.requestMeasure(this.measureReq); this.cursorLayer.style.animationName = this.cursorLayer.style.animationName == "cm-blink" ? "cm-blink2" : "cm-blink"; } } scheduleRedraw() { this.view.requestMeasure(this.measureReq); } readPos() { let { state } = this.view; let cursors = []; for (let r of state.selection.ranges) { let prim = r == state.selection.main; let piece = measureCursor(this.cm, this.view, r, prim); if (piece) cursors.push(piece); } return { cursors }; } drawSel({ cursors }) { if (cursors.length != this.cursors.length || cursors.some((c, i) => !c.eq(this.cursors[i]))) { let oldCursors = this.cursorLayer.children; if (oldCursors.length !== cursors.length) { this.cursorLayer.textContent = ""; for (const c of cursors) this.cursorLayer.appendChild(c.draw()); } else { cursors.forEach((c, idx) => c.adjust(oldCursors[idx])); } this.cursors = cursors; } } destroy() { this.cursorLayer.remove(); } }; var themeSpec = { ".cm-vimMode .cm-line": { "& ::selection": { backgroundColor: "transparent !important" }, "&::selection": { backgroundColor: "transparent !important" }, caretColor: "transparent !important" }, ".cm-fat-cursor": { position: "absolute", background: "#ff9696", border: "none", whiteSpace: "pre" }, "&:not(.cm-focused) .cm-fat-cursor": { background: "none", outline: "solid 1px #ff9696", color: "transparent !important" } }; var hideNativeSelection = Prec.highest(EditorView.theme(themeSpec)); function getBase(view) { let rect = view.scrollDOM.getBoundingClientRect(); let left = view.textDirection == Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth; return { left: left - view.scrollDOM.scrollLeft, top: rect.top - view.scrollDOM.scrollTop }; } function measureCursor(cm, view, cursor, primary) { let head = cursor.head; let fatCursor = false; let hCoeff = 1; let vim2 = cm.state.vim; if (vim2 && (!vim2.insertMode || cm.state.overwrite)) { fatCursor = true; if (vim2.visualBlock && !primary) return null; if (cursor.anchor < cursor.head) head--; if (cm.state.overwrite) hCoeff = 0.2; else if (vim2.status) hCoeff = 0.5; } if (fatCursor) { let letter = head < view.state.doc.length && view.state.sliceDoc(head, head + 1); if (letter && (/[\uDC00-\uDFFF]/.test(letter) && head > 1)) { head--; letter = view.state.sliceDoc(head, head + 1); } let pos = view.coordsAtPos(head, 1); if (!pos) return null; let base = getBase(view); let domAtPos = view.domAtPos(head); let node = domAtPos ? domAtPos.node : view.contentDOM; while (domAtPos && domAtPos.node instanceof HTMLElement) { node = domAtPos.node; domAtPos = { node: domAtPos.node.childNodes[domAtPos.offset], offset: 0 }; } if (!(node instanceof HTMLElement)) { if (!node.parentNode) return null; node = node.parentNode; } let style = getComputedStyle(node); let left = pos.left; if (!letter || letter == "\n" || letter == "\r") { letter = " "; } else if (letter == " ") { letter = " "; var nextPos = view.coordsAtPos(head + 1, -1); if (nextPos) { left = nextPos.left - (nextPos.left - pos.left) / parseInt(style.tabSize); } } else if (/[\uD800-\uDBFF]/.test(letter) && head < view.state.doc.length - 1) { letter += view.state.sliceDoc(head + 1, head + 2); } let h = pos.bottom - pos.top; return new Piece(left - base.left, pos.top - base.top + h * (1 - hCoeff), h * hCoeff, style.fontFamily, style.fontSize, style.fontWeight, style.color, primary ? "cm-fat-cursor cm-cursor-primary" : "cm-fat-cursor cm-cursor-secondary", letter, hCoeff != 1); } else { return null; } } var FIREFOX_LINUX = typeof navigator != "undefined" && /linux/i.test(navigator.platform) && / Gecko\/\d+/.exec(navigator.userAgent); var Vim = initVim(CodeMirror); var HighlightMargin = 250; var vimStyle = EditorView.baseTheme({ ".cm-vimMode .cm-cursorLayer:not(.cm-vimCursorLayer)": { display: "none" }, ".cm-vim-panel": { padding: "0px 10px", fontFamily: "monospace", minHeight: "1.3em" }, ".cm-vim-panel input": { border: "none", outline: "none", backgroundColor: "inherit" }, "&light .cm-searchMatch": { backgroundColor: "#ffff0054" }, "&dark .cm-searchMatch": { backgroundColor: "#00ffff8a" } }); var vimPlugin = ViewPlugin.fromClass(class { constructor(view) { this.status = ""; this.query = null; this.decorations = Decoration.none; this.waitForCopy = false; this.lastKeydown = ""; this.useNextTextInput = false; this.view = view; const cm = this.cm = new CodeMirror(view); Vim.enterVimMode(this.cm); this.view.cm = this.cm; this.cm.state.vimPlugin = this; this.blockCursor = new BlockCursorPlugin(view, cm); this.updateClass(); this.cm.on("vim-command-done", () => { if (cm.state.vim) cm.state.vim.status = ""; this.blockCursor.scheduleRedraw(); this.updateStatus(); }); this.cm.on("vim-mode-change", (e) => { cm.state.vim.mode = e.mode; if (e.subMode) { cm.state.vim.mode += " block"; } cm.state.vim.status = ""; this.blockCursor.scheduleRedraw(); this.updateClass(); this.updateStatus(); }); this.cm.on("dialog", () => { if (this.cm.state.statusbar) { this.updateStatus(); } else { view.dispatch({ effects: showVimPanel.of(!!this.cm.state.dialog) }); } }); this.dom = document.createElement("span"); this.dom.style.cssText = "position: absolute; right: 10px; top: 1px"; this.statusButton = document.createElement("span"); this.statusButton.onclick = (e) => { Vim.handleKey(this.cm, "", "user"); this.cm.focus(); }; this.statusButton.style.cssText = "cursor: pointer"; } update(update) { var _a; if ((update.viewportChanged || update.docChanged) && this.query) { this.highlight(this.query); } if (update.docChanged) { this.cm.onChange(update); } if (update.selectionSet) { this.cm.onSelectionChange(); } if (update.viewportChanged) ; if (this.cm.curOp && !this.cm.curOp.isVimOp) { this.cm.onBeforeEndOperation(); } if (update.transactions) { for (let tr of update.transactions) for (let effect of tr.effects) { if (effect.is(setSearchQuery)) { let forVim = (_a = effect.value) === null || _a === void 0 ? void 0 : _a.forVim; if (!forVim) { this.highlight(null); } else { let query = effect.value.create(); this.highlight(query); } } } } this.blockCursor.update(update); } updateClass() { const state = this.cm.state; if (!state.vim || state.vim.insertMode && !state.overwrite) this.view.scrollDOM.classList.remove("cm-vimMode"); else this.view.scrollDOM.classList.add("cm-vimMode"); } updateStatus() { let dom = this.cm.state.statusbar; let vim2 = this.cm.state.vim; if (!dom || !vim2) return; let dialog = this.cm.state.dialog; if (dialog) { if (dialog.parentElement != dom) { dom.textContent = ""; dom.appendChild(dialog); } } else { dom.textContent = ""; var status = (vim2.mode || "normal").toUpperCase(); if (vim2.insertModeReturn) status += "(C-O)"; this.statusButton.textContent = `--${status}--`; dom.appendChild(this.statusButton); } this.dom.textContent = vim2.status; dom.appendChild(this.dom); } destroy() { Vim.leaveVimMode(this.cm); this.updateClass(); this.blockCursor.destroy(); delete this.view.cm; } highlight(query) { this.query = query; if (!query) return this.decorations = Decoration.none; let { view } = this; let builder = new RangeSetBuilder(); for (let i = 0, ranges = view.visibleRanges, l = ranges.length; i < l; i++) { let { from, to } = ranges[i]; while (i < l - 1 && to > ranges[i + 1].from - 2 * HighlightMargin) to = ranges[++i].to; query.highlight(view.state, from, to, (from2, to2) => { builder.add(from2, to2, matchMark); }); } return this.decorations = builder.finish(); } handleKey(e, view) { const key = CodeMirror.vimKey(e); const cm = this.cm; if (!key) return; let vim2 = cm.state.vim; if (!vim2) return; if (key == "" && !vim2.insertMode && !vim2.visualMode && this.query) { const searchState = vim2.searchState_; if (searchState) { cm.removeOverlay(searchState.getOverlay()); searchState.setOverlay(null); } } let isCopy = key === "" && !CodeMirror.isMac; if (isCopy && cm.somethingSelected()) { this.waitForCopy = true; return true; } vim2.status = (vim2.status || "") + key; let result = Vim.multiSelectHandleKey(cm, key, "user"); vim2 = cm.state.vim; if (!result && vim2.insertMode && cm.state.overwrite) { if (e.key && e.key.length == 1 && !/\n/.test(e.key)) { result = true; cm.overWriteSelection(e.key); } else if (e.key == "Backspace") { result = true; CodeMirror.commands.cursorCharLeft(cm); } } if (result) { CodeMirror.signal(this.cm, "vim-keypress", key); e.preventDefault(); e.stopPropagation(); this.blockCursor.scheduleRedraw(); } this.updateStatus(); return !!result; } }, { eventHandlers: { copy: function(e, view) { if (!this.waitForCopy) return; this.waitForCopy = false; Promise.resolve().then(() => { var cm = this.cm; var vim2 = cm.state.vim; if (!vim2) return; if (vim2.insertMode) { cm.setSelection(cm.getCursor(), cm.getCursor()); } else { cm.operation(() => { if (cm.curOp) cm.curOp.isVimOp = true; Vim.handleKey(cm, "", "user"); }); } }); }, compositionstart: function(e, view) { this.useNextTextInput = true; }, keypress: function(e, view) { if (this.lastKeydown == "Dead") this.handleKey(e, view); }, keydown: function(e, view) { this.lastKeydown = e.key; if (this.lastKeydown == "Unidentified" || this.lastKeydown == "Process" || this.lastKeydown == "Dead") { this.useNextTextInput = true; } else { this.useNextTextInput = false; this.handleKey(e, view); } } }, provide: () => { return [ EditorView.inputHandler.of((view, from, to, text) => { var _a, _b; var cm = getCM(view); if (!cm) return false; var vim2 = (_a = cm.state) === null || _a === void 0 ? void 0 : _a.vim; var vimPlugin2 = cm.state.vimPlugin; if (vim2 && !vim2.insertMode && !((_b = cm.curOp) === null || _b === void 0 ? void 0 : _b.isVimOp)) { if (text === "\0\0") { return true; } if (text.length == 1 && vimPlugin2.useNextTextInput) { vimPlugin2.handleKey({ key: text, preventDefault: () => { }, stopPropagation: () => { } }); } forceEndComposition(view); return true; } return false; }) ]; }, decorations: (v) => v.decorations }); function forceEndComposition(view) { var parent = view.scrollDOM.parentElement; if (!parent) return; if (FIREFOX_LINUX) { view.contentDOM.textContent = "\0\0"; view.contentDOM.dispatchEvent(new CustomEvent("compositionend")); return; } var sibling = view.scrollDOM.nextSibling; var selection = window.getSelection(); var savedSelection = selection && { anchorNode: selection.anchorNode, anchorOffset: selection.anchorOffset, focusNode: selection.focusNode, focusOffset: selection.focusOffset }; view.scrollDOM.remove(); parent.insertBefore(view.scrollDOM, sibling); try { if (savedSelection && selection) { selection.setPosition(savedSelection.anchorNode, savedSelection.anchorOffset); if (savedSelection.focusNode) { selection.extend(savedSelection.focusNode, savedSelection.focusOffset); } } } catch (e) { console.error(e); } view.focus(); view.contentDOM.dispatchEvent(new CustomEvent("compositionend")); } var matchMark = Decoration.mark({ class: "cm-searchMatch" }); var showVimPanel = StateEffect.define(); var vimPanelState = StateField.define({ create: () => false, update(value, tr) { for (let e of tr.effects) if (e.is(showVimPanel)) value = e.value; return value; }, provide: (f) => { return showPanel.from(f, (on2) => on2 ? createVimPanel : null); } }); function createVimPanel(view) { let dom = document.createElement("div"); dom.className = "cm-vim-panel"; let cm = view.cm; if (cm.state.dialog) { dom.appendChild(cm.state.dialog); } return { top: false, dom }; } function statusPanel(view) { let dom = document.createElement("div"); dom.className = "cm-vim-panel"; let cm = view.cm; cm.state.statusbar = dom; cm.state.vimPlugin.updateStatus(); return { dom }; } function vim(options = {}) { return [ vimStyle, vimPlugin, hideNativeSelection, options.status ? showPanel.of(statusPanel) : vimPanelState ]; } function getCM(view) { return view.cm || null; } export { CodeMirror, Vim, getCM, vim }; //# sourceMappingURL=@replit_codemirror-vim.js.map