From 38a69e4ca319f9db3c54a5cb69cd5645f12369d5 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Tue, 10 Dec 2019 21:45:16 +0900 Subject: Add prefix functions in KeySequence --- src/content/domains/KeySequence.ts | 91 +++++++++++++++ src/content/repositories/KeymapRepository.ts | 2 +- src/content/usecases/KeymapUseCase.ts | 2 +- src/shared/settings/KeySequence.ts | 69 ----------- test/content/domains/KeySequence.test.ts | 166 +++++++++++++++++++++++++++ test/shared/settings/KeySequence.test.ts | 103 ----------------- 6 files changed, 259 insertions(+), 174 deletions(-) create mode 100644 src/content/domains/KeySequence.ts delete mode 100644 src/shared/settings/KeySequence.ts create mode 100644 test/content/domains/KeySequence.test.ts delete mode 100644 test/shared/settings/KeySequence.test.ts diff --git a/src/content/domains/KeySequence.ts b/src/content/domains/KeySequence.ts new file mode 100644 index 0000000..4534b60 --- /dev/null +++ b/src/content/domains/KeySequence.ts @@ -0,0 +1,91 @@ +import Key from '../../shared/settings/Key'; + +export default class KeySequence { + constructor( + public readonly keys: Key[], + ) { + } + + 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 (!this.keys[i].equals(o.keys[i])) { + return false; + } + } + return true; + } + + isDigitOnly(): boolean { + return this.keys.every(key => key.isDigit()); + } + + repeatCount(): number { + let nonDigitAt = this.keys.findIndex(key => !key.isDigit()); + if (this.keys.length === 0 || nonDigitAt === 0) { + return 1; + } + if (nonDigitAt === -1) { + nonDigitAt = this.keys.length; + } + let digits = this.keys.slice(0, nonDigitAt) + .map(key => key.key) + .join(''); + return Number(digits); + } + + trimNumericPrefix(): KeySequence { + let nonDigitAt = this.keys.findIndex(key => !key.isDigit()); + if (nonDigitAt === -1) { + nonDigitAt = this.keys.length; + } + return new KeySequence(this.keys.slice(nonDigitAt)); + } + + splitNumericPrefix(): [KeySequence, KeySequence] { + let nonDigitIndex = this.keys.findIndex(key => !key.isDigit()); + if (nonDigitIndex === -1) { + return [this, new KeySequence([])]; + } + return [ + new KeySequence(this.keys.slice(0, nonDigitIndex)), + new KeySequence(this.keys.slice(nonDigitIndex)), + ]; + } + + static fromMapKeys(keys: string): KeySequence { + const fromMapKeysRecursive = ( + remaining: string, mappedKeys: Key[], + ): Key[] => { + if (remaining.length === 0) { + return mappedKeys; + } + + let nextPos = 1; + if (remaining.startsWith('<')) { + let ltPos = remaining.indexOf('>'); + if (ltPos > 0) { + nextPos = ltPos + 1; + } + } + + return fromMapKeysRecursive( + remaining.slice(nextPos), + mappedKeys.concat([Key.fromMapKey(remaining.slice(0, nextPos))]) + ); + }; + + let data = fromMapKeysRecursive(keys, []); + return new KeySequence(data); + } +} diff --git a/src/content/repositories/KeymapRepository.ts b/src/content/repositories/KeymapRepository.ts index 3391229..2944723 100644 --- a/src/content/repositories/KeymapRepository.ts +++ b/src/content/repositories/KeymapRepository.ts @@ -1,5 +1,5 @@ import Key from '../../shared/settings/Key'; -import KeySequence from '../../shared/settings/KeySequence'; +import KeySequence from '../domains/KeySequence'; export default interface KeymapRepository { enqueueKey(key: Key): KeySequence; diff --git a/src/content/usecases/KeymapUseCase.ts b/src/content/usecases/KeymapUseCase.ts index 67d667d..1707f6f 100644 --- a/src/content/usecases/KeymapUseCase.ts +++ b/src/content/usecases/KeymapUseCase.ts @@ -5,7 +5,7 @@ import AddonEnabledRepository from '../repositories/AddonEnabledRepository'; import * as operations from '../../shared/operations'; import Keymaps from '../../shared/settings/Keymaps'; import Key from '../../shared/settings/Key'; -import KeySequence from '../../shared/settings/KeySequence'; +import KeySequence from '../domains/KeySequence'; import AddressRepository from '../repositories/AddressRepository'; type KeymapEntityMap = Map; diff --git a/src/shared/settings/KeySequence.ts b/src/shared/settings/KeySequence.ts deleted file mode 100644 index de1c6bb..0000000 --- a/src/shared/settings/KeySequence.ts +++ /dev/null @@ -1,69 +0,0 @@ -import Key from './Key'; - -export default class KeySequence { - constructor( - public readonly keys: Key[], - ) { - } - - 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 (!this.keys[i].equals(o.keys[i])) { - return false; - } - } - return true; - } - - isDigitOnly(): boolean { - return this.keys.every(key => key.isDigit()); - } - - splitNumericPrefix(): [KeySequence, KeySequence] { - let nonDigitIndex = this.keys.findIndex(key => !key.isDigit()); - if (nonDigitIndex === -1) { - return [this, new KeySequence([])]; - } - return [ - new KeySequence(this.keys.slice(0, nonDigitIndex)), - new KeySequence(this.keys.slice(nonDigitIndex)), - ]; - } - - static fromMapKeys(keys: string): KeySequence { - const fromMapKeysRecursive = ( - remaining: string, mappedKeys: Key[], - ): Key[] => { - if (remaining.length === 0) { - return mappedKeys; - } - - let nextPos = 1; - if (remaining.startsWith('<')) { - let ltPos = remaining.indexOf('>'); - if (ltPos > 0) { - nextPos = ltPos + 1; - } - } - - return fromMapKeysRecursive( - remaining.slice(nextPos), - mappedKeys.concat([Key.fromMapKey(remaining.slice(0, nextPos))]) - ); - }; - - let data = fromMapKeysRecursive(keys, []); - return new KeySequence(data); - } -} diff --git a/test/content/domains/KeySequence.test.ts b/test/content/domains/KeySequence.test.ts new file mode 100644 index 0000000..bc16189 --- /dev/null +++ b/test/content/domains/KeySequence.test.ts @@ -0,0 +1,166 @@ +import KeySequence from '../../../src/content/domains/KeySequence'; +import { expect } from 'chai' +import Key from "../../../src/shared/settings/Key"; + +describe("KeySequence", () => { + describe('#push', () => { + it('append a key to the sequence', () => { + let seq = new KeySequence([]); + seq.push(Key.fromMapKey('g')); + seq.push(Key.fromMapKey('')); + + expect(seq.keys[0].key).to.equal('g'); + expect(seq.keys[1].key).to.equal('U'); + expect(seq.keys[1].shift).to.be.true; + }) + }); + + describe('#startsWith', () => { + it('returns true if the key sequence starts with param', () => { + let seq = new KeySequence([ + Key.fromMapKey('g'), + Key.fromMapKey(''), + ]); + + expect(seq.startsWith(new KeySequence([ + ]))).to.be.true; + expect(seq.startsWith(new KeySequence([ + Key.fromMapKey('g'), + ]))).to.be.true; + expect(seq.startsWith(new KeySequence([ + Key.fromMapKey('g'), Key.fromMapKey(''), + ]))).to.be.true; + expect(seq.startsWith(new KeySequence([ + Key.fromMapKey('g'), Key.fromMapKey(''), Key.fromMapKey('x'), + ]))).to.be.false; + expect(seq.startsWith(new KeySequence([ + Key.fromMapKey('h'), + ]))).to.be.false; + }); + + it('returns true if the empty sequence starts with an empty sequence', () => { + let seq = new KeySequence([]); + + expect(seq.startsWith(new KeySequence([]))).to.be.true; + expect(seq.startsWith(new KeySequence([ + Key.fromMapKey('h'), + ]))).to.be.false; + }) + }); + + describe('#isDigitOnly', () => { + it('returns true the keys are only digits', () => { + expect(new KeySequence([ + new Key({ key: '4' }), + new Key({ key: '0' }), + ]).isDigitOnly()).to.be.true; + expect(new KeySequence([ + new Key({ key: '4' }), + new Key({ key: '0' }), + new Key({ key: 'z' }), + ]).isDigitOnly()).to.be.false; + }) + }); + + describe('#repeatCount', () => { + it('returns repeat count with a numeric prefix', () => { + let seq = new KeySequence([ + new Key({ key: '1' }), new Key({ key: '0' }) , + new Key({ key: 'g' }), new Key({ key: 'g' }) , + ]); + expect(seq.repeatCount()).to.equal(10); + + seq = new KeySequence([ + new Key({ key: '0' }), new Key({ key: '5' }) , + new Key({ key: 'g' }), new Key({ key: 'g' }) , + ]); + expect(seq.repeatCount()).to.equal(5); + }); + + it('returns 1 if no numeric prefix', () => { + let seq = new KeySequence([ + new Key({ key: 'g' }), new Key({ key: 'g' }) , + ]); + expect(seq.repeatCount()).to.equal(1); + + seq = new KeySequence([]); + expect(seq.repeatCount()).to.equal(1); + }); + + it('returns whole keys if digits only sequence', () => { + let seq = new KeySequence([ + new Key({ key: '1' }), new Key({ key: '0' }) , + ]); + expect(seq.repeatCount()).to.equal(10); + + seq = new KeySequence([ + new Key({ key: '0' }), new Key({ key: '5' }) , + ]); + expect(seq.repeatCount()).to.equal(5); + }); + }); + + describe('#trimNumericPrefix', () => { + it('removes numeric prefix', () => { + let seq = new KeySequence([ + new Key({ key: '1' }), new Key({ key: '0' }) , + new Key({ key: 'g' }), new Key({ key: 'g' }) , new Key({ key: '3' }) , + ]).trimNumericPrefix(); + expect(seq.keys.map(key => key.key)).to.deep.equal(['g', 'g', '3']); + }); + + it('returns empty if keys contains only digis', () => { + let seq = new KeySequence([ + new Key({ key: '1' }), new Key({ key: '0' }) , + ]).trimNumericPrefix(); + expect(seq.trimNumericPrefix().keys).to.be.empty; + }); + + it('returns itself if no numeric prefix', () => { + let seq = new KeySequence([ + new Key({ key: 'g' }), new Key({ key: 'g' }) , new Key({ key: '3' }) , + ]).trimNumericPrefix(); + + expect(seq.keys.map(key => key.key)).to.deep.equal(['g', 'g', '3']); + }); + }); + + describe('#splitNumericPrefix', () => { + it('splits numeric prefix', () => { + expect(KeySequence.fromMapKeys('10gg').splitNumericPrefix()).to.deep.equal([ + KeySequence.fromMapKeys('10'), + KeySequence.fromMapKeys('gg'), + ]); + expect(KeySequence.fromMapKeys('10').splitNumericPrefix()).to.deep.equal([ + KeySequence.fromMapKeys('10'), + new KeySequence([]), + ]); + expect(KeySequence.fromMapKeys('gg').splitNumericPrefix()).to.deep.equal([ + new KeySequence([]), + KeySequence.fromMapKeys('gg'), + ]); + }); + }); + + describe('#fromMapKeys', () => { + it('returns mapped keys for Shift+Esc', () => { + let keys = KeySequence.fromMapKeys('').keys; + expect(keys).to.have.lengthOf(1); + expect(keys[0].key).to.equal('Esc'); + expect(keys[0].shift).to.be.true; + }); + + it('returns mapped keys for ad', () => { + let keys = KeySequence.fromMapKeys('ad').keys; + expect(keys).to.have.lengthOf(5); + expect(keys[0].key).to.equal('a'); + expect(keys[1].ctrl).to.be.true; + expect(keys[1].key).to.equal('b'); + expect(keys[2].alt).to.be.true; + expect(keys[2].key).to.equal('c'); + expect(keys[3].key).to.equal('d'); + expect(keys[4].meta).to.be.true; + expect(keys[4].key).to.equal('e'); + }); + }) +}); diff --git a/test/shared/settings/KeySequence.test.ts b/test/shared/settings/KeySequence.test.ts deleted file mode 100644 index 8a7a350..0000000 --- a/test/shared/settings/KeySequence.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -import KeySequence from '../../../src/shared/settings/KeySequence'; -import { expect } from 'chai' -import Key from "../../../src/shared/settings/Key"; - -describe("KeySequence", () => { - describe('#push', () => { - it('append a key to the sequence', () => { - let seq = new KeySequence([]); - seq.push(Key.fromMapKey('g')); - seq.push(Key.fromMapKey('')); - - expect(seq.keys[0].key).to.equal('g'); - expect(seq.keys[1].key).to.equal('U'); - expect(seq.keys[1].shift).to.be.true; - }) - }); - - describe('#startsWith', () => { - it('returns true if the key sequence starts with param', () => { - let seq = new KeySequence([ - Key.fromMapKey('g'), - Key.fromMapKey(''), - ]); - - expect(seq.startsWith(new KeySequence([ - ]))).to.be.true; - expect(seq.startsWith(new KeySequence([ - Key.fromMapKey('g'), - ]))).to.be.true; - expect(seq.startsWith(new KeySequence([ - Key.fromMapKey('g'), Key.fromMapKey(''), - ]))).to.be.true; - expect(seq.startsWith(new KeySequence([ - Key.fromMapKey('g'), Key.fromMapKey(''), Key.fromMapKey('x'), - ]))).to.be.false; - expect(seq.startsWith(new KeySequence([ - Key.fromMapKey('h'), - ]))).to.be.false; - }); - - it('returns true if the empty sequence starts with an empty sequence', () => { - let seq = new KeySequence([]); - - expect(seq.startsWith(new KeySequence([]))).to.be.true; - expect(seq.startsWith(new KeySequence([ - Key.fromMapKey('h'), - ]))).to.be.false; - }) - }); - - describe('#isDigitOnly', () => { - it('returns true the keys are only digits', () => { - expect(new KeySequence([ - new Key({ key: '4' }), - new Key({ key: '0' }), - ]).isDigitOnly()).to.be.true; - expect(new KeySequence([ - new Key({ key: '4' }), - new Key({ key: '0' }), - new Key({ key: 'z' }), - ]).isDigitOnly()).to.be.false; - }) - }); - - describe('#splitNumericPrefix', () => { - it('splits numeric prefix', () => { - expect(KeySequence.fromMapKeys('10gg').splitNumericPrefix()).to.deep.equal([ - KeySequence.fromMapKeys('10'), - KeySequence.fromMapKeys('gg'), - ]); - expect(KeySequence.fromMapKeys('10').splitNumericPrefix()).to.deep.equal([ - KeySequence.fromMapKeys('10'), - new KeySequence([]), - ]); - expect(KeySequence.fromMapKeys('gg').splitNumericPrefix()).to.deep.equal([ - new KeySequence([]), - KeySequence.fromMapKeys('gg'), - ]); - }); - }); - - describe('#fromMapKeys', () => { - it('returns mapped keys for Shift+Esc', () => { - let keys = KeySequence.fromMapKeys('').keys; - expect(keys).to.have.lengthOf(1); - expect(keys[0].key).to.equal('Esc'); - expect(keys[0].shift).to.be.true; - }); - - it('returns mapped keys for ad', () => { - let keys = KeySequence.fromMapKeys('ad').keys; - expect(keys).to.have.lengthOf(5); - expect(keys[0].key).to.equal('a'); - expect(keys[1].ctrl).to.be.true; - expect(keys[1].key).to.equal('b'); - expect(keys[2].alt).to.be.true; - expect(keys[2].key).to.equal('c'); - expect(keys[3].key).to.equal('d'); - expect(keys[4].meta).to.be.true; - expect(keys[4].key).to.equal('e'); - }); - }) -}); -- cgit v1.2.3