From f390c93c96e2f566fb7c69e131f3a39ccfae9bab Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 4 Feb 2024 15:20:51 +0400 Subject: [PATCH] add more types --- src/cm_adapter.ts | 30 +++--- src/types.d.ts | 121 +++++++++++++++--------- src/vim.js | 230 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 303 insertions(+), 78 deletions(-) diff --git a/src/cm_adapter.ts b/src/cm_adapter.ts index cb2481e..65ae4a1 100644 --- a/src/cm_adapter.ts +++ b/src/cm_adapter.ts @@ -6,7 +6,7 @@ import { insertNewlineAndIndent, indentMore, indentLess, indentSelection, cursorCharLeft, undo, redo, cursorLineBoundaryBackward, cursorLineBoundaryForward, cursorCharBackward, } from "@codemirror/commands" -import {vimState, CM5Range} from "./types" +import {vimState, CM5RangeInterface} from "./types" function indexFromPos(doc: Text, pos: Pos): number { var ch = pos.ch; @@ -29,6 +29,7 @@ function posFromIndex(doc: Text, offset: number): Pos { class Pos { line: number ch: number + sticky?: string constructor(line: number, ch: number) { this.line = line; this.ch = ch; } @@ -128,7 +129,7 @@ export class CodeMirror { static isMac = typeof navigator != "undefined" && /Mac/.test(navigator.platform); // -------------------------- static Pos = Pos; - static StringStream = StringStream; + static StringStream = StringStream as unknown as StringStream & { new(_: string): StringStream } static commands = { cursorCharLeft: function (cm: CodeMirror) { cursorCharLeft(cm.cm6); }, redo: function (cm: CodeMirror) { runHistoryCommand(cm, false); }, @@ -143,7 +144,8 @@ export class CodeMirror { }, indentAuto: function (cm: CodeMirror) { indentSelection(cm.cm6) - } + }, + newlineAndIndentContinueComment: undefined }; static defineOption = function (name: string, val: any, setter: Function) { }; static isWordChar = function (ch: string) { @@ -230,12 +232,14 @@ export class CodeMirror { firstLine() { return 0; }; lastLine() { return this.cm6.state.doc.lines - 1; }; lineCount() { return this.cm6.state.doc.lines }; - setCursor(line: Pos | number, ch: number) { + setCursor(line: number, ch: number): void; + setCursor(line: Pos): void; + setCursor(line: Pos | number, ch?: number) { if (typeof line === 'object') { ch = line.ch; line = line.line; } - var offset = indexFromPos(this.cm6.state.doc, { line, ch }) + var offset = indexFromPos(this.cm6.state.doc, { line, ch: ch || 0 }) this.cm6.dispatch({ selection: { anchor: offset } }, { scrollIntoView: !this.curOp }) if (this.curOp && !this.curOp.isVimOp) this.onBeforeEndOperation(); @@ -265,7 +269,7 @@ export class CodeMirror { }; }); }; - setSelections(p: CM5Range[], primIndex?: number) { + setSelections(p: CM5RangeInterface[], primIndex?: number) { var doc = this.cm6.state.doc var ranges = p.map(x => { return EditorSelection.range(indexFromPos(doc, x.anchor), indexFromPos(doc, x.head)) @@ -546,7 +550,7 @@ export class CodeMirror { } } - let pos = posFromIndex(doc, range.head) as Pos&{hitSide: boolean}; + let pos = posFromIndex(doc, range.head) as Pos&{hitSide?: boolean}; // set hitside to true if there was no place to move and cursor was clipped to the edge // of document. Needed for gj/gk if ( @@ -587,7 +591,7 @@ export class CodeMirror { clientHeight: scroller.clientHeight, clientWidth: scroller.clientWidth }; }; - scrollTo(x?: number, y?: number) { + scrollTo(x?: number|null, y?: number|null) { if (x != null) this.cm6.scrollDOM.scrollLeft = x if (y != null) @@ -663,7 +667,7 @@ export class CodeMirror { curOp.cursorActivityHandlers = this._handlers["cursorActivity"] && this._handlers["cursorActivity"].slice(); this.curOp.cursorActivity = true; }; - operation(fn: Function) { + operation(fn: Function, force?: boolean) { if (!this.curOp) this.curOp = { $d: 0 }; this.curOp.$d++; @@ -716,6 +720,8 @@ export class CodeMirror { } }; + getOption(name:"firstLineNumber"|"tabSize"): number; + getOption(name:string): number|boolean|string|undefined; getOption(name: string) { switch (name) { case "firstLineNumber": return 1; @@ -785,7 +791,9 @@ export class CodeMirror { return hardWrap(this, options); } - showMatchesOnScrollbar = undefined // not implemented + showMatchesOnScrollbar: undefined // not implemented + save?: Function + static keyName?: Function = undefined }; type Mutable = { @@ -952,7 +960,7 @@ function scanForBracket(cm: CodeMirror, where: Pos, dir: -1 | 1, style: any, con return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null; } -function findMatchingTag(cm: CodeMirror, pos: Pos) { +function findMatchingTag(cm: CodeMirror, pos: Pos): undefined { } function findEnclosingTag(cm: CodeMirror, pos: Pos) { diff --git a/src/types.d.ts b/src/types.d.ts index 26f22fb..83d7d49 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -33,7 +33,7 @@ export type vimState = { } export type Marker = ReturnType export type LineHandle = ReturnType -export type Pos = { line: number, ch: number } +export type Pos = { line: number, ch: number, sticky?: string } export interface CM5Range { anchor: Pos, @@ -72,14 +72,15 @@ export type OperatorArgs = { } // version of CodeMirror with vim state checked export type CodeMirrorV = CodeMirror & {state: {vim: vimState}} -export type OperatorFn = (cm: CodeMirrorV, args: OperatorArgs, ranges: CM5RangeInterface[], oldAnchor?: Pos, newHead?: Pos) => Pos|void +export type OperatorFn = (cm: CodeMirrorV, args: OperatorArgs, ranges: CM5RangeInterface[], oldAnchor: Pos, newHead?: Pos) => Pos|void export type vimOperators = { change(cm: CodeMirrorV, args: OperatorArgs, ranges: CM5RangeInterface[]): void, delete(cm: CodeMirrorV, args: OperatorArgs, ranges: CM5RangeInterface[]): void, indent(cm: CodeMirrorV, args: OperatorArgs, ranges: CM5RangeInterface[]): void, indentAuto(cm: CodeMirrorV, args: OperatorArgs, ranges: CM5RangeInterface[]): void, - changeCase(cm: CodeMirrorV, args: OperatorArgs, ranges: CM5RangeInterface[], oldAnchor: Pos, newHead: Pos): Pos, - yank(cm: CodeMirrorV, args: OperatorArgs, ranges: CM5RangeInterface[], oldAnchor: Pos): Pos, + hardWrap(cm: CodeMirrorV, args: OperatorArgs, ranges: CM5RangeInterface[], oldAnchor: Pos): Pos|void, + changeCase(cm: CodeMirrorV, args: OperatorArgs, ranges: CM5RangeInterface[], oldAnchor: Pos, newHead?: Pos): Pos|void, + yank(cm: CodeMirrorV, args: OperatorArgs, ranges: CM5RangeInterface[], oldAnchor: Pos): Pos|void, } & { [key: string]: OperatorFn } @@ -114,7 +115,7 @@ export type vimActions = { [key: string]: ActionFn } -export type MotionArgs = { +export type MotionArgsPartial = { repeat?: number, forward?: boolean, selectedCharacter?: string, @@ -130,7 +131,9 @@ export type MotionArgs = { bigWord?: boolean, repeatIsExplicit?: boolean, noRepeat?: boolean -} +}; + +export type MotionArgs = MotionArgsPartial & {repeat: number}; export type MotionFn = (cm: CodeMirrorV, head: Pos, motionArgs: MotionArgs, vim: vimState, inputState: InputStateInterface) => Pos|[Pos,Pos]|null|undefined export type vimMotions = { @@ -180,29 +183,49 @@ export type vimOption = { export type ExFn = ()=> void; - -declare class StringStream { - string: string; - pos: number; - start: number; - constructor(string: string); - eol(): boolean; - sol(): boolean; - peek(): string | undefined; - next(): string | void; - eat(match: string | RegExp | ((ch: string) => boolean)): string | void; - eatWhile(match: string | RegExp | ((ch: string) => boolean)): boolean; - eatSpace(): boolean; - skipToEnd(): void; - skipTo(ch: string): boolean | void; - backUp(n: number): void; - column(): number; - indentation(): number; - match(pattern: string | RegExp, consume?: boolean, caseInsensitive?: boolean): boolean | RegExpMatchArray | null; - current(): string; +type allCommands = { + keys: string, + context?: string, + interlaceInsertRepeat?: boolean, + exitVisualBlock?: boolean, + isEdit?: boolean, + repeatOverride?: number } - -export {StringStream} +export type motionCommand = allCommands & { + type: 'motion', + motion: string, + motionArgs?: MotionArgsPartial, + repeatOverride?: number +} +export type operatorCommand = allCommands & { + type: 'operator', + operator: string, + operatorArgs?: OperatorArgs +} +export type actionCommand = allCommands & { + type: 'action', + action: string, + actionArgs?: ActionArgs, + motion?: string, + operator?: string, + interlaceInsertRepeat?: boolean +} +export type searchCommand = allCommands & { + type: 'search', + searchArgs: SearchArgs +} +export type operatorMotionCommand = allCommands & { + type: 'operatorMotion', + motion: string, + operator: string, + motionArgs?: MotionArgsPartial, + operatorArgs?: OperatorArgs, + operatorMotionArgs?: { [arg: string]: boolean | string } +} +export type idleCommand = allCommands & { type: 'idle' } +export type exCommand = allCommands & { type: 'ex' } +export type keyToExCommand = allCommands & { type: 'keyToEx', exArgs: { [arg: string]: any } } +export type keyToKeyCommand = allCommands & { toKeys: string, type: 'keyToKey' } export type vimKey = motionCommand @@ -214,23 +237,6 @@ export type vimKey = | exCommand | keyToExCommand | keyToKeyCommand; -type allCommands = { keys: string, context?: string, interlaceInsertRepeat?: boolean, exitVisualBlock?: boolean, isEdit?: boolean, } -export type motionCommand = allCommands & { type: 'motion', motion: string, motionArgs?: MotionArgs, repeatOverride?: number } -export type operatorCommand = allCommands & { type: 'operator', operator: string, operatorArgs?: OperatorArgs } -export type actionCommand = allCommands & { type: 'action', action: string, actionArgs?: ActionArgs, motion?: string, operator?: string} -export type searchCommand = allCommands & { type: 'search', searchArgs: SearchArgs } -export type operatorMotionCommand = allCommands & { - type: 'operatorMotion', - motion: string, - operator: string, - motionArgs?: MotionArgs, - operatorArgs?: OperatorArgs, - operatorMotionArgs?: { [arg: string]: boolean | string } -} -export type idleCommand = allCommands & { type: 'idle' } -export type exCommand = allCommands & { type: 'ex' } -export type keyToExCommand = allCommands & { type: 'keyToEx', exArgs: {[arg: string]: any} } -export type keyToKeyCommand = allCommands & { toKeys: string, type: 'keyToKey' } export type vimKeyMap = vimKey[]; @@ -298,5 +304,30 @@ export type InsertModeChanges = { expectCursorActivityForChange: any; visualBlock?: number, maybeReset?: boolean, + ignoreCount?: number, + repeatOverride?: number, +} + +export type ExParams = { + commandName: string, + argString: string, + input: string, + args?: string[], + + line: number, + lineEnd?: number, + selectionLine: number, + selectionLineEnd?: number, + + setCfg?: Object, + callback?: any, + +} + +declare global { + function isNaN(v: any): v is Exclude; + interface String { + trimStart(): string + } } \ No newline at end of file diff --git a/src/vim.js b/src/vim.js index 0e85468..e24a440 100644 --- a/src/vim.js +++ b/src/vim.js @@ -45,6 +45,7 @@ * @typedef { import("./types").ExFn } ExFn * @typedef { import("./types").MotionArgs } MotionArgs * @typedef { import("./types").ActionArgs } ActionArgs + * @typedef { import("./types").OperatorArgs } OperatorArgs * @typedef { import("./types").vimKey } vimKey * @typedef { import("./types").InputStateInterface } InputStateInterface */ @@ -401,7 +402,7 @@ export function initVim(CodeMirror) { * @arg {string} name * @arg {any} defaultValue * @arg {string} type - * @arg {string | any[] | undefined} [aliases] + * @arg {string[] } [aliases] * @arg {optionCallback} [callback] * */ function defineOption(name, defaultValue, type, aliases, callback) { @@ -783,7 +784,7 @@ export function initVim(CodeMirror) { exCommandDispatcher.map(lhs, rhs, ctx, true); }, // Remove all user-defined mappings for the provided context. - /**@type {(ctx: string) => void} */ + /**@arg {string} [ctx]} */ mapclear: function(ctx) { // Partition the existing keymap into user-defined and true defaults. var actualLength = defaultKeymap.length, @@ -834,7 +835,7 @@ export function initVim(CodeMirror) { exCommands[name]=func; exCommandDispatcher.commandMap_[prefix]={name:name, shortName:prefix, type:'api'}; }, - /**@type {(cm: CodeMirrorV, key: string, origin: string) => undefined | boolean} */ + /**@type {(cm: CodeMirror, key: string, origin: string) => undefined | boolean} */ handleKey: function (cm, key, origin) { var command = this.findKey(cm, key, origin); if (typeof command === 'function') { @@ -1254,6 +1255,7 @@ export function initVim(CodeMirror) { } } + /** @arg {CodeMirrorV} cm @arg {string} [reason] */ function clearInputState(cm, reason) { cm.state.vim.inputState = new InputState(); cm.state.vim.expectLiteralNext = false; @@ -1318,6 +1320,8 @@ export function initVim(CodeMirror) { * The name should be a single character that will be used to reference the register. * The register should support setText, pushText, clear, and toString(). See Register * for a reference implementation. + * @arg {string} name + * @arg {Register} register */ function defineRegister(name, register) { var registers = vimGlobalState.registerController.registers; @@ -1331,15 +1335,16 @@ export function initVim(CodeMirror) { validRegisters.push(name); } - /* - * vim registers allow you to keep many independent copy and paste buffers. - * See http://usevim.com/2012/04/13/registers/ for an introduction. - * - * RegisterController keeps the state of all the registers. An initial - * state may be passed in. The unnamed register '"' will always be - * overridden. - */ + /** + * vim registers allow you to keep many independent copy and paste buffers. + * See http://usevim.com/2012/04/13/registers/ for an introduction. + * + * RegisterController keeps the state of all the registers. An initial + * state may be passed in. The unnamed register '"' will always be + * overridden. + */ class RegisterController { + /** @arg {Object} registers */ constructor(registers) { this.registers = registers; this.unnamedRegister = registers['"'] = new Register(); @@ -1398,8 +1403,11 @@ export function initVim(CodeMirror) { // register. 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. + /** + * Gets the register named @name. If one of @name doesn't already exist, + * create it. If @name is invalid, return the unnamedRegister. + * @arg {string} [name] + */ getRegister(name) { if (!this.isValidRegister(name)) { return this.unnamedRegister; @@ -1410,6 +1418,7 @@ export function initVim(CodeMirror) { } return this.registers[name]; } + /**@type {{(name: any): name is string}} */ isValidRegister(name) { return name && (inArray(name, validRegisters) || latinCharRegex.test(name)); } @@ -1484,6 +1493,11 @@ export function initVim(CodeMirror) { } return {type: 'full', command: bestMatch}; }, + /** + * @arg {CodeMirrorV} cm + * @arg {vimState} vim + * @arg {vimKey} command + */ processCommand: function(cm, vim, command) { vim.inputState.repeatOverride = command.repeatOverride; switch (command.type) { @@ -1510,11 +1524,21 @@ export function initVim(CodeMirror) { break; } }, + /** + * @arg {CodeMirrorV} cm + * @arg {vimState} vim + * @arg {import("./types").motionCommand|import("./types").operatorMotionCommand} command + */ processMotion: function(cm, vim, command) { vim.inputState.motion = command.motion; - vim.inputState.motionArgs = copyArgs(command.motionArgs); + vim.inputState.motionArgs = /**@type {MotionArgs}*/(copyArgs(command.motionArgs)); this.evalInput(cm, vim); }, + /** + * @arg {CodeMirrorV} cm + * @arg {vimState} vim + * @arg {import("./types").operatorCommand|import("./types").operatorMotionCommand} command + */ processOperator: function(cm, vim, command) { var inputState = vim.inputState; if (inputState.operator) { @@ -1522,7 +1546,7 @@ export function initVim(CodeMirror) { // Typing an operator twice like 'dd' makes the operator operate // linewise inputState.motion = 'expandToLine'; - inputState.motionArgs = { linewise: true }; + inputState.motionArgs = { linewise: true, repeat: 1 }; this.evalInput(cm, vim); return; } else { @@ -1544,6 +1568,11 @@ export function initVim(CodeMirror) { this.evalInput(cm, vim); } }, + /** + * @arg {CodeMirrorV} cm + * @arg {vimState} vim + * @arg {import("./types").operatorMotionCommand} command + */ processOperatorMotion: function(cm, vim, command) { var visualMode = vim.visualMode; var operatorMotionArgs = copyArgs(command.operatorMotionArgs); @@ -1558,6 +1587,11 @@ export function initVim(CodeMirror) { this.processMotion(cm, vim, command); } }, + /** + * @arg {CodeMirrorV} cm + * @arg {vimState} vim + * @arg {import("./types").actionCommand} command + */ processAction: function(cm, vim, command) { var inputState = vim.inputState; var repeat = inputState.getRepeat(); @@ -1586,6 +1620,7 @@ export function initVim(CodeMirror) { } actions[command.action](cm, actionArgs, vim); }, + /** @arg {CodeMirrorV} cm @arg {vimState} vim @arg {import("./types").searchCommand} command*/ processSearch: function(cm, vim, command) { if (!cm.getSearchCursor) { // Search depends on SearchCursor. @@ -1597,6 +1632,7 @@ export function initVim(CodeMirror) { var promptPrefix = (forward) ? '/' : '?'; var originalQuery = getSearchState(cm).getQuery(); var originalScrollPos = cm.getScrollInfo(); + /** @arg {string} query @arg {boolean} ignoreCase @arg {boolean} smartCase */ function handleQuery(query, ignoreCase, smartCase) { vimGlobalState.searchHistoryController.pushInput(query); vimGlobalState.searchHistoryController.reset(); @@ -1613,6 +1649,7 @@ export function initVim(CodeMirror) { motionArgs: { forward: true, toJumplist: command.searchArgs.toJumplist } }); } + /** @arg {string} query */ function onPromptClose(query) { cm.scrollTo(originalScrollPos.left, originalScrollPos.top); handleQuery(query, true /** ignoreCase */, true /** smartCase */); @@ -1621,6 +1658,11 @@ export function initVim(CodeMirror) { logSearchQuery(macroModeState, query); } } + /** + * @arg {KeyboardEvent&{target:HTMLInputElement}} e + * @arg {any} query + * @arg {(arg0: any) => void} close + */ function onPromptKeyUp(e, query, close) { var keyName = vimKeyFromEvent(e), up, offset; if (keyName == '' || keyName == '') { @@ -1646,6 +1688,7 @@ export function initVim(CodeMirror) { cm.scrollTo(originalScrollPos.left, originalScrollPos.top); } } + /** @arg {KeyboardEvent} e @arg {string} query @arg {(arg0?: string) => void} close */ function onPromptKeyDown(e, query, close) { var keyName = vimKeyFromEvent(e); if (keyName == '' || keyName == '' || keyName == '' || @@ -1713,7 +1756,13 @@ export function initVim(CodeMirror) { break; } }, + /** + * @arg {CodeMirrorV} cm + * @arg {vimState} vim + * @arg {import("./types").exCommand | import("./types").keyToExCommand} command + */ processEx: function(cm, vim, command) { + /**@arg {string} input*/ function onPromptClose(input) { // Give the prompt some time to close so that if processCommand shows // an error, the elements don't overlap. @@ -1722,6 +1771,11 @@ export function initVim(CodeMirror) { exCommandDispatcher.processCommand(cm, input); if (cm.state.vim) clearInputState(cm); } + /** + * @arg {KeyboardEvent&{target:HTMLInputElement}} e + * @arg {string} input + * @arg {(arg0?: string) => void} close + */ function onPromptKeyDown(e, input, close) { var keyName = vimKeyFromEvent(e), up, offset; if (keyName == '' || keyName == '' || keyName == '' || @@ -1761,6 +1815,7 @@ export function initVim(CodeMirror) { } } }, + /**@arg {CodeMirrorV} cm @arg {vimState} vim */ evalInput: function(cm, vim) { // If the motion command is set, execute both the operator and motion. // Otherwise return. @@ -1888,7 +1943,8 @@ export function initVim(CodeMirror) { visualLine: vim.visualLine }; } - var curStart, curEnd, linewise, mode; + var curStart, curEnd, linewise; + /** @type {'block'|'line'|'char'}*/ var mode; var cmSel; if (vim.visualMode) { // Init visual op @@ -1955,6 +2011,7 @@ export function initVim(CodeMirror) { } } }, + /**@arg {vimState} vim @arg {InputStateInterface} inputState, @arg {import("./types").actionCommand} [actionCommand] */ recordLastEdit: function(vim, inputState, actionCommand) { var macroModeState = vimGlobalState.macroModeState; if (macroModeState.isPlaying) { return; } @@ -2340,10 +2397,12 @@ export function initVim(CodeMirror) { textObjectManipulation: function(cm, head, motionArgs, vim) { // TODO: lots of possible exceptions that can be thrown here. Try da( // outside of a () block. + /** @type{Object} */ var mirroredPairs = {'(': ')', ')': '(', '{': '}', '}': '{', '[': ']', ']': '[', '<': '>', '>': '<'}; + /** @type{Object} */ var selfPaired = {'\'': true, '"': true, '`': true}; var character = motionArgs.selectedCharacter; @@ -2447,10 +2506,12 @@ export function initVim(CodeMirror) { } }; + /** @arg {string} name @arg {import("./types").MotionFn} fn */ function defineMotion(name, fn) { motions[name] = fn; } + /** @arg {string} val @arg {number} times */ function fillArray(val, times) { var arr = []; for (var i = 0; i < times; i++) { @@ -2461,8 +2522,9 @@ export function initVim(CodeMirror) { /** * An operator acts on a text selection. It receives the list of selections * as input. The corresponding CodeMirror selection is guaranteed to - * match the input selection. - */ + * match the input selection. + */ + /** @type {import("./types").vimOperators} */ var operators = { change: function(cm, args, ranges) { var finalHead, text; @@ -2575,7 +2637,7 @@ export function initVim(CodeMirror) { cm.execCommand("indentAuto"); return motions.moveToFirstNonWhiteSpaceCharacter(cm, ranges[0].anchor); }, - hardWrap: function(cm, operatorArgs, ranges, oldAnchor, newHead) { + hardWrap: function(cm, operatorArgs, ranges, oldAnchor) { if (!cm.hardWrap) return; var from = ranges[0].anchor.line; var to = ranges[0].head.line; @@ -2628,10 +2690,12 @@ export function initVim(CodeMirror) { } }; + /** @arg {string} name @arg {import("./types").OperatorFn} fn */ function defineOperator(name, fn) { operators[name] = fn; } + /** @type {import("./types").vimActions} */ var actions = { jumpListWalk: function(cm, actionArgs, vim) { if (vim.visualMode) { @@ -2951,7 +3015,7 @@ export function initVim(CodeMirror) { if (actionArgs.matchIndent) { var tabSize = cm.getOption("tabSize"); // length that considers tabs and tabSize - var whitespaceLength = function(str) { + var whitespaceLength = function(/** @type {string} */ str) { var tabs = (str.split("\t").length - 1); var spaces = (str.split(" ").length - 1); return tabs * tabSize + spaces * 1; @@ -3225,6 +3289,7 @@ export function initVim(CodeMirror) { exitInsertMode: exitInsertMode }; + /** @arg {string } name @arg {import("./types").ActionFn} fn */ function defineAction(name, fn) { actions[name] = fn; } @@ -3328,6 +3393,7 @@ export function initVim(CodeMirror) { } return selectedCharacter; } + /** @arg {CodeMirror} cm @arg {{ (cm: CodeMirror): void }} fn @arg {number} repeat */ function repeatFn(cm, fn, repeat) { return function() { for (var i = 0; i < repeat; i++) { @@ -3335,12 +3401,15 @@ export function initVim(CodeMirror) { } }; } + /** @arg {Pos} cur @return {Pos}*/ function copyCursor(cur) { return new Pos(cur.line, cur.ch); } + /** @arg {Pos} cur1 @arg {Pos} cur2 @return {boolean} */ function cursorEqual(cur1, cur2) { return cur1.ch == cur2.ch && cur1.line == cur2.line; } + /** @arg {Pos} cur1 @arg {Pos} cur2 @return {boolean}*/ function cursorIsBefore(cur1, cur2) { if (cur1.line < cur2.line) { return true; @@ -3350,36 +3419,45 @@ export function initVim(CodeMirror) { } return false; } + /** @arg {Pos} cur1 @arg {Pos} cur2 @return {Pos}*/ function cursorMin(cur1, cur2) { if (arguments.length > 2) { + // @ts-ignore cur2 = cursorMin.apply(undefined, Array.prototype.slice.call(arguments, 1)); } return cursorIsBefore(cur1, cur2) ? cur1 : cur2; } + /** @arg {Pos} cur1 @arg {Pos} cur2 @return {Pos} */ function cursorMax(cur1, cur2) { if (arguments.length > 2) { + // @ts-ignore cur2 = cursorMax.apply(undefined, Array.prototype.slice.call(arguments, 1)); } return cursorIsBefore(cur1, cur2) ? cur2 : cur1; } + /** @arg {Pos} cur1 @arg {Pos} cur2 @arg {Pos} cur3 @return {boolean}*/ function cursorIsBetween(cur1, cur2, cur3) { // returns true if cur2 is between cur1 and cur3. var cur1before2 = cursorIsBefore(cur1, cur2); var cur2before3 = cursorIsBefore(cur2, cur3); return cur1before2 && cur2before3; } + /** @arg {CodeMirror} cm @arg {number} lineNum */ function lineLength(cm, lineNum) { return cm.getLine(lineNum).length; } + /** @arg {string} s */ function trim(s) { if (s.trim) { return s.trim(); } return s.replace(/^\s+|\s+$/g, ''); } + /** @arg {string} s */ function escapeRegex(s) { return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, '\\$1'); } + /** @arg {CodeMirror} cm @arg {number} lineNum @arg {number} column */ function extendLineToColumn(cm, lineNum, column) { var endCh = lineLength(cm, lineNum); var spaces = new Array(column-endCh+1).join(' '); @@ -3392,6 +3470,7 @@ export function initVim(CodeMirror) { // Difference in selectionEnd.line and first/last selection.line // Width of the block: // Distance between selectionEnd.ch and any(first considered here) selection.ch + /** @arg {CodeMirror} cm @arg {Pos} selectionEnd */ function selectBlock(cm, selectionEnd) { var selections = [], ranges = cm.listSelections(); var head = copyCursor(cm.clipPos(selectionEnd)); @@ -3428,6 +3507,7 @@ export function initVim(CodeMirror) { base.ch = baseCh; return base; } + /** @arg {CodeMirror} cm @arg {any} head @arg {number} height */ function selectForInsert(cm, head, height) { var sel = []; for (var i = 0; i < height; i++) { @@ -3437,6 +3517,7 @@ export function initVim(CodeMirror) { cm.setSelections(sel, 0); } // getIndex returns the index of the cursor in the selections. + /** @arg {string | any[]} ranges @arg {any} cursor @arg {string | undefined} [end] */ function getIndex(ranges, cursor, end) { for (var i = 0; i < ranges.length; i++) { var atAnchor = end != 'head' && cursorEqual(ranges[i].anchor, cursor); @@ -3447,8 +3528,10 @@ export function initVim(CodeMirror) { } return -1; } + /** @arg {CodeMirror} cm @arg {vimState} vim */ function getSelectedAreaRange(cm, vim) { var lastSelection = vim.lastSelection; + /** @return {[Pos,Pos]} */ var getCurrentSelectedAreaRange = function() { var selections = cm.listSelections(); var start = selections[0]; @@ -3498,6 +3581,7 @@ export function initVim(CodeMirror) { } // Updates the previous selection with the current selection's values. This // should only be called in visual mode. + /** @arg {CodeMirror} cm @arg {vimState} vim */ function updateLastSelection(cm, vim) { var anchor = vim.sel.anchor; var head = vim.sel.head; @@ -3514,6 +3598,7 @@ export function initVim(CodeMirror) { 'visualLine': vim.visualLine, 'visualBlock': vim.visualBlock}; } + /** @arg {CodeMirrorV} cm @arg {Pos} start @arg {Pos} end */ function expandSelection(cm, start, end, move) { var sel = cm.state.vim.sel; var head = move ? start: sel.head; @@ -3540,6 +3625,9 @@ export function initVim(CodeMirror) { /** * Updates the CodeMirror selection to match the provided vim selection. * If no arguments are given, it uses the current vim selection state. + * @arg {CodeMirrorV} cm + * @arg {vimState["sel"]} [sel] + * @arg {"char"|"line"|"block" | undefined} [mode] */ function updateCmSelection(cm, sel, mode) { var vim = cm.state.vim; @@ -3549,6 +3637,13 @@ export function initVim(CodeMirror) { var cmSel = makeCmSelection(cm, sel, mode); cm.setSelections(cmSel.ranges, cmSel.primary); } + /** + * @arg {CodeMirror} cm + * @arg {import("./types").CM5RangeInterface} sel + * @arg {"char"|"line"|"block"} mode + * @arg {boolean|undefined} [exclusive] + * @return {{ranges: any, primary: number}} + */ function makeCmSelection(cm, sel, mode, exclusive) { var head = copyCursor(sel.head); var anchor = copyCursor(sel.anchor); @@ -3778,6 +3873,9 @@ export function initVim(CodeMirror) { * ``` *
* ``` + * @arg {CodeMirror} cm + * @arg {Pos} head + * @arg {boolean} [inclusive] */ function expandTagUnderCursor(cm, head, inclusive) { var cur = head; @@ -3796,12 +3894,14 @@ export function initVim(CodeMirror) { return { start: tags.open.to, end: tags.close.from }; } + /** @arg {CodeMirror} cm @arg {Pos} oldCur @arg {Pos} newCur */ function recordJumpPosition(cm, oldCur, newCur) { if (!cursorEqual(oldCur, newCur)) { vimGlobalState.jumpList.add(cm, oldCur, newCur); } } + /** @arg {number} increment @arg {{ forward?: any; selectedCharacter?: any; }} args */ function recordLastCharacterSearch(increment, args) { vimGlobalState.lastCharacterSearch.increment = increment; vimGlobalState.lastCharacterSearch.forward = args.forward; @@ -3880,6 +3980,7 @@ export function initVim(CodeMirror) { } } }; + /** @arg {CodeMirrorV} cm @arg {number} repeat @arg {boolean|undefined} forward @arg {string} symb */ function findSymbol(cm, repeat, forward, symb) { var cur = copyCursor(cm.getCursor()); var increment = forward ? 1 : -1; @@ -4063,6 +4164,12 @@ export function initVim(CodeMirror) { } } + /** + * @arg {CodeMirror} cm + * @arg {Pos} head + * @arg {MotionArgs} motionArgs + * @arg {vimState} vim + * @arg {boolean} keepHPos */ function moveToEol(cm, head, motionArgs, vim, keepHPos) { var cur = head; var retval= new Pos(cur.line + motionArgs.repeat - 1, Infinity); @@ -4075,6 +4182,13 @@ export function initVim(CodeMirror) { return retval; } + /** + * @arg {CodeMirror} cm + * @arg {number} repeat + * @arg {boolean} [forward] + * @arg {string} [character] + * @arg {Pos} [head] + */ function moveToCharacter(cm, repeat, forward, character, head) { var cur = head || cm.getCursor(); var start = cur.ch; @@ -4090,6 +4204,7 @@ export function initVim(CodeMirror) { return new Pos(cm.getCursor().line, idx); } + /** @arg {CodeMirrorV} cm @arg {number} repeat */ function moveToColumn(cm, repeat) { // repeat is always >= 1, so repeat - 1 always corresponds // to the column we want to go to. @@ -4097,6 +4212,11 @@ export function initVim(CodeMirror) { return clipCursorToContent(cm, new Pos(line, repeat - 1)); } + /** + * @arg {CodeMirror} cm + * @arg {vimState} vim + * @arg {string} markName + * @arg {Pos} pos */ function updateMark(cm, vim, markName, pos) { if (!inArray(markName, validMarks) && !latinCharRegex.test(markName)) { return; @@ -4107,6 +4227,12 @@ export function initVim(CodeMirror) { vim.marks[markName] = cm.setBookmark(pos); } + /** + * @arg {number} start + * @arg {string | any[]} line + * @arg {any} character + * @arg {boolean} [forward] + * @arg {boolean} [includeChar] */ function charIdxInLine(start, line, character, forward, includeChar) { // Search for char in line. // motion_options: {forward, includeChar} @@ -4128,12 +4254,19 @@ export function initVim(CodeMirror) { return idx; } + /** @arg {CodeMirrorV} cm + * @arg {Pos} head + * @arg {number} repeat + * @arg {number} dir + * @arg {boolean} [inclusive] */ function findParagraph(cm, head, repeat, dir, inclusive) { var line = head.line; var min = cm.firstLine(); var max = cm.lastLine(); var start, end, i = line; + /** @arg {number} i */ function isEmpty(i) { return !cm.getLine(i); } + /** @arg {number} i @arg {number} dir @arg {boolean} [any] */ function isBoundary(i, dir, any) { if (any) { return isEmpty(i) != isEmpty(i + dir); } return !isEmpty(i) && isEmpty(i + dir); @@ -4182,6 +4315,11 @@ export function initVim(CodeMirror) { * is used for jumping to sentence beginnings before or after the current cursor position, * whereas getSentence() is for getting the beginning or end of the sentence * at the current cursor position, either including (a) or excluding (i) whitespace. + * @arg {CodeMirror} cm + * @arg {Pos} cur + * @arg {number} repeat + * @arg {number} dir + * @arg {boolean} inclusive */ function getSentence(cm, cur, repeat, dir, inclusive /*includes whitespace*/) { @@ -4874,7 +5012,7 @@ export function initVim(CodeMirror) { return n; } - /** @arg {CodeMirror} cm @arg {string} template */ + /** @arg {CodeMirror} cm @arg {any} template */ function showConfirm(cm, template) { var pre = dom('div', {$color: 'red', $whiteSpace: 'pre', class: 'cm-vim-message'}, template); if (cm.openNotification) { @@ -4989,6 +5127,7 @@ export function initVim(CodeMirror) { }; } var highlightTimeout = 0; + /** @arg {CodeMirrorV} cm @arg {RegExp} query */ function highlightSearchMatches(cm, query) { clearTimeout(highlightTimeout); var searchState = getSearchState(cm); @@ -5084,6 +5223,7 @@ export function initVim(CodeMirror) { return [cursor.from(), cursor.to()]; }); } + /** @arg {CodeMirrorV} cm */ function clearSearchHighlight(cm) { var state = getSearchState(cm); if (state.highlightTimeout) { @@ -5126,6 +5266,7 @@ export function initVim(CodeMirror) { } } } + /** @arg {CodeMirror} cm */ function getUserVisibleLines(cm) { var scrollInfo = cm.getScrollInfo(); var occludeToleranceTop = 6; @@ -5136,6 +5277,7 @@ export function initVim(CodeMirror) { return {top: from.line, bottom: to.line}; } + /** @arg {CodeMirror} cm @arg {vimState} vim @arg {string} [markName] */ function getMarkPos(cm, vim, markName) { if (markName == '\'' || markName == '`') { return vimGlobalState.jumpList.find(cm, -1) || new Pos(0, 0); @@ -5147,6 +5289,7 @@ export function initVim(CodeMirror) { return mark && mark.find(); } + /** @arg {CodeMirror} cm */ function getLastEditPos(cm) { if (cm.getLastEditEnd) { return cm.getLastEditEnd(); @@ -5402,7 +5545,9 @@ export function initVim(CodeMirror) { } } + /** @typedef { import("./types").ExParams} ExParams */ var exCommands = { + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ colorscheme: function(cm, params) { if (!params.args || params.args.length < 1) { showConfirm(cm, cm.getOption('theme')); @@ -5420,15 +5565,25 @@ export function initVim(CodeMirror) { } exCommandDispatcher.map(mapArgs[0], mapArgs[1], ctx, defaultOnly); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ imap: function(cm, params) { this.map(cm, params, 'insert'); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ nmap: function(cm, params) { this.map(cm, params, 'normal'); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ vmap: function(cm, params) { this.map(cm, params, 'visual'); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ omap: function(cm, params) { this.map(cm, params, 'operatorPending'); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ noremap: function(cm, params) { this.map(cm, params, undefined, true); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ inoremap: function(cm, params) { this.map(cm, params, 'insert', true); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ nnoremap: function(cm, params) { this.map(cm, params, 'normal', true); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ vnoremap: function(cm, params) { this.map(cm, params, 'visual', true); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ onoremap: function(cm, params) { this.map(cm, params, 'operatorPending', true); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params @arg {string} ctx*/ unmap: function(cm, params, ctx) { var mapArgs = params.args; if (!mapArgs || mapArgs.length < 1 || !exCommandDispatcher.unmap(mapArgs[0], ctx)) { @@ -5437,11 +5592,17 @@ export function initVim(CodeMirror) { } } }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ mapclear: function(cm, params) { vimApi.mapclear(); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ imapclear: function(cm, params) { vimApi.mapclear('insert'); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ nmapclear: function(cm, params) { vimApi.mapclear('normal'); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ vmapclear: function(cm, params) { vimApi.mapclear('visual'); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ omapclear: function(cm, params) { vimApi.mapclear('operatorPending'); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ move: function(cm, params) { commandDispatcher.processCommand(cm, cm.state.vim, { type: 'motion', @@ -5509,16 +5670,19 @@ export function initVim(CodeMirror) { } } }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ setlocal: function (cm, params) { // setCfg is passed through to setOption params.setCfg = {scope: 'local'}; this.set(cm, params); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ setglobal: function (cm, params) { // setCfg is passed through to setOption params.setCfg = {scope: 'global'}; this.set(cm, params); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ registers: function(cm, params) { var regArgs = params.args; var registers = vimGlobalState.registerController.registers; @@ -5544,6 +5708,7 @@ export function initVim(CodeMirror) { } showConfirm(cm, regInfo); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ sort: function(cm, params) { var reverse, ignoreCase, unique, number, pattern; function parseArgs() { @@ -5636,10 +5801,12 @@ export function initVim(CodeMirror) { } cm.replaceRange(text.join('\n'), curStart, curEnd); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ vglobal: function(cm, params) { // global inspects params.commandName this.global(cm, params); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ normal: function(cm, params) { var argString = params.argString; if (argString && argString[0] == '!') { @@ -5668,6 +5835,7 @@ export function initVim(CodeMirror) { } } }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ global: function(cm, params) { // a global command is of the form // :[range]g/pattern/[cmd] @@ -5739,6 +5907,7 @@ export function initVim(CodeMirror) { }; nextCommand(); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ substitute: function(cm, params) { if (!cm.getSearchCursor) { throw new Error('Search feature not available. Requires searchcursor.js or ' + @@ -5824,11 +5993,13 @@ export function initVim(CodeMirror) { var cursor = cm.getSearchCursor(query, startPos); doReplace(cm, confirm, global, lineStart, lineEnd, cursor, query, replacePart, params.callback); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ startinsert: function(cm, params) { doKeyToKey(cm, params.argString == '!' ? 'A' : 'i', {}); }, redo: CodeMirror.commands.redo, undo: CodeMirror.commands.undo, + /** @arg {CodeMirrorV} cm */ write: function(cm) { if (CodeMirror.commands.save) { // If a save command is defined, call it. @@ -5838,9 +6009,11 @@ export function initVim(CodeMirror) { cm.save(); } }, + /** @arg {CodeMirrorV} cm */ nohlsearch: function(cm) { clearSearchHighlight(cm); }, + /** @arg {CodeMirrorV} cm */ yank: function (cm) { var cur = copyCursor(cm.getCursor()); var line = cur.line; @@ -5848,6 +6021,7 @@ export function initVim(CodeMirror) { vimGlobalState.registerController.pushText( '0', 'yank', lineText, true, true); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ delete: function(cm, params) { var line = params.selectionLine; var lineEnd = isNaN(params.selectionLineEnd) ? line : params.selectionLineEnd; @@ -5856,12 +6030,14 @@ export function initVim(CodeMirror) { head: new Pos(lineEnd + 1, 0) } ]); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ join: function(cm, params) { var line = params.selectionLine; var lineEnd = isNaN(params.selectionLineEnd) ? line : params.selectionLineEnd; cm.setCursor(new Pos(line, 0)); actions.joinLines(cm, {repeat: lineEnd - line}, cm.state.vim); }, + /** @arg {CodeMirrorV} cm @arg {ExParams} params*/ delmarks: function(cm, params) { if (!params.argString || !trim(params.argString)) { showConfirm(cm, 'Argument required'); @@ -5989,6 +6165,7 @@ export function initVim(CodeMirror) { } done = true; } + /** @arg {(() => void) | undefined} [close] */ function stop(close) { if (close) { close(); } cm.focus(); @@ -6000,6 +6177,7 @@ export function initVim(CodeMirror) { } if (callback) { callback(); } } + /** @arg {KeyboardEvent} e @arg {any} _value @arg {any} close */ function onPromptKeyDown(e, _value, close) { // Swallow all keys. CodeMirror.e_stop(e); @@ -6048,6 +6226,7 @@ export function initVim(CodeMirror) { }); } + /** @arg {CodeMirrorV} cm @arg {boolean} [keepCursor] */ function exitInsertMode(cm, keepCursor) { var vim = cm.state.vim; var macroModeState = vimGlobalState.macroModeState; @@ -6087,7 +6266,13 @@ export function initVim(CodeMirror) { defaultKeymap.unshift(command); } - /** @arg {string} keys @arg {string} type @arg {string} name @arg {any} args @arg {{ [x: string]: any; }} extra */ + /** + * @arg {string} keys + * @arg {string} type + * @arg {string} name + * @arg {any} args + * @arg {{ [x: string]: any; }} extra + **/ function mapCommand(keys, type, name, args, extra) { /**@type{any} */ var command = {keys: keys, type: type}; @@ -6372,6 +6557,7 @@ export function initVim(CodeMirror) { } macroModeState.isPlaying = false; } + /**@arg {CodeMirrorV} cm, @arg {string} key */ function sendCmKey(cm, key) { CodeMirror.lookupKey(key, 'vim-insert', function keyHandler(binding) {