Skip to main content

Matcher

Match keyboard events against parsed key bindings and create standalone handlers.

import {
  matchesKeyboardEvent,
  isInputElement,
  createKeyBindHandler,
  createMultiKeyBindHandler,
} from '@gentleduck/vim/matcher'
import {
  matchesKeyboardEvent,
  isInputElement,
  createKeyBindHandler,
  createMultiKeyBindHandler,
} from '@gentleduck/vim/matcher'

Types

MatchOptions

interface MatchOptions {
  /** Ignore case when comparing the non-modifier key. Default: true */
  ignoreCase?: boolean
}
interface MatchOptions {
  /** Ignore case when comparing the non-modifier key. Default: true */
  ignoreCase?: boolean
}

SingleKeyBindOptions

interface SingleKeyBindOptions {
  enabled?: boolean        // Default: true
  preventDefault?: boolean // Default: false
  stopPropagation?: boolean // Default: false
  ignoreInputs?: boolean   // Default: false
}
interface SingleKeyBindOptions {
  enabled?: boolean        // Default: true
  preventDefault?: boolean // Default: false
  stopPropagation?: boolean // Default: false
  ignoreInputs?: boolean   // Default: false
}

KeyBindHandlerConfig

interface KeyBindHandlerConfig {
  binding: string
  handler: (event: KeyboardEvent) => void
  options?: SingleKeyBindOptions
}
interface KeyBindHandlerConfig {
  binding: string
  handler: (event: KeyboardEvent) => void
  options?: SingleKeyBindOptions
}

Functions

matchesKeyboardEvent(parsed, event, options?)

Low-level check: does this KeyboardEvent match a ParsedKeyBind?

function matchesKeyboardEvent(
  parsed: ParsedKeyBind,
  event: KeyboardEvent,
  options?: MatchOptions
): boolean
function matchesKeyboardEvent(
  parsed: ParsedKeyBind,
  event: KeyboardEvent,
  options?: MatchOptions
): boolean

Checks all four modifier flags (ctrlKey, altKey, metaKey, shiftKey) for exact match, then compares the non-modifier key. Handles aliases like space and esc automatically.

Example:

import { parseKeyBind } from '@gentleduck/vim/parser'
 
const parsed = parseKeyBind('ctrl+s')
 
document.addEventListener('keydown', (e) => {
  if (matchesKeyboardEvent(parsed, e)) {
    e.preventDefault()
    save()
  }
})
import { parseKeyBind } from '@gentleduck/vim/parser'
 
const parsed = parseKeyBind('ctrl+s')
 
document.addEventListener('keydown', (e) => {
  if (matchesKeyboardEvent(parsed, e)) {
    e.preventDefault()
    save()
  }
})

isInputElement(el)

Returns true if the element is a text-entry input where key bindings should typically be suppressed.

function isInputElement(el: Element | null): boolean
function isInputElement(el: Element | null): boolean

Considers these as input elements: <input> (except button/submit/reset types), <textarea>, <select>, and any element with contentEditable.


createKeyBindHandler(config)

Creates a standalone keydown event handler for a single binding. This is the simplest way to bind a shortcut without using the full registry system.

function createKeyBindHandler(config: KeyBindHandlerConfig): (event: KeyboardEvent) => void
function createKeyBindHandler(config: KeyBindHandlerConfig): (event: KeyboardEvent) => void

Example:

Create the handler with your binding and options:

const handler = createKeyBindHandler({
  binding: 'Mod+S',
  handler: () => save(),
  options: { preventDefault: true, ignoreInputs: true },
})
const handler = createKeyBindHandler({
  binding: 'Mod+S',
  handler: () => save(),
  options: { preventDefault: true, ignoreInputs: true },
})

Attach it to the DOM:

document.addEventListener('keydown', handler)
document.addEventListener('keydown', handler)

Clean up when no longer needed:

document.removeEventListener('keydown', handler)
document.removeEventListener('keydown', handler)


createMultiKeyBindHandler(configs)

Creates a single event handler that checks multiple bindings. First match wins.

function createMultiKeyBindHandler(configs: KeyBindHandlerConfig[]): (event: KeyboardEvent) => void
function createMultiKeyBindHandler(configs: KeyBindHandlerConfig[]): (event: KeyboardEvent) => void

Example:

const handler = createMultiKeyBindHandler([
  {
    binding: 'Mod+S',
    handler: () => save(),
    options: { preventDefault: true },
  },
  {
    binding: 'Mod+Shift+S',
    handler: () => saveAs(),
    options: { preventDefault: true },
  },
  {
    binding: 'Mod+Z',
    handler: () => undo(),
    options: { preventDefault: true },
  },
])
 
document.addEventListener('keydown', handler)
const handler = createMultiKeyBindHandler([
  {
    binding: 'Mod+S',
    handler: () => save(),
    options: { preventDefault: true },
  },
  {
    binding: 'Mod+Shift+S',
    handler: () => saveAs(),
    options: { preventDefault: true },
  },
  {
    binding: 'Mod+Z',
    handler: () => undo(),
    options: { preventDefault: true },
  },
])
 
document.addEventListener('keydown', handler)