diff options
author | Shin'ya Ueoka <ueokande@i-beam.org> | 2019-05-18 21:43:56 +0900 |
---|---|---|
committer | Shin'ya Ueoka <ueokande@i-beam.org> | 2019-05-18 21:43:56 +0900 |
commit | a5518dce3d101cb1cb65724b82079f66f20c80c8 (patch) | |
tree | 79537b86e4a7bf231e8801c9c6bf2aeb94450343 /src/content/domains | |
parent | 2ec912c262b51fe9523ebf74d5062d0b9bbdab71 (diff) |
Define Key and KeySequence
Diffstat (limited to 'src/content/domains')
-rw-r--r-- | src/content/domains/Key.ts | 74 | ||||
-rw-r--r-- | src/content/domains/KeySequence.ts | 64 |
2 files changed, 138 insertions, 0 deletions
diff --git a/src/content/domains/Key.ts b/src/content/domains/Key.ts new file mode 100644 index 0000000..fbbb4bb --- /dev/null +++ b/src/content/domains/Key.ts @@ -0,0 +1,74 @@ +export default interface Key { + key: string; + shiftKey?: boolean; + ctrlKey?: boolean; + altKey?: boolean; + metaKey?: boolean; + + // eslint-disable-next-line semi +} + +const modifiedKeyName = (name: string): string => { + if (name === ' ') { + return 'Space'; + } + if (name.length === 1) { + return name; + } else if (name === 'Escape') { + return 'Esc'; + } + return name; +}; + +export const fromKeyboardEvent = (e: KeyboardEvent): Key => { + let key = modifiedKeyName(e.key); + let shift = e.shiftKey; + if (key.length === 1 && key.toUpperCase() === key.toLowerCase()) { + // make shift false for symbols to enable key bindings by symbold keys. + // But this limits key bindings by symbol keys with Shift (such as Shift+$>. + shift = false; + } + + return { + key: modifiedKeyName(e.key), + shiftKey: shift, + ctrlKey: e.ctrlKey, + altKey: e.altKey, + metaKey: e.metaKey, + }; +}; + +export const fromMapKey = (key: string): Key => { + if (key.startsWith('<') && key.endsWith('>')) { + let inner = key.slice(1, -1); + let shift = inner.includes('S-'); + let base = inner.slice(inner.lastIndexOf('-') + 1); + if (shift && base.length === 1) { + base = base.toUpperCase(); + } else if (!shift && base.length === 1) { + base = base.toLowerCase(); + } + return { + key: base, + shiftKey: inner.includes('S-'), + ctrlKey: inner.includes('C-'), + altKey: inner.includes('A-'), + metaKey: inner.includes('M-'), + }; + } + return { + key: key, + shiftKey: key.toLowerCase() !== key, + ctrlKey: false, + altKey: false, + metaKey: false, + }; +}; + +export const equals = (e1: Key, e2: Key): boolean => { + return e1.key === e2.key && + e1.ctrlKey === e2.ctrlKey && + e1.metaKey === e2.metaKey && + e1.altKey === e2.altKey && + e1.shiftKey === e2.shiftKey; +}; diff --git a/src/content/domains/KeySequence.ts b/src/content/domains/KeySequence.ts new file mode 100644 index 0000000..6a05c2f --- /dev/null +++ b/src/content/domains/KeySequence.ts @@ -0,0 +1,64 @@ +import Key, * as keyUtils from './Key'; + +export default class KeySequence { + private keys: Key[]; + + private constructor(keys: Key[]) { + this.keys = keys; + } + + static from(keys: Key[]): KeySequence { + return new KeySequence(keys); + } + + push(key: Key): number { + return this.keys.push(key); + } + + length(): number { + return this.keys.length; + } + + startsWith(o: KeySequence): boolean { + if (this.keys.length < o.keys.length) { + return false; + } + for (let i = 0; i < o.keys.length; ++i) { + if (!keyUtils.equals(this.keys[i], o.keys[i])) { + return false; + } + } + return true; + } + + getKeyArray(): Key[] { + return this.keys; + } +} + +export const fromMapKeys = (keys: string): KeySequence => { + const fromMapKeysRecursive = ( + remainings: string, mappedKeys: Key[], + ): Key[] => { + if (remainings.length === 0) { + return mappedKeys; + } + + let nextPos = 1; + if (remainings.startsWith('<')) { + let ltPos = remainings.indexOf('>'); + if (ltPos > 0) { + nextPos = ltPos + 1; + } + } + + return fromMapKeysRecursive( + remainings.slice(nextPos), + mappedKeys.concat([keyUtils.fromMapKey(remainings.slice(0, nextPos))]) + ); + }; + + let data = fromMapKeysRecursive(keys, []); + return KeySequence.from(data); +}; + |