From c09c895333ef5acb5872d92b8413feadf0282e12 Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Tue, 30 Jul 2024 18:08:41 +0200 Subject: [PATCH 1/3] =?UTF-8?q?refactor(web):=20move=20parts=20of=20`keybo?= =?UTF-8?q?ard-processor`=20=E2=86=92=20`js-processor`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move parts of `common/web/keyboard-processor/` → `web/src/engine/js-processor/`. Note this temporarily disable tests for keyboard-processor which depend on js-processor, until #12110 is fixed. Fixes: #12067 --- common/web/keyboard-processor/build.sh | 11 +- common/web/keyboard-processor/src/index.ts | 10 +- .../src/keyboards/keyboard.ts | 74 ++++++++++++- .../src/keyboards/keyboardHarness.ts | 3 + .../keyboard-processor/src/text/keyEvent.ts | 30 +++-- .../src/text/ruleBehavior.interface.ts | 12 ++ .../src/text/systemStores.ts | 36 +++++- .../keyboard-processor/tests/dom/readme.md | 5 - .../tests/dom/web-test-runner.CI.config.mjs | 13 --- .../tests/dom/web-test-runner.config.mjs | 62 ----------- .../keyboard-processor/tests/tsconfig.json | 11 -- .../web/keyboard-processor/tsconfig.all.json | 1 - web/README.md | 4 +- web/build.sh | 2 + web/package.json | 5 + web/src/app/browser/src/beepHandler.ts | 2 +- web/src/app/browser/src/configuration.ts | 3 +- .../app/browser/src/defaultBrowserRules.ts | 2 +- .../app/browser/src/hardwareEventKeyboard.ts | 3 +- web/src/app/webview/src/keymanEngine.ts | 3 +- .../events/src/keyEventSource.interface.ts | 4 +- web/src/engine/interfaces/build.sh | 1 + .../src/prediction/predictionContext.ts | 3 +- web/src/engine/interfaces/tsconfig.json | 1 + web/src/engine/js-processor/build.sh | 41 +++++++ .../engine/js-processor/src}/defaultRules.ts | 29 +++-- web/src/engine/js-processor/src/index.ts | 7 ++ .../engine/js-processor/src}/kbdInterface.ts | 103 +----------------- .../js-processor/src}/keyboardProcessor.ts | 40 +++---- .../engine/js-processor/src}/ruleBehavior.ts | 4 +- web/src/engine/js-processor/tsconfig.json | 13 +++ web/src/engine/main/build.sh | 1 + web/src/engine/main/src/contextManagerBase.ts | 3 +- .../engine/main/src/engineConfiguration.ts | 3 +- web/src/engine/main/src/hardKeyboard.ts | 3 +- .../main/src/headless/inputProcessor.ts | 6 +- web/src/engine/main/src/keyboardInterface.ts | 5 +- web/src/engine/main/src/keymanEngine.ts | 10 +- web/src/engine/main/tsconfig.json | 1 + .../domKeyboardLoader.spec.ts | 3 +- web/src/test/auto/dom/kbdLoader.ts | 2 +- .../prediction/predictionContext.spec.js | 3 +- .../testing/recorder-core/src/nodeProctor.ts | 3 +- .../tools/testing/recorder-core/tsconfig.json | 3 +- 44 files changed, 295 insertions(+), 289 deletions(-) create mode 100644 common/web/keyboard-processor/src/text/ruleBehavior.interface.ts delete mode 100644 common/web/keyboard-processor/tests/dom/readme.md delete mode 100644 common/web/keyboard-processor/tests/dom/web-test-runner.CI.config.mjs delete mode 100644 common/web/keyboard-processor/tests/dom/web-test-runner.config.mjs delete mode 100644 common/web/keyboard-processor/tests/tsconfig.json create mode 100755 web/src/engine/js-processor/build.sh rename {common/web/keyboard-processor/src/text => web/src/engine/js-processor/src}/defaultRules.ts (91%) create mode 100644 web/src/engine/js-processor/src/index.ts rename {common/web/keyboard-processor/src/text => web/src/engine/js-processor/src}/kbdInterface.ts (94%) rename {common/web/keyboard-processor/src/text => web/src/engine/js-processor/src}/keyboardProcessor.ts (96%) rename {common/web/keyboard-processor/src/text => web/src/engine/js-processor/src}/ruleBehavior.ts (96%) create mode 100644 web/src/engine/js-processor/tsconfig.json rename {common/web/keyboard-processor/tests/dom/cases => web/src/test/auto/dom/cases/keyboard-processor}/domKeyboardLoader.spec.ts (95%) diff --git a/common/web/keyboard-processor/build.sh b/common/web/keyboard-processor/build.sh index 3921063bb9..964370c2fb 100755 --- a/common/web/keyboard-processor/build.sh +++ b/common/web/keyboard-processor/build.sh @@ -17,6 +17,7 @@ BUNDLE_CMD="node ${KEYMAN_ROOT}/common/web/es-bundling/build/common-bundle.mjs" builder_describe \ "Compiles the web-oriented utility function module." \ "@/web/src/tools/testing/recorder-core test" \ + "@/web/src/engine/js-processor test" \ "@/common/web/keyman-version" \ "@/common/web/es-bundling" \ "@/common/web/types" \ @@ -64,10 +65,10 @@ function do_build() { --platform node # Tests - builder_echo "Bundle tests" - ${BUNDLE_CMD} "${KEYMAN_ROOT}/common/web/keyboard-processor/build/tests/dom/cases/domKeyboardLoader.spec.js" \ - --out "${KEYMAN_ROOT}/common/web/keyboard-processor/build/tests/dom/domKeyboardLoader.spec.mjs" \ - --format esm + # builder_echo "Bundle tests" + # ${BUNDLE_CMD} "${KEYMAN_ROOT}/common/web/keyboard-processor/build/tests/dom/cases/domKeyboardLoader.spec.js" \ + # --out "${KEYMAN_ROOT}/common/web/keyboard-processor/build/tests/dom/domKeyboardLoader.spec.mjs" \ + # --format esm # Declaration bundling. builder_echo "Declaration bundling" @@ -92,4 +93,4 @@ function do_test() { builder_run_action configure do_configure builder_run_action clean rm -rf ./build builder_run_action build do_build -builder_run_action test do_test +# builder_run_action test do_test diff --git a/common/web/keyboard-processor/src/index.ts b/common/web/keyboard-processor/src/index.ts index f800691cfa..c719d7ee8e 100644 --- a/common/web/keyboard-processor/src/index.ts +++ b/common/web/keyboard-processor/src/index.ts @@ -27,20 +27,14 @@ export { default as StateKeyMap } from "./keyboards/stateKeyMap.js"; export { default as Codes } from "./text/codes.js"; export * from "./text/codes.js"; export * from "./text/deadkeys.js"; -export { default as DefaultRules } from "./text/defaultRules.js"; -export * from "./text/defaultRules.js"; -export { default as KeyboardInterface } from "./text/kbdInterface.js"; -export * from "./text/kbdInterface.js"; -export { default as KeyboardProcessor } from "./text/keyboardProcessor.js"; -export * from "./text/keyboardProcessor.js"; export { default as KeyEvent } from "./text/keyEvent.js"; export * from "./text/keyEvent.js"; export { default as KeyMapping } from "./text/keyMapping.js"; export { default as OutputTarget } from "./text/outputTarget.js"; export * from "./text/outputTarget.js"; -export { default as RuleBehavior } from "./text/ruleBehavior.js"; export * from "./text/stringDivergence.js"; export * from "./text/systemStores.js"; +export * from "./text/ruleBehavior.interface.js"; export * from "@keymanapp/web-utils"; @@ -48,4 +42,4 @@ export * from "@keymanapp/web-utils"; // Without the line below... OutputTarget would likely be aliased there, as it's // the last `export { default as _ }` => `export * from` pairing seen above. -export default undefined; \ No newline at end of file +export default undefined; diff --git a/common/web/keyboard-processor/src/keyboards/keyboard.ts b/common/web/keyboard-processor/src/keyboards/keyboard.ts index 5312d825a6..6ca5f790fd 100644 --- a/common/web/keyboard-processor/src/keyboards/keyboard.ts +++ b/common/web/keyboard-processor/src/keyboards/keyboard.ts @@ -6,11 +6,83 @@ import type OutputTarget from "../text/outputTarget.js"; import { ModifierKeyConstants, TouchLayout } from "@keymanapp/common-types"; type TouchLayoutSpec = TouchLayout.TouchLayoutPlatform & { isDefault?: boolean}; -import type { ComplexKeyboardStore } from "../text/kbdInterface.js"; +import type { ComplexKeyboardStore, KeyboardStore } from "../text/systemStores.js"; import { Version, DeviceSpec } from "@keymanapp/web-utils"; import StateKeyMap from "./stateKeyMap.js"; +export class RuleDeadkey { + /** Discriminant field - 'd' for Deadkey. + */ + t: 'd'; + + /** + * Value: the deadkey's ID. + */ + d: number; // For 'd'eadkey; also reflects the Deadkey class's 'd' property. +} + +export class StoreBeep { + /** Discriminant field - 'b' for `beep` + */ + ['t']: 'b'; +} + +export type RuleChar = string; + +export class ContextAny { + /** Discriminant field - 'a' for `any()`. + */ + ['t']: 'a'; + + /** + * Value: the store to search. + */ + ['a']: KeyboardStore; // For 'a'ny statement. + + /** + * If set to true, negates the 'any'. + */ + ['n']: boolean | 0 | 1; +} + +export class RuleIndex { + /** Discriminant field - 'i' for `index()`. + */ + ['t']: 'i'; + + /** + * Value: the Store from which to output + */ + ['i']: KeyboardStore; + + /** + * Offset: the offset in context for the corresponding `any()`. + */ + ['o']: number; +} + +export class ContextEx { + /** Discriminant field - 'c' for `context()`. + */ + ['t']: 'c'; + + /** + * Value: The offset into the current rule's context to be matched. + */ + ['c']: number; // For 'c'ontext statement. +} + +export class ContextNul { + /** Discriminant field - 'n' for `nul` + */ + ['t']: 'n'; +} + + + +export type StoreNonCharEntry = RuleDeadkey | StoreBeep; + /** * Stores preprocessed properties of a keyboard for quick retrieval later. */ diff --git a/common/web/keyboard-processor/src/keyboards/keyboardHarness.ts b/common/web/keyboard-processor/src/keyboards/keyboardHarness.ts index 671c8fefd7..109d209067 100644 --- a/common/web/keyboard-processor/src/keyboards/keyboardHarness.ts +++ b/common/web/keyboard-processor/src/keyboards/keyboardHarness.ts @@ -1,5 +1,6 @@ import Keyboard from "./keyboard.js"; import Codes from "../text/codes.js"; +import { DeviceSpec } from '@keymanapp/web-utils'; /** * Defines members of the top-level `keyman` global object necessary to guarantee @@ -40,6 +41,8 @@ export const MinimalKeymanGlobal: KeyboardKeymanGlobal = { export class KeyboardHarness { public readonly _jsGlobal: any; public readonly keymanGlobal: KeyboardKeymanGlobal; + activeDevice: DeviceSpec; + /** * Constructs and configures a harness for receiving dynamically-loaded Keyman keyboards. diff --git a/common/web/keyboard-processor/src/text/keyEvent.ts b/common/web/keyboard-processor/src/text/keyEvent.ts index a38992a3a1..7c507cc170 100644 --- a/common/web/keyboard-processor/src/text/keyEvent.ts +++ b/common/web/keyboard-processor/src/text/keyEvent.ts @@ -10,18 +10,30 @@ import type Keyboard from "../keyboards/keyboard.js"; import { type DeviceSpec } from "@keymanapp/web-utils"; import Codes from './codes.js'; -import DefaultRules from './defaultRules.js'; import { ActiveKeyBase } from "../index.js"; +interface DefaultRulesInterface { + forAny(Lkc: KeyEvent, isMnemonic: boolean): string; +} + +export class BASE_DEFAULT_RULES { + private static _instance: DefaultRulesInterface; + + // Prevent direct instantiation. + private constructor() { } + + public static forAny(Lkc: KeyEvent, isMnemonic: boolean): string { + return this._instance.forAny(Lkc, isMnemonic); + } + + public static set instance(value: DefaultRulesInterface) { + this._instance = value; + } +} + // Represents a probability distribution over a keyboard's keys. // Defined here to avoid compilation issues. -export type KeyDistribution = {keySpec: ActiveKeyBase, p: number}[]; - -/** - * A simple instance of the standard 'default rules' for keystroke processing from the - * DefaultRules base class. - */ -const BASE_DEFAULT_RULES = new DefaultRules(); +export type KeyDistribution = { keySpec: ActiveKeyBase, p: number }[]; export interface KeyEventSpec { @@ -188,4 +200,4 @@ export default class KeyEvent implements KeyEventSpec { } } } -}; \ No newline at end of file +}; diff --git a/common/web/keyboard-processor/src/text/ruleBehavior.interface.ts b/common/web/keyboard-processor/src/text/ruleBehavior.interface.ts new file mode 100644 index 0000000000..29f81bcc8c --- /dev/null +++ b/common/web/keyboard-processor/src/text/ruleBehavior.interface.ts @@ -0,0 +1,12 @@ +import { type Transcription } from './outputTarget.js'; + +/** + * Represents the commands and state changes that result from a matched keyboard rule. + */ +export interface RuleBehaviorInterface { + /** + * The before-and-after Transform from matching a keyboard rule. May be `null` + * if no keyboard rules were matched for the keystroke. + */ + transcription: Transcription; +} diff --git a/common/web/keyboard-processor/src/text/systemStores.ts b/common/web/keyboard-processor/src/text/systemStores.ts index 13184baafe..a2f7abff34 100644 --- a/common/web/keyboard-processor/src/text/systemStores.ts +++ b/common/web/keyboard-processor/src/text/systemStores.ts @@ -1,5 +1,31 @@ -import type KeyboardInterface from "./kbdInterface.js"; -import { SystemStoreIDs } from "./kbdInterface.js"; +import { type KeyboardHarness } from "../keyboards/keyboardHarness.js"; +import { StoreNonCharEntry } from '../keyboards/keyboard.js'; + +export enum SystemStoreIDs { + TSS_LAYER = 33, + TSS_PLATFORM = 31, + TSS_NEWLAYER = 42, + TSS_OLDLAYER = 43 +} + +/* +* Type alias definitions to reflect the parameters of the fullContextMatch() callback (KMW 10+). +* No constructors or methods since keyboards will not utilize the same backing prototype, and +* property names are shorthanded to promote minification. +*/ +type PlainKeyboardStore = string; + +export type KeyboardStoreElement = (string | StoreNonCharEntry); +export type ComplexKeyboardStore = KeyboardStoreElement[]; + +export type KeyboardStore = PlainKeyboardStore | ComplexKeyboardStore; + +export type VariableStore = { [name: string]: string }; + +export interface VariableStoreSerializer { + loadStore(keyboardID: string, storeName: string): VariableStore; + saveStore(keyboardID: string, storeName: string, storeMap: VariableStore): void; +} /** * Defines common behaviors associated with system stores. @@ -61,9 +87,9 @@ export class MutableSystemStore extends SystemStore { * Handles checks against the current platform. */ export class PlatformSystemStore extends SystemStore { - private readonly kbdInterface: KeyboardInterface; + private readonly kbdInterface: KeyboardHarness; - constructor(keyboardInterface: KeyboardInterface) { + constructor(keyboardInterface: KeyboardHarness) { super(SystemStoreIDs.TSS_PLATFORM); this.kbdInterface = keyboardInterface; @@ -131,4 +157,4 @@ export class PlatformSystemStore extends SystemStore { // Everything we checked against was valid and had matches - it's a match! return true; } -} \ No newline at end of file +} diff --git a/common/web/keyboard-processor/tests/dom/readme.md b/common/web/keyboard-processor/tests/dom/readme.md deleted file mode 100644 index 153b7f4a1e..0000000000 --- a/common/web/keyboard-processor/tests/dom/readme.md +++ /dev/null @@ -1,5 +0,0 @@ -Automated tests in this subfolder and its children are designed to facilitate simple, browser-independent -unit tests that are DOM-reliant. - -Tests for anything that may reasonably vary depending upon the browser used to run the code should go under -the "integrated" folder instead. \ No newline at end of file diff --git a/common/web/keyboard-processor/tests/dom/web-test-runner.CI.config.mjs b/common/web/keyboard-processor/tests/dom/web-test-runner.CI.config.mjs deleted file mode 100644 index d18a8a9cb0..0000000000 --- a/common/web/keyboard-processor/tests/dom/web-test-runner.CI.config.mjs +++ /dev/null @@ -1,13 +0,0 @@ -// @ts-check -import BASE_CONFIG from './web-test-runner.config.mjs'; -import teamcityReporter from '@keymanapp/common-test-resources/test-runner-TC-reporter.mjs'; -import { sessionStabilityReporter } from '@keymanapp/common-test-resources/test-runner-stability-reporter.mjs'; - -/** @type {import('@web/test-runner').TestRunnerConfig} */ -export default { - ...BASE_CONFIG, - reporters: [ - teamcityReporter(), /* custom-written, for CI-friendly reports */ - sessionStabilityReporter({ciMode: true}) - ] -} \ No newline at end of file diff --git a/common/web/keyboard-processor/tests/dom/web-test-runner.config.mjs b/common/web/keyboard-processor/tests/dom/web-test-runner.config.mjs deleted file mode 100644 index 76651182ac..0000000000 --- a/common/web/keyboard-processor/tests/dom/web-test-runner.config.mjs +++ /dev/null @@ -1,62 +0,0 @@ -// @ts-check -import { devices, playwrightLauncher } from '@web/test-runner-playwright'; -import { esbuildPlugin } from '@web/dev-server-esbuild'; -import { defaultReporter, summaryReporter } from '@web/test-runner'; -import { LauncherWrapper, sessionStabilityReporter } from '@keymanapp/common-test-resources/test-runner-stability-reporter.mjs'; -import { importMapsPlugin } from '@web/dev-server-import-maps'; -import { dirname, resolve } from 'path'; -import { fileURLToPath } from 'url'; - -const dir = dirname(fileURLToPath(import.meta.url)); -const KEYMAN_ROOT = resolve(dir, '../../../../..'); - -/** @type {import('@web/test-runner').TestRunnerConfig} */ -export default { - // debug: true, - browsers: [ - new LauncherWrapper(playwrightLauncher({ product: 'chromium' })), - new LauncherWrapper(playwrightLauncher({ product: 'firefox' })), - new LauncherWrapper(playwrightLauncher({ product: 'webkit', concurrency: 1 })), - ], - concurrency: 10, - nodeResolve: true, - files: [ - 'build/tests/dom/**/*.spec.mjs' - ], - middleware: [ - // Rewrites short-hand paths for test resources, making them fully relative to the repo root. - function rewriteResourcePath(context, next) { - if(context.url.startsWith('/resources/')) { - context.url = '/common/test' + context.url; - } - - return next(); - } - ], - plugins: [ - esbuildPlugin({ts: true, target: 'auto'}), - importMapsPlugin({ - inject: { - importMap: { - // Redirects `eventemitter3` imports to the bundled ESM library. The standard import is an - // ESM wrapper around the CommonJS implementation, and WTR fails when it hits the CommonJS. - imports: { - 'eventemitter3': '/node_modules/eventemitter3/dist/eventemitter3.esm.js' - } - } - } - }) - ], - reporters: [ - summaryReporter({}), /* local-dev mocha-style */ - sessionStabilityReporter({}), - defaultReporter({}) - ], - /* - Un-comment the next two lines for easy interactive debugging; it'll launch the - test page in your preferred browser. - */ - // open: true, - // manual: true, - rootDir: KEYMAN_ROOT -} diff --git a/common/web/keyboard-processor/tests/tsconfig.json b/common/web/keyboard-processor/tests/tsconfig.json deleted file mode 100644 index 0e978545b8..0000000000 --- a/common/web/keyboard-processor/tests/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "baseUrl": "../", - "outDir": "../build/tests/", - "tsBuildInfoFile": "../build/tests/tsconfig.tsbuildinfo", - "rootDir": "./" - }, - "include": [ "./dom/**/*.ts"], - "exclude": [] -} diff --git a/common/web/keyboard-processor/tsconfig.all.json b/common/web/keyboard-processor/tsconfig.all.json index dabb262d9c..79f10644d1 100644 --- a/common/web/keyboard-processor/tsconfig.all.json +++ b/common/web/keyboard-processor/tsconfig.all.json @@ -9,7 +9,6 @@ "references": [ { "path": "./src/keyboards/loaders/tsconfig.dom.json" }, { "path": "./src/keyboards/loaders/tsconfig.node.json" }, - { "path": "./tests/tsconfig.json" }, ], // Actual main-body compilation is in tsconfig.json. This config is just a wrapper // to trigger all three components at once. diff --git a/web/README.md b/web/README.md index 1249805165..775f303df5 100644 --- a/web/README.md +++ b/web/README.md @@ -82,6 +82,7 @@ title: Dependency Graph graph TD; OSK["/web/src/engine/osk"]; KP["@keymanapp/keyboard-processor
(/common/web/keyboard-processor)"]; + JSProc["/web/src/engine/js-processor"]; OSK-->KP; WebUtils["@keymanapp/web-utils
(/common/web/utils)"]; KP---->WebUtils; @@ -107,6 +108,7 @@ graph TD; Fully headless components`"] direction LR KP; + JSProc-->KP; WebUtils; PredText; Gestures; @@ -127,9 +129,9 @@ graph TD; OSK-->Gestures; Interfaces["/web/src/engine/interfaces"]; Interfaces-->KP; + Interfaces-->JSProc; OSK-->Interfaces; CommonEngine["/web/src/engine/main"]; - CommonEngine-->Interfaces; CommonEngine-->Device; CommonEngine-->KeyboardCache; CommonEngine-->OSK; diff --git a/web/build.sh b/web/build.sh index 7248f5c35a..6f4737710a 100755 --- a/web/build.sh +++ b/web/build.sh @@ -27,6 +27,7 @@ builder_describe "Builds engine modules for Keyman Engine for Web (KMW)." \ ":engine/dom-utils A common subset of function used for DOM calculations, layout, etc" \ ":engine/events Specialized classes utilized to support KMW API events" \ ":engine/element-wrappers Subset used to integrate with website elements" \ + ":engine/js-processor Build JS processor for KMW" \ ":engine/main Builds all common code used by KMW's app/-level targets" \ ":engine/osk Builds the Web OSK module" \ ":engine/package-cache Subset used to collate keyboards and request them from the cloud" \ @@ -57,6 +58,7 @@ builder_describe_outputs \ build:engine/dom-utils "/web/build/engine/dom-utils/obj/index.js" \ build:engine/events "/web/build/engine/events/lib/index.mjs" \ build:engine/element-wrappers "/web/build/engine/element-wrappers/lib/index.mjs" \ + build:engine/js-processor "/web/build/engine/js-processor/lib/index.mjs" \ build:engine/main "/web/build/engine/main/lib/index.mjs" \ build:engine/osk "/web/build/engine/osk/lib/index.mjs" \ build:engine/package-cache "/web/build/engine/package-cache/lib/index.mjs" \ diff --git a/web/package.json b/web/package.json index 458ea85de0..4baff29490 100644 --- a/web/package.json +++ b/web/package.json @@ -37,6 +37,11 @@ "types": "./build/engine/events/obj/index.d.ts", "import": "./build/engine/events/obj/index.js" }, + "./engine/js-processor": { + "es6-bundling": "./src/engine/js-processor/src/index.ts", + "types": "./build/engine/js-processor/obj/index.d.ts", + "import": "./build/engine/js-processor/obj/index.js" + }, "./engine/package-cache": { "es6-bundling": "./src/engine/package-cache/src/index.ts", "types": "./build/engine/package-cache/obj/index.d.ts", diff --git a/web/src/app/browser/src/beepHandler.ts b/web/src/app/browser/src/beepHandler.ts index 3659f8d7fd..eb6d6583ee 100644 --- a/web/src/app/browser/src/beepHandler.ts +++ b/web/src/app/browser/src/beepHandler.ts @@ -1,4 +1,4 @@ -import { type KeyboardInterface } from '@keymanapp/keyboard-processor'; +import { type KeyboardInterface } from 'keyman/engine/js-processor'; import { DesignIFrame, OutputTarget } from 'keyman/engine/element-wrappers'; // Utility object used to handle beep (keyboard error response) operations. diff --git a/web/src/app/browser/src/configuration.ts b/web/src/app/browser/src/configuration.ts index 07be8264e7..4f2f268438 100644 --- a/web/src/app/browser/src/configuration.ts +++ b/web/src/app/browser/src/configuration.ts @@ -1,7 +1,8 @@ import { EngineConfiguration, InitOptionSpec, InitOptionDefaults } from "keyman/engine/main"; import { OutputTarget as DOMOutputTarget } from 'keyman/engine/element-wrappers'; -import { isEmptyTransform, OutputTarget, RuleBehavior } from '@keymanapp/keyboard-processor'; +import { isEmptyTransform, OutputTarget } from '@keymanapp/keyboard-processor'; +import { RuleBehavior } from 'keyman/engine/js-processor'; import { AlertHost } from "./utils/alertHost.js"; import { whenDocumentReady } from "./utils/documentReady.js"; diff --git a/web/src/app/browser/src/defaultBrowserRules.ts b/web/src/app/browser/src/defaultBrowserRules.ts index 1ac4801689..fc084ec3f4 100644 --- a/web/src/app/browser/src/defaultBrowserRules.ts +++ b/web/src/app/browser/src/defaultBrowserRules.ts @@ -1,10 +1,10 @@ import { ModifierKeyConstants } from '@keymanapp/common-types'; import { Codes, - DefaultRules, type KeyEvent, type OutputTarget } from '@keymanapp/keyboard-processor'; +import { DefaultRules } from 'keyman/engine/js-processor'; import ContextManager from './contextManager.js'; diff --git a/web/src/app/browser/src/hardwareEventKeyboard.ts b/web/src/app/browser/src/hardwareEventKeyboard.ts index 29e867aa13..f6874fc77b 100644 --- a/web/src/app/browser/src/hardwareEventKeyboard.ts +++ b/web/src/app/browser/src/hardwareEventKeyboard.ts @@ -1,4 +1,5 @@ -import { Codes, DeviceSpec, KeyEvent, KeyMapping, Keyboard, KeyboardProcessor } from '@keymanapp/keyboard-processor'; +import { Codes, DeviceSpec, KeyEvent, KeyMapping, Keyboard } from '@keymanapp/keyboard-processor'; +import { KeyboardProcessor } from 'keyman/engine/js-processor'; import { ModifierKeyConstants } from '@keymanapp/common-types'; import { HardKeyboard, processForMnemonicsAndLegacy } from 'keyman/engine/main'; diff --git a/web/src/app/webview/src/keymanEngine.ts b/web/src/app/webview/src/keymanEngine.ts index ba98f8c477..2f07350548 100644 --- a/web/src/app/webview/src/keymanEngine.ts +++ b/web/src/app/webview/src/keymanEngine.ts @@ -1,4 +1,5 @@ -import { DefaultRules, DeviceSpec, RuleBehavior } from '@keymanapp/keyboard-processor' +import { DeviceSpec } from '@keymanapp/keyboard-processor' +import { DefaultRules, RuleBehavior } from 'keyman/engine/js-processor'; import { KeymanEngine as KeymanEngineBase, KeyboardInterface } from 'keyman/engine/main'; import { AnchoredOSKView, ViewConfiguration, StaticActivator } from 'keyman/engine/osk'; import { getAbsoluteX, getAbsoluteY } from 'keyman/engine/dom-utils'; diff --git a/web/src/engine/events/src/keyEventSource.interface.ts b/web/src/engine/events/src/keyEventSource.interface.ts index d3c39fa49d..22621d2de0 100644 --- a/web/src/engine/events/src/keyEventSource.interface.ts +++ b/web/src/engine/events/src/keyEventSource.interface.ts @@ -1,7 +1,7 @@ import { EventEmitter } from "eventemitter3"; -import { type KeyEvent, type RuleBehavior } from "@keymanapp/keyboard-processor"; +import { type KeyEvent, type RuleBehaviorInterface } from "@keymanapp/keyboard-processor"; -export type KeyEventResultCallback = (result: RuleBehavior, error?: Error) => void; +export type KeyEventResultCallback = (result: RuleBehaviorInterface, error?: Error) => void; export type KeyEventHandler = (event: KeyEvent, callback?: KeyEventResultCallback) => void; interface EventMap { diff --git a/web/src/engine/interfaces/build.sh b/web/src/engine/interfaces/build.sh index ca61e64865..528083d4d8 100755 --- a/web/src/engine/interfaces/build.sh +++ b/web/src/engine/interfaces/build.sh @@ -15,6 +15,7 @@ SUBPROJECT_NAME=engine/interfaces builder_describe "Builds configuration subclasses used by the Keyman Engine for Web (KMW)." \ "@/common/web/es-bundling" \ "@/common/web/keyboard-processor" \ + "@/web/src/engine/js-processor" \ "clean" \ "configure" \ "build" \ diff --git a/web/src/engine/interfaces/src/prediction/predictionContext.ts b/web/src/engine/interfaces/src/prediction/predictionContext.ts index d47bf4b8e2..90427c3020 100644 --- a/web/src/engine/interfaces/src/prediction/predictionContext.ts +++ b/web/src/engine/interfaces/src/prediction/predictionContext.ts @@ -1,6 +1,7 @@ import { EventEmitter } from "eventemitter3"; import { type LanguageProcessorSpec , ReadySuggestions, type InvalidateSourceEnum, StateChangeHandler } from './languageProcessor.interface.js'; -import { type KeyboardProcessor, type OutputTarget } from "@keymanapp/keyboard-processor"; +import { type OutputTarget } from "@keymanapp/keyboard-processor"; +import { type KeyboardProcessor } from 'keyman/engine/js-processor'; interface PredictionContextEventMap { update: (suggestions: Suggestion[]) => void; diff --git a/web/src/engine/interfaces/tsconfig.json b/web/src/engine/interfaces/tsconfig.json index d5a193e021..634e62bada 100644 --- a/web/src/engine/interfaces/tsconfig.json +++ b/web/src/engine/interfaces/tsconfig.json @@ -13,5 +13,6 @@ "references": [ { "path": "../../../../common/web/keyboard-processor" }, + { "path": "../js-processor" } ] } diff --git a/web/src/engine/js-processor/build.sh b/web/src/engine/js-processor/build.sh new file mode 100755 index 0000000000..57adbd1dd2 --- /dev/null +++ b/web/src/engine/js-processor/build.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +## START STANDARD BUILD SCRIPT INCLUDE +# adjust relative paths as necessary +THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" +. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh" +## END STANDARD BUILD SCRIPT INCLUDE + +SUBPROJECT_NAME=engine/js-processor + +. "${KEYMAN_ROOT}/web/common.inc.sh" +. "${KEYMAN_ROOT}/resources/shellHelperFunctions.sh" + +# ################################ Main script ################################ + +builder_describe "Builds configuration subclasses used by the Keyman Engine for Web (KMW)." \ + "clean" \ + "configure" \ + "build" \ + "test" \ + "--ci+ Set to utilize CI-based test configurations & reporting." + +builder_describe_outputs \ + configure "/node_modules" \ + build "/web/build/${SUBPROJECT_NAME}/lib/index.mjs" + +builder_parse "$@" + +#### Build action definitions #### + +do_build () { + compile "${SUBPROJECT_NAME}" + + ${BUNDLE_CMD} "${KEYMAN_ROOT}/web/build/${SUBPROJECT_NAME}/obj/index.js" \ + --out "${KEYMAN_ROOT}/web/build/${SUBPROJECT_NAME}/lib/index.mjs" \ + --format esm +} + +builder_run_action configure verify_npm_setup +builder_run_action clean rm -rf "${KEYMAN_ROOT}/web/build/${SUBPROJECT_NAME}" +builder_run_action build do_build diff --git a/common/web/keyboard-processor/src/text/defaultRules.ts b/web/src/engine/js-processor/src/defaultRules.ts similarity index 91% rename from common/web/keyboard-processor/src/text/defaultRules.ts rename to web/src/engine/js-processor/src/defaultRules.ts index 931eb5200b..edaf440bf9 100644 --- a/common/web/keyboard-processor/src/text/defaultRules.ts +++ b/web/src/engine/js-processor/src/defaultRules.ts @@ -1,18 +1,11 @@ -// TODO: Move to separate folder: 'codes' -// We should start splitting off code needed by keyboards even without a KeyboardProcessor active. -// There's an upcoming `/common/web/types` package that 'codes' and 'keyboards' may fit well within. +/* + * Keyman is copyright (C) SIL International. MIT License. + * + * Implementation of default rules + */ -import { ModifierKeyConstants} from '@keymanapp/common-types'; -import Codes from "./codes.js"; -import type KeyEvent from "./keyEvent.js"; -import type OutputTarget from "./outputTarget.js"; - -// The only members referenced are to produce warning and error logs. A little abstraction -// via an optional 'logger' interface can maintain it while facilitating a the split alluded -// to above. -// -// Alternatively, we could just... not take in the parameter at all, which'd also facilitate -// the future modularization effort. +import { ModifierKeyConstants } from '@keymanapp/common-types'; +import { BASE_DEFAULT_RULES, Codes, type KeyEvent, type OutputTarget } from "@keymanapp/keyboard-processor"; import RuleBehavior from "./ruleBehavior.js"; export enum EmulationKeystrokes { @@ -191,7 +184,7 @@ export default class DefaultRules { // check if exact match to SHIFT's code. Only the 'default' and 'shift' layers should have default key outputs. // TODO: Extend to allow AltGr as well - better mnemonic support. - if(keyShiftState == ModifierKeyConstants.K_SHIFTFLAG) { + if (keyShiftState == ModifierKeyConstants.K_SHIFTFLAG) { keyShiftState = 1; } else if(keyShiftState != 0) { if(ruleBehavior) { @@ -224,3 +217,9 @@ export default class DefaultRules { return null; } } + +/** + * A simple instance of the standard 'default rules' for keystroke processing from the + * DefaultRules base class. + */ +BASE_DEFAULT_RULES.instance = new DefaultRules(); diff --git a/web/src/engine/js-processor/src/index.ts b/web/src/engine/js-processor/src/index.ts new file mode 100644 index 0000000000..1c51de80d9 --- /dev/null +++ b/web/src/engine/js-processor/src/index.ts @@ -0,0 +1,7 @@ +export { default as DefaultRules } from "./defaultRules.js"; +export * from "./defaultRules.js"; +export { default as KeyboardProcessor } from "./keyboardProcessor.js"; +export * from "./keyboardProcessor.js"; +export { default as RuleBehavior } from "./ruleBehavior.js"; +export * from './kbdInterface.js'; +export { default as KeyboardInterface } from "./kbdInterface.js"; diff --git a/common/web/keyboard-processor/src/text/kbdInterface.ts b/web/src/engine/js-processor/src/kbdInterface.ts similarity index 94% rename from common/web/keyboard-processor/src/text/kbdInterface.ts rename to web/src/engine/js-processor/src/kbdInterface.ts index 438d328f0d..2b34eadce9 100644 --- a/common/web/keyboard-processor/src/text/kbdInterface.ts +++ b/web/src/engine/js-processor/src/kbdInterface.ts @@ -7,18 +7,8 @@ import { type DeviceSpec } from "@keymanapp/web-utils"; import { ModifierKeyConstants } from '@keymanapp/common-types'; - -import Codes from "./codes.js"; -import type KeyEvent from "./keyEvent.js"; -import type { Deadkey } from "./deadkeys.js"; -import KeyMapping from "./keyMapping.js"; -import { SystemStore, MutableSystemStore, PlatformSystemStore } from "./systemStores.js"; -import type { VariableStoreSerializer } from "./keyboardProcessor.js"; -import type OutputTarget from "./outputTarget.js"; -import { Mock } from "./outputTarget.js"; +import { Codes, type KeyEvent, type Deadkey, ComplexKeyboardStore, type KeyboardStore, KeyboardStoreElement, KeyMapping, SystemStoreIDs, SystemStore, MutableSystemStore, PlatformSystemStore, type OutputTarget, Mock, Keyboard, VariableStore, VariableStoreDictionary, VariableStoreSerializer, KeyboardHarness, KeyboardKeymanGlobal, RuleDeadkey, ContextAny, RuleIndex, ContextEx, ContextNul, RuleChar } from "@keymanapp/keyboard-processor"; import RuleBehavior from "./ruleBehavior.js"; -import Keyboard, { VariableStoreDictionary } from "../keyboards/keyboard.js"; -import { KeyboardHarness, KeyboardKeymanGlobal } from "../keyboards/keyboardHarness.js"; //#endregion @@ -30,93 +20,9 @@ export class KeyInformation { modifiers: number; } -/* -* Type alias definitions to reflect the parameters of the fullContextMatch() callback (KMW 10+). -* No constructors or methods since keyboards will not utilize the same backing prototype, and -* property names are shorthanded to promote minification. -*/ -type PlainKeyboardStore = string; - -export type KeyboardStoreElement = (string|StoreNonCharEntry); -export type ComplexKeyboardStore = KeyboardStoreElement[]; - -type KeyboardStore = PlainKeyboardStore | ComplexKeyboardStore; - -export type VariableStore = {[name: string]: string}; - -type RuleChar = string; - -class RuleDeadkey { - /** Discriminant field - 'd' for Deadkey. - */ - t: 'd'; - - /** - * Value: the deadkey's ID. - */ - d: number; // For 'd'eadkey; also reflects the Deadkey class's 'd' property. -} - -class ContextAny { - /** Discriminant field - 'a' for `any()`. - */ - ['t']: 'a'; - - /** - * Value: the store to search. - */ - ['a']: KeyboardStore; // For 'a'ny statement. - - /** - * If set to true, negates the 'any'. - */ - ['n']: boolean|0|1; -} - -class RuleIndex { - /** Discriminant field - 'i' for `index()`. - */ - ['t']: 'i'; - - /** - * Value: the Store from which to output - */ - ['i']: KeyboardStore; - - /** - * Offset: the offset in context for the corresponding `any()`. - */ - ['o']: number; -} - -class ContextEx { - /** Discriminant field - 'c' for `context()`. - */ - ['t']: 'c'; - - /** - * Value: The offset into the current rule's context to be matched. - */ - ['c']: number; // For 'c'ontext statement. -} - -class ContextNul { - /** Discriminant field - 'n' for `nul` - */ - ['t']: 'n'; -} - -class StoreBeep { - /** Discriminant field - 'b' for `beep` - */ - ['t']: 'b'; -} - type ContextNonCharEntry = RuleDeadkey | ContextAny | RuleIndex | ContextEx | ContextNul; type ContextEntry = RuleChar | ContextNonCharEntry; -type StoreNonCharEntry = RuleDeadkey | StoreBeep; - /** * Cache of context storing and retrieving return values from KC * Must be reset prior to each keystroke and after any text changes @@ -183,13 +89,6 @@ class CachedContextEx { } }; -export enum SystemStoreIDs { - TSS_LAYER = 33, - TSS_PLATFORM = 31, - TSS_NEWLAYER = 42, - TSS_OLDLAYER = 43 -} - //#endregion export default class KeyboardInterface extends KeyboardHarness { diff --git a/common/web/keyboard-processor/src/text/keyboardProcessor.ts b/web/src/engine/js-processor/src/keyboardProcessor.ts similarity index 96% rename from common/web/keyboard-processor/src/text/keyboardProcessor.ts rename to web/src/engine/js-processor/src/keyboardProcessor.ts index 836c9912a6..1a6d216dce 100644 --- a/common/web/keyboard-processor/src/text/keyboardProcessor.ts +++ b/web/src/engine/js-processor/src/keyboardProcessor.ts @@ -1,23 +1,21 @@ +/* + * Keyman is copyright (C) SIL International. MIT License. + * + * Implementation of the JavaScript keyboard processor + */ + // #region Big ol' list of imports import { EventEmitter } from 'eventemitter3'; - -import Codes from "./codes.js"; -import type Keyboard from "../keyboards/keyboard.js"; -import { MinimalKeymanGlobal } from '../keyboards/keyboardHarness.js'; -import KeyEvent from "./keyEvent.js"; -import { Layouts } from "../keyboards/defaultLayouts.js"; -import type { MutableSystemStore } from "./systemStores.js"; - -import DefaultRules, { EmulationKeystrokes } from "./defaultRules.js"; -import type OutputTarget from "./outputTarget.js"; -import { Mock } from "./outputTarget.js"; - -import KeyboardInterface, { SystemStoreIDs, VariableStore } from "./kbdInterface.js"; -import RuleBehavior from "./ruleBehavior.js"; - -import { DeviceSpec, globalObject } from "@keymanapp/web-utils"; import { ModifierKeyConstants } from '@keymanapp/common-types'; +import { + Codes, type Keyboard, MinimalKeymanGlobal, KeyEvent, Layouts, type MutableSystemStore, + type OutputTarget, Mock, SystemStoreIDs, +} from "@keymanapp/keyboard-processor"; +import DefaultRules, { EmulationKeystrokes } from "./defaultRules.js"; +import RuleBehavior from "./ruleBehavior.js"; +import KeyboardInterface from './kbdInterface.js'; +import { DeviceSpec, globalObject } from "@keymanapp/web-utils"; // #endregion @@ -26,11 +24,6 @@ import { ModifierKeyConstants } from '@keymanapp/common-types'; export type BeepHandler = (outputTarget: OutputTarget) => void; export type LogMessageHandler = (str: string) => void; -export interface VariableStoreSerializer { - loadStore(keyboardID: string, storeName: string): VariableStore; - saveStore(keyboardID: string, storeName: string, storeMap: VariableStore): void; -} - export interface ProcessorInitOptions { baseLayout?: string; keyboardInterface?: KeyboardInterface; @@ -277,7 +270,8 @@ export default class KeyboardProcessor extends EventEmitter { const lockNames = ['CAPS', 'NUM_LOCK', 'SCROLL_LOCK'] as const; const lockKeys = ['K_CAPS', 'K_NUMLOCK', 'K_SCROLL'] as const; - const lockModifiers = [ ModifierKeyConstants.CAPITALFLAG, ModifierKeyConstants.NUMLOCKFLAG, ModifierKeyConstants.SCROLLFLAG] as const; + const lockModifiers = [ModifierKeyConstants.CAPITALFLAG, ModifierKeyConstants.NUMLOCKFLAG, ModifierKeyConstants.SCROLLFLAG] as const; + if(!this.activeKeyboard) { return true; @@ -329,6 +323,8 @@ export default class KeyboardProcessor extends EventEmitter { const lockModifiers = [ModifierKeyConstants.CAPITALFLAG, ModifierKeyConstants.NUMLOCKFLAG, ModifierKeyConstants.SCROLLFLAG] as const; const noLockModifers = [ModifierKeyConstants.NOTCAPITALFLAG, ModifierKeyConstants.NOTNUMLOCKFLAG, ModifierKeyConstants.NOTSCROLLFLAG] as const; + + for(let i=0; i < lockKeys.length; i++) { const key = lockKeys[i]; const flag = this.stateKeys[key]; diff --git a/common/web/keyboard-processor/src/text/ruleBehavior.ts b/web/src/engine/js-processor/src/ruleBehavior.ts similarity index 96% rename from common/web/keyboard-processor/src/text/ruleBehavior.ts rename to web/src/engine/js-processor/src/ruleBehavior.ts index e5d669e909..aced7065d2 100644 --- a/common/web/keyboard-processor/src/text/ruleBehavior.ts +++ b/web/src/engine/js-processor/src/ruleBehavior.ts @@ -1,9 +1,7 @@ /// import KeyboardProcessor from "./keyboardProcessor.js"; -import OutputTarget, { Mock, type Transcription } from "./outputTarget.js"; -import { VariableStoreDictionary } from "../keyboards/keyboard.js"; -import type { VariableStore } from "./kbdInterface.js"; +import { OutputTarget, Mock, type Transcription, VariableStoreDictionary, type VariableStore } from "@keymanapp/keyboard-processor"; /** * Represents the commands and state changes that result from a matched keyboard rule. diff --git a/web/src/engine/js-processor/tsconfig.json b/web/src/engine/js-processor/tsconfig.json new file mode 100644 index 0000000000..a57467139b --- /dev/null +++ b/web/src/engine/js-processor/tsconfig.json @@ -0,0 +1,13 @@ +{ + // While the actual references themselves are headless, it compiles against the DOM-reliant OSK module. + "extends": "../../tsconfig.dom.json", + + "compilerOptions": { + "baseUrl": "./", + "outDir": "../../../build/engine/js-processor/obj/", + "tsBuildInfoFile": "../../../build/engine/js-processor/obj/tsconfig.tsbuildinfo", + "rootDir": "./src" + }, + + "include": [ "**/*.ts" ], +} diff --git a/web/src/engine/main/build.sh b/web/src/engine/main/build.sh index cceb9b4b8b..4e6ca67db1 100755 --- a/web/src/engine/main/build.sh +++ b/web/src/engine/main/build.sh @@ -18,6 +18,7 @@ builder_describe "Builds the Keyman Engine for Web's common top-level base class "@/common/predictive-text" \ "@/web/src/engine/interfaces build" \ "@/web/src/engine/device-detect build" \ + "@/web/src/engine/js-processor build" \ "@/web/src/engine/package-cache build" \ "@/web/src/engine/osk build" \ "@/developer/src/kmc-model test" \ diff --git a/web/src/engine/main/src/contextManagerBase.ts b/web/src/engine/main/src/contextManagerBase.ts index c3f9581003..e745dbe312 100644 --- a/web/src/engine/main/src/contextManagerBase.ts +++ b/web/src/engine/main/src/contextManagerBase.ts @@ -1,5 +1,6 @@ import { EventEmitter } from 'eventemitter3'; -import { ManagedPromise, type Keyboard, type KeyboardInterface, type OutputTarget } from '@keymanapp/keyboard-processor'; +import { ManagedPromise, type Keyboard, type OutputTarget } from '@keymanapp/keyboard-processor'; +import { type KeyboardInterface } from 'keyman/engine/js-processor'; import { StubAndKeyboardCache, type KeyboardStub } from 'keyman/engine/package-cache'; import { PredictionContext } from 'keyman/engine/interfaces'; import { EngineConfiguration } from './engineConfiguration.js'; diff --git a/web/src/engine/main/src/engineConfiguration.ts b/web/src/engine/main/src/engineConfiguration.ts index c66d3bd805..bb82844c56 100644 --- a/web/src/engine/main/src/engineConfiguration.ts +++ b/web/src/engine/main/src/engineConfiguration.ts @@ -1,6 +1,7 @@ import { EventEmitter } from "eventemitter3"; -import { DeviceSpec, KeyboardProperties, ManagedPromise, OutputTarget, physicalKeyDeviceAlias, RuleBehavior, SpacebarText } from "@keymanapp/keyboard-processor"; +import { DeviceSpec, KeyboardProperties, ManagedPromise, OutputTarget, physicalKeyDeviceAlias, SpacebarText } from "@keymanapp/keyboard-processor"; +import { RuleBehavior } from 'keyman/engine/js-processor'; import { PathConfiguration, PathOptionDefaults, PathOptionSpec } from "keyman/engine/interfaces"; import { Device } from "keyman/engine/device-detect"; import { KeyboardStub } from "keyman/engine/package-cache"; diff --git a/web/src/engine/main/src/hardKeyboard.ts b/web/src/engine/main/src/hardKeyboard.ts index a07904f4a6..7dc9b5e357 100644 --- a/web/src/engine/main/src/hardKeyboard.ts +++ b/web/src/engine/main/src/hardKeyboard.ts @@ -1,5 +1,6 @@ import { EventEmitter } from "eventemitter3"; -import { Keyboard, KeyMapping, KeyEvent, type RuleBehavior, Codes } from "@keymanapp/keyboard-processor"; +import { Keyboard, KeyMapping, KeyEvent, Codes } from "@keymanapp/keyboard-processor"; +import { type RuleBehavior } from 'keyman/engine/js-processor'; import { KeyEventSourceInterface } from 'keyman/engine/events'; import { ModifierKeyConstants } from '@keymanapp/common-types'; diff --git a/web/src/engine/main/src/headless/inputProcessor.ts b/web/src/engine/main/src/headless/inputProcessor.ts index 7b89a0e242..a96170457e 100644 --- a/web/src/engine/main/src/headless/inputProcessor.ts +++ b/web/src/engine/main/src/headless/inputProcessor.ts @@ -12,15 +12,13 @@ import { Codes, isEmptyTransform, type Keyboard, - KeyboardInterface, - KeyboardProcessor, type KeyEvent, Mock, type OutputTarget, - type ProcessorInitOptions, - RuleBehavior, SystemStoreIDs, } from "@keymanapp/keyboard-processor"; +import { KeyboardInterface, KeyboardProcessor, RuleBehavior, type ProcessorInitOptions } from 'keyman/engine/js-processor'; + import { TranscriptionCache } from "./transcriptionCache.js"; export class InputProcessor { diff --git a/web/src/engine/main/src/keyboardInterface.ts b/web/src/engine/main/src/keyboardInterface.ts index a18bafecd0..d966c26bad 100644 --- a/web/src/engine/main/src/keyboardInterface.ts +++ b/web/src/engine/main/src/keyboardInterface.ts @@ -1,6 +1,5 @@ -import { - KeyboardInterface as KeyboardInterfaceBase, KeyboardObject, -} from "@keymanapp/keyboard-processor"; +import { KeyboardObject } from "@keymanapp/keyboard-processor"; +import { KeyboardInterface as KeyboardInterfaceBase } from 'keyman/engine/js-processor'; import { KeyboardStub, RawKeyboardStub, toUnprefixedKeyboardId as unprefixed } from 'keyman/engine/package-cache'; import { ContextManagerBase } from './contextManagerBase.js'; diff --git a/web/src/engine/main/src/keymanEngine.ts b/web/src/engine/main/src/keymanEngine.ts index eaa54e5994..86ff9d5898 100644 --- a/web/src/engine/main/src/keymanEngine.ts +++ b/web/src/engine/main/src/keymanEngine.ts @@ -1,4 +1,5 @@ -import { type Keyboard, KeyboardKeymanGlobal, ProcessorInitOptions } from "@keymanapp/keyboard-processor"; +import { type KeyEvent, type Keyboard, KeyboardKeymanGlobal } from "@keymanapp/keyboard-processor"; +import { ProcessorInitOptions, RuleBehavior } from 'keyman/engine/js-processor'; import { DOMKeyboardLoader as KeyboardLoader } from "@keymanapp/keyboard-processor/dom-keyboard-loader"; import { InputProcessor } from './headless/inputProcessor.js'; import { OSKView } from "keyman/engine/osk"; @@ -10,7 +11,7 @@ import KeyboardInterface from "./keyboardInterface.js"; import { ContextManagerBase } from "./contextManagerBase.js"; import HardKeyboardBase from "./hardKeyboard.js"; import { LegacyAPIEvents } from "./legacyAPIEvents.js"; -import { KeyEventHandler, EventNames, EventListener, LegacyEventEmitter } from "keyman/engine/events"; +import { EventNames, EventListener, LegacyEventEmitter } from "keyman/engine/events"; import DOMCloudRequester from "keyman/engine/package-cache/dom-requester"; import KEYMAN_VERSION from "@keymanapp/keyman-version"; @@ -29,6 +30,9 @@ function determineBaseLayout(): string { } } +export type KeyEventFullResultCallback = (result: RuleBehavior, error?: Error) => void; +export type KeyEventFullHandler = (event: KeyEvent, callback?: KeyEventFullResultCallback) => void; + export default class KeymanEngine< Configuration extends EngineConfiguration, ContextManager extends ContextManagerBase, @@ -47,7 +51,7 @@ export default class KeymanEngine< protected keyEventRefocus?: () => void; - private keyEventListener: KeyEventHandler = (event, callback) => { + private keyEventListener: KeyEventFullHandler = (event, callback) => { const outputTarget = this.contextManager.activeTarget; if(!this.contextManager.activeKeyboard || !outputTarget) { diff --git a/web/src/engine/main/tsconfig.json b/web/src/engine/main/tsconfig.json index 9711d06a77..e3b2b0616c 100644 --- a/web/src/engine/main/tsconfig.json +++ b/web/src/engine/main/tsconfig.json @@ -15,5 +15,6 @@ { "path": "../osk" }, { "path": "../package-cache" }, { "path": "../interfaces" }, + { "path": "../js-processor" } ] } diff --git a/common/web/keyboard-processor/tests/dom/cases/domKeyboardLoader.spec.ts b/web/src/test/auto/dom/cases/keyboard-processor/domKeyboardLoader.spec.ts similarity index 95% rename from common/web/keyboard-processor/tests/dom/cases/domKeyboardLoader.spec.ts rename to web/src/test/auto/dom/cases/keyboard-processor/domKeyboardLoader.spec.ts index 1965adc853..63414662fa 100644 --- a/common/web/keyboard-processor/tests/dom/cases/domKeyboardLoader.spec.ts +++ b/web/src/test/auto/dom/cases/keyboard-processor/domKeyboardLoader.spec.ts @@ -1,7 +1,8 @@ import { assert } from 'chai'; import { DOMKeyboardLoader } from '@keymanapp/keyboard-processor/dom-keyboard-loader'; -import { extendString, KeyboardHarness, Keyboard, KeyboardInterface, MinimalKeymanGlobal, Mock, DeviceSpec, KeyboardKeymanGlobal } from '@keymanapp/keyboard-processor'; +import { extendString, KeyboardHarness, Keyboard, MinimalKeymanGlobal, Mock, DeviceSpec, KeyboardKeymanGlobal } from '@keymanapp/keyboard-processor'; +import { KeyboardInterface } from 'keyman/engine/js-processor'; declare let window: typeof globalThis; // KeymanEngine from the web/ folder... when available. diff --git a/web/src/test/auto/dom/kbdLoader.ts b/web/src/test/auto/dom/kbdLoader.ts index 629ade205e..2254011857 100644 --- a/web/src/test/auto/dom/kbdLoader.ts +++ b/web/src/test/auto/dom/kbdLoader.ts @@ -4,11 +4,11 @@ import { import { Keyboard, - KeyboardInterface, KeyboardProperties, MinimalKeymanGlobal } from '@keymanapp/keyboard-processor'; +import { KeyboardInterface } from 'keyman/engine/js-processor'; import { KeyboardStub } from 'keyman/engine/package-cache'; const loader = new DOMKeyboardLoader(new KeyboardInterface(window, MinimalKeymanGlobal)); diff --git a/web/src/test/auto/headless/engine/interfaces/prediction/predictionContext.spec.js b/web/src/test/auto/headless/engine/interfaces/prediction/predictionContext.spec.js index ba4808e66a..97ce24d036 100644 --- a/web/src/test/auto/headless/engine/interfaces/prediction/predictionContext.spec.js +++ b/web/src/test/auto/headless/engine/interfaces/prediction/predictionContext.spec.js @@ -4,7 +4,8 @@ import sinon from 'sinon'; import { LanguageProcessor, TranscriptionCache } from 'keyman/engine/main'; import { PredictionContext } from 'keyman/engine/interfaces'; import { Worker as LMWorker } from "@keymanapp/lexical-model-layer/node"; -import { DeviceSpec, KeyboardProcessor, Mock } from '@keymanapp/keyboard-processor'; +import { DeviceSpec, Mock } from '@keymanapp/keyboard-processor'; +import { KeyboardProcessor } from 'keyman/engine/js-processor'; function compileDummyModel(suggestionSets) { return ` diff --git a/web/src/tools/testing/recorder-core/src/nodeProctor.ts b/web/src/tools/testing/recorder-core/src/nodeProctor.ts index 6f6d7fd6a0..852002d4b3 100644 --- a/web/src/tools/testing/recorder-core/src/nodeProctor.ts +++ b/web/src/tools/testing/recorder-core/src/nodeProctor.ts @@ -8,8 +8,9 @@ import { RecordedSyntheticKeystroke } from "./index.js"; -import { KeyboardInterface, KeyEvent, KeyEventSpec, KeyboardProcessor, Mock, type OutputTarget, KeyboardHarness } from "@keymanapp/keyboard-processor"; +import { KeyEvent, KeyEventSpec, Mock, type OutputTarget, KeyboardHarness } from "@keymanapp/keyboard-processor"; import { DeviceSpec } from "@keymanapp/web-utils"; +import { KeyboardInterface, KeyboardProcessor } from 'keyman/engine/js-processor'; export default class NodeProctor extends Proctor { private keyboardWithHarness: KeyboardHarness; diff --git a/web/src/tools/testing/recorder-core/tsconfig.json b/web/src/tools/testing/recorder-core/tsconfig.json index a7bd08c374..38af26e1a4 100644 --- a/web/src/tools/testing/recorder-core/tsconfig.json +++ b/web/src/tools/testing/recorder-core/tsconfig.json @@ -17,6 +17,7 @@ { "path": "../../../../../common/web/keyman-version" }, { "path": "../../../../../common/web/utils/" }, { "path": "../../../../../common/web/keyboard-processor/" }, - { "path": "../../../../../common/web/lm-message-types" } + { "path": "../../../../../common/web/lm-message-types" }, + { "path": "../../../engine/js-processor" } ], } From bba694c306d17a841b2e86606de86638bbfa4397 Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Mon, 12 Aug 2024 19:33:29 +0200 Subject: [PATCH 2/3] refactor(web): work around global in interface --- .../keyboard-processor/src/text/keyEvent.ts | 19 +++++-------------- .../engine/js-processor/src/defaultRules.ts | 12 ++++-------- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/common/web/keyboard-processor/src/text/keyEvent.ts b/common/web/keyboard-processor/src/text/keyEvent.ts index 7c507cc170..2d0ee49322 100644 --- a/common/web/keyboard-processor/src/text/keyEvent.ts +++ b/common/web/keyboard-processor/src/text/keyEvent.ts @@ -12,23 +12,14 @@ import { type DeviceSpec } from "@keymanapp/web-utils"; import Codes from './codes.js'; import { ActiveKeyBase } from "../index.js"; -interface DefaultRulesInterface { +export interface DefaultRulesInterface { forAny(Lkc: KeyEvent, isMnemonic: boolean): string; } -export class BASE_DEFAULT_RULES { - private static _instance: DefaultRulesInterface; +let defaultRules: DefaultRulesInterface; - // Prevent direct instantiation. - private constructor() { } - - public static forAny(Lkc: KeyEvent, isMnemonic: boolean): string { - return this._instance.forAny(Lkc, isMnemonic); - } - - public static set instance(value: DefaultRulesInterface) { - this._instance = value; - } +export function setDefaultRules(rules: DefaultRulesInterface) { + defaultRules = rules; } // Represents a probability distribution over a keyboard's keys. @@ -168,7 +159,7 @@ export default class KeyEvent implements KeyEventSpec { // the actual keyname instead. mappingEvent.kName = 'K_xxxx'; mappingEvent.Lmodifiers = (shifted ? 0x10 : 0); // mnemonic lookups only exist for default & shift layers. - var mappedChar: string = BASE_DEFAULT_RULES.forAny(mappingEvent, true); + var mappedChar: string = defaultRules.forAny(mappingEvent, true); /* First, save a backup of the original code. This one won't needlessly trigger keyboard * rules, but allows us to replicate/emulate commands after rule processing if needed. diff --git a/web/src/engine/js-processor/src/defaultRules.ts b/web/src/engine/js-processor/src/defaultRules.ts index edaf440bf9..3d56a2dad8 100644 --- a/web/src/engine/js-processor/src/defaultRules.ts +++ b/web/src/engine/js-processor/src/defaultRules.ts @@ -5,7 +5,7 @@ */ import { ModifierKeyConstants } from '@keymanapp/common-types'; -import { BASE_DEFAULT_RULES, Codes, type KeyEvent, type OutputTarget } from "@keymanapp/keyboard-processor"; +import { Codes, type KeyEvent, type OutputTarget, setDefaultRules } from "@keymanapp/keyboard-processor"; import RuleBehavior from "./ruleBehavior.js"; export enum EmulationKeystrokes { @@ -13,11 +13,13 @@ export enum EmulationKeystrokes { Backspace = '\b' } + /** * Defines a collection of static library functions that define KeymanWeb's default (implied) keyboard rule behaviors. */ export default class DefaultRules { public constructor() { + setDefaultRules(this); } codeForEvent(Lkc: KeyEvent) { @@ -28,7 +30,7 @@ export default class DefaultRules { * Serves as a default keycode lookup table. This may be referenced safely by mnemonic handling without fear of side-effects. * Also used by Processor.defaultRuleBehavior to generate output after filtering for special cases. */ - public forAny(Lkc: KeyEvent, isMnemonic: boolean, ruleBehavior?: RuleBehavior) { + public forAny(Lkc: KeyEvent, isMnemonic: boolean, ruleBehavior?: RuleBehavior): string { var char = ''; // A pretty simple table of lookups, corresponding VERY closely to the original defaultKeyOutput. @@ -217,9 +219,3 @@ export default class DefaultRules { return null; } } - -/** - * A simple instance of the standard 'default rules' for keystroke processing from the - * DefaultRules base class. - */ -BASE_DEFAULT_RULES.instance = new DefaultRules(); From dedc18e15698249860ced0291a20eef2a9b9982c Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Tue, 13 Aug 2024 17:14:51 +0200 Subject: [PATCH 3/3] refactor(web): move some files around MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - move `systemStore.ts` → `js-processor` - move `keyEventSource.interface.ts` → `osk` - move `defaultRules.ts` → back to `keyboard-processor` - in `KeyboardProcessor` pass `ruleBehavior` as parameter to `forAny`, looks like that accidentally got omitted previously Addresses code review comments. --- common/web/keyboard-processor/src/index.ts | 4 +- .../src/keyboards/keyboard.ts | 74 +------------------ .../src/text}/defaultRules.ts | 35 ++++----- .../keyboard-processor/src/text/keyEvent.ts | 19 ++--- .../src/text/ruleBehavior.interface.ts | 12 --- .../app/browser/src/defaultBrowserRules.ts | 2 +- web/src/app/webview/src/keymanEngine.ts | 4 +- web/src/engine/events/src/index.ts | 1 - web/src/engine/js-processor/src/index.ts | 3 +- .../engine/js-processor/src/kbdInterface.ts | 73 +++++++++++++++++- .../js-processor/src/keyboardProcessor.ts | 8 +- .../engine/js-processor/src/ruleBehavior.ts | 3 +- .../engine/js-processor/src}/systemStores.ts | 4 +- web/src/engine/main/src/hardKeyboard.ts | 2 +- .../main/src/headless/inputProcessor.ts | 3 +- .../main/src/variableStoreCookieSerializer.ts | 2 +- web/src/engine/osk/src/index.ts | 1 + .../src/views}/keyEventSource.interface.ts | 5 +- web/src/engine/osk/src/views/oskView.ts | 8 +- web/src/engine/osk/src/visualKeyboard.ts | 2 +- 20 files changed, 125 insertions(+), 140 deletions(-) rename {web/src/engine/js-processor/src => common/web/keyboard-processor/src/text}/defaultRules.ts (88%) delete mode 100644 common/web/keyboard-processor/src/text/ruleBehavior.interface.ts rename {common/web/keyboard-processor/src/text => web/src/engine/js-processor/src}/systemStores.ts (96%) rename web/src/engine/{events/src => osk/src/views}/keyEventSource.interface.ts (63%) diff --git a/common/web/keyboard-processor/src/index.ts b/common/web/keyboard-processor/src/index.ts index c719d7ee8e..d9d0b2137f 100644 --- a/common/web/keyboard-processor/src/index.ts +++ b/common/web/keyboard-processor/src/index.ts @@ -27,14 +27,14 @@ export { default as StateKeyMap } from "./keyboards/stateKeyMap.js"; export { default as Codes } from "./text/codes.js"; export * from "./text/codes.js"; export * from "./text/deadkeys.js"; +export { default as DefaultRules } from "./text/defaultRules.js"; +export * from "./text/defaultRules.js"; export { default as KeyEvent } from "./text/keyEvent.js"; export * from "./text/keyEvent.js"; export { default as KeyMapping } from "./text/keyMapping.js"; export { default as OutputTarget } from "./text/outputTarget.js"; export * from "./text/outputTarget.js"; export * from "./text/stringDivergence.js"; -export * from "./text/systemStores.js"; -export * from "./text/ruleBehavior.interface.js"; export * from "@keymanapp/web-utils"; diff --git a/common/web/keyboard-processor/src/keyboards/keyboard.ts b/common/web/keyboard-processor/src/keyboards/keyboard.ts index 6ca5f790fd..d791d6ea75 100644 --- a/common/web/keyboard-processor/src/keyboards/keyboard.ts +++ b/common/web/keyboard-processor/src/keyboards/keyboard.ts @@ -6,82 +6,10 @@ import type OutputTarget from "../text/outputTarget.js"; import { ModifierKeyConstants, TouchLayout } from "@keymanapp/common-types"; type TouchLayoutSpec = TouchLayout.TouchLayoutPlatform & { isDefault?: boolean}; -import type { ComplexKeyboardStore, KeyboardStore } from "../text/systemStores.js"; - import { Version, DeviceSpec } from "@keymanapp/web-utils"; import StateKeyMap from "./stateKeyMap.js"; -export class RuleDeadkey { - /** Discriminant field - 'd' for Deadkey. - */ - t: 'd'; - - /** - * Value: the deadkey's ID. - */ - d: number; // For 'd'eadkey; also reflects the Deadkey class's 'd' property. -} - -export class StoreBeep { - /** Discriminant field - 'b' for `beep` - */ - ['t']: 'b'; -} - -export type RuleChar = string; - -export class ContextAny { - /** Discriminant field - 'a' for `any()`. - */ - ['t']: 'a'; - - /** - * Value: the store to search. - */ - ['a']: KeyboardStore; // For 'a'ny statement. - - /** - * If set to true, negates the 'any'. - */ - ['n']: boolean | 0 | 1; -} - -export class RuleIndex { - /** Discriminant field - 'i' for `index()`. - */ - ['t']: 'i'; - - /** - * Value: the Store from which to output - */ - ['i']: KeyboardStore; - - /** - * Offset: the offset in context for the corresponding `any()`. - */ - ['o']: number; -} - -export class ContextEx { - /** Discriminant field - 'c' for `context()`. - */ - ['t']: 'c'; - - /** - * Value: The offset into the current rule's context to be matched. - */ - ['c']: number; // For 'c'ontext statement. -} - -export class ContextNul { - /** Discriminant field - 'n' for `nul` - */ - ['t']: 'n'; -} - - - -export type StoreNonCharEntry = RuleDeadkey | StoreBeep; +type ComplexKeyboardStore = ( string | { t: 'd', d: number } | { ['t']: 'b' })[]; /** * Stores preprocessed properties of a keyboard for quick retrieval later. diff --git a/web/src/engine/js-processor/src/defaultRules.ts b/common/web/keyboard-processor/src/text/defaultRules.ts similarity index 88% rename from web/src/engine/js-processor/src/defaultRules.ts rename to common/web/keyboard-processor/src/text/defaultRules.ts index 3d56a2dad8..94acb02688 100644 --- a/web/src/engine/js-processor/src/defaultRules.ts +++ b/common/web/keyboard-processor/src/text/defaultRules.ts @@ -5,23 +5,24 @@ */ import { ModifierKeyConstants } from '@keymanapp/common-types'; -import { Codes, type KeyEvent, type OutputTarget, setDefaultRules } from "@keymanapp/keyboard-processor"; -import RuleBehavior from "./ruleBehavior.js"; +import Codes from './codes.js'; +import type KeyEvent from './keyEvent.js'; +import type OutputTarget from './outputTarget.js'; export enum EmulationKeystrokes { Enter = '\n', Backspace = '\b' } +export class LogMessages { + errorLog?: string; + warningLog?: string; +} /** * Defines a collection of static library functions that define KeymanWeb's default (implied) keyboard rule behaviors. */ export default class DefaultRules { - public constructor() { - setDefaultRules(this); - } - codeForEvent(Lkc: KeyEvent) { return Codes.keyCodes[Lkc.kName] || Lkc.Lcode;; } @@ -30,7 +31,7 @@ export default class DefaultRules { * Serves as a default keycode lookup table. This may be referenced safely by mnemonic handling without fear of side-effects. * Also used by Processor.defaultRuleBehavior to generate output after filtering for special cases. */ - public forAny(Lkc: KeyEvent, isMnemonic: boolean, ruleBehavior?: RuleBehavior): string { + public forAny(Lkc: KeyEvent, isMnemonic: boolean, logMessages?: LogMessages): string { var char = ''; // A pretty simple table of lookups, corresponding VERY closely to the original defaultKeyOutput. @@ -38,9 +39,9 @@ export default class DefaultRules { return char; } else if(!isMnemonic && ((char = this.forNumpadKeys(Lkc)) != null)) { return char; - } else if((char = this.forUnicodeKeynames(Lkc, ruleBehavior)) != null) { + } else if((char = this.forUnicodeKeynames(Lkc, logMessages)) != null) { return char; - } else if((char = this.forBaseKeys(Lkc, ruleBehavior)) != null) { + } else if((char = this.forBaseKeys(Lkc, logMessages)) != null) { return char; } else { // // For headless and embeddded, we may well allow '\t'. It's DOM mode that has other uses. @@ -147,7 +148,7 @@ export default class DefaultRules { // Test for fall back to U_xxxxxx key id // For this first test, we ignore the keyCode and use the keyName - public forUnicodeKeynames(Lkc: KeyEvent, ruleBehavior?: RuleBehavior) { + public forUnicodeKeynames(Lkc: KeyEvent, logMessages?: LogMessages) { const keyName = Lkc.kName; // Test for fall back to U_xxxxxx key id @@ -164,8 +165,8 @@ export default class DefaultRules { // Code points [U_0000 - U_001F] and [U_0080 - U_009F] refer to Unicode C0 and C1 control codes. // Check the codePoint number and do not allow output of these codes via U_xxxxxx shortcuts. // Also handles invalid identifiers (e.g. `U_ghij`) for which parseInt returns NaN - if(ruleBehavior) { - ruleBehavior.errorLog = ("Suppressing Unicode control code in " + keyName); + if(logMessages) { + logMessages.errorLog = ("Suppressing Unicode control code in " + keyName); } // We'll attempt to add valid chars continue; @@ -180,7 +181,7 @@ export default class DefaultRules { // Test for otherwise unimplemented keys on the the base default & shift layers. // Those keys must be blocked by keyboard rules if intentionally unimplemented; otherwise, this function will trigger. - public forBaseKeys(Lkc: KeyEvent, ruleBehavior?: RuleBehavior) { + public forBaseKeys(Lkc: KeyEvent, logMessages?: LogMessages) { let n = Lkc.Lcode; let keyShiftState = Lkc.Lmodifiers; @@ -189,8 +190,8 @@ export default class DefaultRules { if (keyShiftState == ModifierKeyConstants.K_SHIFTFLAG) { keyShiftState = 1; } else if(keyShiftState != 0) { - if(ruleBehavior) { - ruleBehavior.warningLog = "KMW only defines default key output for the 'default' and 'shift' layers!"; + if(logMessages) { + logMessages.warningLog = "KMW only defines default key output for the 'default' and 'shift' layers!"; } return null; } @@ -211,8 +212,8 @@ export default class DefaultRules { return keyShiftState ? '|' : '\\'; } } catch (e) { - if(ruleBehavior) { - ruleBehavior.errorLog = "Error detected with default mapping for key: code = " + n + ", shift state = " + (keyShiftState == 1 ? 'shift' : 'default'); + if(logMessages) { + logMessages.errorLog = "Error detected with default mapping for key: code = " + n + ", shift state = " + (keyShiftState == 1 ? 'shift' : 'default'); } } diff --git a/common/web/keyboard-processor/src/text/keyEvent.ts b/common/web/keyboard-processor/src/text/keyEvent.ts index 2d0ee49322..7d6483d0e9 100644 --- a/common/web/keyboard-processor/src/text/keyEvent.ts +++ b/common/web/keyboard-processor/src/text/keyEvent.ts @@ -10,22 +10,19 @@ import type Keyboard from "../keyboards/keyboard.js"; import { type DeviceSpec } from "@keymanapp/web-utils"; import Codes from './codes.js'; +import DefaultRules from "./defaultRules.js"; import { ActiveKeyBase } from "../index.js"; -export interface DefaultRulesInterface { - forAny(Lkc: KeyEvent, isMnemonic: boolean): string; -} - -let defaultRules: DefaultRulesInterface; - -export function setDefaultRules(rules: DefaultRulesInterface) { - defaultRules = rules; -} - // Represents a probability distribution over a keyboard's keys. // Defined here to avoid compilation issues. export type KeyDistribution = { keySpec: ActiveKeyBase, p: number }[]; +/** + * A simple instance of the standard 'default rules' for keystroke processing from the + * DefaultRules base class. + */ +const BASE_DEFAULT_RULES = new DefaultRules(); + export interface KeyEventSpec { Lcode: number; @@ -159,7 +156,7 @@ export default class KeyEvent implements KeyEventSpec { // the actual keyname instead. mappingEvent.kName = 'K_xxxx'; mappingEvent.Lmodifiers = (shifted ? 0x10 : 0); // mnemonic lookups only exist for default & shift layers. - var mappedChar: string = defaultRules.forAny(mappingEvent, true); + var mappedChar: string = BASE_DEFAULT_RULES.forAny(mappingEvent, true); /* First, save a backup of the original code. This one won't needlessly trigger keyboard * rules, but allows us to replicate/emulate commands after rule processing if needed. diff --git a/common/web/keyboard-processor/src/text/ruleBehavior.interface.ts b/common/web/keyboard-processor/src/text/ruleBehavior.interface.ts deleted file mode 100644 index 29f81bcc8c..0000000000 --- a/common/web/keyboard-processor/src/text/ruleBehavior.interface.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { type Transcription } from './outputTarget.js'; - -/** - * Represents the commands and state changes that result from a matched keyboard rule. - */ -export interface RuleBehaviorInterface { - /** - * The before-and-after Transform from matching a keyboard rule. May be `null` - * if no keyboard rules were matched for the keystroke. - */ - transcription: Transcription; -} diff --git a/web/src/app/browser/src/defaultBrowserRules.ts b/web/src/app/browser/src/defaultBrowserRules.ts index fc084ec3f4..1ac4801689 100644 --- a/web/src/app/browser/src/defaultBrowserRules.ts +++ b/web/src/app/browser/src/defaultBrowserRules.ts @@ -1,10 +1,10 @@ import { ModifierKeyConstants } from '@keymanapp/common-types'; import { Codes, + DefaultRules, type KeyEvent, type OutputTarget } from '@keymanapp/keyboard-processor'; -import { DefaultRules } from 'keyman/engine/js-processor'; import ContextManager from './contextManager.js'; diff --git a/web/src/app/webview/src/keymanEngine.ts b/web/src/app/webview/src/keymanEngine.ts index 2f07350548..dfae058a65 100644 --- a/web/src/app/webview/src/keymanEngine.ts +++ b/web/src/app/webview/src/keymanEngine.ts @@ -1,5 +1,5 @@ -import { DeviceSpec } from '@keymanapp/keyboard-processor' -import { DefaultRules, RuleBehavior } from 'keyman/engine/js-processor'; +import { DeviceSpec, DefaultRules } from '@keymanapp/keyboard-processor' +import { RuleBehavior } from 'keyman/engine/js-processor'; import { KeymanEngine as KeymanEngineBase, KeyboardInterface } from 'keyman/engine/main'; import { AnchoredOSKView, ViewConfiguration, StaticActivator } from 'keyman/engine/osk'; import { getAbsoluteX, getAbsoluteY } from 'keyman/engine/dom-utils'; diff --git a/web/src/engine/events/src/index.ts b/web/src/engine/events/src/index.ts index 2d95307bc9..ee5674b66c 100644 --- a/web/src/engine/events/src/index.ts +++ b/web/src/engine/events/src/index.ts @@ -1,4 +1,3 @@ export { DomEventTracker } from './domEventTracker.js'; export { EmitterListenerSpy } from './emitterListenerSpy.js'; -export * from './keyEventSource.interface.js'; export * from './legacyEventEmitter.js'; \ No newline at end of file diff --git a/web/src/engine/js-processor/src/index.ts b/web/src/engine/js-processor/src/index.ts index 1c51de80d9..f87a044bd7 100644 --- a/web/src/engine/js-processor/src/index.ts +++ b/web/src/engine/js-processor/src/index.ts @@ -1,7 +1,6 @@ -export { default as DefaultRules } from "./defaultRules.js"; -export * from "./defaultRules.js"; export { default as KeyboardProcessor } from "./keyboardProcessor.js"; export * from "./keyboardProcessor.js"; export { default as RuleBehavior } from "./ruleBehavior.js"; export * from './kbdInterface.js'; export { default as KeyboardInterface } from "./kbdInterface.js"; +export * from "./systemStores.js"; diff --git a/web/src/engine/js-processor/src/kbdInterface.ts b/web/src/engine/js-processor/src/kbdInterface.ts index 2b34eadce9..6272637d04 100644 --- a/web/src/engine/js-processor/src/kbdInterface.ts +++ b/web/src/engine/js-processor/src/kbdInterface.ts @@ -7,8 +7,9 @@ import { type DeviceSpec } from "@keymanapp/web-utils"; import { ModifierKeyConstants } from '@keymanapp/common-types'; -import { Codes, type KeyEvent, type Deadkey, ComplexKeyboardStore, type KeyboardStore, KeyboardStoreElement, KeyMapping, SystemStoreIDs, SystemStore, MutableSystemStore, PlatformSystemStore, type OutputTarget, Mock, Keyboard, VariableStore, VariableStoreDictionary, VariableStoreSerializer, KeyboardHarness, KeyboardKeymanGlobal, RuleDeadkey, ContextAny, RuleIndex, ContextEx, ContextNul, RuleChar } from "@keymanapp/keyboard-processor"; +import { Codes, type KeyEvent, type Deadkey, KeyMapping, type OutputTarget, Mock, Keyboard, KeyboardHarness, KeyboardKeymanGlobal, VariableStoreDictionary } from "@keymanapp/keyboard-processor"; import RuleBehavior from "./ruleBehavior.js"; +import { ComplexKeyboardStore, type KeyboardStore, KeyboardStoreElement, SystemStoreIDs, SystemStore, MutableSystemStore, PlatformSystemStore, VariableStore, VariableStoreSerializer } from "./systemStores.js"; //#endregion @@ -20,9 +21,79 @@ export class KeyInformation { modifiers: number; } +type RuleChar = string; + +class RuleDeadkey { + /** Discriminant field - 'd' for Deadkey. + */ + t: 'd'; + + /** + * Value: the deadkey's ID. + */ + d: number; // For 'd'eadkey; also reflects the Deadkey class's 'd' property. +} + +class ContextAny { + /** Discriminant field - 'a' for `any()`. + */ + ['t']: 'a'; + + /** + * Value: the store to search. + */ + ['a']: KeyboardStore; // For 'a'ny statement. + + /** + * If set to true, negates the 'any'. + */ + ['n']: boolean | 0 | 1; +} + +class RuleIndex { + /** Discriminant field - 'i' for `index()`. + */ + ['t']: 'i'; + + /** + * Value: the Store from which to output + */ + ['i']: KeyboardStore; + + /** + * Offset: the offset in context for the corresponding `any()`. + */ + ['o']: number; +} + +class ContextEx { + /** Discriminant field - 'c' for `context()`. + */ + ['t']: 'c'; + + /** + * Value: The offset into the current rule's context to be matched. + */ + ['c']: number; // For 'c'ontext statement. +} + +class ContextNul { + /** Discriminant field - 'n' for `nul` + */ + ['t']: 'n'; +} + +class StoreBeep { + /** Discriminant field - 'b' for `beep` + */ + ['t']: 'b'; +} + type ContextNonCharEntry = RuleDeadkey | ContextAny | RuleIndex | ContextEx | ContextNul; type ContextEntry = RuleChar | ContextNonCharEntry; +export type StoreNonCharEntry = RuleDeadkey | StoreBeep; + /** * Cache of context storing and retrieving return values from KC * Must be reset prior to each keystroke and after any text changes diff --git a/web/src/engine/js-processor/src/keyboardProcessor.ts b/web/src/engine/js-processor/src/keyboardProcessor.ts index 1a6d216dce..12efa4e951 100644 --- a/web/src/engine/js-processor/src/keyboardProcessor.ts +++ b/web/src/engine/js-processor/src/keyboardProcessor.ts @@ -9,13 +9,13 @@ import { EventEmitter } from 'eventemitter3'; import { ModifierKeyConstants } from '@keymanapp/common-types'; import { - Codes, type Keyboard, MinimalKeymanGlobal, KeyEvent, Layouts, type MutableSystemStore, - type OutputTarget, Mock, SystemStoreIDs, + Codes, type Keyboard, MinimalKeymanGlobal, KeyEvent, Layouts, + type OutputTarget, Mock, DefaultRules, EmulationKeystrokes } from "@keymanapp/keyboard-processor"; -import DefaultRules, { EmulationKeystrokes } from "./defaultRules.js"; import RuleBehavior from "./ruleBehavior.js"; import KeyboardInterface from './kbdInterface.js'; import { DeviceSpec, globalObject } from "@keymanapp/web-utils"; +import { type MutableSystemStore, SystemStoreIDs } from "./systemStores.js"; // #endregion @@ -167,7 +167,7 @@ export default class KeyboardProcessor extends EventEmitter { let isMnemonic = this.activeKeyboard && this.activeKeyboard.isMnemonic; if(!matched) { - if((char = this.defaultRules.forAny(Lkc, isMnemonic)) != null) { + if((char = this.defaultRules.forAny(Lkc, isMnemonic, ruleBehavior)) != null) { special = this.defaultRules.forSpecialEmulation(Lkc) if(special == EmulationKeystrokes.Backspace) { // A browser's default backspace may fail to delete both parts of an SMP character. diff --git a/web/src/engine/js-processor/src/ruleBehavior.ts b/web/src/engine/js-processor/src/ruleBehavior.ts index aced7065d2..08881eabcc 100644 --- a/web/src/engine/js-processor/src/ruleBehavior.ts +++ b/web/src/engine/js-processor/src/ruleBehavior.ts @@ -1,7 +1,8 @@ /// import KeyboardProcessor from "./keyboardProcessor.js"; -import { OutputTarget, Mock, type Transcription, VariableStoreDictionary, type VariableStore } from "@keymanapp/keyboard-processor"; +import { OutputTarget, Mock, type Transcription, VariableStoreDictionary } from "@keymanapp/keyboard-processor"; +import { type VariableStore } from "./systemStores.js"; /** * Represents the commands and state changes that result from a matched keyboard rule. diff --git a/common/web/keyboard-processor/src/text/systemStores.ts b/web/src/engine/js-processor/src/systemStores.ts similarity index 96% rename from common/web/keyboard-processor/src/text/systemStores.ts rename to web/src/engine/js-processor/src/systemStores.ts index a2f7abff34..9d02dbddbc 100644 --- a/common/web/keyboard-processor/src/text/systemStores.ts +++ b/web/src/engine/js-processor/src/systemStores.ts @@ -1,5 +1,5 @@ -import { type KeyboardHarness } from "../keyboards/keyboardHarness.js"; -import { StoreNonCharEntry } from '../keyboards/keyboard.js'; +import { type KeyboardHarness } from '@keymanapp/keyboard-processor'; +import { StoreNonCharEntry } from './kbdInterface.js'; export enum SystemStoreIDs { TSS_LAYER = 33, diff --git a/web/src/engine/main/src/hardKeyboard.ts b/web/src/engine/main/src/hardKeyboard.ts index 7dc9b5e357..491073f729 100644 --- a/web/src/engine/main/src/hardKeyboard.ts +++ b/web/src/engine/main/src/hardKeyboard.ts @@ -1,7 +1,7 @@ import { EventEmitter } from "eventemitter3"; import { Keyboard, KeyMapping, KeyEvent, Codes } from "@keymanapp/keyboard-processor"; import { type RuleBehavior } from 'keyman/engine/js-processor'; -import { KeyEventSourceInterface } from 'keyman/engine/events'; +import { KeyEventSourceInterface } from 'keyman/engine/osk'; import { ModifierKeyConstants } from '@keymanapp/common-types'; interface EventMap { diff --git a/web/src/engine/main/src/headless/inputProcessor.ts b/web/src/engine/main/src/headless/inputProcessor.ts index a96170457e..99a31db609 100644 --- a/web/src/engine/main/src/headless/inputProcessor.ts +++ b/web/src/engine/main/src/headless/inputProcessor.ts @@ -15,9 +15,8 @@ import { type KeyEvent, Mock, type OutputTarget, - SystemStoreIDs, } from "@keymanapp/keyboard-processor"; -import { KeyboardInterface, KeyboardProcessor, RuleBehavior, type ProcessorInitOptions } from 'keyman/engine/js-processor'; +import { KeyboardInterface, KeyboardProcessor, RuleBehavior, type ProcessorInitOptions, SystemStoreIDs } from 'keyman/engine/js-processor'; import { TranscriptionCache } from "./transcriptionCache.js"; diff --git a/web/src/engine/main/src/variableStoreCookieSerializer.ts b/web/src/engine/main/src/variableStoreCookieSerializer.ts index 35b51150c0..62c250329a 100644 --- a/web/src/engine/main/src/variableStoreCookieSerializer.ts +++ b/web/src/engine/main/src/variableStoreCookieSerializer.ts @@ -1,4 +1,4 @@ -import { VariableStore, VariableStoreSerializer } from "@keymanapp/keyboard-processor"; +import { VariableStore, VariableStoreSerializer } from 'keyman/engine/js-processor'; import { CookieSerializer } from "keyman/engine/dom-utils"; // While there's little reason we couldn't store all of a keyboard's store values within diff --git a/web/src/engine/osk/src/index.ts b/web/src/engine/osk/src/index.ts index 388ea32750..f2a543e175 100644 --- a/web/src/engine/osk/src/index.ts +++ b/web/src/engine/osk/src/index.ts @@ -4,6 +4,7 @@ export { default as OSKView } from './views/oskView.js'; export { default as FloatingOSKView, FloatingOSKViewConfiguration } from './views/floatingOskView.js'; export { default as AnchoredOSKView } from './views/anchoredOskView.js'; export { default as InlinedOSKView } from './views/inlinedOskView.js'; +export { type KeyEventResultCallback, type KeyEventHandler, KeyEventSourceInterface } from './views/keyEventSource.interface.js'; export { BannerController } from './banner/bannerController.js'; // Is referenced by at least one desktop UI module. export { FloatingOSKCookie as FloatingOSKViewCookie } from './views/floatingOskCookie.js'; diff --git a/web/src/engine/events/src/keyEventSource.interface.ts b/web/src/engine/osk/src/views/keyEventSource.interface.ts similarity index 63% rename from web/src/engine/events/src/keyEventSource.interface.ts rename to web/src/engine/osk/src/views/keyEventSource.interface.ts index 22621d2de0..36a065bd32 100644 --- a/web/src/engine/events/src/keyEventSource.interface.ts +++ b/web/src/engine/osk/src/views/keyEventSource.interface.ts @@ -1,7 +1,8 @@ import { EventEmitter } from "eventemitter3"; -import { type KeyEvent, type RuleBehaviorInterface } from "@keymanapp/keyboard-processor"; +import { type KeyEvent } from "@keymanapp/keyboard-processor"; +import { type RuleBehavior } from 'keyman/engine/js-processor'; -export type KeyEventResultCallback = (result: RuleBehaviorInterface, error?: Error) => void; +export type KeyEventResultCallback = (result: RuleBehavior, error?: Error) => void; export type KeyEventHandler = (event: KeyEvent, callback?: KeyEventResultCallback) => void; interface EventMap { diff --git a/web/src/engine/osk/src/views/oskView.ts b/web/src/engine/osk/src/views/oskView.ts index afdc99f882..09b3cbc827 100644 --- a/web/src/engine/osk/src/views/oskView.ts +++ b/web/src/engine/osk/src/views/oskView.ts @@ -16,16 +16,16 @@ import { Keyboard, KeyboardProperties, ManagedPromise, - type MinimalCodesInterface, - type MutableSystemStore, - type SystemStoreMutationHandler + type MinimalCodesInterface } from '@keymanapp/keyboard-processor'; import { createUnselectableElement, getAbsoluteX, getAbsoluteY, StylesheetManager } from 'keyman/engine/dom-utils'; -import { EventListener, KeyEventHandler, KeyEventSourceInterface, LegacyEventEmitter } from 'keyman/engine/events'; +import { EventListener, LegacyEventEmitter } from 'keyman/engine/events'; +import { type MutableSystemStore, type SystemStoreMutationHandler } from 'keyman/engine/js-processor'; import Configuration from '../config/viewConfiguration.js'; import Activator, { StaticActivator } from './activator.js'; import TouchEventPromiseMap from './touchEventPromiseMap.js'; +import { KeyEventHandler, KeyEventSourceInterface } from './keyEventSource.interface.js'; // These will likely be eliminated from THIS file at some point.\ diff --git a/web/src/engine/osk/src/visualKeyboard.ts b/web/src/engine/osk/src/visualKeyboard.ts index fac43d5125..1136e696ba 100644 --- a/web/src/engine/osk/src/visualKeyboard.ts +++ b/web/src/engine/osk/src/visualKeyboard.ts @@ -31,7 +31,7 @@ import { import { createStyleSheet, StylesheetManager } from 'keyman/engine/dom-utils'; -import { KeyEventHandler, KeyEventResultCallback } from 'keyman/engine/events'; +import { KeyEventHandler, KeyEventResultCallback } from './views/keyEventSource.interface.js'; import GlobeHint from './globehint.interface.js'; import KeyboardView from './components/keyboardView.interface.js';