From 02e1ea59a200bfbd4596afd14208c2dc5bfbef25 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Sun, 5 Nov 2023 06:12:42 +0700 Subject: [PATCH] feat(developer): provide line number for some kmw compiler messages Fixes #9931. Provide additional line and file context for some KeymanWeb compiler messages. This change requires debug data in the intermediate kmx data provided to the kmw compiler, which should have no impact on the final .js if debug=false. This commit only addresses line number data in Error_NotSupportedInKeymanWebContext and Error_NotSupportedInKeymanWebOutput. There are other compiler messages which could benefit from this data: * Warn_OptionStoreNameInvalid * Error_VirtualCharacterKeysNotSupportedInKeymanWeb * Error_VirtualKeysNotValidForMnemonicLayouts * Warn_ExtendedShiftFlagsNotSupportedInKeymanWeb * Hint_UnreachableKeyCode * Error_NotSupportedInKeymanWebStore * Warn_HelpFileMissing * Warn_EmbedJsFileMissing * Error_NotAnyRequiresVersion14 --- .../src/kmc-kmn/src/compiler/compiler.ts | 6 +++ .../src/kmw-compiler/javascript-strings.ts | 8 ++-- .../src/kmw-compiler/kmw-compiler-messages.ts | 45 +++++++++++++++---- ...ror_not_supported_in_keyman_web_output.kmn | 11 +++++ developer/src/kmc-kmn/test/test-messages.ts | 7 +++ 5 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_not_supported_in_keyman_web_output.kmn diff --git a/developer/src/kmc-kmn/src/compiler/compiler.ts b/developer/src/kmc-kmn/src/compiler/compiler.ts index df20b69852..317cfc757c 100644 --- a/developer/src/kmc-kmn/src/compiler/compiler.ts +++ b/developer/src/kmc-kmn/src/compiler/compiler.ts @@ -298,6 +298,12 @@ export class KmnCompiler implements UnicodeSetParser { if(wasm_result.extra.targets & COMPILETARGETS_JS) { wasm_options.target = 1; // CKF_KEYMANWEB TODO use COMPILETARGETS_JS + + // We always want debug data in the intermediate .kmx, so that error + // messages from KMW compiler can give line numbers in .kmn. This + // should have no impact on the final .js if options.debug is false + wasm_options.saveDebug = true; + wasm_result = Module.kmcmp_compile(infile, wasm_options, wasm_interface); if(!wasm_result.result) { return null; diff --git a/developer/src/kmc-kmn/src/kmw-compiler/javascript-strings.ts b/developer/src/kmc-kmn/src/kmw-compiler/javascript-strings.ts index bafbae2794..b7b9817572 100644 --- a/developer/src/kmc-kmn/src/kmw-compiler/javascript-strings.ts +++ b/developer/src/kmc-kmn/src/kmw-compiler/javascript-strings.ts @@ -490,7 +490,7 @@ function JavaScript_CompositeContextValue(fk: KMX.KEYBOARD, fkp: KMX.KEY, pwsz: `this.s${JavaScript_Name(rec.Any.StoreIndex, rec.Any.Store.dpName)})`; break; default: - callbacks.reportMessage(KmwCompilerMessages.Error_NotSupportedInKeymanWebContext({code: GetCodeName(rec.Code)})); + callbacks.reportMessage(KmwCompilerMessages.Error_NotSupportedInKeymanWebContext({line: fkp.Line, code: GetCodeName(rec.Code)})); Result += '/*.*/ 0 '; } } @@ -579,7 +579,7 @@ function JavaScript_FullContextValue(fk: KMX.KEYBOARD, fkp: KMX.KEY, pwsz: strin `o:${rec.Index.Index}}`; // I4611 break; default: - callbacks.reportMessage(KmwCompilerMessages.Error_NotSupportedInKeymanWebContext({code: GetCodeName(rec.Code)})); + callbacks.reportMessage(KmwCompilerMessages.Error_NotSupportedInKeymanWebContext({line: fkp.Line, code: GetCodeName(rec.Code)})); Result += '/*.*/ 0 '; } } @@ -677,7 +677,7 @@ export function JavaScript_OutputString(fk: KMX.KEYBOARD, FTabStops: string, fkp // These have no output for a context emit break; default: - callbacks.reportMessage(KmwCompilerMessages.Error_NotSupportedInKeymanWebContext({code: GetCodeName(recContext.Code)})); + callbacks.reportMessage(KmwCompilerMessages.Error_NotSupportedInKeymanWebContext({line: fkp.Line, code: GetCodeName(recContext.Code)})); Result += nlt + '/*.*/ '; // I4611 } } @@ -885,7 +885,7 @@ export function JavaScript_OutputString(fk: KMX.KEYBOARD, FTabStops: string, fkp len = -1; break; default: - callbacks.reportMessage(KmwCompilerMessages.Error_NotSupportedInKeymanWebOutput({code: GetCodeName(rec.Code)})); + callbacks.reportMessage(KmwCompilerMessages.Error_NotSupportedInKeymanWebOutput({line: fkp.Line, code: GetCodeName(rec.Code)})); Result += ''; } } diff --git a/developer/src/kmc-kmn/src/kmw-compiler/kmw-compiler-messages.ts b/developer/src/kmc-kmn/src/kmw-compiler/kmw-compiler-messages.ts index 0c2e5bbdb6..a952125571 100644 --- a/developer/src/kmc-kmn/src/kmw-compiler/kmw-compiler-messages.ts +++ b/developer/src/kmc-kmn/src/kmw-compiler/kmw-compiler-messages.ts @@ -1,5 +1,6 @@ import { KmnCompilerMessages } from "../compiler/kmn-compiler-messages.js"; -import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m } from "@keymanapp/common-types"; +import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerEvent, CompilerMessageSpec } from "@keymanapp/common-types"; +import { kmnfile } from "./compiler-globals.js"; const Namespace = CompilerErrorNamespace.KmwCompiler; // const SevInfo = CompilerErrorSeverity.Info | Namespace; @@ -8,6 +9,12 @@ const Namespace = CompilerErrorNamespace.KmwCompiler; const SevError = CompilerErrorSeverity.Error | Namespace; // const SevFatal = CompilerErrorSeverity.Fatal | Namespace; +const m = (code: number, message: string, o?: {e?: any, filename?: string, line?: number}) : CompilerEvent => ({ + ...CompilerMessageSpec(code, message, o?.e), + filename: o?.filename ?? kmnfile, + line: o?.line, +}); + export class KmwCompilerMessages extends KmnCompilerMessages { // Note: for legacy reasons, KMWCompilerMessages extends from // KMNCompilerMessages as they share the same error codes. This can be a @@ -19,44 +26,66 @@ export class KmwCompilerMessages extends KmnCompilerMessages { static Error_InvalidBegin = () => m(this.ERROR_InvalidBegin, `A "begin unicode" statement is required to compile a KeymanWeb keyboard`); + static Error_InvalidTouchLayoutFile = (o:{filename:string}) => m(this.ERROR_InvalidTouchLayoutFile, `Touch layout file ${o.filename} is not valid`); + static Warn_DontMixChiralAndNonChiralModifiers = () => m(this.WARN_DontMixChiralAndNonChiralModifiers, `This keyboard contains Ctrl,Alt and LCtrl,LAlt,RCtrl,RAlt sets of modifiers. Use only one or the other set for web target.`); + static Warn_OptionStoreNameInvalid = (o:{name:string}) => m(this.WARN_OptionStoreNameInvalid, `The option store ${o.name} should be named with characters in the range A-Z, a-z, 0-9 and _ only.`); + static Error_VirtualCharacterKeysNotSupportedInKeymanWeb = () => m(this.ERROR_VirtualCharacterKeysNotSupportedInKeymanWeb, `Virtual character keys not currently supported in KeymanWeb`); + static Error_VirtualKeysNotValidForMnemonicLayouts = () => m(this.ERROR_VirtualKeysNotValidForMnemonicLayouts, `Virtual keys are not valid for mnemonic layouts`); + static Warn_ExtendedShiftFlagsNotSupportedInKeymanWeb = (o:{flags:string}) => m(this.WARN_ExtendedShiftFlagsNotSupportedInKeymanWeb, `Extended shift flags ${o.flags} are not supported in KeymanWeb`); + static Hint_UnreachableKeyCode = (o:{key:string}) => m(this.HINT_UnreachableKeyCode, `The rule will never be matched for key ${o.key} because its key code is never fired.`); + static Error_NotSupportedInKeymanWebStore = (o:{code:string,store:string}) => m(this.ERROR_NotSupportedInKeymanWebStore, - `${o.code} is not currently supported in store '${o.store}' when used by any or index`); - static Error_NotSupportedInKeymanWebContext = (o:{code:String}) => m(this.ERROR_NotSupportedInKeymanWebContext, - `Statement ${o.code} is not currently supported in context`); - static Error_NotSupportedInKeymanWebOutput = (o:{code:string}) => m(this.ERROR_NotSupportedInKeymanWebOutput, - `Statement ${o.code} is not currently supported in output`); + `'${o.code}' is not currently supported in store '${o.store}' when used by any or index for web and touch targets`); + + static Error_NotSupportedInKeymanWebContext = (o:{line:number, code:String}) => m(this.ERROR_NotSupportedInKeymanWebContext, + `Statement '${o.code}' is not currently supported in context for web and touch targets`, o); + + static Error_NotSupportedInKeymanWebOutput = (o:{line:number, code:string}) => m(this.ERROR_NotSupportedInKeymanWebOutput, + `Statement '${o.code}' is not currently supported in output for web and touch targets`, o); + static Warn_HelpFileMissing = (o:{filename: string, e:any}) => m(this.WARN_HelpFileMissing, `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 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 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}'.`); diff --git a/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_not_supported_in_keyman_web_output.kmn b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_not_supported_in_keyman_web_output.kmn new file mode 100644 index 0000000000..cd2c34bb64 --- /dev/null +++ b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_not_supported_in_keyman_web_output.kmn @@ -0,0 +1,11 @@ +c Tests ERROR_NotSupportedInKeymanWebOutput for `return` + +store(&NAME) 'ERROR_NotSupportedInKeymanWebOutput' +store(&VERSION) '9.0' +store(&TARGETS) 'web' + +begin unicode > use(main) + +group(main) using keys + ++ 'a' > return diff --git a/developer/src/kmc-kmn/test/test-messages.ts b/developer/src/kmc-kmn/test/test-messages.ts index 0220c2d037..af07abc2ad 100644 --- a/developer/src/kmc-kmn/test/test-messages.ts +++ b/developer/src/kmc-kmn/test/test-messages.ts @@ -80,4 +80,11 @@ describe('CompilerMessages', function () { assert.equal(callbacks.messages[0].message, "A key on layer \"default\" has no identifier."); }); + // ERROR_NotSupportedInKeymanWebOutput + + it('should generate ERROR_NotSupportedInKeymanWebOutput if a rule has `return` in the output', async function() { + await testForMessage(this, ['invalid-keyboards', 'error_not_supported_in_keyman_web_output.kmn'], KmnCompilerMessages.ERROR_NotSupportedInKeymanWebOutput); + assert.equal(callbacks.messages[0].message, "Statement \"return\" is not currently supported in output"); + }); + });