mirror of
https://github.com/keymanapp/keyman.git
synced 2026-10-23 22:37:43 +00:00
Merge pull request #8132 from keymanapp/chore/developer/8069-todocleanup-epic-ldml
fix(developer): todo cleanup, common code, tests 🙀
This commit is contained in:
commit
031998f0a1
5 changed files with 153 additions and 58 deletions
|
|
@ -6,6 +6,7 @@ import GlobalSections = KMXPlus.GlobalSections;
|
|||
import Key2 = KMXPlus.Key2;
|
||||
import ListItem = KMXPlus.ListItem;
|
||||
import Key2Flicks = KMXPlus.Key2Flicks;
|
||||
import { allUsedKeyIdsInLayers, calculateUniqueKeys } from '../util/util.js';
|
||||
|
||||
|
||||
export class Key2Compiler extends SectionCompiler {
|
||||
|
|
@ -20,28 +21,6 @@ export class Key2Compiler extends SectionCompiler {
|
|||
return valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a set of all used keys
|
||||
* public for test
|
||||
*/
|
||||
public allUsedKeyIds(): Set<string> {
|
||||
const s = new Set<string>();
|
||||
if (this.keyboard?.layers) {
|
||||
for (const layers of this.keyboard.layers || []) {
|
||||
for (const layer of layers.layer || []) {
|
||||
for (const row of layer.row || []) {
|
||||
if (row.keys) {
|
||||
for (const k of row.keys.split(" ")) {
|
||||
s.add(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public compile(sections: GlobalSections): Key2 {
|
||||
if (!this.keyboard?.keys?.key && !this.keyboard?.keys?.flicks) {
|
||||
// short-circuit if no keys or flicks
|
||||
|
|
@ -81,20 +60,8 @@ export class Key2Compiler extends SectionCompiler {
|
|||
}
|
||||
|
||||
public loadKeys(sections: GlobalSections, sect: Key2) {
|
||||
const usedKeys = this.allUsedKeyIds();
|
||||
|
||||
// TODO-LDML factor common code
|
||||
// Need 'newer' (later) keys to override older ones.
|
||||
const reverseKeys = [...this.keyboard.keys.key].reverse(); // newest to oldest
|
||||
const alreadySeen = new Set<string>();
|
||||
// filter out only the keys that haven't already been seen
|
||||
const uniqueKeys = reverseKeys.filter(({id}) => {
|
||||
if (!alreadySeen.has(id)) {
|
||||
alreadySeen.add(id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
const usedKeys = allUsedKeyIdsInLayers(this.keyboard?.layers);
|
||||
const uniqueKeys = calculateUniqueKeys([...this.keyboard.keys?.key]);
|
||||
|
||||
for (let key of uniqueKeys) {
|
||||
if (!usedKeys.has(key.id)) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { SectionCompiler } from "./section-compiler.js";
|
|||
import GlobalSections = KMXPlus.GlobalSections;
|
||||
import Keys = KMXPlus.Keys;
|
||||
import USVirtualKeyMap = Constants.USVirtualKeyMap;
|
||||
import { calculateUniqueKeys } from '../util/util.js';
|
||||
|
||||
export class KeysCompiler extends SectionCompiler {
|
||||
|
||||
|
|
@ -14,19 +15,7 @@ export class KeysCompiler extends SectionCompiler {
|
|||
}
|
||||
|
||||
private validateHardwareLayer(layer: LDMLKeyboard.LKLayer) {
|
||||
// TODO-LDML factor common code
|
||||
// Need 'newer' (later) keys to override older ones.
|
||||
const reverseKeys = [...this.keyboard.keys?.key].reverse(); // newest to oldest
|
||||
const alreadySeen = new Set<string>();
|
||||
// filter out only the keys that haven't already been seen
|
||||
const uniqueKeys = reverseKeys.filter(({id}) => {
|
||||
if (!alreadySeen.has(id)) {
|
||||
alreadySeen.add(id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
const uniqueKeys = calculateUniqueKeys([...this.keyboard.keys?.key]);
|
||||
let valid = true;
|
||||
if(layer.row.length > USVirtualKeyMap.length) {
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_HardwareLayerHasTooManyRows());
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { LKKey, LKLayers } from "@keymanapp/common-types/src/ldml-keyboard/ldml-keyboard-xml";
|
||||
|
||||
/**
|
||||
* Verifies that value is an item in the enumeration.
|
||||
|
|
@ -5,3 +6,51 @@
|
|||
export function isValidEnumValue<T extends {[key: number]: string | number}>(enu: T, value: string) {
|
||||
return (Object.values(enu) as string[]).includes(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unique LKKeys only, preserving later ones
|
||||
* in case of conflict. (i.e. later overrides)
|
||||
* @param keys list of keys to consider. (mutated)
|
||||
* @returns Array of unique keys. Order is not specified.
|
||||
*/
|
||||
export function calculateUniqueKeys(keys?: LKKey[]): LKKey[] {
|
||||
if (!keys) {
|
||||
return [];
|
||||
}
|
||||
// Need 'newer' (later) keys to override older ones.
|
||||
const reverseKeys = keys.reverse(); // newest to oldest
|
||||
const alreadySeen = new Set<string>();
|
||||
// filter out only the keys that haven't already been seen
|
||||
const uniqueKeys = reverseKeys.filter(({ id }) => {
|
||||
if (!alreadySeen.has(id)) {
|
||||
alreadySeen.add(id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
return uniqueKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param layersList list of layers elements, from `keyboard?.layers`
|
||||
* @returns set of key IDs
|
||||
*/
|
||||
export function allUsedKeyIdsInLayers(layersList : LKLayers[] | null): Set<string> {
|
||||
const s = new Set<string>();
|
||||
if (layersList) {
|
||||
for (const layers of layersList || []) {
|
||||
for (const layer of layers.layer || []) {
|
||||
for (const row of layer.row || []) {
|
||||
if (row.keys) {
|
||||
for (const k of row.keys.split(" ")) {
|
||||
s.add(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
import 'mocha';
|
||||
import {assert} from 'chai';
|
||||
|
||||
describe('null-test', function () {
|
||||
it('should-pass-null-test', function() {
|
||||
assert.isTrue(true);
|
||||
});
|
||||
});
|
||||
|
||||
99
developer/src/kmc-keyboard/test/test-utils.ts
Normal file
99
developer/src/kmc-keyboard/test/test-utils.ts
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
import 'mocha';
|
||||
import {assert} from 'chai';
|
||||
import { isValidEnumValue, calculateUniqueKeys, allUsedKeyIdsInLayers } from '../src/util/util.js';
|
||||
|
||||
describe('test of util/util.ts', () => {
|
||||
describe('isValidEnumValue()', () => {
|
||||
enum MyFruit {
|
||||
apple, peach, pear, plum
|
||||
};
|
||||
enum MyNumbers {
|
||||
wieħed=1, tnejn=2, tlieta=3
|
||||
};
|
||||
enum MyLetters {
|
||||
akka='h', magħtugha='ħ'
|
||||
};
|
||||
|
||||
it('should handle a bare enum', ()=> {
|
||||
assert.ok(isValidEnumValue(MyFruit, 'apple'));
|
||||
assert.notOk(isValidEnumValue(MyFruit, 'banana'));
|
||||
assert.notOk(isValidEnumValue(MyFruit, ''));
|
||||
assert.notOk(isValidEnumValue(MyFruit, undefined));
|
||||
assert.notOk(isValidEnumValue(MyFruit, null));
|
||||
assert.notOk(isValidEnumValue(MyFruit, 'unknown'));
|
||||
});
|
||||
|
||||
it('should handle a numerical enum', ()=> {
|
||||
assert.ok(isValidEnumValue(MyNumbers, 'tlieta'));
|
||||
assert.notOk(isValidEnumValue(MyNumbers, '3'));
|
||||
assert.notOk(isValidEnumValue(MyNumbers, 'seventy-six'));
|
||||
assert.notOk(isValidEnumValue(MyNumbers, '76'));
|
||||
});
|
||||
|
||||
it('should handle a string enum', ()=> {
|
||||
assert.ok(isValidEnumValue(MyLetters, 'h'));
|
||||
assert.ok(isValidEnumValue(MyLetters, 'ħ'));
|
||||
assert.notOk(isValidEnumValue(MyLetters, 'akka'));
|
||||
assert.notOk(isValidEnumValue(MyLetters, 'z'));
|
||||
assert.notOk(isValidEnumValue(MyLetters, 'zed'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('calculateUniqueKeys()', () => {
|
||||
it('should handle some null cases', () => {
|
||||
assert.sameDeepMembers(calculateUniqueKeys(null),
|
||||
[]);
|
||||
assert.sameDeepMembers(calculateUniqueKeys([]),
|
||||
[]);
|
||||
});
|
||||
it('should handle some real cases', () => {
|
||||
assert.sameDeepMembers(calculateUniqueKeys([
|
||||
{ id: 'a', to: 'a' },
|
||||
{ id: 'a', to: 'a' }, // dup
|
||||
{ id: 'b', to: 'b' },
|
||||
{ id: 'a', to: 'å' }, // override
|
||||
]),[
|
||||
{ id: 'b', to: 'b' },
|
||||
{ id: 'a', to: 'å' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('allUsedKeyIdsInLayers()', () => {
|
||||
it('should handle a null case', () => {
|
||||
assert.sameDeepMembers(Array.from(allUsedKeyIdsInLayers(null).values()),
|
||||
[]);
|
||||
});
|
||||
it('should handle a real case', () => {
|
||||
assert.sameDeepMembers(Array.from(allUsedKeyIdsInLayers([
|
||||
{
|
||||
layer: [
|
||||
{
|
||||
row: [
|
||||
{ keys: 'q w e r t y' },
|
||||
{ keys: 'a s d f' },
|
||||
]
|
||||
},
|
||||
{
|
||||
row: [
|
||||
{ keys: 'Q W E R T Y' },
|
||||
{ keys: 'A S D F' },
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
layer: [
|
||||
{
|
||||
row: [
|
||||
{ keys: 'q w e r t y' },
|
||||
{ keys: '0 1 2 3' }
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
]).values()),
|
||||
'q w e r t y Q W E R T Y a s d f A S D F 0 1 2 3'.split(' '));
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue