Merge pull request #11424 from keymanapp/chore/web/partial-config-cleanup

chore(web): clean up remaining low-level Web .tsconfig settings 🔩
This commit is contained in:
Joshua Horton 2024-06-03 15:16:00 +07:00 • committed by GitHub
commit 604d2fcb11
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 356 additions and 115 deletions

View file

@ -178,7 +178,7 @@ export class GestureMatcher<Type, StateToken = any> implements PredecessorMatch<
return this._isCancelled;
}
private finalize(matched: boolean, cause: FulfillmentCause) {
private finalize(matched: boolean, cause: FulfillmentCause): MatchResult<Type> {
if(this.publishedPromise.isFulfilled) {
return this._result;
}
@ -242,6 +242,13 @@ export class GestureMatcher<Type, StateToken = any> implements PredecessorMatch<
/* c8 ignore next 3 */
} catch(err) {
this.publishedPromise.reject(err);
return {
matched: false,
action: {
type: 'none',
item: null
}
}
}
}

View file

@ -211,7 +211,7 @@ export class MatcherSelector<Type, StateToken = any> extends EventEmitter<EventM
*/
const sourceNotYetStaged = source instanceof GestureSource;
const determinePredecessorSources = (source: PredecessorMatch<Type, StateToken>) => {
const determinePredecessorSources = (source: PredecessorMatch<Type, StateToken>): GestureSource<Type>[] => {
const directSources = (source.sources as GestureSourceSubview<Type>[]).map((source => source.baseSource));
if(directSources && directSources.length > 0) {

View file

@ -1,3 +1,4 @@
import { FulfillmentCause } from "../matchers/pathMatcher.js";
import { GestureResolutionSpec, RejectionReplace } from "./gestureModel.js";
import { GestureModelDefs } from "./gestureModelDefs.js";
@ -99,7 +100,7 @@ export function validateModelDefs(definitions: GestureModelDefs<any, any>): Mode
processAction(entry.resolutionAction, `model: ${entry.id}`);
Object.keys(entry.rejectionActions ?? {}).forEach((key) => {
processAction(entry.rejectionActions[key], `model: ${entry.id}`);
processAction(entry.rejectionActions[key as Exclude<FulfillmentCause, 'cancelled'>], `model: ${entry.id}`);
});
});

View file

@ -115,8 +115,9 @@ export abstract class InputEngineBase<HoveredItemType, StateToken = any> extends
this._activeTouchpoints = this._activeTouchpoints.filter((pt) => point != pt);
for(const key of Object.keys(this.identifierMap)) {
if(this.identifierMap[key] == id) {
delete this.identifierMap[key];
const keyVal = Number.parseInt(key, 10);
if(this.identifierMap[keyVal] == id) {
delete this.identifierMap[keyVal];
}
}
}

View file

@ -323,7 +323,7 @@ export class TouchpointCoordinator<HoveredItemType, StateToken=any> extends Even
}
public get historyJSON() {
const sanitizingReplacer = function (key: string, value) {
const sanitizingReplacer = function (key: string, value: any) {
if(key == 'item') {
// KMW 'key' elements involve circular refs.
// Just return the key ID. (Assumes use in KMW)

View file

@ -76,8 +76,9 @@ export class TouchEventEngine<ItemType, StateToken = any> extends InputEventEngi
super.dropTouchpoint(source);
for(const key of Object.keys(this.safeBoundMaskMap)) {
if(this.getTouchpointWithId(Number.parseInt(key, 10)) == source) {
delete this.safeBoundMaskMap[key];
const keyVal = Number.parseInt(key, 10);
if(this.getTouchpointWithId(keyVal) == source) {
delete this.safeBoundMaskMap[keyVal];
}
}
}
@ -226,6 +227,8 @@ export class TouchEventEngine<ItemType, StateToken = any> extends InputEventEngi
this.inputStartSignalMap.set(touchpoint, eventSignalPromise);
return eventSignalPromise.corePromise;
} else {
return Promise.resolve();
}
});
}

View file

@ -9,5 +9,7 @@
// Not needed when testing via Node, and when `true` it seems to desync preset breakpoints
// worse than when `false`.
"importHelpers": false,
// Makes the tests... somewhat tricky to handle properly.
"noImplicitAny": false
}
}

View file

@ -1,4 +1,4 @@
import { GestureDebugSource } from "@keymanapp/gesture-recognizer";
import { GestureDebugSource, GestureSource } from "@keymanapp/gesture-recognizer";
import { HostFixtureLayoutController } from "./hostFixtureLayoutController.js";
import { RecordedCoordSequenceSet } from "./inputRecording.js";
@ -32,9 +32,9 @@ export class SequenceRecorder {
}
private _attachRecognizerHooks() {
this.controller.recognizer.on('inputstart', (wrappedSequence: WrappedInputSequence) => {
this.controller.recognizer.on('inputstart', (wrappedSequence: GestureSource<any>) => {
const id = wrappedSequence.identifier;
this.records[id] = wrappedSequence;
this.records[id] = wrappedSequence as WrappedInputSequence;
this.startOrder.push(id);
});

View file

@ -5,7 +5,16 @@
"lib": [ "dom", "es6" ],
"outDir": "./build/obj/",
"rootDir": "./src/engine",
"tsBuildInfoFile": "./build/obj/tsconfig.tsbuildinfo"
"tsBuildInfoFile": "./build/obj/tsconfig.tsbuildinfo",
// TODO: These override ../tsconfig.base.json settings, and so should be removed if possible,
// but existing code in web/ breaks some of these settings
//
// At present, the code actually compiles without them... but the TS tests are a different matter.
"noImplicitAny": false,
"noUnusedLocals": false,
"strictFunctionTypes": false,
"noImplicitReturns": false
},
"include": ["./src/engine/**/*.ts"],
"exclude": ["./src/test/**/*.ts", "./src/tools/**/*.ts"],

View file

@ -167,6 +167,12 @@ export default class LanguageProcessor extends EventEmitter<LanguageProcessorEve
} else if(outputTarget) {
let transcription = outputTarget.buildTranscriptionFrom(outputTarget, null, false);
return this.predict_internal(transcription, true, layerId);
} else {
// if there's no active context source, there's nothing to
// provide suggestions for. In that case, there's no reason
// to even request suggestions, so bypass the prediction
// engine and say that there aren't any.
return Promise.resolve([]);
}
}
@ -290,7 +296,7 @@ export default class LanguageProcessor extends EventEmitter<LanguageProcessorEve
let original = this.getPredictionState(-reversion.transformId);
if(!original) {
console.warn("Could not apply the Suggestion!");
return;
return Promise.resolve([]);
}
// Apply the Reversion!

View file

@ -80,6 +80,8 @@ export default class PredictionContext extends EventEmitter<PredictionContextEve
this.suggestionApplier = (suggestion) => {
if(validSuggestionState()) {
return langProcessor.applySuggestion(suggestion, this.currentTarget, () => kbdProcessor.layerId);
} else {
return null;
}
}
@ -131,7 +133,7 @@ export default class PredictionContext extends EventEmitter<PredictionContextEve
}
public get currentSuggestions(): Suggestion[] {
let suggestions = [];
let suggestions: Suggestion[] = [];
// Insert 'current text' if/when valid as the leading option.
// Since we don't yet do auto-corrections, we only show 'keep' whenever it's
// a valid word (according to the model).
@ -243,7 +245,7 @@ export default class PredictionContext extends EventEmitter<PredictionContextEve
* Should return 'false' if the current state allows reverting a recently-applied suggestion and act accordingly.
* Otherwise, return true.
*/
private doTryRevert = (/*returnObj: {shouldSwallow: boolean}*/): boolean => {
private doTryRevert = (/*returnObj: {shouldSwallow: boolean}*/): void => {
// Has the revert keystroke (BKSP) already been sent once since the last accept?
if(this.doRevert) {
// If so, clear the 'revert' option and start doing normal predictions again.

View file

@ -297,9 +297,9 @@ export class ActiveKeyBase {
const value = KeyTypesOfKeyMap[key as keyof typeof KeyTypesOfKeyMap];
switch(value) {
case 'subkeys':
const arr = rawKey[key] as LayoutSubKey[];
const arr = rawKey[key as 'sk' | 'multitap'] as LayoutSubKey[];
if(!Array.isArray(arr)) {
delete rawKey[key];
delete rawKey[key as 'sk' | 'multitap'];
} else {
for(let i=0; i < arr.length; i++) {
const sk = arr[i];
@ -312,9 +312,9 @@ export class ActiveKeyBase {
}
break;
case 'flicks':
const flickObj = rawKey[key];
const flickObj = rawKey[key as 'flick'];
if(typeof flickObj != 'object') {
delete rawKey[key];
delete rawKey[key as 'flick'];
} else {
for(const flickKey of KeyTypesOfFlickList) {
const sk = flickObj[flickKey];
@ -327,9 +327,9 @@ export class ActiveKeyBase {
}
break;
default:
const prop = rawKey[key];
const prop = rawKey[key as keyof (LayoutKey | LayoutSubKey)];
if(typeof prop != value) {
delete rawKey[key];
delete rawKey[key as keyof (LayoutKey | LayoutSubKey)];
}
}
}
@ -395,7 +395,7 @@ export class ActiveKeyBase {
if(defaultHint?.includes('flick-')) {
if(spec.flick) {
// 6 = length of 'flick-'
const dir = defaultHint.substring(6);
const dir = defaultHint.substring(6) as keyof TouchLayoutFlick;
if(spec.flick[dir]?.text) {
spec.hintSrc = spec.flick[dir];

View file

@ -19,7 +19,26 @@ export { ButtonClasses };
import Codes from "../text/codes.js";
import type Keyboard from "./keyboard.js";
export type KLS = {[layerName: string]: string[]};
export interface EncodedVisualKeyboard {
/** Represents CSS font styling to use for VisualKeyboard text */
F: string;
/** Should there be a 102nd key? */
K102?: boolean,
/**
* Keyboard Layer Specification: an object-based map of layer name to the keycaps for its
* 65 keys. The 65 keys are ordered from left to right, then top to bottom.
*
* The key ID corresponding to each index of the array is specified within `Codes.dfltCodes`.
* Entries corresponding to `K_*` in `Codes.dfltCodes` are reserved for future use.
*/
KLS?: {[layerName: string]: string[]},
/**
* @deprecated
* The older form for data in KLS - defines keycaps for 'default' keys, then 'shift' keys,
* in a single concatenated array.
*/
BK?: string[];
}
// The following types provide type definitions for the full JSON format we use for visual keyboard definitions.
export type ButtonClass = 0 | 1 | 2 | 3 | 4 | /*5 | 6 | 7 |*/ 8 | 9 | 10;
@ -30,9 +49,9 @@ export interface LayoutLayer extends LayoutLayerBase {
capsKey?: LayoutKey,
numKey?: LayoutKey,
scrollKey?: LayoutKey,
aligned?: boolean
aligned?: boolean,
nextlayer?: string
};
export interface LayoutFormFactor extends LayoutFormFactorBase {
// To facilitate those post-processing elements.
layer: LayoutLayer[]
@ -96,9 +115,9 @@ export class Layouts {
* @param {string} formFactor (really utils.FormFactor)
* @return {LayoutFormFactor}
*/
static buildDefaultLayout(PVK, keyboard: Keyboard, formFactor: string): LayoutFormFactor {
static buildDefaultLayout(PVK: EncodedVisualKeyboard, keyboard: Keyboard, formFactor: string): LayoutFormFactor {
// Build a layout using the default for the device
var layoutType=formFactor;
let layoutType = formFactor as keyof TouchLayout.TouchLayoutFile;
if(typeof Layouts.dfltLayout[layoutType] != 'object') {
layoutType = 'desktop';
@ -119,7 +138,7 @@ export class Layouts {
// Clone the default layout object for this device
var layout: LayoutFormFactor = deepCopy(Layouts.dfltLayout[layoutType]);
var n,layers=layout['layer'], keyLabels: KLS=PVK['KLS'], key102=PVK['K102'];
var n,layers=layout['layer'], keyLabels: EncodedVisualKeyboard['KLS'] = PVK['KLS'], key102=PVK['K102'];
var i, j, k, rows: LayoutRow[], key: LayoutKey, keys: LayoutKey[];
var chiral: boolean = (kbdBitmask & Codes.modifierBitmasks.IS_CHIRAL) != 0;
@ -142,7 +161,7 @@ export class Layouts {
// *** Step 1: instantiate the layer objects. ***
// Get the list of valid layers, enforcing that the 'default' layer must be the first one processed.
var validIdList = Object.getOwnPropertyNames(keyLabels), invalidIdList = [];
var validIdList = Object.getOwnPropertyNames(keyLabels), invalidIdList: string[] = [];
validIdList.splice(validIdList.indexOf('default'), 1);
validIdList = [ 'default' ].concat(validIdList);
@ -200,14 +219,14 @@ export class Layouts {
// Erase the legacy shifted subkey array.
shiftKey['sk'] = [];
for(var layerID in keyLabels) {
for(let layerID in keyLabels) {
if(layerID == 'default' || layerID == 'shift') {
// These two are accessible from the layer without subkeys.
continue;
}
// Create a new subkey for the specified layer so that it will be accessible via OSK.
var specialChar = Layouts.modifierSpecials[layerID];
var specialChar = Layouts.modifierSpecials[(layerID as keyof typeof Layouts.modifierSpecials)];
let subkey: LayoutSubKey = {
id: `K_${specialChar}`,
text: specialChar,
@ -307,12 +326,14 @@ export class Layouts {
layer.numKey=numKey;
layer.scrollKey=scrollKey;
const layerId = layers[n].id as keyof typeof Layouts.modifierSpecials;
// Set modifier key appearance and behaviour for non-desktop devices using the default layout
if(formFactor != 'desktop') {
if(n > 0 && shiftKey != null) {
shiftKey['sp']=ButtonClasses.specialActive;
shiftKey['sk']=null;
shiftKey['text'] = Layouts.modifierSpecials[layers[n].id] ? Layouts.modifierSpecials[layers[n].id] : "*Shift*";
shiftKey['text'] = Layouts.modifierSpecials[layerId] ?? "*Shift*";
}
}
}
@ -482,11 +503,11 @@ export class Layouts {
* @param {Array} BK keyboard object (as loaded)
* @return {Object}
*/
static processLegacyDefinitions(BK: string[]): KLS {
static processLegacyDefinitions(BK: string[]): EncodedVisualKeyboard['KLS'] {
//['default','shift','ctrl','shiftctrl','alt','shiftalt','ctrlalt','shiftctrlalt'];
var idList=Layouts.generateLayerIds(false); // Non-chiral.
var KLS: KLS = {};
var KLS: EncodedVisualKeyboard['KLS'] = {};
// The old default: eight auto-managed layers...
for(var n=0; n<idList.length; n++) {
@ -851,6 +872,6 @@ export class Layouts {
}
]
}
};
} as TouchLayout.TouchLayoutFile;
/* c8 ignore end */
}

View file

@ -1,5 +1,5 @@
import Codes from "../text/codes.js";
import { Layouts, type LayoutFormFactor } from "./defaultLayouts.js";
import { EncodedVisualKeyboard, LayoutSpec, Layouts, type LayoutFormFactor } from "./defaultLayouts.js";
import { ActiveKey, ActiveLayout, ActiveSubKey } from "./activeLayout.js";
import KeyEvent from "../text/keyEvent.js";
import type OutputTarget from "../text/outputTarget.js";
@ -30,6 +30,156 @@ export interface VariableStoreDictionary {
[name: string]: string;
};
export type KeyboardObject = {
/**
* Used internally by Keyman Engine for Web to hold preprocessed stores.
*/
_kmw?: CacheTag;
/**
* group-start: the function triggering processing for the keyboard's
* "Unicode" start group, corresponding to `begin Unicode > use(_____)` in
* Keyman keyboard language.
* @param outputTarget The context to which the keystroke applies
* @param keystroke The full, pre-processed keystroke triggering
* keyboard-rule application.
*/
gs(outputTarget: OutputTarget, keystroke: KeyEvent): boolean;
/**
* group-newcontext: the function triggering processing for the keyboard's
* "NewContext" start group, corresponding to `begin NewContext > use(_____)`
* in Keyman keyboard language.
* @param outputTarget The new context to be used with future keystrokes
* @param keystroke A 'null' `KeyEvent` providing current modifier + state information.
*/
gn?(outputTarget: OutputTarget, keystroke: KeyEvent): boolean;
/**
* group-postkeystroke: the function triggering processing for the keyboard's
* "PostKeystroke" start group, corresponding to `begin PostKeystroke >
* use(_____)` in Keyman keyboard language.
* @param outputTarget The context altered by a recent keystroke. As a
* precondition, all changes due to `gs` / `begin Unicode` should already be
* applied.
* @param keystroke A 'null' `KeyEvent` providing current modifier + state information.
*/
gpk?(outputTarget: OutputTarget, keystroke: KeyEvent): boolean;
/**
* Keyboard ID: the uniquely-identifying name for this keyboard. Includes the standard
* `Keyboard_` prefix. May be 'namespaced' with a prefix corresponding to a package name
* within app/webview.
*/
KI: string;
/**
* Keyboard Name: the human-readable name of the keyboard.
*/
KN: string;
/**
* Encoded data usable to construct a desktop/hardware-oriented on-screen keyboard.
*/
KV: EncodedVisualKeyboard;
/**
* Keyboard Language Code: set within select keyboards.
*
* Currently, it's only used to determine the need for CJK-picker support. Is missing
* in most compiled keyboards.
*/
KLC?: string;
/**
* @deprecated
* Keyboard Language Code: set within select keyboards.
*
* Currently, it's only used to determine the need for CJK-picker support.
* Is (probably) an older name of KLC with the identical purpose. Is missing
* in most compiled keyboards.
*/
LanguageCode?: string;
/**
* Keyboard CSS: provides the definition for custom keyboard style sheets
*/
KCSS?: string;
/**
* Keyboard is RTL: a simple flag noting if the keyboard's script is RTL.
*/
KRTL?: boolean;
/**
* Keyboard Modifier BitMask: a set of bitflags indicating which modifiers
* the keyboard's rules utilize. See also: `Codes.modifierCodes`.
*/
KMBM?: number;
/**
* Keyboard Supplementary plane: set to 1 if the keyboard uses non-BMP Unicode
* characters.
*/
KS?: number;
/**
* Keyman Visual Keyboard Layout: defines the touch-layout definitions used for
* 'phone' and 'tablet' form-factors.
*/
KVKL?: LayoutSpec;
/**
* Keyboard is Mnemonic: set to 1 if the keyboard uses a mnemonic layout.
*/
KM?: number;
/**
* KeyBoard VERsion: the version of this keyboard.
*/
KBVER?: string;
/**
* Keyman VERsion: the version of Keyman Developer used to compile this keyboard.
*/
KVER?: string;
/**
* Keyman Variable Stores: an array of the names of all variable stores used by the
* keyboard.
*/
KVS?: (`s${number}`)[];
/**
* Keyboard Help: HTML help text, as specified by either the &kmw_helptext or &kmw_helpfile system stores.
*
* Reference: https://help.keyman.com/developer/language/reference/kmw_helptext,
* https://help.keyman.com/developer/language/reference/kmw_helpfile
*/
KH?: string;
/**
* Keyboard Virtual Key Dictionary: the Developer-compiled, minified dictionary of virtual-key codes
*/
KVKD?: string;
/**
* Keyboard Display Underlying: set to 1 if the desktop form of the keyboard
* should show the US QWERTY underlying keycaps. These may also appear on
* touch layouts if set and no touch-layout information is available.
*/
KDU?: number;
/**
* Virtual Key Dictionary: the engine pre-processed, unminified dictionary. This is built within
* Keyman Engine for Web at runtime as needed based on the definitions in `KVKD`.
*/
VKDictionary?: Record<string, number>,
/**
* Keyboard Help File: Embedded JS script designed for use with a keyboard's
* HTML help text. Always defined within the file referenced by &kmw_embedjs
* in a keyboard's source, though that file may also contain _other_ script
* definitions as well. (`KHF` must be explicitly defined within that file.)
* @param e Will be provided with the root element (a <div>) of the On-Screen Keyboard.
* @returns
*/
KHF?: (e: any) => string;
/**
* Keyboard Notify Shift: Provided by CJK-picker keyboards to properly
* interface them with Keyman Engine for Web.
* @param {number} _PCommand event code (16,17,18) or 0; 16-18
* correspond to modifier codes when pressed, while 0 corresponds to loss of focus
* @param {Object} _PTarget target element
* @param {number} _PData 1 or 0
* @returns
*/
KNS?: (_PCommand: number, _PTarget: OutputTarget, _PData: number) => void;
} & Record<`s${number}`, string>
/**
* Acts as a wrapper class for Keyman keyboards compiled to JS, providing type information
@ -37,8 +187,8 @@ export interface VariableStoreDictionary {
* wrapped keyboard itself.
*/
export default class Keyboard {
public static DEFAULT_SCRIPT_OBJECT = {
'gs': function(outputTarget, keystroke) { return false; }, // no matching rules; rely on defaultRuleOutput entirely
public static DEFAULT_SCRIPT_OBJECT: KeyboardObject = {
'gs': function(outputTarget: OutputTarget, keystroke: KeyEvent) { return false; }, // no matching rules; rely on defaultRuleOutput entirely
'KI': '', // The currently-existing default keyboard ID; we already have checks that focus against this.
'KN': '',
'KV': Layouts.DEFAULT_RAW_SPEC,
@ -51,7 +201,7 @@ export default class Keyboard {
*
* TODO: Make this private instead. But there are a LOT of references that must be rooted out first.
*/
public readonly scriptObject: any;
public readonly scriptObject: KeyboardObject;
private layoutStates: {[layout: string]: LayoutState};
constructor(keyboardScript: any) {
@ -109,7 +259,7 @@ export default class Keyboard {
*/
get variableStores(): VariableStoreDictionary {
const storeNames = this.scriptObject['KVS'];
let values = {};
let values: VariableStoreDictionary = {};
if(Array.isArray(storeNames)) {
for(let store of storeNames) {
values[store] = this.scriptObject[store];
@ -147,11 +297,11 @@ export default class Keyboard {
}
// May return null if no layouts exist or have been initialized.
private get _layouts(): {[formFactor: string]: LayoutFormFactor} {
private get _layouts(): LayoutSpec {
return this.scriptObject['KVKL']; // This one is compiled by Developer's visual keyboard layout editor.
}
private set _layouts(value) {
private set _layouts(value: LayoutSpec) {
this.scriptObject['KVKL'] = value;
}
@ -488,7 +638,7 @@ export default class Keyboard {
'K_NUMLOCK': Codes.stateBitmasks.NUM_LOCK,
'K_SCROLL': Codes.stateBitmasks.SCROLL_LOCK
};
const bitmask = bitmap[Lkc.kName];
const bitmask = bitmap[Lkc.kName as keyof typeof bitmap];
if(bitmask) {
Lkc.Lstates ^= bitmask;
@ -558,8 +708,8 @@ export default class Keyboard {
* @return {number} key code > 255 on success, or 0 if not found
*/
getVKDictionaryCode(keyName: string) {
const dict = this.scriptObject['VKDictionary'] || {} as KeyboardObject['VKDictionary'];
if(!this.scriptObject['VKDictionary']) {
const a=[];
if(typeof this.scriptObject['KVKD'] == 'string') {
// Build the VK dictionary
// TODO: Move the dictionary build into the compiler -- so compiler generates code such as following.
@ -567,13 +717,13 @@ export default class Keyboard {
// this.KVKD={"K_ABC":256,"K_DEF":257,...};
const s=this.scriptObject['KVKD'].split(' ');
for(var i=0; i<s.length; i++) {
a[s[i].toUpperCase()]=i+256; // We force upper-case since virtual keys should be case-insensitive.
dict[s[i].toUpperCase()]=i+256; // We force upper-case since virtual keys should be case-insensitive.
}
}
this.scriptObject['VKDictionary']=a;
this.scriptObject['VKDictionary']=dict;
}
const res=this.scriptObject['VKDictionary'][keyName.toUpperCase()];
const res=dict[keyName.toUpperCase()];
return res ? res : 0;
}
}

View file

@ -5,8 +5,8 @@ import KeyboardProperties from "./keyboardProperties.js";
type KeyboardStub = KeyboardProperties & { filename: string };
export interface KeyboardLoadErrorBuilder {
scriptError(err?: Error);
missingError(err: Error);
scriptError(err?: Error): void;
missingError(err: Error): void;
}
export class KeyboardScriptError extends Error {

View file

@ -136,6 +136,7 @@ export default class KeyboardProperties implements KeyboardInternalPropertySpec
public constructor(keyboardId: string, languageCode: string);
public constructor(arg1: MetadataObj | string, arg2?: string) {
if(!(typeof arg1 == 'string')) {
// @ts-ignore
if(arg1['KI'] || arg1['KL'] || arg1['KLC'] || arg1['KFont'] || arg1['KOskFont']) {
const other = arg1 as KeyboardInternalPropertySpec;
this.KI = other.KI;

View file

@ -46,7 +46,7 @@ export class DOMKeyboardLoader extends KeyboardLoaderBase {
script.id = id;
}
document.head.appendChild(script);
script.onerror = (err) => {
script.onerror = (err: any) => {
promise.reject(errorBuilder.missingError(err));
}
script.onload = () => {

View file

@ -29,7 +29,7 @@ const Codes = {
// Note: keys_mod_other = 0x10000, used by KMX+ for the
// other modifier flag in layers, > 16 bit so not available here.
// See keys_mod_other in keyman_core_ldml.ts
},
} as {[name: string]: number},
modifierBitmasks: {
"ALL":0x007F,
@ -39,14 +39,14 @@ const Codes = {
"NON_CHIRAL":0x0070, // The default bitmask, for non-chiral keyboards,
// Represents all modifier codes not supported by KMW 1.0 legacy keyboards.
"NON_LEGACY": 0x006F // ALL, but without the SHIFT bit
},
} as {[name: string]: number},
stateBitmasks: {
"ALL":0x3F00,
"CAPS":0x0300,
"NUM_LOCK":0x0C00,
"SCROLL_LOCK":0x3000
},
} as {[name: string]: number},
// Define standard keycode numbers (exposed for use by other modules)
keyCodes: {
@ -79,7 +79,7 @@ const Codes = {
"K_UPPER":50006,"K_LOWER":50007,"K_ALPHA":50008,
"K_SHIFTED":50009,"K_ALTGR":50010,
"K_TABBACK":50011,"K_TABFWD":50012
},
} as {[name: string]: number},
codesUS: [
['0123456789',';=,-./`', '[\\]\''],

View file

@ -48,12 +48,12 @@ type RuleChar = string;
class RuleDeadkey {
/** Discriminant field - 'd' for Deadkey.
*/
['t']: 'd';
t: 'd';
/**
* Value: the deadkey's ID.
*/
['d']: number; // For 'd'eadkey; also reflects the Deadkey class's 'd' property.
d: number; // For 'd'eadkey; also reflects the Deadkey class's 'd' property.
}
class ContextAny {
@ -254,7 +254,7 @@ export default class KeyboardInterface extends KeyboardHarness {
* In web-core, this also activates the keyboard; in other modules, this method
* may be replaced with other implementations.
*/
registerKeyboard(Pk): void {
registerKeyboard(Pk: any): void {
// NOTE: This implementation is web-core specific and is intentionally replaced, whole-sale,
// by DOM-aware code.
let keyboard = new Keyboard(Pk);
@ -400,7 +400,7 @@ export default class KeyboardInterface extends KeyboardHarness {
var subCache = cache;
subCache.valContext = subCache.valContext.slice(0, ln);
for(var i=0; i < subCache.valContext.length; i++) {
if(subCache[i] == '\ufffe') {
if(subCache.valContext[i] == '\ufffe') {
subCache.valContext.splice(0, 1);
subCache.deadContext.splice(0, 1);
}
@ -709,12 +709,14 @@ export default class KeyboardInterface extends KeyboardHarness {
s = this._ExplodeStore(s);
var Lix = -1;
for(var i=0; i < s.length; i++) {
if(typeof(s[i]) == 'string') {
const entry = s[i];
if(typeof(entry) == 'string') {
if(s[i] == ch) {
Lix = i;
break;
}
} else if(s[i]['d'] === ch['d']) {
// @ts-ignore // Needs to test against .t for automatic inference, but it's not actually there.
} else if(entry.d === (ch as RuleDeadkey).d) {
Lix = i;
break;
}
@ -765,21 +767,19 @@ export default class KeyboardInterface extends KeyboardHarness {
if(indexChar !== "") {
if(typeof indexChar == 'string' ) {
this.output(Pdn, outputTarget, indexChar); //I3319
} else if(indexChar['t']) {
var storeEntry = indexChar as StoreNonCharEntry;
switch(storeEntry.t) {
} else if(indexChar.t) {
switch(indexChar.t) {
case 'b': // Beep commands may appear within stores.
this.beep(outputTarget);
break;
case 'd':
this.deadkeyOutput(Pdn, outputTarget, indexChar['d']);
this.deadkeyOutput(Pdn, outputTarget, indexChar.d);
break;
default:
assertNever(storeEntry);
assertNever(indexChar);
}
} else { // For keyboards developed during 10.0's alpha phase - t:'d' was assumed.
this.deadkeyOutput(Pdn, outputTarget, indexChar['d']);
this.deadkeyOutput(Pdn, outputTarget, (indexChar as any).d);
}
}
}
@ -1057,7 +1057,7 @@ export default class KeyboardInterface extends KeyboardHarness {
return this.process(this.activeKeyboard.process.bind(this.activeKeyboard), outputTarget, keystroke, false);
}
private process(callee, outputTarget: OutputTarget, keystroke: KeyEvent, readonly: boolean): RuleBehavior {
private process(callee: (outputTarget: OutputTarget, keystroke: KeyEvent) => boolean, outputTarget: OutputTarget, keystroke: KeyEvent, readonly: boolean): RuleBehavior {
// Clear internal state tracking data from prior keystrokes.
if(!outputTarget) {
throw "No target specified for keyboard output!";
@ -1138,16 +1138,19 @@ export default class KeyboardInterface extends KeyboardHarness {
// Keyboard callbacks
let prototype = this.prototype;
var exportKBCallback = function(miniName: string, longName: string) {
var exportKBCallback = function(miniName: string, longName: keyof KeyboardInterface) {
if(prototype[longName]) {
// @ts-ignore
prototype[miniName] = prototype[longName];
}
}
exportKBCallback('KSF', 'saveFocus');
// @ts-ignore // is defined at a higher level
exportKBCallback('KBR', 'beepReset');
exportKBCallback('KT', 'insertText');
exportKBCallback('KR', 'registerKeyboard');
// @ts-ignore // is defined at a higher level
exportKBCallback('KRS', 'registerStub');
exportKBCallback('KC', 'context');
exportKBCallback('KN', 'nul');

View file

@ -104,7 +104,9 @@ export default class KeyEvent implements KeyEventSpec {
public constructor(keyEventSpec: KeyEventSpec) {
for(let key in keyEventSpec) {
// @ts-ignore
if(keyEventSpec[key] !== undefined) {
// @ts-ignore
this[key] = keyEventSpec[key];
}
}
@ -146,7 +148,8 @@ export default class KeyEvent implements KeyEventSpec {
// We need to determine the mnemonic base character - for example, SHIFT + K_PERIOD needs to map to '>'.
let mappingEvent: KeyEvent = new KeyEvent(this);
for(let key in (this as KeyEvent)) {
mappingEvent[key] = this[key];
// @ts-ignore
mappingEvent[key as keyof KeyEvent] = this[key];
}
// To facilitate storing relevant commands, we should probably reverse-lookup

View file

@ -27,7 +27,7 @@ export type LogMessageHandler = (str: string) => void;
export interface VariableStoreSerializer {
loadStore(keyboardID: string, storeName: string): VariableStore;
saveStore(keyboardID: string, storeName: string, storeMap: VariableStore);
saveStore(keyboardID: string, storeName: string, storeMap: VariableStore): void;
}
export interface ProcessorInitOptions {
@ -273,8 +273,8 @@ export default class KeyboardProcessor extends EventEmitter<EventMap> {
_UpdateVKShift(e: KeyEvent): boolean {
let keyShiftState=0;
const lockNames = ['CAPS', 'NUM_LOCK', 'SCROLL_LOCK'];
const lockKeys = ['K_CAPS', 'K_NUMLOCK', 'K_SCROLL'];
const lockNames = ['CAPS', 'NUM_LOCK', 'SCROLL_LOCK'] as const;
const lockKeys = ['K_CAPS', 'K_NUMLOCK', 'K_SCROLL'] as const;
if(!this.activeKeyboard) {
return true;
@ -322,8 +322,8 @@ export default class KeyboardProcessor extends EventEmitter<EventMap> {
}
private updateStates(): void {
var lockNames = ['CAPS', 'NUM_LOCK', 'SCROLL_LOCK'];
var lockKeys = ['K_CAPS', 'K_NUMLOCK', 'K_SCROLL'];
var lockNames = ['CAPS', 'NUM_LOCK', 'SCROLL_LOCK'] as const;
var lockKeys = ['K_CAPS', 'K_NUMLOCK', 'K_SCROLL'] as const;
for(let i=0; i < lockKeys.length; i++) {
const key = lockKeys[i];
@ -443,7 +443,7 @@ export default class KeyboardProcessor extends EventEmitter<EventMap> {
// Do not change layer unless needed (27/08/2015)
if(id == activeLayer && keyEvent.device.formFactor != DeviceSpec.FormFactor.Desktop) {
return false;
return;
}
var idx=id;

View file

@ -109,4 +109,13 @@ export var LMLayerWorkerSourcemapComment = "${srcMapString}";
// --END:LMLayerWorkerCode
`;
fs.writeFileSync(destFile, wrapper);
fs.writeFileSync(destFile, wrapper);
// Don't let there be multiples; TS may signal typing conflict if it thinks
// these are defined multiple times.
let wrapperDeclarations = `
export const LMLayerWorkerCode: string;
export const LMLayerWorkerSourcemapComment: string;
`;
fs.writeFileSync('build/lib/worker-main.wrapped.d.ts', wrapperDeclarations);

View file

@ -5,10 +5,12 @@
"exports": {
"./worker-main.wrapped.js": {
"es6-bundling": "./build/lib/worker-main.wrapped.js",
"types": "./build/lib/worker-main.wrapped.d.ts",
"default": "./build/lib/worker-main.wrapped.es5.js"
},
"./worker-main.wrapped.min.js": {
"es6-bundling": "./build/lib/worker-main.wrapped.min.js",
"types": "./build/lib/worker-main.wrapped.d.ts",
"default": "./build/lib/worker-main.wrapped.es5.min.js"
}
},

View file

@ -413,7 +413,7 @@ export class ContextTracker extends CircularArray<TrackedContextState> {
There's already an implied whitespace to the left, so we conceptually
merge the new whitespace with that one.
*/
return state;
return;
} else if(isBackspace) {
// Consider backspace entry for this case?
state.replaceTailForBackspace(finalToken, primaryInput.id);

View file

@ -39,9 +39,9 @@ export class SearchNode {
// Internal lazy-cache for .inputSamplingCost, as it's a bit expensive to re-compute.
private _inputCost?: number;
constructor(rootTraversal: LexiconTraversal, toKey?: (USVString) => USVString);
constructor(rootTraversal: LexiconTraversal, toKey?: (arg0: USVString) => USVString);
constructor(node: SearchNode);
constructor(rootTraversal: LexiconTraversal | SearchNode, toKey?: (USVString) => USVString) {
constructor(rootTraversal: LexiconTraversal | SearchNode, toKey?: (arg0: USVString) => USVString) {
toKey = toKey || (x => x);
if(rootTraversal instanceof SearchNode) {
@ -549,7 +549,7 @@ export class SearchSpace {
let inputIndex = nextTier.index;
let deletionEdges = [];
let deletionEdges: SearchNode[] = [];
if(!substitutionsOnly) {
deletionEdges = currentNode.buildDeletionEdges(this.inputSequence[inputIndex-1]);
}

View file

@ -97,7 +97,10 @@ export default class LMLayerWorker {
private _currentModelSource: ModelSourceSpec;
constructor(options = {
constructor(options: {
importScripts: typeof importScripts,
postMessage: typeof postMessage
} = {
importScripts: null,
postMessage: null
}) {
@ -401,9 +404,13 @@ export default class LMLayerWorker {
// Ensures that the worker instance is accessible for loaded model scripts.
// Assists unit-testing.
// @ts-ignore
scope['LMLayerWorker'] = worker;
// @ts-ignore
scope['models'] = models;
// @ts-ignore
scope['correction'] = correction;
// @ts-ignore
scope['wordBreakers'] = wordBreakers;
return worker;

View file

@ -434,18 +434,24 @@ export default class ModelCompositor {
});
let suggestions = suggestionDistribution.splice(0, ModelCompositor.MAX_SUGGESTIONS).map(function(value) {
if(value.sample['p']) {
let sample: Suggestion & {
p?: number,
"lexical-p"?: number,
"correction-p"?: number
} = value.sample;
if(sample['p']) {
// For analysis / debugging
value.sample['lexical-p'] = value.sample['p'];
value.sample['correction-p'] = value.p / value.sample['p'];
sample['lexical-p'] = sample['p'];
sample['correction-p'] = value.p / sample['p'];
// Use of the Trie model always exposed the lexical model's probability for a word to KMW.
// It's useful for debugging right now, so may as well repurpose it as the posterior.
//
// We still condition on 'p' existing so that test cases aren't broken.
value.sample['p'] = value.p;
sample['p'] = value.p;
}
//
return value.sample;
return sample;
});
if(keepOption) {

View file

@ -104,6 +104,8 @@ export abstract class RecordedKeystroke {
return new RecordedPhysicalKeystroke(obj as RecordedPhysicalKeystroke);
} else if(obj.type == "osk") {
return new RecordedSyntheticKeystroke(obj as RecordedSyntheticKeystroke);
} else {
return null;
}
}

View file

@ -32,7 +32,7 @@ export default abstract class Proctor {
abstract beforeAll(): Promise<void>;
// Performs per-test setup
abstract before();
abstract before(): void;
/**
* Allows the proctor to indicate if is capable of executing a suite of tests or not.
@ -43,7 +43,7 @@ export default abstract class Proctor {
/**
* Indicates whether or not this Proctor is capable of running the specified set of tests.
*/
abstract matchesTestSet(testSet: TestSet<any>);
abstract matchesTestSet(testSet: TestSet<any>): boolean;
/**
* Simulates the specified test sequence for use in testing.

View file

@ -8,6 +8,7 @@ import { default as SentryType } from '@sentry/browser';
// a pre-bundled Sentry build artifact.
let Sentry: {
init: typeof SentryType.init;
// @ts-ignore
} = window['Sentry'];
/**
@ -34,7 +35,7 @@ export class KeymanSentryManager {
'kmwuitoggle.js': 'kmwuitoggle.js',
'kmwuitoolbar.js': 'kmwuitoolbar.js'
// Also add entries for the naming system used by Android and iOS - and map them to the EMBEDDED upload, not the std 'native' one.
}
} as Record<string, string>;
static DEFAULT_OPTIONS: Options = {
hostPlatform: "native-web"
@ -104,6 +105,7 @@ export class KeymanSentryManager {
attachEventMetadata(event: any) {
// Ensure that the 'extra' object exists. (May not exist for synthetic/custom Errors.)
event.extra = event.extra || {};
//@ts-ignore
event.extra.keymanState = window['keyman']?.['getDebugInfo']?.();
event.extra.keymanHostPlatform = this.keymanPlatform;
}
@ -215,7 +217,7 @@ export class KeymanSentryManager {
return oldConsoleWarn.apply(console, args);
}
function reduceConsoleArgs(args) {
function reduceConsoleArgs(args: any) {
let errorMsg = args[0];
// Make sure errorMsg is either an error or string.
// It's therefore best to pass in new Error('msg') instead of just 'msg' since
@ -225,7 +227,7 @@ export class KeymanSentryManager {
if (!(errorMsg instanceof Error)) {
// stringify all args as a new Error (which creates a stack trace)
errorMsg = new Error(
args.reduce(function(accumulator, currentValue) {
args.reduce(function(accumulator: any, currentValue: any) {
return accumulator.toString() + ' ' + currentValue.toString();
}, '')
);
@ -259,4 +261,5 @@ export class KeymanSentryManager {
}
// Publish to the window.
// @ts-ignore
window['KeymanSentryManager'] = KeymanSentryManager;

View file

@ -2,22 +2,26 @@
* Function deepCopy
* Scope Private
* @param {Object} p object to copy
* @param {Array=} c0 array member being copied
* @return {Object} clone ('deep copy') of object
* Description Makes an actual copy (not a reference) of an object, copying simple members,
* arrays and member objects but not functions, so use with care!
*/
export default function deepCopy<T>(p:T, c0?): T {
var c = c0 || {};
for (var i in p) {
if(typeof p[i] === 'object' && p[i] != null) {
c[i] = (p[i].constructor === Array ) ? [] : {};
deepCopy(p[i],c[i]);
}
else {
c[i] = p[i];
}
}
export default function deepCopy<T extends ({[key: string | number | symbol]: any} | Array<any>)>(p:T): T {
// typeof undefined == 'undefined', ![] == false, !{} == false
if(typeof p != 'object' || !p) {
return p;
} else {
const clone = Array.isArray(p) ? [] : {};
// For arrays, skips over sparse entries. Not that we use sparse arrays, but still.
const keys = Object.keys(p);
return c;
for(let key of keys) {
// @ts-ignore
if(p[key] !== undefined) {
// @ts-ignore
clone[key] = deepCopy(p[key]);
}
}
return clone as T;
}
}

View file

@ -12,7 +12,7 @@ declare global {
interface StringConstructor {
kmwFromCharCode(cp0: number): string,
_kmwFromCharCode(cp0: number): string,
kmwEnableSupplementaryPlane(bEnable: boolean)
kmwEnableSupplementaryPlane(bEnable: boolean): void
}
interface String {

View file

@ -51,7 +51,7 @@ export default class TimeoutPromise extends ManagedPromise<Boolean> {
* A simplified, but non-cancelable, version of `TimeoutPromise`. Returns a simple,
* Promise that resolves after the specified timeout duration.
*/
export const timedPromise = (time) => {
export const timedPromise = (time: number) => {
const promise = new TimeoutPromise(time);
return promise.corePromise;
}

View file

@ -2,6 +2,12 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"lib": [ "DOM", "ES6"]
"lib": [ "DOM", "ES6"],
// TODO: These override ../tsconfig.base.json settings, and so should be removed if possible,
// but existing code in web/ breaks some of these settings
"noImplicitAny": false,
"strictFunctionTypes": false,
"noImplicitReturns": false,
"strictNullChecks": false,
}
}

View file

@ -10,13 +10,6 @@
"lib": ["es6"],
"target": "es5",
// TODO: These override ../tsconfig.base.json settings, and so should be removed if possible,
// but existing code in web/ breaks some of these settinsg
"noImplicitReturns": false,
"noImplicitAny": false,
"strictFunctionTypes": false,
"strictNullChecks": false,
// Other settings - declaration files, sourcemapping, and other miscellaneous bits.
"allowJs": false,
"declaration": true,