mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-13 12:19:25 +00:00
feat(developer): automatic version detection for U_xxxx_yyyy ids
Currently, automatic Keyman minimum version determination works for all features in Keyman keyboards _except_ `U_xxxx_yyyy` touch key identifiers. This change adds the automatic keyboard versioning functionality to the keymanweb compiler as well, following the pattern that's in kmcmplib. This makes the `store(&version)` line completely optional, and it may be possible to deprecate it in future versions of Keyman Developer.
This commit is contained in:
parent
c537bcd8fa
commit
3497e97b00
12 changed files with 273 additions and 82 deletions
|
|
@ -7,6 +7,25 @@ import * as r from 'restructure';
|
|||
// In memory representations of KMX structures
|
||||
// kmx-builder will transform these to the corresponding COMP_xxxx
|
||||
|
||||
export enum KMX_Version {
|
||||
VERSION_30 = 0x00000300,
|
||||
VERSION_31 = 0x00000301,
|
||||
VERSION_32 = 0x00000302,
|
||||
VERSION_40 = 0x00000400,
|
||||
VERSION_50 = 0x00000500,
|
||||
VERSION_501 = 0x00000501,
|
||||
VERSION_60 = 0x00000600,
|
||||
VERSION_70 = 0x00000700,
|
||||
VERSION_80 = 0x00000800,
|
||||
VERSION_90 = 0x00000900,
|
||||
VERSION_100 = 0x00000A00,
|
||||
VERSION_140 = 0x00000E00,
|
||||
VERSION_150 = 0x00000F00,
|
||||
VERSION_160 = 0x00001000,
|
||||
VERSION_170 = 0x00001100
|
||||
};
|
||||
|
||||
|
||||
export class KEYBOARD {
|
||||
fileVersion?: number; // dwFileVersion (TSS_FILEVERSION)
|
||||
|
||||
|
|
@ -129,22 +148,21 @@ export class KMXFile {
|
|||
// File version identifiers (COMP_KEYBOARD.dwFileVersion)
|
||||
//
|
||||
|
||||
public static readonly VERSION_30 = 0x00000300;
|
||||
public static readonly VERSION_31 = 0x00000301;
|
||||
public static readonly VERSION_32 = 0x00000302;
|
||||
public static readonly VERSION_40 = 0x00000400;
|
||||
public static readonly VERSION_50 = 0x00000500;
|
||||
public static readonly VERSION_501 = 0x00000501;
|
||||
public static readonly VERSION_60 = 0x00000600;
|
||||
public static readonly VERSION_70 = 0x00000700;
|
||||
public static readonly VERSION_80 = 0x00000800;
|
||||
public static readonly VERSION_90 = 0x00000900;
|
||||
public static readonly VERSION_100 = 0x00000A00;
|
||||
public static readonly VERSION_140 = 0x00000E00;
|
||||
public static readonly VERSION_150 = 0x00000F00;
|
||||
|
||||
public static readonly VERSION_160 = 0x00001000;
|
||||
public static readonly VERSION_170 = 0x00001100;
|
||||
public static readonly VERSION_30 = KMX_Version.VERSION_30;
|
||||
public static readonly VERSION_31 = KMX_Version.VERSION_31;
|
||||
public static readonly VERSION_32 = KMX_Version.VERSION_32;
|
||||
public static readonly VERSION_40 = KMX_Version.VERSION_40;
|
||||
public static readonly VERSION_50 = KMX_Version.VERSION_50;
|
||||
public static readonly VERSION_501 = KMX_Version.VERSION_501;
|
||||
public static readonly VERSION_60 = KMX_Version.VERSION_60;
|
||||
public static readonly VERSION_70 = KMX_Version.VERSION_70;
|
||||
public static readonly VERSION_80 = KMX_Version.VERSION_80;
|
||||
public static readonly VERSION_90 = KMX_Version.VERSION_90;
|
||||
public static readonly VERSION_100 = KMX_Version.VERSION_100;
|
||||
public static readonly VERSION_140 = KMX_Version.VERSION_140;
|
||||
public static readonly VERSION_150 = KMX_Version.VERSION_150;
|
||||
public static readonly VERSION_160 = KMX_Version.VERSION_160;
|
||||
public static readonly VERSION_170 = KMX_Version.VERSION_170;
|
||||
|
||||
public static readonly VERSION_MIN = this.VERSION_50;
|
||||
public static readonly VERSION_MAX = this.VERSION_170;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,18 @@ export let FUnreachableKeys: KMX.KEY[];
|
|||
export let FCallFunctions: string[];
|
||||
export let FFix183_LadderLength: number;
|
||||
|
||||
let _minimumKeymanVersion: number;
|
||||
|
||||
export function minimumKeymanVersion() {
|
||||
return _minimumKeymanVersion;
|
||||
}
|
||||
|
||||
export function minimumKeymanVersionToString() {
|
||||
const major = (_minimumKeymanVersion & KMX.KMXFile.VERSION_MASK_MAJOR) >> 8;
|
||||
const minor = _minimumKeymanVersion & KMX.KMXFile.VERSION_MASK_MINOR;
|
||||
return `${major}.${minor}`;
|
||||
}
|
||||
|
||||
export function setupGlobals(
|
||||
_callbacks: CompilerCallbacks,
|
||||
_options: CompilerOptions,
|
||||
|
|
@ -33,20 +45,45 @@ export function setupGlobals(
|
|||
FUnreachableKeys = [];
|
||||
FCallFunctions = [];
|
||||
FFix183_LadderLength = 100; // TODO: option
|
||||
_minimumKeymanVersion = fk.fileVersion;
|
||||
}
|
||||
|
||||
export function IsKeyboardVersion10OrLater(): boolean {
|
||||
function isKeyboardVersionAutomaticallyDetermined() {
|
||||
return (fk.flags & KMX.KMXFile.KF_AUTOMATICVERSION) == KMX.KMXFile.KF_AUTOMATICVERSION;
|
||||
}
|
||||
|
||||
function verifyMinimumRequiredKeymanVersion(version: KMX.KMX_Version) {
|
||||
if(isKeyboardVersionAutomaticallyDetermined()) {
|
||||
_minimumKeymanVersion = Math.max(version, _minimumKeymanVersion);
|
||||
return true;
|
||||
}
|
||||
|
||||
return _minimumKeymanVersion >= version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that minimum supported Keyman version in the keyboard is version 15.0,
|
||||
* and upgrade to that version if possible and necessary.
|
||||
*
|
||||
* Will upgrade the minimum version to 15.0 if `KF_AUTOMATICVERSION` flag is set
|
||||
* for the keyboard, which correlates to having no `store(&version)` line in the
|
||||
* .kmn source file.
|
||||
*
|
||||
* @returns `true` if the version is now 15.0 or higher, `false` if a lower
|
||||
* version has been specified in the source file `store(&version)` line.
|
||||
*/
|
||||
export function verifyMinimumRequiredKeymanVersion15(): boolean {
|
||||
return verifyMinimumRequiredKeymanVersion(KMX.KMX_Version.VERSION_150);
|
||||
}
|
||||
|
||||
export function isKeyboardVersion10OrLater(): boolean {
|
||||
return fk.fileVersion >= KMX.KMXFile.VERSION_100;
|
||||
}
|
||||
|
||||
export function IsKeyboardVersion14OrLater(): boolean {
|
||||
export function isKeyboardVersion14OrLater(): boolean {
|
||||
return fk.fileVersion >= KMX.KMXFile.VERSION_140;
|
||||
}
|
||||
|
||||
export function IsKeyboardVersion15OrLater(): boolean {
|
||||
return fk.fileVersion >= KMX.KMXFile.VERSION_150;
|
||||
}
|
||||
|
||||
export function IsKeyboardVersion17OrLater(): boolean {
|
||||
export function isKeyboardVersion17OrLater(): boolean {
|
||||
return fk.fileVersion >= KMX.KMXFile.VERSION_170;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import { TSentinelRecord, GetSuppChar, ExpandSentinel, incxstr, xstrlen, xstrlen_printing } from "./util.js";
|
||||
import { KMX } from "@keymanapp/common-types";
|
||||
|
||||
import { callbacks, FCallFunctions, FFix183_LadderLength, FMnemonic, FTabStop, FUnreachableKeys, IsKeyboardVersion10OrLater, IsKeyboardVersion14OrLater, kmxResult, nl, options } from "./compiler-globals.js";
|
||||
import { callbacks, FCallFunctions, FFix183_LadderLength, FMnemonic, FTabStop, FUnreachableKeys,
|
||||
kmxResult, nl, options, isKeyboardVersion10OrLater, isKeyboardVersion14OrLater } from "./compiler-globals.js";
|
||||
import { KmwCompilerMessages } from "./kmw-compiler-messages.js";
|
||||
import { FormatModifierAsBitflags, RuleIsExcludedByPlatform } from "./kmw-compiler.js";
|
||||
import { KMXCodeNames, SValidIdentifierCharSet, UnreachableKeyCodes, USEnglishShift, USEnglishUnshift, USEnglishValues } from "./constants.js";
|
||||
import { KMXCodeNames, SValidIdentifierCharSet, UnreachableKeyCodes, USEnglishShift,
|
||||
USEnglishUnshift, USEnglishValues } from "./constants.js";
|
||||
import { KMWVKeyNames, TKeymanWebTouchStandardKey, VKeyNames } from "./keymanweb-key-codes.js";
|
||||
|
||||
export function JavaScript_Name(i: number, pwszName: string, KeepNameForPersistentStorage: boolean = false): string { // I3659
|
||||
|
|
@ -49,7 +51,7 @@ export function JavaScript_Store(fk: KMX.KEYBOARD, line: number, pwsz: string):
|
|||
let n = pwsz.indexOf(wcsentinel);
|
||||
|
||||
// Start: plain text store. Always use for < 10.0, conditionally for >= 10.0.
|
||||
if(n < 0 || !IsKeyboardVersion10OrLater()) {
|
||||
if(n < 0 || !isKeyboardVersion10OrLater()) {
|
||||
result = '"';
|
||||
while(pwsz.length) {
|
||||
if(pwsz.charCodeAt(0) == KMX.KMXFile.UC_SENTINEL) {
|
||||
|
|
@ -247,20 +249,31 @@ export function JavaScript_Shift(fkp: KMX.KEY, FMnemonic: boolean): number {
|
|||
}
|
||||
|
||||
if (fkp.ShiftFlags & KMX.KMXFile.ISVIRTUALKEY) {
|
||||
if(IsKeyboardVersion10OrLater()) {
|
||||
if(isKeyboardVersion10OrLater()) {
|
||||
// don't attempt to upgrade to v10 at this point, only if we are already v10+
|
||||
// Full chiral modifier and state key support starts with KeymanWeb 10.0
|
||||
return fkp.ShiftFlags;
|
||||
}
|
||||
|
||||
// Non-chiral support only and no support for state keys
|
||||
if (fkp.ShiftFlags & (KMX.KMXFile.LCTRLFLAG | KMX.KMXFile.RCTRLFLAG | KMX.KMXFile.LALTFLAG | KMX.KMXFile.RALTFLAG)) { // I4118
|
||||
// TODO: automatic version upgrade
|
||||
// if(verifyMinimumRequiredKeymanVersion10()) {
|
||||
// // upgrade to v10 if possible
|
||||
// return fkp.ShiftFlags;
|
||||
// }
|
||||
callbacks.reportMessage(KmwCompilerMessages.Warn_ExtendedShiftFlagsNotSupportedInKeymanWeb({line:fkp.Line, flags: 'LALT, RALT, LCTRL, RCTRL'}));
|
||||
}
|
||||
|
||||
if (fkp.ShiftFlags & (
|
||||
KMX.KMXFile.CAPITALFLAG | KMX.KMXFile.NOTCAPITALFLAG | KMX.KMXFile.NUMLOCKFLAG | KMX.KMXFile.NOTNUMLOCKFLAG |
|
||||
KMX.KMXFile.SCROLLFLAG | KMX.KMXFile.NOTSCROLLFLAG)) { // I4118
|
||||
callbacks.reportMessage(KmwCompilerMessages.Warn_ExtendedShiftFlagsNotSupportedInKeymanWeb({line:fkp.Line, flags: 'CAPS and NCAPS'}));
|
||||
// TODO: automatic version upgrade
|
||||
// if(verifyMinimumRequiredKeymanVersion10()) {
|
||||
// // upgrade to v10 if possible
|
||||
// return fkp.ShiftFlags;
|
||||
// }
|
||||
callbacks.reportMessage(KmwCompilerMessages.Warn_ExtendedShiftFlagsNotSupportedInKeymanWeb({line:fkp.Line, flags: 'CAPS and NCAPS'}));
|
||||
}
|
||||
|
||||
return KMX.KMXFile.ISVIRTUALKEY | (fkp.ShiftFlags & (KMX.KMXFile.K_SHIFTFLAG | KMX.KMXFile.K_CTRLFLAG | KMX.KMXFile.K_ALTFLAG));
|
||||
|
|
@ -403,7 +416,7 @@ export function JavaScript_KeyAsString(fkp: KMX.KEY, FMnemonic: boolean): string
|
|||
}
|
||||
|
||||
export function JavaScript_ContextMatch(fk: KMX.KEYBOARD, fkp: KMX.KEY, context: string): string {
|
||||
if(IsKeyboardVersion10OrLater()) {
|
||||
if(isKeyboardVersion10OrLater()) {
|
||||
return JavaScript_FullContextValue(fk, fkp, context);
|
||||
}
|
||||
else {
|
||||
|
|
@ -429,7 +442,7 @@ function CheckStoreForInvalidFunctions(fk: KMX.KEYBOARD, key: KMX.KEY, store: KM
|
|||
n = store.dpString.indexOf(wcsentinel);
|
||||
|
||||
// Disable the check with versions >= 10.0, since we now support deadkeys in stores.
|
||||
if (n >= 0 && !IsKeyboardVersion10OrLater) {
|
||||
if (n >= 0 && !isKeyboardVersion10OrLater()) {
|
||||
rec = ExpandSentinel(fk, store.dpString, n);
|
||||
callbacks.reportMessage(KmwCompilerMessages.Error_NotSupportedInKeymanWebStore({code:GetCodeName(rec.Code), store:store.dpName}));
|
||||
}
|
||||
|
|
@ -630,7 +643,7 @@ export function JavaScript_OutputString(fk: KMX.KEYBOARD, FTabStops: string, fkp
|
|||
for(let I = 1; I < Index; I++) {
|
||||
let recContext = ExpandSentinel(fk, pwszContext, x);
|
||||
|
||||
if(IsKeyboardVersion10OrLater()) {
|
||||
if(isKeyboardVersion10OrLater()) {
|
||||
if(recContext.IsSentinel && [KMX.KMXFile.CODE_NUL, KMX.KMXFile.CODE_IFOPT, KMX.KMXFile.CODE_IFSYSTEMSTORE].includes(recContext.Code)) {
|
||||
Result--;
|
||||
}
|
||||
|
|
@ -666,7 +679,8 @@ export function JavaScript_OutputString(fk: KMX.KEYBOARD, FTabStops: string, fkp
|
|||
case KMX.KMXFile.CODE_NOTANY:
|
||||
// #917: Minimum version required is 14.0: the KCXO function was only added for 14.0
|
||||
// Note that this is checked in compiler.cpp as well, so this error can probably never occur
|
||||
if(!IsKeyboardVersion14OrLater()) {
|
||||
// TODO: automatic version upgrade
|
||||
if(!isKeyboardVersion14OrLater()) {
|
||||
callbacks.reportMessage(KmwCompilerMessages.Error_NotAnyRequiresVersion14({line:fkp.Line}));
|
||||
}
|
||||
Result += nlt + `k.KCXO(${len},t,${AdjustIndex(fkp.dpContext, xstrlen(fkp.dpContext))},${AdjustIndex(fkp.dpContext, ContextIndex)+1});`;
|
||||
|
|
@ -701,7 +715,7 @@ export function JavaScript_OutputString(fk: KMX.KEYBOARD, FTabStops: string, fkp
|
|||
let pwsz = pwszOutput;
|
||||
|
||||
if(fkp != null) {
|
||||
if(IsKeyboardVersion10OrLater()) {
|
||||
if(isKeyboardVersion10OrLater()) {
|
||||
// KMW >= 10.0 use the full, sentinel-based length for context deletions.
|
||||
len = xstrlen(fkp.dpContext);
|
||||
let n = len;
|
||||
|
|
@ -726,7 +740,7 @@ export function JavaScript_OutputString(fk: KMX.KEYBOARD, FTabStops: string, fkp
|
|||
}
|
||||
|
||||
let x = 0;
|
||||
if(IsKeyboardVersion10OrLater() && pwsz.length > 0) {
|
||||
if(isKeyboardVersion10OrLater() && pwsz.length > 0) {
|
||||
if(!isGroupReadOnly(fk, fgp)) {
|
||||
Result += nlt+`k.KDC(${len},t);`; // I3681
|
||||
}
|
||||
|
|
@ -932,7 +946,8 @@ export function zeroPadHex(n: number, len: number): string {
|
|||
* @return string of JavaScript code, e.g. 'keyCodes.K_A /* 0x41 * /'
|
||||
*/
|
||||
function FormatKeyAsString(key: number): string {
|
||||
if(IsKeyboardVersion10OrLater()) {
|
||||
// TODO: automatic version upgrade
|
||||
if(isKeyboardVersion10OrLater()) {
|
||||
// Depends on flags defined in KeymanWeb 10.0
|
||||
if (key <= 255 && KMWVKeyNames[key] != '') {
|
||||
return 'keyCodes.'+KMWVKeyNames[key]+ ' /* 0x' + zeroPadHex(key, 2) + ' */';
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerEvent, CompilerM
|
|||
import { kmnfile } from "./compiler-globals.js";
|
||||
|
||||
const Namespace = CompilerErrorNamespace.KmwCompiler;
|
||||
// const SevInfo = CompilerErrorSeverity.Info | Namespace;
|
||||
const SevInfo = CompilerErrorSeverity.Info | Namespace;
|
||||
const SevHint = CompilerErrorSeverity.Hint | Namespace;
|
||||
// const SevWarn = CompilerErrorSeverity.Warn | Namespace;
|
||||
const SevError = CompilerErrorSeverity.Error | Namespace;
|
||||
|
|
@ -49,4 +49,8 @@ export class KmwCompilerMessages extends KmnCompilerMessages {
|
|||
static HINT_TouchLayoutUsesUnsupportedGesturesDownlevel = SevHint | 0x0005;
|
||||
static Hint_TouchLayoutUsesUnsupportedGesturesDownlevel = (o:{keyId:string}) => m(this.HINT_TouchLayoutUsesUnsupportedGesturesDownlevel,
|
||||
`The touch layout uses a flick or multi-tap gesture on key ${def(o.keyId)}, which is only available on version 17.0+ of Keyman`);
|
||||
|
||||
static INFO_MinimumEngineVersion = SevInfo | 0x0006;
|
||||
static Info_MinimumEngineVersion = (o:{version:string}) => m(this.INFO_MinimumEngineVersion,
|
||||
`The compiler has assigned a minimum web engine version of ${o.version} based on features used in this keyboard`);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { KMX, CompilerOptions, CompilerCallbacks, KvkFileReader, VisualKeyboard, KmxFileReader, KvkFile } from "@keymanapp/common-types";
|
||||
import { ExpandSentinel, incxstr, xstrlen } from "./util.js";
|
||||
import { options, nl, FTabStop, setupGlobals, IsKeyboardVersion10OrLater, callbacks, FFix183_LadderLength, FCallFunctions, fk, IsKeyboardVersion17OrLater } from "./compiler-globals.js";
|
||||
import { options, nl, FTabStop, setupGlobals, callbacks, FFix183_LadderLength, FCallFunctions, fk, minimumKeymanVersionToString, isKeyboardVersion10OrLater, isKeyboardVersion17OrLater } from "./compiler-globals.js";
|
||||
import { JavaScript_ContextMatch, JavaScript_KeyAsString, JavaScript_Name, JavaScript_OutputString, JavaScript_Rules, JavaScript_Shift, JavaScript_ShiftAsString, JavaScript_Store, zeroPadHex } from './javascript-strings.js';
|
||||
import { KmwCompilerMessages } from "./kmw-compiler-messages.js";
|
||||
import { ValidateLayoutFile } from "./validate-layout-file.js";
|
||||
|
|
@ -206,8 +206,13 @@ export function WriteCompiledKeyboard(
|
|||
// Following line caches the Keyman major version
|
||||
`${FTabStop}this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;${nl}` +
|
||||
`${FTabStop}this.KI="${sName}";${nl}` +
|
||||
`${FTabStop}this.KN="${RequotedString(sFullName)}";${nl}` +
|
||||
`${FTabStop}this.KMINVER="${(keyboard.fileVersion & KMX.KMXFile.VERSION_MASK_MAJOR) >> 8}.${keyboard.fileVersion & KMX.KMXFile.VERSION_MASK_MINOR}";${nl}` +
|
||||
`${FTabStop}this.KN="${RequotedString(sFullName)}";${nl}`;
|
||||
|
||||
// We split the result text here to allow for setting the minimum required
|
||||
// version at the very end of this function
|
||||
const resultPrefix = result;
|
||||
|
||||
result =
|
||||
`${FTabStop}this.KV=${sVisualKeyboard};${nl}` +
|
||||
`${FTabStop}this.KDU=${fDisplayUnderlying?'1':'0'};${nl}` +
|
||||
`${FTabStop}this.KH=${sHelp};${nl}` +
|
||||
|
|
@ -400,7 +405,17 @@ export function WriteCompiledKeyboard(
|
|||
}
|
||||
|
||||
result += sEmbedJS + '}' + nl; // I3681
|
||||
return result;
|
||||
|
||||
const minVer = minimumKeymanVersionToString();
|
||||
const resultMinVer = `${FTabStop}this.KMINVER="${minVer}";${nl}`;
|
||||
|
||||
if((fk.flags & KMX.KMXFile.KF_AUTOMATICVERSION) == KMX.KMXFile.KF_AUTOMATICVERSION) {
|
||||
// Note: the KeymanWeb compiler is responsible for reporting minimum
|
||||
// version for the web targets
|
||||
callbacks.reportMessage(KmwCompilerMessages.Info_MinimumEngineVersion({version:minVer}));
|
||||
}
|
||||
|
||||
return resultPrefix + resultMinVer + result;
|
||||
}
|
||||
|
||||
///
|
||||
|
|
@ -440,9 +455,9 @@ function GetKeyboardModifierBitmask(keyboard: KMX.KEYBOARD, fMnemonic: boolean):
|
|||
/// @return string of JavaScript code
|
||||
///
|
||||
function JavaScript_SetupDebug() {
|
||||
if(IsKeyboardVersion10OrLater()) {
|
||||
if(isKeyboardVersion10OrLater()) {
|
||||
if(options.saveDebug) {
|
||||
if(IsKeyboardVersion17OrLater()) {
|
||||
if(isKeyboardVersion17OrLater()) {
|
||||
return 'var modCodes = KeymanWeb.Codes.modifierCodes;'+nl+
|
||||
FTabStop+'var keyCodes = KeymanWeb.Codes.keyCodes;'+nl;
|
||||
} else {
|
||||
|
|
@ -455,7 +470,7 @@ function JavaScript_SetupDebug() {
|
|||
}
|
||||
|
||||
function JavaScript_SetupProlog() {
|
||||
if(IsKeyboardVersion10OrLater()) {
|
||||
if(isKeyboardVersion10OrLater()) {
|
||||
return 'if(typeof keyman === \'undefined\') {'+nl+
|
||||
FTabStop+'console.log(\'Keyboard requires KeymanWeb 10.0 or later\');'+nl+
|
||||
FTabStop+'if(typeof tavultesoft !== \'undefined\') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");'+nl+
|
||||
|
|
@ -465,7 +480,7 @@ function JavaScript_SetupProlog() {
|
|||
}
|
||||
|
||||
function JavaScript_SetupEpilog() {
|
||||
if(IsKeyboardVersion10OrLater()) {
|
||||
if(isKeyboardVersion10OrLater()) {
|
||||
return '}';
|
||||
}
|
||||
return '';
|
||||
|
|
@ -560,7 +575,7 @@ export function FormatModifierAsBitflags(FBitMask: number): string {
|
|||
//TODO: We need to think about mnemonic layouts which are incompletely supported at present
|
||||
//tavultesoft.keymanweb.osk.
|
||||
|
||||
if(IsKeyboardVersion10OrLater()) {
|
||||
if(isKeyboardVersion10OrLater()) {
|
||||
// This depends on flags defined in KeymanWeb 10.0
|
||||
result = '';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import { KMX, TouchLayout, TouchLayoutFileReader, TouchLayoutFileWriter } from "@keymanapp/common-types";
|
||||
import { callbacks, fk, IsKeyboardVersion14OrLater, IsKeyboardVersion15OrLater, IsKeyboardVersion17OrLater } from "./compiler-globals.js";
|
||||
import { callbacks, minimumKeymanVersion, verifyMinimumRequiredKeymanVersion15,
|
||||
isKeyboardVersion14OrLater, isKeyboardVersion17OrLater } from "./compiler-globals.js";
|
||||
import { JavaScript_Key } from "./javascript-strings.js";
|
||||
import { TRequiredKey, CRequiredKeys, CSpecialText, CSpecialText14Map, CSpecialText17Map, CSpecialTextMinVer, CSpecialTextMaxVer } from "./constants.js";
|
||||
import { TRequiredKey, CRequiredKeys, CSpecialText, CSpecialText14Map, CSpecialText17Map,
|
||||
CSpecialTextMinVer, CSpecialTextMaxVer } from "./constants.js";
|
||||
import { KeymanWebTouchStandardKeyNames, KMWAdditionalKeyNames, VKeyNames } from "./keymanweb-key-codes.js";
|
||||
import { KmwCompilerMessages } from "./kmw-compiler-messages.js";
|
||||
import * as Osk from '../compiler/osk.js';
|
||||
|
|
@ -121,7 +123,7 @@ function CheckKey(
|
|||
callbacks.reportMessage(KmwCompilerMessages.Error_TouchLayoutInvalidIdentifier({keyId: FId, platformName: platformId, layerId: layer.id}));
|
||||
return false;
|
||||
}
|
||||
else if (FValid == TKeyIdType.Key_Unicode_Multi && !IsKeyboardVersion15OrLater()) {
|
||||
else if (FValid == TKeyIdType.Key_Unicode_Multi && !verifyMinimumRequiredKeymanVersion15()) {
|
||||
callbacks.reportMessage(KmwCompilerMessages.Error_TouchLayoutIdentifierRequires15({keyId: FId, platformName: platformId, layerId: layer.id}));
|
||||
return false;
|
||||
}
|
||||
|
|
@ -144,10 +146,11 @@ function CheckKey(
|
|||
// Keyman versions before 14 do not support '*special*' labels on non-special keys.
|
||||
// ZWNJ use, however, is safe because it will be transformed in function
|
||||
// TransformSpecialKeys14 to '<|>', which does not require the custom OSK font.
|
||||
const mapVersion = Math.max(Math.min(fk.fileVersion, CSpecialTextMaxVer), CSpecialTextMinVer);
|
||||
const mapVersion = Math.max(Math.min(minimumKeymanVersion(), CSpecialTextMaxVer), CSpecialTextMinVer);
|
||||
const specialText = CSpecialText.get(mapVersion);
|
||||
if(specialText.includes(FText) &&
|
||||
!IsKeyboardVersion14OrLater() &&
|
||||
// TODO: automatic version upgrade
|
||||
!isKeyboardVersion14OrLater() &&
|
||||
!([TouchLayout.TouchLayoutKeySp.special, TouchLayout.TouchLayoutKeySp.specialActive].includes(FKeyType))) {
|
||||
callbacks.reportMessage(KmwCompilerMessages.Warn_TouchLayoutSpecialLabelOnNormalKey({
|
||||
keyId: FId,
|
||||
|
|
@ -187,7 +190,7 @@ function CheckDictionaryKeyValidity(fk: KMX.KEYBOARD, FDictionary: string[]) {
|
|||
function TransformSpecialKeys14(FDebug: boolean, sLayoutFile: string): string {
|
||||
// Rewrite Special key labels that are only supported in Keyman 14+
|
||||
// This code is a little ugly but effective.
|
||||
if(!IsKeyboardVersion14OrLater()) {
|
||||
if(!isKeyboardVersion14OrLater()) {
|
||||
for(let i = 0; i < CSpecialText14Map.length; i++) {
|
||||
// Assumes the JSON output format will not change
|
||||
if(FDebug) {
|
||||
|
|
@ -203,7 +206,7 @@ function TransformSpecialKeys14(FDebug: boolean, sLayoutFile: string): string {
|
|||
function TransformSpecialKeys17(FDebug: boolean, sLayoutFile: string): string {
|
||||
// Rewrite Special key labels that are only supported in Keyman 17+
|
||||
// This code is a little ugly but effective.
|
||||
if(!IsKeyboardVersion17OrLater()) {
|
||||
if(!isKeyboardVersion17OrLater()) {
|
||||
for(let i = 0; i < CSpecialText17Map.length; i++) {
|
||||
// Assumes the JSON output format will not change
|
||||
if(FDebug) {
|
||||
|
|
@ -239,7 +242,8 @@ export function ValidateLayoutFile(fk: KMX.KEYBOARD, FDebug: boolean, sLayoutFil
|
|||
|
||||
let hasWarnedOfGestureUseDownlevel = false;
|
||||
const warnGesturesIfNeeded = function(keyId: string) {
|
||||
if(!hasWarnedOfGestureUseDownlevel && !IsKeyboardVersion17OrLater()) {
|
||||
// TODO: automatic version upgrade
|
||||
if(!hasWarnedOfGestureUseDownlevel && !isKeyboardVersion17OrLater()) {
|
||||
hasWarnedOfGestureUseDownlevel = true;
|
||||
callbacks.reportMessage(KmwCompilerMessages.Hint_TouchLayoutUsesUnsupportedGesturesDownlevel({keyId}));
|
||||
}
|
||||
|
|
|
|||
53
developer/src/kmc-kmn/test/fixtures/kmw/version_u_xxxx_yyyy.keyman-touch-layout
vendored
Normal file
53
developer/src/kmc-kmn/test/fixtures/kmw/version_u_xxxx_yyyy.keyman-touch-layout
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"tablet": {
|
||||
"font": "Tahoma",
|
||||
"layer": [
|
||||
{
|
||||
"id": "default",
|
||||
"row": [
|
||||
{
|
||||
"id": 1,
|
||||
"key": [
|
||||
{
|
||||
"id": "K_Q",
|
||||
"text": "q"
|
||||
},
|
||||
{
|
||||
"id": "U_0041_0300",
|
||||
"text": "A'"
|
||||
},
|
||||
{
|
||||
"id": "U_0042_0300",
|
||||
"text": "B'"
|
||||
},
|
||||
{
|
||||
"id": "K_BKSP",
|
||||
"text": "*BkSp*",
|
||||
"width": 90,
|
||||
"sp": 1
|
||||
},
|
||||
{
|
||||
"id": "K_LOPT",
|
||||
"text": "*Menu*",
|
||||
"width": 120,
|
||||
"sp": 1
|
||||
},
|
||||
{
|
||||
"id": "K_SPACE",
|
||||
"text": "",
|
||||
"width": 630,
|
||||
"sp": 0
|
||||
},
|
||||
{
|
||||
"id": "K_ENTER",
|
||||
"text": "*Enter*",
|
||||
"width": 140,
|
||||
"sp": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
7
developer/src/kmc-kmn/test/fixtures/kmw/version_u_xxxx_yyyy.kmn
vendored
Normal file
7
developer/src/kmc-kmn/test/fixtures/kmw/version_u_xxxx_yyyy.kmn
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
store(&NAME) 'version_u_xxxx_yyyy'
|
||||
store(&TARGETS) 'mobile'
|
||||
store(&LAYOUTFILE) 'version_u_xxxx_yyyy.keyman-touch-layout'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
8
developer/src/kmc-kmn/test/fixtures/kmw/version_u_xxxx_yyyy_14.kmn
vendored
Normal file
8
developer/src/kmc-kmn/test/fixtures/kmw/version_u_xxxx_yyyy_14.kmn
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
store(&NAME) 'version_u_xxxx_yyyy'
|
||||
store(&TARGETS) 'mobile'
|
||||
store(&LAYOUTFILE) 'version_u_xxxx_yyyy.keyman-touch-layout'
|
||||
store(&VERSION) '14.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
|
@ -6,8 +6,10 @@ import { fileURLToPath } from 'url';
|
|||
import fs from 'fs';
|
||||
import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers';
|
||||
import { KmnCompilerResult, KmnCompiler } from '../../src/compiler/compiler.js';
|
||||
import { ETLResult, extractTouchLayout as parseWebTestResult } from './util.js';
|
||||
import { ETLResult, extractTouchLayout } from './util.js';
|
||||
import { KeymanFileTypes } from '@keymanapp/common-types';
|
||||
import { KmnCompilerMessages } from '../../src/compiler/kmn-compiler-messages.js';
|
||||
import { KmwCompilerMessages } from '../../src/kmw-compiler/kmw-compiler-messages.js';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url)).replace(/\\/g, '/');
|
||||
const fixturesDir = __dirname + '/../../../test/fixtures/kmw/';
|
||||
|
|
@ -38,50 +40,74 @@ describe('KeymanWeb Compiler', function() {
|
|||
callbacks.clear();
|
||||
});
|
||||
|
||||
it('should compile a complex keyboard', function() {
|
||||
run_test_keyboard(kmnCompiler, 'khmer_angkor');
|
||||
it('should compile a complex keyboard', async function() {
|
||||
await run_test_keyboard(kmnCompiler, 'khmer_angkor');
|
||||
});
|
||||
|
||||
it('should handle option stores', function() {
|
||||
it('should handle option stores', async function() {
|
||||
//
|
||||
// This is enough to verify that the option store is set appropriately with
|
||||
// KLOAD because the fixture has that code present:
|
||||
//
|
||||
// this.s_foo_6=KeymanWeb.KLOAD(this.KI,"foo","0");
|
||||
//
|
||||
run_test_keyboard(kmnCompiler, 'test_options');
|
||||
await run_test_keyboard(kmnCompiler, 'test_options');
|
||||
});
|
||||
|
||||
it('should translate every "character style" key correctly', function() {
|
||||
it('should translate every "character style" key correctly', async function() {
|
||||
//
|
||||
// This is enough to verify that every character style key is encoded in the
|
||||
// same way as the fixture.
|
||||
//
|
||||
run_test_keyboard(kmnCompiler, 'test_keychars');
|
||||
await run_test_keyboard(kmnCompiler, 'test_keychars');
|
||||
});
|
||||
|
||||
it('should handle readonly groups', function() {
|
||||
run_test_keyboard(kmnCompiler, 'test_readonly_groups');
|
||||
it('should handle readonly groups', async function() {
|
||||
await run_test_keyboard(kmnCompiler, 'test_readonly_groups');
|
||||
});
|
||||
|
||||
it('should handle context(n) in output of rule, v10.0 generation', function() {
|
||||
run_test_keyboard(kmnCompiler, 'test_contextn_in_output');
|
||||
it('should handle context(n) in output of rule, v10.0 generation', async function() {
|
||||
await run_test_keyboard(kmnCompiler, 'test_contextn_in_output');
|
||||
});
|
||||
|
||||
it('should handle context(n) in output of rule, v9.0 generation', function() {
|
||||
run_test_keyboard(kmnCompiler, 'test_contextn_in_output_9');
|
||||
it('should handle context(n) in output of rule, v9.0 generation', async function() {
|
||||
await run_test_keyboard(kmnCompiler, 'test_contextn_in_output_9');
|
||||
});
|
||||
|
||||
it('should handle context(n) in context part of rule, v9.0 generation', function() {
|
||||
run_test_keyboard(kmnCompiler, 'test_context_in_context_9');
|
||||
it('should handle context(n) in context part of rule, v9.0 generation', async function() {
|
||||
await run_test_keyboard(kmnCompiler, 'test_context_in_context_9');
|
||||
});
|
||||
|
||||
it('should handle context(n) in context part of rule, v10.0 generation', function() {
|
||||
run_test_keyboard(kmnCompiler, 'test_context_in_context');
|
||||
it('should handle context(n) in context part of rule, v10.0 generation', async function() {
|
||||
await run_test_keyboard(kmnCompiler, 'test_context_in_context');
|
||||
});
|
||||
|
||||
it('should determine the minimum version correctly with U_xxxx_yyyy touch ids', async function() {
|
||||
const filenames = generateTestFilenames('version_u_xxxx_yyyy');
|
||||
|
||||
let result = await kmnCompiler.run(filenames.source, null);
|
||||
assert.isNotNull(result);
|
||||
assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumEngineVersion));
|
||||
// The min version message from the .kmn compiler is generic 208A INFO_Info;
|
||||
// we expect only 1 of the info messages -- for the .kmx target (not 2)
|
||||
assert.equal(callbacks.messages.filter(item => item.code == KmnCompilerMessages.INFO_Info).length, 1);
|
||||
|
||||
const data = new TextDecoder('utf-8').decode(result.artifacts.js.data);
|
||||
assert.match(data, /KMINVER="15.0"/, `Could not find expected 'KMINVER="15.0"'`);
|
||||
});
|
||||
|
||||
it('should give an error if the minimum version specified in the keyboard does not support U_xxxx_yyyy touch ids', async function() {
|
||||
const filenames = generateTestFilenames('version_u_xxxx_yyyy_14');
|
||||
|
||||
let result = await kmnCompiler.run(filenames.source, null);
|
||||
assert.isNull(result);
|
||||
// The min version message from the .kmn compiler is generic 208A INFO_Info
|
||||
assert.isFalse(callbacks.hasMessage(KmnCompilerMessages.INFO_Info));
|
||||
assert.isFalse(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumEngineVersion));
|
||||
assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.ERROR_TouchLayoutIdentifierRequires15));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
async function run_test_keyboard(kmnCompiler: KmnCompiler, id: string):
|
||||
Promise<{ result: KmnCompilerResult, actualCode: string, actual: ETLResult, expectedCode: string, expected: ETLResult }> {
|
||||
const filenames = generateTestFilenames(id);
|
||||
|
|
@ -96,11 +122,11 @@ async function run_test_keyboard(kmnCompiler: KmnCompiler, id: string):
|
|||
expected: <ETLResult>null,
|
||||
actual: <ETLResult>null,
|
||||
};
|
||||
value.actual = parseWebTestResult(value.actualCode);
|
||||
value.expected = parseWebTestResult(value.expectedCode);
|
||||
value.actual = extractTouchLayout(value.actualCode);
|
||||
value.expected = extractTouchLayout(value.expectedCode);
|
||||
|
||||
if(debug) {
|
||||
// This is mostly to allow us to verify that parseWebTestResult is doing what we want
|
||||
// This is mostly to allow us to verify that extractTouchLayout is doing what we want
|
||||
// fs.writeFileSync(filenames.binary + '.strip.js', value.actual.js);
|
||||
// fs.writeFileSync(filenames.fixture + '.strip.js', value.expected.js);
|
||||
fs.writeFileSync(filenames.binary, value.actualCode);
|
||||
|
|
|
|||
|
|
@ -24,21 +24,21 @@ describe('Keyboard compiler features', function() {
|
|||
|
||||
const versions = [
|
||||
// TODO(lowpri): we should add a test for each version + also test automatic feature detection
|
||||
['16.0', '160', KMX.KMXFile.VERSION_160],
|
||||
['17.0', '170', KMX.KMXFile.VERSION_170],
|
||||
{major: '16.0', vstr: '160', vernum: KMX.KMXFile.VERSION_160},
|
||||
{major: '17.0', vstr: '170', vernum: KMX.KMXFile.VERSION_170},
|
||||
];
|
||||
|
||||
for(const v of versions) {
|
||||
it(`should build a version ${v[0]} keyboard`, async function() {
|
||||
const fixtureName = makePathToFixture('features', `version_${v[1]}.kmn`);
|
||||
it(`should build a version ${v.major} keyboard`, async function() {
|
||||
const fixtureName = makePathToFixture('features', `version_${v.vstr}.kmn`);
|
||||
|
||||
const result = await compiler.run(fixtureName, `version_${v[1]}.kmx`);
|
||||
const result = await compiler.run(fixtureName, `version_${v.vstr}.kmx`);
|
||||
if(result === null) callbacks.printMessages();
|
||||
assert.isNotNull(result);
|
||||
|
||||
const reader = new KmxFileReader();
|
||||
const keyboard = reader.read(result.artifacts.kmx.data);
|
||||
assert.equal(keyboard.fileVersion, v[2]);
|
||||
assert.equal(keyboard.fileVersion, v.vernum);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,8 +12,12 @@ KMX_BOOL kmcmp::CheckKeyboardFinalVersion(PFILE_KEYBOARD fk) {
|
|||
fk->version = VERSION_60; // minimum version that we can be safe with
|
||||
}
|
||||
|
||||
snprintf(buf, 128, "The compiler has assigned a minimum engine version of %d.%d based on features used in this keyboard", (int)((fk->version & 0xFF00) >> 8), (int)(fk->version & 0xFF));
|
||||
AddCompileWarning(buf);
|
||||
if(kmcmp::CompileTarget != CKF_KEYMANWEB) {
|
||||
// Note: the KeymanWeb compiler is responsible for reporting minimum
|
||||
// version for the web targets
|
||||
snprintf(buf, 128, "The compiler has assigned a minimum engine version of %d.%d based on features used in this keyboard", (int)((fk->version & 0xFF00) >> 8), (int)(fk->version & 0xFF));
|
||||
AddCompileWarning(buf);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue