From 7ccef2e5c640152b6217de21ebbc36d665954be9 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Mon, 26 Jun 2023 07:01:24 +0700 Subject: [PATCH] chore(developer): handle layout file errors in kmw-compiler Also removes leftover commented code, and changes an enum to string enum type in order to get constant names for free for an error message. Error_TouchLayoutIdentifierRequires15 was split into its own error message because it was overloaded with CERR_TouchLayoutInvalidIdentifier previously but had a different message. --- .../src/kmc-kmn/src/kmw-compiler/constants.ts | 2 +- .../src/kmc-kmn/src/kmw-compiler/messages.ts | 23 ++++- .../src/kmw-compiler/validate-layout-file.ts | 83 +++++++++++-------- 3 files changed, 70 insertions(+), 38 deletions(-) diff --git a/developer/src/kmc-kmn/src/kmw-compiler/constants.ts b/developer/src/kmc-kmn/src/kmw-compiler/constants.ts index 511ca785a36..55bae9fdb90 100644 --- a/developer/src/kmc-kmn/src/kmw-compiler/constants.ts +++ b/developer/src/kmc-kmn/src/kmw-compiler/constants.ts @@ -1,5 +1,5 @@ export enum TRequiredKey { - K_LOPT, K_BKSP, K_ENTER + K_LOPT='K_LOPT', K_BKSP='K_BKSP', K_ENTER='K_ENTER' }; // I4447 export const diff --git a/developer/src/kmc-kmn/src/kmw-compiler/messages.ts b/developer/src/kmc-kmn/src/kmw-compiler/messages.ts index 806e2751c40..ee53ad139f2 100644 --- a/developer/src/kmc-kmn/src/kmw-compiler/messages.ts +++ b/developer/src/kmc-kmn/src/kmw-compiler/messages.ts @@ -43,10 +43,31 @@ export class KmwCompilerMessages extends KmnCompilerMessages { `File ${o.filename} could not be loaded: ${(o.e??'').toString()}`); static Warn_EmbedJsFileMissing = (o:{filename: string, e:any}) => m(this.WARN_EmbedJsFileMissing, `File ${o.filename} could not be loaded: ${(o.e??'').toString()}`); - + static Warn_TouchLayoutMissingLayer = (o:{keyId:string, platformName:string, layerId:string, nextLayer:string}) => m(this.WARN_TouchLayoutMissingLayer, + `Key "${o.keyId}" on platform "${o.platformName}", layer "${o.layerId}", references a missing layer "${o.nextLayer}"`); + static Warn_TouchLayoutUnidentifiedKey = (o:{layerId:string}) => m(this.WARN_TouchLayoutUnidentifiedKey, + `A key on layer "${o.layerId}" has no identifier.`); + static Error_TouchLayoutInvalidIdentifier = (o:{keyId:string, platformName: string, layerId:string}) => m(this.ERROR_TouchLayoutInvalidIdentifier, + `Key "${o.keyId}" on "${o.platformName}", layer "${o.layerId}" has an invalid identifier.`); + static Warn_TouchLayoutCustomKeyNotDefined = (o:{keyId:string, platformName:string, layerId:string}) => m(this.WARN_TouchLayoutCustomKeyNotDefined, + `Key "${o.keyId}" on platform "${o.platformName}", layer "${o.layerId}", is a custom key but has no corresponding rule in the source.`); + static Warn_TouchLayoutSpecialLabelOnNormalKey = (o:{keyId:string, platformName:string, layerId:string, label:string}) => m(this.WARN_TouchLayoutSpecialLabelOnNormalKey, + `Key "${o.keyId}" on platform "${o.platformName}", layer "${o.layerId}" does not have the key type "Special" or "Special (active)" but has the label "${o.label}". This feature is only supported in Keyman 14 or later`); + static Error_InvalidKeyCode = (o:{keyId: string}) => m(this.ERROR_InvalidKeyCode, + `Invalid key identifier "${o.keyId}"`); + static Error_InvalidTouchLayoutFile = (o:{msg: string}) => m(this.ERROR_InvalidTouchLayoutFile, + `Invalid touch layout file: ${o.msg}`); + static Warn_TouchLayoutFontShouldBeSameForAllPlatforms = () => m(this.WARN_TouchLayoutFontShouldBeSameForAllPlatforms, + `The touch layout font should be the same for all platforms.`); + static Warn_TouchLayoutMissingRequiredKeys = (o:{layerId:string, platformName:string, missingKeys:string}) => m(this.WARN_TouchLayoutMissingRequiredKeys, + `Layer "${o.layerId}" on platform "${o.platformName}" is missing the required key(s) '${o.missingKeys}.`); // Following messages are kmw-compiler only, so use KmwCompiler error namespace static Error_NotAnyRequiresVersion14 = () => m(this.ERROR_NotAnyRequiresVersion14, `Statement notany in context() match requires version 14.0+ of KeymanWeb`); static ERROR_NotAnyRequiresVersion14 = SevError | 0x0001; + + static Error_TouchLayoutIdentifierRequires15 = (o:{keyId:string, platformName:string, layerId:string}) => m(this.ERROR_TouchLayoutIdentifierRequires15, + `Key "${o.keyId}" on "${o.platformName}", layer "${o.layerId}" has a multi-part identifier which requires version 15.0 or newer.`); + static ERROR_TouchLayoutIdentifierRequires15 = SevError | 0x0002; }; diff --git a/developer/src/kmc-kmn/src/kmw-compiler/validate-layout-file.ts b/developer/src/kmc-kmn/src/kmw-compiler/validate-layout-file.ts index 2784a97deff..6b01f307947 100644 --- a/developer/src/kmc-kmn/src/kmw-compiler/validate-layout-file.ts +++ b/developer/src/kmc-kmn/src/kmw-compiler/validate-layout-file.ts @@ -3,6 +3,7 @@ import { callbacks, IsKeyboardVersion14OrLater, IsKeyboardVersion15OrLater } fro import { JavaScript_Key } from "./javascript-strings.js"; import { TRequiredKey, CRequiredKeys, CSpecialText10, CSpecialText14, CSpecialText14ZWNJ, CSpecialText14Map } from "./constants.js"; import { VKeyNames } from "./keymanweb-key-codes.js"; +import { KmwCompilerMessages } from "./messages.js"; interface VLFOutput { @@ -49,9 +50,17 @@ function KeyIdType(FId: string): TKeyIdType { // I4142 } -function CheckKey(FPlatform: TouchLayout.TouchLayoutPlatform, - FId: string, FText: string, FNextLayer: string, FKeyType: TouchLayout.TouchLayoutKeySp, - FRequiredKeys: TRequiredKey[], FDictionary: string[]) { // I4119 +function CheckKey( + platformId: string, + FPlatform: TouchLayout.TouchLayoutPlatform, + layer: TouchLayout.TouchLayoutLayer, + FId: string, + FText: string, + FNextLayer: string, + FKeyType: TouchLayout.TouchLayoutKeySp, + FRequiredKeys: TRequiredKey[], + FDictionary: string[] +) { // I4119 // // Check that each touch layer has K_LOPT, [K_ROPT,] K_BKSP, K_ENTER @@ -70,7 +79,12 @@ function CheckKey(FPlatform: TouchLayout.TouchLayoutPlatform, if(typeof FNextLayer == 'string' && FNextLayer.length > 0) { if(FPlatform.layer.find(l => l.id.toLowerCase() == FNextLayer.toLowerCase()) == undefined) { - // TODO: callbacks.reportMessage() ReportError(0, CWARN_TouchLayoutMissingLayer, 'Key "'+FId+'" on platform "'+FPlatform.Name+'", layer "'+FLayer.Id+'", platform "'+FPlatform.Name+'", references a missing layer "'+FNextLayer+'".'); + callbacks.reportMessage(KmwCompilerMessages.Warn_TouchLayoutMissingLayer({ + keyId: FId, + layerId: layer.id, + nextLayer: FNextLayer, + platformName: platformId + })); } } @@ -80,7 +94,7 @@ function CheckKey(FPlatform: TouchLayout.TouchLayoutPlatform, if(FId.trim() == '') { if(!(FKeyType in [TouchLayout.TouchLayoutKeySp.blank, TouchLayout.TouchLayoutKeySp.spacer]) && FNextLayer == '') { - // TODO: ReportError(0, CWARN_TouchLayoutUnidentifiedKey, 'A key on layer "'+FLayer.Id+'" has no identifier.'); + callbacks.reportMessage(KmwCompilerMessages.Warn_TouchLayoutUnidentifiedKey({layerId: layer.id})); } return; } @@ -88,10 +102,10 @@ function CheckKey(FPlatform: TouchLayout.TouchLayoutPlatform, let FValid = KeyIdType(FId); if(FValid == TKeyIdType.Key_Invalid) { - // TODO: ReportError(0, CERR_TouchLayoutInvalidIdentifier, 'Key "'+FId+'" on "'+FPlatform.Name+'", layer "'+FLayer.Id+'" has an invalid identifier.'); + callbacks.reportMessage(KmwCompilerMessages.Error_TouchLayoutInvalidIdentifier({keyId: FId, platformName: platformId, layerId: layer.id})); } else if (FValid == TKeyIdType.Key_Unicode_Multi && !IsKeyboardVersion15OrLater()) { - // TODO: ReportError(0, CERR_TouchLayoutInvalidIdentifier, 'Key "'+FId+'" on "'+FPlatform.Name+'", layer "'+FLayer.Id+'" has a multi-part identifier which requires version 15.0 or newer.'); + callbacks.reportMessage(KmwCompilerMessages.Error_TouchLayoutIdentifierRequires15({keyId: FId, platformName: platformId, layerId: layer.id})); } // @@ -101,7 +115,7 @@ function CheckKey(FPlatform: TouchLayout.TouchLayoutPlatform, if (FValid == TKeyIdType.Key_Touch && FNextLayer == '' && FKeyType in [TouchLayout.TouchLayoutKeySp.normal, TouchLayout.TouchLayoutKeySp.deadkey]) { // Search for the key in the key dictionary - ignore K_LOPT, K_ROPT... if(FDictionary.indexOf(FId) < 0) { - // TODO: ReportError(0, CWARN_TouchLayoutCustomKeyNotDefined, 'Key "'+FId+'" on layer "'+FLayer.Id+'", platform "'+FPlatform.Name+'", is a custom key but has no corresponding rule in the source.'); + callbacks.reportMessage(KmwCompilerMessages.Warn_TouchLayoutCustomKeyNotDefined({keyId: FId, platformName: platformId, layerId: layer.id})); } } @@ -116,10 +130,12 @@ function CheckKey(FPlatform: TouchLayout.TouchLayoutPlatform, !CSpecialText14ZWNJ.includes(FText) && !IsKeyboardVersion14OrLater() && !(FKeyType in [TouchLayout.TouchLayoutKeySp.special, TouchLayout.TouchLayoutKeySp.specialActive])) { - // TODO: ReportError(0, CWARN_TouchLayoutSpecialLabelOnNormalKey, - // Format('Key "%s" on layout "%s", platform "%s" does not have the key type "Special" or "Special (active)" but has the label "%s". This feature is only supported in Keyman 14 or later', [ - // FId, FLayer.Id, FPlatform.Name, FText - // ])); + callbacks.reportMessage(KmwCompilerMessages.Warn_TouchLayoutSpecialLabelOnNormalKey({ + keyId: FId, + platformName: platformId, + layerId: layer.id, + label: FText + })); } } } @@ -138,7 +154,7 @@ function CheckDictionaryKeyValidity(fk: KMX.KEYBOARD, FDictionary: string[]) { if(fgp.fUsingKeys) { for(let fkp of fgp.keys) { if(JavaScript_Key(fkp, fk.isMnemonic) == i+256) { - // TODO: ReportError(fkp.Line, CERR_InvalidKeyCode, 'Invalid key identifier "'+FDictionary[i]+'"'); + callbacks.reportMessage(KmwCompilerMessages.Error_InvalidKeyCode({keyId: FDictionary[i]})); } } } @@ -164,28 +180,19 @@ function TransformSpecialKeys14(FDebug: boolean, sLayoutFile: string): string { } export function ValidateLayoutFile(fk: KMX.KEYBOARD, FDebug: boolean, sLayoutFile: string, sVKDictionary: string, displayMap: Osk.PuaMap): VLFOutput { // I4060 // I4139 - -/* -var - FPlatform: TTouchLayoutPlatform; - FLayer: TTouchLayoutLayer; - FRow: TTouchLayoutRow; - FKey: TTouchLayoutKey; - FSubKey: TTouchLayoutSubKey; - FRequiredKeys: set of TRequiredKey; - FDictionary: TStringList; - FDirection: TTouchLayoutFlickDirection; - -*/ - let FDictionary: string[] = sVKDictionary.split(/\s+/); CheckDictionaryKeyValidity(fk, FDictionary); // I4142 let reader = new TouchLayoutFileReader(); - let data = reader.read(callbacks.loadFile(sLayoutFile)); - if(!data) { - // TODO: ReportError(0, CERR_InvalidTouchLayoutFile, sMsg); + let data: TouchLayout.TouchLayoutFile; + try { + data = reader.read(callbacks.loadFile(sLayoutFile)); + if(!data) { + throw new Error('Unknown error reading touch layout file'); + } + } catch(e) { + callbacks.reportMessage(KmwCompilerMessages.Error_InvalidTouchLayoutFile({msg: (e??'Unspecified error').toString()})); return {output:null, result: false}; } @@ -200,7 +207,7 @@ var FTouchLayoutFont = platform.font; } else if(platform.font.toLowerCase() != FTouchLayoutFont) { - // TODO: ReportError(0, CWARN_TouchLayoutFontShouldBeSameForAllPlatforms, 'The touch layout font should be the same for all platforms.'); + callbacks.reportMessage(KmwCompilerMessages.Warn_TouchLayoutFontShouldBeSameForAllPlatforms()); // TODO: why support multiple font values if it has to be the same across all platforms?! } @@ -209,30 +216,34 @@ var let FRequiredKeys: TRequiredKey[] = []; for(let row of layer.row) { for(let key of row.key) { - CheckKey(platform, key.id, key.text, key.nextlayer, key.sp, FRequiredKeys, FDictionary); // I4119 + CheckKey(pid, platform, layer, key.id, key.text, key.nextlayer, key.sp, FRequiredKeys, FDictionary); // I4119 if(key.sk) { for(let subkey of key.sk) { - CheckKey(platform, subkey.id, subkey.text, subkey.nextlayer, subkey.sp, FRequiredKeys, FDictionary); + CheckKey(pid, platform, layer, subkey.id, subkey.text, subkey.nextlayer, subkey.sp, FRequiredKeys, FDictionary); } } let direction: keyof TouchLayout.TouchLayoutFlick; if(key.flick) { for(direction in key.flick) { - CheckKey(platform, key.flick[direction].id, key.flick[direction].text, + CheckKey(pid, platform, layer, key.flick[direction].id, key.flick[direction].text, key.flick[direction].nextlayer, key.flick[direction].sp, FRequiredKeys, FDictionary); } } if(key.multitap) { for(let subkey of key.multitap) { - CheckKey(platform, subkey.id, subkey.text, subkey.nextlayer, subkey.sp, FRequiredKeys, FDictionary); + CheckKey(pid, platform, layer, subkey.id, subkey.text, subkey.nextlayer, subkey.sp, FRequiredKeys, FDictionary); } } } } if(FRequiredKeys.length != CRequiredKeys.length) { - // TODO: ReportError(0, CWARN_TouchLayoutMissingRequiredKeys, 'Layer "'+FLayer.Id+'" on platform "'+FPlatform.Name+'" is missing the required key(s) '+RequiredKeysToString(CRequiredKeys-FRequiredKeys)+'.'); + callbacks.reportMessage(KmwCompilerMessages.Warn_TouchLayoutMissingRequiredKeys({ + layerId: layer.id, + platformName: pid, + missingKeys: CRequiredKeys.filter(x => !FRequiredKeys.includes(x)).join(', ') + })); } } }