Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ module.exports = {
testEnvironment: 'jsdom',
coverageDirectory: './coverage',
coverageReporters: ['lcov'],
testPathIgnorePatterns: ['/node_modules/', '/benchmarks/'],
testPathIgnorePatterns: ['/node_modules/', '/benchmarks/', '/tmp/'],
}
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"author": "Amine Ben hammou",
"description": "A Typescript library to handle keybindings efficiently.",
"license": "MIT",
"version": "1.0.0-beta.2",
"version": "1.0.0-beta.3",
"source": "src/index.ts",
"main": "dist/index.js",
"module": "dist/index.mjs",
Expand All @@ -25,15 +25,15 @@
"just-types": "^1.4.0"
},
"devDependencies": {
"@parcel/packager-ts": "^2.2.1",
"@parcel/transformer-typescript-types": "^2.2.1",
"@parcel/packager-ts": "^2.3.2",
"@parcel/transformer-typescript-types": "^2.3.2",
"@testing-library/dom": "^8.11.3",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.4.0",
"jest": "^27.4.7",
"parcel": "^2.2.1",
"@types/jest": "^27.4.1",
"jest": "^27.5.1",
"parcel": "^2.3.2",
"ts-jest": "^27.1.3",
"tslib": "^2.3.1",
"typescript": "^4.5.5"
"typescript": "^4.6.2"
}
}
27 changes: 11 additions & 16 deletions src/handler/Handler.test.ts → src/Handler.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import {codes} from '../constants'
import {event, fnMocks} from '../test-utils'
import {fnMocks} from './test-utils'
import {Handler} from './Handler'
import {createEvent} from './event'

describe('Handler', () => {
const handler = new Handler({
codes,
aliases: {
space: ' ',
plus: '+',
},
history: [],
historySize: 0,
bindings: new Map(),
Expand All @@ -21,30 +16,30 @@ describe('Handler', () => {
handler.add('ctrl+a', fns.get('ctrl+a1'))
handler.add('ctrl+a', fns.get('ctrl+a2'))

handler.handle(event('ctrl', 'a'))
handler.handle(createEvent('ctrl+a'))
fns.call('ctrl+a1', 'ctrl+a2').checkCalls()

handler.handle(event('ctrl', 'b'))
handler.handle(createEvent('ctrl+b'))
fns.checkCalls()
})

it(`removes bindings`, () => {
handler.remove('ctrl+a', fns.get('ctrl+a2'))

handler.handle(event('ctrl', 'a'))
handler.handle(createEvent('ctrl+a'))
fns.call('ctrl+a1').checkCalls()
})

it(`handles multi-keys sequences`, () => {
handler.add(['ctrl+alt', 'ctrl+plus'], fns.get('ctrl+alt ctrl+plus'))
handler.add(['ctrl+shift+space', 'c'], fns.get('ctrl+shift+space c'))
handler.add('ctrl+alt', 'ctrl+plus', fns.get('ctrl+alt ctrl+plus'))
handler.add('ctrl+shift+space', 'c', fns.get('ctrl+shift+space c'))

handler.handle(event('ctrl', 'alt'))
handler.handle(event('ctrl', '+'))
handler.handle(createEvent('ctrl+alt'))
handler.handle(createEvent('ctrl++'))
fns.call('ctrl+alt ctrl+plus').checkCalls()

handler.handle(event('ctrl', 'shift', ' '))
handler.handle(event('c'))
handler.handle(createEvent('ctrl+shift+ '))
handler.handle(createEvent('c'))
fns.call('ctrl+shift+space c').checkCalls()
})
})
40 changes: 40 additions & 0 deletions src/Handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {Callback, HandlerInterface, State, Binding, Sequence} from './types'
import {addBinding, disableSequence, enableSequence, handleEvent, removeBinding} from './state'

export class Handler implements HandlerInterface {
constructor(protected state: State) {
this.add = this.add.bind(this)
this.remove = this.remove.bind(this)
this.handle = this.handle.bind(this)
}

add(...args: Binding): this {
const keys = args.slice(0, -1) as Sequence
const fn = args.at(-1) as Callback
this.state = addBinding(this.state, keys, fn)
return this
}

remove(...args: Binding): this {
const keys = args.slice(0, -1) as Sequence
const fn = args.at(-1) as Callback
this.state = removeBinding(this.state, keys, fn)
return this
}

enable(...keys: Sequence): this {
this.state = enableSequence(this.state, keys)
return this
}

disable(...keys: Sequence): this {
this.state = disableSequence(this.state, keys)
return this
}

handle(event: KeyboardEvent) {
const [state, fns] = handleEvent(this.state, event)
this.state = state
return fns.length > 0
}
}
30 changes: 28 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {AliasCharacter, Character} from './types'

export const modifiers = ['ctrl', 'alt', 'meta', 'shift'] as const

export const chars =
Expand All @@ -9,12 +11,36 @@ chars[1] = '_'
export const codes: Record<string, number> = {}
for (const [i, c] of chars.entries()) codes[c] = i

export const aliases = {
export const aliases: Record<AliasCharacter, Character> = {
space: ' ',
plus: '+',
up: 'arrowup',
down: 'arrowdown',
left: 'arrowleft',
right: 'arrowright',
esc: 'escape',
} as const
}

/**
We want to encode keys and sequences of keys as numbers for performance
A key code is 13 bits: XXXXXXXXXCAMS
XXXXXXXXX: 9 bits representing the character (event.key value).
A character can't have a code `0`, that's a special value.
We can have at most 511 different characters!
CAMS: 4 bits representing the modifiers `Ctrl`, `Alt`, `Meta` and `Shift` in this order.
A sequence of keys is represented by the concatenation of codes of the keys.
An integer can safely be represented with 53 bits in Javascript `Number.MAX_SAFE_INTEGER`
Since every key takes 13bits, a sequence can at most contain 4 keys = 52 bits!
*/

export const MODIFIERS_SIZE = 4
export const CHARACTER_SIZE = 9
export const CTRL_MASK = 0b1000
export const ALT_MASK = 0b0100
export const META_MASK = 0b0010
export const SHIFT_MASK = 0b0001
export const KEY_SIZE = CHARACTER_SIZE + MODIFIERS_SIZE
export const MODIFIERS_UPPER_BOUND = 2 ** MODIFIERS_SIZE
export const ONE_KEY_UPPER_BOUND = 2 ** KEY_SIZE
export const TWO_KEYS_UPPER_BOUND = 2 ** (2 * KEY_SIZE)
export const THREE_KEYS_UPPER_BOUND = 2 ** (3 * KEY_SIZE)
173 changes: 0 additions & 173 deletions src/encode.test.ts

This file was deleted.

Loading