mirror of
https://github.com/tridactyl/tridactyl.git
synced 2026-09-10 07:16:33 -04:00
Merge pull request #4440 from treapster/gobble-accept-terminator
Use MinimalKey internally, add terminator key to gobble mode
This commit is contained in:
commit
ab67dea1c3
|
|
@ -52,7 +52,7 @@ import * as genericParser from "@src/parsers/genericmode"
|
|||
import * as perf from "@src/perf"
|
||||
import state, * as State from "@src/state"
|
||||
import * as R from "ramda"
|
||||
import { KeyEventLike } from "@src/lib/keyseq"
|
||||
import { MinimalKey, minimalKeyFromKeyboardEvent } from "@src/lib/keyseq"
|
||||
import { TabGroupCompletionSource } from "@src/completions/TabGroup"
|
||||
|
||||
/** @hidden **/
|
||||
|
|
@ -78,7 +78,7 @@ const commandline_state = {
|
|||
* tl;dr TODO: delete this and better resolve race condition
|
||||
*/
|
||||
isVisible: false,
|
||||
keyEvents: new Array<KeyEventLike>(),
|
||||
keyEvents: new Array<MinimalKey>(),
|
||||
refresh_completions,
|
||||
state,
|
||||
}
|
||||
|
|
@ -186,7 +186,7 @@ commandline_state.clInput.addEventListener(
|
|||
"keydown",
|
||||
function (keyevent: KeyboardEvent) {
|
||||
if (!keyevent.isTrusted) return
|
||||
commandline_state.keyEvents.push(keyevent)
|
||||
commandline_state.keyEvents.push(minimalKeyFromKeyboardEvent(keyevent))
|
||||
const response = keyParser(commandline_state.keyEvents)
|
||||
if (response.isMatch) {
|
||||
keyevent.preventDefault()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,12 @@ import { isTextEditable } from "@src/lib/dom"
|
|||
import { contentState, ModeName } from "@src/content/state_content"
|
||||
import Logger from "@src/lib/logging"
|
||||
import * as controller from "@src/lib/controller"
|
||||
import { KeyEventLike, ParserResponse } from "@src/lib/keyseq"
|
||||
import {
|
||||
KeyEventLike,
|
||||
ParserResponse,
|
||||
minimalKeyFromKeyboardEvent,
|
||||
MinimalKey,
|
||||
} from "@src/lib/keyseq"
|
||||
import { deepestShadowRoot } from "@src/lib/dom"
|
||||
|
||||
import * as hinting from "@src/content/hinting"
|
||||
|
|
@ -97,7 +102,7 @@ export const canceller = new KeyCanceller()
|
|||
/** Accepts keyevents, resolves them to maps, maps to exstrs, executes exstrs */
|
||||
function* ParserController() {
|
||||
const parsers: {
|
||||
[mode_name in ModeName]: (keys: KeyEventLike[]) => ParserResponse
|
||||
[mode_name in ModeName]: (keys: MinimalKey[]) => ParserResponse
|
||||
} = {
|
||||
normal: keys => generic.parser("nmaps", keys),
|
||||
insert: keys => generic.parser("imaps", keys),
|
||||
|
|
@ -112,25 +117,35 @@ function* ParserController() {
|
|||
while (true) {
|
||||
let exstr = ""
|
||||
let previousSuffix = null
|
||||
let keyEvents: KeyEventLike[] = []
|
||||
let keyEvents: MinimalKey[] = []
|
||||
try {
|
||||
while (true) {
|
||||
const keyevent: KeyEventLike = yield
|
||||
let shadowRoot = null
|
||||
let textEditable = false
|
||||
|
||||
const shadowRoot =
|
||||
keyevent instanceof KeyboardEvent
|
||||
? deepestShadowRoot((keyevent.target as Element).shadowRoot)
|
||||
: null
|
||||
if (keyevent instanceof KeyboardEvent) {
|
||||
shadowRoot = deepestShadowRoot(
|
||||
(keyevent.target as Element).shadowRoot,
|
||||
)
|
||||
|
||||
textEditable =
|
||||
shadowRoot === null
|
||||
? isTextEditable(keyevent.target as Element)
|
||||
: isTextEditable(shadowRoot.activeElement)
|
||||
// Accumulate key events. The parser will cut this
|
||||
// down whenever it's not a valid prefix of a known
|
||||
// binding, so it can't grow indefinitely unless you
|
||||
// have a combination of maps that permits bindings of
|
||||
// unbounded length.
|
||||
keyEvents.push(minimalKeyFromKeyboardEvent(keyevent))
|
||||
} else {
|
||||
keyEvents.push(keyevent)
|
||||
}
|
||||
|
||||
// _just to be safe_, cache this to make the following
|
||||
// code more thread-safe.
|
||||
const currentMode = contentState.mode
|
||||
const textEditable =
|
||||
keyevent instanceof KeyboardEvent
|
||||
? shadowRoot === null
|
||||
? isTextEditable(keyevent.target as Element)
|
||||
: isTextEditable(shadowRoot.activeElement)
|
||||
: false
|
||||
|
||||
// This code was sort of the cause of the most serious bug in Tridactyl
|
||||
// to date (March 2018).
|
||||
|
|
@ -153,17 +168,10 @@ function* ParserController() {
|
|||
|
||||
const newMode = contentState.mode
|
||||
if (newMode !== currentMode) {
|
||||
keyEvents = []
|
||||
keyEvents = keyEvents.slice(-1)
|
||||
previousSuffix = null
|
||||
}
|
||||
|
||||
// Accumulate key events. The parser will cut this
|
||||
// down whenever it's not a valid prefix of a known
|
||||
// binding, so it can't grow indefinitely unless you
|
||||
// have a combination of maps that permits bindings of
|
||||
// unbounded length.
|
||||
keyEvents.push(keyevent)
|
||||
|
||||
const response = (
|
||||
parsers[contentState.mode] ||
|
||||
(keys => generic.parser(contentState.mode + "maps", keys))
|
||||
|
|
|
|||
|
|
@ -1192,7 +1192,7 @@ function focusRightHint() {
|
|||
}
|
||||
|
||||
/** @hidden */
|
||||
export function parser(keys: keyseq.KeyEventLike[]) {
|
||||
export function parser(keys: keyseq.MinimalKey[]) {
|
||||
const parsed = keyseq.parse(
|
||||
keys,
|
||||
keyseq.mapstrMapToKeyMap(
|
||||
|
|
|
|||
|
|
@ -5334,13 +5334,15 @@ export function run_exstr(...commands: string[]) {
|
|||
|
||||
/** Initialize gobble mode.
|
||||
|
||||
It will read `nChars` input keys, append them to `endCmd` and execute that
|
||||
string.
|
||||
|
||||
If numKeysOrTerminator is a number, it will read the provided amount of keys,
|
||||
append them to `endCmd` and execute that string.
|
||||
If numKeysOrTerminator is a key or key combination like 'k', '<CR>' or '<C-j>',
|
||||
it will read keys until the provided key is pressed, append them to `endCmd` and
|
||||
execute that string.
|
||||
*/
|
||||
//#content
|
||||
export async function gobble(nChars: number, endCmd: string) {
|
||||
return gobbleMode.init(nChars, endCmd)
|
||||
export async function gobble(numKeysOrTerminator: string, endCmd: string) {
|
||||
return gobbleMode.init(numKeysOrTerminator, endCmd)
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
import { mapstrToKeyseq } from "@src/lib/keyseq"
|
||||
import { canonicaliseMapstr } from "@src/lib/keyseq"
|
||||
|
||||
export const mode2maps = new Map([
|
||||
["normal", "nmaps"],
|
||||
|
|
@ -46,8 +46,7 @@ export function parse_bind_args(...args: string[]): bind_args {
|
|||
|
||||
const key = args.shift()
|
||||
// Convert key to internal representation
|
||||
const keyseq = mapstrToKeyseq(key)
|
||||
result.key = keyseq.map(k => k.toMapstr()).join("")
|
||||
result.key = canonicaliseMapstr(key)
|
||||
|
||||
result.excmd = args.join(" ")
|
||||
|
||||
|
|
|
|||
|
|
@ -371,7 +371,7 @@ export class default_config {
|
|||
".": "repeat",
|
||||
"<AS-ArrowUp><AS-ArrowUp><AS-ArrowDown><AS-ArrowDown><AS-ArrowLeft><AS-ArrowRight><AS-ArrowLeft><AS-ArrowRight>ba":
|
||||
"open https://www.youtube.com/watch?v=M3iOROuTuMA",
|
||||
"m": "gobble 1 markadd",
|
||||
m: "gobble 1 markadd",
|
||||
"`": "gobble 1 markjump",
|
||||
}
|
||||
|
||||
|
|
@ -1265,9 +1265,10 @@ const platform_defaults = {
|
|||
& '%TEMP%/tridactyl_installnative.ps1' -Tag %TAG;\
|
||||
Remove-Item '%TEMP%/tridactyl_installnative.ps1'"`,
|
||||
downloadforbiddenchars: "#%&{}\\<>*?/$!'\":@+`|=",
|
||||
downloadforbiddennames: "CON, PRN, AUX, NUL, COM1, COM2,"
|
||||
+ "COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1,"
|
||||
+ "LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9,",
|
||||
downloadforbiddennames:
|
||||
"CON, PRN, AUX, NUL, COM1, COM2," +
|
||||
"COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1," +
|
||||
"LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9,",
|
||||
},
|
||||
linux: {
|
||||
nmaps: {
|
||||
|
|
|
|||
|
|
@ -38,42 +38,57 @@ export interface KeyModifiers {
|
|||
shiftKey?: boolean
|
||||
}
|
||||
|
||||
// Format modifiers
|
||||
const modifiers = new Map([
|
||||
["A", "altKey"],
|
||||
["C", "ctrlKey"],
|
||||
["M", "metaKey"],
|
||||
["S", "shiftKey"],
|
||||
])
|
||||
export class MinimalKey {
|
||||
readonly altKey = false
|
||||
readonly ctrlKey = false
|
||||
readonly metaKey = false
|
||||
readonly shiftKey = false
|
||||
|
||||
translated = false
|
||||
constructor(readonly key: string, modifiers?: KeyModifiers) {
|
||||
if (modifiers !== undefined) {
|
||||
for (const mod of Object.keys(modifiers)) {
|
||||
if (
|
||||
this.key.length === 1 &&
|
||||
this.key !== " " &&
|
||||
mod === "shiftKey"
|
||||
)
|
||||
continue
|
||||
this[mod] = modifiers[mod]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Does this key match a given MinimalKey extending object? */
|
||||
public match(keyevent) {
|
||||
// 'in' doesn't include prototypes, so it's safe for this object.
|
||||
for (const attr in this) {
|
||||
// Don't check shiftKey for normal keys.
|
||||
if (attr === "shiftKey" && this.key.length === 1) continue
|
||||
/** Does this key match another MinimalKey */
|
||||
public match(keyevent: MinimalKey) {
|
||||
if (this.key !== keyevent.key) return false
|
||||
for (const [_, attr] of modifiers.entries()) {
|
||||
if (this[attr] !== keyevent[attr]) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public translate(keytranslatemap: { [inkey: string]: string }): MinimalKey {
|
||||
let newkey = keytranslatemap[this.key]
|
||||
if (newkey === undefined) newkey = this.key
|
||||
return new MinimalKey(newkey, {
|
||||
altKey: this.altKey,
|
||||
ctrlKey: this.ctrlKey,
|
||||
metaKey: this.metaKey,
|
||||
shiftKey: this.shiftKey,
|
||||
})
|
||||
}
|
||||
|
||||
public toMapstr() {
|
||||
let str = ""
|
||||
let needsBrackets = this.key.length > 1
|
||||
|
||||
// Format modifiers
|
||||
const modifiers = new Map([
|
||||
["A", "altKey"],
|
||||
["C", "ctrlKey"],
|
||||
["M", "metaKey"],
|
||||
["S", "shiftKey"],
|
||||
])
|
||||
for (const [letter, attr] of modifiers.entries()) {
|
||||
if (this[attr]) {
|
||||
str += letter
|
||||
|
|
@ -98,6 +113,9 @@ export class MinimalKey {
|
|||
|
||||
return str
|
||||
}
|
||||
public isPrintable() {
|
||||
return this.key.length === 1
|
||||
}
|
||||
}
|
||||
|
||||
export type KeyEventLike = MinimalKey | KeyboardEvent
|
||||
|
|
@ -110,7 +128,7 @@ type MapTarget = string | ((...args: any[]) => any)
|
|||
type KeyMap = Map<MinimalKey[], MapTarget>
|
||||
|
||||
export interface ParserResponse {
|
||||
keys?: KeyEventLike[]
|
||||
keys?: MinimalKey[]
|
||||
value?: string
|
||||
exstr?: string
|
||||
isMatch?: boolean
|
||||
|
|
@ -118,8 +136,8 @@ export interface ParserResponse {
|
|||
}
|
||||
|
||||
function splitNumericPrefix(
|
||||
keyseq: KeyEventLike[],
|
||||
): [KeyEventLike[], KeyEventLike[]] {
|
||||
keyseq: MinimalKey[],
|
||||
): [MinimalKey[], MinimalKey[]] {
|
||||
// If the first key is in 1:9, partition all numbers until you reach a non-number.
|
||||
if (
|
||||
!hasModifiers(keyseq[0]) &&
|
||||
|
|
@ -148,7 +166,7 @@ export function stripOnlyModifiers(keyseq) {
|
|||
)
|
||||
}
|
||||
|
||||
export function parse(keyseq: KeyEventLike[], map: KeyMap): ParserResponse {
|
||||
export function parse(keyseq: MinimalKey[], map: KeyMap): ParserResponse {
|
||||
// Remove bare modifiers
|
||||
keyseq = stripOnlyModifiers(keyseq)
|
||||
|
||||
|
|
@ -156,7 +174,7 @@ export function parse(keyseq: KeyEventLike[], map: KeyMap): ParserResponse {
|
|||
if (keyseq.length === 0) return { keys: [], isMatch: false }
|
||||
|
||||
// Split into numeric prefix and non-numeric suffix
|
||||
let numericPrefix: KeyEventLike[]
|
||||
let numericPrefix: MinimalKey[]
|
||||
;[numericPrefix, keyseq] = splitNumericPrefix(keyseq)
|
||||
|
||||
// If keyseq is a prefix of a key in map, proceed, else try dropping keys
|
||||
|
|
@ -198,7 +216,7 @@ export function parse(keyseq: KeyEventLike[], map: KeyMap): ParserResponse {
|
|||
}
|
||||
|
||||
/** True if seq1 is a prefix or equal to seq2 */
|
||||
function prefixes(seq1: KeyEventLike[], seq2: MinimalKey[]) {
|
||||
function prefixes(seq1: MinimalKey[], seq2: MinimalKey[]) {
|
||||
if (seq1.length > seq2.length) {
|
||||
return false
|
||||
} else {
|
||||
|
|
@ -210,7 +228,7 @@ function prefixes(seq1: KeyEventLike[], seq2: MinimalKey[]) {
|
|||
}
|
||||
|
||||
/** returns the fragment of `map` that keyseq is a valid prefix of. */
|
||||
export function completions(keyseq: KeyEventLike[], map: KeyMap): KeyMap {
|
||||
export function completions(keyseq: MinimalKey[], map: KeyMap): KeyMap {
|
||||
return new Map(
|
||||
filter(map.entries(), ([ks, _maptarget]) => prefixes(keyseq, ks)),
|
||||
)
|
||||
|
|
@ -295,10 +313,8 @@ function expandAliases(key: string) {
|
|||
export function bracketexprToKey(inputStr) {
|
||||
if (inputStr.indexOf(">") > 0) {
|
||||
try {
|
||||
const [
|
||||
[modifiers, key],
|
||||
remainder,
|
||||
] = bracketexpr_parser.feedUntilError(inputStr)
|
||||
const [[modifiers, key], remainder] =
|
||||
bracketexpr_parser.feedUntilError(inputStr)
|
||||
return [new MinimalKey(expandAliases(key), modifiers), remainder]
|
||||
} catch (e) {
|
||||
// No valid bracketExpr
|
||||
|
|
@ -350,6 +366,12 @@ export function mapstrToKeyseq(mapstr: string): MinimalKey[] {
|
|||
return keyseq
|
||||
}
|
||||
|
||||
export function canonicaliseMapstr(mapstr: string): string {
|
||||
return mapstrToKeyseq(mapstr)
|
||||
.map(k => k.toMapstr())
|
||||
.join("")
|
||||
}
|
||||
|
||||
export const commandKey2jsKey = {
|
||||
Comma: ",",
|
||||
Period: ".",
|
||||
|
|
@ -440,7 +462,7 @@ export function keyMap(conf): KeyMap {
|
|||
|
||||
// {{{ Utility functions for dealing with KeyboardEvents
|
||||
|
||||
export function hasModifiers(keyEvent: KeyEventLike) {
|
||||
export function hasModifiers(keyEvent: MinimalKey) {
|
||||
return (
|
||||
keyEvent.ctrlKey ||
|
||||
keyEvent.altKey ||
|
||||
|
|
@ -450,16 +472,11 @@ export function hasModifiers(keyEvent: KeyEventLike) {
|
|||
}
|
||||
|
||||
/** shiftKey is true for any capital letter, most numbers, etc. Generally care about other modifiers. */
|
||||
export function hasNonShiftModifiers(keyEvent: KeyEventLike) {
|
||||
export function hasNonShiftModifiers(keyEvent: MinimalKey) {
|
||||
return keyEvent.ctrlKey || keyEvent.altKey || keyEvent.metaKey
|
||||
}
|
||||
|
||||
/** A simple key event is a non-special key (length 1) that is not modified by ctrl, alt, or shift. */
|
||||
export function isSimpleKey(keyEvent: KeyEventLike) {
|
||||
return !(keyEvent.key.length > 1 || hasNonShiftModifiers(keyEvent))
|
||||
}
|
||||
|
||||
function numericPrefixToExstrSuffix(numericPrefix: KeyEventLike[]) {
|
||||
function numericPrefixToExstrSuffix(numericPrefix: MinimalKey[]) {
|
||||
if (numericPrefix.length > 0) {
|
||||
return " " + numericPrefix.map(k => k.key).join("")
|
||||
} else {
|
||||
|
|
@ -473,37 +490,34 @@ function numericPrefixToExstrSuffix(numericPrefix: KeyEventLike[]) {
|
|||
* translation map must be length-1 strings.
|
||||
*/
|
||||
export function translateKeysUsingKeyTranslateMap(
|
||||
keyEvents: KeyEventLike[],
|
||||
keyEvents: MinimalKey[],
|
||||
keytranslatemap: { [inkey: string]: string },
|
||||
) {
|
||||
for (let index = 0; index < keyEvents.length; index++) {
|
||||
const keyEvent = keyEvents[index]
|
||||
const newkey = keytranslatemap[keyEvent.key]
|
||||
|
||||
// KeyboardEvents can't have been translated, MinimalKeys may
|
||||
// have been. We can't add anything to the MinimalKey without
|
||||
// breaking a ton of other stuff, so instead we'll just assume
|
||||
// that the only way we've gotten a MinimalKey is if the key
|
||||
// has already been translated. We err way on the side of
|
||||
// safety becase translating anything more than once would
|
||||
// Translating anything more than once would
|
||||
// almost certainly mean oscillations and other super-weird
|
||||
// breakage.
|
||||
const neverTranslated = keyEvent instanceof KeyboardEvent
|
||||
if (neverTranslated && newkey !== undefined) {
|
||||
// We can't update the keyEvent in place. However, the
|
||||
// entire pipeline works with MinimalKeys all the way
|
||||
// through, so we just swap the key event out for a new
|
||||
// MinimalKey with the right key and modifiers copied from
|
||||
// the original.
|
||||
keyEvents[index] = new MinimalKey(newkey, {
|
||||
altKey: keyEvent.altKey,
|
||||
ctrlKey: keyEvent.ctrlKey,
|
||||
metaKey: keyEvent.metaKey,
|
||||
shiftKey: keyEvent.shiftKey,
|
||||
})
|
||||
if (!keyEvents[index].translated) {
|
||||
keyEvents[index] = keyEvents[index].translate(keytranslatemap)
|
||||
keyEvents[index].translated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Convert keyboardEvent to internal type MinimalKey
|
||||
* for further use. Key is obtained through layout-independent
|
||||
* code if config says so.
|
||||
*/
|
||||
export function minimalKeyFromKeyboardEvent(
|
||||
keyEvent: KeyboardEvent,
|
||||
): MinimalKey {
|
||||
return new MinimalKey(keyEvent.key, {
|
||||
altKey: keyEvent.altKey,
|
||||
ctrlKey: keyEvent.ctrlKey,
|
||||
metaKey: keyEvent.metaKey,
|
||||
shiftKey: keyEvent.shiftKey,
|
||||
})
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,26 @@
|
|||
import { contentState } from "@src/content/state_content"
|
||||
import { isSimpleKey, KeyEventLike } from "@src/lib/keyseq"
|
||||
import { MinimalKey, canonicaliseMapstr } from "@src/lib/keyseq"
|
||||
|
||||
/** Simple container for the gobble state. */
|
||||
class GobbleState {
|
||||
public numChars = 0
|
||||
public chars = ""
|
||||
public numKeysOrTerminator: number | string = 0
|
||||
public keyCombination = ""
|
||||
public endCommand = ""
|
||||
}
|
||||
|
||||
let modeState: GobbleState
|
||||
|
||||
/** Init gobble mode. After parsing the defined number of input keys, execute
|
||||
`endCmd` with attached parsed input. `Escape` cancels the mode and returns to
|
||||
normal mode. */
|
||||
export function init(numChars: number, endCommand: string) {
|
||||
/** Init gobble mode. After parsing the defined number of input keys,
|
||||
* or until provided terminator key, execute `endCmd` with attached parsed input.
|
||||
* `Escape` cancels the mode and returns to normal mode. */
|
||||
export function init(numKeysOrTerminator: string, endCommand: string) {
|
||||
contentState.mode = "gobble"
|
||||
modeState = new GobbleState()
|
||||
modeState.numChars = numChars
|
||||
const number = Number(numKeysOrTerminator)
|
||||
if (!isNaN(number)) {
|
||||
modeState.numKeysOrTerminator = number
|
||||
} else
|
||||
modeState.numKeysOrTerminator = canonicaliseMapstr(numKeysOrTerminator)
|
||||
modeState.endCommand = endCommand
|
||||
}
|
||||
|
||||
|
|
@ -27,18 +31,29 @@ function reset() {
|
|||
}
|
||||
|
||||
/** Receive keypress. If applicable, execute a command. */
|
||||
export function parser(keys: KeyEventLike[]) {
|
||||
export function parser(keys: MinimalKey[]) {
|
||||
function exec() {
|
||||
const exstr = modeState.endCommand + " " + modeState.keyCombination
|
||||
reset()
|
||||
return { keys: [], exstr }
|
||||
}
|
||||
|
||||
const key = keys[0].key
|
||||
|
||||
if (key === "Escape") {
|
||||
reset()
|
||||
} else if (isSimpleKey(keys[0])) {
|
||||
modeState.chars += key
|
||||
if (modeState.chars.length >= modeState.numChars) {
|
||||
const exstr = modeState.endCommand + " " + modeState.chars
|
||||
reset()
|
||||
return { keys: [], exstr }
|
||||
}
|
||||
} else if (
|
||||
typeof modeState.numKeysOrTerminator === "string" &&
|
||||
modeState.numKeysOrTerminator === keys[0].toMapstr()
|
||||
) {
|
||||
return exec()
|
||||
} else if (keys[0].isPrintable()) {
|
||||
modeState.keyCombination += keys[0].toMapstr()
|
||||
if (
|
||||
typeof modeState.numKeysOrTerminator === "number" &&
|
||||
--modeState.numKeysOrTerminator <= 0
|
||||
)
|
||||
return exec()
|
||||
}
|
||||
return { keys: [], exstr: "", isMatch: true }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export function init(endCommand: string, mode = "normal", numCommands = 1) {
|
|||
}
|
||||
|
||||
/** Receive keypress. If applicable, execute a command. */
|
||||
export function parser(keys: keyseq.KeyEventLike[]) {
|
||||
export function parser(keys: keyseq.MinimalKey[]) {
|
||||
keys = keyseq.stripOnlyModifiers(keys)
|
||||
if (keys.length === 0) return { keys: [], isMatch: false }
|
||||
const conf = mode2maps.get(modeState.mode) || modeState.mode + "maps"
|
||||
|
|
|
|||
Loading…
Reference in a new issue