diff --git a/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts b/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts index 0787ada9bc..568d186ae6 100644 --- a/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts +++ b/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts @@ -71,6 +71,8 @@ export interface LKKeys { export interface LKKey { id?: string; to?: string; + gap?: boolean; + switch?: string; }; export interface LKLayers { diff --git a/developer/src/kmc-keyboard/src/compiler/keys.ts b/developer/src/kmc-keyboard/src/compiler/keys.ts index 733e98049c..4e54bbf8a6 100644 --- a/developer/src/kmc-keyboard/src/compiler/keys.ts +++ b/developer/src/kmc-keyboard/src/compiler/keys.ts @@ -38,6 +38,11 @@ export class KeysCompiler extends SectionCompiler { valid = false; continue; } + if (!keydef.to && !keydef.gap && !keydef.switch) { + this.callbacks.reportMessage(CompilerMessages.Error_KeyMissingToGapOrSwitch({keyId: key})); + valid = false; + continue; + } } } diff --git a/developer/src/kmc-keyboard/src/compiler/messages.ts b/developer/src/kmc-keyboard/src/compiler/messages.ts index 7f6c37f57e..0a6801f9d2 100644 --- a/developer/src/kmc-keyboard/src/compiler/messages.ts +++ b/developer/src/kmc-keyboard/src/compiler/messages.ts @@ -77,6 +77,10 @@ export class CompilerMessages { m(this.ERROR_DisplayIsRepeated, `display to='${o.to}' has more than one display entry.`); static ERROR_DisplayIsRepeated = SevError | 0x0010; + static Error_KeyMissingToGapOrSwitch = (o:{keyId: string}) => + m(this.ERROR_KeyMissingToGapOrSwitch, `key id='${o.keyId}' must have either to=, gap=, or switch=.`); +static ERROR_KeyMissingToGapOrSwitch = SevError | 0x0011; + static severityName(code: number): string { let severity = code & CompilerErrorSeverity.Severity_Mask; switch(severity) { diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/gap-switch.xml b/developer/src/kmc-keyboard/test/fixtures/sections/keys/gap-switch.xml new file mode 100644 index 0000000000..7701b7ca36 --- /dev/null +++ b/developer/src/kmc-keyboard/test/fixtures/sections/keys/gap-switch.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-key-missing-attrs.xml b/developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-key-missing-attrs.xml new file mode 100644 index 0000000000..21c798d8f9 --- /dev/null +++ b/developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-key-missing-attrs.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/developer/src/kmc-keyboard/test/test-keys.ts b/developer/src/kmc-keyboard/test/test-keys.ts index cdc2fbfaac..d435a3d628 100644 --- a/developer/src/kmc-keyboard/test/test-keys.ts +++ b/developer/src/kmc-keyboard/test/test-keys.ts @@ -55,4 +55,17 @@ describe('keys', function () { assert.deepEqual(compilerTestCallbacks.messages[0], CompilerMessages.Error_KeyNotFoundInKeyBag({col: 1, form: 'hardware', keyId: 'foo', layer: 'base', row: 1})); }); + it('should reject layouts with invalid keys', function() { + let keys = loadSectionFixture(KeysCompiler, 'sections/keys/invalid-key-missing-attrs.xml', compilerTestCallbacks) as Keys; + assert.isNull(keys); + assert.equal(compilerTestCallbacks.messages.length, 1); + + assert.deepEqual(compilerTestCallbacks.messages[0], CompilerMessages.Error_KeyMissingToGapOrSwitch({keyId: 'Q'})); + }); + it('should accept layouts with gap/switch keys', function() { + let keys = loadSectionFixture(KeysCompiler, 'sections/keys/gap-switch.xml', compilerTestCallbacks) as Keys; + assert.isNotNull(keys); + assert.equal(compilerTestCallbacks.messages.length, 0); + assert.equal(keys.keys.length, 2); + }); });