diff --git a/.github/workflows/deb-packaging.yml b/.github/workflows/deb-packaging.yml
index 8c745bef3071cbfadd576e89610860a4fb7c0712..8495f5577a6617e31affd811a93b27038295aadb 100644
--- a/.github/workflows/deb-packaging.yml
+++ b/.github/workflows/deb-packaging.yml
@@ -119,8 +119,8 @@ jobs:
platform: "${{ matrix.arch }}"
source_dir: "artifacts/keyman-srcpkg"
sourcepackage: "keyman_${{ needs.sourcepackage.outputs.VERSION }}-1.dsc"
- deb_fullname: $DEBFULLNAME
- deb_email: $DEBEMAIL
+ deb_fullname: ${{env.DEBFULLNAME}}
+ deb_email: ${{env.DEBEMAIL}}
prerelease_tag: ${{ needs.sourcepackage.outputs.PRERELEASE_TAG }}
- name: Output resulting .deb files
diff --git a/HISTORY.md b/HISTORY.md
index c05c241eff96740ca9e41e287d1a851045aaf025..50d64334643faebc41b87d6c8af2d042dab78340 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,5 +1,37 @@
# Keyman Version History
+## 17.0.118 alpha 2023-06-05
+
+* chore(ios): replace fv cert (#8900)
+* fix(core): Fix compilation if hotdoc is installed (#8912)
+* feat(linux): Rename column and add tooltip (#8918)
+
+## 17.0.117 alpha 2023-06-04
+
+* chore(developer): verify long lines compile correctly (#8915)
+
+## 17.0.116 alpha 2023-06-02
+
+* feat(linux): Add column for installation location (#8897)
+* chore(developer): verify kvks files and report errors (#8892)
+* refactor(developer): rearrange kmcmplib interface source (#8899)
+* refactor(developer): move filename consistency check to kmc (#8907)
+* chore(developer): loadFile callback error check and optimization (#8908)
+* chore(common): remove url module ref from common/web/types (#8914)
+
+## 17.0.115 alpha 2023-06-01
+
+* refactor(developer): complete fs move out of kmcmplib (#8882)
+* fix(common): tweak pack/publish support for npm 9.5.1 and node 18.16.0 (#8894)
+* feat(linux): Implement Options page and option to disable Sentry error reporting (#7989)
+
+## 17.0.114 alpha 2023-05-31
+
+* chore(developer): replace cwrap wasm bindings (#8857)
+* chore(developer): refactor kmcmplib interfaces (#8870)
+* chore(developer): move keyboard repo fixtures (#8874)
+* chore(linux): Move build steps to build.sh (#8864)
+
## 17.0.113 alpha 2023-05-26
* docs(android): Update documentation for building Android on Linux (#8860)
diff --git a/VERSION.md b/VERSION.md
index a3a3dc809e3ff7ff42f204abcc79812de3a7b640..7d14d88a17924860f9933506ff2ae81ec0be6d1f 100644
--- a/VERSION.md
+++ b/VERSION.md
@@ -1 +1 @@
-17.0.114
\ No newline at end of file
+17.0.119
\ No newline at end of file
diff --git a/common/models/templates/build.sh b/common/models/templates/build.sh
index b5ae2a8c236991d95c7f3e41edf2543f947e3087..0fe305ca5ad6dbc337deb59dad00554b7e3406c5 100755
--- a/common/models/templates/build.sh
+++ b/common/models/templates/build.sh
@@ -18,8 +18,9 @@ cd "$(dirname "$THIS_SCRIPT")"
################################ Main script ################################
builder_describe "Builds the predictive-text model template implementation module" \
- "@../../web/keyman-version" \
- "@../wordbreakers" \
+ "@/common/web/keyman-version" \
+ "@/common/web/tslib" \
+ "@/common/models/wordbreakers" \
"clean" \
"configure" \
"build" \
diff --git a/common/models/templates/tsconfig.json b/common/models/templates/tsconfig.json
index 9284bddd209ed1961257b57f5a69802315727412..7ff603ef29d85f26f774ea751fa7dfd2030b507a 100644
--- a/common/models/templates/tsconfig.json
+++ b/common/models/templates/tsconfig.json
@@ -7,6 +7,7 @@
"moduleResolution": "node16",
"sourceMap": true,
"sourceRoot": "/common/models/templates/src",
+ "importHelpers": true,
"inlineSources": true,
"strict": true,
"lib": ["es6"],
diff --git a/common/models/wordbreakers/src/default/index.ts b/common/models/wordbreakers/src/default/index.ts
index 65dbd0faf00e4aca401e7d2f808b79a2a89f3de2..20e159ed1988e81ae47cd07300ee56307c38f364 100644
--- a/common/models/wordbreakers/src/default/index.ts
+++ b/common/models/wordbreakers/src/default/index.ts
@@ -431,7 +431,7 @@ function findBoundaries(text: string, options?: DefaultWordBreakerOptions): numb
}
// Do not break across certain punctuation
// WB6: (Don't break before apostrophes in contractions)
- const SET_ALL_MIDLETTER = [WordBreakProperty.MidLetter, ...SET_MIDNUMLETQ];
+ const SET_ALL_MIDLETTER = [WordBreakProperty.MidLetter].concat(SET_MIDNUMLETQ);
if(state.match(null, SET_AHLETTER, SET_ALL_MIDLETTER, SET_AHLETTER)) {
continue;
}
@@ -474,7 +474,7 @@ function findBoundaries(text: string, options?: DefaultWordBreakerOptions): numb
}
// Do not break within sequences, such as 3.2, 3,456.789
// WB11
- const SET_ALL_MIDNUM = [WordBreakProperty.MidNum, ...SET_MIDNUMLETQ];
+ const SET_ALL_MIDNUM = [WordBreakProperty.MidNum].concat(SET_MIDNUMLETQ);
if(state.match([WordBreakProperty.Numeric], SET_ALL_MIDNUM, [WordBreakProperty.Numeric], null)) {
continue;
}
@@ -488,9 +488,7 @@ function findBoundaries(text: string, options?: DefaultWordBreakerOptions): numb
}
// Do not break from extenders (e.g., U+202F NARROW NO-BREAK SPACE)
// WB13a
- const SET_NUM_KAT_LET = [WordBreakProperty.Katakana,
- WordBreakProperty.Numeric,
- ...SET_AHLETTER];
+ const SET_NUM_KAT_LET = [WordBreakProperty.Katakana, WordBreakProperty.Numeric].concat(SET_AHLETTER);
if(state.match(null, SET_NUM_KAT_LET, [WordBreakProperty.ExtendNumLet], null)) {
continue;
}
diff --git a/common/models/wordbreakers/tsconfig.json b/common/models/wordbreakers/tsconfig.json
index 6c029da40af640ef26854455c45f800dc263a4de..549b7410d6e99f994c68f7b8590adc3a0c9a1cd9 100644
--- a/common/models/wordbreakers/tsconfig.json
+++ b/common/models/wordbreakers/tsconfig.json
@@ -8,6 +8,7 @@
"moduleResolution": "Node16",
"declaration": true,
"sourceMap": true,
+ "importHelpers": true,
"inlineSources": true,
"sourceRoot": "/common/models/wordbreakers/src",
"strict": true,
diff --git a/common/predictive-text/build-bundler.js b/common/predictive-text/build-bundler.js
index 2b2bff8713af7a23c451b2c5e8ecb7ffdb543ddd..a666e008fd6270fb454670f49027b07cb74acb9c 100644
--- a/common/predictive-text/build-bundler.js
+++ b/common/predictive-text/build-bundler.js
@@ -9,6 +9,9 @@ import esbuild from 'esbuild';
import { spawn } from 'child_process';
await esbuild.build({
+ alias: {
+ 'tslib': '@keymanapp/tslib'
+ },
bundle: true,
sourcemap: true,
format: "esm",
diff --git a/common/predictive-text/build.sh b/common/predictive-text/build.sh
index 56680781f82e5ee69edc250dd5667a1edd518ee6..4fe5a744cb5fc87e126955f9cf2e8900cf8c0bd7 100755
--- a/common/predictive-text/build.sh
+++ b/common/predictive-text/build.sh
@@ -26,6 +26,7 @@ cd "$(dirname "$THIS_SCRIPT")"
builder_describe "Builds the lm-layer module" \
"@/common/web/keyman-version" \
+ "@/common/web/tslib" \
"@/common/web/lm-worker" \
"clean" \
"configure" \
diff --git a/common/predictive-text/src/node/mappedWorker.ts b/common/predictive-text/src/node/mappedWorker.ts
new file mode 100644
index 0000000000000000000000000000000000000000..aafbba52323d6f516f40d867b449cd5e07377aac
--- /dev/null
+++ b/common/predictive-text/src/node/mappedWorker.ts
@@ -0,0 +1,88 @@
+// We use a subset of the Worker interface here; compiling directly against the true
+// WebWorker type definitions would require us to implement more methods than we do.
+///
+
+// Defines types related to Node workers.
+import * as worker from 'worker_threads';
+import { Buffer } from 'buffer';
+import { URL } from 'url';
+
+/**
+ * Defines mappings from Node Worker signatures to WebWorker signatures
+ */
+const nodeWorkerToWebWorkerMappingSource = `
+import { parentPort } from 'worker_threads';
+import fs from 'fs';
+import vm from 'vm';
+
+function postMessage(...args) {
+ parentPort.postMessage.call(parentPort, args);
+}
+
+parentPort.on('message', (ev) => {
+ onmessage({data: ev});
+});
+
+function importScripts(...args) {
+ function loadScriptInContext(scriptPath) {
+ let scriptStr = fs.readFileSync(scriptPath);
+ var script = new vm.Script(scriptStr, { filename: scriptPath });
+ script.runInThisContext();
+ }
+
+ for(let arg of args) {
+ loadScriptInContext(arg);
+ }
+}
+
+/*
+ * You'd think the method signature mapping would be implied from the first line,
+ * but all three lines must be explicitly specified or the emulation will fail.
+ */
+const self = globalThis;
+self.postMessage = postMessage;
+self.importScripts = importScripts;
+`;
+
+/**
+ * Uses the Node version of Workers to provide proper, authentic separate-thread
+ * 'sandboxing'. Also intercepts and interprets certain WebWorker method signatures
+ * necessary to run the WebWorker-oriented worker code.
+ *
+ * Alternatively, only after writing this did I discover this package:
+ * https://github.com/developit/web-worker. They also ran one notable issue I did:
+ * Node 18.x, at least, does not support use of Node Blobs for construction of a
+ * Worker: https://github.com/developit/web-worker/pull/32... unlike Web Workers.
+ *
+ * So... Base64-encoded Data URLs it is.
+ *
+ * What we have here is perfectly fine for now, but if we need more complicated
+ * cross-platform Worker support in the future, it may be wise to swap to use of
+ * that package.
+ */
+export default class MappedWorker extends worker.Worker implements Worker {
+ constructor(scriptStr: string) {
+ const concatenatedScript = `
+ ${nodeWorkerToWebWorkerMappingSource}
+
+ ${scriptStr}
+ `;
+ const buffer = Buffer.from(concatenatedScript);
+ const dataSrc = "data:text/javascript;base64," + buffer.toString('base64');
+ //@ts-ignore
+ super(new URL(dataSrc));
+
+ // WebWorkers have a defined `onmessage` function, rather than this.on('message', ...)
+ this.on('message', (ev) => {
+ if(this.onmessage) {
+ this.onmessage({data: ev[0]});
+ }
+ });
+ }
+
+ /**
+ * Accepts a callback function that will receive messages sent from the `VirtualizedWorker`'s `postMessage` function,
+ * much like the standard `Worker.onmessage`.
+ */
+ onmessage: (this: Worker, ev: MessageEvent) => any;
+}
\ No newline at end of file
diff --git a/common/predictive-text/src/node/sourcemappedWorker.ts b/common/predictive-text/src/node/sourcemappedWorker.ts
index 5fd8bb9ec4266db97c699db2256643e54e5cff1c..aaaf79e0f6fcc1f1d42207dd767b4663f4f09e95 100644
--- a/common/predictive-text/src/node/sourcemappedWorker.ts
+++ b/common/predictive-text/src/node/sourcemappedWorker.ts
@@ -1,4 +1,4 @@
-import VirtualizedWorker from "./virtualizedWorker.js";
+import MappedWorker from "./mappedWorker.js";
import unwrap from '../unwrap.js';
import { LMLayerWorkerCode, LMLayerWorkerSourcemapComment } from "@keymanapp/lm-worker/worker-main.wrapped.js";
@@ -12,7 +12,7 @@ export default class SourcemappedWorker {
// if(false) {
scriptStr += '\n' + LMLayerWorkerSourcemapComment;
// }
- let worker = new VirtualizedWorker(scriptStr);
+ let worker = new MappedWorker(scriptStr);
return worker as any as Worker;
}
diff --git a/common/predictive-text/src/node/tsconfig.json b/common/predictive-text/src/node/tsconfig.json
index b9567ad113e7744f3abed6efeee318de75fa632e..dacf350335ead81ed5db44aea20fa1d1277d73d5 100644
--- a/common/predictive-text/src/node/tsconfig.json
+++ b/common/predictive-text/src/node/tsconfig.json
@@ -5,10 +5,13 @@
"declaration": true,
"module": "es6",
"moduleResolution": "Node16",
+ "importHelpers": true,
"inlineSources": true,
"sourceMap": true,
"sourceRoot": "keyman",
- "target": "es5",
+ // This build-configuration only targets Node and exists solely for unit tests.
+ // Also, we need ES6 in order to properly inherit from the Node Worker-type.
+ "target": "ES6",
"lib": ["es6"],
"types": ["node"],
"baseUrl": "../",
diff --git a/common/predictive-text/src/node/virtualizedWorker.ts b/common/predictive-text/src/node/virtualizedWorker.ts
deleted file mode 100644
index f32d265c9982d880c5830267c98ee8da32b7cb0f..0000000000000000000000000000000000000000
--- a/common/predictive-text/src/node/virtualizedWorker.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-// We use a subset of the Worker interface here; compiling directly against the true
-// WebWorker type definitions would require us to implement more methods than we do.
-///
-
-import * as fs from 'fs';
-import * as vm from 'vm';
-
-class VirtualizedWorkerContext {
- // The LMLayerWorker installs itself to 'self', the expected Worker global, so we provide an alias.
- self: VirtualizedWorkerContext;
-
- constructor() {
- this.self = this;
- }
-
- postMessage: (message: any) => void;
-
- importScripts(...scriptNames: string[]) {
- /* Use of vm.createContext and script.runInContext allow us to avoid
- * polluting the global scope with imports. When we throw away the
- * context object, imported scripts will be automatically GC'd.
- */
- for(let script of scriptNames) {
- this.__importScriptString(fs.readFileSync(script, "UTF-8"));
- }
- }
-
- __importScriptString(scriptStr: string) {
- let context = vm.createContext(this);
- var script = new vm.Script(scriptStr);
- script.runInContext(context);
- }
-}
-
-/**
- * Note: this does not create an actual Worker, separate process, or thread. Everything will
- * be executed in-line on a virtualized context.
- *
- * In the future, it might be nice to use Node's Worker Threads implementation.
- */
-export default class VirtualizedWorker implements Worker {
- private _workerContext: VirtualizedWorkerContext;
-
- constructor(scriptStr: string) {
- this._workerContext = new VirtualizedWorkerContext();
- // Needs to exist before setting up the worker; must exist by `.install()`.
- this._workerContext.postMessage = this.workerPostMessage.bind(this);
-
- // Initialize the "worker".
- this._workerContext.__importScriptString(scriptStr);
- }
-
- // Sends the worker's postMessage messages to the appropriate `onmessage` handler.
- private workerPostMessage(message: unknown) {
- if(this.onmessage) {
- this.onmessage({data: message} as any as MessageEvent);
- }
- }
-
- /**
- * Accepts a callback function that will receive messages sent from the `VirtualizedWorker`'s `postMessage` function,
- * much like the standard `Worker.onmessage`.
- */
- onmessage: (this: Worker, ev: MessageEvent) => any;
-
- postMessage(message: any) {
- let msgObj = {data: message};
- let msgJSON = JSON.stringify(msgObj);
-
- /*
- * Execute the command within the virtualized worker's scope. The worker's returned
- * `postMessage` calls will still reach outside, as they have a reference to `this` via
- * `postMessage` (which we've set to a bound `this.workerPostMessage`).
- *
- * Among other things, this will allow the worker to use its internal namespaces without issue.
- */
- let msgCommand = "onmessage(" + msgJSON + ")";
- this._workerContext.__importScriptString(msgCommand);
- }
-
- terminate(): void {
- this._workerContext = null;
- }
-}
\ No newline at end of file
diff --git a/common/predictive-text/src/node/worker.ts b/common/predictive-text/src/node/worker.ts
index 68f14b587bd6b34d6114b3fdebecf8b9d4c6a8c9..5ad04e55d0be08b11b34073ef60d72b1e25981b7 100644
--- a/common/predictive-text/src/node/worker.ts
+++ b/common/predictive-text/src/node/worker.ts
@@ -1,4 +1,4 @@
-import VirtualizedWorker from "./virtualizedWorker.js";
+import MappedWorker from "./mappedWorker.js";
import unwrap from '../unwrap.js';
import { LMLayerWorkerCode, LMLayerWorkerSourcemapComment } from "@keymanapp/lm-worker/worker-main.wrapped.min.js";
@@ -8,7 +8,7 @@ export default class Worker {
let scriptStr = unwrap(LMLayerWorkerCode);
scriptStr += '\n' + LMLayerWorkerSourcemapComment;
- let worker = new VirtualizedWorker(scriptStr);
+ let worker = new MappedWorker(scriptStr);
return worker as any as Worker;
}
diff --git a/common/predictive-text/src/tsconfig.json b/common/predictive-text/src/tsconfig.json
index 40fb9f45cd2e24632faba3b8fc1fea112c8a69da..f6fe671043ff00b8c01edca82b3eefbdcbbda656 100644
--- a/common/predictive-text/src/tsconfig.json
+++ b/common/predictive-text/src/tsconfig.json
@@ -6,6 +6,7 @@
"declaration": true,
"module": "es6",
"moduleResolution": "Node16",
+ "importHelpers": true,
"inlineSources": true,
"sourceMap": true,
"sourceRoot": "keyman",
diff --git a/common/predictive-text/src/unwrap.ts b/common/predictive-text/src/unwrap.ts
index 410c9db718fdf12dfeec84a75f2332e71c4721fd..fc0769e5bd112e3738169f0cc9de63bcf8b920af 100644
--- a/common/predictive-text/src/unwrap.ts
+++ b/common/predictive-text/src/unwrap.ts
@@ -6,6 +6,6 @@
* @param fn The function whose body will be returned.
*/
export default function unwrap(encodedSrc: string): string {
- let wrapper = decodeURIComponent(encodedSrc);
- return wrapper;
+ // There used to be more to this, but now it's a pretty simple passthrough!
+ return encodedSrc;
}
\ No newline at end of file
diff --git a/common/predictive-text/src/web/tsconfig.json b/common/predictive-text/src/web/tsconfig.json
index 9e1f43978e58f4d4973c89c2c8b5d1d840fdd995..bcb94fca1060674a915cf39762dd09712f8c92dd 100644
--- a/common/predictive-text/src/web/tsconfig.json
+++ b/common/predictive-text/src/web/tsconfig.json
@@ -5,6 +5,7 @@
"declaration": true,
"module": "es6",
"moduleResolution": "Node16",
+ "importHelpers": true,
"inlineSources": true,
"sourceMap": true,
"sourceRoot": "keyman",
diff --git a/common/predictive-text/tsconfig.all.json b/common/predictive-text/tsconfig.all.json
index 33fe749e21e92ef1a461414e20ac5e3635e4b21d..5cb3fdf96bfec7ce985c0b17641328dd897a747e 100644
--- a/common/predictive-text/tsconfig.all.json
+++ b/common/predictive-text/tsconfig.all.json
@@ -10,6 +10,7 @@
"declaration": true,
"module": "es6",
"moduleResolution": "Node16",
+ "importHelpers": true,
"inlineSources": true,
"sourceMap": true,
"sourceRoot": "keyman",
diff --git a/common/predictive-text/unit_tests/headless/worker-dummy-integration.js b/common/predictive-text/unit_tests/headless/worker-dummy-integration.js
index 6e6adb426f5116aa0a1960945b762466d3fd4f60..d546edb70403a4e4e6e1a11f126f3de3d41a2197 100644
--- a/common/predictive-text/unit_tests/headless/worker-dummy-integration.js
+++ b/common/predictive-text/unit_tests/headless/worker-dummy-integration.js
@@ -16,10 +16,23 @@ import { capabilities, iGotDistractedByHazel } from '@keymanapp/common-test-reso
* of suggestions when loaded and return them sequentially.
*/
describe('LMLayer using dummy model', function () {
+ let lmLayer;
+ let worker;
+
+ beforeEach(function() {
+ worker = Worker.constructInstance();
+ lmLayer = new LMLayer(capabilities(), worker);
+ });
+
+ afterEach(function () {
+ // As we're using Node worker threads here, failure to terminate them will cause the
+ // headless test run to hang after completion.
+ lmLayer.shutdown();
+ worker.terminate(); // should be covered by the former, but just in case... for CI stability.
+ });
+
describe('Prediction', function () {
it('will predict future suggestions (loaded from file)', function () {
- var lmLayer = new LMLayer(capabilities(), Worker.constructInstance());
-
var stripIDs = function(suggestions) {
suggestions.forEach(function(suggestion) {
delete suggestion.id;
@@ -51,14 +64,11 @@ describe('LMLayer using dummy model', function () {
}).then(function (suggestions) {
stripIDs(suggestions);
assert.deepEqual(suggestions, iGotDistractedByHazel()[3]);
- lmLayer.shutdown();
return Promise.resolve();
});
});
it('will predict future suggestions (loaded from raw source)', function () {
- var lmLayer = new LMLayer(capabilities(), Worker.constructInstance());
-
var stripIDs = function(suggestions) {
suggestions.forEach(function(suggestion) {
delete suggestion.id;
@@ -92,7 +102,6 @@ describe('LMLayer using dummy model', function () {
}).then(function (suggestions) {
stripIDs(suggestions);
assert.deepEqual(suggestions, iGotDistractedByHazel()[3]);
- lmLayer.shutdown();
return Promise.resolve();
});
});
@@ -100,8 +109,6 @@ describe('LMLayer using dummy model', function () {
describe('Wordbreaking', function () {
it('will perform (default) wordbreaking and return word at caret', function () {
- var lmLayer = new LMLayer(capabilities(), Worker.constructInstance());
-
// We're testing many as asynchronous messages in a row.
// this would be cleaner using async/await syntax.
// Not done yet, as this test case is a slightly-edited copy of the in-browser version.
diff --git a/common/predictive-text/unit_tests/headless/worker-trie-integration.js b/common/predictive-text/unit_tests/headless/worker-trie-integration.js
index 88ae133097806f4d19d365de820312cdf4466b7e..8cd257878d1491e56937ba5f01bbcb41267343a9 100644
--- a/common/predictive-text/unit_tests/headless/worker-trie-integration.js
+++ b/common/predictive-text/unit_tests/headless/worker-trie-integration.js
@@ -10,11 +10,24 @@ import { capabilities } from '@keymanapp/common-test-resources/model-helpers.mjs
* How to run the worlist
*/
describe('LMLayer using the trie model', function () {
+ let lmLayer;
+ let worker;
+
+ beforeEach(function() {
+ worker = Worker.constructInstance();
+ lmLayer = new LMLayer(capabilities(), worker);
+ });
+
+ afterEach(function () {
+ // As we're using Node worker threads here, failure to terminate them will cause the
+ // headless test run to hang after completion.
+ lmLayer.shutdown();
+ worker.terminate(); // should be covered by the former, but just in case... for CI stability.
+ });
+
describe('Prediction', function () {
var EXPECTED_SUGGESTIONS = 3;
it('will predict an empty buffer', function () {
- var lmLayer = new LMLayer(capabilities(), Worker.constructInstance());
-
// We're testing many as asynchronous messages in a row.
// this would be cleaner using async/await syntax.
// Not done yet, as this test case is a slightly-edited copy of the in-browser version.
@@ -55,8 +68,6 @@ describe('LMLayer using the trie model', function () {
//
// https://community.software.sil.org/t/search-term-to-key-in-lexical-model-not-working-both-ways-by-default/3133
it('should use the default searchTermToKey()', function () {
- var lmLayer = new LMLayer(capabilities(), Worker.constructInstance());
-
return lmLayer.loadModel(
// We're running headlessly, so the path can be relative to the npm root directory.
require.resolve("@keymanapp/common-test-resources/models/naive-trie.js")
diff --git a/common/predictive-text/unit_tests/in_browser/manual.conf.cjs b/common/predictive-text/unit_tests/in_browser/manual.conf.cjs
index 9f9463447334d0fb0b9bd31ac07520945fdc720e..738f2b7713d597b9cf047fc53af14c9aba5d3d36 100644
--- a/common/predictive-text/unit_tests/in_browser/manual.conf.cjs
+++ b/common/predictive-text/unit_tests/in_browser/manual.conf.cjs
@@ -19,7 +19,7 @@ module.exports = function(config) {
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
- // Can't set in base.conf.js b/c of constant's definition style.
+ // Can't set in base.conf.cjs b/c of constant's definition style.
logLevel: config.LOG_INFO
};
diff --git a/common/test/resources/json/engine_tests/chirality.json b/common/test/resources/json/engine_tests/chirality.json
index f46ab5399ddb4f94005f7245f063d929fe907a3b..4f3153c2758fc2fc484e405068397e1dba6a8972 100644
--- a/common/test/resources/json/engine_tests/chirality.json
+++ b/common/test/resources/json/engine_tests/chirality.json
@@ -4,7 +4,13 @@
"id": "test_chirality",
"name": "KeymanWeb Chirality Tester",
"filename": "resources/keyboards/test_chirality.js",
- "languages": {}
+ "languages": [
+ {
+ "id": "en",
+ "name": "English",
+ "region": "World"
+ }
+ ]
},
"inputTestSets": [
{
diff --git a/common/test/resources/json/engine_tests/deadkeys.json b/common/test/resources/json/engine_tests/deadkeys.json
index 1764915605bda0c7fe2b229d4dc992d69265f233..aec422f0cc6e23a6b91760a68ec43b696ce3e849 100644
--- a/common/test/resources/json/engine_tests/deadkeys.json
+++ b/common/test/resources/json/engine_tests/deadkeys.json
@@ -4,7 +4,13 @@
"id": "test_deadkeys",
"name": "Keyman Deadkey Stress-Tester",
"filename": "resources/keyboards/test_deadkeys.js",
- "languages": {}
+ "languages": [
+ {
+ "id": "en",
+ "name": "English",
+ "region": "World"
+ }
+ ]
},
"inputTestSets": [
{
diff --git a/common/web/input-processor/build-bundler.js b/common/web/input-processor/build-bundler.js
index 34614fa6808e787c9d6074644c27937b573b9171..f16b207454a72af0866e5f8e88021e0dba4d065d 100644
--- a/common/web/input-processor/build-bundler.js
+++ b/common/web/input-processor/build-bundler.js
@@ -11,6 +11,9 @@ import { spawn } from 'child_process';
// Bundled ES module version
esbuild.buildSync({
entryPoints: ['build/obj/index.js'],
+ alias: {
+ 'tslib': '@keymanapp/tslib'
+ },
bundle: true,
sourcemap: true,
external: ['fs', 'vm'],
@@ -29,6 +32,9 @@ esbuild.buildSync({
// Bundled CommonJS (classic Node) module version
esbuild.buildSync({
entryPoints: ['build/obj/index.js'],
+ alias: {
+ 'tslib': '@keymanapp/tslib'
+ },
bundle: true,
sourcemap: true,
external: ['fs', 'vm'],
diff --git a/common/web/input-processor/src/text/prediction/languageProcessor.ts b/common/web/input-processor/src/text/prediction/languageProcessor.ts
index 8aea39eedc65a588db047c251626689b4c307362..58c6e4299327f3d7f1217c15b83c000993097261 100644
--- a/common/web/input-processor/src/text/prediction/languageProcessor.ts
+++ b/common/web/input-processor/src/text/prediction/languageProcessor.ts
@@ -80,11 +80,10 @@ export default class LanguageProcessor extends EventEmitter`; else, `null`.
+ * @returns if `suggestion` is a `Suggestion`, will return a `Promise`; else, `null`.
*/
public accept(suggestion: Suggestion): Promise | null {
let _this = this;
diff --git a/common/web/input-processor/tests/cases/inputProcessor.js b/common/web/input-processor/tests/cases/inputProcessor.js
index f3f3ca6b04d8ec56c15492c15bf3bacb62e6bb60..a2ba363edcec7e47b4910fef83f0aae5adcf100d 100644
--- a/common/web/input-processor/tests/cases/inputProcessor.js
+++ b/common/web/input-processor/tests/cases/inputProcessor.js
@@ -35,26 +35,32 @@ describe('InputProcessor', function() {
it('has expected default values after initialization', function () {
// Can construct without the second parameter; if so, the final assertion - .mayPredict
// will be invalidated. (No worker, no ability to predict.)
- let core = new InputProcessor(device, Worker.constructInstance());
-
- assert.isOk(core.keyboardProcessor);
- assert.isDefined(core.keyboardProcessor.contextDevice);
- assert.isOk(core.languageProcessor);
- assert.isOk(core.keyboardInterface);
- assert.isUndefined(core.activeKeyboard); // No keyboard should be loaded yet.
- assert.isUndefined(core.activeModel); // Same for the model.
-
- // These checks are lifted from the keyboard-processor init checks found in
- // common/web/keyboard-processor/tests/cases/basic-init.js.
- assert.equal('us', core.keyboardProcessor.baseLayout, 'KeyboardProcessor has unexpected base layout')
- assert.isNotNull(global.KeymanWeb, 'KeymanWeb global was not automatically installed');
- assert.equal('default', core.keyboardProcessor.layerId, 'Default layer is not set to "default"');
- assert.isUndefined(core.keyboardProcessor.activeKeyboard, 'Initialized with already-active keyboard');
-
- // Lifted from languageProcessor.js - the core should not be changing these with its init.
- assert.isUndefined(core.languageProcessor.activeModel);
- assert.isFalse(core.languageProcessor.isActive);
- assert.isTrue(core.languageProcessor.mayPredict);
+ let worker = Worker.constructInstance();
+
+ try {
+ let core = new InputProcessor(device, worker);
+
+ assert.isOk(core.keyboardProcessor);
+ assert.isDefined(core.keyboardProcessor.contextDevice);
+ assert.isOk(core.languageProcessor);
+ assert.isOk(core.keyboardInterface);
+ assert.isUndefined(core.activeKeyboard); // No keyboard should be loaded yet.
+ assert.isUndefined(core.activeModel); // Same for the model.
+
+ // These checks are lifted from the keyboard-processor init checks found in
+ // common/web/keyboard-processor/tests/cases/basic-init.js.
+ assert.equal('us', core.keyboardProcessor.baseLayout, 'KeyboardProcessor has unexpected base layout')
+ assert.isNotNull(global.KeymanWeb, 'KeymanWeb global was not automatically installed');
+ assert.equal('default', core.keyboardProcessor.layerId, 'Default layer is not set to "default"');
+ assert.isUndefined(core.keyboardProcessor.activeKeyboard, 'Initialized with already-active keyboard');
+
+ // Lifted from languageProcessor.js - the core should not be changing these with its init.
+ assert.isUndefined(core.languageProcessor.activeModel);
+ assert.isFalse(core.languageProcessor.isActive);
+ assert.isTrue(core.languageProcessor.mayPredict);
+ } finally {
+ worker.terminate();
+ }
});
});
diff --git a/common/web/input-processor/tests/cases/languageProcessor.js b/common/web/input-processor/tests/cases/languageProcessor.js
index 06c017b426f90eeec3afa79cee25c066a5310a36..f1934a5f6eea6063cae4c85f53d80343943bcb78 100644
--- a/common/web/input-processor/tests/cases/languageProcessor.js
+++ b/common/web/input-processor/tests/cases/languageProcessor.js
@@ -21,14 +21,24 @@ String.kmwEnableSupplementaryPlane(false);
// Test the KeyboardProcessor interface.
describe('LanguageProcessor', function() {
+ let worker;
+
+ beforeEach(function() {
+ worker = LMWorker.constructInstance();
+ });
+
+ afterEach(function() {
+ worker.terminate();
+ });
+
describe('[[constructor]]', function () {
it('should initialize without errors', function () {
- let lp = new LanguageProcessor(LMWorker.constructInstance());
+ let lp = new LanguageProcessor(worker);
assert.isNotNull(lp);
});
it('has expected default values after initialization', function () {
- let languageProcessor = new LanguageProcessor(LMWorker.constructInstance());
+ let languageProcessor = new LanguageProcessor(worker);
// These checks are lifted from the keyboard-processor init checks found in
// common/web/keyboard-processor/tests/cases/basic-init.js.
@@ -70,7 +80,7 @@ describe('LanguageProcessor', function() {
};
it("successfully loads the model", function(done) {
- let languageProcessor = new LanguageProcessor(LMWorker.constructInstance());
+ let languageProcessor = new LanguageProcessor(worker);
languageProcessor.loadModel(modelSpec).then(function() {
assert.isOk(languageProcessor.activeModel); // is only set after a successful load.
@@ -81,7 +91,7 @@ describe('LanguageProcessor', function() {
});
it("generates the expected prediction set", function(done) {
- let languageProcessor = new LanguageProcessor(LMWorker.constructInstance());
+ let languageProcessor = new LanguageProcessor(worker);
let contextSource = new Mock("li", 2);
let transcription = contextSource.buildTranscriptionFrom(contextSource, null, null);
@@ -117,7 +127,7 @@ describe('LanguageProcessor', function() {
describe("does not alter casing when input is lowercased", function() {
it("when input is fully lowercased", function(done) {
- let languageProcessor = new LanguageProcessor(LMWorker.constructInstance());
+ let languageProcessor = new LanguageProcessor(worker);
let contextSource = new Mock("li", 2);
let transcription = contextSource.buildTranscriptionFrom(contextSource, null, null);
@@ -136,7 +146,7 @@ describe('LanguageProcessor', function() {
});
it("when input has non-initial uppercased letters", function(done) {
- let languageProcessor = new LanguageProcessor(LMWorker.constructInstance());
+ let languageProcessor = new LanguageProcessor(worker);
let contextSource = new Mock("lI", 2);
let transcription = contextSource.buildTranscriptionFrom(contextSource, null, null);
@@ -156,7 +166,7 @@ describe('LanguageProcessor', function() {
});
it("unless the suggestion has uppercased letters", function(done) {
- let languageProcessor = new LanguageProcessor(LMWorker.constructInstance());
+ let languageProcessor = new LanguageProcessor(worker);
let contextSource = new Mock("i", 1);
let transcription = contextSource.buildTranscriptionFrom(contextSource, null, null);
@@ -177,7 +187,7 @@ describe('LanguageProcessor', function() {
describe("uppercases suggestions when input is fully capitalized ", function() {
it("for suggestions with default casing (== 'lower')", function(done) {
- let languageProcessor = new LanguageProcessor(LMWorker.constructInstance());
+ let languageProcessor = new LanguageProcessor(worker);
let contextSource = new Mock("LI", 2);
let transcription = contextSource.buildTranscriptionFrom(contextSource, null, null);
@@ -197,7 +207,7 @@ describe('LanguageProcessor', function() {
});
it("for precapitalized suggestions", function(done) {
- let languageProcessor = new LanguageProcessor(LMWorker.constructInstance());
+ let languageProcessor = new LanguageProcessor(worker);
let contextSource = new Mock("I", 1);
let transcription = contextSource.buildTranscriptionFrom(contextSource, null, null);
@@ -219,7 +229,7 @@ describe('LanguageProcessor', function() {
describe("initial-cases suggestions when input uses initial casing ", function() {
describe("when input is a single capitalized letter", function() {
it("for suggestions with default casing (== 'lower')", function(done) {
- let languageProcessor = new LanguageProcessor(LMWorker.constructInstance());
+ let languageProcessor = new LanguageProcessor(worker);
let contextSource = new Mock("L", 1);
let transcription = contextSource.buildTranscriptionFrom(contextSource, null, null);
@@ -241,7 +251,7 @@ describe('LanguageProcessor', function() {
describe("input length > 1", function() {
it("for suggestions with default casing (== 'lower')", function(done) {
- let languageProcessor = new LanguageProcessor(LMWorker.constructInstance());
+ let languageProcessor = new LanguageProcessor(worker);
let contextSource = new Mock("Li", 2);
let transcription = contextSource.buildTranscriptionFrom(contextSource, null, null);
diff --git a/common/web/input-processor/tests/cases/predictionContext.js b/common/web/input-processor/tests/cases/predictionContext.js
index 3db0e89b584e19f9323f2f3a5ec4c2e26c5c665f..fd03dda639e20e30b8f7dee40aed18c6aef1e11b 100644
--- a/common/web/input-processor/tests/cases/predictionContext.js
+++ b/common/web/input-processor/tests/cases/predictionContext.js
@@ -66,8 +66,18 @@ const appleDummyModel = {
};
describe("PredictionContext", () => {
- it('receives predictions as they are generated', async () => {
- const langProcessor = new LanguageProcessor(LMWorker.constructInstance());
+ let worker;
+
+ beforeEach(function() {
+ worker = LMWorker.constructInstance();
+ });
+
+ afterEach(function() {
+ worker.terminate();
+ });
+
+ it('receives predictions as they are generated', async function () {
+ const langProcessor = new LanguageProcessor(worker);
await langProcessor.loadModel(appleDummyModel); // await: must fully 'configure', load script into worker.
const kbdProcessor = new KeyboardProcessor(deviceSpec);
@@ -107,8 +117,8 @@ describe("PredictionContext", () => {
assert.equal(suggestions.find((obj) => obj.transform.deleteLeft != 0).displayAs, 'apps');
});
- it('sendUpdateState retrieves the most recent suggestion set', async () => {
- const langProcessor = new LanguageProcessor(LMWorker.constructInstance());
+ it('sendUpdateState retrieves the most recent suggestion set', async function() {
+ const langProcessor = new LanguageProcessor(worker);
await langProcessor.loadModel(appleDummyModel); // await: must fully 'configure', load script into worker.
const kbdProcessor = new KeyboardProcessor(deviceSpec);
@@ -133,8 +143,8 @@ describe("PredictionContext", () => {
assert.sameOrderedMembers(suggestions, initialSuggestions);
});
- it('suggestion application logic & triggered effects', async () => {
- const langProcessor = new LanguageProcessor(LMWorker.constructInstance());
+ it('suggestion application logic & triggered effects', async function () {
+ const langProcessor = new LanguageProcessor(worker);
await langProcessor.loadModel(appleDummyModel); // await: must fully 'configure', load script into worker.
const kbdProcessor = new KeyboardProcessor(deviceSpec);
@@ -167,6 +177,15 @@ describe("PredictionContext", () => {
const suggestionApply = suggestions.find((obj) => obj.displayAs == 'apply');
assert.isOk(suggestionApply);
+ // For awaiting the suggestions generated upon applying our desired suggestion.
+ // We aren't given a direct Promise for that, but we can construct one this way.
+ let postApplySuggestions = new Promise((resolve) => {
+ predictiveContext.once('update', resolve);
+ });
+
+ // Apply the desired suggestion. Also passively generates new, post-acceptance
+ // suggestions, but this function itself don't provide a Promise for that...
+ // hence the previous block.
let promiseForApplyReversion = predictiveContext.accept(suggestionApply);
assert.equal(updateFake.callCount, 1); // No new 'update' has been raised yet.
@@ -177,8 +196,7 @@ describe("PredictionContext", () => {
assert.equal(textState.getText(), 'apply ');
let reversion = await promiseForApplyReversion;
- // We don't seem to need to additionally rig a wait for the triggered predict call; it
- // always completes first. Neat. If test becomes unstable... yeah, rig up a wait for it.
+ await postApplySuggestions;
// Check 2: a second 'update' - post-application predictions!
assert.equal(updateFake.callCount, 2);
@@ -193,8 +211,8 @@ describe("PredictionContext", () => {
// All other reversion details are tested in the 'reversion application logic...' section defined below.
});
- it('reversion application logic & triggered effects', async () => {
- const langProcessor = new LanguageProcessor(LMWorker.constructInstance());
+ it('reversion application logic & triggered effects', async function () {
+ const langProcessor = new LanguageProcessor(worker);
await langProcessor.loadModel(appleDummyModel); // await: must fully 'configure', load script into worker.
const kbdProcessor = new KeyboardProcessor(deviceSpec);
@@ -222,7 +240,15 @@ describe("PredictionContext", () => {
assert.isOk(suggestionApply);
let previousTextState = Mock.from(textState);
+
+ // For awaiting the suggestions generated upon applying our desired suggestion.
+ // We aren't given a direct Promise for that, but we can construct one this way.
+ let postApplySuggestions = new Promise((resolve) => {
+ predictiveContext.once('update', resolve);
+ });
+
let reversion = await predictiveContext.accept(suggestionApply);
+ await postApplySuggestions;
// Test setup complete.
@@ -243,6 +269,19 @@ describe("PredictionContext", () => {
// Fire away! Time to apply the reversion.
previousTextState = Mock.from(textState);
+
+ // Since the test uses a separate thread via Worker, make sure to set up any important event handlers
+ // before we request the reversion.
+ let updateFake = sinon.fake();
+ predictiveContext.on('update', updateFake);
+
+ // Now, in order to synchronize... we rely on a Promise. The callback is indeed
+ // called synchronously.
+ let postRevertSuggestions = new Promise((resolve) => {
+ predictiveContext.once('update', resolve);
+ });
+
+ // And now, apply the reversion itself.
let returnValue = predictiveContext.accept(reversion);
// 'accepting' a reversion performs a rewind; there's no need for async ops here.
@@ -256,17 +295,8 @@ describe("PredictionContext", () => {
// Note: no space appended.
assert.equal(textState.getText(), rewoundTextStateWithInput.getText());
- // Note: accepting a reversion will trigger a new prediction that completes
- // asynchronously, and unfortunately... we have no handle for it.
-
- let updateFake = sinon.fake();
- predictiveContext.on('update', updateFake);
-
- // Now, in order to synchronize... we rely on a Promise. The callback is indeed
- // called synchronously.
- await new Promise((resolve) => {
- predictiveContext.once('update', resolve);
- });
+ // Re-synchronize once we've received word of the new post-reversion predictions.
+ await postRevertSuggestions;
assert.equal(updateFake.callCount, 1);
const suggestionsPostReversion = updateFake.firstCall.args[0];
diff --git a/common/web/input-processor/tsconfig.json b/common/web/input-processor/tsconfig.json
index 1a03881a16e8ff9e162711aa955bfcb77b98e32a..6e04340f4d1b6476e4aece4933478dbf5fcc7792 100644
--- a/common/web/input-processor/tsconfig.json
+++ b/common/web/input-processor/tsconfig.json
@@ -6,6 +6,7 @@
"module": "es6",
"moduleResolution": "Node16",
"declaration": true,
+ "importHelpers": true,
"inlineSources": true,
"sourceMap": true,
"sourceRoot": "keyman/",
diff --git a/common/web/keyboard-processor/build-bundler.js b/common/web/keyboard-processor/build-bundler.js
index 5c3ecb05a974d54c1661a97aa088b5ad3029c3ec..87625dd19ce9b6d3753677e76b5cd0f4a181cd5e 100644
--- a/common/web/keyboard-processor/build-bundler.js
+++ b/common/web/keyboard-processor/build-bundler.js
@@ -10,6 +10,9 @@ import { spawn } from 'child_process';
/** @type {esbuild.BuildOptions} */
const commonConfig = {
+ alias: {
+ 'tslib': '@keymanapp/tslib'
+ },
bundle: true,
sourcemap: true,
format: "esm",
@@ -23,6 +26,9 @@ const commonConfig = {
// Bundled ES module version
esbuild.buildSync({
+ alias: {
+ 'tslib': '@keymanapp/tslib'
+ },
entryPoints: ['build/obj/index.js'],
outfile: "build/lib/index.mjs",
format: "esm",
@@ -31,6 +37,9 @@ esbuild.buildSync({
// Bundled CommonJS (classic Node) module version
esbuild.buildSync({
+ alias: {
+ 'tslib': '@keymanapp/tslib'
+ },
entryPoints: ['build/obj/index.js'],
outfile: 'build/lib/index.cjs',
bundle: true,
@@ -42,6 +51,9 @@ esbuild.buildSync({
esbuild.buildSync({
+ alias: {
+ 'tslib': '@keymanapp/tslib'
+ },
entryPoints: ['build/obj/keyboards/loaders/dom-keyboard-loader.js'],
outfile: 'build/lib/dom-keyboard-loader.mjs',
format: "esm",
@@ -50,6 +62,9 @@ esbuild.buildSync({
// The node-based keyboard loader needs an extra parameter due to Node-built-in imports:
esbuild.buildSync({
+ alias: {
+ 'tslib': '@keymanapp/tslib'
+ },
entryPoints: ['build/obj/keyboards/loaders/node-keyboard-loader.js'],
outfile: 'build/lib/node-keyboard-loader.mjs',
format: "esm",
diff --git a/common/web/keyboard-processor/build.sh b/common/web/keyboard-processor/build.sh
index fc7bf1f1a60378b98205f21e7b00355bdd20b789..01939b9e62414ffebb0f0cbcc4886c1fd2985f9c 100755
--- a/common/web/keyboard-processor/build.sh
+++ b/common/web/keyboard-processor/build.sh
@@ -21,6 +21,7 @@ builder_describe \
"Compiles the web-oriented utility function module." \
"@/common/web/recorder test" \
"@/common/web/keyman-version" \
+ "@/common/web/tslib" \
"@/common/web/utils" \
configure \
clean \
diff --git a/common/web/keyboard-processor/src/keyboards/activeLayout.ts b/common/web/keyboard-processor/src/keyboards/activeLayout.ts
index 065f8c519378f31adbf7481203ef920aaf0c959a..e6d2ab9f1578b82cc6ec97eeae3fc02db2e8200c 100644
--- a/common/web/keyboard-processor/src/keyboards/activeLayout.ts
+++ b/common/web/keyboard-processor/src/keyboards/activeLayout.ts
@@ -5,9 +5,7 @@ import type { KeyDistribution } from "../text/keyEvent.js";
import type { LayoutKey, LayoutRow, LayoutLayer, LayoutFormFactor, ButtonClass } from "./defaultLayouts.js";
import type Keyboard from "./keyboard.js";
-import KeyboardProcessor from "../text/keyboardProcessor.js";
-
-import { deepCopy, type DeviceSpec } from "@keymanapp/web-utils";
+import { type DeviceSpec } from "@keymanapp/web-utils";
// TS 3.9 changed behavior of getters to make them
// non-enumerable by default. This broke our 'polyfill'
@@ -151,7 +149,7 @@ export class ActiveKey implements LayoutKey {
@Enumerable
public get baseKeyEvent(): KeyEvent {
- return deepCopy(this._baseKeyEvent);
+ return new KeyEvent(this._baseKeyEvent);
}
/**
diff --git a/common/web/keyboard-processor/src/keyboards/keyboard.ts b/common/web/keyboard-processor/src/keyboards/keyboard.ts
index a657ff8d038cae830b09ac5c87bf1525fc5b8aa8..82c3883785ef270d4e8049d515f4d6412f6bba28 100644
--- a/common/web/keyboard-processor/src/keyboards/keyboard.ts
+++ b/common/web/keyboard-processor/src/keyboards/keyboard.ts
@@ -499,6 +499,15 @@ export default class Keyboard {
// devices, the only state key in use currently is Caps Lock, which is set
// when the 'caps' layer is active in ActiveKey::constructBaseKeyEvent.
if(!Lkc.device.touchable) {
+ /*
+ * For desktop-style keyboards, start from a blank slate. They have a 'default'
+ * (implicit 'NO_CAPS') layer but not a 'caps' layer. With caps set, it just
+ * highlights the key on the 'default' layer instead.
+ *
+ * We should never set both `CAPS` and `NO_CAPS` at the same time, and
+ * same for the other modifiers.
+ */
+ Lkc.Lstates = 0;
Lkc.Lstates |= stateKeys['K_CAPS'] ? Codes.modifierCodes['CAPS'] : Codes.modifierCodes['NO_CAPS'];
Lkc.Lstates |= stateKeys['K_NUMLOCK'] ? Codes.modifierCodes['NUM_LOCK'] : Codes.modifierCodes['NO_NUM_LOCK'];
Lkc.Lstates |= stateKeys['K_SCROLL'] ? Codes.modifierCodes['SCROLL_LOCK'] : Codes.modifierCodes['NO_SCROLL_LOCK'];
diff --git a/common/web/keyboard-processor/src/keyboards/keyboardProperties.ts b/common/web/keyboard-processor/src/keyboards/keyboardProperties.ts
index 8495d6253e942e81477182ced8e48887bcbe89d6..e83d1a137f192aa5460dce6531a9103a5d22e4e1 100644
--- a/common/web/keyboard-processor/src/keyboards/keyboardProperties.ts
+++ b/common/web/keyboard-processor/src/keyboards/keyboardProperties.ts
@@ -142,6 +142,7 @@ export default class KeyboardProperties implements KeyboardInternalPropertySpec
this.KN = other.KN;
this.KL = other.KL;
this.KLC = other.KLC;
+ // Do NOT apply fontPath here; the mobile apps will have font issues if you do!
this.KFont = other.KFont;
this.KOskFont = other.KOskFont;
this._displayName = (other instanceof KeyboardProperties) ? other._displayName : other.displayName;
diff --git a/common/web/keyboard-processor/src/keyboards/loaders/tsconfig.dom.json b/common/web/keyboard-processor/src/keyboards/loaders/tsconfig.dom.json
index 5120d36a6c1e677d4d878af567b3ad6dfd7d0585..e5ab52729e78c9ce3a5b54fd4e3e920e1e37f5e6 100644
--- a/common/web/keyboard-processor/src/keyboards/loaders/tsconfig.dom.json
+++ b/common/web/keyboard-processor/src/keyboards/loaders/tsconfig.dom.json
@@ -6,6 +6,7 @@
"module": "es6",
"moduleResolution": "Node16",
"declaration": true,
+ "importHelpers": true,
"inlineSources": true,
"sourceMap": true,
"sourceRoot": "keyman/",
diff --git a/common/web/keyboard-processor/src/text/outputTarget.ts b/common/web/keyboard-processor/src/text/outputTarget.ts
index 30c9468295edf74fdda8f6b24bf80ccb254401f6..97a47928479356a009d1d28e40b25caf5e38baa6 100644
--- a/common/web/keyboard-processor/src/text/outputTarget.ts
+++ b/common/web/keyboard-processor/src/text/outputTarget.ts
@@ -390,11 +390,19 @@ export class Mock extends OutputTarget {
let priorMock = outputTarget as Mock;
clone = new Mock(priorMock.text, priorMock.selStart, priorMock.selEnd);
} else {
- let text = outputTarget.getText();
- let beforeText = outputTarget.getTextBeforeCaret();
- let afterText = outputTarget.getTextAfterCaret();
- let selectionStart = beforeText._kmwLength();
- let selectionEnd = text._kmwLength() - afterText._kmwLength();
+ const text = outputTarget.getText();
+ const textLen = text._kmwLength();
+
+ // If !hasSelection()
+ let selectionStart: number = textLen;
+ let selectionEnd: number = 0;
+
+ if(outputTarget.hasSelection()) {
+ let beforeText = outputTarget.getTextBeforeCaret();
+ let afterText = outputTarget.getTextAfterCaret();
+ selectionStart = beforeText._kmwLength();
+ selectionEnd = textLen - afterText._kmwLength();
+ }
// readonly group or not, the returned Mock remains the same.
// New-context events should act as if the caret were at the earlier-in-context
diff --git a/common/web/keyboard-processor/tests/dom/CI.conf.cjs b/common/web/keyboard-processor/tests/dom/CI.conf.cjs
index c7c9813df7286705d3a73bea06a043ee99dd3f6d..a96ffebbede2a334c04f2f14a58f42e58c1d2723 100644
--- a/common/web/keyboard-processor/tests/dom/CI.conf.cjs
+++ b/common/web/keyboard-processor/tests/dom/CI.conf.cjs
@@ -18,7 +18,7 @@ module.exports = function(config) {
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
- // Can't set in base.conf.js b/c of constant's definition style.
+ // Can't set in base.conf.cjs b/c of constant's definition style.
logLevel: config.LOG_INFO
};
diff --git a/common/web/keyboard-processor/tests/dom/manual.conf.cjs b/common/web/keyboard-processor/tests/dom/manual.conf.cjs
index 88e20d283cf2f7c4df2ed53f8a728cf577ca9210..37d982e9b5a70c983c7b64cb766adb1f00b9adfd 100644
--- a/common/web/keyboard-processor/tests/dom/manual.conf.cjs
+++ b/common/web/keyboard-processor/tests/dom/manual.conf.cjs
@@ -18,7 +18,7 @@ module.exports = function(config) {
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
- // Can't set in base.conf.js b/c of constant's definition style.
+ // Can't set in base.conf.cjs b/c of constant's definition style.
logLevel: config.LOG_INFO
};
diff --git a/common/web/keyboard-processor/tsconfig.common.json b/common/web/keyboard-processor/tsconfig.common.json
index 9ed8c35d5433dcc4461eb64355f6875e5ea6d127..30fcd19eda836e2381c025c76fd9df69e32d6470 100644
--- a/common/web/keyboard-processor/tsconfig.common.json
+++ b/common/web/keyboard-processor/tsconfig.common.json
@@ -6,6 +6,7 @@
"module": "es6",
"moduleResolution": "Node16",
"declaration": true,
+ "importHelpers": true,
"inlineSources": true,
"sourceMap": true,
"sourceRoot": "keyman/",
diff --git a/common/web/keyboard-processor/tsconfig.json b/common/web/keyboard-processor/tsconfig.json
index df6ac8969bd95aececebb8f1629e3157434b7869..95071c36a70a844feb41d2e993da6d8f029304e2 100644
--- a/common/web/keyboard-processor/tsconfig.json
+++ b/common/web/keyboard-processor/tsconfig.json
@@ -6,6 +6,7 @@
"module": "es6",
"moduleResolution": "Node16",
"declaration": true,
+ "importHelpers": true,
"inlineSources": true,
"sourceMap": true,
"sourceRoot": "keyman/",
diff --git a/common/web/lm-worker/build-bundler.js b/common/web/lm-worker/build-bundler.js
index 44c03ec2f1d9aaef0b65cf82ba36a152a0de8648..eb7bb5ea415abc36951f2fcf8ffa96dec06de646 100644
--- a/common/web/lm-worker/build-bundler.js
+++ b/common/web/lm-worker/build-bundler.js
@@ -6,7 +6,49 @@
*/
import esbuild from 'esbuild';
-import { spawn } from 'child_process';
+import fs from 'fs';
+import { determineNeededDowncompileHelpers, buildTslibTreeshaker } from '@keymanapp/tslib/esbuild-tools';
+
+/*
+ * Refer to https://github.com/microsoft/TypeScript/issues/13721#issuecomment-307259227 -
+ * the `@class` emit comment-annotation is designed to facilitate tree-shaking for ES5-targeted
+ * down-level emits. `esbuild` doesn't look for it by default... but we can override that with
+ * this plugin.
+ */
+let es5ClassAnnotationAsPurePlugin = {
+ name: '@class -> __PURE__',
+ setup(build) {
+ build.onLoad({filter: /\.js$/ }, async (args) => {
+ let source = await fs.promises.readFile(args.path, 'utf8');
+ return {
+ // Marks any classes compiled by TS (as per the /** @class */ annotation)
+ // as __PURE__ in order to facilitate tree-shaking.
+ contents: source.replace('/** @class */', '/* @__PURE__ */ /** @class */'),
+ loader: 'js'
+ }
+ });
+ }
+}
+
+let EMIT_FILESIZE_PROFILE = false;
+
+if(process.argv.length > 2) {
+ for(let i = 2; i < process.argv.length; i++) {
+ const arg = process.argv[i];
+
+ switch(arg) {
+ case '':
+ break;
+ case '--ci':
+ EMIT_FILESIZE_PROFILE=true
+ break;
+ // May add other options if desired in the future.
+ default:
+ console.error("Invalid command-line option set for script; only --ci is permitted.");
+ process.exit(1);
+ }
+ }
+}
await esbuild.build({
bundle: true,
@@ -28,12 +70,13 @@ await esbuild.build({
},
outdir: 'build/lib',
outExtension: { '.js': '.mjs' },
+ plugins: [ es5ClassAnnotationAsPurePlugin ],
tsconfig: 'tsconfig.json',
- target: "es5"
+ target: "es5",
});
// Bundled CommonJS (classic Node) module version
-esbuild.buildSync({
+await esbuild.build({
bundle: true,
sourcemap: true,
format: "cjs",
@@ -44,12 +87,17 @@ esbuild.buildSync({
},
outdir: 'build/lib',
outExtension: { '.js': '.cjs' },
+ plugins: [ es5ClassAnnotationAsPurePlugin ],
tsconfig: 'tsconfig.json',
target: "es5"
});
-// Direct-use version
-esbuild.buildSync({
+// The one that's actually a component of our releases.
+
+const embeddedWorkerBuildOptions = {
+ alias: {
+ 'tslib': '@keymanapp/tslib'
+ },
bundle: true,
sourcemap: true,
format: "iife",
@@ -58,6 +106,39 @@ esbuild.buildSync({
'worker-main': 'build/obj/worker-main.js'
},
outdir: 'build/lib',
+ plugins: [ es5ClassAnnotationAsPurePlugin ],
tsconfig: 'tsconfig.json',
target: "es5"
-});
+}
+
+// Prepare the needed setup for `tslib` treeshaking.
+const unusedHelpers = await determineNeededDowncompileHelpers(embeddedWorkerBuildOptions);
+embeddedWorkerBuildOptions.plugins = [buildTslibTreeshaker(unusedHelpers), ...embeddedWorkerBuildOptions.plugins];
+
+// Direct-use version
+await esbuild.build(embeddedWorkerBuildOptions);
+
+// ------------------------
+
+// Now to generate a filesize profile for the minified version of the worker.
+// We want a specialized bundle build here instead; no output, but minified like
+// the actual release worker.
+const minifiedProfilingOptions = {
+ ...embeddedWorkerBuildOptions,
+ minify: true,
+ metafile: true,
+ write: false // don't actually write the file.
+}
+
+let result = await esbuild.build(minifiedProfilingOptions);
+let filesizeProfile = await esbuild.analyzeMetafile(result.metafile, { verbose: true });
+fs.writeFileSync('build/filesize-profile.log', `
+// Minified Worker filesize profile, before polyfilling
+${filesizeProfile}
+`);
+
+if(EMIT_FILESIZE_PROFILE) {
+ console.log("Minified, pre-polyfill worker filesize profile:");
+ // Profiles the sourcecode!
+ console.log(filesizeProfile);
+}
diff --git a/common/web/lm-worker/build-wrap-and-minify.js b/common/web/lm-worker/build-wrap-and-minify.js
index 0b816f02ddc7cf956c62b2a6a79d872fece03256..bf50537d9beed44781a6de303d65ea57e9138b71 100644
--- a/common/web/lm-worker/build-wrap-and-minify.js
+++ b/common/web/lm-worker/build-wrap-and-minify.js
@@ -65,11 +65,16 @@ const srcMapString = `//# sourceMappingURL=data:application/json;charset=utf-8;b
* but my attempts to do so end up triggering errors when loading.
*/
+let rawScript = workerConcatenation.script.toString();
+// Two layers of encoding: one for the raw source (parsed by the JS engine),
+// one to 'unwrap' it from a string _within_ that source.
+let jsonEncoded = JSON.stringify(rawScript);
+
let wrapper = `
// Autogenerated code. Do not modify!
// --START:LMLayerWorkerCode--
-export var LMLayerWorkerCode = \`${encodeURIComponent(workerConcatenation.script.toString())}\`
+export var LMLayerWorkerCode = ${jsonEncoded};
${MINIFY && "// Sourcemaps have been omitted for this release build."}
export var LMLayerWorkerSourcemapComment = "${DEBUG ? srcMapString : ''}";
diff --git a/common/web/lm-worker/build.sh b/common/web/lm-worker/build.sh
index 91322527e665d56cbf0a59a57bac8c4399cd71ec..82fba9bb39ba3496392d96e6b3fad0d59965e1d8 100755
--- a/common/web/lm-worker/build.sh
+++ b/common/web/lm-worker/build.sh
@@ -28,6 +28,9 @@ WORKER_OUTPUT_FILENAME=build/lib/worker-main.js
builder_describe \
"Compiles the Language Modeling Layer for common use in predictive text and autocorrective applications." \
"@/common/web/keyman-version" \
+ "@/common/web/tslib" \
+ "@/common/models/wordbreakers" \
+ "@/common/models/templates" \
"@/common/tools/sourcemap-path-remapper" \
configure clean build test --ci
@@ -56,9 +59,13 @@ if builder_start_action build; then
# Build worker with tsc first
tsc -b $builder_verbose || builder_die "Could not build worker."
+ EXT_FLAGS=
+ if builder_has_option --ci; then
+ EXT_FLAGS=--ci
+ fi
echo "Bundling worker modules"
- node build-bundler.js
+ node build-bundler.js "$EXT_FLAGS"
# Declaration bundling.
npm run tsc -- --emitDeclarationOnly --outFile ./build/lib/index.d.ts
diff --git a/common/web/lm-worker/tsconfig.json b/common/web/lm-worker/tsconfig.json
index 551f1645a950a4902e12c0af66b158fe11d4d1b0..2fdda0135a121b963f752307aa01074a355a467a 100644
--- a/common/web/lm-worker/tsconfig.json
+++ b/common/web/lm-worker/tsconfig.json
@@ -5,7 +5,8 @@
"allowJs": false,
"declaration": true,
"module": "es6",
- "moduleResolution": "node",
+ "moduleResolution": "node16",
+ "importHelpers": true,
"inlineSourceMap": true,
"inlineSources": true,
"sourceRoot": "/common/web/lm-worker/src",
diff --git a/common/web/tslib/README.md b/common/web/tslib/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..4c87474f627b9528f7f774d7c0b50e2a3175bbf8
--- /dev/null
+++ b/common/web/tslib/README.md
@@ -0,0 +1,15 @@
+The default import setup for the `tslib` package is unfortunately incompatible with `esbuild` when in ES5 mode. But...
+with a little elbow grease, we can fix that with _this_ package by importing its ES5-compatible file and exporting it
+as _this_ package's default export for use in anything looking to `"importHelpers"`.
+
+To utilize this with `esbuild` while enabling the `"importHelpers"` compilation option in your tsconfig.json, you'll want
+to set the following in your `esbuild` config:
+
+```javascript
+ alias: {
+ 'tslib': '@keymanapp/tslib'
+ },
+```
+
+Note that esbuild 0.15.16 is the minimum required version to utilize the 'alias' feature necessary to replace `tslib` for
+`tsc`-generated `import { /* */ } from 'tslib'` statements that result from enabling `"importHelpers"` in a tsconfig.
\ No newline at end of file
diff --git a/common/web/tslib/build.sh b/common/web/tslib/build.sh
new file mode 100755
index 0000000000000000000000000000000000000000..411e32d8bbb4c3817687363b738f1cc3790e5ca6
--- /dev/null
+++ b/common/web/tslib/build.sh
@@ -0,0 +1,27 @@
+#!/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/build-utils.sh"
+## END STANDARD BUILD SCRIPT INCLUDE
+
+. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
+
+cd "$THIS_SCRIPT_PATH"
+
+################################ Main script ################################
+
+builder_describe \
+ "A ES5 + esbuild compatibility wrapper for the 'tslib' package." \
+ clean configure build
+
+builder_describe_outputs \
+ configure "/node_modules" \
+ build "/common/web/tslib/build/index.js"
+
+builder_parse "$@"
+
+builder_run_action configure verify_npm_setup
+builder_run_action clean rm -rf build/
+builder_run_action build tsc --build
\ No newline at end of file
diff --git a/common/web/tslib/package.json b/common/web/tslib/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..ceec8c64099e23bd539dc23302fe11b39846a4b0
--- /dev/null
+++ b/common/web/tslib/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "@keymanapp/tslib",
+ "description": "An ES5 + esbuild-compatible wrapper for the 'tslib' library",
+ "main": "./build/index.js",
+ "exports": {
+ ".": {
+ "types": "./build/index.d.ts",
+ "import": "./build/index.js"
+ },
+ "./esbuild-tools": {
+ "types": "./build/esbuild-tools.d.ts",
+ "import": "./build/esbuild-tools.js"
+ }
+ },
+ "scripts": {
+ "build": "gosh ./build.sh build",
+ "clean": "gosh ./build.sh clean"
+ },
+ "dependencies": {
+ "tslib": "^2.5.2",
+ "typescript": "^4.9.5"
+ },
+ "devDependencies": {
+ "@keymanapp/resources-gosh": "*",
+ "esbuild": "^0.15.16"
+ },
+ "type": "module"
+}
diff --git a/common/web/tslib/src/esbuild-tools.ts b/common/web/tslib/src/esbuild-tools.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9e258992535eab18c6f1e4b471b538bd429a4393
--- /dev/null
+++ b/common/web/tslib/src/esbuild-tools.ts
@@ -0,0 +1,327 @@
+import esbuild from 'esbuild';
+import fs from 'fs';
+
+// Note: this package is intended 100% as a dev-tool, hence why esbuild is just a dev-dependency.
+
+// Component #1: Detect all `tslib` helpers we actually want to use.
+export async function determineNeededDowncompileHelpers(config: esbuild.BuildOptions, ignoreFilePattern?: RegExp, log?: boolean) {
+ const tslibHelperNames = [
+ "__extends",
+ "__assign",
+ "__rest",
+ "__decorate",
+ "__param",
+ "__metadata",
+ "__awaiter",
+ "__generator",
+ "__exportStar", // uses __createBinding
+ "__createBinding",
+ "__values",
+ "__read",
+ "__spread",
+ "__spreadArray",
+ "__spreadArrays",
+ "__await",
+ "__asyncGenerator",
+ "__asyncDelegator",
+ "__asyncValues",
+ "__makeTemplateObject",
+ "__importStar",
+ "__setModuleDefault", // only used by the previous entry!
+ "__importDefault",
+ "__classPrivateFieldGet",
+ "__classPrivateFieldSet",
+ "__classPrivateFieldIn",
+ "__runInitializers",
+ "__setFunctionName",
+ "__propKey",
+ "__esDecorate"
+ ];
+
+ const detectedHelpers: string[] = [];
+
+ let tslibHelperDetectionPlugin = {
+ name: 'tslib helper use detection',
+ setup(build) {
+ build.onLoad({filter: /\.js$/}, async (args) => {
+ let source = await fs.promises.readFile(args.path, 'utf8');
+
+ let warnings: esbuild.Message[] = [];
+
+ if(/tslib.js$/.test(args.path)) {
+ let declarationRegex = /var (__[a-zA-Z0-9]+)/g;
+ let results = source.match(declarationRegex);
+
+ for(let result of results) {
+ let capture = result.substring(4);
+
+ if(!tslibHelperNames.find((entry) => entry == capture)) {
+ // Match: `result` itself. Grab index, build location, build message.
+ let index = source.indexOf(result);
+
+ warnings.push(buildMessage(
+ 'tslib helper use detection',
+ 'Probable `tslib` helper `' + capture + '` has not been validated for tree-shaking potential',
+ indexToSourceLocation(source, index, result.length, args.path)
+ ));
+ }
+ }
+
+ // Returning `undefined` for `content` makes this 'pass-through' - it doesn't prevent other
+ // configured plugins from working.
+ return {
+ warnings: warnings
+ };
+ }
+
+ if(ignoreFilePattern?.test(args.path)) {
+ return;
+ }
+
+ for(let helper of tslibHelperNames) {
+ if(source.indexOf(helper) > -1 && !detectedHelpers.find((entry) => entry == helper)) {
+ detectedHelpers.push(helper);
+
+ if(helper == '__importStar') {
+ detectedHelpers.push('__setModuleDefault');
+ detectedHelpers.push('__createBinding');
+ } else if(helper == '__exportStar') {
+ detectedHelpers.push('__createBinding');
+ }
+ }
+ }
+
+ // Returning `undefined` makes this 'pass-through' - it doesn't prevent other
+ // configured plugins from working.
+ return;
+ });
+ }
+ }
+
+ // tslib tree-shake phase 1 - detecting which helpers are safe to remove.
+ await esbuild.build({
+ ...config,
+ // `tslibHelperDetectionPlugin` is pass-through, and so has no net effect on
+ // the build. We just use this run to scan for any utilized `tslib` helper funcs,
+ // not to manipulate the actual source in any way.
+ plugins: [ tslibHelperDetectionPlugin, ...(config.plugins ?? []) ],
+ write: false
+ });
+
+ // At this point, we can determine what's unused.
+ detectedHelpers.sort();
+
+ const unusedHelpers: string[] = [];
+ tslibHelperNames.forEach((entry) => {
+ if(!detectedHelpers.find((detected) => detected == entry)) {
+ unusedHelpers.push(entry);
+ }
+ });
+
+ if(log) {
+ // Logs on the tree-shaking decisions
+ console.log("Detected helpers from tslib: ");
+ console.log();
+
+ console.log();
+ console.log("Unused helpers: ");
+ console.log(unusedHelpers);
+ }
+
+ return unusedHelpers;
+}
+
+function indexToSourceLocation(source: string, index: number, matchLength: number, file: string): esbuild.Location {
+ let priorText = source.substring(0, index);
+ let lineNum = priorText.split('\n').length;
+ let lastLineBreakIndex = priorText.lastIndexOf('\n');
+ let colNum = index - lastLineBreakIndex;
+ let nextLineBreakIndex = source.indexOf('\n', lastLineBreakIndex+1);
+
+ return {
+ file: file,
+ line: lineNum,
+ column: colNum,
+ lineText: source.substring(lastLineBreakIndex+1, nextLineBreakIndex),
+ namespace: '',
+ suggestion: '',
+ length: matchLength
+ }
+}
+
+function buildMessage(pluginName: string, message: string, location: esbuild.Location): esbuild.Message {
+ return {
+ id: '',
+ notes: [],
+ detail: '',
+ pluginName: pluginName,
+ text: message,
+ location: location
+ }
+}
+
+// Ugh. https://github.com/evanw/esbuild/issues/2656#issuecomment-1304013941
+function wrapClassPlugin(instancePlugin: esbuild.Plugin) {
+ return {
+ name: instancePlugin.name,
+ setup: instancePlugin.setup.bind(instancePlugin)
+ };
+}
+
+// Component #2: when we've actually determined which ones are safe to remove, this plugin
+// can remove their code.
+class TslibTreeshaker implements esbuild.Plugin {
+ public readonly name = 'tslib forced treeshaking';
+
+ private unusedHelpers: string[];
+
+ constructor(unusedHelpers: string[]) {
+ this.unusedHelpers = unusedHelpers;
+ }
+
+ treeshakeDefinition(source: string, start: number, expectTernary: boolean, name: string, location: esbuild.Location) {
+ let scopeDepth = 0;
+ let parenDepth = 0;
+ let topLevelTernaryActive = 0;
+ let i = start;
+ let char = source.charAt(i);
+ while(char != '}' || --scopeDepth != 0 || topLevelTernaryActive) {
+ if(char == '{') {
+ scopeDepth++;
+ } else if(char == '(') {
+ parenDepth++;
+ } else if(char == ')') {
+ parenDepth--;
+ } else if(char == '?' && scopeDepth == 0) {
+ if(!expectTernary) {
+ return {
+ source: source,
+ error: buildMessage(this.name, `Unexpected conditional for definition of ${name}`, location)
+ }
+ }
+ topLevelTernaryActive++;
+ } else if(char == ':' && topLevelTernaryActive && scopeDepth == 0) {
+ topLevelTernaryActive--;
+ }
+ i++;
+
+ if(i > source.length) {
+ return {
+ source: source,
+ error: buildMessage(this.name, `Failed to determine end of definition for ${name}`, location)
+ };
+ }
+
+ char = source.charAt(i);
+ }
+ i++; // we want to erase the final '}', too.
+
+ // The functions may be wrapped with parens.
+ if(parenDepth == 1) {
+ let nextOpen = source.indexOf('(', i);
+ let nextClosed = source.indexOf(')', i);
+
+ if(nextOpen < nextClosed) {
+ return {
+ source: source,
+ error: buildMessage(this.name, `Failed to determine end of definition for ${name}`, location)
+ };
+ } else {
+ i = nextClosed + 1;
+ }
+ }
+
+ if(source.charAt(i) == ';') {
+ i++;
+ }
+
+ return {
+ source: source.replace(source.substring(start, i), '')
+ };
+ }
+
+ setup(build: esbuild.PluginBuild) {
+ build.onLoad({filter: /tslib.js$/}, async (args) => {
+ const trueSource = await fs.promises.readFile(args.path, 'utf8');
+ let source = trueSource;
+
+ let warnings: esbuild.Message[] = [];
+ let errors: esbuild.Message[] = [];
+
+ for(let unusedHelper of this.unusedHelpers) {
+ // Removes the 'exporter' line used to actually export it from the tslib source.
+ source = source.replace(`exporter\(\"${unusedHelper}\", ${unusedHelper}\);`, '');
+
+ // Removes the actual helper function definition - obviously, the biggest filesize savings to be had here.
+ let matchString = `${unusedHelper} = function`;
+ let expectTernary = false;
+
+ // A special case - it has two different versions depending on if Object.create exists or not.
+ // This is established via a ternary conditional. Just adds a bit to the parsing.
+ if(unusedHelper == '__createBinding') {
+ matchString = `${unusedHelper} = `;
+ expectTernary = true;
+ } else if(unusedHelper == '__setModuleDefault') {
+ matchString = `var ${unusedHelper} = `; // is inlined, not declared at top!
+ expectTernary = true;
+ }
+ let definitionStart = source.indexOf(matchString);
+
+ if(definitionStart > -1) {
+ const result = this.treeshakeDefinition(
+ source,
+ definitionStart,
+ expectTernary,
+ unusedHelper,
+ indexToSourceLocation(trueSource, trueSource.indexOf(matchString), matchString.length, args.path)
+ );
+
+ if(result.error) {
+ errors.push(result.error);
+ } else {
+ source = result.source;
+ }
+ continue;
+ }
+
+ // If we reached this point, we couldn't treeshake the unused helper appropriately.
+ if(definitionStart == -1) {
+ // Matches the standard definition pattern's left-hand assignment component.
+ matchString = `${unusedHelper} =`
+ let bestGuessIndex = trueSource.indexOf(matchString);
+
+ if(bestGuessIndex == -1) {
+ // Failing the above, we match the declaration higher up within the file.
+ matchString = `var ${unusedHelper}`
+ bestGuessIndex = trueSource.indexOf(matchString);
+ }
+
+ // Failing THAT, we just give up and go start-of-file.
+ if(bestGuessIndex == -1) {
+ matchString = '';
+ bestGuessIndex = 0;
+ }
+
+ warnings.push(
+ buildMessage(
+ this.name,
+ "tslib has likely been updated recently; could not force-treeshake tslib helper " + unusedHelper,
+ indexToSourceLocation(trueSource, bestGuessIndex, matchString.length, args.path)
+ )
+ );
+ }
+ }
+
+ return {
+ contents: source,
+ loader: 'js',
+ warnings: warnings,
+ errors: errors
+ };
+ });
+ }
+};
+
+export function buildTslibTreeshaker(unusedHelpers: string[]) {
+ return wrapClassPlugin(new TslibTreeshaker(unusedHelpers));
+};
\ No newline at end of file
diff --git a/common/web/tslib/src/index.ts b/common/web/tslib/src/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2f173ad83fa91de2a1a7c3f982e39bae3e5617d9
--- /dev/null
+++ b/common/web/tslib/src/index.ts
@@ -0,0 +1,3 @@
+export * from '../../../../node_modules/tslib/tslib.js';
+
+export * as tslib from '../../../../node_modules/tslib/tslib.js';
\ No newline at end of file
diff --git a/common/web/tslib/tsconfig.json b/common/web/tslib/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..c141af3d3c5f0ae12b4bb8c67f824a443b13383a
--- /dev/null
+++ b/common/web/tslib/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "extends": "../../../tsconfig-base.json",
+ "compilerOptions": {
+ "allowJs": true,
+ "module": "es6",
+ "moduleResolution": "Node",
+ "allowSyntheticDefaultImports": true,
+ "inlineSources": true,
+ "sourceMap": true,
+ "declaration": true,
+ "target": "es5",
+ "tsBuildInfoFile": "./build/tsconfig.tsbuildinfo",
+ "types": ["node"],
+ "lib": ["es6", "DOM"], // The latter is b/c esbuild expects the type.
+ "baseUrl": "./src",
+ "outDir": "./build/",
+ "rootDir": "./src"
+ },
+ "include": [
+ "./src/*.ts"
+ ]
+}
diff --git a/common/web/types/src/kpj/kpj-file-reader.ts b/common/web/types/src/kpj/kpj-file-reader.ts
index 5f768c54f1ea159dccd318e1eb997be1d1d5e5c6..21e92c975b2313a3dd0f1508cbd663ef20654188 100644
--- a/common/web/types/src/kpj/kpj-file-reader.ts
+++ b/common/web/types/src/kpj/kpj-file-reader.ts
@@ -27,8 +27,8 @@ export class KPJFileReader {
return data as KPJFile;
}
- public validate(source: KPJFile, schemaBuffer: Buffer): void {
- const schema = JSON.parse(schemaBuffer.toString('utf8'));
+ public validate(source: KPJFile, schemaBuffer: Uint8Array): void {
+ const schema = JSON.parse(new TextDecoder().decode(schemaBuffer));
const ajv = new Ajv();
if(!ajv.validate(schema, source)) {
throw new Error(ajv.errorsText());
diff --git a/common/web/types/src/kvk/kvks-file-reader.ts b/common/web/types/src/kvk/kvks-file-reader.ts
index c12b7036e4a3828098dd05ae5f99536d9e081783..13c6c8716c11c1b6a02e98f8b63fc18ed12cb6d0 100644
--- a/common/web/types/src/kvk/kvks-file-reader.ts
+++ b/common/web/types/src/kvk/kvks-file-reader.ts
@@ -7,12 +7,6 @@ import { VisualKeyboard, VisualKeyboardHeaderFlags, VisualKeyboardKey, VisualKey
import { USVirtualKeyCodes } from '../consts/virtual-key-constants.js';
import { BUILDER_KVK_HEADER_VERSION } from './kvk-file.js';
-export enum KVKSParseErrorType { invalidVkey };
-export class KVKSParseError extends Error {
- public type: KVKSParseErrorType;
- public vkey: string;
-};
-
export default class KVKSFileReader {
public read(file: Uint8Array): KVKSourceFile {
let source: KVKSourceFile;
@@ -33,10 +27,12 @@ export default class KVKSFileReader {
// rather than using the version tagged on npmjs.com.
});
- parser.parseString(file, (e: unknown, r: unknown) => { source = r as KVKSourceFile });
- source = this.boxArrays(source);
- this.cleanupFlags(source);
- this.cleanupUnderscore('visualkeyboard', source.visualkeyboard);
+ parser.parseString(file, (e: unknown, r: unknown) => { if(e) { throw e }; source = r as KVKSourceFile });
+ if(source) {
+ source = this.boxArrays(source);
+ this.cleanupFlags(source);
+ this.cleanupUnderscore('visualkeyboard', source.visualkeyboard);
+ }
return source;
}
@@ -72,15 +68,15 @@ export default class KVKSFileReader {
}
}
- public validate(source: KVKSourceFile, schemaBuffer: Buffer): void {
- const schema = JSON.parse(schemaBuffer.toString('utf8'));
+ public validate(source: KVKSourceFile, schemaBuffer: Uint8Array): void {
+ const schema = JSON.parse(new TextDecoder().decode(schemaBuffer));
const ajv = new Ajv();
if(!ajv.validate(schema, source)) {
throw new Error(ajv.errorsText());
}
}
- public transform(source: KVKSourceFile, errors?: KVKSParseError[]): VisualKeyboard {
+ public transform(source: KVKSourceFile, invalidVkeys?: string[]): VisualKeyboard {
// NOTE: at this point, the xml should have been validated
// and matched the schema result so we can assume properties exist
let result: VisualKeyboard = {
@@ -118,11 +114,8 @@ export default class KVKSFileReader {
for(let sourceKey of layer.key) {
let vkey = (USVirtualKeyCodes as any)[sourceKey.$?.vkey];
if(!vkey) {
- if(errors) {
- let e = new KVKSParseError();
- e.type = KVKSParseErrorType.invalidVkey;
- e.vkey = sourceKey.$?.vkey;
- errors.push(e);
+ if(typeof invalidVkeys !== 'undefined') {
+ invalidVkeys.push(sourceKey.$?.vkey);
}
continue;
}
diff --git a/common/web/types/src/kvk/kvks-file-writer.ts b/common/web/types/src/kvk/kvks-file-writer.ts
index 613c6caa5108289ac9c99901ddc00a68383313dd..1b3a819f580f2d2ef23f1582aade95c98a6b97a7 100644
--- a/common/web/types/src/kvk/kvks-file-writer.ts
+++ b/common/web/types/src/kvk/kvks-file-writer.ts
@@ -96,7 +96,6 @@ export default class KVKSFileWriter {
l.key.push(k);
}
- // console.dir(kvks, {depth:8});
let result = builder.buildObject(kvks);
return result; //Uint8Array.from(result);
}
diff --git a/common/web/types/src/ldml-keyboard/ldml-keyboard-xml-reader.ts b/common/web/types/src/ldml-keyboard/ldml-keyboard-xml-reader.ts
index 35d7f627956417b508e9b358473ce019147afc80..fa74126462451b8a0dd4c70a30532f7f82cc6c53 100644
--- a/common/web/types/src/ldml-keyboard/ldml-keyboard-xml-reader.ts
+++ b/common/web/types/src/ldml-keyboard/ldml-keyboard-xml-reader.ts
@@ -14,16 +14,20 @@ interface NameAndProps {
'$$'?: any; // children
};
-export default class LDMLKeyboardXMLSourceFileReader {
- callbacks: CompilerCallbacks;
+export class LDMLKeyboardXMLSourceFileReaderOptions {
+ importsPath: string;
+};
+
+export class LDMLKeyboardXMLSourceFileReader {
+ constructor(private options: LDMLKeyboardXMLSourceFileReaderOptions, private callbacks : CompilerCallbacks) {
+ }
- constructor(callbacks : CompilerCallbacks) {
- this.callbacks = callbacks;
+ static get defaultImportsURL() {
+ return new URL(`../import/`, import.meta.url);
}
- readImportFile(version: string, subpath: string): Buffer {
- // TODO-LDML: sanitize input string
- let importPath = new URL(`../import/${version}/${subpath}`, import.meta.url);
+ readImportFile(version: string, subpath: string): Uint8Array {
+ const importPath = this.callbacks.resolveFilename(this.options.importsPath, `${version}/${subpath}`);
return this.callbacks.loadFile(importPath);
}
@@ -201,8 +205,8 @@ export default class LDMLKeyboardXMLSourceFileReader {
/**
* @returns true if valid, false if invalid
*/
- public validate(source: LDMLKeyboardXMLSourceFile | LDMLKeyboardTestDataXMLSourceFile, schemaSource: Buffer): boolean {
- const schema = JSON.parse(schemaSource.toString('utf8'));
+ public validate(source: LDMLKeyboardXMLSourceFile | LDMLKeyboardTestDataXMLSourceFile, schemaSource: Uint8Array): boolean {
+ const schema = JSON.parse(new TextDecoder().decode(schemaSource));
const ajv = new Ajv();
if(!ajv.validate(schema, source)) {
for (let err of ajv.errors) {
diff --git a/common/web/types/src/main.ts b/common/web/types/src/main.ts
index 4e7755038403dc3b57ceecc96d2dbd9470a19aa9..ddaa318d3e073cba9a368cd1f20bcb71eb7c40fc 100644
--- a/common/web/types/src/main.ts
+++ b/common/web/types/src/main.ts
@@ -14,7 +14,7 @@ export * as KvksFile from './kvk/kvk-file.js';
export * as LDMLKeyboard from './ldml-keyboard/ldml-keyboard-xml.js';
export { LDMLKeyboardTestDataXMLSourceFile } from './ldml-keyboard/ldml-keyboard-testdata-xml.js';
-export { default as LDMLKeyboardXMLSourceFileReader } from './ldml-keyboard/ldml-keyboard-xml-reader.js';
+export { LDMLKeyboardXMLSourceFileReader, LDMLKeyboardXMLSourceFileReaderOptions } from './ldml-keyboard/ldml-keyboard-xml-reader.js';
export * as Constants from './consts/virtual-key-constants.js';
diff --git a/common/web/types/src/util/compiler-interfaces.ts b/common/web/types/src/util/compiler-interfaces.ts
index 849e945321fb5b811f2c81b9b05b7d5d74455381..d3932b7bfa887a997569f686c7309f5421de0152 100644
--- a/common/web/types/src/util/compiler-interfaces.ts
+++ b/common/web/types/src/util/compiler-interfaces.ts
@@ -120,13 +120,11 @@ export interface CompilerFileSystemCallbacks {
export interface CompilerCallbacks {
/**
* Attempt to load a file. Return falsy if not found.
- * TODO: accept only string
* TODO: never return falsy, just throw if not found?
- * TODO: Buffer is Node-only.
* @param baseFilename
* @param filename
*/
- loadFile(filename: string | URL): Buffer;
+ loadFile(filename: string): Uint8Array;
get path(): CompilerPathCallbacks;
get fs(): CompilerFileSystemCallbacks;
@@ -138,7 +136,7 @@ export interface CompilerCallbacks {
*/
resolveFilename(baseFilename: string, filename: string): string;
- loadSchema(schema: CompilerSchema): Buffer;
+ loadSchema(schema: CompilerSchema): Uint8Array;
reportMessage(event: CompilerEvent): void;
debug(msg: string): void;
};
diff --git a/common/web/types/test/helpers/TestCompilerCallbacks.ts b/common/web/types/test/helpers/TestCompilerCallbacks.ts
index 596a731d58880a1fbf4d7dab95a81c795bf212e1..8316264a774bf8b85e4adeb94c48937935247fdf 100644
--- a/common/web/types/test/helpers/TestCompilerCallbacks.ts
+++ b/common/web/types/test/helpers/TestCompilerCallbacks.ts
@@ -42,7 +42,7 @@ export class TestCompilerCallbacks implements CompilerCallbacks {
return resolveFilename(baseFilename, filename);
}
- loadFile(filename: string | URL): Buffer {
+ loadFile(filename: string): Uint8Array {
// TODO: error management, does it belong here?
try {
return loadFile(filename);
diff --git a/common/web/types/test/helpers/index.ts b/common/web/types/test/helpers/index.ts
index 58dc32dc9c1618da13acb027d39ad3a379a53394..06906df148589fb5a10808b92e685888d5fd8a7b 100644
--- a/common/web/types/test/helpers/index.ts
+++ b/common/web/types/test/helpers/index.ts
@@ -28,7 +28,10 @@ export function loadSchema(schema: CompilerSchema): Buffer {
}
export function resolveFilename(baseFilename: string, filename: string) {
- const basePath = path.dirname(baseFilename);
+ const basePath =
+ baseFilename.endsWith('/') || baseFilename.endsWith('\\') ?
+ baseFilename :
+ path.dirname(baseFilename);
// Transform separators to platform separators -- we are agnostic
// in our use here but path prefers files may use
// either / or \, although older kps files were always \.
diff --git a/common/web/types/test/helpers/reader-callback-test.ts b/common/web/types/test/helpers/reader-callback-test.ts
index 8c85a759acf270a8a99e5eb947faf8b51db8bb93..204e0ec5bf4875168e082630354ff2453b756def 100644
--- a/common/web/types/test/helpers/reader-callback-test.ts
+++ b/common/web/types/test/helpers/reader-callback-test.ts
@@ -1,11 +1,16 @@
import 'mocha';
import {assert} from 'chai';
import { loadFile, makePathToFixture, loadSchema } from '../helpers/index.js';
-import LDMLKeyboardXMLSourceFileReader from '../../src/ldml-keyboard/ldml-keyboard-xml-reader.js';
+import { LDMLKeyboardXMLSourceFileReader, LDMLKeyboardXMLSourceFileReaderOptions } from '../../src/ldml-keyboard/ldml-keyboard-xml-reader.js';
import { CompilerEvent } from '../../src/util/compiler-interfaces.js';
import { LDMLKeyboardXMLSourceFile } from '../../src/ldml-keyboard/ldml-keyboard-xml.js';
import { LDMLKeyboardTestDataXMLSourceFile } from '../ldml-keyboard/ldml-keyboard-testdata-xml.js';
import { TestCompilerCallbacks } from './TestCompilerCallbacks.js';
+import { fileURLToPath } from 'url';
+
+const readerOptions: LDMLKeyboardXMLSourceFileReaderOptions = {
+ importsPath: fileURLToPath(LDMLKeyboardXMLSourceFileReader.defaultImportsURL)
+};
export interface CompilationCase {
/**
@@ -71,7 +76,7 @@ export interface TestDataCase {
export function testReaderCases(cases : CompilationCase[]) {
// we need our own callbacks rather than using the global so messages don't get mixed
const callbacks = new TestCompilerCallbacks();
- const reader = new LDMLKeyboardXMLSourceFileReader(callbacks);
+ const reader = new LDMLKeyboardXMLSourceFileReader(readerOptions, callbacks);
for (let testcase of cases) {
const expectFailure = testcase.throws || !!(testcase.errors); // if true, we expect this to fail
const testHeading = expectFailure ? `should fail to load: ${testcase.subpath}`:
@@ -122,7 +127,7 @@ export function testReaderCases(cases : CompilationCase[]) {
export function testTestdataReaderCases(cases : TestDataCase[]) {
// we need our own callbacks rather than using the global so messages don't get mixed
const callbacks = new TestCompilerCallbacks();
- const reader = new LDMLKeyboardXMLSourceFileReader(callbacks);
+ const reader = new LDMLKeyboardXMLSourceFileReader(readerOptions, callbacks);
for (let testcase of cases) {
const expectFailure = testcase.throws || !!(testcase.errors); // if true, we expect this to fail
const testHeading = expectFailure ? `should fail to load: ${testcase.subpath}`:
diff --git a/common/web/types/test/kpj/test-kpj-file-reader.ts b/common/web/types/test/kpj/test-kpj-file-reader.ts
index b7c2f710131db0d9bdf8be3f9190a267ee4a2ad5..95b5f1d01ae86030139e1ff594eed17fb5796713 100644
--- a/common/web/types/test/kpj/test-kpj-file-reader.ts
+++ b/common/web/types/test/kpj/test-kpj-file-reader.ts
@@ -15,7 +15,6 @@ describe('kpj-file-reader', function () {
const input = fs.readFileSync(path);
const reader = new KPJFileReader(callbacks);
const kpj = reader.read(input);
- console.dir(kpj);
assert.doesNotThrow(() => {
reader.validate(kpj, loadSchema('kpj'));
});
@@ -79,7 +78,6 @@ describe('kpj-file-reader', function () {
assert.lengthOf(project.files, 2);
let f: KeymanDeveloperProjectFile10 = project.files[0];
- console.dir(f);
assert.equal(f.id, 'id_f347675c33d2e6b1c705c787fad4941a');
assert.equal(f.filename, 'khmer_angkor.kmn');
assert.equal(f.filePath, 'source/khmer_angkor.kmn');
diff --git a/common/web/types/test/kvk/test-kvk-round-trip.ts b/common/web/types/test/kvk/test-kvk-round-trip.ts
index 41dbe2dbc0cb25b553c790172998f006269c416b..031ad60b9e6e1c2fda395c2dda7caf8614794a90 100644
--- a/common/web/types/test/kvk/test-kvk-round-trip.ts
+++ b/common/web/types/test/kvk/test-kvk-round-trip.ts
@@ -5,7 +5,7 @@ import Hexy from 'hexy';
import gitDiff from 'git-diff';
const { hexy } = Hexy;
import { loadSchema, makePathToFixture } from '../helpers/index.js';
-import KvksFileReader, { KVKSParseError } from "../../src/kvk/kvks-file-reader.js";
+import KvksFileReader from "../../src/kvk/kvks-file-reader.js";
import KvkFileReader from "../../src/kvk/kvk-file-reader.js";
import KvkFileWriter from "../../src/kvk/kvk-file-writer.js";
import KvksFileWriter from "../../src/kvk/kvks-file-writer.js";
@@ -53,9 +53,9 @@ describe('kvks-file-reader', function () {
assert.doesNotThrow(() => {
reader.validate(kvks, loadSchema('kvks'));
});
- const errors: KVKSParseError[] = [];
- const vk = reader.transform(kvks, errors);
- assert.isEmpty(errors);
+ const invalidVkeys: string[] = [];
+ const vk = reader.transform(kvks, invalidVkeys);
+ assert.isEmpty(invalidVkeys);
const writer = new KvkFileWriter();
const output = writer.write(vk);
assertBufferMatch(Buffer.from(output), compiled);
@@ -76,9 +76,9 @@ describe('kvks-file-reader', function () {
assert.doesNotThrow(() => {
kvksReader.validate(kvks, loadSchema('kvks'));
});
- const errors: KVKSParseError[] = [];
- const vk2 = kvksReader.transform(kvks, errors);
- assert.isEmpty(errors);
+ const invalidVkeys: string[] = [];
+ const vk2 = kvksReader.transform(kvks, invalidVkeys);
+ assert.isEmpty(invalidVkeys);
// make sure the binary is the same
assert.deepEqual(vk2, vk);
diff --git a/common/web/types/test/kvk/test-kvks-file.ts b/common/web/types/test/kvk/test-kvks-file.ts
index 523bfaf54d618ce21da9f9137c67d9b04c39bd6a..4cbb299a0085b188d405d5a46aa23e964ad1a344 100644
--- a/common/web/types/test/kvk/test-kvks-file.ts
+++ b/common/web/types/test/kvk/test-kvks-file.ts
@@ -1,7 +1,7 @@
import * as fs from 'fs';
import 'mocha';
import { loadSchema, makePathToFixture } from '../helpers/index.js';
-import KvksFileReader, { KVKSParseError } from "../../src/kvk/kvks-file-reader.js";
+import KvksFileReader from "../../src/kvk/kvks-file-reader.js";
import KvksFileWriter from "../../src/kvk/kvks-file-writer.js";
import { verify_khmer_angkor } from './test-kvk-utils.js';
import { assert } from 'chai';
@@ -16,9 +16,9 @@ describe('kvks-file-reader', function() {
assert.doesNotThrow(() => {
reader.validate(kvks, loadSchema('kvks'));
});
- const errors: KVKSParseError[] = [];
- const vk = reader.transform(kvks, errors);
- assert.isEmpty(errors);
+ const invalidVkeys: string[] = [];
+ const vk = reader.transform(kvks, invalidVkeys);
+ assert.isEmpty(invalidVkeys);
verify_khmer_angkor(vk);
});
});
@@ -30,9 +30,9 @@ describe('kvks-file-writer', function() {
const reader = new KvksFileReader();
const kvksExpected = reader.read(input);
- const errors: KVKSParseError[] = [];
- const vk = reader.transform(kvksExpected, errors);
- assert.isEmpty(errors);
+ const invalidVkeys: string[] = [];
+ const vk = reader.transform(kvksExpected, invalidVkeys);
+ assert.isEmpty(invalidVkeys);
const writer = new KvksFileWriter();
const output = writer.write(vk);
diff --git a/common/web/utils/build-bundler.js b/common/web/utils/build-bundler.js
index 72df8e0ae1e9540388ee26abcde5c941ab4126a1..cda215474f8ecc4eae4c086c72028cf3bdfffcb2 100644
--- a/common/web/utils/build-bundler.js
+++ b/common/web/utils/build-bundler.js
@@ -8,6 +8,9 @@ import { spawn } from 'child_process';
// Bundles to a compact ESModule
esbuild.buildSync({
entryPoints: ['build/obj/index.js'],
+ alias: {
+ 'tslib': '@keymanapp/tslib'
+ },
bundle: true,
sourcemap: true,
//minify: true, // No need to minify a module.
@@ -24,6 +27,9 @@ esbuild.buildSync({
// Bundles to a compact CommonJS (classic Node) module
esbuild.buildSync({
entryPoints: ['build/obj/index.js'],
+ alias: {
+ 'tslib': '@keymanapp/tslib'
+ },
bundle: true,
sourcemap: true,
//minify: true, // No need to minify a module.
diff --git a/common/web/utils/build.sh b/common/web/utils/build.sh
index f8daffb4a3e282fc16170b501f86f4afb3ff6c50..d4ff9ef8f31dde20531e589695cae39ffc2dd1ac 100755
--- a/common/web/utils/build.sh
+++ b/common/web/utils/build.sh
@@ -19,6 +19,7 @@ cd "$THIS_SCRIPT_PATH"
builder_describe \
"Compiles the web-oriented utility function module." \
"@/common/web/keyman-version" \
+ "@/common/web/tslib" \
clean configure build test \
"--ci For use with action ${BUILDER_TERM_START}test${BUILDER_TERM_END} - emits CI-friendly test reports"
@@ -39,16 +40,12 @@ if builder_start_action clean; then
fi
if builder_start_action build; then
- # Note: in a dependency build, we'll expect utils to be built by tsc -b
- if builder_is_dep_build; then
- builder_echo "skipping tsc -b; will be completed by $builder_dep_parent"
- else
- tsc --build "$THIS_SCRIPT_PATH/tsconfig.json"
- node build-bundler.js
-
- # So... tsc does declaration-bundling on its own pretty well, at least for local development.
- tsc --emitDeclarationOnly --outFile ./build/lib/index.d.ts
- fi
+ tsc --build "$THIS_SCRIPT_PATH/tsconfig.json"
+ node build-bundler.js
+
+ # So... tsc does declaration-bundling on its own pretty well, at least for local development.
+ tsc --emitDeclarationOnly --outFile ./build/lib/index.d.ts
+
builder_finish_action success build
fi
diff --git a/common/web/utils/tsconfig.json b/common/web/utils/tsconfig.json
index 838a704dfb31ced141fb365755e78f9450eb7f85..e19eadb2d3efe6a14b9648489b578aec941539fa 100644
--- a/common/web/utils/tsconfig.json
+++ b/common/web/utils/tsconfig.json
@@ -4,6 +4,7 @@
"allowJs": true,
"module": "es6",
"moduleResolution": "Node16",
+ "importHelpers": true,
"inlineSources": true,
"sourceMap": true,
"declaration": true,
diff --git a/common/windows/cpp/include/legacy_kmx_file.h b/common/windows/cpp/include/legacy_kmx_file.h
index ae18543312f2303575ac276e1521c7bff9c20eb7..d961250912f78fc5bab8726af0d652f6b6db86e6 100644
--- a/common/windows/cpp/include/legacy_kmx_file.h
+++ b/common/windows/cpp/include/legacy_kmx_file.h
@@ -409,7 +409,6 @@ typedef COMP_GROUP *PCOMP_GROUP;
typedef struct _COMPILER_OPTIONS {
DWORD dwSize;
BOOL ShouldAddCompilerVersion;
- BOOL UseKmcmpLib;
} COMPILER_OPTIONS;
typedef COMPILER_OPTIONS *PCOMPILER_OPTIONS;
diff --git a/core/doc/hotdoc.json b/core/doc/hotdoc.json
index 61a0028fb0bd464c8e01ff40a8d4b22a32b41d51..4ece50d0f52c505abb5a21b46c5da2e8199d9227 100644
--- a/core/doc/hotdoc.json
+++ b/core/doc/hotdoc.json
@@ -1,10 +1,14 @@
{
"project_name": "@project_name@",
"project_version": "@project_version@",
- "sitemap": "../../doc/sitemap.txt",
- "index": "../../doc/markdown_files/index.md",
+ "sitemap": "@doc_dir@/sitemap.txt",
+ "index": "@doc_dir@/markdown_files/index.md",
"c_sources": [
- "../include/keyboardprocessor.h"
+ "@include_dir@/keyman/keyboardprocessor.h"
+ ],
+ "c_include_directories": [
+ "@include_dir@",
+ "@doc_dir@/../../common/include"
],
"c_smart_index" : true,
"output": ".",
diff --git a/core/doc/meson.build b/core/doc/meson.build
index 2772819ce7ff8b54cea5774e59f13cbea7802a00..01fcb5c98ad60250fb7e3ac3561c5ac5d9791a22 100644
--- a/core/doc/meson.build
+++ b/core/doc/meson.build
@@ -12,18 +12,20 @@ if hotdoc.found()
cfg = configuration_data()
cfg.set('project_name', meson.project_name())
cfg.set('project_version', meson.project_version())
+ cfg.set('doc_dir', meson.current_source_dir())
+ cfg.set('include_dir', meson.current_source_dir() / '../include')
configure_file(input: 'hotdoc.json',
output: 'hotdoc.json',
configuration: cfg)
deps = files(
- '../include/keyman/keyboardprocessor.h.in',
+ '../include/keyman/keyboardprocessor.h',
'../src/jsonpp.hpp',
'../src/utfcodec.hpp'
)
docs = custom_target('docs',
output: ['html'],
- input: ['sitemap.txt', 'markdown_files/index.md'],
+ input: ['sitemap.txt', 'markdown_files/index.md', deps],
command: [hotdoc, '--verbose', '--conf-file=doc/hotdoc.json', '--output=doc', 'run'],
depend_files: deps,
install: true,
diff --git a/core/include/keyman/keyboardprocessor.h b/core/include/keyman/keyboardprocessor.h
index 3513cdede2e88223e47ada46547975b3d3bc856a..429a8ae6cbf9118850a12460db2a289c262920f1 100644
--- a/core/include/keyman/keyboardprocessor.h
+++ b/core/include/keyman/keyboardprocessor.h
@@ -779,7 +779,7 @@ km_kbp_keyboard_get_key_list(km_kbp_keyboard const *keyboard,
km_kbp_keyboard_key **out);
-/*
+/**
```
### `km_kbp_keyboard_key_list_dispose`
##### Description:
@@ -794,28 +794,41 @@ returned by `km_kbp_keyboard_get_key_list`.
KMN_API
void km_kbp_keyboard_key_list_dispose(km_kbp_keyboard_key *key_list);
-
/**
- * Returns the list of IMX libraries and function names that are referenced by
- * the keyboard. The matching dispose call needs to be called to free the memory.
+ * km_kbp_keyboard_get_imx_list:
+ *
+ * Returns: the list of IMX libraries and function names that are referenced by
+ * the keyboard.The matching dispose call needs to be called to free the memory.
*/
KMN_API
-km_kbp_status km_kbp_keyboard_get_imx_list(km_kbp_keyboard const *keyboard, km_kbp_keyboard_imx** imx_list);
+km_kbp_status km_kbp_keyboard_get_imx_list(km_kbp_keyboard const *keyboard, km_kbp_keyboard_imx **imx_list);
/**
+ * km_kbp_keyboard_imx_list_dispose:
+ *
* Disposes of the IMX list
+ *
+ * Returns: --
*/
KMN_API
void km_kbp_keyboard_imx_list_dispose(km_kbp_keyboard_imx *imx_list);
/**
+ * km_kbp_state_imx_register_callback:
+ *
* Register the IMX callback endpoint for the client.
+ *
+ * Returns: --
*/
KMN_API
void km_kbp_state_imx_register_callback(km_kbp_state *state, km_kbp_keyboard_imx_platform imx_callback, void *callback_object);
/**
+ * km_kbp_state_imx_deregister_callback:
+ *
* De-register IMX callback endpoint for the client.
+ *
+ * Returns: --
*/
KMN_API
void km_kbp_state_imx_deregister_callback(km_kbp_state *state);
@@ -1042,6 +1055,8 @@ enum km_kbp_tech_value {
};
/**
+ * km_kbp_event_flags:
+ *
* Bit flags to be used with the event_flags parameter of km_kbp_process_event
*/
enum km_kbp_event_flags {
@@ -1162,10 +1177,8 @@ km_kbp_event(
);
enum km_kbp_event_code {
- /**
- * A keyboard has been activated by the user. The processor may use this
- * event, for example, to switch caps lock state or provide other UX.
- */
+ // A keyboard has been activated by the user. The processor may use this
+ // event, for example, to switch caps lock state or provide other UX.
KM_KBP_EVENT_KEYBOARD_ACTIVATED = 1,
//future: KM_KBP_EVENT_KEYBOARD_DEACTIVATED = 2,
};
diff --git a/developer/src/common/delphi/compiler/compile.pas b/developer/src/common/delphi/compiler/compile.pas
index b89ef0eb72f63f230ba04a9142175b0ff8c278b0..0124150ef6297561d33f62191f572bc06cf03945 100644
--- a/developer/src/common/delphi/compiler/compile.pas
+++ b/developer/src/common/delphi/compiler/compile.pas
@@ -176,7 +176,6 @@ type
TCompilerOptions = record
dwSize: DWORD;
ShouldAddCompilerVersion: BOOL;
- UseKmcmpLib: BOOL;
end;
COMPILER_OPTIONS = TCompilerOptions;
diff --git a/developer/src/common/include/kmn_compiler_errors.h b/developer/src/common/include/kmn_compiler_errors.h
index 6cfae03fc9ea00c559fe53f1b55ddb0e5759b66a..31c5faf0c7b33a806b43ce14d013714ac07f4e54 100644
--- a/developer/src/common/include/kmn_compiler_errors.h
+++ b/developer/src/common/include/kmn_compiler_errors.h
@@ -224,8 +224,8 @@
#define CWARN_KeyShouldIncludeNCaps 0x000020AD
#define CHINT_UnreachableRule 0x000010AE
-#define CHINT_FilenameHasDifferingCase 0x000010AF
-#define CWARN_MissingFile 0x000020B0
+#define CHINT_FilenameHasDifferingCase 0x000010AF // only used in kmcmpdll
+#define CWARN_MissingFile 0x000020B0 // only used in kmcmpdll
#define CERR_BufferOverflow 0x000080C0
#define CERR_Break 0x000080C1
diff --git a/developer/src/common/web/test-helpers/index.ts b/developer/src/common/web/test-helpers/index.ts
index 7f34107dc91a8406b6d82c395f9fc0b0a6f21e36..a134aa67dfd40551100ede0ac74d57ac2ea0a075 100644
--- a/developer/src/common/web/test-helpers/index.ts
+++ b/developer/src/common/web/test-helpers/index.ts
@@ -36,7 +36,7 @@ export class TestCompilerCallbacks implements CompilerCallbacks {
/* CompilerCallbacks */
- loadFile(filename: string | URL): Buffer {
+ loadFile(filename: string): Uint8Array {
try {
return fs.readFileSync(filename);
} catch(e) {
@@ -57,7 +57,10 @@ export class TestCompilerCallbacks implements CompilerCallbacks {
}
resolveFilename(baseFilename: string, filename: string): string {
- const basePath = path.dirname(baseFilename);
+ const basePath =
+ baseFilename.endsWith('/') || baseFilename.endsWith('\\') ?
+ baseFilename :
+ path.dirname(baseFilename);
// Transform separators to platform separators -- we are agnostic
// in our use here but path prefers files may use
// either / or \, although older kps files were always \.
@@ -77,7 +80,7 @@ export class TestCompilerCallbacks implements CompilerCallbacks {
this.messages.push(event);
}
- loadSchema(schema: CompilerSchema): Buffer {
+ loadSchema(schema: CompilerSchema): Uint8Array {
return fs.readFileSync(new URL(SCHEMA_BASE + schema + '.schema.json', import.meta.url));
}
diff --git a/developer/src/kmc-kmn/src/compiler/compiler.ts b/developer/src/kmc-kmn/src/compiler/compiler.ts
index c46b894cb3caa3be6ee6cb430f9befc671468cac..9962ea6abd5048b6a1561de0dcae2605163f36fb 100644
--- a/developer/src/kmc-kmn/src/compiler/compiler.ts
+++ b/developer/src/kmc-kmn/src/compiler/compiler.ts
@@ -42,62 +42,26 @@ const baseOptions: CompilerOptions = {
*/
let callbackProcIdentifier = 0;
-/**
- * Pointer in wasm-space
- */
-type WasmPtr = number;
-
-/**
- * The wrapped functions
- */
-class WasmWrapper {
- Module: any;
-
- compileKeyboardFile?: (pszInfile: string, pszOutfile: string, aSaveDebug: number, aCompilerWarningsAsErrors: number, aWarnDeprecatedCode: number, msgProc: string) => boolean;
- parseUnicodeSet?: (pat: string, buf: WasmPtr, length: number) => number;
- setCompilerOptions?: (shouldAddCompilerVersion: number) => boolean;
-
- constructor(wasmModule: any) {
- this.Module = wasmModule;
- if (!wasmModule) {
- throw Error(`wasm host did not load`);
- }
- this.compileKeyboardFile = this.Module.cwrap('kmcmp_Wasm_CompileKeyboardFile', 'boolean', ['string', 'string', 'number', 'number', 'number', 'string']);
- this.parseUnicodeSet = this.Module.cwrap('kmcmp_Wasm_ParseUnicodeSet', 'number', ['string', 'number', 'number']);
- this.setCompilerOptions = this.Module.cwrap('kmcmp_Wasm_SetCompilerOptions', 'boolean', ['number']);
-
- if (this.parseUnicodeSet === undefined
- || this.setCompilerOptions === undefined
- || this.compileKeyboardFile === undefined) {
- throw Error(`some wasm functions did not load properly.`);
- }
- }
-
- /**
- * Entry point into Wasm functions
- * @returns WasmWrapper
- */
- public static async load() : Promise {
- return new WasmWrapper(await loadWasmHost());
- }
-};
+const
+ callbackPrefix = 'kmnCompilerCallbacks_';
export class KmnCompiler {
- callbackName: string;
+ private Module: any;
+ callbackID: string; // a unique numeric id added to globals with prefixed names
callbacks: CompilerCallbacks;
- wasm: WasmWrapper;
constructor() {
- this.callbackName = 'kmnCompilerCallback' + callbackProcIdentifier;
+ this.callbackID = callbackPrefix + callbackProcIdentifier.toString();
callbackProcIdentifier++;
}
public async init(callbacks: CompilerCallbacks): Promise {
this.callbacks = callbacks;
- if(!this.wasm) {
+ if(!this.Module) {
try {
- this.wasm = await WasmWrapper.load();
+ this.Module = await loadWasmHost();
} catch(e: any) {
+ /* c8 ignore next 3 */
this.callbacks.reportMessage(CompilerMessages.Fatal_MissingWasmModule({e}));
return false;
}
@@ -114,7 +78,9 @@ export class KmnCompiler {
// Can't report a message here.
throw Error('Must call Compiler.init(callbacks) before proceeding');
}
- if(!this.wasm) { // fail if wasm not loaded or function not found
+ if(!this.Module) {
+ /* c8 ignore next 4 */
+ // fail if wasm not loaded or function not found
this.callbacks.reportMessage(CompilerMessages.Fatal_MissingWasmModule({}));
return false;
}
@@ -122,16 +88,7 @@ export class KmnCompiler {
}
public run(infile: string, outfile: string, options?: CompilerOptions): boolean {
- if(!this.verifyInitialized()) {
- return false;
- }
-
- options = {...baseOptions, ...options};
- (globalThis as any)[this.callbackName] = this.compilerMessageCallback;
- // TODO: use callbacks for file access -- so kmc-kmn is entirely fs agnostic
let result = this.runCompiler(infile, outfile, options);
- delete (globalThis as any)[this.callbackName];
- //TODO: write the file out!
if(result) {
if(result.kmx) {
this.callbacks.fs.writeFileSync(result.kmx.filename, result.kmx.data);
@@ -151,17 +108,66 @@ export class KmnCompiler {
return 1;
}
- private runCompiler(infile: string, outfile: string, options: CompilerOptions): CompilerResult {
+ private cachedFile: {filename: string; data: Uint8Array} = {
+ filename: null,
+ data: null
+ };
+
+ private loadFileCallback = (filename: string, baseFilename: string, buffer: number, bufferSize: number): number => {
+ let resolvedFilename = this.callbacks.resolveFilename(baseFilename, filename);
+ let data: Uint8Array;
+ if(this.cachedFile.filename == resolvedFilename) {
+ data = this.cachedFile.data;
+ }
+ else {
+ data = this.callbacks.loadFile(resolvedFilename);
+ if(!data) {
+ return -1;
+ }
+ this.cachedFile.filename = resolvedFilename;
+ this.cachedFile.data = data;
+ }
+
+ if(buffer == 0) {
+ /* We need to return buffer size required */
+ return data.byteLength;
+ }
+
+ if(bufferSize != data.byteLength) {
+ /* c8 ignore next 2 */
+ throw new Error(`loadFileCallback: second call, expected file size ${bufferSize} == ${data.byteLength}`);
+ }
+
+ this.Module.HEAP8.set(data, buffer);
+
+ return 1;
+ }
+
+ public runCompiler(infile: string, outfile: string, options: CompilerOptions): CompilerResult {
+ if(!this.verifyInitialized()) {
+ /* c8 ignore next 2 */
+ return null;
+ }
+
+ options = {...baseOptions, ...options};
+
+ (globalThis as any)[this.callbackID] = {
+ message: this.compilerMessageCallback,
+ loadFile: this.loadFileCallback
+ };
+
let result: CompilerResult = {};
- let wasm_interface = new this.wasm.Module.CompilerInterface();
+ let wasm_interface = new this.Module.CompilerInterface();
+ let wasm_options = new this.Module.CompilerOptions();
let wasm_result = null;
try {
- wasm_interface.saveDebug = options.saveDebug;
- wasm_interface.compilerWarningsAsErrors = options.compilerWarningsAsErrors;
- wasm_interface.warnDeprecatedCode = options.warnDeprecatedCode;
- wasm_interface.messageCallback = this.callbackName;
- wasm_interface.loadFileCallback = this.callbackName; // TODO: this is wrong, needs to be a new callback; not yet used though
- wasm_result = this.wasm.Module.kmcmp_compile(infile, wasm_interface);
+ wasm_options.saveDebug = options.saveDebug;
+ wasm_options.compilerWarningsAsErrors = options.compilerWarningsAsErrors;
+ wasm_options.warnDeprecatedCode = options.warnDeprecatedCode;
+ wasm_options.shouldAddCompilerVersion = options.shouldAddCompilerVersion;
+ wasm_options.target = 0; //CKF_KEYMAN; TODO, support KMW
+ wasm_interface.callbacksKey = this.callbackID; // key of object on globalThis
+ wasm_result = this.Module.kmcmp_compile(infile, wasm_options, wasm_interface);
if(!wasm_result.result) {
return null;
}
@@ -175,11 +181,12 @@ export class KmnCompiler {
result.kmx = {
filename: outfile,
- data: new Uint8Array(this.wasm.Module.HEAP8.buffer, wasm_result.kmx, wasm_result.kmxSize)
+ data: new Uint8Array(this.Module.HEAP8.buffer, wasm_result.kmx, wasm_result.kmxSize)
};
return result;
} catch(e) {
+ /* c8 ignore next 3 */
this.callbacks.reportMessage(CompilerMessages.Fatal_UnexpectedException({e:e}));
return null;
} finally {
@@ -187,6 +194,8 @@ export class KmnCompiler {
wasm_result.delete();
}
wasm_interface.delete();
+ wasm_options.delete();
+ delete (globalThis as any)[this.callbackID];
}
}
@@ -194,20 +203,19 @@ export class KmnCompiler {
// The compiler detected a .kvks file, which needs to be captured
let reader = new KvksFileReader();
kvksFilename = this.callbacks.resolveFilename(kmnFilename, kvksFilename);
- let kvks = reader.read(this.callbacks.loadFile(kvksFilename));
+ let filename = this.callbacks.path.basename(kvksFilename);
+ let kvks = null;
try {
+ kvks = reader.read(this.callbacks.loadFile(kvksFilename));
reader.validate(kvks, this.callbacks.loadSchema('kvks'));
} catch(e) {
- console.log(e);
- // TODO: this.callbacks.reportMessage(CompilerMessages.Error_InvalidKvksFile({e}));
+ this.callbacks.reportMessage(CompilerMessages.Error_InvalidKvksFile({filename, e}));
return null;
}
- let errors: any = []; //TODO: KVKSParseError[];
- let vk = reader.transform(kvks, errors);
- if(!vk || errors.length) {
- console.dir(errors);
- // TODO: this.callbacks.reportMessage(CompilerMessages.Error_InvalidKvksFile({e}));
- return null;
+ let invalidVkeys: string[] = [];
+ let vk = reader.transform(kvks, invalidVkeys);
+ for(let invalidVkey of invalidVkeys) {
+ this.callbacks.reportMessage(CompilerMessages.Warn_InvalidVkeyInKvksFile({filename, invalidVkey}));
}
let writer = new KvkFileWriter();
return {
@@ -224,22 +232,23 @@ export class KmnCompiler {
*/
public parseUnicodeSet(pattern: string, bufferSize: number) : UnicodeSet | null {
if(!this.verifyInitialized()) {
+ /* c8 ignore next 2 */
return null;
}
if (!bufferSize) {
+ /* c8 ignore next 2 */
bufferSize = 100; // TODO-LDML: Preflight mode? Reuse buffer?
}
- const { Module } = this.wasm;
- const buf = Module.asm.malloc(bufferSize * 2 * Module.HEAPU32.BYTES_PER_ELEMENT);
+ const buf = this.Module.asm.malloc(bufferSize * 2 * this.Module.HEAPU32.BYTES_PER_ELEMENT);
// TODO-LDML: Catch OOM
- const rc = this.wasm.parseUnicodeSet(pattern, buf, bufferSize);
+ const rc = this.Module.kmcmp_parseUnicodeSet(pattern, buf, bufferSize);
if (rc >= 0) {
const ranges = [];
- const startu = (buf / Module.HEAPU32.BYTES_PER_ELEMENT);
+ const startu = (buf / this.Module.HEAPU32.BYTES_PER_ELEMENT);
for (let i = 0; i < rc; i++) {
- const low = Module.HEAPU32[startu + (i * 2) + 0];
- const high = Module.HEAPU32[startu + (i * 2) + 1];
+ const low = this.Module.HEAPU32[startu + (i * 2) + 0];
+ const high = this.Module.HEAPU32[startu + (i * 2) + 1];
ranges.push([low, high]);
}
// TODO-LDML: no free??
@@ -276,6 +285,7 @@ function getUnicodeSetError(rc: number) : CompilerEvent {
case KMCMP_FATAL_OUT_OF_RANGE:
return CompilerMessages.Fatal_UnicodeSetOutOfRange();
default:
+ /* c8 ignore next */
return CompilerMessages.Fatal_UnexpectedException({e: `Unexpected UnicodeSet error code ${rc}`});
}
}
diff --git a/developer/src/kmc-kmn/src/compiler/messages.ts b/developer/src/kmc-kmn/src/compiler/messages.ts
index 10578870d7c1499350936abe16981a503cc8167a..0769096e070014b221a545ce4c34736ec95b9d02 100644
--- a/developer/src/kmc-kmn/src/compiler/messages.ts
+++ b/developer/src/kmc-kmn/src/compiler/messages.ts
@@ -67,6 +67,14 @@ export class CompilerMessages {
static Error_UnicodeSetSyntaxError = () => m(this.ERROR_UnicodeSetSyntaxError, `UnicodeSet had a Syntax Error while parsing`);
static ERROR_UnicodeSetSyntaxError = SevError | 0x1007;
+
+ static Error_InvalidKvksFile = (o:{filename: string, e: any}) => m(this.ERROR_InvalidKvksFile,
+ `Error encountered parsing ${o.filename}: ${o.e}`);
+ static ERROR_InvalidKvksFile = SevError | 0x1008;
+
+ static Warn_InvalidVkeyInKvksFile = (o:{filename: string, invalidVkey: string}) => m(this.WARN_InvalidVkeyInKvksFile,
+ `Invalid virtual key ${o.invalidVkey} found in ${o.filename}`);
+ static WARN_InvalidVkeyInKvksFile = SevWarn | 0x1009;
}
export function mapErrorFromKmcmplib(line: number, code: number, msg: string): CompilerEvent {
diff --git a/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_invalid_kvks_file.kmn b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_invalid_kvks_file.kmn
new file mode 100644
index 0000000000000000000000000000000000000000..f37d6397c5099dbbb61bfb3fa464c90c29dc9958
--- /dev/null
+++ b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_invalid_kvks_file.kmn
@@ -0,0 +1,12 @@
+c Description: Verifies that kmc throws an error with an invalid .kvks file
+
+store(&NAME) 'error_invalid_kvks_file'
+store(&VERSION) '9.0'
+store(&VISUALKEYBOARD) 'error_invalid_kvks_file.kvks'
+
+begin unicode > use(main)
+
+group(main) using keys
+
++ [K_A] > 'a'
+'a' + [K_B] > 'ážáŸ’មែរ'
diff --git a/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_invalid_kvks_file.kvks b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_invalid_kvks_file.kvks
new file mode 100644
index 0000000000000000000000000000000000000000..c681064f5b188af0703f5c1a31c66332abccf68c
--- /dev/null
+++ b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_invalid_kvks_file.kvks
@@ -0,0 +1,9 @@
+
+
+
+ 10.0
+ caps_lock_layer_3620
+
+
+
+
diff --git a/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/warn_invalid_vkey_in_kvks_file.kmn b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/warn_invalid_vkey_in_kvks_file.kmn
new file mode 100644
index 0000000000000000000000000000000000000000..18a88973e93d128ea46c93c30f5346cc0395c3b1
--- /dev/null
+++ b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/warn_invalid_vkey_in_kvks_file.kmn
@@ -0,0 +1,12 @@
+c Description: Verifies that kmc reports a warning with an invalid vkey in the kvks
+
+store(&NAME) 'warn_invalid_vkey_in_kvks_file'
+store(&VERSION) '9.0'
+store(&VISUALKEYBOARD) 'warn_invalid_vkey_in_kvks_file.kvks'
+
+begin unicode > use(main)
+
+group(main) using keys
+
++ [K_A] > 'a'
+'a' + [K_B] > 'ážáŸ’មែរ'
diff --git a/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/warn_invalid_vkey_in_kvks_file.kvks b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/warn_invalid_vkey_in_kvks_file.kvks
new file mode 100644
index 0000000000000000000000000000000000000000..f7b247db34e1f9ea8072c40c3a57289d3b461380
--- /dev/null
+++ b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/warn_invalid_vkey_in_kvks_file.kvks
@@ -0,0 +1,14 @@
+
+
+
+ 10.0
+ caps_lock_layer_3620
+
+
+
+
+
+ ážž
+
+
+
diff --git a/developer/src/kmc-kmn/test/helpers/index.ts b/developer/src/kmc-kmn/test/helpers/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8250dfd101a0a07fd2966cf3e5537ac3386a92f5
--- /dev/null
+++ b/developer/src/kmc-kmn/test/helpers/index.ts
@@ -0,0 +1,17 @@
+/**
+ * Helpers and utilities for the Mocha tests.
+ */
+import * as path from 'path';
+import { fileURLToPath } from 'url';
+
+/**
+ * Builds a path to the fixture with the given path components.
+ *
+ * e.g., makePathToFixture('example.qaa.trivial')
+ * e.g., makePathToFixture('example.qaa.trivial', 'model.ts')
+ *
+ * @param components One or more path components.
+ */
+ export function makePathToFixture(...components: string[]): string {
+ return fileURLToPath(new URL(path.join('..', '..', '..', 'test', 'fixtures', ...components), import.meta.url));
+}
diff --git a/developer/src/kmc-kmn/test/test-compiler.ts b/developer/src/kmc-kmn/test/test-compiler.ts
index 925656f8f554e938141b13bbb35f4f7de817ff76..8c353983613f6d766f6fb8780b91153558c8b51c 100644
--- a/developer/src/kmc-kmn/test/test-compiler.ts
+++ b/developer/src/kmc-kmn/test/test-compiler.ts
@@ -86,20 +86,20 @@ describe('Compiler class', function() {
const kmxFixture = fixtureDir + '/binary/caps_lock_layer_3620.kmx';
const kvkFixture = fixtureDir + '/binary/caps_lock_layer_3620.kvk';
- const kmxfile = __dirname + '/caps_lock_layer_3620.kmx';
- const kvkfile = __dirname + '/caps_lock_layer_3620.kvk';
+ const resultingKmxfile = __dirname + '/caps_lock_layer_3620.kmx';
+ const resultingKvkfile = __dirname + '/caps_lock_layer_3620.kvk';
- assert(compiler.run(infile, kmxfile, {saveDebug: true, shouldAddCompilerVersion: false}));
+ assert(compiler.run(infile, resultingKmxfile, {saveDebug: true, shouldAddCompilerVersion: false}));
- assert(fs.existsSync(kmxfile));
- assert(fs.existsSync(kvkfile));
+ assert(fs.existsSync(resultingKmxfile));
+ assert(fs.existsSync(resultingKvkfile));
- const kmxData = fs.readFileSync(kmxfile);
+ const kmxData = fs.readFileSync(resultingKmxfile);
const kmxFixtureData = fs.readFileSync(kmxFixture);
assert.equal(kmxData.byteLength, kmxFixtureData.byteLength);
assert.deepEqual(kmxData, kmxFixtureData);
- const kvkData = fs.readFileSync(kvkfile);
+ const kvkData = fs.readFileSync(resultingKvkfile);
const kvkFixtureData = fs.readFileSync(kvkFixture);
assert.equal(kvkData.byteLength, kvkFixtureData.byteLength);
assert.deepEqual(kvkData, kvkFixtureData);
diff --git a/developer/src/kmc-kmn/test/test-messages.ts b/developer/src/kmc-kmn/test/test-messages.ts
index 0dee53f12112d47b67ca19bc266630a791dd4bf9..4d724daaf5338213125c96876ebef03af699093b 100644
--- a/developer/src/kmc-kmn/test/test-messages.ts
+++ b/developer/src/kmc-kmn/test/test-messages.ts
@@ -1,9 +1,55 @@
import 'mocha';
+import path from 'path';
+import { assert } from 'chai';
import { CompilerMessages } from '../src/compiler/messages.js';
-import { verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers';
+import { TestCompilerCallbacks, verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers';
+import { makePathToFixture } from './helpers/index.js';
+import { KmnCompiler } from '../src/main.js';
describe('CompilerMessages', function () {
+ const callbacks = new TestCompilerCallbacks();
+
it('should have a valid CompilerMessages object', function() {
return verifyCompilerMessagesObject(CompilerMessages);
});
+
+ //
+ // Message tests
+ //
+
+ async function testForMessage(context: Mocha.Context, fixture: string[], messageId?: number) {
+ context.timeout(10000);
+
+ callbacks.clear();
+
+ const compiler = new KmnCompiler();
+ assert(await compiler.init(callbacks));
+ assert(compiler.verifyInitialized());
+
+ const kmnPath = makePathToFixture(...fixture);
+ const outfile = path.basename(kmnPath, '.kmn') + '.kmx';
+
+ // Note: throwing away compile results (just to memory)
+ compiler.runCompiler(kmnPath, outfile, {saveDebug: true, shouldAddCompilerVersion: false});
+
+ if(messageId) {
+ assert.isTrue(callbacks.hasMessage(messageId), `messageId ${messageId.toString(16)} not generated, instead got: `+JSON.stringify(callbacks.messages,null,2));
+ assert.lengthOf(callbacks.messages, 1);
+ } else {
+ assert.lengthOf(callbacks.messages, 0, `messages should be empty, but instead got: `+JSON.stringify(callbacks.messages,null,2));
+ }
+ }
+
+ // ERROR_InvalidKvksFile
+
+ it('should generate ERROR_InvalidKvksFile if the kvks is not valid XML', async function() {
+ await testForMessage(this, ['invalid-keyboards', 'error_invalid_kvks_file.kmn'], CompilerMessages.ERROR_InvalidKvksFile);
+ });
+
+ // WARN_InvalidVkeyInKvksFile
+
+ it('should generate WARN_InvalidVkeyInKvksFile if the kvks contains an invalid virtual key', async function() {
+ await testForMessage(this, ['invalid-keyboards', 'warn_invalid_vkey_in_kvks_file.kmn'], CompilerMessages.WARN_InvalidVkeyInKvksFile);
+ });
+
});
diff --git a/developer/src/kmc-kmn/test/tsconfig.json b/developer/src/kmc-kmn/test/tsconfig.json
index fdb9e7bbabfa8fe9da1e568ed2c915fd546b5c91..eb43dc68f19d86f3f6b9b1d0637e6de450c6bc03 100644
--- a/developer/src/kmc-kmn/test/tsconfig.json
+++ b/developer/src/kmc-kmn/test/tsconfig.json
@@ -13,7 +13,8 @@
},
},
"include": [
- "**/test-*.ts"
+ "**/test-*.ts",
+ "./helpers/index.ts"
],
"references": [
{ "path": "../../../../common/web/keyman-version/" },
diff --git a/developer/src/kmc-ldml/src/compiler/compiler-options.ts b/developer/src/kmc-ldml/src/compiler/compiler-options.ts
index 3d29e6393ad3de65cca0db87f464c8cc4f91c8f1..da81ef2f7d5489f7ccf3b742a4bf0d7088fc6e13 100644
--- a/developer/src/kmc-ldml/src/compiler/compiler-options.ts
+++ b/developer/src/kmc-ldml/src/compiler/compiler-options.ts
@@ -1,4 +1,4 @@
-
+import { LDMLKeyboardXMLSourceFileReaderOptions } from "@keymanapp/common-types";
export interface CompilerOptions {
/**
@@ -10,4 +10,9 @@ export interface CompilerOptions {
* Add metadata about the compiler version to .kmx file when compiling
*/
addCompilerVersion?: boolean;
+
+ /**
+ * Paths and other options required for reading .xml files
+ */
+ readerOptions: LDMLKeyboardXMLSourceFileReaderOptions;
};
diff --git a/developer/src/kmc-ldml/src/compiler/compiler.ts b/developer/src/kmc-ldml/src/compiler/compiler.ts
index b7b46e49b2a2e9304f0b2616ff9d22eab6ab0268..4b1472a58530dac22ca620b783c7c4d8b68290df 100644
--- a/developer/src/kmc-ldml/src/compiler/compiler.ts
+++ b/developer/src/kmc-ldml/src/compiler/compiler.ts
@@ -30,16 +30,14 @@ const SECTION_COMPILERS = [
export class LdmlKeyboardCompiler {
private readonly callbacks: CompilerCallbacks;
- // private readonly options: CompilerOptions; // not currently used
+ private readonly options: CompilerOptions;
- constructor (callbacks: CompilerCallbacks, _options?: CompilerOptions) {
- /*
+ constructor (callbacks: CompilerCallbacks, options: CompilerOptions) {
this.options = {
debug: false,
addCompilerVersion: true,
...options
};
- */
this.callbacks = callbacks;
}
@@ -54,7 +52,7 @@ export class LdmlKeyboardCompiler {
* @returns the source file, or null if invalid
*/
public load(filename: string): LDMLKeyboardXMLSourceFile | null {
- const reader = new LDMLKeyboardXMLSourceFileReader(this.callbacks);
+ const reader = new LDMLKeyboardXMLSourceFileReader(this.options.readerOptions, this.callbacks);
const data = this.callbacks.loadFile(filename);
if(!data) {
this.callbacks.reportMessage(CompilerMessages.Error_InvalidFile({errorText: 'Unable to read XML file'}));
@@ -84,7 +82,7 @@ export class LdmlKeyboardCompiler {
* @returns the source file, or null if invalid
*/
public loadTestData(filename: string): LDMLKeyboardTestDataXMLSourceFile | null {
- const reader = new LDMLKeyboardXMLSourceFileReader(this.callbacks);
+ const reader = new LDMLKeyboardXMLSourceFileReader(this.options.readerOptions, this.callbacks);
const data = this.callbacks.loadFile(filename);
if(!data) {
this.callbacks.reportMessage(CompilerMessages.Error_InvalidFile({errorText: 'Unable to read XML file'}));
diff --git a/developer/src/kmc-ldml/test/helpers/index.ts b/developer/src/kmc-ldml/test/helpers/index.ts
index 8ba8a28aa84ded8a6549461d6ea8b657302bb431..f10af945c82488d9e1543efbe9e623a138488c12 100644
--- a/developer/src/kmc-ldml/test/helpers/index.ts
+++ b/developer/src/kmc-ldml/test/helpers/index.ts
@@ -33,6 +33,12 @@ export function makePathToFixture(...components: string[]): string {
export const compilerTestCallbacks = new TestCompilerCallbacks();
+export const compilerTestOptions: CompilerOptions = {
+ readerOptions: {
+ importsPath: fileURLToPath(LDMLKeyboardXMLSourceFileReader.defaultImportsURL)
+ }
+};
+
beforeEach(function() {
compilerTestCallbacks.clear();
});
@@ -43,14 +49,13 @@ afterEach(function() {
}
});
-
export function loadSectionFixture(compilerClass: typeof SectionCompiler, filename: string, callbacks: TestCompilerCallbacks): Section {
callbacks.messages = [];
const inputFilename = makePathToFixture(filename);
const data = callbacks.loadFile(inputFilename);
assert.isNotNull(data);
- const reader = new LDMLKeyboardXMLSourceFileReader(callbacks);
+ const reader = new LDMLKeyboardXMLSourceFileReader(compilerTestOptions.readerOptions, callbacks);
const source = reader.load(data);
assert.isNotNull(source);
diff --git a/developer/src/kmc-ldml/test/test-compiler-e2e.ts b/developer/src/kmc-ldml/test/test-compiler-e2e.ts
index ec711549051a4f4a5d43911f8ae8b2924996b962..7d8b179f3125782b8ae557143558a344afa61b86 100644
--- a/developer/src/kmc-ldml/test/test-compiler-e2e.ts
+++ b/developer/src/kmc-ldml/test/test-compiler-e2e.ts
@@ -2,7 +2,7 @@ import 'mocha';
import {assert} from 'chai';
import x_hextobin from '@keymanapp/hextobin';
import { KMXBuilder } from '@keymanapp/common-types';
-import {checkMessages, compileKeyboard, makePathToFixture} from './helpers/index.js';
+import {checkMessages, compileKeyboard, compilerTestOptions, makePathToFixture} from './helpers/index.js';
const hextobin = (x_hextobin as any).default;
@@ -17,7 +17,7 @@ describe('compiler-tests', function() {
const binaryFilename = makePathToFixture('basic.txt');
// Compile the keyboard
- const kmx = compileKeyboard(inputFilename, {debug: true, addCompilerVersion: false});
+ const kmx = compileKeyboard(inputFilename, {...compilerTestOptions, debug: true, addCompilerVersion: false});
assert.isNotNull(kmx);
// Use the builder to generate the binary output file
diff --git a/developer/src/kmc-ldml/test/test-keymanweb-compiler.ts b/developer/src/kmc-ldml/test/test-keymanweb-compiler.ts
index fb4f28daeeedf1fef9a6c191df57b55e0c5f2933..711820e5e4051a751304f1620369e7f1f131d151 100644
--- a/developer/src/kmc-ldml/test/test-keymanweb-compiler.ts
+++ b/developer/src/kmc-ldml/test/test-keymanweb-compiler.ts
@@ -1,6 +1,6 @@
import 'mocha';
import { assert } from 'chai';
-import { checkMessages, compilerTestCallbacks, makePathToFixture } from './helpers/index.js';
+import { checkMessages, compilerTestCallbacks, compilerTestOptions, makePathToFixture } from './helpers/index.js';
import { LdmlKeyboardKeymanWebCompiler } from '../src/compiler/keymanweb-compiler.js';
import { LdmlKeyboardCompiler } from '../src/compiler/compiler.js';
import * as fs from 'fs';
@@ -16,7 +16,7 @@ describe('LdmlKeyboardKeymanWebCompiler', function() {
// Load input data; we'll use the LDML keyboard compiler loader to save us
// effort here
- const k = new LdmlKeyboardCompiler(compilerTestCallbacks, {debug: true, addCompilerVersion: false});
+ const k = new LdmlKeyboardCompiler(compilerTestCallbacks, {...compilerTestOptions, debug: true, addCompilerVersion: false});
const source = k.load(inputFilename);
checkMessages();
assert.isNotNull(source, 'k.load should not have returned null');
@@ -27,7 +27,7 @@ describe('LdmlKeyboardKeymanWebCompiler', function() {
assert.isTrue(valid, 'k.validate should not have failed');
// Actual test: compile to javascript
- const jsCompiler = new LdmlKeyboardKeymanWebCompiler(compilerTestCallbacks, {debug: true});
+ const jsCompiler = new LdmlKeyboardKeymanWebCompiler(compilerTestCallbacks, {...compilerTestOptions, debug: true});
const output = jsCompiler.compile('basic.xml', source);
assert.isNotNull(output);
@@ -36,7 +36,7 @@ describe('LdmlKeyboardKeymanWebCompiler', function() {
assert.strictEqual(output, outputFixture);
// Second test: compile to javascript without debug formatting
- const jsCompilerNoDebug = new LdmlKeyboardKeymanWebCompiler(compilerTestCallbacks, {debug: false});
+ const jsCompilerNoDebug = new LdmlKeyboardKeymanWebCompiler(compilerTestCallbacks, {...compilerTestOptions, debug: false});
const outputNoDebug = jsCompilerNoDebug.compile('basic.xml', source);
assert.isNotNull(outputNoDebug);
diff --git a/developer/src/kmc-ldml/test/test-metadata-compiler.ts b/developer/src/kmc-ldml/test/test-metadata-compiler.ts
index 59500d09950c313568948fb70b8322ea0799b0d7..ede77ed4bccfba209a0d6f1e570ffa9bd56e7755 100644
--- a/developer/src/kmc-ldml/test/test-metadata-compiler.ts
+++ b/developer/src/kmc-ldml/test/test-metadata-compiler.ts
@@ -1,6 +1,6 @@
import 'mocha';
import { assert } from 'chai';
-import { checkMessages, compileKeyboard, makePathToFixture } from './helpers/index.js';
+import { checkMessages, compileKeyboard, compilerTestOptions, makePathToFixture } from './helpers/index.js';
import { KMX } from '@keymanapp/common-types';
import KEYMAN_VERSION from '@keymanapp/keyman-version';
@@ -13,7 +13,7 @@ describe('kmx metadata compiler', function () {
const inputFilename = makePathToFixture('basic.xml');
// Compile the keyboard
- const kmx = compileKeyboard(inputFilename, {debug:true, addCompilerVersion:true});
+ const kmx = compileKeyboard(inputFilename, {...compilerTestOptions, debug:true, addCompilerVersion:true});
checkMessages();
assert.isNotNull(kmx);
@@ -48,7 +48,7 @@ describe('kmx metadata compiler', function () {
const inputFilename = makePathToFixture('basic.xml');
// Compile the keyboard
- const kmx = compileKeyboard(inputFilename, {debug:true, addCompilerVersion:false});
+ const kmx = compileKeyboard(inputFilename, {...compilerTestOptions, debug:true, addCompilerVersion:false});
checkMessages();
assert.isNotNull(kmx);
diff --git a/developer/src/kmc-ldml/test/test-testdata-e2e.ts b/developer/src/kmc-ldml/test/test-testdata-e2e.ts
index 25ac1963213a5fed475292821837b4fbb8c005fb..ece4ce85608174bac50522c48810ca3f89fac3df 100644
--- a/developer/src/kmc-ldml/test/test-testdata-e2e.ts
+++ b/developer/src/kmc-ldml/test/test-testdata-e2e.ts
@@ -1,7 +1,7 @@
import { readFileSync } from 'fs';
import 'mocha';
import {assert} from 'chai';
-import {loadTestdata, makePathToFixture} from './helpers/index.js';
+import {compilerTestOptions, loadTestdata, makePathToFixture} from './helpers/index.js';
describe('testdata-tests', function() {
this.slow(500); // 0.5 sec -- json schema validation takes a while
@@ -14,7 +14,7 @@ describe('testdata-tests', function() {
const jsonFilename = makePathToFixture('test-fr.json');
// Compile the keyboard
- const testData = loadTestdata(inputFilename, {debug: true, addCompilerVersion: false});
+ const testData = loadTestdata(inputFilename, {...compilerTestOptions, debug: true, addCompilerVersion: false});
assert.isNotNull(testData);
const jsonData = JSON.parse(readFileSync(jsonFilename, 'utf-8'));
diff --git a/developer/src/kmc-ldml/test/test-visual-keyboard-compiler-e2e.ts b/developer/src/kmc-ldml/test/test-visual-keyboard-compiler-e2e.ts
index 1b1e34069e5ac2b13982f5ee18654b80d1bacb6f..4af68278387c8dd89283108fcbe6d031ee8b51a8 100644
--- a/developer/src/kmc-ldml/test/test-visual-keyboard-compiler-e2e.ts
+++ b/developer/src/kmc-ldml/test/test-visual-keyboard-compiler-e2e.ts
@@ -2,7 +2,7 @@ import 'mocha';
import {assert} from 'chai';
import x_hextobin from '@keymanapp/hextobin';
import { KvkFileWriter } from '@keymanapp/common-types';
-import {checkMessages, compileVisualKeyboard, makePathToFixture} from './helpers/index.js';
+import {checkMessages, compilerTestOptions, compileVisualKeyboard, makePathToFixture} from './helpers/index.js';
const hextobin = (x_hextobin as any).default;
@@ -17,7 +17,7 @@ describe('visual-keyboard-compiler', function() {
const binaryFilename = makePathToFixture('basic-kvk.txt');
// Compile the visual keyboard
- const vk = compileVisualKeyboard(inputFilename, {debug: true, addCompilerVersion: false});
+ const vk = compileVisualKeyboard(inputFilename, {...compilerTestOptions, debug: true, addCompilerVersion: false});
assert.isNotNull(vk);
// Use the builder to generate the binary output file
diff --git a/developer/src/kmc-package/src/compiler/kmp-compiler.ts b/developer/src/kmc-package/src/compiler/kmp-compiler.ts
index b660641df9c564972064f4ed644433dd793c3f65..198a1ef2de2823b43902867c517aa9b85aa66b91 100644
--- a/developer/src/kmc-package/src/compiler/kmp-compiler.ts
+++ b/developer/src/kmc-package/src/compiler/kmp-compiler.ts
@@ -16,7 +16,12 @@ export class KmpCompiler {
public transformKpsToKmpObject(kpsFilename: string): KmpJsonFile.KmpJsonFile {
// Load the KPS data from XML as JS structured data.
- const data = this.callbacks.fs.readFileSync(kpsFilename, 'utf-8');
+ const buffer = this.callbacks.loadFile(kpsFilename);
+ if(!buffer) {
+ this.callbacks.reportMessage(CompilerMessages.Error_FileDoesNotExist({filename: kpsFilename}));
+ return null;
+ }
+ const data = new TextDecoder().decode(buffer);
const kpsPackage = (() => {
let a: KpsFile.KpsPackage;
@@ -24,7 +29,8 @@ export class KmpCompiler {
tagNameProcessors: [xml2js.processors.firstCharLowerCase],
explicitArray: false
});
- parser.parseString(data, (e: unknown, r: unknown) => { a = r as KpsFile.KpsPackage });
+ // TODO: add unit test for xml errors parsing .kps file
+ parser.parseString(data, (e: unknown, r: unknown) => { if(e) throw e; a = r as KpsFile.KpsPackage });
return a;
})();
@@ -284,9 +290,20 @@ export class KmpCompiler {
* we want that to remain the responsibility of the keyboard compiler, so we'll warn the
* few users who are still doing this
*/
- private warnIfKvkFileIsNotBinary(filename: string, data: Buffer) {
- // TODO: Buffer is not available on web
- if(filename.match(/\.kvk$/) && data.compare(Buffer.from(KvkFile.KVK_HEADER_IDENTIFIER_BYTES), 0, 3, 0, 3) != 0) {
+ private warnIfKvkFileIsNotBinary(filename: string, data: Uint8Array) {
+ if(!filename.match(/\.kvk$/)) {
+ return;
+ }
+
+ if(data.byteLength < 4) {
+ // TODO: Not a valid .kvk file; should we be reporting this?
+ return;
+ }
+
+ if(data[0] != KvkFile.KVK_HEADER_IDENTIFIER_BYTES[0] ||
+ data[1] != KvkFile.KVK_HEADER_IDENTIFIER_BYTES[1] ||
+ data[2] != KvkFile.KVK_HEADER_IDENTIFIER_BYTES[2] ||
+ data[3] != KvkFile.KVK_HEADER_IDENTIFIER_BYTES[3]) {
this.callbacks.reportMessage(CompilerMessages.Warn_FileIsNotABinaryKvkFile({filename: filename}));
}
}
diff --git a/developer/src/kmc/src/commands/build/BuildLdmlKeyboard.ts b/developer/src/kmc/src/commands/build/BuildLdmlKeyboard.ts
index 024f7f534002f6231322b561c7b36c0315debe04..40b9513d320e1971824a93f30c62c3f4ea206604 100644
--- a/developer/src/kmc/src/commands/build/BuildLdmlKeyboard.ts
+++ b/developer/src/kmc/src/commands/build/BuildLdmlKeyboard.ts
@@ -1,8 +1,9 @@
import * as path from 'path';
import * as fs from 'fs';
import * as kmcLdml from '@keymanapp/kmc-ldml';
-import { KvkFileWriter, CompilerCallbacks } from '@keymanapp/common-types';
+import { KvkFileWriter, CompilerCallbacks, LDMLKeyboardXMLSourceFileReader } from '@keymanapp/common-types';
import { BuildActivity, BuildActivityOptions } from './BuildActivity.js';
+import { fileURLToPath } from 'url';
export class BuildLdmlKeyboard extends BuildActivity {
public get name(): string { return 'LDML keyboard'; }
@@ -46,11 +47,14 @@ function buildLdmlKeyboardToMemory(inputFilename: string, callbacks: CompilerCal
let compilerOptions: kmcLdml.CompilerOptions = {
debug: options.debug ?? false,
addCompilerVersion: options.compilerVersion ?? true,
+ readerOptions: {
+ importsPath: fileURLToPath(LDMLKeyboardXMLSourceFileReader.defaultImportsURL)
+ }
// TODO: warnDeprecatedCode: options.warnDeprecatedCode,
// TODO: treatWarningsAsErrors: options.treatWarningsAsErrors,
}
- const k = new kmcLdml.LdmlKeyboardCompiler(callbacks, options);
+ const k = new kmcLdml.LdmlKeyboardCompiler(callbacks, compilerOptions);
let source = k.load(inputFilename);
if (!source) {
return [null, null, null];
diff --git a/developer/src/kmc/src/commands/buildTestData/index.ts b/developer/src/kmc/src/commands/buildTestData/index.ts
index 0990b91edb55de750acdb544ffa0a6e1105cbd09..b6ebf34a739326e797565407d2dddb217a39a622 100644
--- a/developer/src/kmc/src/commands/buildTestData/index.ts
+++ b/developer/src/kmc/src/commands/buildTestData/index.ts
@@ -1,8 +1,9 @@
import * as fs from 'fs';
import * as path from 'path';
import * as kmc from '@keymanapp/kmc-ldml';
-import { CompilerCallbacks, LDMLKeyboardTestDataXMLSourceFile } from '@keymanapp/common-types';
+import { CompilerCallbacks, LDMLKeyboardTestDataXMLSourceFile, LDMLKeyboardXMLSourceFileReader } from '@keymanapp/common-types';
import { NodeCompilerCallbacks } from '../../messages/NodeCompilerCallbacks.js';
+import { fileURLToPath } from 'url';
export interface BuildTestDataOptions {
outFile?: string;
@@ -11,7 +12,10 @@ export interface BuildTestDataOptions {
export function buildTestData(infile: string, options: BuildTestDataOptions) {
let compilerOptions: kmc.CompilerOptions = {
debug: false,
- addCompilerVersion: false
+ addCompilerVersion: false,
+ readerOptions: {
+ importsPath: fileURLToPath(LDMLKeyboardXMLSourceFileReader.defaultImportsURL)
+ }
};
let testData = loadTestData(infile, compilerOptions);
diff --git a/developer/src/kmc/src/messages/NodeCompilerCallbacks.ts b/developer/src/kmc/src/messages/NodeCompilerCallbacks.ts
index ceae18c4c59e7f8165a39460fab40558621fd211..dfcb7e2ecbfae760a3c72f66b9b1baad50819040 100644
--- a/developer/src/kmc/src/messages/NodeCompilerCallbacks.ts
+++ b/developer/src/kmc/src/messages/NodeCompilerCallbacks.ts
@@ -1,14 +1,52 @@
import * as fs from 'fs';
import * as path from 'path';
import { CompilerCallbacks, CompilerSchema, CompilerEvent, compilerErrorSeverityName, CompilerPathCallbacks, CompilerFileSystemCallbacks } from '@keymanapp/common-types';
+import { InfrastructureMessages } from './messages.js';
/**
* Concrete implementation for CLI use
*/
+// TODO: Make a common class for all the CompilerCallbacks implementations
+
export class NodeCompilerCallbacks implements CompilerCallbacks {
- // TODO: REMOVE!
- loadFile(filename: string | URL): Buffer {
+ /* NodeCompilerCallbacks */
+
+ messages: CompilerEvent[] = [];
+ silent: boolean;
+
+ constructor(silent?: boolean) {
+ this.silent = !!silent;
+ }
+
+ clear() {
+ this.messages = [];
+ }
+
+ hasMessage(code: number): boolean {
+ return this.messages.find((item) => item.code == code) === undefined ? false : true;
+ }
+
+ private verifyFilenameConsistency(originalFilename: string): void {
+ if(fs.existsSync(originalFilename)) {
+ // Note, we only check this if the file exists, because
+ // if it is not found, that will be returned as an error
+ // from loadFile anyway.
+ const filename = fs.realpathSync(originalFilename);
+ const nativeFilename = fs.realpathSync.native(filename);
+ if(filename != nativeFilename) {
+ this.reportMessage(InfrastructureMessages.Hint_FilenameHasDifferingCase({
+ reference: originalFilename,
+ filename: nativeFilename
+ }));
+ }
+ }
+ }
+
+ /* CompilerCallbacks */
+
+ loadFile(filename: string): Uint8Array {
+ this.verifyFilenameConsistency(filename);
try {
return fs.readFileSync(filename);
} catch (e) {
@@ -29,6 +67,10 @@ export class NodeCompilerCallbacks implements CompilerCallbacks {
}
reportMessage(event: CompilerEvent): void {
+ this.messages.push(event);
+ if(this.silent) {
+ return;
+ }
const code = event.code.toString(16);
if(event.line) {
console.log(`${compilerErrorSeverityName(event.code)} ${code} [${event.line}]: ${event.message}`);
@@ -41,7 +83,7 @@ export class NodeCompilerCallbacks implements CompilerCallbacks {
console.debug(msg);
}
- loadSchema(schema: CompilerSchema) {
+ loadSchema(schema: CompilerSchema): Uint8Array {
let schemaPath = new URL('../util/' + schema + '.schema.json', import.meta.url);
return fs.readFileSync(schemaPath);
}
@@ -51,7 +93,10 @@ export class NodeCompilerCallbacks implements CompilerCallbacks {
}
resolveFilename(baseFilename: string, filename: string) {
- const basePath = path.dirname(baseFilename);
+ const basePath =
+ baseFilename.endsWith('/') || baseFilename.endsWith('\\') ?
+ baseFilename :
+ path.dirname(baseFilename);
// Transform separators to platform separators -- we are agnostic
// in our use here but path prefers files may use
// either / or \, although older kps files were always \.
diff --git a/developer/src/kmc/src/messages/messages.ts b/developer/src/kmc/src/messages/messages.ts
index 1f1761e715d6f70f523d95d074b2eebab1fe1500..358a59406bb3a80f1c025f9c88ed80a9e1945009 100644
--- a/developer/src/kmc/src/messages/messages.ts
+++ b/developer/src/kmc/src/messages/messages.ts
@@ -2,7 +2,7 @@ import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m
const Namespace = CompilerErrorNamespace.Infrastructure;
const SevInfo = CompilerErrorSeverity.Info | Namespace;
-// const SevHint = CompilerErrorSeverity.Hint | Namespace;
+const SevHint = CompilerErrorSeverity.Hint | Namespace;
// const SevWarn = CompilerErrorSeverity.Warn | Namespace;
const SevError = CompilerErrorSeverity.Error | Namespace;
const SevFatal = CompilerErrorSeverity.Fatal | Namespace;
@@ -39,5 +39,9 @@ export class InfrastructureMessages {
static Error_InvalidProjectFile = (o:{message:string}) => m(this.ERROR_InvalidProjectFile,
`Project file is not valid: ${o.message}`);
static ERROR_InvalidProjectFile = SevError | 0x0008;
+
+ static Hint_FilenameHasDifferingCase = (o:{reference:string, filename:string}) => m(this.HINT_FilenameHasDifferingCase,
+ `File ${o.filename} differs in case from reference ${o.reference}; this will fail on platforms with case-sensitive filesystems.`);
+ static HINT_FilenameHasDifferingCase = SevHint | 0x0009;
}
diff --git a/developer/src/kmc/test/fixtures/invalid-keyboards/hint_filename_has_differing_case.kmn b/developer/src/kmc/test/fixtures/invalid-keyboards/hint_filename_has_differing_case.kmn
new file mode 100644
index 0000000000000000000000000000000000000000..5792016e10383d7bee622a0a49d6ec5e9ee03e09
--- /dev/null
+++ b/developer/src/kmc/test/fixtures/invalid-keyboards/hint_filename_has_differing_case.kmn
@@ -0,0 +1,6 @@
+store(&name) 'hint_filename_has_differing_case'
+store(&version) '7.0'
+
+begin unicode > use(main)
+
+group(main) using keys
diff --git a/developer/src/kmc/test/test-messages.ts b/developer/src/kmc/test/test-messages.ts
index 7b591ad72de7a2e3dec348e55f52155c77030cbb..045718075daaf989c6871ede3648af8fe5e0d675 100644
--- a/developer/src/kmc/test/test-messages.ts
+++ b/developer/src/kmc/test/test-messages.ts
@@ -1,9 +1,80 @@
import 'mocha';
+import { assert } from 'chai';
import { InfrastructureMessages } from '../src/messages/messages.js';
import { verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers';
+import { makePathToFixture } from './helpers/index.js';
+import { NodeCompilerCallbacks } from '../src/messages/NodeCompilerCallbacks.js';
describe('InfrastructureMessages', function () {
it('should have a valid InfrastructureMessages object', function() {
return verifyCompilerMessagesObject(InfrastructureMessages);
});
+
+ //
+ // Message tests
+ //
+
+ /*
+ TODO:
+
+ let callbacks = new TestCompilerCallbacks();
+
+ async function testForMessage(context: Mocha.Context, fixture: string[], messageId?: number) {
+ context.timeout(10000);
+
+ callbacks.clear();
+
+ const builder = new BuildKmnKeyboard();
+ const path = makePathToFixture(...fixture);
+ let result = await builder.build(path, callbacks, {
+ compilerVersion: false,
+ compilerWarningsAsErrors: true,
+ debug: false,
+ warnDeprecatedCode: true,
+ });
+
+ if(messageId) {
+ assert.isTrue(callbacks.hasMessage(messageId), `messageId ${messageId.toString(16)} not generated, instead got: `+JSON.stringify(callbacks.messages,null,2));
+ assert.lengthOf(callbacks.messages, 1);
+ } else {
+ assert.lengthOf(callbacks.messages, 0, `messages should be empty, but instead got: `+JSON.stringify(callbacks.messages,null,2));
+ assert.isTrue(result);
+ }
+ }
+
+ // ERROR_FileDoesNotExist
+
+ it('should generate ERROR_FileDoesNotExist if a file does not exist', async function() {
+ await testForMessage(this, ['invalid-keyboards', 'error_file_does_not_exist.kmn'], CompilerMessages.ERROR_FileDoesNotExist);
+ });
+
+ // ERROR_FileTypeNotRecognized
+
+ it('should generate ERROR_FileTypeNotRecognized if a file is not a recognized type', async function() {
+ await testForMessage(this, ['invalid-keyboards', 'error_file_type_not_recognized.xxx'], CompilerMessages.ERROR_FileTypeNotRecognized);
+ });
+
+ // ERROR_OutFileNotValidForProjects
+
+ it('should generate ERROR_OutFileNotValidForProjects if an output file is specified for a project build', async function() {
+ await testForMessage(this, ['invalid-keyboards', 'error_out_file_not_valid_for_projects.kpj'], CompilerMessages.ERROR_OutFileNotValidForProjects);
+ });
+
+ // ERROR_InvalidProjectFile
+
+ it('should generate ERROR_InvalidProjectFile if a project file is invalid', async function() {
+ await testForMessage(this, ['invalid-keyboards', 'error_invalid_project_file.kpj'], CompilerMessages.ERROR_InvalidProjectFile);
+ });
+ */
+
+ // HINT_FilenameHasDifferingCase
+
+ it('should generate HINT_FilenameHasDifferingCase if a referenced file has differing case', async function() {
+ // This message is generated by NodeCompilerCallbacks, because that's where the filesystem is visible,
+ // so we can't use our usual testForMessage pattern.
+ const ncb = new NodeCompilerCallbacks(true);
+ ncb.loadFile(makePathToFixture('invalid-keyboards', 'Hint_Filename_Has_Differing_Case.kmn'));
+ assert.isTrue(ncb.hasMessage(InfrastructureMessages.HINT_FilenameHasDifferingCase),
+ `HINT_FilenameHasDifferingCase not generated, instead got: `+JSON.stringify(ncb.messages,null,2));
+ });
});
diff --git a/developer/src/kmcmpdll/Compiler.cpp b/developer/src/kmcmpdll/Compiler.cpp
index 8cc28d2ab7f18bfdc5e6e65dd78b99bf1fe8afe4..100e43fc5bd4356d1fe7cd95b427b6e28a6afe6d 100644
--- a/developer/src/kmcmpdll/Compiler.cpp
+++ b/developer/src/kmcmpdll/Compiler.cpp
@@ -90,8 +90,6 @@
#include "UnreachableRules.h"
#include "CheckForDuplicates.h"
-#include "../kmcmplib/include/kmcmplibapi.h"
-
int xatoi(PWSTR *p);
int atoiW(PWSTR p);
void safe_wcsncpy(PWSTR out, PWSTR in, int cbMax);
@@ -291,33 +289,15 @@ BOOL AddCompileMessage(DWORD msg)
return FALSE;
}
-bool flag_use_new_kmcomp = true; // flag to switch to new kmcompx
-
extern "C" BOOL __declspec(dllexport) SetCompilerOptions(PCOMPILER_OPTIONS options) {
if(!options || options->dwSize < sizeof(COMPILER_OPTIONS)) {
return FALSE;
}
- flag_use_new_kmcomp = options->UseKmcmpLib;
-
- //printf("\n---> started in SetCompilerOptions() of kmcmpdll\n");
- if ( flag_use_new_kmcomp )
- {
- KMCMP_COMPILER_OPTIONS kmcmp_options = {0};
- kmcmp_options.dwSize = sizeof(KMCMP_COMPILER_OPTIONS);
- kmcmp_options.ShouldAddCompilerVersion = options->ShouldAddCompilerVersion;
- return kmcmp_SetCompilerOptions(&kmcmp_options);
- }
- //printf("---> stayed in SetCompilerOptions() of kmcmpdll\n");
-
FShouldAddCompilerVersion = options->ShouldAddCompilerVersion;
return TRUE;
}
-int kmcmpMsgproc(int line, uint32_t dwMsgCode, char* szText, void* context) {
- return static_cast(context)(line, dwMsgCode, szText);
-}
-
extern "C" BOOL __declspec(dllexport) CompileKeyboardFile(PSTR pszInfile, PSTR pszOutfile, BOOL ASaveDebug, BOOL ACompilerWarningsAsErrors, BOOL AWarnDeprecatedCode, CompilerMessageProc pMsgProc) // I4865 // I4866
{
HANDLE hInfile = INVALID_HANDLE_VALUE, hOutfile = INVALID_HANDLE_VALUE;
@@ -325,15 +305,6 @@ extern "C" BOOL __declspec(dllexport) CompileKeyboardFile(PSTR pszInfile, PSTR p
DWORD len;
char str[260];
- //printf("\n---> started in CompileKeyboardFile() of kmcmpdll\n");
-
- if ( flag_use_new_kmcomp )
- {
- return kmcmp_CompileKeyboardFile(pszInfile, pszOutfile, ASaveDebug, ACompilerWarningsAsErrors,AWarnDeprecatedCode, kmcmpMsgproc, (void*) pMsgProc);
- }
-
- //printf("---> stayed in CompileKeyboardFile() of kmcmpdll\n");
-
FSaveDebug = ASaveDebug;
FCompilerWarningsAsErrors = ACompilerWarningsAsErrors; // I4865
FWarnDeprecatedCode = AWarnDeprecatedCode; // I4866
@@ -418,15 +389,6 @@ extern "C" BOOL __declspec(dllexport) CompileKeyboardFileToBuffer(PSTR pszInfile
DWORD len;
char str[260];
- //printf("\n---> started in CompileKeyboardFileToBuffer() of kmcmpdll\n");
- if ( flag_use_new_kmcomp )
- {
- return kmcmp_CompileKeyboardFileToBuffer( pszInfile, (void*) pfkBuffer, ACompilerWarningsAsErrors, AWarnDeprecatedCode, kmcmpMsgproc, (void*) pMsgProc, Target);
- }
-
- //printf("---> stayed in CompileKeyboardFileToBuffer() of kmcmpdll\n");
-
-
FSaveDebug = TRUE; // I3681
FCompilerWarningsAsErrors = ACompilerWarningsAsErrors; // I4865
FWarnDeprecatedCode = AWarnDeprecatedCode; // I4866
diff --git a/developer/src/kmcmpdll/json-validation.cpp b/developer/src/kmcmpdll/json-validation.cpp
index e35dc0485dcae1480b00b1f002bad5fa75b56d26..c3c1af0484318b176cacef53dec22f58c7f2f118 100644
--- a/developer/src/kmcmpdll/json-validation.cpp
+++ b/developer/src/kmcmpdll/json-validation.cpp
@@ -1,14 +1,11 @@
#include
#include
-#include "../kmcmplib/include/kmcmplibapi.h"
#include
typedef bool (*kmcmp_ValidateJsonMessageProc)(int64_t offset, const char* szText, void* context);
-extern bool flag_use_new_kmcomp;
-
using nlohmann::json;
using nlohmann::json_uri;
using nlohmann::json_schema_draft4::json_validator;
@@ -35,13 +32,6 @@ bool kmcmpMessageProc(int64_t offset, const char* szText, void* context) {
extern "C" BOOL __declspec(dllexport) ValidateJsonFile(PWSTR pwszSchemaFile, PWSTR pwszJsonFile, ValidateJsonMessageProc MessageProc) {
- if ( flag_use_new_kmcomp )
- {
- std::fstream f(pwszSchemaFile);
- std::fstream fd(pwszJsonFile);
- return kmcmp_ValidateJsonFile(f, fd, kmcmpMessageProc, (void*)MessageProc);
- }
-
std::fstream f(pwszSchemaFile);
if (!f.good()) {
MessageProc(-1, "Schema file could not be loaded.");
diff --git a/developer/src/kmcmpdll/kmcmpdll.vcxproj b/developer/src/kmcmpdll/kmcmpdll.vcxproj
index c7f7e1f48f062c068d8cc15e1846f86f25942d9f..2bcc038bd00df813dc7fc9cd7db7a3f3b268d034 100644
--- a/developer/src/kmcmpdll/kmcmpdll.vcxproj
+++ b/developer/src/kmcmpdll/kmcmpdll.vcxproj
@@ -89,25 +89,25 @@
$(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\developer\src\common\include;$(KEYMAN_ROOT)\developer\src\ext\json;$(KEYMAN_ROOT)\developer\src\ext\json-schema-validator
$(ProjectDir)bin\$(Platform)\$(Configuration)\
- $(KEYMAN_ROOT)\developer\src\kmcmplib\build\x86\$(Configuration)\src;$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86)
+ $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86)
$(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\developer\src\common\include;$(KEYMAN_ROOT)\developer\src\ext\json;$(KEYMAN_ROOT)\developer\src\ext\json-schema-validator
$(ProjectDir)obj\$(Platform)\$(Configuration)\
$(ProjectName).x64
$(ProjectDir)bin\$(Platform)\$(Configuration)\
- $(KEYMAN_ROOT)\developer\src\kmcmplib\build\x64\$(Configuration)\src;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64)
+ $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64)
$(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\developer\src\common\include;$(KEYMAN_ROOT)\developer\src\ext\json;$(KEYMAN_ROOT)\developer\src\ext\json-schema-validator
- $(KEYMAN_ROOT)\developer\src\kmcmplib\build\x86\$(Configuration)\src;$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86)
+ $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86)
$(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\developer\src\common\include;$(KEYMAN_ROOT)\developer\src\ext\json;$(KEYMAN_ROOT)\developer\src\ext\json-schema-validator
$(ProjectDir)obj\$(Platform)\$(Configuration)\
$(ProjectName).x64
$(ProjectDir)bin\$(Platform)\$(Configuration)\
- $(KEYMAN_ROOT)\developer\src\kmcmplib\build\x64\$(Configuration)\src;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64)
+ $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64)
@@ -142,7 +142,7 @@
$(KEYMAN_ROOT)\windows\src\global\inc
- libkmcmplib.a;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies)
+ version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies)
true
true
@@ -190,7 +190,7 @@
$(KEYMAN_ROOT)\windows\src\global\inc
- libkmcmplib.a;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies)
+ version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies)
true
true
Windows
@@ -233,7 +233,7 @@
$(KEYMAN_ROOT)\windows\src\global\inc
- libkmcmplib.a;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies)
+ version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies)
true
true
true
@@ -277,7 +277,7 @@
$(KEYMAN_ROOT)\windows\src\global\inc
- libkmcmplib.a;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies)
+ version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies)
true
true
true
diff --git a/developer/src/kmcmplib/checkout-keyboards.inc.sh b/developer/src/kmcmplib/checkout-keyboards.inc.sh
index defcb479ef4070a759b2767115d1949e7cc78ac1..6c76d53caa1fc9f39eeef1a9bcdce0140c57ed86 100644
--- a/developer/src/kmcmplib/checkout-keyboards.inc.sh
+++ b/developer/src/kmcmplib/checkout-keyboards.inc.sh
@@ -9,8 +9,8 @@ function locate_keyboards_repo() {
builder_die "keyboards_commit_ref.txt does not exist, run prep.sh"
fi
- if [[ ! -d "$THIS_SCRIPT_PATH/tests/fixtures" ]]; then
- builder_die "fixtures folder does not exist, run prep.sh"
+ if [[ ! -d "$THIS_SCRIPT_PATH/tests/fixtures/keyboards-repo" ]]; then
+ builder_die "fixtures/keyboards-repo folder does not exist, run prep.sh"
fi
if [[ -z "${KEYBOARDS_ROOT+x}" ]]; then
diff --git a/developer/src/kmcmplib/include/kmcmplibapi.h b/developer/src/kmcmplib/include/kmcmplibapi.h
index 2fb35979b2def0839a4f36fa552c76abe8604cfe..0b41af69909254876d3a92c0d3bf61d37e143162 100644
--- a/developer/src/kmcmplib/include/kmcmplibapi.h
+++ b/developer/src/kmcmplib/include/kmcmplibapi.h
@@ -16,53 +16,52 @@
#define EXTERN EMSCRIPTEN_KEEPALIVE
#endif
-typedef struct _KMCMP_COMPILER_OPTIONS {
- uint32_t dwSize;
- bool ShouldAddCompilerVersion;
-} KMCMP_COMPILER_OPTIONS;
-
-EXTERN bool kmcmp_SetCompilerOptions(
- KMCMP_COMPILER_OPTIONS* options
-);
-
-typedef int (*kmcmp_CompilerMessageProc)(int line, uint32_t dwMsgCode, char* szText, void* context);
-
-EXTERN bool kmcmp_CompileKeyboardFile(
- char* pszInfile,
- char* pszOutfile,
- bool ASaveDebug,
- bool ACompilerWarningsAsErrors,
- bool AWarnDeprecatedCode,
- kmcmp_CompilerMessageProc pMsgproc,
- void* AmsgprocContext
-);
-
/* Compile target */
#define CKF_KEYMAN 0
#define CKF_KEYMANWEB 1
-EXTERN bool kmcmp_CompileKeyboardFileToBuffer(
- char* pszInfile,
- void* pfkBuffer,
- bool ACompilerWarningsAsErrors,
- bool AWarnDeprecatedCode,
- kmcmp_CompilerMessageProc pMsgproc,
- void* AmsgprocContext,
- int Target
-);
-
-typedef bool (*kmcmp_ValidateJsonMessageProc)(int64_t offset, const char* szText, void* context);
-
-EXTERN bool kmcmp_ValidateJsonFile(
- std::fstream& f,
- std::fstream& fd,
- kmcmp_ValidateJsonMessageProc MessageProc,
- void* context
+struct KMCMP_COMPILER_OPTIONS {
+ bool saveDebug;
+ bool compilerWarningsAsErrors;
+ bool warnDeprecatedCode;
+ bool shouldAddCompilerVersion;
+ int target; // CKF_KEYMAN, CKF_KEYMANWEB
+};
+
+struct KMCMP_COMPILER_RESULT {
+ void* kmx;
+ size_t kmxSize;
+ std::string kvksFilename;
+};
+
+// TODO: parameters in UTF-8 #8887
+typedef int (*kmcmp_CompilerMessageProc)(int line, uint32_t dwMsgCode, const char* szText, void* context);
+
+// parameters in UTF-8
+// TODO typical usage:
+// if(!kmcmp_LoadFileProc("filename.ico", "/tmp/filename.kmn", nullptr, &size)) {
+// return error;
+// }
+// buf = new unsigned char[size];
+// if(!kmcmp_LoadFileProc("filename.ico", "/tmp/filename.kmn", buf, &size)) {
+// delete[] buf;
+// return error;
+// }
+typedef bool (*kmcmp_LoadFileProc)(const char* loadFilename, const char* baseFilename, void* buffer, int* bufferSize, void* context);
+
+// Parameters in UTF-8
+EXTERN bool kmcmp_CompileKeyboard(
+ const char* pszInfile,
+ const KMCMP_COMPILER_OPTIONS& options,
+ kmcmp_CompilerMessageProc messageProc,
+ kmcmp_LoadFileProc loadFileProc,
+ const void* procContext,
+ KMCMP_COMPILER_RESULT& result
);
/**
- * kmcmp_ParseUnicodeSet is successful if it returns >= USET_OK
+ * kmcmp_parseUnicodeSet is successful if it returns >= USET_OK
*/
static const int KMCMP_USET_OK = 0;
@@ -83,24 +82,19 @@ static const int KMCMP_ERROR_UNSUPPORTED_PROPERTY = -3;
*/
static const int KMCMP_FATAL_OUT_OF_RANGE = -4;
-/**
- * Function pointer to kmcmp_ParseUnicodeSet
- */
-typedef int (*kmcmp_ParseUnicodeSetProc)(const char* szText, uint32_t* output, uint32_t outputLength);
-
/**
* Parse a UnicodeSet into 32-bit ranges.
* For example, "[]" will return 0 (KMCMP_USET_OK) as a zero-length set.
* "[" will return KMCMP_ERROR_SYNTAX_ERR,
* and "[x A-C]" will return 2 and [0x41, 0x43, 0x78, 0x78]
- * @param szText input txt, null terminated, in UTF-8 format
+ * @param text input txt, null terminated, in UTF-8 format
* @param outputBuffer output buffer, owned by caller: Pairs of ranges in order
* @param outputBufferSize length of output buffer. Needs to be twice the number of expected ranges
* @return If >= KMCMP_USET_OK, number of ranges, otherwise one of the negative error values.
*/
-EXTERN int kmcmp_ParseUnicodeSet(
- const char* szText,
- uint32_t* outputBuffer,
+EXTERN int kmcmp_parseUnicodeSet(
+ const std::string text,
+ uintptr_t outputBuffer_,
uint32_t outputBufferSize
);
diff --git a/developer/src/kmcmplib/include/meson.build b/developer/src/kmcmplib/include/meson.build
index f98a4d8c762da2134f33054a4b18948aa3a5e658..dde8e69f6b0cd52d1c8f1ce66f2a65c413e3809f 100644
--- a/developer/src/kmcmplib/include/meson.build
+++ b/developer/src/kmcmplib/include/meson.build
@@ -20,7 +20,5 @@ configure_file(
inc = include_directories(
'.',
'../../common/include',
- '../../../../common/include',
- '../../ext/json',
- '../../ext/json-schema-validator'
+ '../../../../common/include'
)
diff --git a/developer/src/kmcmplib/src/CheckFilenameConsistency.cpp b/developer/src/kmcmplib/src/CheckFilenameConsistency.cpp
deleted file mode 100644
index 627298bf375de6155aeae55fb1ddff96a157df14..0000000000000000000000000000000000000000
--- a/developer/src/kmcmplib/src/CheckFilenameConsistency.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
-
-#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING 1
-#include "pch.h"
-#include "compfile.h"
-#include
-#include "kmcmplib.h"
-#include
-#include "CheckFilenameConsistency.h"
-#include "kmx_u16.h"
-#include "filesystem.h"
-
-#ifdef _MSC_VER
-#include
-#endif
-
-namespace kmcmp {
- extern KMX_CHAR CompileDir[260]; // TODO: this should not be a fixed buffer
-}
-bool IsRelativePath(KMX_CHAR const * p) {
- // Relative path (returns TRUE):
- // ..\...\BITMAP.BMP
- // PATH\BITMAP.BMP
- // BITMAP.BMP
-
- // Semi-absolute path (returns FALSE):
- // \...\BITMAP.BMP
-
- // Absolute path (returns FALSE):
- // C:\...\BITMAP.BMP
- // \\SERVER\SHARE\...\BITMAP.BMP
-
- if ((*p == '\\') || (*p == '/')) return FALSE;
- if (*p && *(p + 1) == ':') return FALSE;
-
- return TRUE;
-}
-
-bool IsRelativePath(KMX_WCHAR const * p) {
- // Relative path (returns TRUE):
- // ..\...\BITMAP.BMP
- // PATH\BITMAP.BMP
- // BITMAP.BMP
-
- // Semi-absolute path (returns FALSE):
- // \...\BITMAP.BMP
-
- // Absolute path (returns FALSE):
- // C:\...\BITMAP.BMP
- // \\SERVER\SHARE\...\BITMAP.BMP
-
- if ((*p == u'\\') || (*p == u'/'))return FALSE;
- if (*p && *(p + 1) == u':') return FALSE;
-
- return TRUE;
-}
-
-KMX_DWORD CheckFilenameConsistency( KMX_CHAR const * Filename, bool ReportMissingFile) {
- PKMX_WCHAR WFilename = strtowstr(( KMX_CHAR *)Filename);
- KMX_DWORD const result = CheckFilenameConsistency(WFilename, ReportMissingFile);
- delete WFilename;
- return result;
-}
-
-KMX_DWORD CheckFilenameConsistency(KMX_WCHAR const * Filename, bool ReportMissingFile) {
- // not ready yet: needs more attention-> common includes for non-Windows platforms
- KMX_WCHAR Name[260]; // TODO: fixed buffer sizes bad
-
-
- if (IsRelativePath(Filename)) {
- PKMX_WCHAR WCompileDir = strtowstr(kmcmp::CompileDir);
- u16ncpy(Name, WCompileDir, _countof(Name)); // I3481
- u16ncat(Name, Filename, _countof(Name)); // I3481
- delete[] WCompileDir;
- } else {
- u16ncpy(Name, Filename, _countof(Name)); // I3481
- }
-
-#ifndef _MSC_VER
- // Filename consistency only needs to be checked on Windows, because other
- // platforms are going to fail if the filename is inconsistent anyway!
- if(!kmcmp_FileExists(Name)) {
- if (ReportMissingFile) {
- u16cpy(ErrExtraW, u"referenced file '");
- u16ncat(ErrExtraW, Filename, 256);
- u16ncat(ErrExtraW, u"'", 256);
- strcpy(ErrExtraLIB, string_from_u16string(ErrExtraW).c_str());
- AddWarning(CWARN_MissingFile);
- }
- return CERR_None;
- }
- return CERR_None;
-#else
- _wfinddata_t fi;
- intptr_t n;
- if ((n = _wfindfirst((const wchar_t*) Name, &fi)) == -1) {
- if (ReportMissingFile) {
- sprintf(ErrExtraLIB, "referenced file '%ls'", (wchar_t*) Filename);
- AddWarning(CWARN_MissingFile);
- }
- return CERR_None;
- }
-
- _findclose(n);
-
- KMX_WCHAR FName[_MAX_FNAME], Ext[_MAX_EXT];
- wchar_t WChName[_MAX_PATH];
- _wsplitpath_s((const wchar_t*)Filename, nullptr, 0, nullptr, 0, (wchar_t*) FName, _MAX_FNAME, (wchar_t*) Ext, _MAX_EXT);
- _wmakepath_s(WChName, _MAX_PATH, nullptr, nullptr, (const wchar_t*) FName, (const wchar_t*) Ext);
- if (wcscmp(WChName, fi.name) != 0) {
- sprintf(ErrExtraLIB, "reference '%ls' does not match actual filename '%ls'", WChName, fi.name);
-
- AddWarning(CHINT_FilenameHasDifferingCase);
-
- }
-#endif
-
- return CERR_None;
-}
-
-KMX_DWORD CheckFilenameConsistencyForCalls(PFILE_KEYBOARD fk) {
- // call() statements depend on a fairly ugly hack for js,
- // where store(DllFunction) "my.dll:func" will look for a
- // file called function.call_js. ( or should this be func.call_js ? )
- // This is ripe for rewrite!
- // But let's check what we have anyway
-
- PFILE_STORE sp;
- KMX_DWORD i, msg;
- for (i = 0, sp = fk->dpStoreArray; i < fk->cxStoreArray; i++, sp++) {
- if (!sp->fIsCall) continue;
-
- const std::u16string callsite(sp->dpString);
- const auto colon = callsite.find(':');
- if (colon == std::u16string::npos) continue;
-
- auto func1 = callsite.substr(colon + 1);
- std::u16string str_js(u".call_js");
- std::u16string func = func1+ str_js;
-
- if ((msg = CheckFilenameConsistency(func.c_str(), FALSE)) != CERR_None) {
- return msg;
- }
- }
- return CERR_None;
-}
diff --git a/developer/src/kmcmplib/src/CheckFilenameConsistency.h b/developer/src/kmcmplib/src/CheckFilenameConsistency.h
deleted file mode 100644
index 39f86db391b1ac6b870d7a521f297372059bd9b4..0000000000000000000000000000000000000000
--- a/developer/src/kmcmplib/src/CheckFilenameConsistency.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include "compfile.h"
-#include "kmcmplib.h"
-
-KMX_DWORD CheckFilenameConsistencyForCalls(PFILE_KEYBOARD fk);
-KMX_DWORD CheckFilenameConsistency(KMX_CHAR const * Filename, bool ReportMissingFile);
-KMX_DWORD CheckFilenameConsistency(KMX_WCHAR const * Filename, bool ReportMissingFile);
-
-bool IsRelativePath(KMX_CHAR const * p);
-bool IsRelativePath(KMX_WCHAR const * p);
diff --git a/developer/src/kmcmplib/src/CompMsg.cpp b/developer/src/kmcmplib/src/CompMsg.cpp
index 2fa0d68322dc1217207bb8541a62e304fee40f76..dd41336a0e9d262a96a0b4026cbaac54b80683a3 100644
--- a/developer/src/kmcmplib/src/CompMsg.cpp
+++ b/developer/src/kmcmplib/src/CompMsg.cpp
@@ -111,7 +111,6 @@ const struct CompilerError CompilerErrors[] = {
{ CERR_DuplicateStore , "A store with this name has already been defined."},
{ CERR_RepeatedBegin , "Begin has already been set"},
- { CHINT_FilenameHasDifferingCase , "Casing differences may fail on some platforms: "},
{ CHINT_UnreachableRule , "This rule will never be matched as another rule takes precedence"},
{ CWARN_TooManyWarnings , "Too many warnings or errors"},
@@ -142,7 +141,6 @@ const struct CompilerError CompilerErrors[] = {
{ CWARN_NulNotFirstStatementInContext , "nul must be the first statement in the context"},
{ CWARN_IfShouldBeAtStartOfContext , "if, platform and baselayout should be at start of context (after nul, if present)"},
{ CWARN_KeyShouldIncludeNCaps , "Other rules which reference this key include CAPS or NCAPS modifiers, so this rule must include NCAPS modifier to avoid inconsistent matches"},
- { CWARN_MissingFile , "The referenced file could not be found: "},
{ 0, nullptr }
};
diff --git a/developer/src/kmcmplib/src/CompileKeyboardBuffer.cpp b/developer/src/kmcmplib/src/CompileKeyboardBuffer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e90938f18a735ffca4ab986ee53004bb3703a62a
--- /dev/null
+++ b/developer/src/kmcmplib/src/CompileKeyboardBuffer.cpp
@@ -0,0 +1,160 @@
+#include "pch.h"
+#include
+#include "kmcmplib.h"
+#include "CheckNCapsConsistency.h"
+#include "DeprecationChecks.h"
+#include "versioning.h"
+#include "CompileKeyboardBuffer.h"
+#include "../../../../common/windows/cpp/include/keymanversion.h"
+
+bool CompileKeyboardBuffer(KMX_BYTE* infile, int sz, PFILE_KEYBOARD fk)
+{
+ PKMX_WCHAR str, p;
+
+ KMX_DWORD msg;
+
+ kmcmp::FMnemonicLayout = FALSE;
+
+ if (!fk) {
+ AddCompileError(CERR_SomewhereIGotItWrong);
+ return FALSE;
+ }
+
+ str = new KMX_WCHAR[LINESIZE];
+ if (!str) {
+ AddCompileError(CERR_CannotAllocateMemory);
+ return FALSE;
+ }
+
+ fk->KeyboardID = 0;
+ fk->version = 0;
+ fk->dpStoreArray = NULL;
+ fk->dpGroupArray = NULL;
+ fk->cxStoreArray = 0;
+ fk->cxGroupArray = 0;
+ fk->StartGroup[0] = fk->StartGroup[1] = -1;
+ fk->szName[0] = 0;
+ fk->szCopyright[0] = 0;
+ fk->dwFlags = KF_AUTOMATICVERSION;
+ fk->currentGroup = 0xFFFFFFFF;
+ fk->currentStore = 0;
+ fk->cxDeadKeyArray = 0;
+ fk->dpDeadKeyArray = NULL;
+ fk->cxVKDictionary = 0; // I3438
+ fk->dpVKDictionary = NULL; // I3438
+ fk->extra->kvksFilename = u"";
+/* fk->szMessage[0] = 0;
+ fk->szLanguageName[0] = 0;*/
+ fk->dwBitmapSize = 0;
+ fk->dwHotKey = 0;
+
+ kmcmp::BeginLine[BEGIN_ANSI] = -1;
+ kmcmp::BeginLine[BEGIN_UNICODE] = -1;
+ kmcmp::BeginLine[BEGIN_NEWCONTEXT] = -1;
+ kmcmp::BeginLine[BEGIN_POSTKEYSTROKE] = -1;
+
+
+ /* Add a store for the Keyman 6.0 copyright information string */
+
+ if(kmcmp::FShouldAddCompilerVersion) {
+ u16sprintf(str,LINESIZE, L"Created with Keyman Developer version %d.%d.%d.%d", KEYMAN_VersionMajor, KEYMAN_VersionMinor, KEYMAN_VersionPatch, 0);
+ AddStore(fk, TSS_KEYMANCOPYRIGHT, str);
+ }
+
+ /* Add a system store for the Keyman edition number */
+ AddStore(fk, TSS_CUSTOMKEYMANEDITION, u"0");
+ AddStore(fk, TSS_CUSTOMKEYMANEDITIONNAME, u"Keyman");
+
+ int offset = 0;
+
+ // must preprocess for group and store names -> this isn't really necessary, but never mind!
+ while ((msg = ReadLine(infile, sz, offset, str, TRUE)) == CERR_None)
+ {
+ p = str;
+ switch (LineTokenType(&p))
+ {
+ case T_VERSION:
+ *(p + 4) = 0;
+ if ((msg = AddStore(fk, TSS_VERSION, p)) != CERR_None) {
+ AddCompileError(msg);
+ return FALSE;
+ }
+ break;
+
+ case T_GROUP:
+ if ((msg = ProcessGroupLine(fk, p)) != CERR_None) {
+ AddCompileError(msg);
+ return FALSE;
+ }
+ break;
+
+ case T_STORE:
+ if ((msg = ProcessStoreLine(fk, p)) != CERR_None) {
+ AddCompileError(msg);
+ return FALSE;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (msg != CERR_EndOfFile) {
+ AddCompileError(msg);
+ return FALSE;
+ }
+
+ offset = 0;
+ kmcmp::currentLine = 0;
+
+ /* Reindex the list of codeconstants after stores added */
+
+ kmcmp::CodeConstants->reindex();
+
+ /* ReadLine will automatically skip over $Keyman lines, and parse wrapped lines */
+ while ((msg = ReadLine(infile, sz, offset, str, FALSE)) == CERR_None)
+ {
+ msg = ParseLine(fk, str);
+ if (msg != CERR_None) {
+ AddCompileError(msg);
+ return FALSE;
+ }
+ }
+
+ if (msg != CERR_EndOfFile) {
+ AddCompileError(msg);
+ return FALSE;
+ }
+
+ ProcessGroupFinish(fk);
+
+ if (kmcmp::FSaveDebug) kmcmp::RecordDeadkeyNames(fk);
+
+ /* Add the compiler version as a system store */
+ if ((msg = kmcmp::AddCompilerVersionStore(fk)) != CERR_None) {
+ AddCompileError(msg);
+ return FALSE;
+ }
+
+ if ((msg = BuildVKDictionary(fk)) != CERR_None) {
+ AddCompileError(msg);
+ return FALSE;
+ }
+
+ delete str;
+
+ if (!kmcmp::CheckKeyboardFinalVersion(fk)) {
+ return FALSE;
+ }
+
+ /* Warn on inconsistent use of NCAPS */
+ if (!kmcmp::FMnemonicLayout) {
+ CheckNCapsConsistency(fk);
+ }
+
+ /* Flag presence of deprecated features */
+ kmcmp::CheckForDeprecatedFeatures(fk);
+
+ return TRUE;
+}
diff --git a/developer/src/kmcmplib/src/CompileKeyboardBuffer.h b/developer/src/kmcmplib/src/CompileKeyboardBuffer.h
new file mode 100644
index 0000000000000000000000000000000000000000..78bac6665399d721b52baf563c62de3732a570fb
--- /dev/null
+++ b/developer/src/kmcmplib/src/CompileKeyboardBuffer.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "compfile.h"
+
+bool CompileKeyboardBuffer(KMX_BYTE* infile, int sz, PFILE_KEYBOARD fk);
diff --git a/developer/src/kmcmplib/src/Compiler.cpp b/developer/src/kmcmplib/src/Compiler.cpp
index b09e36cbc1079fdf47feeb9ac882eaf640b3f425..e5d5f94cbc7d0381c8aaec67f59cdbdd43379035 100644
--- a/developer/src/kmcmplib/src/Compiler.cpp
+++ b/developer/src/kmcmplib/src/Compiler.cpp
@@ -94,11 +94,12 @@
#include "CasedKeys.h"
#include
#include
-#include "CheckFilenameConsistency.h"
+#include
+#include
+
#include "UnreachableRules.h"
#include "CheckForDuplicates.h"
#include "kmx_u16.h"
-#include "filesystem.h"
#include
/* These macros are adapted from winnt.h and legacy use only */
@@ -122,7 +123,6 @@ namespace kmcmp{
KMX_BOOL FMnemonicLayout = FALSE;
KMX_BOOL FOldCharPosMatching = FALSE;
int CompileTarget;
- KMX_CHAR CompileDir[260]; // TODO: this should not be a fixed buffer
int BeginLine[4];
KMX_BOOL IsValidCallStore(PFILE_STORE fs);
@@ -230,6 +230,8 @@ enum LinePrefixType { lptNone, lptKeymanAndKeymanWeb, lptKeymanWebOnly, lptKeyma
/* Compile target */
kmcmp_CompilerMessageProc msgproc = NULL;
+kmcmp_LoadFileProc loadfileproc = NULL;
+
void* msgprocContext = NULL;
int kmcmp::currentLine = 0;
@@ -862,7 +864,6 @@ KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE
int i, j;
KMX_DWORD msg;
PKMX_WCHAR p, q;
- KMX_CHAR *pp;
if (!pssBuf) pssBuf = new KMX_WCHAR[GLOBAL_BUFSIZE];
PKMX_WCHAR buf = pssBuf;
@@ -912,13 +913,9 @@ KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE
case TSS_INCLUDECODES:
VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_NamedCodes);
- pp = wstrtostr(sp->dpString);
- if (!kmcmp::CodeConstants->LoadFile(pp))
- {
- delete[] pp;
+ if (!kmcmp::CodeConstants->LoadFile(fk, sp->dpString)) {
return CERR_CannotLoadIncludeFile;
}
- delete[] pp;
kmcmp::CodeConstants->reindex(); // I4982
break;
@@ -1047,10 +1044,6 @@ KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE
delete[] sp->dpString;
sp->dpString = q;
-
- if ((msg = CheckFilenameConsistency( (sp->dpString), FALSE)) != CERR_None) {
- return msg;
- }
}
break;
case TSS_KMW_RTL:
@@ -1061,16 +1054,10 @@ KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE
case TSS_KMW_HELPFILE:
case TSS_KMW_EMBEDJS:
VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly);
- if ((msg = CheckFilenameConsistency(sp->dpString, FALSE)) != CERR_None) {
- return msg;
- }
break;
case TSS_KMW_EMBEDCSS:
VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyEmbedCSS);
- if ((msg = CheckFilenameConsistency(sp->dpString, FALSE)) != CERR_None) {
- return msg;
- }
break;
case TSS_TARGETS: // I4504
@@ -1120,9 +1107,6 @@ KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE
case TSS_LAYOUTFILE: // I3483
VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyLayoutFile); // I4140
- if ((msg = CheckFilenameConsistency(sp->dpString, FALSE)) != CERR_None) {
- return msg;
- }
// Used by KMW compiler
break;
@@ -3068,7 +3052,7 @@ KMX_DWORD WriteCompiledKeyboard(PFILE_KEYBOARD fk, KMX_BYTE**data, size_t& dataS
return CERR_None;
}
-KMX_DWORD ReadLine(FILE* fp_in , PKMX_WCHAR wstr, KMX_BOOL PreProcess)
+KMX_DWORD ReadLine(KMX_BYTE* infile, int sz, int& offset, PKMX_WCHAR wstr, KMX_BOOL PreProcess)
{
KMX_DWORD len;
PKMX_WCHAR p;
@@ -3076,21 +3060,26 @@ KMX_DWORD ReadLine(FILE* fp_in , PKMX_WCHAR wstr, KMX_BOOL PreProcess)
KMX_DWORD n;
KMX_WCHAR currentQuotes = 0;
KMX_WCHAR str[LINESIZE + 3];
- len = (KMX_DWORD)fread( str , 1 ,LINESIZE * 2,fp_in);
- if (ferror(fp_in) ) return CERR_CannotReadInfile;
- len /= 2;
- str[len] = 0; auto cur = ftell(fp_in);
- fseek(fp_in, 0, SEEK_END);
- auto fsize = ftell(fp_in);
- fseek(fp_in, cur, SEEK_SET);
- if (cur == fsize)
+ if(offset >= sz) {
+ return CERR_EndOfFile;
+ }
- // \r\n is still added here even though Linux doesn`t use \r.
- // This is to ensure to still have a working windows-only-version
- u16ncat(str, u"\r\n", _countof(str)); // I3481 // Always a "\r\n" to the EOF, avoids funny bugs
+ len = offset + LINESIZE*2 > sz ? sz-offset : LINESIZE*2;
+ memcpy(str, infile+offset, len);
+ offset += len;
+ len /= 2;
+ str[len] = 0;
- if (len == 0) return CERR_EndOfFile;
+ if(offset == sz) {
+ // \r\n is still added here even though Linux doesn`t use \r.
+ // This is to ensure to still have a working windows-only-version
+ u16ncat(str, u"\r\n", _countof(str)); // I3481 // Always a "\r\n" to the EOF, avoids funny bugs
+ }
+
+ if (len == 0) {
+ return CERR_EndOfFile;
+ }
// neccessary to add this block for using on non-windows platforms (removes all \r for platforms that use \n instead of \r\n)
for (p = str, n = 0; n < len; n++, p++) {
@@ -3172,9 +3161,13 @@ KMX_DWORD ReadLine(FILE* fp_in , PKMX_WCHAR wstr, KMX_BOOL PreProcess)
return (PreProcess ? CERR_None : CERR_LineTooLong);
}
- if (*p == L'\n') kmcmp::currentLine++;
+ kmcmp::currentLine++;
- fseek(fp_in, -(int)(len * 2 - (int)(p - str) * 2 - 2), SEEK_CUR);
+ offset -= (int)(len * 2 - (int)(p - str) * 2 - 2);
+ if(offset >= sz) {
+ // If we've appended a \n, we can go past EOF
+ offset = sz;
+ }
p--;
while (p >= str && iswspace(*p)) p--;
@@ -3220,58 +3213,38 @@ KMX_BOOL IsSameToken(PKMX_WCHAR *p, KMX_WCHAR const * token)
return FALSE;
}
-KMX_DWORD ImportBitmapFile(PFILE_KEYBOARD fk, PKMX_WCHAR szName, PKMX_DWORD FileSize, PKMX_BYTE *Buf)
+static bool endsWith(const std::string& str, const std::string& suffix)
{
- FILE *fp;
- KMX_WCHAR szNewName[260];
-
- if (IsRelativePath(szName))
- {
- PKMX_WCHAR WCompileDir = strtowstr(kmcmp::CompileDir);
- u16ncpy(szNewName, WCompileDir, _countof(szNewName)); // I3481
- u16ncat(szNewName,szName, _countof(szNewName )); // I3481
- }
- else
- u16ncpy(szNewName, szName, _countof(szNewName)); // I3481
-
- fp=Open_File(szNewName, u"rb");
-
- if ( fp == NULL)
- {
- // else if filename.bmp is not in the folder -> attempt to open filename.bmp.bmp !
- if ( u16cmp(szNewName+u16len(szNewName)-4, u".bmp") )
- u16ncat(szNewName, u".bmp", _countof(szNewName)); // I3481
+ return str.size() >= suffix.size() && 0 == str.compare(str.size()-suffix.size(), suffix.size(), suffix);
+}
- fp= Open_File(szNewName, u"rb");
+KMX_DWORD ImportBitmapFile(PFILE_KEYBOARD fk, PKMX_WCHAR szName, PKMX_DWORD FileSize, PKMX_BYTE *Buf)
+{
+ auto szNameUtf8 = string_from_u16string(szName);
- if ( fp == NULL)
+ if(!loadfileproc(szNameUtf8.c_str(), fk->extra->kmnFilename.c_str(), nullptr, (int*) FileSize, msgprocContext)) {
+ // Append .bmp and try again
+ if(endsWith(szNameUtf8, ".bmp")) {
return CERR_CannotReadBitmapFile;
+ }
+ szNameUtf8.append(".bmp");
+ if(!loadfileproc(szNameUtf8.c_str(), fk->extra->kmnFilename.c_str(), nullptr, (int*) FileSize, msgprocContext)) {
+ return CERR_CannotReadBitmapFile;
+ }
}
- KMX_DWORD msg;
- if ((msg = CheckFilenameConsistency(szNewName, FALSE)) != CERR_None) {
- return msg;
- }
-
- fseek(fp, 0, SEEK_END);
- *FileSize = (KMX_DWORD)ftell(fp);
- fseek(fp ,0,SEEK_SET);
- if (*FileSize < 0) {
- fclose(fp);
+ if(*FileSize < 2) {
+ // Zero-byte file is invalid; 2 byte file is too, but we only really care
+ // about the prolog at this point so we don't overrun our buffer
return CERR_CannotReadBitmapFile;
}
- if (*FileSize < 2) return CERR_CannotReadBitmapFile;
*Buf = new KMX_BYTE[*FileSize];
-
- if (fread(*Buf, 1, *FileSize, fp) < (size_t) *FileSize) {
- delete[] * Buf;
- *Buf = NULL;
+ if(!loadfileproc(szNameUtf8.c_str(), fk->extra->kmnFilename.c_str(), *Buf, (int*) FileSize, msgprocContext)) {
+ delete[] *Buf;
return CERR_CannotReadBitmapFile;
}
- fclose(fp);
-
/* Test for version 7.0 icon support */
if (*((PKMX_CHAR)*Buf) != 'B' && *(((PKMX_CHAR)*Buf) + 1) != 'M') {
VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly);
@@ -3427,81 +3400,54 @@ KMX_BOOL kmcmp::IsValidCallStore(PFILE_STORE fs)
return i == 1;
}
-FILE* CreateTempFile()
-{
- return tmpfile();
+///////////////////
+
+bool hasPreamble(std::u16string result) {
+ return result.size() > 0 && result[0] == 0xFEFF;
}
-///////////////////
+#include "unicode/ucnv.h"
-FILE* UTF16TempFromUTF8(FILE* fp_in , KMX_BOOL hasPreamble)
-{
- FILE *fp_out = CreateTempFile();
- if(fp_out == NULL) // I3228 // I3510
- {
- fclose(fp_in);
- return NULL; //return INVALID_HANDLE_VALUE; _S2 can I exchange that?
+bool UTF16TempFromUTF8(KMX_BYTE* infile, int sz, KMX_BYTE** tempfile, int *sz16) {
+ if(sz == 0) {
+ return FALSE;
}
- PKMX_BYTE buf, p;
- PKMX_WCHAR outbuf, poutbuf;
- KMX_DWORD len;
- KMX_DWORD len2;
- KMX_WCHAR prolog = 0xFEFF;
- fwrite(&prolog,2, 1, fp_out);
-
- fseek(fp_in, 0, SEEK_END);
- len = (KMX_DWORD)ftell(fp_in);
- fseek(fp_in, 0, SEEK_SET);
- if (hasPreamble) {
- fseek( fp_in,3,SEEK_SET); // Cut off UTF-8 marker
- len -= 3;
- }
-
- buf = new KMX_BYTE[len + 1]; // null terminated
- outbuf = new KMX_WCHAR[len + 1];
-
- len2= (KMX_DWORD)fread(buf,1,len,fp_in);
- if (len2) {
- buf[len2] = 0;
- p = buf;
- poutbuf = outbuf;
- if (hasPreamble) {
- // We have a preamble, so we attempt to read as UTF-8 and allow conversion errors to be filtered. This is not great for a
- // compiler but matches existing behaviour -- in future versions we may not do lenient conversion.
- ConvertUTF8toUTF16(&p, &buf[len2], (UTF16 **)&poutbuf, (const UTF16 *)&outbuf[len], lenientConversion);
- fwrite(outbuf, (KMX_DWORD)(poutbuf - outbuf) * 2 , 1, fp_out);
+ std::u16string result;
+
+ try {
+ std::wstring_convert, char16_t> converter;
+ result = converter.from_bytes((char*)infile, (char*)infile+sz);
+ } catch(std::range_error e) {
+ UErrorCode status = U_ZERO_ERROR;
+ // TODO: we need ICU data files here #8884
+ UConverter* conv = ucnv_open("windows-1252", &status);
+ if(U_FAILURE(status)) {
+ return FALSE;
}
- else {
- // No preamble, so we attempt to read as strict UTF-8 and fall back to ANSI if that fails
- ConversionResult cr = ConvertUTF8toUTF16(&p, &buf[len2], (UTF16 **)&poutbuf, (const UTF16 *)&outbuf[len], strictConversion);
- if (cr == sourceIllegal) {
- // Not a valid UTF-8 file, so fall back to ANSI
- // AddCompileError(CHINT_NonUnicodeFile);
- // note, while this message is defined, for now we will not emit it
- // because we don't support HINT/INFO messages yet and we don't want
- // this to cause a blocking compile at this stage
- // do strtowstr only when no invalid characters are found
- if( p==0){
- poutbuf = strtowstr((PKMX_STR)buf);
- fwrite(poutbuf, (KMX_DWORD)u16len(poutbuf) * 2 , 1, fp_out);
- delete[] poutbuf;
- }
- else
- AddCompileError(CERR_InvalidCharacter);
- }
- else {
- fwrite(outbuf, (KMX_DWORD)(poutbuf - outbuf) * 2 , 1, fp_out);
- }
+ char16_t* dest = new char16_t[sz*2];
+ ucnv_toUChars(conv, dest, sz*2, (char*)infile, sz, &status);
+ if(U_FAILURE(status)) {
+ delete[] dest;
+ return FALSE;
}
+
+ result = dest;
+ delete[] dest;
}
- fclose( fp_in);
- delete[] buf;
- delete[] outbuf;
- fseek( fp_out,2,SEEK_SET);
- return fp_out;
+ if(hasPreamble(result)) {
+ *sz16 = result.size() * 2 - 2;
+ *tempfile = new KMX_BYTE[*sz16];
+ memcpy(*tempfile, result.c_str() + 1, *sz16);
+ } else {
+ *sz16 = result.size() * 2;
+ *tempfile = new KMX_BYTE[*sz16];
+ memcpy(*tempfile, result.c_str(), *sz16);
+ }
+
+ return TRUE;
}
PFILE_STORE FindSystemStore(PFILE_KEYBOARD fk, KMX_DWORD dwSystemID)
diff --git a/developer/src/kmcmplib/src/CompilerInterfaces.cpp b/developer/src/kmcmplib/src/CompilerInterfaces.cpp
index a1609d35449562695c622e689649f962f6f6c57f..fdfbdf19613cc26d3de1161ca47e2dfdeb9c7bd7 100644
--- a/developer/src/kmcmplib/src/CompilerInterfaces.cpp
+++ b/developer/src/kmcmplib/src/CompilerInterfaces.cpp
@@ -1,453 +1,110 @@
#include "pch.h"
-
#include
#include
#include "kmcmplib.h"
-#include "filesystem.h"
-#include "CheckFilenameConsistency.h"
-#include "CheckNCapsConsistency.h"
-#include "DeprecationChecks.h"
-#include "versioning.h"
#include "../../../../common/windows/cpp/include/ConvertUTF.h"
-#include "../../../../common/windows/cpp/include/keymanversion.h"
-
-bool CompileKeyboardHandle(FILE* fp_in, PFILE_KEYBOARD fk);
-bool CompileKeyboard(const char* pszInfile,
- void* pfkBuffer, bool ASaveDebug, bool ACompilerWarningsAsErrors,
- bool AWarnDeprecatedCode, kmcmp_CompilerMessageProc pMsgproc, const void* AmsgprocContext,
- int Target);
-
-EXTERN bool kmcmp_SetCompilerOptions(KMCMP_COMPILER_OPTIONS* options) {
- //printf("°°-> changed to SetCompilerOptions() of kmcmplib \n");
- if(!options || options->dwSize < sizeof(KMCMP_COMPILER_OPTIONS)) {
- return FALSE;
- }
- kmcmp::FShouldAddCompilerVersion = options->ShouldAddCompilerVersion;
- return TRUE;
-}
-
-#ifdef __EMSCRIPTEN__
-
-/*
- WASM interface for compiler message callback
-*/
-EM_JS(int, wasm_msgproc, (int line, int msgcode, char* text, char* context), {
- const proc = globalThis[UTF8ToString(context)];
- if(!proc || typeof proc != 'function') {
- console.log(`[${line}: ${msgcode}: ${UTF8ToString(text)}]`);
- return 0;
- } else {
- return proc(line, msgcode, UTF8ToString(text));
- }
-});
-
-int wasm_CompilerMessageProc(int line, uint32_t dwMsgCode, char* szText, void* context) {
- char* msgProc = static_cast(context);
- return wasm_msgproc(line, dwMsgCode, szText, msgProc);
-}
-
-//DEPRECATED
-EXTERN bool kmcmp_Wasm_SetCompilerOptions(int ShouldAddCompilerVersion) {
- KMCMP_COMPILER_OPTIONS options;
- options.dwSize = sizeof(KMCMP_COMPILER_OPTIONS);
- options.ShouldAddCompilerVersion = ShouldAddCompilerVersion;
- return kmcmp_SetCompilerOptions(&options);
-}
-
-//DEPRECATED
-EXTERN bool kmcmp_Wasm_CompileKeyboardFile(char* pszInfile,
- char* pszOutfile, int ASaveDebug, int ACompilerWarningsAsErrors,
- int AWarnDeprecatedCode, char* msgProc
+#include "CompileKeyboardBuffer.h"
+
+EXTERN bool kmcmp_CompileKeyboard(
+ const char* pszInfile,
+ const KMCMP_COMPILER_OPTIONS& options,
+ kmcmp_CompilerMessageProc messageProc,
+ kmcmp_LoadFileProc loadFileProc,
+ const void* procContext,
+ KMCMP_COMPILER_RESULT& result
) {
- return kmcmp_CompileKeyboardFile(
- pszInfile,
- pszOutfile,
- ASaveDebug,
- ACompilerWarningsAsErrors,
- AWarnDeprecatedCode,
- wasm_CompilerMessageProc,
- msgProc
- );
-}
-
-EXTERN int kmcmp_Wasm_ParseUnicodeSet(char* pat,
- uint32_t* buf, int length
-) {
- return kmcmp_ParseUnicodeSet(
- pat, buf, length
- );
-}
-
-
-struct COMPILER_INTERFACE {
- bool saveDebug;
- bool compilerWarningsAsErrors;
- bool warnDeprecatedCode;
- bool shouldAddCompilerVersion;
- int target; // CKF_KEYMAN, CKF_KEYMANWEB
- std::string messageCallback; // int line, uint32_t dwMsgCode, char* szText
- std::string loadFileCallback; // char* infile, char* filenameRelativeToInfile --> buffer
-};
-
-struct COMPILER_RESULT {
- bool result;
- // Following are pointer offsets in heap + buffer size
- int kmx;
- int kmxSize;
- // Following are compiler side-channel data, required for
- // follow-on transform
- std::string kvksFilename;
- // TODO: additional data to be passed back
-};
-
-COMPILER_RESULT kmcmp_compile(std::string pszInfile, const COMPILER_INTERFACE intf) {
- COMPILER_RESULT r = {false};
FILE_KEYBOARD fk;
+ fk.extra = new FILE_KEYBOARD_EXTRA;
+ fk.extra->kmnFilename = pszInfile;
- // TODO: this should be included in CompileKeyboard?
- kmcmp::FShouldAddCompilerVersion = intf.shouldAddCompilerVersion;
-
- r.result = CompileKeyboard(
- pszInfile.c_str(),
- &fk,
- intf.saveDebug,
- intf.compilerWarningsAsErrors,
- intf.warnDeprecatedCode,
- wasm_CompilerMessageProc,
- intf.messageCallback.c_str(),
- intf.target);
-
- if(!r.result) {
- return r;
- }
-
- KMX_DWORD msg;
- KMX_BYTE* data = nullptr;
- size_t dataSize = 0;
- msg = WriteCompiledKeyboard(&fk, &data, dataSize);
- //TODO: FreeKeyboardPointers(fk);
-
- if(msg != CERR_None) {
- AddCompileError(msg);
- r.result = FALSE;
- return r;
- }
-
- r.kmx = (int) data;
- r.kmxSize = (int) dataSize;
- r.kvksFilename = string_from_u16string(fk.extra->kvksFilename); // convert to UTF8
-
- return r;
-}
-
-EMSCRIPTEN_BINDINGS(compiler_interface) {
- emscripten::class_("CompilerInterface")
- .constructor<>()
- .property("saveDebug", &COMPILER_INTERFACE::saveDebug)
- .property("compilerWarningsAsErrors", &COMPILER_INTERFACE::compilerWarningsAsErrors)
- .property("warnDeprecatedCode", &COMPILER_INTERFACE::warnDeprecatedCode)
- .property("shouldAddCompilerVersion", &COMPILER_INTERFACE::shouldAddCompilerVersion)
- .property("target", &COMPILER_INTERFACE::target)
- .property("messageCallback", &COMPILER_INTERFACE::messageCallback)
- .property("loadFileCallback", &COMPILER_INTERFACE::loadFileCallback)
- ;
-
- emscripten::class_("CompilerResult")
- .constructor<>()
- .property("result", &COMPILER_RESULT::result)
- .property("kmx", &COMPILER_RESULT::kmx)
- .property("kmxSize", &COMPILER_RESULT::kmxSize)
- .property("kvksFilename", &COMPILER_RESULT::kvksFilename)
- ;
-
- emscripten::function("kmcmp_compile", &kmcmp_compile);
-}
-
-#endif
-
-bool CompileKeyboard(const char* pszInfile,
- void* pfkBuffer, bool ASaveDebug, bool ACompilerWarningsAsErrors,
- bool AWarnDeprecatedCode, kmcmp_CompilerMessageProc pMsgproc, const void* AmsgprocContext,
- int Target) {
-
- FILE* fp_in = NULL;
- KMX_CHAR str[260];
+ kmcmp::FSaveDebug = options.saveDebug; // I3681
+ kmcmp::FCompilerWarningsAsErrors = options.compilerWarningsAsErrors; // I4865
+ AWarnDeprecatedCode_GLOBAL_LIB = options.warnDeprecatedCode;
+ kmcmp::FShouldAddCompilerVersion = options.shouldAddCompilerVersion;
+ kmcmp::CompileTarget = options.target;
- kmcmp::FSaveDebug = ASaveDebug; // I3681
- kmcmp::FCompilerWarningsAsErrors = ACompilerWarningsAsErrors; // I4865
- AWarnDeprecatedCode_GLOBAL_LIB = AWarnDeprecatedCode;
-
- kmcmp::CompileTarget = Target;
-
- if (!pMsgproc || !pszInfile || !pfkBuffer) {
+ if (!messageProc || !loadFileProc || !pszInfile) {
AddCompileError(CERR_BadCallParams);
return FALSE;
}
- PKMX_STR p;
-
- if ((p = strrchr_slash((char*)pszInfile)) != nullptr)
- {
- strncpy(kmcmp::CompileDir, pszInfile, (int)(p - pszInfile + 1)); // I3481
- kmcmp::CompileDir[(int)(p - pszInfile + 1)] = 0;
- }
- else
- kmcmp::CompileDir[0] = 0;
-
- msgproc = pMsgproc;
- msgprocContext = (void*)AmsgprocContext;
+ msgproc = messageProc;
+ loadfileproc = loadFileProc;
+ msgprocContext = (void*)procContext;
kmcmp::currentLine = 0;
kmcmp::nErrors = 0;
- fp_in = Open_File(pszInfile, "rb");
-
- if (fp_in == NULL) {
+ int sz;
+ if(!loadFileProc(pszInfile, "", nullptr, &sz, msgprocContext)) {
AddCompileError(CERR_InfileNotExist);
return FALSE;
}
- // Transfer the file to a memory stream for processing UTF-8 or ANSI to UTF-16?
- // What about really large files? Transfer to a temp file...
- if (!fread(str, 1, 3, fp_in)) {
- fclose(fp_in);
+ if(sz < 3) {
+ // Technically, a 3 byte file can never be a valid .kmn, so we can shortcut
+ // here and avoid testing outside memory bounds for looking at BOM
AddCompileError(CERR_CannotReadInfile);
return FALSE;
}
- fseek(fp_in, 0, SEEK_SET);
- if (str[0] == UTF8Sig[0] && str[1] == UTF8Sig[1] && str[2] == UTF8Sig[2])
- fp_in = UTF16TempFromUTF8(fp_in, TRUE);
- else if (str[0] == UTF16Sig[0] && str[1] == UTF16Sig[1])
- fseek(fp_in, 2, SEEK_SET);
- else
- fp_in = UTF16TempFromUTF8(fp_in, FALSE);
- if (fp_in == NULL) {
- AddCompileError(CERR_CannotCreateTempfile);
+ KMX_BYTE* infile = new KMX_BYTE[sz+1];
+ if(!infile) {
+ AddCompileError(CERR_CannotAllocateMemory);
return FALSE;
}
-
- kmcmp::CodeConstants = new kmcmp::NamedCodeConstants;
- bool result = CompileKeyboardHandle(fp_in, static_cast(pfkBuffer));
- delete kmcmp::CodeConstants;
-
- fclose(fp_in);
-
- if (kmcmp::nErrors > 0) {
+ if(!loadFileProc(pszInfile, "", infile, &sz, msgprocContext)) {
+ delete[] infile;
+ AddCompileError(CERR_CannotReadInfile);
return FALSE;
}
+ infile[sz] = 0; // zero-terminate for safety, not technically needed but helps avoid memory bugs
- return result;
-}
-
-EXTERN bool kmcmp_CompileKeyboardFileToBuffer(char* pszInfile, void* pfkBuffer, bool ACompilerWarningsAsErrors, bool AWarnDeprecatedCode,
- kmcmp_CompilerMessageProc pMsgproc, void* AmsgprocContext, int Target) // I4865 // I4866
-{
- if (!pMsgproc || !pszInfile || !pfkBuffer) {
- AddCompileError(CERR_BadCallParams);
- return FALSE;
+ int offset = 0;
+ if(infile[0] == (KMX_BYTE) UTF16Sig[0] && infile[1] == (KMX_BYTE) UTF16Sig[1]) {
+ // UTF-16 source file
+ offset = 2;
+ } else {
+ // UTF-8 source file
+ KMX_BYTE* infile16;
+ int sz16;
+ if(!UTF16TempFromUTF8(infile, sz, &infile16, &sz16)) {
+ delete[] infile;
+ AddCompileError(CERR_CannotCreateTempfile);
+ return FALSE;
+ }
+ delete[] infile;
+ infile = infile16;
+ sz = sz16;
}
- return CompileKeyboard(pszInfile, pfkBuffer, TRUE, ACompilerWarningsAsErrors, AWarnDeprecatedCode,
- pMsgproc, AmsgprocContext, Target);
-}
+ kmcmp::CodeConstants = new kmcmp::NamedCodeConstants;
+ bool success = CompileKeyboardBuffer(infile+offset, sz-offset, &fk);
+ delete kmcmp::CodeConstants;
-EXTERN bool kmcmp_CompileKeyboardFile(char* pszInfile,
- char* pszOutfile, bool ASaveDebug, bool ACompilerWarningsAsErrors,
- bool AWarnDeprecatedCode, kmcmp_CompilerMessageProc pMsgproc, void* AmsgprocContext) // I4865 // I4866
-{
- if (!pMsgproc || !pszInfile || !pszOutfile) {
- AddCompileError(CERR_BadCallParams);
- return FALSE;
- }
+ delete[] infile;
- FILE_KEYBOARD fk;
- if(!CompileKeyboard(pszInfile, &fk, ASaveDebug, ACompilerWarningsAsErrors, AWarnDeprecatedCode,
- pMsgproc, AmsgprocContext, CKF_KEYMAN)) {
- // any errors will have been reported directly by CompileKeyboard
- return FALSE;
- }
-
- FILE* fp_out = Open_File(pszOutfile, "wb");
- if (fp_out == NULL) {
- AddCompileError(CERR_CannotCreateOutfile);
+ if (kmcmp::nErrors > 0 || !success) {
return FALSE;
}
+ // fill in result data
KMX_DWORD msg;
KMX_BYTE* data = nullptr;
size_t dataSize = 0;
- if ((msg = WriteCompiledKeyboard(&fk, &data, dataSize)) != CERR_None) {
- AddCompileError(msg);
- } else {
- if(fwrite(data, 1, dataSize, fp_out) != dataSize) {
- AddCompileError(CERR_UnableToWriteFully);
- }
- delete[] data;
- }
-
- fclose(fp_out);
-
- if (kmcmp::nErrors > 0) {
- remove(pszOutfile);
- return FALSE;
- }
-
- return TRUE;
-}
-
-bool CompileKeyboardHandle(FILE* fp_in, PFILE_KEYBOARD fk)
-{
- PKMX_WCHAR str, p;
-
- KMX_DWORD msg;
-
- kmcmp::FMnemonicLayout = FALSE;
-
- if (!fk) {
- AddCompileError(CERR_SomewhereIGotItWrong);
- return FALSE;
- }
-
- str = new KMX_WCHAR[LINESIZE];
- if (!str) {
- AddCompileError(CERR_CannotAllocateMemory);
- return FALSE;
- }
-
- fk->KeyboardID = 0;
- fk->version = 0;
- fk->dpStoreArray = NULL;
- fk->dpGroupArray = NULL;
- fk->cxStoreArray = 0;
- fk->cxGroupArray = 0;
- fk->StartGroup[0] = fk->StartGroup[1] = -1;
- fk->szName[0] = 0;
- fk->szCopyright[0] = 0;
- fk->dwFlags = KF_AUTOMATICVERSION;
- fk->currentGroup = 0xFFFFFFFF;
- fk->currentStore = 0;
- fk->cxDeadKeyArray = 0;
- fk->dpDeadKeyArray = NULL;
- fk->cxVKDictionary = 0; // I3438
- fk->dpVKDictionary = NULL; // I3438
- fk->extra = new FILE_KEYBOARD_EXTRA;
- fk->extra->kvksFilename = u"";
-/* fk->szMessage[0] = 0;
- fk->szLanguageName[0] = 0;*/
- fk->dwBitmapSize = 0;
- fk->dwHotKey = 0;
-
- kmcmp::BeginLine[BEGIN_ANSI] = -1;
- kmcmp::BeginLine[BEGIN_UNICODE] = -1;
- kmcmp::BeginLine[BEGIN_NEWCONTEXT] = -1;
- kmcmp::BeginLine[BEGIN_POSTKEYSTROKE] = -1;
-
-
- /* Add a store for the Keyman 6.0 copyright information string */
-
- if(kmcmp::FShouldAddCompilerVersion) {
- u16sprintf(str,LINESIZE, L"Created with Keyman Developer version %d.%d.%d.%d", KEYMAN_VersionMajor, KEYMAN_VersionMinor, KEYMAN_VersionPatch, 0);
- AddStore(fk, TSS_KEYMANCOPYRIGHT, str);
- }
-
- /* Add a system store for the Keyman edition number */
- AddStore(fk, TSS_CUSTOMKEYMANEDITION, u"0");
- AddStore(fk, TSS_CUSTOMKEYMANEDITIONNAME, u"Keyman");
-
- // must preprocess for group and store names -> this isn't really necessary, but never mind!
- while ((msg = ReadLine(fp_in, str, TRUE)) == CERR_None)
- {
- p = str;
- switch (LineTokenType(&p))
- {
- case T_VERSION:
- *(p + 4) = 0;
- if ((msg = AddStore(fk, TSS_VERSION, p)) != CERR_None) {
- AddCompileError(msg);
- return FALSE;
- }
- break;
-
- case T_GROUP:
- if ((msg = ProcessGroupLine(fk, p)) != CERR_None) {
- AddCompileError(msg);
- return FALSE;
- }
- break;
-
- case T_STORE:
- if ((msg = ProcessStoreLine(fk, p)) != CERR_None) {
- AddCompileError(msg);
- return FALSE;
- }
- break;
-
- default:
- break;
- }
- }
-
- if (msg != CERR_EndOfFile) {
- AddCompileError(msg);
- return FALSE;
- }
-
- fseek( fp_in,2,SEEK_SET);
- kmcmp::currentLine = 0;
-
- /* Reindex the list of codeconstants after stores added */
-
- kmcmp::CodeConstants->reindex();
-
- /* ReadLine will automatically skip over $Keyman lines, and parse wrapped lines */
- while ((msg = ReadLine(fp_in, str, FALSE)) == CERR_None)
- {
- msg = ParseLine(fk, str);
- if (msg != CERR_None) {
- AddCompileError(msg);
- return FALSE;
- }
- }
-
- if (msg != CERR_EndOfFile) {
- AddCompileError(msg);
- return FALSE;
- }
-
- ProcessGroupFinish(fk);
-
- if (kmcmp::FSaveDebug) kmcmp::RecordDeadkeyNames(fk);
-
- /* Add the compiler version as a system store */
- if ((msg = kmcmp::AddCompilerVersionStore(fk)) != CERR_None) {
- AddCompileError(msg);
- return FALSE;
- }
+ msg = WriteCompiledKeyboard(&fk, &data, dataSize);
- if ((msg = BuildVKDictionary(fk)) != CERR_None) {
- AddCompileError(msg);
- return FALSE;
- }
+ //TODO: FreeKeyboardPointers(fk);
- if ((msg = CheckFilenameConsistencyForCalls(fk)) != CERR_None) {
+ if(msg != CERR_None) {
AddCompileError(msg);
return FALSE;
}
- delete str;
-
- if (!kmcmp::CheckKeyboardFinalVersion(fk)) {
- return FALSE;
- }
-
- /* Warn on inconsistent use of NCAPS */
- if (!kmcmp::FMnemonicLayout) {
- CheckNCapsConsistency(fk);
- }
-
- /* Flag presence of deprecated features */
- kmcmp::CheckForDeprecatedFeatures(fk);
+ result.kmx = data;
+ result.kmxSize = dataSize;
+ result.kvksFilename = string_from_u16string(fk.extra->kvksFilename); // convert to UTF8
return TRUE;
}
+
diff --git a/developer/src/kmcmplib/src/CompilerInterfacesWasm.cpp b/developer/src/kmcmplib/src/CompilerInterfacesWasm.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0aab9ba51d4abf4262595356feed46819d2c0c4a
--- /dev/null
+++ b/developer/src/kmcmplib/src/CompilerInterfacesWasm.cpp
@@ -0,0 +1,117 @@
+#include "pch.h"
+#include
+
+#ifdef __EMSCRIPTEN__
+
+/*
+ WASM interface for compiler message callback
+*/
+EM_JS(int, wasm_msgproc, (int line, int msgcode, const char* text, char* context), {
+ const proc = globalThis[UTF8ToString(context)].message;
+ if(!proc || typeof proc != 'function') {
+ console.log(`[${line}: ${msgcode}: ${UTF8ToString(text)}]`);
+ return 0;
+ } else {
+ return proc(line, msgcode, UTF8ToString(text));
+ }
+});
+
+EM_JS(int, wasm_loadfileproc, (const char* filename, const char* baseFilename, void* buffer, int bufferSize, char* context), {
+ const proc = globalThis[UTF8ToString(context)].loadFile;
+ if(!proc || typeof proc != 'function') {
+ return 0;
+ } else {
+ if(buffer == 0) {
+ return proc(UTF8ToString(filename), UTF8ToString(baseFilename), 0, 0);
+ } else {
+ return proc(UTF8ToString(filename), UTF8ToString(baseFilename), buffer, bufferSize);
+ }
+ }
+});
+
+bool wasm_LoadFileProc(const char* filename, const char* baseFilename, void* buffer, int* bufferSize, void* context) {
+ char* msgProc = static_cast(context);
+ if(buffer == nullptr) {
+ *bufferSize = wasm_loadfileproc(filename, baseFilename, 0, 0, msgProc);
+ return *bufferSize != -1;
+ } else {
+ return wasm_loadfileproc(filename, baseFilename, buffer, *bufferSize, msgProc) == 1;
+ }
+}
+
+int wasm_CompilerMessageProc(int line, uint32_t dwMsgCode, const char* szText, void* context) {
+ char* msgProc = static_cast(context);
+ return wasm_msgproc(line, dwMsgCode, szText, msgProc);
+}
+
+struct WASM_COMPILER_INTERFACE {
+ std::string callbacksKey; // key of callbacks object on globalThis
+};
+
+struct WASM_COMPILER_RESULT {
+ bool result;
+ // Following are pointer offsets in heap + buffer size
+ int kmx;
+ int kmxSize;
+ // Following are compiler side-channel data, required for
+ // follow-on transform
+ std::string kvksFilename;
+ // TODO: additional data to be passed back
+};
+
+WASM_COMPILER_RESULT kmcmp_wasm_compile(std::string pszInfile, const KMCMP_COMPILER_OPTIONS options, const WASM_COMPILER_INTERFACE intf) {
+ WASM_COMPILER_RESULT r = {false};
+ KMCMP_COMPILER_RESULT kr;
+
+ r.kmx = 0;
+ r.kmxSize = 0;
+ r.kvksFilename = "";
+
+ r.result = kmcmp_CompileKeyboard(
+ pszInfile.c_str(),
+ options,
+ wasm_CompilerMessageProc,
+ wasm_LoadFileProc,
+ intf.callbacksKey.c_str(),
+ kr
+ );
+
+ if(r.result) {
+ // TODO: additional data as required by kmc_kmw
+ r.kmx = (int) kr.kmx;
+ r.kmxSize = (int) kr.kmxSize;
+ r.kvksFilename = kr.kvksFilename;
+ }
+
+ return r;
+}
+
+EMSCRIPTEN_BINDINGS(compiler_interface) {
+
+ emscripten::class_("CompilerOptions")
+ .constructor<>()
+ .property("saveDebug", &KMCMP_COMPILER_OPTIONS::saveDebug)
+ .property("compilerWarningsAsErrors", &KMCMP_COMPILER_OPTIONS::compilerWarningsAsErrors)
+ .property("warnDeprecatedCode", &KMCMP_COMPILER_OPTIONS::warnDeprecatedCode)
+ .property("shouldAddCompilerVersion", &KMCMP_COMPILER_OPTIONS::shouldAddCompilerVersion)
+ .property("target", &KMCMP_COMPILER_OPTIONS::target)
+ ;
+
+ emscripten::class_("CompilerInterface")
+ .constructor<>()
+ .property("callbacksKey", &WASM_COMPILER_INTERFACE::callbacksKey)
+ ;
+
+ emscripten::class_("CompilerResult")
+ .constructor<>()
+ .property("result", &WASM_COMPILER_RESULT::result)
+ .property("kmx", &WASM_COMPILER_RESULT::kmx)
+ .property("kmxSize", &WASM_COMPILER_RESULT::kmxSize)
+ .property("kvksFilename", &WASM_COMPILER_RESULT::kvksFilename)
+ ;
+
+ emscripten::function("kmcmp_compile", &kmcmp_wasm_compile);
+ emscripten::function("kmcmp_parseUnicodeSet", &kmcmp_parseUnicodeSet);
+}
+
+#endif
diff --git a/developer/src/kmcmplib/src/NamedCodeConstants.cpp b/developer/src/kmcmplib/src/NamedCodeConstants.cpp
index e6b1ae6d70ce20846ba23d24e0ab0efd05d2120a..a8af8fa783c23ac87f211452266d1c85cb9b87c8 100644
--- a/developer/src/kmcmplib/src/NamedCodeConstants.cpp
+++ b/developer/src/kmcmplib/src/NamedCodeConstants.cpp
@@ -24,18 +24,14 @@
#include "pch.h"
#include
#include "NamedCodeConstants.h"
-#include "CheckFilenameConsistency.h"
#include
#include "kmcompx.h"
-#include "filesystem.h"
using namespace kmcmp;
int IsHangulSyllable(const KMX_WCHAR *codename, int *code);
namespace kmcmp {
- extern KMX_CHAR CompileDir[];
-
int __cdecl sort_entries(const void *elem1, const void *elem2)
{
return u16icmp(
@@ -117,85 +113,57 @@ char *kmc_strupr(char *s) {
return s;
}
-KMX_BOOL NamedCodeConstants::IntLoadFile(const KMX_CHAR *filename)
-{
+KMX_BOOL NamedCodeConstants::LoadFile(PFILE_KEYBOARD fk, const KMX_WCHAR *filename) {
const int str_size = 256;
- FILE *fp = NULL;
- if (CheckFilenameConsistency(filename, FALSE) != 0) {
+ auto szNameUtf8 = string_from_u16string(filename);
+
+ int FileSize;
+ KMX_BYTE* Buf;
+ if(!loadfileproc(szNameUtf8.c_str(), fk->extra->kmnFilename.c_str(), nullptr, &FileSize, msgprocContext)) {
return FALSE;
}
- fp = Open_File(filename, "rt");
- if(fp == NULL) {
- return FALSE; // I3481
+ Buf = new KMX_BYTE[FileSize+1];
+ if(!loadfileproc(szNameUtf8.c_str(), fk->extra->kmnFilename.c_str(), Buf, &FileSize, msgprocContext)) {
+ delete[] Buf;
+ return FALSE;
}
+ Buf[FileSize] = 0; // zero-terminate for strtok
- KMX_CHAR str[str_size], *p, *q, *context = NULL;
- KMX_BOOL isEol , first = TRUE;
+ char* filetok;
+ char* filecontext;
+ filetok = strtok_r((char*)Buf, "\n", &filecontext);
- while(fgets(str, str_size, fp))
- {
- isEol = *(strchr(str, 0) - 1) == '\n';
- p = strtok_r(str, ";", &context); // I3481
- q = strtok_r(NULL, ";\n", &context);
- if(p && q)
- {
- if(first && *p == (KMX_CHAR)0xEF && *(p+1) == (KMX_CHAR)0xBB && *(p+2) == (KMX_CHAR)0xBF) p += 3; // I3056 UTF-8 // I3512
- first = FALSE;
+ if(*filetok == (KMX_CHAR)0xEF && *(filetok+1) == (KMX_CHAR)0xBB && *(filetok+2) == (KMX_CHAR)0xBF) filetok += 3; // I3056 UTF-8 // I3512
+
+ while(filetok) {
+ KMX_CHAR str[str_size], *p, *q, *context = NULL;
+
+ if(strlen(filetok) >= str_size) {
+ delete[] Buf;
+ // TODO chuck a wobbly
+ return FALSE;
+ }
+ strcpy(str, filetok);
+ p = strtok_r(str, ";\r", &context); // I3481
+ q = strtok_r(nullptr, ";\r", &context);
+ if(p && q) {
kmc_strupr(q); // I3481 // I3641
- long n = strtol(p, NULL, 16);
+ long n = strtol(p, nullptr, 16);
if (*q != '<') {
PKMX_WCHAR q0 = strtowstr(q);
AddCode_IncludedCodes((int)n, q0);
delete[] q0;
}
}
- if(!isEol )
- {
- while(fgets(str, str_size, fp)) if(*(strchr(str, 0)-1) == '\n') break;
- }
+ filetok = strtok_r(nullptr, "\n", &filecontext);
}
- fclose(fp);
-
- return TRUE;
-}
-
-KMX_BOOL NamedCodeConstants::LoadFile(const KMX_CHAR *filename)
-{
- const int buf_size = 260;
- KMX_CHAR buf[buf_size];
- // Look in current directory first -- REMOVED AS DANGEROUS
- /* strncpy(buf, filename, (buf_size-1)); buf[buf_size-1] = 0; // I3481
- if(kmcmp_FileExists(buf))
- return IntLoadFile(buf);
- */
- // Then look in keyboard file directory (CompileDir)
- strncpy(buf, CompileDir, (buf_size-1)); buf[buf_size-1] = 0; // I3481
- strncat(buf, filename, (buf_size-1)-strlen(CompileDir)); buf[buf_size-1] = 0;
- if(kmcmp_FileExists(buf))
- return IntLoadFile(buf);
-
- //TODO: sort out how to find common includes in non-Windows platforms:
- #ifdef _WINDOWS_
- // Finally look in kmcmpdll.dll directory
- GetModuleFileName(0, buf, buf_size);
-
- KMX_CHAR *p = strrchr_slash(buf);
- if(p)
- p++;
- else
- p = buf;
- *p = 0;
- strncat_s(buf, _countof(buf), filename, (buf_size-1)-strlen(buf)); buf[buf_size-1] = 0; // I3481 // I3641
- if(kmcmp_FileExists(buf))
- return IntLoadFile(buf);
- #endif
+ delete[] Buf;
reindex();
-
- return FALSE;
+ return TRUE;
}
void NamedCodeConstants::reindex()
diff --git a/developer/src/kmcmplib/src/NamedCodeConstants.h b/developer/src/kmcmplib/src/NamedCodeConstants.h
index 83d707d092cc4535a24cf3676aaa3d2a44d7b859..c537a73361b40f5539a75275fe86787e244b3541 100644
--- a/developer/src/kmcmplib/src/NamedCodeConstants.h
+++ b/developer/src/kmcmplib/src/NamedCodeConstants.h
@@ -2,6 +2,8 @@
#ifndef NAMEDCODECONSTANTS_H
#define NAMEDCODECONSTANTS_H
+#include "compfile.h"
+
#define MAX_ENAME 128
#define ALLOC_SIZE 256
@@ -23,14 +25,13 @@ namespace kmcmp{
int GetCode_IncludedCodes(const KMX_WCHAR *codename);
void AddCode_IncludedCodes(int n, const KMX_WCHAR *p);
- KMX_BOOL IntLoadFile(const KMX_CHAR *filename);
public:
NamedCodeConstants();
~NamedCodeConstants();
void reindex();
void AddCode(int n, const KMX_WCHAR *p, KMX_DWORD storeIndex);
- KMX_BOOL LoadFile(const KMX_CHAR *filename);
+ KMX_BOOL LoadFile(PFILE_KEYBOARD fk, const KMX_WCHAR *filename);
int GetCode(const KMX_WCHAR *codename, KMX_DWORD *storeIndex);
};
}
diff --git a/developer/src/kmcmplib/src/compfile.h b/developer/src/kmcmplib/src/compfile.h
index 595d1fececb2cf8b01b25e49a026fda1c7d103ac..c54663aba7659f871ce6271089494aa81420f6d0 100644
--- a/developer/src/kmcmplib/src/compfile.h
+++ b/developer/src/kmcmplib/src/compfile.h
@@ -123,7 +123,8 @@ typedef FILE_VKDICTIONARY *PFILE_VKDICTIONARY;
* Extra metadata for API consumers
*/
struct FILE_KEYBOARD_EXTRA {
- std::u16string kvksFilename; // original TSS_VISUALKEYBOARD value
+ std::string kmnFilename; // utf-8
+ std::u16string kvksFilename; // utf-16, original TSS_VISUALKEYBOARD value
};
typedef struct FILE_KEYBOARD_EXTRA* PFILE_KEYBOARD_EXTRA;
@@ -197,33 +198,4 @@ struct COMPILEMESSAGES {
typedef COMPILEMESSAGES *PCOMPILEMESSAGES;
-/*
-struct TVersion
-{
- //int MinVersion; // 0x0500 usually
- //int CompilerVersion[4];
- //int MinCompilerVersion[4];
- int KeyboardVersion; // 0x0500 usually
-};
-
-extern TVersion FVersionInfo;
-*/
-
-/*
-#define bstrcpy(c,d) (LPBYTE)strcpy((LPSTR)(c),(LPSTR)(d))
-#define bstrlen(c) strlen((LPSTR)(c))
-#define bstrcmp(c,d) strcmp((LPSTR)(c),(LPSTR)(d))
-#define bstrncmp(c,d,n) strncmp((LPSTR)(c),(LPSTR)(d),(n))
-#define bstrnicmp(c,d,n) strnicmp((LPSTR)(c),(LPSTR)(d),(n))
-#define bstricmp(c,d) stricmp((LPSTR)(c),(LPSTR)(d))
-#define bstrchr(c,ch) (LPBYTE)strchr((LPSTR)(c),(char)ch)
-#define bstrncpy(c,d,n) (LPBYTE)strncpy((LPSTR)(c),(LPSTR)(d),(n))
-#define bstrtok(c,d) (LPBYTE)strtok((LPSTR)(c),(LPSTR)(d))
-#define bstrcat(c,d) (LPBYTE)strcat((LPSTR)(c),(LPSTR)(d))
-#define bstrncat(c,d,n) (LPBYTE)strncat((LPSTR)(c),(LPSTR)(d),(n))
-#define bstrrev(c) (LPBYTE)strrev((LPSTR)(c))
-#define batoi(c) atoi((LPSTR)(c))
-#define bstrtol(c,d,n) strtol((LPSTR)(c),(LPSTR *)(d),(n))
-*/
-
#endif // _COMPFILE_H
diff --git a/developer/src/kmcmplib/src/json-validation.cpp b/developer/src/kmcmplib/src/json-validation.cpp
deleted file mode 100644
index f58d8c950534943427285a4f400fb39c190217ff..0000000000000000000000000000000000000000
--- a/developer/src/kmcmplib/src/json-validation.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-
-#include
-#include
-
-#include
-#include "kmcompx.h"
-
-using nlohmann::json;
-using nlohmann::json_uri;
-using nlohmann::json_schema_draft4::json_validator;
-
-static void loader(const json_uri &uri, json &schema)
-{
- std::fstream lf("." + uri.path());
- if (!lf.good())
- throw std::invalid_argument("could not open " + uri.url() + " tried with " + uri.path());
-
- lf >> schema;
-}
-
-EXTERN bool kmcmp_ValidateJsonFile(std::fstream& f, std::fstream& fd, kmcmp_ValidateJsonMessageProc MessageProc, void* context) {
- if (!f.good()) {
- MessageProc(-1, "Schema file could not be loaded.", context);
- return FALSE;
- }
-
- // 1) Read the schema for the document you want to validate
- json schema;
- try {
- f >> schema;
- }
- catch (std::exception &e) {
- MessageProc(f.tellp(), e.what(), context);
- return FALSE;
- }
-
- // 2) create the validator and
- json_validator validator(loader, [](const std::string &, const std::string &) {});
-
- try {
- // insert this schema as the root to the validator
- // this resolves remote-schemas, sub-schemas and references via the given loader-function
- validator.set_root_schema(schema);
- }
- catch (std::exception &e) {
- MessageProc(-2, e.what(), context);
- return FALSE;
- }
-
- // 3) do the actual validation of the document
- json document;
-
- if (!fd.good()) {
- MessageProc(-3, "Json file could not be loaded.", context);
- return FALSE;
- }
-
- try {
- fd >> document;
- validator.validate(document);
- }
- catch (std::exception &e) {
- MessageProc(fd.tellp(), e.what(), context);
- return FALSE;
- }
-
- return TRUE;
-}
\ No newline at end of file
diff --git a/developer/src/kmcmplib/src/kmcmplib.h b/developer/src/kmcmplib/src/kmcmplib.h
index 78e945ee0d866af69158dd7a9b1e04e3916a914a..f22c836baeeff7cdf0e72de4a3fb2caab4dfd740 100644
--- a/developer/src/kmcmplib/src/kmcmplib.h
+++ b/developer/src/kmcmplib/src/kmcmplib.h
@@ -14,7 +14,6 @@ namespace kmcmp {
extern KMX_BOOL FMnemonicLayout;
extern KMX_BOOL FOldCharPosMatching;
extern int CompileTarget;
- extern KMX_CHAR CompileDir[260]; // TODO: this should not be a fixed buffer
extern int BeginLine[4];
extern int currentLine;
extern NamedCodeConstants *CodeConstants;
@@ -24,6 +23,7 @@ namespace kmcmp {
}
extern kmcmp_CompilerMessageProc msgproc;
+extern kmcmp_LoadFileProc loadfileproc;
extern void* msgprocContext;
extern KMX_BOOL AWarnDeprecatedCode_GLOBAL_LIB;
@@ -40,10 +40,10 @@ KMX_BOOL AddCompileError(KMX_DWORD msg);
PKMX_WCHAR strtowstr(PKMX_STR in);
PFILE_STORE FindSystemStore(PFILE_KEYBOARD fk, KMX_DWORD dwSystemID);
-FILE* UTF16TempFromUTF8(FILE* fp_in , KMX_BOOL hasPreamble);
+bool UTF16TempFromUTF8(KMX_BYTE* infile, int sz, KMX_BYTE** tempfile, int *sz16);
KMX_DWORD WriteCompiledKeyboard(PFILE_KEYBOARD fk, KMX_BYTE**data, size_t& dataSize);
KMX_DWORD AddStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, const KMX_WCHAR * str, KMX_DWORD *dwStoreID= NULL);
-KMX_DWORD ReadLine(FILE* fp_in , PKMX_WCHAR wstr, KMX_BOOL PreProcess);
+KMX_DWORD ReadLine(KMX_BYTE* infile, int sz, int& offset, PKMX_WCHAR wstr, KMX_BOOL PreProcess);
KMX_DWORD ParseLine(PFILE_KEYBOARD fk, PKMX_WCHAR str);
KMX_DWORD ProcessGroupLine(PFILE_KEYBOARD fk, PKMX_WCHAR p);
KMX_DWORD ProcessGroupFinish(PFILE_KEYBOARD fk);
diff --git a/developer/src/kmcmplib/src/meson.build b/developer/src/kmcmplib/src/meson.build
index 292b158ce276abf20060f38c8770cdbf1f0dcb84..c6ed2b84b01869c07311687fa5445dfed1b9f06d 100644
--- a/developer/src/kmcmplib/src/meson.build
+++ b/developer/src/kmcmplib/src/meson.build
@@ -7,6 +7,7 @@
# TODO: is this required? It should be Keyman Core only
defns += ['-DKMN_KBP_EXPORTING']
version_res = []
+lib_links = []
if cpp_compiler.get_id() == 'gcc' or cpp_compiler.get_id() == 'clang'
warns += [
@@ -24,12 +25,10 @@ endif
name_suffix = []
if cpp_compiler.get_id() == 'emscripten'
- links += ['-lnodefs.js', '-sMODULARIZE', '-sEXPORT_ES6', '--whole-archive', '--bind', '-sEXPORTED_RUNTIME_METHODS=[\'cwrap\', \'UTF8ToString\']']
- # tests are building as ES6 so we need to declare the file extension
- # note that meson currently struggles with the sanitycheckc_cross.exe
- # program, because it has a hard coded extension (.exe) which is not
- # valid for node programs in module mode.
- name_suffix = '.mjs'
+ # wasm-exceptions supported in Node 18+, Chrome 95+, Firefox 100+, Safari 15.2+
+ flags += ['-fwasm-exceptions']
+ lib_links = ['--whole-archive', '-sMODULARIZE', '-sEXPORT_ES6']
+ links += ['-fwasm-exceptions', '--bind', '-sEXPORTED_RUNTIME_METHODS=[\'UTF8ToString\']']
endif
icu = subproject('icu-for-uset', default_options: [ 'default_library=static', 'cpp_std=c++17', 'warning_level=0', 'werror=false'])
@@ -38,36 +37,29 @@ icuuc_dep = icu.get_variable('icuuc_dep')
lib = library('kmcmplib',
'CasedKeys.cpp',
'CharToKeyConversion.cpp',
+ 'CheckForDuplicates.cpp',
+ 'CheckNCapsConsistency.cpp',
+ 'CompileKeyboardBuffer.cpp',
'Compiler.cpp',
'CompilerInterfaces.cpp',
+ 'CompilerInterfacesWasm.cpp',
+ 'CompMsg.cpp',
'DeprecationChecks.cpp',
'Edition.cpp',
- 'filesystem.cpp',
- 'json-validation.cpp',
+ 'kmx_u16.cpp',
'NamedCodeConstants.cpp',
+ 'UnreachableRules.cpp',
+ 'uset-api.cpp',
'versioning.cpp',
'virtualcharkeys.cpp',
'xstring.cpp',
- 'CharToKeyConversion.cpp',
- 'CheckFilenameConsistency.cpp',
- 'CheckForDuplicates.cpp',
- 'CheckNCapsConsistency.cpp',
- 'UnreachableRules.cpp',
- 'kmx_u16.cpp',
- 'CompMsg.cpp',
- 'uset-api.cpp',
-
'../../../../common/windows/cpp/src/ConvertUTF.c',
'../../../../common/windows/cpp/src/crc32.cpp',
'../../../../common/windows/cpp/src/vkeys.cpp',
- 'xstring.cpp',
- '../../ext/json-schema-validator/json-schema-draft4.json.cpp',
- '../../ext/json-schema-validator/json-uri.cpp',
- '../../ext/json-schema-validator/json-validator.cpp',
version_res,
cpp_args: defns + warns + flags,
- link_args: links,
+ link_args: links + lib_links,
version: meson.project_version(),
include_directories: inc,
install: true,
@@ -81,7 +73,7 @@ if cpp_compiler.get_id() == 'emscripten'
host = executable('wasm-host', #'wasm-host.cpp',
cpp_args: defns,
include_directories: inc,
- link_args: links,
+ link_args: links + lib_links,
objects: lib.extract_all_objects(),
dependencies: icuuc_dep)
endif
diff --git a/developer/src/kmcmplib/src/pch.h b/developer/src/kmcmplib/src/pch.h
index f878ad11be4f892ad4e9f0ece5864712c6af5c6b..358e28998abc8e65bba25cf2b594ca95cbed5b57 100644
--- a/developer/src/kmcmplib/src/pch.h
+++ b/developer/src/kmcmplib/src/pch.h
@@ -10,6 +10,4 @@
#include "../../../../common/windows/cpp/include/crc32.h"
#include
-#include
-
#include
diff --git a/developer/src/kmcmplib/src/uset-api.cpp b/developer/src/kmcmplib/src/uset-api.cpp
index 1aa22477b2b63465a8cb0db8b7d624409d9ce083..c2c72cab347587e64998ce7ecfed41614ea8fc49 100644
--- a/developer/src/kmcmplib/src/uset-api.cpp
+++ b/developer/src/kmcmplib/src/uset-api.cpp
@@ -4,16 +4,13 @@
#include "unicode/uniset.h"
#include "unicode/unistr.h"
-EXTERN int kmcmp_ParseUnicodeSet(
- const char* szText,
- uint32_t* outputBuffer,
+EXTERN int kmcmp_parseUnicodeSet(
+ const std::string text,
+ uintptr_t outputBuffer_,
uint32_t outputBufferSize
) {
- if (szText == nullptr) {
- // null string coming in
- return KMCMP_ERROR_SYNTAX_ERR;
- }
- const icu::UnicodeString str = icu::UnicodeString::fromUTF8(szText);
+ uint32_t* outputBuffer = reinterpret_cast(outputBuffer_);
+ const icu::UnicodeString str = icu::UnicodeString::fromUTF8(text.c_str());
if (str.isBogus() || str.isEmpty()) {
// empty string
return KMCMP_ERROR_SYNTAX_ERR;
diff --git a/developer/src/kmcmplib/tests/api-test.cpp b/developer/src/kmcmplib/tests/api-test.cpp
index 4c46b1f1a408c166cda98dda0ef43d7449471a1e..cb0337caef680107910d8b219bca8a69171c5a15 100644
--- a/developer/src/kmcmplib/tests/api-test.cpp
+++ b/developer/src/kmcmplib/tests/api-test.cpp
@@ -18,32 +18,20 @@
#include
#include "../src/compfile.h"
#include
+#include "util_filesystem.h"
+#include "util_callbacks.h"
void setup();
-void test_kmcmp_CompileKeyboardFile();
-void test_kmcmp_CompileKeyboardFileToBuffer();
-
-std::vector error_vec;
-
-int msgproc(int line, uint32_t dwMsgCode, char* szText, void* context) {
- error_vec.push_back(dwMsgCode);
- const char*t = "unknown";
- switch(dwMsgCode & 0xF000) {
- case CERR_HINT: t=" hint"; break;
- case CERR_WARNING: t="warning"; break;
- case CERR_ERROR: t=" error"; break;
- case CERR_FATAL: t=" fatal"; break;
- }
- printf("line %d %s %04.4x: %s\n", line, t, (unsigned int)dwMsgCode, szText);
- return 1;
-}
+void test_kmcmp_CompileKeyboard(char *kmn_file);
int main(int argc, char *argv[]) {
+ if(argc < 1) {
+ puts("Usage: api-test ");
+ puts("Warning: blank_keyboard will be overwritten");
+ return 1;
+ }
setup();
- test_kmcmp_CompileKeyboardFile();
-
- setup();
- test_kmcmp_CompileKeyboardFileToBuffer();
+ test_kmcmp_CompileKeyboard(argv[1]);
return 0;
}
@@ -52,36 +40,27 @@ void setup() {
error_vec.clear();
}
-void test_kmcmp_CompileKeyboardFile() {
- char kmn_file[L_tmpnam], kmx_file[L_tmpnam];
- tmpnam(kmn_file);
- tmpnam(kmx_file);
-
- // Create an empty file
- FILE *fp = fopen(kmn_file, "w");
- fclose(fp);
-
- // It should fail when a zero-byte file is passed in
- assert(!kmcmp_CompileKeyboardFile(kmn_file, kmx_file, true, false, true, msgproc, nullptr));
- assert(error_vec.size() == 1);
- assert(error_vec[0] == CERR_CannotReadInfile);
-
- unlink(kmn_file);
-}
-
-void test_kmcmp_CompileKeyboardFileToBuffer() {
- char kmn_file[L_tmpnam], kmx_file[L_tmpnam];
- tmpnam(kmn_file);
- tmpnam(kmx_file);
+/*
+ TODO: tests to run:
+ 4. ANSI (no BOM of course) #8884
+ 8. file without blank last line (cannot compare with fixture due to bug in kmcmpdll...)
+ Hint to add: k004_ansi.kmn: Hint: 10A6 Keyman Developer has detected that the file has ANSI encoding. Consider converting this file to UTF-8
+*/
+void test_kmcmp_CompileKeyboard(char *kmn_file) {
// Create an empty file
- FILE *fp = fopen(kmn_file, "w");
+ FILE *fp = Open_File(kmn_file, "wb");
fclose(fp);
- FILE_KEYBOARD fk;
-
// It should fail when a zero-byte file is passed in
- assert(!kmcmp_CompileKeyboardFileToBuffer(kmn_file, &fk, true, false, msgproc, nullptr, CKF_KEYMAN));
+ KMCMP_COMPILER_RESULT result;
+ KMCMP_COMPILER_OPTIONS options;
+ options.saveDebug = true;
+ options.compilerWarningsAsErrors = false;
+ options.warnDeprecatedCode = true;
+ options.shouldAddCompilerVersion = false;
+ options.target = CKF_KEYMAN;
+ assert(!kmcmp_CompileKeyboard(kmn_file, options, msgproc, loadfileProc, nullptr, result));
assert(error_vec.size() == 1);
assert(error_vec[0] == CERR_CannotReadInfile);
diff --git a/developer/src/kmcmplib/tests/fixtures/adiga_danef.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/adiga_danef.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/adiga_danef.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/adiga_danef.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/ahom_star.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/ahom_star.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/ahom_star.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/ahom_star.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/aksarabali_panlex.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/aksarabali_panlex.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/aksarabali_panlex.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/aksarabali_panlex.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/alephwithbeth.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/alephwithbeth.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/alephwithbeth.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/alephwithbeth.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/anii.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/anii.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/anii.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/anii.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/arabic_izza.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/arabic_izza.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/arabic_izza.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/arabic_izza.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/aramaic_hebrew.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/aramaic_hebrew.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/aramaic_hebrew.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/aramaic_hebrew.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/armenian_mnemonic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/armenian_mnemonic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/armenian_mnemonic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/armenian_mnemonic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/armenian_mnemonic_r.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/armenian_mnemonic_r.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/armenian_mnemonic_r.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/armenian_mnemonic_r.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/athinkra_vai.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/athinkra_vai.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/athinkra_vai.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/athinkra_vai.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/athinkra_vai_typewriter.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/athinkra_vai_typewriter.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/athinkra_vai_typewriter.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/athinkra_vai_typewriter.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/ausephon.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/ausephon.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/ausephon.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/ausephon.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/balochi_inpage.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_inpage.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/balochi_inpage.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_inpage.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/balochi_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_latin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/balochi_latin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_latin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/balochi_persian.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_persian.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/balochi_persian.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_persian.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/balochi_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_phonetic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/balochi_phonetic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_phonetic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/balochi_scientific.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_scientific.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/balochi_scientific.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_scientific.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/balochi_urdu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_urdu.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/balochi_urdu.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_urdu.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/bangla_joy.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bangla_joy.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/bangla_joy.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bangla_joy.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/bangla_munir.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bangla_munir.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/bangla_munir.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bangla_munir.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/bangla_probhat.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bangla_probhat.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/bangla_probhat.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bangla_probhat.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbda1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbda1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbda1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbda1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbda2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbda2.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbda2.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbda2.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbda3.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbda3.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbda3.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbda3.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdadlm.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdadlm.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdadlm.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdadlm.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdal.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdal.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdal.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdal.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdarme.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdarme.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdarme.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdarme.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdarmph.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdarmph.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdarmph.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdarmph.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdarmty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdarmty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdarmty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdarmty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdarmw.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdarmw.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdarmw.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdarmw.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdaze.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdaze.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdaze.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdaze.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdazel.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdazel.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdazel.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdazel.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdazst.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdazst.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdazst.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdazst.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbash.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbash.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbash.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbash.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbe.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbe.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbe.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbe.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbene.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbene.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbene.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbene.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbgph.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbgph.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbgph.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbgph.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbgph1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbgph1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbgph1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbgph1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbhc.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbhc.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbhc.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbhc.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdblr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdblr.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdblr.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdblr.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbr.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbr.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbr.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbu.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbu.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbu.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbug.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbug.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbug.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbug.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbulg.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbulg.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbulg.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbulg.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdca.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdca.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdca.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdca.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcan.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdcan.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcan.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcher.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcher.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdcher.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcher.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcherp.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcherp.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdcherp.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcherp.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcr.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdcr.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcr.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcz.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcz.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdcz.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcz.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcz1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcz1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdcz1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcz1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcz2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcz2.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdcz2.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcz2.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdda.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdda.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdda.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdda.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbddiv1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbddiv1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbddiv1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbddiv1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbddiv2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbddiv2.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbddiv2.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbddiv2.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbddv.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbddv.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbddv.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbddv.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbddzo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbddzo.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbddzo.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbddzo.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdes.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdes.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdes.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdes.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdest.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdest.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdest.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdest.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfa.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdfa.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfa.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfar.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfar.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdfar.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfar.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfi.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdfi.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfi.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfi1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfi1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdfi1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfi1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfo.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdfo.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfo.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfr.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdfr.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfr.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfthrk.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfthrk.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdfthrk.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfthrk.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgae.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgae.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgae.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgae.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgeo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeo.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgeo.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeo.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgeoer.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeoer.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgeoer.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeoer.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgeome.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeome.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgeome.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeome.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgeooa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeooa.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgeooa.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeooa.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgeoqw.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeoqw.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgeoqw.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeoqw.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgkl.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgkl.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgkl.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgkl.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgn.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgn.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgn.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgn.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgr.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgr.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgr.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgr1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgr1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgr1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgr1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgrlnd.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgrlnd.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgrlnd.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgrlnd.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgthc.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgthc.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgthc.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgthc.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhau.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhau.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhau.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhau.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhaw.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhaw.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhaw.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhaw.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhe.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhe.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhe.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhe.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhe220.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhe220.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhe220.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhe220.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhe319.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhe319.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhe319.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhe319.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdheb.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdheb.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdheb.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdheb.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhebl3.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhebl3.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhebl3.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhebl3.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhela2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhela2.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhela2.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhela2.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhela3.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhela3.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhela3.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhela3.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhept.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhept.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhept.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhept.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhu.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhu.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhu.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhu1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhu1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhu1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhu1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdibo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdibo.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdibo.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdibo.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinasa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinasa.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinasa.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinasa.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinbe2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinbe2.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinbe2.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinbe2.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinben.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinben.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinben.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinben.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdindev.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdindev.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdindev.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdindev.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinen.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinen.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinen.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinen.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinguj.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinguj.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinguj.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinguj.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinhin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinhin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinhin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinhin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinkan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinkan.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinkan.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinkan.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinmal.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinmal.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinmal.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinmal.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinmar.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinmar.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinmar.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinmar.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinori.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinori.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinori.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinori.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinpun.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinpun.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinpun.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinpun.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdintam.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdintam.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdintam.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdintam.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdintel.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdintel.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdintel.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdintel.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinuk2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinuk2.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinuk2.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinuk2.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdir.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdir.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdir.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdir.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdit.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdit.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdit.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdit.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdit142.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdit142.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdit142.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdit142.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdiulat.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdiulat.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdiulat.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdiulat.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdjav.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdjav.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdjav.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdjav.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdkaz.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkaz.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdkaz.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkaz.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdkhmr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkhmr.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdkhmr.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkhmr.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdkni.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkni.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdkni.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkni.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdkurd.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkurd.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdkurd.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkurd.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdkyr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkyr.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdkyr.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkyr.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdla.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdla.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdla.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdla.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlao.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlao.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdlao.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlao.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlisub.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlisub.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdlisub.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlisub.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlisus.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlisus.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdlisus.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlisus.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlt.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlt.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdlt.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlt.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlt1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlt1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdlt1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlt1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlt2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlt2.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdlt2.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlt2.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlv.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlv.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdlv.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlv.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlv1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlv1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdlv1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlv1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlvst.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlvst.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdlvst.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlvst.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmac.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmac.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdmac.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmac.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmacst.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmacst.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdmacst.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmacst.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmaori.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmaori.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdmaori.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmaori.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmlt47.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmlt47.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdmlt47.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmlt47.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmlt48.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmlt48.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdmlt48.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmlt48.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmon.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmon.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdmon.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmon.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmonmo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmonmo.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdmonmo.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmonmo.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmonst.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmonst.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdmonst.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmonst.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmyan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmyan.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdmyan.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmyan.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdne.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdne.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdne.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdne.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdnepr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdnepr.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdnepr.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdnepr.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdnko.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdnko.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdnko.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdnko.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdno.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdno.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdno.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdno.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdno1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdno1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdno1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdno1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdnso.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdnso.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdnso.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdnso.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdntl.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdntl.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdntl.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdntl.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdogham.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdogham.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdogham.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdogham.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdolch.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdolch.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdolch.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdolch.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdoldit.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdoldit.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdoldit.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdoldit.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdosa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdosa.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdosa.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdosa.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdosm.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdosm.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdosm.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdosm.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdpash.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdpash.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdpash.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdpash.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdphags.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdphags.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdphags.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdphags.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdpl.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdpl.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdpl.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdpl.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdpl1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdpl1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdpl1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdpl1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdpo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdpo.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdpo.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdpo.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdropr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdropr.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdropr.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdropr.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdrost.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdrost.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdrost.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdrost.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdru.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdru.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdru.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdru.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdru1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdru1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdru1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdru1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdrum.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdrum.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdrum.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdrum.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsf.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsf.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsf.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsf.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsg.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsg.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsg.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsg.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsl.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsl.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsl.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsl.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsl1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsl1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsl1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsl1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsmsfi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsmsfi.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsmsfi.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsmsfi.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsmsno.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsmsno.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsmsno.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsmsno.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsn1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsn1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsn1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsn1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsora.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsora.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsora.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsora.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsorex.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsorex.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsorex.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsorex.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsors1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsors1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsors1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsors1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsp.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsp.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsp.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsp.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsw.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsw.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsw.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsw.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsw09.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsw09.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsw09.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsw09.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsyr1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsyr1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsyr1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsyr1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsyr2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsyr2.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsyr2.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsyr2.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtaile.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtaile.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtaile.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtaile.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtajik.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtajik.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtajik.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtajik.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtam99.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtam99.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtam99.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtam99.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdth0.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdth0.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdth0.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdth0.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdth1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdth1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdth1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdth1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdth2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdth2.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdth2.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdth2.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdth3.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdth3.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdth3.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdth3.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtifi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtifi.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtifi.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtifi.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtifi2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtifi2.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtifi2.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtifi2.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtiprd.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtiprd.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtiprd.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtiprd.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtt102.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtt102.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtt102.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtt102.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtuf.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtuf.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtuf.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtuf.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtuq.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtuq.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtuq.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtuq.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdturme.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdturme.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdturme.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdturme.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtzm.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtzm.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtzm.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtzm.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdughr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdughr.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdughr.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdughr.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdughr1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdughr1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdughr1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdughr1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbduk.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbduk.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbduk.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbduk.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdukx.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdukx.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdukx.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdukx.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdur.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdur.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdur.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdur.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdur1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdur1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdur1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdur1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdurdu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdurdu.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdurdu.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdurdu.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdus.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdus.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdus.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdus.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdusa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdusa.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdusa.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdusa.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdusl.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdusl.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdusl.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdusl.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdusr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdusr.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdusr.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdusr.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdusx.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdusx.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdusx.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdusx.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbduzb.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbduzb.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbduzb.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbduzb.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdvntc.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdvntc.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdvntc.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdvntc.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdwol.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdwol.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdwol.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdwol.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdyak.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdyak.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdyak.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdyak.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdyba.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdyba.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdyba.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdyba.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdycc.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdycc.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdycc.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdycc.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdycl.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdycl.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/basic_kbdycl.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdycl.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/batak.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/batak.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/batak.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/batak.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/baybayin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/baybayin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/baybayin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/baybayin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/bhaiksuki_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bhaiksuki_inscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/bhaiksuki_inscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bhaiksuki_inscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/bj_cree_east.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_east.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/bj_cree_east.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_east.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/bj_cree_east_james_bay.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_east_james_bay.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/bj_cree_east_james_bay.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_east_james_bay.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/bj_cree_east_latn.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_east_latn.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/bj_cree_east_latn.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_east_latn.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/bj_cree_west_latn.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_west_latn.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/bj_cree_west_latn.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_west_latn.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/bj_cree_woods.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_woods.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/bj_cree_woods.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_woods.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/bj_innu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_innu.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/bj_innu.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_innu.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/bj_innu_phonemic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_innu_phonemic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/bj_innu_phonemic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_innu_phonemic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/bj_mista_wasaha_cree.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_mista_wasaha_cree.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/bj_mista_wasaha_cree.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_mista_wasaha_cree.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/bj_naskapi_classic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_naskapi_classic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/bj_naskapi_classic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_naskapi_classic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/bj_naskapi_common.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_naskapi_common.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/bj_naskapi_common.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_naskapi_common.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/bj_oji_cree.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_oji_cree.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/bj_oji_cree.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_oji_cree.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/brahmi_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/brahmi_inscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/brahmi_inscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/brahmi_inscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/btl_kenya.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/btl_kenya.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/btl_kenya.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/btl_kenya.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/bu_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bu_phonetic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/bu_phonetic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bu_phonetic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/buhid.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/buhid.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/buhid.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/buhid.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/burushaski_girminas.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/burushaski_girminas.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/burushaski_girminas.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/burushaski_girminas.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/cabecar.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/cabecar.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/cabecar.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/cabecar.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/carian.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/carian.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/carian.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/carian.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/chalchiteko.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/chalchiteko.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/chalchiteko.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/chalchiteko.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/chechen_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/chechen_latin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/chechen_latin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/chechen_latin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/chinuk_wawa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/chinuk_wawa.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/chinuk_wawa.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/chinuk_wawa.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/choctaw_modern.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/choctaw_modern.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/choctaw_modern.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/choctaw_modern.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/cim.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/cim.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/cim.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/cim.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/clavbur9.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/clavbur9.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/clavbur9.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/clavbur9.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/colchis_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/colchis_latin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/colchis_latin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/colchis_latin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/colchis_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/colchis_phonetic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/colchis_phonetic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/colchis_phonetic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/common_devanagari.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/common_devanagari.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/common_devanagari.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/common_devanagari.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/coptic_greek.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/coptic_greek.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/coptic_greek.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/coptic_greek.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/coptic_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/coptic_qwerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/coptic_qwerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/coptic_qwerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/dagbani.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/dagbani.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/dagbani.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/dagbani.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/dega.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/dega.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/dega.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/dega.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/dene.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/dene.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/dene.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/dene.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/deseret.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/deseret.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/deseret.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/deseret.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/dogra_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/dogra_inscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/dogra_inscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/dogra_inscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/easy_chakma.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/easy_chakma.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/easy_chakma.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/easy_chakma.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/ekwtamil99uni.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/ekwtamil99uni.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/ekwtamil99uni.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/ekwtamil99uni.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/el_dinka.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_dinka.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/el_dinka.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_dinka.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/el_harari_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_harari_latin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/el_harari_latin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_harari_latin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/el_naija.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_naija.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/el_naija.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_naija.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/el_nuer.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_nuer.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/el_nuer.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_nuer.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/el_pasifika.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_pasifika.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/el_pasifika.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_pasifika.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/el_yolngu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_yolngu.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/el_yolngu.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_yolngu.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/embera_north.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/embera_north.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/embera_north.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/embera_north.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/enga.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/enga.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/enga.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/enga.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/enggano.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/enggano.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/enggano.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/enggano.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/english_shavian_igc.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/english_shavian_igc.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/english_shavian_igc.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/english_shavian_igc.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/english_shavian_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/english_shavian_qwerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/english_shavian_qwerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/english_shavian_qwerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/engram.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/engram.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/engram.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/engram.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/esperuni.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/esperuni.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/esperuni.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/esperuni.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/farsiman.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/farsiman.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/farsiman.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/farsiman.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/finongan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/finongan.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/finongan.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/finongan.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fulfulde_ajami_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fulfulde_ajami_qwerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fulfulde_ajami_qwerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fulfulde_ajami_qwerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fulfulde_latin_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fulfulde_latin_qwerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fulfulde_latin_qwerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fulfulde_latin_qwerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_dakelh.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_dakelh.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_dakelh.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_dakelh.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_dane_zaa_zaage.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_dane_zaa_zaage.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_dane_zaa_zaage.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_dane_zaa_zaage.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_denesuline_epsilon.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_denesuline_epsilon.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_denesuline_epsilon.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_denesuline_epsilon.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_diitiidatx.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_diitiidatx.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_diitiidatx.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_diitiidatx.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_gitsenimx.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_gitsenimx.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_gitsenimx.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_gitsenimx.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_gwichin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_gwichin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_gwichin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_gwichin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_hailzaqvla.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_hailzaqvla.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_hailzaqvla.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_hailzaqvla.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_haisla.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_haisla.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_haisla.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_haisla.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_halqemeylem.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_halqemeylem.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_halqemeylem.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_halqemeylem.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_han.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_han.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_han.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_han.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_henqeminem.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_henqeminem.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_henqeminem.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_henqeminem.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_hlgaagilda_xaayda_kil.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_hlgaagilda_xaayda_kil.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_hlgaagilda_xaayda_kil.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_hlgaagilda_xaayda_kil.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_kanienkeha_e.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_kanienkeha_e.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_kanienkeha_e.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_kanienkeha_e.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_ktunaxa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_ktunaxa.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_ktunaxa.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_ktunaxa.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_kwakwala.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_kwakwala.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_kwakwala.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_kwakwala.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_kwakwala_liqwala.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_kwakwala_liqwala.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_kwakwala_liqwala.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_kwakwala_liqwala.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_migmaq.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_migmaq.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_migmaq.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_migmaq.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_natwits.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_natwits.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_natwits.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_natwits.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_nisgaa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_nisgaa.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_nisgaa.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_nisgaa.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_nlekepmxcin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_nlekepmxcin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_nlekepmxcin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_nlekepmxcin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_northern_tutchone.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_northern_tutchone.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_northern_tutchone.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_northern_tutchone.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_nsilxcen.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_nsilxcen.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_nsilxcen.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_nsilxcen.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_nuucaanul.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_nuucaanul.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_nuucaanul.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_nuucaanul.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_secwepemctsin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_secwepemctsin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_secwepemctsin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_secwepemctsin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_sencoten.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_sencoten.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_sencoten.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_sencoten.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_shashishalhem.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_shashishalhem.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_shashishalhem.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_shashishalhem.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_smalgyax.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_smalgyax.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_smalgyax.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_smalgyax.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_southern_tutchone.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_southern_tutchone.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_southern_tutchone.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_southern_tutchone.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_statimcets.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_statimcets.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_statimcets.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_statimcets.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_stlatlimxec.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_stlatlimxec.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_stlatlimxec.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_stlatlimxec.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_tagizi_dene.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_tagizi_dene.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_tagizi_dene.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_tagizi_dene.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_taltan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_taltan.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_taltan.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_taltan.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_tlingit.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_tlingit.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_tlingit.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_tlingit.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_tsekehne.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_tsekehne.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_tsekehne.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_tsekehne.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_tsilhqotin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_tsilhqotin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_tsilhqotin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_tsilhqotin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_uwikala.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_uwikala.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_uwikala.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_uwikala.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/fv_xaislakala.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_xaislakala.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/fv_xaislakala.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_xaislakala.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/galaxie_greek_mnemonic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/galaxie_greek_mnemonic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/galaxie_greek_mnemonic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/galaxie_greek_mnemonic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/galaxie_greek_positional.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/galaxie_greek_positional.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/galaxie_greek_positional.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/galaxie_greek_positional.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/galaxie_hebrew_mnemonic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/galaxie_hebrew_mnemonic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/galaxie_hebrew_mnemonic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/galaxie_hebrew_mnemonic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/galaxie_hebrew_positional.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/galaxie_hebrew_positional.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/galaxie_hebrew_positional.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/galaxie_hebrew_positional.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/gandhari.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gandhari.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/gandhari.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gandhari.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/geezbrhan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/geezbrhan.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/geezbrhan.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/geezbrhan.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/gff_amh_7.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_amh_7.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/gff_amh_7.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_amh_7.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/gff_amharic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_amharic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/gff_amharic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_amharic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/gff_blin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_blin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/gff_blin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_blin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/gff_ethiopic_7.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_ethiopic_7.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/gff_ethiopic_7.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_ethiopic_7.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/gff_geez.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_geez.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/gff_geez.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_geez.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/gff_gurage.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_gurage.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/gff_gurage.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_gurage.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/gff_gurage_legacy.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_gurage_legacy.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/gff_gurage_legacy.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_gurage_legacy.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/gff_musnad.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_musnad.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/gff_musnad.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_musnad.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/gff_tigrinya_eritrea.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_tigrinya_eritrea.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/gff_tigrinya_eritrea.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_tigrinya_eritrea.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/gff_tigrinya_ethiopia.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_tigrinya_ethiopia.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/gff_tigrinya_ethiopia.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_tigrinya_ethiopia.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/ghana.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/ghana.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/ghana.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/ghana.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/gilaki.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gilaki.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/gilaki.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gilaki.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/gilaki_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gilaki_phonetic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/gilaki_phonetic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gilaki_phonetic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/gondi_dev.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gondi_dev.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/gondi_dev.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gondi_dev.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/gondi_gunjala.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gondi_gunjala.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/gondi_gunjala.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gondi_gunjala.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/gondi_tel.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gondi_tel.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/gondi_tel.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gondi_tel.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/greekclassical.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/greekclassical.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/greekclassical.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/greekclassical.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/hanunoo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/hanunoo.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/hanunoo.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/hanunoo.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/haroi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/haroi.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/haroi.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/haroi.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/hatran_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/hatran_inscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/hatran_inscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/hatran_inscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/hausa_ajami_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/hausa_ajami_qwerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/hausa_ajami_qwerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/hausa_ajami_qwerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/hausa_kano.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/hausa_kano.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/hausa_kano.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/hausa_kano.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/hcesar.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/hcesar.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/hcesar.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/hcesar.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/hieroglyphic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/hieroglyphic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/hieroglyphic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/hieroglyphic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/himyarit_musnad.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/himyarit_musnad.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/himyarit_musnad.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/himyarit_musnad.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/hindi_modular.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/hindi_modular.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/hindi_modular.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/hindi_modular.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/indigenous_nt.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/indigenous_nt.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/indigenous_nt.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/indigenous_nt.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/indonesia.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/indonesia.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/indonesia.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/indonesia.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/indonesian_suku.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/indonesian_suku.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/indonesian_suku.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/indonesian_suku.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/inuktitut_naqittaut.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/inuktitut_naqittaut.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/inuktitut_naqittaut.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/inuktitut_naqittaut.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/ishkashimi_cyrillic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/ishkashimi_cyrillic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/ishkashimi_cyrillic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/ishkashimi_cyrillic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_bengali.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_bengali.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/itrans_bengali.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_bengali.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_devanagari_hindi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_devanagari_hindi.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/itrans_devanagari_hindi.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_devanagari_hindi.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_devanagari_sanskrit_vedic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_devanagari_sanskrit_vedic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/itrans_devanagari_sanskrit_vedic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_devanagari_sanskrit_vedic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_gujarati.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_gujarati.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/itrans_gujarati.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_gujarati.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_gurmukhi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_gurmukhi.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/itrans_gurmukhi.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_gurmukhi.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_odia.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_odia.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/itrans_odia.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_odia.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_roman.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_roman.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/itrans_roman.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_roman.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/jawa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/jawa.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/jawa.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/jawa.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/jorai.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/jorai.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/jorai.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/jorai.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/karakalpak_cyrillic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/karakalpak_cyrillic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/karakalpak_cyrillic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/karakalpak_cyrillic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/karakalpak_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/karakalpak_latin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/karakalpak_latin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/karakalpak_latin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/kayan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/kayan.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/kayan.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/kayan.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/kbdsn1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/kbdsn1.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/kbdsn1.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/kbdsn1.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/kharoshthi_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/kharoshthi_inscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/kharoshthi_inscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/kharoshthi_inscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/khmer_advanced.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/khmer_advanced.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/khmer_advanced.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/khmer_advanced.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/khmer_angkor.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/khmer_angkor.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/khmer_angkor.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/khmer_angkor.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/khojki_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/khojki_inscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/khojki_inscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/khojki_inscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/kmhmu_2008.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/kmhmu_2008.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/kmhmu_2008.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/kmhmu_2008.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/koalibrere.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/koalibrere.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/koalibrere.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/koalibrere.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/korean_rr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/korean_rr.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/korean_rr.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/korean_rr.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/koreguaje.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/koreguaje.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/koreguaje.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/koreguaje.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/krung.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/krung.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/krung.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/krung.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/lahu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/lahu.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/lahu.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/lahu.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/lamkaang.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/lamkaang.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/lamkaang.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/lamkaang.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/landuma.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/landuma.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/landuma.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/landuma.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/lao_2008_basic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_2008_basic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/lao_2008_basic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_2008_basic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/lao_2008_rapid.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_2008_rapid.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/lao_2008_rapid.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_2008_rapid.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/lao_pali.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_pali.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/lao_pali.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_pali.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/lao_pali_us.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_pali_us.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/lao_pali_us.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_pali_us.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/lao_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_phonetic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/lao_phonetic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_phonetic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/lazuri.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/lazuri.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/lazuri.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/lazuri.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/libtralo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/libtralo.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/libtralo.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/libtralo.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/makasar_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/makasar_inscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/makasar_inscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/makasar_inscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/malar_braille.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/malar_braille.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/malar_braille.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/malar_braille.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/malar_malayalam.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/malar_malayalam.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/malar_malayalam.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/malar_malayalam.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/malar_malayalam_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/malar_malayalam_inscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/malar_malayalam_inscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/malar_malayalam_inscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/malar_tirhuta.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/malar_tirhuta.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/malar_tirhuta.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/malar_tirhuta.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/maltese.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/maltese.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/maltese.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/maltese.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/mandaic_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/mandaic_phonetic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/mandaic_phonetic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/mandaic_phonetic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/masaram_gondi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/masaram_gondi.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/masaram_gondi.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/masaram_gondi.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/me_en.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/me_en.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/me_en.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/me_en.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/meitei_legacy.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/meitei_legacy.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/meitei_legacy.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/meitei_legacy.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/miluk_hanis_siuslaw.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/miluk_hanis_siuslaw.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/miluk_hanis_siuslaw.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/miluk_hanis_siuslaw.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/modi_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/modi_inscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/modi_inscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/modi_inscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/mon_anonta.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/mon_anonta.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/mon_anonta.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/mon_anonta.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/mon_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/mon_phonetic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/mon_phonetic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/mon_phonetic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/mongolian_cyrillic_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/mongolian_cyrillic_qwerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/mongolian_cyrillic_qwerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/mongolian_cyrillic_qwerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/mozhi_malayalam.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/mozhi_malayalam.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/mozhi_malayalam.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/mozhi_malayalam.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/mro_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/mro_phonetic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/mro_phonetic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/mro_phonetic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/multani_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/multani_inscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/multani_inscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/multani_inscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/multi_pak_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/multi_pak_phonetic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/multi_pak_phonetic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/multi_pak_phonetic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/munji.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/munji.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/munji.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/munji.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/myancode_san.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/myancode_san.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/myancode_san.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/myancode_san.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nabataean_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nabataean_inscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nabataean_inscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nabataean_inscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/naijatype.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/naijatype.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/naijatype.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/naijatype.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nailangs.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nailangs.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nailangs.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nailangs.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nasa_yuwe.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nasa_yuwe.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nasa_yuwe.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nasa_yuwe.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nepali_traditional.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nepali_traditional.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nepali_traditional.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nepali_traditional.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/newa_romanized.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/newa_romanized.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/newa_romanized.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/newa_romanized.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/newa_traditional.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/newa_traditional.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/newa_traditional.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/newa_traditional.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nias.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nias.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nias.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nias.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nisenan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nisenan.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nisenan.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nisenan.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nko.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nko.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nko.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nko.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nkonya.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nkonya.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nkonya.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nkonya.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_bengali_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_bengali_winscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nlci_bengali_winscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_bengali_winscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_devanagari_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_devanagari_winscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nlci_devanagari_winscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_devanagari_winscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_gujarati_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_gujarati_winscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nlci_gujarati_winscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_gujarati_winscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_gurmukhi_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_gurmukhi_winscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nlci_gurmukhi_winscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_gurmukhi_winscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_ipa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_ipa.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nlci_ipa.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_ipa.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_kannada_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_kannada_winscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nlci_kannada_winscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_kannada_winscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_malayalam_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_malayalam_winscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nlci_malayalam_winscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_malayalam_winscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_oriya_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_oriya_winscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nlci_oriya_winscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_oriya_winscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_tamil_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_tamil_winscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nlci_tamil_winscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_tamil_winscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_telugu_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_telugu_winscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nlci_telugu_winscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_telugu_winscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nrc_makah.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nrc_makah.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nrc_makah.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nrc_makah.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/ntl_onekey.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/ntl_onekey.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/ntl_onekey.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/ntl_onekey.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/numanggang.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/numanggang.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/numanggang.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/numanggang.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/nw_iranian_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nw_iranian_latin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/nw_iranian_latin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nw_iranian_latin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/o_tissi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/o_tissi.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/o_tissi.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/o_tissi.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/obolo_chwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/obolo_chwerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/obolo_chwerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/obolo_chwerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/obolo_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/obolo_qwerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/obolo_qwerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/obolo_qwerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/old_hungarian.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/old_hungarian.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/old_hungarian.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/old_hungarian.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/old_turkic_udw21_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/old_turkic_udw21_qwerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/old_turkic_udw21_qwerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/old_turkic_udw21_qwerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/orma.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/orma.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/orma.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/orma.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/osage_nation.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/osage_nation.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/osage_nation.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/osage_nation.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/osage_nation_new.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/osage_nation_new.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/osage_nation_new.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/osage_nation_new.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/otoe_missouria.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/otoe_missouria.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/otoe_missouria.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/otoe_missouria.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/persian_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/persian_phonetic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/persian_phonetic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/persian_phonetic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/pid_piaroa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/pid_piaroa.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/pid_piaroa.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/pid_piaroa.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/pingelap.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/pingelap.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/pingelap.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/pingelap.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/postmodern_english_uk_dualstroke.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/postmodern_english_uk_dualstroke.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/postmodern_english_uk_dualstroke.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/postmodern_english_uk_dualstroke.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/postmodern_english_uk_natural.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/postmodern_english_uk_natural.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/postmodern_english_uk_natural.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/postmodern_english_uk_natural.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/postmodern_english_us_dualstroke.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/postmodern_english_us_dualstroke.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/postmodern_english_us_dualstroke.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/postmodern_english_us_dualstroke.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/postmodern_english_us_natural.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/postmodern_english_us_natural.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/postmodern_english_us_natural.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/postmodern_english_us_natural.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/pukapuka.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/pukapuka.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/pukapuka.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/pukapuka.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/qom.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/qom.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/qom.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/qom.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/quinault.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/quinault.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/quinault.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/quinault.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/qwerty_farang.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/qwerty_farang.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/qwerty_farang.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/qwerty_farang.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_aer.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_aer.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_aer.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_aer.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_arabic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_arabic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_arabic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_arabic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_balti.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_balti.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_balti.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_balti.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_brahui.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_brahui.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_brahui.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_brahui.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_brahui_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_brahui_latin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_brahui_latin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_brahui_latin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_burushaski.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_burushaski.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_burushaski.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_burushaski.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_dameli.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_dameli.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_dameli.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_dameli.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_dhatki.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_dhatki.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_dhatki.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_dhatki.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_dogri.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_dogri.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_dogri.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_dogri.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_gawar_bati.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_gawar_bati.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_gawar_bati.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_gawar_bati.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_gawri.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_gawri.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_gawri.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_gawri.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_hazaragi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_hazaragi.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_hazaragi.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_hazaragi.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_hindko.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_hindko.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_hindko.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_hindko.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_indus_kohistani.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_indus_kohistani.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_indus_kohistani.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_indus_kohistani.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_kalasha.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_kalasha.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_kalasha.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_kalasha.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_kashmir_shina.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_kashmir_shina.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_kashmir_shina.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_kashmir_shina.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_kashmiri.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_kashmiri.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_kashmiri.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_kashmiri.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_khowar.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_khowar.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_khowar.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_khowar.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_marwari.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_marwari.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_marwari.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_marwari.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_munji.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_munji.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_munji.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_munji.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_oadki.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_oadki.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_oadki.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_oadki.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_ormuri.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_ormuri.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_ormuri.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_ormuri.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_pahari.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_pahari.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_pahari.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_pahari.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_palula.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_palula.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_palula.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_palula.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_parkari_koli.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_parkari_koli.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_parkari_koli.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_parkari_koli.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_pashai.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_pashai.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_pashai.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_pashai.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_pashto.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_pashto.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_pashto.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_pashto.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_saraiki.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_saraiki.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_saraiki.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_saraiki.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_shina.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_shina.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_shina.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_shina.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_sindhi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_sindhi.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_sindhi.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_sindhi.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_torwali.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_torwali.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_torwali.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_torwali.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_urdu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_urdu.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_urdu.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_urdu.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_ushojo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_ushojo.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_ushojo.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_ushojo.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_uyghur.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_uyghur.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_uyghur.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_uyghur.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_wadiyara.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_wadiyara.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_wadiyara.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_wadiyara.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_wakhi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_wakhi.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_wakhi.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_wakhi.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_western_punjabi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_western_punjabi.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_western_punjabi.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_western_punjabi.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rac_yidgha.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_yidgha.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rac_yidgha.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_yidgha.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rawang.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rawang.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rawang.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rawang.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rejang.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rejang.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rejang.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rejang.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/remington_gail.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/remington_gail.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/remington_gail.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/remington_gail.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rohingya_arab.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rohingya_arab.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rohingya_arab.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rohingya_arab.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/rossel.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rossel.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/rossel.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rossel.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/runeboard.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/runeboard.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/runeboard.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/runeboard.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/russian_mnemonic_r.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/russian_mnemonic_r.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/russian_mnemonic_r.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/russian_mnemonic_r.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sabdalipi_assamese.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sabdalipi_assamese.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sabdalipi_assamese.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sabdalipi_assamese.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sahaptin_umatilla.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sahaptin_umatilla.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sahaptin_umatilla.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sahaptin_umatilla.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sahaptin_yakima.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sahaptin_yakima.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sahaptin_yakima.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sahaptin_yakima.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sanjha_punjabi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sanjha_punjabi.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sanjha_punjabi.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sanjha_punjabi.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/santali_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/santali_latin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/santali_latin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/santali_latin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/saraiki.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/saraiki.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/saraiki.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/saraiki.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/satere.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/satere.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/satere.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/satere.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/shahmukhi_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/shahmukhi_phonetic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/shahmukhi_phonetic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/shahmukhi_phonetic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/shan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/shan.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/shan.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/shan.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/shaw_2layer.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/shaw_2layer.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/shaw_2layer.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/shaw_2layer.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/siddham_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/siddham_inscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/siddham_inscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/siddham_inscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_akha_act.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_akha_act.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_akha_act.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_akha_act.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_arabic_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_arabic_phonetic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_arabic_phonetic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_arabic_phonetic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_areare.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_areare.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_areare.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_areare.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_bari.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bari.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_bari.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bari.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_bengali_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bengali_phonetic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_bengali_phonetic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bengali_phonetic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_bolivia.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bolivia.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_bolivia.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bolivia.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_boonkit.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_boonkit.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_boonkit.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_boonkit.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_brao.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_brao.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_brao.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_brao.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_bru.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bru.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_bru.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bru.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_buang.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_buang.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_buang.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_buang.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_bunong.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bunong.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_bunong.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bunong.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_busa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_busa.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_busa.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_busa.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_bwe_karen.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bwe_karen.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_bwe_karen.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bwe_karen.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_cameroon_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cameroon_azerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_cameroon_azerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cameroon_azerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_cameroon_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cameroon_qwerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_cameroon_qwerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cameroon_qwerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_cherokee_nation.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cherokee_nation.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_cherokee_nation.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cherokee_nation.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_cheyenne.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cheyenne.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_cheyenne.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cheyenne.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_cipher_music.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cipher_music.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_cipher_music.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cipher_music.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_devanagari_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_devanagari_phonetic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_devanagari_phonetic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_devanagari_phonetic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_devanagari_romanized.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_devanagari_romanized.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_devanagari_romanized.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_devanagari_romanized.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_devanagari_typewriter.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_devanagari_typewriter.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_devanagari_typewriter.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_devanagari_typewriter.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_dzongkha.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_dzongkha.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_dzongkha.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_dzongkha.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_eastern_congo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_eastern_congo.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_eastern_congo.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_eastern_congo.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_el_ethiopian_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_el_ethiopian_latin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_el_ethiopian_latin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_el_ethiopian_latin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_ethiopic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ethiopic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_ethiopic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ethiopic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_ethiopic_power_g.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ethiopic_power_g.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_ethiopic_power_g.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ethiopic_power_g.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_euro_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_euro_latin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_euro_latin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_euro_latin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_extended_urdu_np.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_extended_urdu_np.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_extended_urdu_np.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_extended_urdu_np.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_greek_polytonic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_greek_polytonic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_greek_polytonic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_greek_polytonic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_hawaiian.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hawaiian.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_hawaiian.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hawaiian.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_hebr_grek_trans.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hebr_grek_trans.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_hebr_grek_trans.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hebr_grek_trans.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_hebrew.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hebrew.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_hebrew.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hebrew.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_hebrew_legacy.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hebrew_legacy.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_hebrew_legacy.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hebrew_legacy.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_hmd_plrd.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hmd_plrd.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_hmd_plrd.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hmd_plrd.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_indic_roman.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_indic_roman.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_indic_roman.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_indic_roman.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_ipa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ipa.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_ipa.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ipa.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_jarai.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_jarai.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_jarai.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_jarai.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_kayah_kali.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kayah_kali.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_kayah_kali.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kayah_kali.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_kayah_latn.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kayah_latn.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_kayah_latn.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kayah_latn.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_kayah_mymr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kayah_mymr.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_kayah_mymr.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kayah_mymr.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_khamti.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_khamti.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_khamti.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_khamti.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_khmer.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_khmer.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_khmer.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_khmer.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_khowar.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_khowar.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_khowar.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_khowar.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_kmhmu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kmhmu.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_kmhmu.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kmhmu.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_korda_jamo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_korda_jamo.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_korda_jamo.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_korda_jamo.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_korda_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_korda_latin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_korda_latin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_korda_latin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_korean_morse.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_korean_morse.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_korean_morse.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_korean_morse.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_kvl_kayaw.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kvl_kayaw.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_kvl_kayaw.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kvl_kayaw.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_lepcha.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_lepcha.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_lepcha.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_lepcha.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_limbu_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_limbu_phonetic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_limbu_phonetic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_limbu_phonetic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_limbu_typewriter.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_limbu_typewriter.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_limbu_typewriter.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_limbu_typewriter.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_lisu_basic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_lisu_basic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_lisu_basic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_lisu_basic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_lisu_standard.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_lisu_standard.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_lisu_standard.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_lisu_standard.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_lpo_plrd.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_lpo_plrd.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_lpo_plrd.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_lpo_plrd.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_madi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_madi.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_madi.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_madi.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_makuri.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_makuri.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_makuri.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_makuri.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_mali_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_mali_azerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_mali_azerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_mali_azerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_mali_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_mali_qwerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_mali_qwerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_mali_qwerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_mali_qwertz.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_mali_qwertz.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_mali_qwertz.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_mali_qwertz.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_moore.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_moore.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_moore.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_moore.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_myanmar_my3.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_myanmar_my3.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_myanmar_my3.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_myanmar_my3.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_myanmar_mywinext.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_myanmar_mywinext.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_myanmar_mywinext.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_myanmar_mywinext.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_nigeria_dot.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nigeria_dot.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_nigeria_dot.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nigeria_dot.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_nigeria_odd_vowels.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nigeria_odd_vowels.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_nigeria_odd_vowels.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nigeria_odd_vowels.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_nigeria_underline.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nigeria_underline.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_nigeria_underline.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nigeria_underline.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_nko.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nko.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_nko.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nko.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_nubian.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nubian.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_nubian.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nubian.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_pan_africa_mnemonic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_pan_africa_mnemonic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_pan_africa_mnemonic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_pan_africa_mnemonic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_pan_africa_positional.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_pan_africa_positional.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_pan_africa_positional.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_pan_africa_positional.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_philippines.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_philippines.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_philippines.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_philippines.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_sahu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_sahu.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_sahu.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_sahu.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_bsc_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_bsc_azerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_bsc_azerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_bsc_azerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_cou_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_cou_azerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_cou_azerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_cou_azerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_dyo_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_dyo_azerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_dyo_azerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_dyo_azerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_gsl_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_gsl_azerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_gsl_azerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_gsl_azerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_krx_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_krx_azerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_krx_azerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_krx_azerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_ndv_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_ndv_azerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_ndv_azerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_ndv_azerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_sav_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_sav_azerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_sav_azerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_sav_azerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_snf_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_snf_azerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_snf_azerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_snf_azerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_srr_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_srr_azerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_srr_azerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_srr_azerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_wo_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_wo_azerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_wo_azerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_wo_azerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_sgaw_karen.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_sgaw_karen.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_sgaw_karen.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_sgaw_karen.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_shan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_shan.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_shan.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_shan.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tai_dam.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tai_dam.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_tai_dam.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tai_dam.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tai_dam_lao.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tai_dam_lao.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_tai_dam_lao.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tai_dam_lao.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tai_dam_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tai_dam_latin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_tai_dam_latin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tai_dam_latin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tai_dam_typewriter.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tai_dam_typewriter.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_tai_dam_typewriter.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tai_dam_typewriter.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tawallammat.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tawallammat.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_tawallammat.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tawallammat.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tchad.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tchad.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_tchad.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tchad.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tepehuan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tepehuan.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_tepehuan.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tepehuan.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_torwali.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_torwali.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_torwali.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_torwali.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tunisian.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tunisian.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_tunisian.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tunisian.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_uganda_tanzania.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_uganda_tanzania.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_uganda_tanzania.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_uganda_tanzania.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_vai.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_vai.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_vai.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_vai.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_wayuu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_wayuu.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_wayuu.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_wayuu.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_ygp_plrd.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ygp_plrd.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_ygp_plrd.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ygp_plrd.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yi.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_yi.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yi.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yna_plrd.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yna_plrd.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_yna_plrd.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yna_plrd.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yoruba8.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yoruba8.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_yoruba8.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yoruba8.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yoruba_bar.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yoruba_bar.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_yoruba_bar.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yoruba_bar.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yoruba_dot.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yoruba_dot.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_yoruba_dot.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yoruba_dot.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yupik_cyrillic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yupik_cyrillic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_yupik_cyrillic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yupik_cyrillic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yupik_cyrillic_ru.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yupik_cyrillic_ru.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_yupik_cyrillic_ru.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yupik_cyrillic_ru.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sil_ywq_plrd.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ywq_plrd.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sil_ywq_plrd.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ywq_plrd.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/slc_saliba.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/slc_saliba.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/slc_saliba.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/slc_saliba.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/soqotri_arabic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/soqotri_arabic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/soqotri_arabic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/soqotri_arabic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/srr_ajami_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/srr_ajami_qwerty.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/srr_ajami_qwerty.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/srr_ajami_qwerty.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sundanese.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sundanese.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sundanese.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sundanese.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sundanese_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sundanese_latin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sundanese_latin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sundanese_latin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/swanalekha_malayalam.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/swanalekha_malayalam.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/swanalekha_malayalam.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/swanalekha_malayalam.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sxava.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sxava.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sxava.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sxava.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sxava_eo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sxava_eo.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sxava_eo.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sxava_eo.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/sylheti_nagri.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sylheti_nagri.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/sylheti_nagri.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sylheti_nagri.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/syriac_arabic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/syriac_arabic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/syriac_arabic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/syriac_arabic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/syriac_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/syriac_phonetic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/syriac_phonetic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/syriac_phonetic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/tagbanwa_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tagbanwa_inscript.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/tagbanwa_inscript.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tagbanwa_inscript.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/taigi_poj.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/taigi_poj.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/taigi_poj.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/taigi_poj.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/tainua.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tainua.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/tainua.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tainua.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/tangsa_lakhum.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tangsa_lakhum.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/tangsa_lakhum.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tangsa_lakhum.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/tawallammat_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tawallammat_latin.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/tawallammat_latin.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tawallammat_latin.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/teggargrent_lat.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/teggargrent_lat.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/teggargrent_lat.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/teggargrent_lat.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/tem_kdh.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tem_kdh.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/tem_kdh.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tem_kdh.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/thamizha_anjal_paangu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/thamizha_anjal_paangu.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/thamizha_anjal_paangu.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/thamizha_anjal_paangu.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/thamizha_bamini.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/thamizha_bamini.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/thamizha_bamini.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/thamizha_bamini.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/thamizha_new_typewriter.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/thamizha_new_typewriter.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/thamizha_new_typewriter.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/thamizha_new_typewriter.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/thamizha_tamil99_ext.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/thamizha_tamil99_ext.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/thamizha_tamil99_ext.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/thamizha_tamil99_ext.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/tibetan_direct_input.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tibetan_direct_input.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/tibetan_direct_input.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tibetan_direct_input.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/tibetan_ewts.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tibetan_ewts.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/tibetan_ewts.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tibetan_ewts.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/tirhuta.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tirhuta.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/tirhuta.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tirhuta.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/tlahuica.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tlahuica.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/tlahuica.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tlahuica.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/tsakonian.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tsakonian.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/tsakonian.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tsakonian.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/tuareg_tifinagh.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tuareg_tifinagh.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/tuareg_tifinagh.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tuareg_tifinagh.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/turkmen_cyrl.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/turkmen_cyrl.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/turkmen_cyrl.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/turkmen_cyrl.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/txo_toto.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/txo_toto.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/txo_toto.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/txo_toto.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/udi_keyboard.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/udi_keyboard.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/udi_keyboard.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/udi_keyboard.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/ukwuani.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/ukwuani.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/ukwuani.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/ukwuani.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/uma_graphic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/uma_graphic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/uma_graphic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/uma_graphic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/uma_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/uma_phonetic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/uma_phonetic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/uma_phonetic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/urdu_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/urdu_phonetic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/urdu_phonetic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/urdu_phonetic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/urdu_phonetic_crulp.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/urdu_phonetic_crulp.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/urdu_phonetic_crulp.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/urdu_phonetic_crulp.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/venetia_et_histria.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/venetia_et_histria.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/venetia_et_histria.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/venetia_et_histria.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/vm_tamil_modular.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/vm_tamil_modular.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/vm_tamil_modular.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/vm_tamil_modular.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/vm_tamil_typewriter.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/vm_tamil_typewriter.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/vm_tamil_typewriter.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/vm_tamil_typewriter.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/wakhi_anglicized.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/wakhi_anglicized.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/wakhi_anglicized.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/wakhi_anglicized.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/wakhi_cyrillic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/wakhi_cyrillic.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/wakhi_cyrillic.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/wakhi_cyrillic.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/wakhi_standard.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/wakhi_standard.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/wakhi_standard.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/wakhi_standard.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/wancho.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/wancho.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/wancho.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/wancho.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/warang_citi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/warang_citi.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/warang_citi.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/warang_citi.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/wolofal.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/wolofal.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/wolofal.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/wolofal.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/xinaliq.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/xinaliq.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/xinaliq.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/xinaliq.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/yiddish_pasekh.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/yiddish_pasekh.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/yiddish_pasekh.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/yiddish_pasekh.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/yidgha.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/yidgha.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/yidgha.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/yidgha.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/younger_futhark_short_twig.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/younger_futhark_short_twig.kmx
similarity index 100%
rename from developer/src/kmcmplib/tests/fixtures/younger_futhark_short_twig.kmx
rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/younger_futhark_short_twig.kmx
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/compile_legacy.bat b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/compile_legacy.bat
new file mode 100644
index 0000000000000000000000000000000000000000..5e8b891ea6cf163c1e4dce6c089a835c6b153e78
--- /dev/null
+++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/compile_legacy.bat
@@ -0,0 +1,4 @@
+@echo off
+echo Compiles the keyboards using the legacy kmcomp.exe
+echo to use as baseline comparisons for kmcmplib
+for %%d in (*.kmn) do ..\..\..\..\..\bin\kmcomp -no-compiler-version -d %%d
\ No newline at end of file
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k001_utf16.kmn b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k001_utf16.kmn
new file mode 100644
index 0000000000000000000000000000000000000000..2b3180c36ab4b473e74b1b3487fc8f0a61d623f6
Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k001_utf16.kmn differ
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k001_utf16.kmx b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k001_utf16.kmx
new file mode 100644
index 0000000000000000000000000000000000000000..13ea2a686e09c16db68ff7bb355e114b9125d76d
Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k001_utf16.kmx differ
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k002_utf8_without_bom.kmn b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k002_utf8_without_bom.kmn
new file mode 100644
index 0000000000000000000000000000000000000000..da6d4e70873e5762dd49a41d13721abebb5ba668
--- /dev/null
+++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k002_utf8_without_bom.kmn
@@ -0,0 +1,11 @@
+c Description: Verifies that kmcmplib can compile a UTF-8 file (without BOM)
+
+store(&NAME) 'k002_utf8_without_bom'
+store(&VERSION) '9.0'
+
+begin unicode > use(main)
+
+group(main) using keys
+
++ [K_A] > 'a'
+'a' + [K_B] > 'ážáŸ’មែរ'
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k002_utf8_without_bom.kmx b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k002_utf8_without_bom.kmx
new file mode 100644
index 0000000000000000000000000000000000000000..0b9c9294182d9387165c98e1ffc84c8fe45452e3
Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k002_utf8_without_bom.kmx differ
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k003_utf8_with_bom.kmn b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k003_utf8_with_bom.kmn
new file mode 100644
index 0000000000000000000000000000000000000000..b2bccb036caad4d891ef62c5bc31c02ebaaffddd
--- /dev/null
+++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k003_utf8_with_bom.kmn
@@ -0,0 +1,11 @@
+c Description: Verifies that kmcmplib can compile a UTF-8 file (with BOM)
+
+store(&NAME) 'k003_utf8_with_bom'
+store(&VERSION) '9.0'
+
+begin unicode > use(main)
+
+group(main) using keys
+
++ [K_A] > 'a'
+'a' + [K_B] > 'ážáŸ’មែរ'
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k003_utf8_with_bom.kmx b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k003_utf8_with_bom.kmx
new file mode 100644
index 0000000000000000000000000000000000000000..ee1cf4225502e3c7944b58f301e5be649af4ec00
Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k003_utf8_with_bom.kmx differ
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k004_ansi.kmn b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k004_ansi.kmn
new file mode 100644
index 0000000000000000000000000000000000000000..50a1b31ea1c7b4d369e66dc69021be22f9cdeacb
--- /dev/null
+++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k004_ansi.kmn
@@ -0,0 +1,13 @@
+c Description: Verifies that kmcmplib can compile an ANSI file
+c This has some high-ascii letters in cp1252 to ensure that
+c it fails to load as 'utf8 without bom'
+
+store(&NAME) 'k004_ansi'
+store(&VERSION) '9.0'
+
+begin unicode > use(main)
+
+group(main) using keys
+
++ [K_A] > 'a'
+'a' + [K_B] > 'ÀÐ'
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k004_ansi.kmx b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k004_ansi.kmx
new file mode 100644
index 0000000000000000000000000000000000000000..6c41da979cb2a3ec20aadf6de0ca778fb8ce5ec1
Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k004_ansi.kmx differ
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k005_bitmap.bmp b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k005_bitmap.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..509b89ae6df7f09a442ae60f215a6c7b3211b34f
Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k005_bitmap.bmp differ
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k005_bitmap.kmn b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k005_bitmap.kmn
new file mode 100644
index 0000000000000000000000000000000000000000..926ccdeeafbf5bf648903d0bdc530a5e0ffa9ce9
--- /dev/null
+++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k005_bitmap.kmn
@@ -0,0 +1,12 @@
+c Description: Verifies that kmcmplib can load a bitmap without file extension
+
+store(&NAME) 'k005_bitmap'
+store(&VERSION) '9.0'
+store(&BITMAP) 'k005_bitmap'
+
+begin unicode > use(main)
+
+group(main) using keys
+
++ [K_A] > 'a'
+'a' + [K_B] > 'ážáŸ’មែរ'
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k005_bitmap.kmx b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k005_bitmap.kmx
new file mode 100644
index 0000000000000000000000000000000000000000..bc9bc9b671724050e169e59fce976a18d54a030e
Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k005_bitmap.kmx differ
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k006_icon.ico b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k006_icon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..e1b64cf9013e6702420d97fd88b120490f33b137
Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k006_icon.ico differ
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k006_icon.kmn b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k006_icon.kmn
new file mode 100644
index 0000000000000000000000000000000000000000..4ad3e61659683213ddc6b44a0454c87cc7576d11
--- /dev/null
+++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k006_icon.kmn
@@ -0,0 +1,12 @@
+c Description: Verifies that kmcmplib can load an icon with file extension
+
+store(&NAME) 'k006_icon'
+store(&VERSION) '9.0'
+store(&BITMAP) 'k006_icon.ico'
+
+begin unicode > use(main)
+
+group(main) using keys
+
++ [K_A] > 'a'
+'a' + [K_B] > 'ážáŸ’មែរ'
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k006_icon.kmx b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k006_icon.kmx
new file mode 100644
index 0000000000000000000000000000000000000000..f994a1dcac9e5ae797eb88eed9cee4b0e6f85b00
Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k006_icon.kmx differ
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k007_includecodes_r_n.kmn b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k007_includecodes_r_n.kmn
new file mode 100644
index 0000000000000000000000000000000000000000..71d72d5120b522cd20303f3d0843e4720791230c
--- /dev/null
+++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k007_includecodes_r_n.kmn
@@ -0,0 +1,12 @@
+c Description: Verifies that kmcmplib can load an includecodes file with \r\n line endings
+
+store(&NAME) 'k007_includecodes_r_n'
+store(&VERSION) '9.0'
+store(&includecodes) 'k007_includecodes_r_n.txt'
+
+begin unicode > use(main)
+
+group(main) using keys
+
++ [K_A] > $LOWER_A
+$LOWER_A + [K_B] > $LOWER_B
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k007_includecodes_r_n.kmx b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k007_includecodes_r_n.kmx
new file mode 100644
index 0000000000000000000000000000000000000000..f8b4eac1b0d9103c7767e487b5109a06aed8e1ec
Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k007_includecodes_r_n.kmx differ
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k007_includecodes_r_n.txt b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k007_includecodes_r_n.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4cc3fe4351ddeb205cdd985d04648ec403065e35
--- /dev/null
+++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k007_includecodes_r_n.txt
@@ -0,0 +1,2 @@
+0061;LOWER_A
+0062;LOWER_B
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k008_includecodes_n.kmn b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k008_includecodes_n.kmn
new file mode 100644
index 0000000000000000000000000000000000000000..ea357b4b0ef6fc8331efb7f63aca9e3fc9291652
--- /dev/null
+++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k008_includecodes_n.kmn
@@ -0,0 +1,12 @@
+c Description: Verifies that kmcmplib can load an includecodes file with \n line endings
+
+store(&NAME) 'k008_includecodes_n'
+store(&VERSION) '9.0'
+store(&includecodes) 'k008_includecodes_n.txt'
+
+begin unicode > use(main)
+
+group(main) using keys
+
++ [K_A] > $LOWER_A
+$LOWER_A + [K_B] > $LOWER_B
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k008_includecodes_n.kmx b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k008_includecodes_n.kmx
new file mode 100644
index 0000000000000000000000000000000000000000..aa158cdd546cdde915e4a55c1db521e57d4abb04
Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k008_includecodes_n.kmx differ
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k008_includecodes_n.txt b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k008_includecodes_n.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4cc3fe4351ddeb205cdd985d04648ec403065e35
--- /dev/null
+++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k008_includecodes_n.txt
@@ -0,0 +1,2 @@
+0061;LOWER_A
+0062;LOWER_B
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k009_long_lines.kmn b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k009_long_lines.kmn
new file mode 100644
index 0000000000000000000000000000000000000000..a0ad9d0d9fbc820432982d13455f5a02acda4018
--- /dev/null
+++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k009_long_lines.kmn
@@ -0,0 +1,31 @@
+c Description: Verifies that kmcmplib can deal with long lines continued with backslashes, and also comments
+
+store(&NAME) 'k009_long_lines'
+store(&VERSION) '9.0'
+
+begin unicode > use(main)
+
+group(main) using keys
+
+c
+c ***Note*** This file has intentional whitespace at the end of line ending in [SHIFT K_N]
+c in order to verify that line continuations are working correctly
+c
+
+store(c_key) [K_K] [K_X] [SHIFT K_K] [SHIFT K_X] [K_G] \
+ [K_C] [K_Q] [SHIFT K_C] [SHIFT K_Q] [SHIFT K_J] \
+ [K_D] [K_Z] [SHIFT K_D] [SHIFT K_Z] [SHIFT K_N] \
+ [K_T] [K_F] [SHIFT K_T] [SHIFT K_F] [K_N] \
+ [K_B] [K_P] [SHIFT K_B] [SHIFT K_P] [K_M] \
+ [K_Y] [K_R] [K_L] [K_V] [K_S] [K_H] [SHIFT K_L] [SHIFT K_G] \
+ [RALT K_K] [RALT K_B]
+store(c_out) U+1780 U+1781 U+1782 U+1783 U+1784 \
+ U+1785 U+1786 U+1787 U+1788 U+1789 \
+ U+178A U+178B U+178C U+178D U+178E \
+ U+178F U+1790 U+1791 U+1792 U+1793 \
+ U+1794 U+1795 U+1796 U+1797 U+1798 \
+ U+1799 U+179A U+179B U+179C U+179F U+17A0 U+17A1 U+17A2 \
+ U+179D U+179E c deprecated, but they are used in minority languages
+
++ any(c_key) > index(c_out, 1)
+
diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k009_long_lines.kmx b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k009_long_lines.kmx
new file mode 100644
index 0000000000000000000000000000000000000000..4acd4cc051adc10d35c6ae842b24be5cf43f0cf4
Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k009_long_lines.kmx differ
diff --git a/developer/src/kmcmplib/tests/kmcompxtest.cpp b/developer/src/kmcmplib/tests/kmcompxtest.cpp
index 1ce542917d389df52ad136047d9b7ae09d15d9dd..5dbaee88bc9990af9513c8ffd84882250c25d92d 100644
--- a/developer/src/kmcmplib/tests/kmcompxtest.cpp
+++ b/developer/src/kmcmplib/tests/kmcompxtest.cpp
@@ -13,6 +13,8 @@
#include
#include
#include
+#include "util_filesystem.h"
+#include "util_callbacks.h"
#ifdef _MSC_VER
#else
@@ -21,29 +23,11 @@
using namespace std;
-vector < int > error_vec;
-
#define CERR_FATAL 0x00008000
#define CERR_ERROR 0x00004000
#define CERR_WARNING 0x00002000
#define CERR_HINT 0x00001000
-int msgproc(int line, uint32_t dwMsgCode, char* szText, void* context)
-{
- error_vec.push_back(dwMsgCode);
- const char*t = "unknown";
- switch(dwMsgCode & 0xF000) {
- case CERR_HINT: t=" hint"; break;
- case CERR_WARNING: t="warning"; break;
- case CERR_ERROR: t=" error"; break;
- case CERR_FATAL: t=" fatal"; break;
- }
- printf("line %d %s %04.4x: %s\n", line, t, (unsigned int)dwMsgCode, szText);
- return 1;
-}
-
-#include "../src/filesystem.h"
-
int main(int argc, char *argv[])
{
if(argc < 4) {
@@ -71,14 +55,15 @@ int main(int argc, char *argv[])
char first5[6] = "CERR_";
char* pfirst5 = first5;
- KMCMP_COMPILER_OPTIONS kcopts;
- kcopts.dwSize = sizeof(KMCMP_COMPILER_OPTIONS);
- kcopts.ShouldAddCompilerVersion = false; // So we can compare against existing compiled keyboards that also don't have compiler version
- if(!kmcmp_SetCompilerOptions(&kcopts)) {
- return __LINE__;
- }
+ KMCMP_COMPILER_RESULT result;
+ KMCMP_COMPILER_OPTIONS options;
+ options.saveDebug = true;
+ options.compilerWarningsAsErrors = false;
+ options.warnDeprecatedCode = true;
+ options.shouldAddCompilerVersion = false;
+ options.target = CKF_KEYMAN;
- if(kmcmp_CompileKeyboardFile(kmn_file, kmx_file, true, false, true, msgproc, nullptr)) {
+ if(kmcmp_CompileKeyboard(kmn_file, options, msgproc, loadfileProc, nullptr, result)) {
char* testname = strrchr( (char*) kmn_file, '/') + 1;
if(strncmp(testname, pfirst5, 5) == 0){
return __LINE__; // exit code: CERR_ in Name + no Error found
@@ -87,26 +72,24 @@ int main(int argc, char *argv[])
// On non-win32 platforms, we cannot get kmcmpdll.dll to build keyboards
// legacy-mode, so we'll compare to a hopefully existing file that we've
// been passed
- FILE* fp1 = Open_File(kmx_file, "rb");
+ FILE* fp1 = Open_File(kmx_file, "wb");
if(!fp1) return __LINE__;
+ // Write out for reference
+ fwrite(result.kmx, 1, result.kmxSize, fp1);
+
FILE* fp2 = Open_File(reference_kmx, "rb");
if(!fp2) return __LINE__; // exit code: fail if no reference kmx file in build-folder
- fseek(fp1, 0, SEEK_END);
- auto sz1 = ftell(fp1);
- fseek(fp1, 0, SEEK_SET);
fseek(fp2, 0, SEEK_END);
auto sz2 = ftell(fp2);
fseek(fp2, 0, SEEK_SET);
- if (sz1 != sz2) return __LINE__; // exit code: size of kmx-file in build differs from size of kmx-file in source folder
+ if (result.kmxSize != sz2) return __LINE__; // exit code: size of kmx-file in build differs from size of kmx-file in source folder
- char* buf1 = new char[sz1];
- char* buf2 = new char[sz1];
- fread(buf1, 1, sz1, fp1);
- fread(buf2, 1, sz1, fp2);
- return memcmp(buf1, buf2, sz1) ? __LINE__ : 0; // exit code: when contents of kmx-file in build differs from contents of kmx-file in source folder
+ char* buf2 = new char[result.kmxSize];
+ fread(buf2, 1, result.kmxSize, fp2);
+ return memcmp(result.kmx, buf2, result.kmxSize) ? __LINE__ : 0; // exit code: when contents of kmx-file in build differs from contents of kmx-file in source folder
// success: when contents of kmx-file in build and source folder are the same
}
else { /*if Errors found: check number (e.g. CERR_4061_balochi_phonetic.kmn should produce Error 4061)*/
diff --git a/developer/src/kmcmplib/tests/meson.build b/developer/src/kmcmplib/tests/meson.build
index b81429806e16a3d414c2626c34fba9caa4d2bc12..a048bc8236b61803f5f16ef5f91bad7b7e56eae0 100644
--- a/developer/src/kmcmplib/tests/meson.build
+++ b/developer/src/kmcmplib/tests/meson.build
@@ -6,16 +6,20 @@
fs = import('fs')
-tests_flags = []
+tests_links = []
+
+if cpp_compiler.get_id() == 'emscripten'
+ tests_links += ['-lnodefs.js']
+endif
input_path = meson.current_source_dir() / '../../../../common/test/keyboards/baseline'
output_path = meson.current_build_dir()
-kmcompxtest = executable('kmcompxtest', 'kmcompxtest.cpp',
- cpp_args: defns,
+kmcompxtest = executable('kmcompxtest', ['kmcompxtest.cpp','util_filesystem.cpp','util_callbacks.cpp'],
+ cpp_args: defns + flags,
include_directories: inc,
name_suffix: name_suffix,
- link_args: links + tests_flags,
+ link_args: links + tests_links,
objects: lib.extract_all_objects(),
dependencies: icuuc_dep,
)
@@ -76,6 +80,26 @@ foreach kbd : tests
test(kbd, kmcompxtest, args: [kbd_src, kbd_obj, join_paths(input_path, kbd) + '.kmx'])
endforeach
+valid_keyboard_tests = [
+ 'k001_utf16',
+ 'k002_utf8_without_bom',
+ 'k003_utf8_with_bom',
+ # 'k004_ansi', # TODO: enable ansi test when we have the icu datafiles #8884
+ 'k005_bitmap',
+ 'k006_icon',
+ 'k007_includecodes_r_n',
+ 'k008_includecodes_n',
+ 'k009_long_lines',
+]
+
+fixtures_path = meson.current_source_dir() / 'fixtures/valid-keyboards'
+
+foreach kbd : valid_keyboard_tests
+ kbd_src = join_paths(fixtures_path, kbd) + '.kmn'
+ kbd_obj = join_paths(output_path, kbd) + '.kmx'
+ test(kbd, kmcompxtest, args: [kbd_src, kbd_obj, join_paths(fixtures_path, kbd) + '.kmx'])
+endforeach
+
# Test fixtures that come from keyboards repo -- but only for a "full" test,
# which typically we run on CI no more than once a day, because it's expensive.
@@ -96,7 +120,7 @@ if get_option('full_test')
# kbd is going to be an absolute path
kbd_obj = output_path / test_basename + '.kmx'
- reference_kmx = meson.current_source_dir() / 'fixtures' / test_basename + '.kmx'
+ reference_kmx = meson.current_source_dir() / 'fixtures/keyboards-repo' / test_basename + '.kmx'
if fs.is_file(reference_kmx)
test(test_basename, kmcompxtest, args: [kbd, kbd_obj, reference_kmx])
@@ -107,22 +131,22 @@ endif
# Test the API endpoints
-apitest = executable('api-test', 'api-test.cpp',
- cpp_args: defns,
+apitest = executable('api-test', ['api-test.cpp','util_filesystem.cpp','util_callbacks.cpp'],
+ cpp_args: defns + flags,
include_directories: inc,
name_suffix: name_suffix,
- link_args: links + tests_flags,
+ link_args: links + tests_links,
objects: lib.extract_all_objects(),
- dependencies: icuuc_dep,
+ dependencies: icuuc_dep
)
-test('api-test', apitest)
+test('api-test', apitest, args: [output_path / 'blank_keyboard.kmx'])
usetapitest = executable('uset-api-test', 'uset-api-test.cpp',
- cpp_args: defns,
+ cpp_args: defns + flags,
include_directories: inc,
name_suffix: name_suffix,
- link_args: links + tests_flags,
+ link_args: links + tests_links,
objects: lib.extract_all_objects(),
dependencies: icuuc_dep,
)
diff --git a/developer/src/kmcmplib/tests/prep.sh b/developer/src/kmcmplib/tests/prep.sh
index 058d4a1b9b0bfdd50fe13d29bcbdff7af508bd3c..8ad9b81171f165903fe74d4b7eb47f6f39154800 100755
--- a/developer/src/kmcmplib/tests/prep.sh
+++ b/developer/src/kmcmplib/tests/prep.sh
@@ -39,30 +39,30 @@ if [[ ! -d "$KEYBOARDS_ROOT/.git" ]] || [[ ! -f "$KEYBOARDS_ROOT/tools/regressio
fi
if builder_start_action clean; then
- rm -rf "$THIS_SCRIPT_PATH/fixtures"
+ rm -rf "$THIS_SCRIPT_PATH/fixtures/keyboards-repo"
rm -f "$THIS_SCRIPT_PATH/keyboards_commit_ref.txt"
builder_finish_action success clean
fi
if builder_start_action build; then
# yes, remove existing fixtures before build so we don't end up with stale fixtures
- rm -rf "$THIS_SCRIPT_PATH/fixtures"
+ rm -rf "$THIS_SCRIPT_PATH/fixtures/keyboards-repo"
# record the revision of the keyboards repo
pushd "$KEYBOARDS_ROOT" > /dev/null
git rev-parse head > "$THIS_SCRIPT_PATH/keyboards_commit_ref.txt"
popd > /dev/null
- "$KEYBOARDS_ROOT/tools/regression.sh" --use-legacy-compiler --local "$KEYMAN_ROOT/developer/bin/" --output "$THIS_SCRIPT_PATH/fixtures/"
+ "$KEYBOARDS_ROOT/tools/regression.sh" --use-legacy-compiler --local "$KEYMAN_ROOT/developer/bin/" --output "$THIS_SCRIPT_PATH/fixtures/keyboards-repo/"
cd "$THIS_SCRIPT_PATH"
# We don't need visual keyboards
- rm ./fixtures/*.kvk
+ rm ./fixtures/keyboards-repo/*.kvk
# Nor do we need kmw outputs
- rm ./fixtures/*.js
+ rm ./fixtures/keyboards-repo/*.js
# Finally, we remove any keyboards over 500kb, as we won't automatically test them for now
# TODO: consider if we want to keep those large keyboards in the future
- find ./fixtures/ -size +500k -exec rm {} \+
+ find ./fixtures/keyboards-repo/ -size +500k -exec rm {} \+
builder_finish_action success build
fi
\ No newline at end of file
diff --git a/developer/src/kmcmplib/tests/uset-api-test.cpp b/developer/src/kmcmplib/tests/uset-api-test.cpp
index 4fe599ca2e83a1af9d50d61368bafa4b337f3c82..61d263ad157cc365d885c463bc6bc200a893add7 100644
--- a/developer/src/kmcmplib/tests/uset-api-test.cpp
+++ b/developer/src/kmcmplib/tests/uset-api-test.cpp
@@ -19,30 +19,32 @@
#include "../src/compfile.h"
#include
-void test_kmcmp_ParseUnicodeSetProc();
+void test_kmcmp_parseUnicodeSet();
// std::vector error_vec;
int main(int argc, char *argv[]) {
- test_kmcmp_ParseUnicodeSetProc();
+ test_kmcmp_parseUnicodeSet();
return 0;
}
-void test_kmcmp_ParseUnicodeSetProc() {
+void test_kmcmp_parseUnicodeSet() {
{
// null test
const auto bufsiz = 128;
uint32_t buf[bufsiz];
- int rc = kmcmp_ParseUnicodeSet(u8"[]", buf, bufsiz);
+ uintptr_t buf_ = reinterpret_cast(buf);
+ int rc = kmcmp_parseUnicodeSet(u8"[]", buf_, bufsiz);
assert(rc == KMCMP_USET_OK);
}
{
// basic test
const auto bufsiz = 128;
uint32_t buf[bufsiz];
- int rc = kmcmp_ParseUnicodeSet(u8"[x A-C]", buf, bufsiz);
+ uintptr_t buf_ = reinterpret_cast(buf);
+ int rc = kmcmp_parseUnicodeSet(u8"[x A-C]", buf_, bufsiz);
assert(rc == 2);
assert(buf[0] == 0x41);
assert(buf[1] == 0x43);
@@ -53,7 +55,8 @@ void test_kmcmp_ParseUnicodeSetProc() {
// bigger test
const auto bufsiz = 128;
uint32_t buf[bufsiz];
- int rc = kmcmp_ParseUnicodeSet(u8"[[🙀A-C]-[CB]]", buf, bufsiz);
+ uintptr_t buf_ = reinterpret_cast(buf);
+ int rc = kmcmp_parseUnicodeSet(u8"[[🙀A-C]-[CB]]", buf_, bufsiz);
assert(rc == 2);
assert(buf[0] == 0x41);
assert(buf[1] == 0x41);
@@ -64,35 +67,40 @@ void test_kmcmp_ParseUnicodeSetProc() {
// overflow test
const auto bufsiz = 1;
uint32_t buf[bufsiz];
- int rc = kmcmp_ParseUnicodeSet(u8"[x A-C]", buf, bufsiz);
+ uintptr_t buf_ = reinterpret_cast(buf);
+ int rc = kmcmp_parseUnicodeSet(u8"[x A-C]", buf_, bufsiz);
assert(rc == KMCMP_FATAL_OUT_OF_RANGE);
}
{
// err test
const auto bufsiz = 128;
uint32_t buf[bufsiz];
- int rc = kmcmp_ParseUnicodeSet(u8"[:Adlm:]", buf, bufsiz);
+ uintptr_t buf_ = reinterpret_cast(buf);
+ int rc = kmcmp_parseUnicodeSet(u8"[:Adlm:]", buf_, bufsiz);
assert(rc == KMCMP_ERROR_UNSUPPORTED_PROPERTY);
}
{
// err test
const auto bufsiz = 128;
uint32_t buf[bufsiz];
- int rc = kmcmp_ParseUnicodeSet(u8"[[\\p{Mn}]&[A-Z]]", buf, bufsiz);
+ uintptr_t buf_ = reinterpret_cast(buf);
+ int rc = kmcmp_parseUnicodeSet(u8"[[\\p{Mn}]&[A-Z]]", buf_, bufsiz);
assert(rc == KMCMP_ERROR_UNSUPPORTED_PROPERTY);
}
{
// err test
const auto bufsiz = 128;
uint32_t buf[bufsiz];
- int rc = kmcmp_ParseUnicodeSet(u8"[abc{def}]", buf, bufsiz);
+ uintptr_t buf_ = reinterpret_cast(buf);
+ int rc = kmcmp_parseUnicodeSet(u8"[abc{def}]", buf_, bufsiz);
assert(rc == KMCMP_ERROR_HAS_STRINGS);
}
{
// err test
const auto bufsiz = 128;
uint32_t buf[bufsiz];
- int rc = kmcmp_ParseUnicodeSet(u8"[[]", buf, bufsiz);
+ uintptr_t buf_ = reinterpret_cast(buf);
+ int rc = kmcmp_parseUnicodeSet(u8"[[]", buf_, bufsiz);
assert(rc == KMCMP_ERROR_SYNTAX_ERR);
}
}
diff --git a/developer/src/kmcmplib/tests/util_callbacks.cpp b/developer/src/kmcmplib/tests/util_callbacks.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e610a54c09e8beb6974675d23deb0cc5199140d6
--- /dev/null
+++ b/developer/src/kmcmplib/tests/util_callbacks.cpp
@@ -0,0 +1,59 @@
+#include
+#include
+#include
+#include "util_filesystem.h"
+#include "../src/compfile.h"
+#include
+
+std::vector error_vec;
+
+int msgproc(int line, uint32_t dwMsgCode, const char* szText, void* context) {
+ error_vec.push_back(dwMsgCode);
+ const char*t = "unknown";
+ switch(dwMsgCode & 0xF000) {
+ case CERR_HINT: t=" hint"; break;
+ case CERR_WARNING: t="warning"; break;
+ case CERR_ERROR: t=" error"; break;
+ case CERR_FATAL: t=" fatal"; break;
+ }
+ printf("line %d %s %04.4x: %s\n", line, t, (unsigned int)dwMsgCode, szText);
+ return 1;
+}
+
+bool loadfileProc(const char* filename, const char* baseFilename, void* data, int* size, void* context) {
+ std::string resolvedFilename = filename;
+ if(baseFilename && *baseFilename && IsRelativePath(filename)) {
+ char* p;
+ if ((p = strrchr_slash((char*)baseFilename)) != nullptr) {
+ std::string basePath = std::string(baseFilename, (int)(p - baseFilename + 1));
+ resolvedFilename = basePath;
+ resolvedFilename.append(filename);
+ }
+ }
+
+ FILE* fp = Open_File(resolvedFilename.c_str(), "rb");
+ if(!fp) {
+ return false;
+ }
+
+ if(!data) {
+ // return size
+ if(fseek(fp, 0, SEEK_END) != 0) {
+ fclose(fp);
+ return false;
+ }
+ *size = ftell(fp);
+ if(*size == -1L) {
+ fclose(fp);
+ return false;
+ }
+ } else {
+ // return data
+ if(fread(data, 1, *size, fp) != *size) {
+ fclose(fp);
+ return false;
+ }
+ }
+ fclose(fp);
+ return true;
+}
\ No newline at end of file
diff --git a/developer/src/kmcmplib/tests/util_callbacks.h b/developer/src/kmcmplib/tests/util_callbacks.h
new file mode 100644
index 0000000000000000000000000000000000000000..345de64b9f484b568f28c1652e800ee20e7307ee
--- /dev/null
+++ b/developer/src/kmcmplib/tests/util_callbacks.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include
+
+int msgproc(int line, uint32_t dwMsgCode, const char* szText, void* context);
+bool loadfileProc(const char* filename, const char* baseFilename, void* data, int* size, void* context);
+
+extern std::vector error_vec;
\ No newline at end of file
diff --git a/developer/src/kmcmplib/src/filesystem.cpp b/developer/src/kmcmplib/tests/util_filesystem.cpp
similarity index 84%
rename from developer/src/kmcmplib/src/filesystem.cpp
rename to developer/src/kmcmplib/tests/util_filesystem.cpp
index a343641acc128ed5537e0e1835d45a2253c29f2e..7f9edd6b367a303573e165767ca1805f13bfd558 100644
--- a/developer/src/kmcmplib/src/filesystem.cpp
+++ b/developer/src/kmcmplib/tests/util_filesystem.cpp
@@ -7,7 +7,7 @@
#include
#include
#include
-#include "filesystem.h"
+#include "util_filesystem.h"
#ifdef _MSC_VER
#include
@@ -186,3 +186,42 @@ KMX_BOOL kmcmp_FileExists(const KMX_WCHAR* filename) {
return FALSE;
};
+
+
+bool IsRelativePath(KMX_CHAR const * p) {
+ // Relative path (returns TRUE):
+ // ..\...\BITMAP.BMP
+ // PATH\BITMAP.BMP
+ // BITMAP.BMP
+
+ // Semi-absolute path (returns FALSE):
+ // \...\BITMAP.BMP
+
+ // Absolute path (returns FALSE):
+ // C:\...\BITMAP.BMP
+ // \\SERVER\SHARE\...\BITMAP.BMP
+
+ if ((*p == '\\') || (*p == '/')) return FALSE;
+ if (*p && *(p + 1) == ':') return FALSE;
+
+ return TRUE;
+}
+
+bool IsRelativePath(KMX_WCHAR const * p) {
+ // Relative path (returns TRUE):
+ // ..\...\BITMAP.BMP
+ // PATH\BITMAP.BMP
+ // BITMAP.BMP
+
+ // Semi-absolute path (returns FALSE):
+ // \...\BITMAP.BMP
+
+ // Absolute path (returns FALSE):
+ // C:\...\BITMAP.BMP
+ // \\SERVER\SHARE\...\BITMAP.BMP
+
+ if ((*p == u'\\') || (*p == u'/'))return FALSE;
+ if (*p && *(p + 1) == u':') return FALSE;
+
+ return TRUE;
+}
\ No newline at end of file
diff --git a/developer/src/kmcmplib/src/filesystem.h b/developer/src/kmcmplib/tests/util_filesystem.h
similarity index 82%
rename from developer/src/kmcmplib/src/filesystem.h
rename to developer/src/kmcmplib/tests/util_filesystem.h
index 786b7268ac0c09bdda634296445e7e448404cc1e..4c94dae6bf4e4049d80efe8fa5d52d4b4fd7bb93 100644
--- a/developer/src/kmcmplib/src/filesystem.h
+++ b/developer/src/kmcmplib/tests/util_filesystem.h
@@ -1,7 +1,7 @@
#pragma once
#include
-#include "kmx_u16.h"
+#include "../src/kmx_u16.h"
// Opens files on windows and non-windows platforms. Datatypes for Filename and mode must be the same.
// returns FILE* if file could be opened; FILE needs to be closed in calling function
@@ -10,3 +10,6 @@ FILE* Open_File(const KMX_WCHART* Filename, const KMX_WCHART* mode);
FILE* Open_File(const KMX_WCHAR* Filename, const KMX_WCHAR* mode);
KMX_BOOL kmcmp_FileExists(const KMX_CHAR *filename);
KMX_BOOL kmcmp_FileExists(const KMX_WCHAR *filename);
+
+bool IsRelativePath(KMX_CHAR const * p);
+bool IsRelativePath(KMX_WCHAR const * p);
diff --git a/developer/src/kmcomp/main.pas b/developer/src/kmcomp/main.pas
index 5a29927a807bccd47dea671dbb6ce8d629a1f727..4aa29cf6c1ace54494285d0f9bd9c1f701d45040 100644
--- a/developer/src/kmcomp/main.pas
+++ b/developer/src/kmcomp/main.pas
@@ -65,7 +65,7 @@ uses
UKeymanTargets;
function CompileKeyboard(FInFile, FOutFile: string; FDebug, FWarnAsError: Boolean): Boolean; forward; // I4706
-function KCSetCompilerOptions(const FInFile: string; FShouldAddCompilerVersion, FUseKmcmpLib: Boolean): Boolean; forward;
+function KCSetCompilerOptions(const FInFile: string; FShouldAddCompilerVersion: Boolean): Boolean; forward;
//function CompilerMessage(line: Integer; msgcode: LongWord; text: PAnsiChar): Integer; stdcall; forward;
procedure FixupPathSlashes(var path: string); forward;
@@ -90,7 +90,6 @@ var
FParamDistribution: Boolean;
FMergingValidateIds: Boolean;
FShouldAddCompilerVersion: Boolean;
- FUseKmcmpLib: Boolean;
FJsonSchemaPath: string;
FParamSourcePath: string;
FParamHelpLink: string;
@@ -117,7 +116,6 @@ begin
FColorMode := cmDefault;
FShouldAddCompilerVersion := True;
- FUseKmcmpLib := True;
FParamInfile := '';
FParamOutfile := '';
@@ -193,8 +191,6 @@ begin
FColorMode := cmForceNoColor
else if s = '-no-compiler-version' then
FShouldAddCompilerVersion := False
- else if s = '-use-legacy-compiler' then
- FUseKmcmpLib := False
else if (s = '-help') or (s = '-h') then
begin
// Force help
@@ -223,8 +219,6 @@ begin
writeln(SKeymanDeveloperName + ' Compiler (32-bit)');
{$ENDIF}
writeln('Version ' + CKeymanVersionInfo.VersionWithTag + ', ' + GetVersionCopyright);
- if not FUseKmcmpLib then
- writeln('Note: using legacy compiler');
end;
if FError or (FParamInfile = '') then
@@ -234,7 +228,7 @@ begin
writeln('');
writeln('Usage: '+cmd+' [-s[s]] [-nologo] [-c] [-d] [-w] [-cfc] [-v[s|d]] [-source-path path] [-schema-path path] ');
writeln(' '+spc+' [-m] infile [-m infile] [-t target] [outfile.kmx|outfile.js [error.log]]'); // I4699
- writeln(' '+spc+' [-add-help-link path] [-color|-no-color] [-no-compiler-version] [-use-legacy-compiler]');
+ writeln(' '+spc+' [-add-help-link path] [-color|-no-color] [-no-compiler-version]');
writeln(' '+spc+' [-extract-keyboard-info field[,field...]]');
writeln(' infile can be a .kmn file (Keyboard Source, .kps file (Package Source), or .kpj (project)'); // I4699 // I4825
writeln(' if -v specified, can also be a .keyboard_info file');
@@ -258,7 +252,6 @@ begin
writeln(' uses console mode to determine whether color should be used.');
writeln;
writeln(' -no-compiler-version Don''t embed the compiler version stores, useful for regression tests.');
- writeln(' -use-legacy-compiler Use legacy compiler (will be removed in 18.0)');
writeln;
writeln(' JSON .keyboard_info compile targets:');
writeln(' -v[s] validate infile against source schema');
@@ -298,7 +291,7 @@ begin
TProjectLogConsole.Create(FSilent, FFullySilent, hOutfile, FColorMode);
- KCSetCompilerOptions(FParamInfile, FShouldAddCompilerVersion, FUseKmcmpLib);
+ KCSetCompilerOptions(FParamInfile, FShouldAddCompilerVersion);
if FValidateRepoChanges then
FError := not TValidateRepoChanges.Execute(FParamInfile, FParamOutfile)
@@ -324,7 +317,7 @@ begin
ExitCode := 1;
end;
-function KCSetCompilerOptions(const FInFile: string; FShouldAddCompilerVersion, FUseKmcmpLib: Boolean): Boolean;
+function KCSetCompilerOptions(const FInFile: string; FShouldAddCompilerVersion: Boolean): Boolean;
var
opt: TCompilerOptions;
begin
@@ -332,7 +325,6 @@ begin
opt.dwSize := sizeof(TCompilerOptions);
opt.ShouldAddCompilerVersion := FShouldAddCompilerVersion;
- opt.UseKmcmpLib := FUseKmcmpLib;
Result := SetCompilerOptions(@opt, @CompilerMessageW);
diff --git a/developer/src/server/build.sh b/developer/src/server/build.sh
index 53585f1df6b567f62a912e47239d8177c2aa8a22..c735e26fecb3f93b9cd7962f5726cc48969c656c 100755
--- a/developer/src/server/build.sh
+++ b/developer/src/server/build.sh
@@ -124,20 +124,18 @@ fi
if (( build_keymanweb )); then
pushd "$KEYMAN_ROOT/web/"
- ./build.sh --no-minify
+ ./build.sh build --debug
popd
fi
if (( copy_keymanweb )); then
- WEB_SRC="$KEYMAN_ROOT/web/build/app/web/debug"
- UI_SRC="$KEYMAN_ROOT/web/build/app/ui/debug"
+ WEB_SRC="$KEYMAN_ROOT/web/build/publish/debug"
DST="$(dirname "$THIS_SCRIPT")/src/site/resource"
rm -rf "$DST"
mkdir -p "$DST/osk"
mkdir -p "$DST/ui"
cp "$WEB_SRC/"*.js "$WEB_SRC/"*.js.map "$DST/"
- cp "$UI_SRC/"*.js "$UI_SRC/"*.js.map "$DST/"
cp -R "$WEB_SRC/osk/"* "$DST/osk/"
cp -R "$WEB_SRC/ui/"* "$DST/ui/"
cp "$KEYMAN_ROOT/web/LICENSE" "$DST/"
diff --git a/developer/src/server/src/site/chargrid.js b/developer/src/server/src/site/chargrid.js
index 1ce9206bae57604d9fcf6ee7abec88a38098413b..f6c31f2df30dd4acd221dcc6cdc0004f357354d4 100644
--- a/developer/src/server/src/site/chargrid.js
+++ b/developer/src/server/src/site/chargrid.js
@@ -73,17 +73,10 @@
function updateLogCursor() {
var i, selStart, selLength, selDirection;
- if(keyman.isPositionSynthesized()) { // this is an internal function
- // For touch devices, we need to ask KMW
- selStart = 0;
- selLength = 0;
- selDirection = 'forward';
- } else {
- // For desktop devices, we use the position reported by the textarea control
- selStart = ta1.selectionStart;
- selLength = ta1.selectionEnd - ta1.selectionStart;
- selDirection = ta1.selectionDirection;
- }
+ // We use the position reported by the textarea control
+ selStart = ta1.selectionStart;
+ selLength = ta1.selectionEnd - ta1.selectionStart;
+ selDirection = ta1.selectionDirection;
selLength = calculateLengthByCodepoint(ta1.value, selStart, selLength);
selStart = calculateLengthByCodepoint(ta1.value, 0, selStart);
diff --git a/developer/src/server/src/site/test.js b/developer/src/server/src/site/test.js
index 954ac203c45c841e609a9140a007bb42e2a5c7bb..60a27cc484f6cb85ef1df344bafeefbbed4eec79 100644
--- a/developer/src/server/src/site/test.js
+++ b/developer/src/server/src/site/test.js
@@ -221,7 +221,7 @@ window.onload = function() {
// Create a new on screen keyboard view and tell KeymanWeb that
// we are using the targetDevice for context input.
- newOSK = new com.keyman.osk.InlinedOSKView(targetDevice, keyman.util.device.coreSpec);
+ newOSK = new keyman.views.InlinedOSKView(keyman, { device: targetDevice });
keyman.core.contextDevice = targetDevice;
keyman.osk = newOSK;
@@ -238,11 +238,10 @@ window.onload = function() {
keyman.addEventListener('keyboardchange', function(keyboardProperties) {
if(newOSK) {
keyman.osk = newOSK;
- newOSK.activeKeyboard = keyman.core.activeKeyboard;
+ newOSK.activeKeyboard = keyman.contextManager.activeKeyboard; // Private API refs on both sides
}
keyboardDropdown.set(keyboardProperties.internalName);
window.sessionStorage.setItem('current-keyboard', keyboardProperties.internalName);
- keyman.alignInputs();
});
}
diff --git a/developer/src/tike/project/Keyman.Developer.System.Project.kmnProjectFileAction.pas b/developer/src/tike/project/Keyman.Developer.System.Project.kmnProjectFileAction.pas
index 703a1e4a0b6edd891950b647e5240c2e1250f02b..2b6dea94ec580d34adc0c54f471f021683ec3b44 100644
--- a/developer/src/tike/project/Keyman.Developer.System.Project.kmnProjectFileAction.pas
+++ b/developer/src/tike/project/Keyman.Developer.System.Project.kmnProjectFileAction.pas
@@ -115,7 +115,7 @@ begin
TProject.CompilerMessageFile := Self;
options.dwSize := sizeof(COMPILER_OPTIONS);
options.ShouldAddCompilerVersion := addVersion;
- options.UseKmcmpLib := not useLegacyCompiler;
+ // TODO: useLegacyCompiler means we switch to kmcmpdll vs kmc
if not SetCompilerOptions(@options, ProjectCompilerMessage) then
begin
Log(plsFatal, 'Unable to set compiler options', CERR_FATAL, 0);
diff --git a/linux/debian/rules b/linux/debian/rules
index ab4ad409e4e38c790121455b5e84b54834aa4af1..db1f9250174663f6c5a46a58d0c60b0c6f282306 100755
--- a/linux/debian/rules
+++ b/linux/debian/rules
@@ -9,7 +9,9 @@ export PYBUILD_INSTALL_ARGS=--install-scripts=/usr/share/keyman-config/
export KEYMAN_PKG_BUILD=1
+export HOME=$(shell mktemp -d)
export XDG_DATA_HOME=$(shell mktemp -d)
+export XDG_CONFIG_HOME=$(shell mktemp -d)
# xenial needs this to be explicit
export LC_ALL=C.UTF-8
@@ -32,26 +34,23 @@ override_dh_auto_configure:
--wrap-mode=nodownload --prefix=/usr --sysconfdir=/etc --localstatedir=/var
linux/keyman-system-service/build.sh configure -- \
--wrap-mode=nodownload --prefix=/usr --sysconfdir=/etc --localstatedir=/var
- # keyman-config
+ linux/keyman-config/build.sh configure
override_dh_auto_build:
core/build.sh --no-tests build:arch
linux/ibus-keyman/build.sh build
linux/keyman-system-service/build.sh build
- # keyman-config
+ linux/keyman-config/build.sh build
cd linux/keyman-config && \
- make man && \
sed -i -e "s/^__pkgversion__ = \"[^\"]*\"/__pkgversion__ = \"$(DEB_VERSION)\"/g" keyman_config/version.py && \
make compile-po
- dh_auto_build --sourcedir=linux/keyman-config --buildsystem=pybuild $@
override_dh_auto_test:
ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
core/build.sh --no-tests test:arch
linux/ibus-keyman/build.sh test
linux/keyman-system-service/build.sh test
- # keyman-config
- dh_auto_test --sourcedir=linux/keyman-config --buildsystem=pybuild $@
+ linux/keyman-config/build.sh test
endif
override_dh_auto_install:
@@ -63,6 +62,7 @@ override_dh_auto_install:
install -d $(CURDIR)/debian/keyman/usr/share/
cp -r linux/keyman-config/locale/ $(CURDIR)/debian/keyman/usr/share/
rm $(CURDIR)/debian/keyman/usr/share/locale/*.po*
+ # Don't call `build.sh install` - dh_auto_install does some extra smarts
dh_auto_install --sourcedir=linux/keyman-config --buildsystem=pybuild $@
# Unfortunately bash-completion 2.10 (focal) doesn't yet provide dh-sequence-bash-completion,
# which we could add as build-dependency, so we'll have to explicitly call
diff --git a/linux/keyman-config/Makefile b/linux/keyman-config/Makefile
index 41eaab30efb727be2c2e527059e32a1f2d7aa9e4..f057dcad0ce8c0f81e2e82af0014c99e429a41f8 100644
--- a/linux/keyman-config/Makefile
+++ b/linux/keyman-config/Makefile
@@ -1,49 +1,19 @@
#!/usr/bin/make
-default: clean version man langtags
- python3 setup.py build
-
-langtags:
- cd buildtools && python3 ./build-langtags.py
+default:
+ ./build.sh clean build
install:
- if [ -n "${SUDO_USER}" ]; then \
- make install-sudo; \
- else \
- make install-temp; \
- fi
-
-install-sudo: # run as sudo
- pip3 install qrcode sentry-sdk
- # eventually change this to: pip3 install .
- python3 setup.py install
- # install icons
- mkdir -p /usr/local/share/keyman/icons
- cp keyman_config/icons/* /usr/local/share/keyman/icons
- # install man pages
- mkdir -p /usr/local/share/man/man1
- cp ../../debian/man/*.1 /usr/local/share/man/man1
-
-install-temp:
- mkdir -p /tmp/keyman/$(shell python3 -c 'import sys;import os;pythonver="python%d.%d" % (sys.version_info[0], sys.version_info[1]);sitedir = os.path.join("lib", pythonver, "site-packages");print(sitedir)')
- # when we no longer have to support old pip version (python > 3.6) change this to:
- # pip3 install --prefix /tmp/keyman .
- PYTHONUSERBASE=/tmp/keyman python3 setup.py install --user
-
-uninstall: clean # run as sudo
- rm -rf /usr/local/share/keyman/icons
- rm -f /usr/local/share/man/man1/km-*.1
- pip3 uninstall keyman_config
- rm -f /usr/local/bin/km-config
- rm -f /usr/local/bin/km-kvk2ldml
- rm -f /usr/local/bin/km-package-get
- rm -f /usr/local/bin/km-package-install
- rm -f /usr/local/bin/km-package-list-installed
- rm -f /usr/local/bin/km-package-uninstall
+ ./build.sh install
+
+uninstall: # run as sudo
+ ./build.sh uninstall
clean:
- -rm -rf dist make_deb build keyman_config/version.py *.egg-info __pycache__ \
- keyman_config/standards/lang_tags_map.py
+ ./build.sh clean
+
+check:
+ ./build.sh test
devdist: version
python3 setup.py egg_info -b.`TZ=UTC git log -1 --pretty=format:%cd --date=format-local:%Y%m%d%H%M` sdist
@@ -93,6 +63,3 @@ $(MOFILES): %/LC_MESSAGES/keyman-config.mo: %.po
msgfmt -v --check --output-file=$@ $^
compile-po: $(MOFILES)
-
-check:
- ./run-tests.sh
diff --git a/linux/keyman-config/build.sh b/linux/keyman-config/build.sh
index 38cf22e917b40ab8a37c60b15a5748681bc5c66e..1cb603f091b8801ffc7fd807d02c07b461cac688 100755
--- a/linux/keyman-config/build.sh
+++ b/linux/keyman-config/build.sh
@@ -24,9 +24,90 @@ cd "$THIS_SCRIPT_PATH"
builder_describe_outputs \
build "/linux/keyman-config/keyman_config/standards/lang_tags_map.py"
-builder_run_action clean make clean
+clean_action() {
+ rm -rf dist make_deb build keyman_config/version.py ./*.egg-info __pycache__ \
+ keyman_config/standards/lang_tags_map.py
+}
+
+build_man_pages() {
+ local TEMP_DATA_DIR SCHEMA_DIR
+ TEMP_DATA_DIR=$(mktemp -d)
+ SCHEMA_DIR=$TEMP_DATA_DIR/glib-2.0/schemas
+ export XDG_DATA_DIRS=$TEMP_DATA_DIR:${XDG_DATA_DIRS-}
+ export GSETTINGS_SCHEMA_DIR="${SCHEMA_DIR}"
+ mkdir -p "$SCHEMA_DIR"
+ cp ./com.keyman.gschema.xml "$SCHEMA_DIR"/
+ glib-compile-schemas "$SCHEMA_DIR"
+ ./build-help.sh --man --no-reconf
+ export XDG_DATA_DIRS=${XDG_DATA_DIRS#*:}
+ unset GSETTINGS_SCHEMA_DIR
+ rm -rf "$TEMP_DATA_DIR"
+}
+
+build_action() {
+ builder_echo "Create version.py"
+ pushd keyman_config
+ sed \
+ -e "s/_VERSION_/${VERSION}/g" \
+ -e "s/_VERSIONWITHTAG_/${VERSION_WITH_TAG}/g" \
+ -e "s/_VERSIONGITTAG_/${VERSION_GIT_TAG}/g" \
+ -e "s/_MAJORVERSION_/${VERSION_MAJOR}/g" \
+ -e "s/_RELEASEVERSION_/${VERSION_RELEASE}/g" \
+ -e "s/_TIER_/${TIER}/g" \
+ -e "s/_ENVIRONMENT_/${VERSION_ENVIRONMENT}/g" \
+ -e "s/_UPLOADSENTRY_/${UPLOAD_SENTRY}/g" \
+ version.py.in > version.py
+ popd
+ pushd buildtools
+ if [ -f build-langtags.py ]; then
+ builder_echo "Create lang_tags_map.py"
+ python3 ./build-langtags.py
+ else
+ builder_echo "Skip building lang_tags_map.py during package build"
+ fi
+ popd
+ builder_echo "Building man pages"
+ build_man_pages
+ builder_echo "Building keyman-config"
+ python3 setup.py build
+}
+
+install_action() {
+ if [ -n "${SUDO_USER:-}" ]; then
+ pip3 install qrcode sentry-sdk
+ # eventually change this to: pip3 install .
+ python3 setup.py install
+ # install icons
+ mkdir -p /usr/local/share/keyman/icons
+ cp keyman_config/icons/* /usr/local/share/keyman/icons
+ # install man pages
+ mkdir -p /usr/local/share/man/man1
+ cp ../../debian/man/*.1 /usr/local/share/man/man1
+ else
+ mkdir -p "/tmp/keyman/$(python3 -c 'import sys;import os;pythonver="python%d.%d" % (sys.version_info[0], sys.version_info[1]);sitedir = os.path.join("lib", pythonver, "site-packages");print(sitedir)')"
+ # when we no longer have to support old pip version (python > 3.6) change this to:
+ # pip3 install --prefix /tmp/keyman .
+ PYTHONUSERBASE=${DESTDIR:-/tmp/keyman} python3 setup.py install --user
+ fi
+}
+
+uninstall_action() {
+ # run as sudo
+ clean_action
+ rm -rf /usr/local/share/keyman/icons
+ rm -f /usr/local/share/man/man1/km-*.1
+ pip3 uninstall keyman_config
+ rm -f /usr/local/bin/km-config
+ rm -f /usr/local/bin/km-kvk2ldml
+ rm -f /usr/local/bin/km-package-get
+ rm -f /usr/local/bin/km-package-install
+ rm -f /usr/local/bin/km-package-list-installed
+ rm -f /usr/local/bin/km-package-uninstall
+}
+
+builder_run_action clean clean_action
builder_run_action configure # nothing to do
-builder_run_action build make
+builder_run_action build build_action
builder_run_action test ./run-tests.sh
-builder_run_action install make install
-builder_run_action uninstall make uninstall
+builder_run_action install install_action
+builder_run_action uninstall uninstall_action
diff --git a/linux/keyman-config/com.keyman.gschema.xml b/linux/keyman-config/com.keyman.gschema.xml
index 8f05a42bf9b0c70d4bc2ee9ffdb6b3ec6716c9c3..df50060d8c44eb97e2ec4e8d4848ab23c34ef665 100644
--- a/linux/keyman-config/com.keyman.gschema.xml
+++ b/linux/keyman-config/com.keyman.gschema.xml
@@ -1,8 +1,12 @@
-
+
+ true
+ Automatic error reporting
+ true to enable automatic reporting of errors to Sentry
+
diff --git a/linux/keyman-config/keyman_config/__init__.py b/linux/keyman-config/keyman_config/__init__.py
index e5cecf4cc82d6b6ae333dab67dd5aa615454b3e2..04098ade00fcbeeed91a9f7b5ee63070780d60af 100644
--- a/linux/keyman-config/keyman_config/__init__.py
+++ b/linux/keyman-config/keyman_config/__init__.py
@@ -1,20 +1,18 @@
-import getpass
import gettext
-import importlib
-import logging
-import os
-import platform
-import sys
-from .version import __version__
-from .version import __versionwithtag__
-from .version import __versiongittag__
-from .version import __majorversion__
-from .version import __releaseversion__
-from .version import __tier__
-from .version import __pkgversion__
-from .version import __environment__
-from .version import __uploadsentry__
+from keyman_config.sentry_handling import SentryErrorHandling
+
+from keyman_config.version import (
+ __version__,
+ __versionwithtag__,
+ __versiongittag__,
+ __majorversion__,
+ __releaseversion__,
+ __tier__,
+ __pkgversion__,
+ __environment__,
+ __uploadsentry__
+)
def _(txt):
@@ -24,7 +22,7 @@ def _(txt):
return translation
-def secure_lookup(data, key1, key2 = None):
+def secure_lookup(data, key1, key2=None):
"""
Return data[key1][key2] while dealing with data being None or key1 or key2 not existing
"""
@@ -38,18 +36,10 @@ def secure_lookup(data, key1, key2 = None):
return None
-def before_send(event, hint):
- if 'exc_info' in hint:
- exc_type, exc_value, tb = hint['exc_info']
- if isinstance(exc_value, KeyboardInterrupt):
- # Ignore KeyboardInterrupt exception
- return None
- return event
-
-
gettext.bindtextdomain('keyman-config', '/usr/share/locale')
gettext.textdomain('keyman-config')
+
#if __tier__ == 'alpha' or __tier__ == 'beta': // #7227 disabling:
# Alpha and beta versions will work against the staging server so that they
# can access new APIs etc that will only be available there. The staging
@@ -60,60 +50,8 @@ gettext.textdomain('keyman-config')
KeymanComUrl = 'https://keyman.com'
KeymanApiUrl = 'https://api.keyman.com'
+
# There's no staging site for downloads
KeymanDownloadsUrl = 'https://downloads.keyman.com'
-if 'unittest' in sys.modules.keys():
- print('Not reporting to Sentry', file=sys.stderr)
-elif os.environ.get('KEYMAN_NOSENTRY'):
- print('Not reporting to Sentry because KEYMAN_NOSENTRY environment variable set', file=sys.stderr)
-elif not __uploadsentry__:
- print('Not reporting to Sentry because UPLOAD_SENTRY is false (%s)' % __environment__, file=sys.stderr)
-else:
- try:
- # Try new sentry-sdk first
- sentry_sdk = importlib.import_module('sentry_sdk')
- from sentry_sdk import configure_scope, set_user
- from sentry_sdk.integrations.logging import LoggingIntegration
- HaveSentryNewSdk = True
-
- sentry_logging = LoggingIntegration(
- level=logging.INFO, # Capture info and above as breadcrumbs
- event_level=logging.CRITICAL # Send critical errors as events
- )
- SentryUrl = "https://1d0edbf2d0dc411b87119b6e92e2c357@o1005580.ingest.sentry.io/5983525"
- sentry_sdk.init(
- dsn=SentryUrl,
- environment=__environment__,
- release=__versiongittag__,
- integrations=[sentry_logging],
- before_send=before_send
- )
- set_user({'id': hash(getpass.getuser())})
- with configure_scope() as scope:
- scope.set_tag("app", os.path.basename(sys.argv[0]))
- scope.set_tag("pkgversion", __pkgversion__)
- scope.set_tag("platform", platform.platform())
- scope.set_tag("system", platform.system())
- scope.set_tag("tier", __tier__)
- except ImportError:
- try:
- # sentry-sdk is not available, so use older raven
- raven = importlib.import_module('raven')
- from raven import Client
- HaveSentryNewSdk = False
-
- # Note, legacy raven API requires secret (https://github.com/keymanapp/keyman/pull/5787#discussion_r721457909)
- SentryUrl = "https://1d0edbf2d0dc411b87119b6e92e2c357:e6d5a81ee6944fc79bd9f0cbb1f2c2a4@o1005580.ingest.sentry.io/5983525"
- client = Client(SentryUrl, environment=__environment__, release=__versiongittag__)
- client.user_context({'id': hash(getpass.getuser())})
- client.tags_context({
- 'app': os.path.basename(sys.argv[0]),
- 'pkgversion': __pkgversion__,
- 'platform': platform.platform(),
- 'system': platform.system(),
- 'tier': __tier__,
- })
- except ImportError:
- # even raven is not available. This is the case on Ubuntu 16.04. Just ignore.
- print(_('Neither sentry-sdk nor raven is available. Not enabling Sentry error reporting.'), file=sys.stderr)
+SentryErrorHandling().initialize_sentry()
diff --git a/linux/keyman-config/keyman_config/dconf_util.py b/linux/keyman-config/keyman_config/dconf_util.py
index 09b95d1815a5f42021674cba68b40a6c2f7887fa..c28ec3fd19c5aa7b6a126c020caadb3412890f3b 100644
--- a/linux/keyman-config/keyman_config/dconf_util.py
+++ b/linux/keyman-config/keyman_config/dconf_util.py
@@ -2,25 +2,25 @@
from gi.repository import Gio
-# DConf path destkop/ibus/keyman/options
-DCONF_BASE = "com.keyman.options"
+# GSettings path destkop/ibus/keyman/options
+GSETTINGS_BASE = "com.keyman.options"
-# Utilities to get and set Keyman options in DConf:
+# Utilities to get and set Keyman options in GSettings:
# /desktop/ibus/keyman/options/packageID/keyboardID/options
def get_child_schema(info):
- settings = Gio.Settings.new(DCONF_BASE)
+ settings = Gio.Settings.new(GSETTINGS_BASE)
path = settings.get_property('path')
if not path.endswith('/'):
path += '/'
path += info['packageID'] + '/' + info['keyboardID'] + '/'
- return Gio.Settings(DCONF_BASE + '.child', path)
+ return Gio.Settings(GSETTINGS_BASE + '.child', path)
def get_option(info):
"""
- Get the Keyman keyboard options from DConf
+ Get the Keyman keyboard options from GSettings
Convert from list of comma-separated strings into dictionary
Args:
@@ -41,7 +41,7 @@ def get_option(info):
def set_option(info, options):
"""
- Store the Keyman keyboard options in DConf as a list of strings
+ Store the Keyman keyboard options in GSettings as a list of strings
Args:
info: dictionary
diff --git a/linux/keyman-config/keyman_config/get_kmp.py b/linux/keyman-config/keyman_config/get_kmp.py
index af7b34063d3336fd2c451c4ddddbdcf4073a1570..ed3f7ec8a444fdc178238f495f9ef29ca05f41f9 100755
--- a/linux/keyman-config/keyman_config/get_kmp.py
+++ b/linux/keyman-config/keyman_config/get_kmp.py
@@ -9,6 +9,7 @@ import requests
import requests_cache
from gi.repository import GObject
+from keyman_config import _
from keyman_config import KeymanApiUrl, KeymanDownloadsUrl
from keyman_config.deprecated_decorator import deprecated
@@ -20,6 +21,16 @@ class InstallLocation(GObject.GEnum):
Unknown = 99
+def get_install_area_string(area):
+ if area == InstallLocation.OS:
+ return _('System')
+ elif area == InstallLocation.Shared:
+ return _('Shared')
+ elif area == InstallLocation.User:
+ return _('User')
+ return _('Unknown')
+
+
def get_package_download_data(packageID, weekCache=False):
"""
Get package download data from keyboards download api.
diff --git a/linux/keyman-config/keyman_config/sentry_handling.py b/linux/keyman-config/keyman_config/sentry_handling.py
new file mode 100644
index 0000000000000000000000000000000000000000..f2b2f8adc3441b83ced5dc75879f06cbea06310f
--- /dev/null
+++ b/linux/keyman-config/keyman_config/sentry_handling.py
@@ -0,0 +1,152 @@
+#!/usr/bin/python3
+import getpass
+import importlib
+import logging
+import os
+import platform
+import sys
+from keyman_config.gsettings import GSettings
+from keyman_config.version import (
+ __version__,
+ __versionwithtag__,
+ __versiongittag__,
+ __majorversion__,
+ __releaseversion__,
+ __tier__,
+ __pkgversion__,
+ __environment__,
+ __uploadsentry__
+)
+
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gio, Gtk
+
+
+class SentryErrorHandling:
+ def __init__(self) -> None:
+ self.settings = Gio.Settings.new('com.keyman.options')
+
+ def initialize_sentry(self):
+ (enabled, reason) = self.is_sentry_enabled()
+ if not enabled:
+ print(reason, file=sys.stderr)
+ logging.info(reason)
+ return (enabled, reason)
+ else:
+ try:
+ self._sentry_sdk_initialize()
+ except ImportError:
+ try:
+ self._raven_initialize()
+ except ImportError:
+ # even raven is not available. This is the case on Ubuntu 16.04. Just ignore.
+ print(_('Neither sentry-sdk nor raven is available. Not enabling Sentry error reporting.'),
+ file=sys.stderr)
+ logging.info('Neither sentry-sdk nor raven is available. Not enabling Sentry error reporting.')
+ return (False, _('Neither sentry-sdk nor raven is available. Not enabling Sentry error reporting.'))
+ return (True, '')
+
+ def is_sentry_enabled(self):
+ if 'unittest' in sys.modules.keys():
+ return (False, 'Running unit tests, not reporting to Sentry')
+ elif self._get_environ_nosentry():
+ return (False, 'Not reporting to Sentry because KEYMAN_NOSENTRY environment variable set')
+ elif not __uploadsentry__:
+ return (False, f'Not reporting to Sentry because UPLOAD_SENTRY is false ({__environment__})')
+ elif not self._get_setting():
+ return (False, 'Not reporting to Sentry because disabled in GSettings')
+ return (True, 'Reporting to Sentry')
+
+ def is_sentry_disabled_by_variable(self):
+ return self._get_environ_nosentry() or not __uploadsentry__
+
+ def bind_checkbutton(self, button: Gtk.CheckButton):
+ self.settings.bind("error-reporting", button, "active", Gio.SettingsBindFlags.NO_SENSITIVITY)
+ self.settings.connect("changed::error-reporting", self._on_sentry_reporting_toggled)
+
+ def set_enabled(self, enabled):
+ assert not self.is_sentry_disabled_by_variable()
+ was_enabled = self.is_sentry_enabled()
+ self._save_setting(enabled)
+ if enabled != was_enabled:
+ self._handle_enabled(enabled)
+
+ def _get_environ_nosentry(self):
+ keyman_nosentry = os.environ.get('KEYMAN_NOSENTRY')
+ return keyman_nosentry and (int(keyman_nosentry) == 1)
+
+ def _handle_enabled(self, enabled):
+ if enabled:
+ self.initialize_sentry()
+ else:
+ self._close_sentry()
+
+ def _save_setting(self, enabled: bool):
+ self.settings.set_boolean('error-reporting', enabled)
+
+ def _get_setting(self) -> bool:
+ return self.settings.get_boolean('error-reporting')
+
+ def _on_sentry_reporting_toggled(self, settings, key):
+ self._handle_enabled(self.settings.get_boolean('error-reporting'))
+
+ def _close_sentry(self):
+ from sentry_sdk import Hub
+ logging.info("Shutting down Sentry error reporting")
+ client = Hub.current.client
+ if client is not None:
+ client.close(timeout=2.0)
+
+ def _sentry_sdk_initialize(self):
+ # Try new sentry-sdk first
+ sentry_sdk = importlib.import_module('sentry_sdk')
+ from sentry_sdk import configure_scope, set_user
+ from sentry_sdk.integrations.logging import LoggingIntegration
+
+ sentry_logging = LoggingIntegration(
+ level=logging.INFO, # Capture info and above as breadcrumbs
+ event_level=logging.CRITICAL # Send critical errors as events
+ )
+ SentryUrl = "https://1d0edbf2d0dc411b87119b6e92e2c357@o1005580.ingest.sentry.io/5983525"
+ sentry_sdk.init(
+ dsn=SentryUrl,
+ environment=__environment__,
+ release=__versiongittag__,
+ integrations=[sentry_logging],
+ before_send=self._before_send
+ )
+ set_user({'id': hash(getpass.getuser())})
+ with configure_scope() as scope:
+ scope.set_tag("app", os.path.basename(sys.argv[0]))
+ scope.set_tag("pkgversion", __pkgversion__)
+ scope.set_tag("platform", platform.platform())
+ scope.set_tag("system", platform.system())
+ scope.set_tag("tier", __tier__)
+ logging.info("Initialized Sentry error reporting")
+
+ def _raven_initialize(self):
+ # sentry-sdk is not available, so use older raven
+ raven = importlib.import_module('raven')
+ from raven import Client
+
+ # Note, legacy raven API requires secret (https://github.com/keymanapp/keyman/pull/5787#discussion_r721457909)
+ SentryUrl = "https://1d0edbf2d0dc411b87119b6e92e2c357:e6d5a81ee6944fc79bd9f0cbb1f2c2a4@o1005580.ingest.sentry.io/5983525"
+ client = Client(SentryUrl, environment=__environment__, release=__versiongittag__)
+ client.user_context({'id': hash(getpass.getuser())})
+ client.tags_context({
+ 'app': os.path.basename(sys.argv[0]),
+ 'pkgversion': __pkgversion__,
+ 'platform': platform.platform(),
+ 'system': platform.system(),
+ 'tier': __tier__,
+ })
+ logging.info("Initialized Sentry error reporting (raven)")
+
+ def _before_send(self, event, hint):
+ if 'exc_info' in hint:
+ exc_type, exc_value, tb = hint['exc_info']
+ if isinstance(exc_value, KeyboardInterrupt):
+ # Ignore KeyboardInterrupt exception
+ return None
+ return event
diff --git a/linux/keyman-config/keyman_config/view_installed.py b/linux/keyman-config/keyman_config/view_installed.py
index 01b1a59b89b3e413619c87abd56c479667abeab2..263d665d25f8ec89ef1694185015fad5e7f2d9b4 100755
--- a/linux/keyman-config/keyman_config/view_installed.py
+++ b/linux/keyman-config/keyman_config/view_installed.py
@@ -19,12 +19,13 @@ from keyman_config.accelerators import bind_accelerator, init_accel
from keyman_config.dbus_util import get_keyman_config_service
from keyman_config.downloadkeyboard import DownloadKmpWindow
from keyman_config.get_kmp import (InstallLocation, get_keyboard_dir,
- get_keyman_dir)
+ get_keyman_dir, get_install_area_string)
from keyman_config.install_window import InstallKmpWindow, find_keyman_image
from keyman_config.keyboard_details import KeyboardDetailsView
from keyman_config.kmpmetadata import get_fonts, parsemetadata
from keyman_config.list_installed_kmp import get_installed_kmp
from keyman_config.options import OptionsView
+from keyman_config.sentry_handling import SentryErrorHandling
from keyman_config.uninstall_kmp import uninstall_kmp
from keyman_config.welcome import WelcomeView
@@ -94,7 +95,7 @@ class ViewInstalledWindowBase(Gtk.Window):
self.close()
def run(self):
- self.resize(576, 324)
+ self.resize(776, 424)
self.connect("destroy", Gtk.main_quit)
self.show_all()
Gtk.main()
@@ -104,26 +105,60 @@ class ViewInstalledWindow(ViewInstalledWindowBase):
def __init__(self):
ViewInstalledWindowBase.__init__(self)
-# window is split left/right hbox
-# right is ButtonBox
-# possibly 2 ButtonBox in a vbox
-# top one with _Remove, _About, ?_Welcome? or ?Read_Me?, _Options
-# bottom one with _Download, _Install, Re_fresh, _Close
-# left is GtkTreeView - does it need to be inside anything else apart from the hbox?
-# with liststore which defines columns
-# GdkPixbuf icon
-# gchararray name
-# gchararray version
-# gchararray packageID (hidden)
-# enum? area (user, shared, system) (icon or hidden?)
-# gchararray welcomefile (hidden) (or just use area and packageID?)
-# changing selected item in treeview changes what buttons are activated
-# on selected_item_changed signal set the data that the buttons will use in their callbacks
-# see https://developer.gnome.org/gtk3/stable/TreeWidget.html#TreeWidget
+ self.sentry = SentryErrorHandling()
+ outerHbox = Gtk.HBox()
+ sidebar = Gtk.StackSidebar()
+ stack = Gtk.Stack()
+
+ outerHbox.pack_start(sidebar, False, False, 0)
+ outerHbox.pack_end(Gtk.Separator(), False, False, 0)
+ outerHbox.pack_end(stack, True, True, 0)
+ stack.set_hexpand(True)
+ stack.set_vexpand(True)
+ sidebar.set_stack(stack)
+
+ stack.add_titled(self.add_keyboard_layouts_widget(), "KeyboardLayouts", _("Keyboard Layouts"))
+ stack.add_titled(self.add_options_widget(), "Options", _("Options"))
+
+ outmostVBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ outmostVBox.pack_start(outerHbox, True, True, 0)
+
+ bbox_bottom = Gtk.ButtonBox(spacing=12, orientation=Gtk.Orientation.HORIZONTAL)
+ bbox_bottom.set_layout(Gtk.ButtonBoxStyle.START)
+
+ button = Gtk.Button.new_with_mnemonic(_("_Install keyboard..."))
+ button.set_tooltip_text(_("Install a keyboard from a file"))
+ button.connect("clicked", self.on_installfile_clicked)
+ bbox_bottom.add(button)
+
+ button = Gtk.Button.new_with_mnemonic(_("_Download keyboard..."))
+ button.set_tooltip_text(_("Download and install a keyboard from the Keyman website"))
+ button.connect("clicked", self.on_download_clicked)
+ bbox_bottom.add(button)
+
+ button = Gtk.Button.new_with_mnemonic(_("_Refresh"))
+ button.set_tooltip_text(_("Refresh keyboard list"))
+ button.connect("clicked", self.on_refresh_clicked)
+ bbox_bottom.add(button)
+
+ button = Gtk.Button.new_with_mnemonic(_("_Close"))
+ button.set_tooltip_text(_("Close window"))
+ button.connect("clicked", self.on_close_clicked)
+ bind_accelerator(self.accelerators, button, 'q')
+ bind_accelerator(self.accelerators, button, 'w')
+ bbox_bottom.add(button)
+
+ bbox_hbox = Gtk.HBox(spacing=12)
+ bbox_hbox.pack_start(bbox_bottom, True, True, 12)
+ outmostVBox.pack_end(bbox_hbox, False, True, 12)
+ self.add(outmostVBox)
+
+ def add_keyboard_layouts_widget(self):
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
- s = Gtk.ScrolledWindow()
- hbox.pack_start(s, True, True, 0)
+
+ scrolledWindow = Gtk.ScrolledWindow()
+ hbox.pack_start(scrolledWindow, True, True, 0)
self.store = Gtk.ListStore(
GdkPixbuf.Pixbuf, # icon
@@ -131,15 +166,11 @@ class ViewInstalledWindow(ViewInstalledWindowBase):
str, # version
str, # packageID
int, # enum InstallLocation (KmpArea is GObject version)
+ str, # InstallLocation area
+ str, # Tooltip with InstallLocation path
str, # path to welcome file if it exists or None
str) # path to options file if it exists or None
- # add installed keyboards to the the store e.g.
- # treeiter = store.append([GdkPixbuf.Pixbuf.new_from_file_at_size(
- # "/usr/local/share/keyman/libtralo/libtralo.ico.png", 16, 16), \
- # "LIBTRALO", "1.6.1", \
- # "libtralo", KmpArea.SHARED, True])
-
self.refresh_installed_kmp()
self.tree = Gtk.TreeView(self.store)
@@ -155,11 +186,16 @@ class ViewInstalledWindow(ViewInstalledWindowBase):
# i18n: column header in table displaying installed keyboards
column = Gtk.TreeViewColumn(_("Version"), renderer, text=2)
self.tree.append_column(column)
+ # i18n: column header in table displaying installed keyboards
+ column = Gtk.TreeViewColumn(_("Area"), renderer, text=5)
+ self.tree.append_column(column)
+
+ self.tree.set_tooltip_column(column=6)
select = self.tree.get_selection()
select.connect("changed", self.on_tree_selection_changed)
- s.add(self.tree)
+ scrolledWindow.add(self.tree)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
@@ -191,36 +227,34 @@ class ViewInstalledWindow(ViewInstalledWindowBase):
bbox_top.add(self.options_button)
vbox.pack_start(bbox_top, False, False, 12)
+ hbox.pack_start(vbox, False, False, 12)
- bbox_bottom = Gtk.ButtonBox(spacing=12, orientation=Gtk.Orientation.VERTICAL)
- bbox_bottom.set_layout(Gtk.ButtonBoxStyle.END)
+ return hbox
- button = Gtk.Button.new_with_mnemonic(_("_Refresh"))
- button.set_tooltip_text(_("Refresh keyboard list"))
- button.connect("clicked", self.on_refresh_clicked)
- bbox_bottom.add(button)
+ def add_options_widget(self):
+ vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ label = Gtk.Label(_("General"))
+ label.set_padding(5, 5)
+ label.set_halign(Gtk.Align.START)
+ vbox.pack_start(label, False, False, 10)
- button = Gtk.Button.new_with_mnemonic(_("_Download"))
- button.set_tooltip_text(_("Download and install a keyboard from the Keyman website"))
- button.connect("clicked", self.on_download_clicked)
- bbox_bottom.add(button)
-
- button = Gtk.Button.new_with_mnemonic(_("_Install"))
- button.set_tooltip_text(_("Install a keyboard from a file"))
- button.connect("clicked", self.on_installfile_clicked)
- bbox_bottom.add(button)
+ (enabled, reason) = self.sentry.is_sentry_enabled()
+ disabledByVariable = self.sentry.is_sentry_disabled_by_variable()
- button = Gtk.Button.new_with_mnemonic(_("_Close"))
- button.set_tooltip_text(_("Close window"))
- button.connect("clicked", self.on_close_clicked)
- bind_accelerator(self.accelerators, button, 'q')
- bind_accelerator(self.accelerators, button, 'w')
- bbox_bottom.add(button)
+ self.errorReportingButton = Gtk.CheckButton(_("Automatically report errors to keyman.com"))
+ self.errorReportingButton.set_active(enabled)
+ self.errorReportingButton.set_sensitive(not disabledByVariable)
+ self.sentry.bind_checkbutton(self.errorReportingButton)
+ vbox.pack_start(self.errorReportingButton, False, False, 0)
- vbox.pack_end(bbox_bottom, False, False, 12)
+ if disabledByVariable:
+ label = Gtk.Label(reason)
+ label.set_halign(Gtk.Align.START)
+ label.set_padding(25, 0)
+ label.set_sensitive(False)
+ vbox.pack_start(label, False, False, 0)
- hbox.pack_start(vbox, False, False, 12)
- self.add(hbox)
+ return vbox
def addlistitems(self, installed_kmp, store, install_area):
for kmp in sorted(installed_kmp):
@@ -255,6 +289,8 @@ class ViewInstalledWindow(ViewInstalledWindowBase):
kmpdata['version'],
kmpdata['packageID'],
install_area,
+ get_install_area_string(install_area),
+ path.replace(os.path.expanduser('~'), '~'),
welcome_file,
options_file])
diff --git a/linux/keyman-config/run-tests.sh b/linux/keyman-config/run-tests.sh
index 3b8664512adc639aa376c6e6ed4b5da8e479713c..b390ce613bc589d926da09a8ca39c28ecd7c3a19 100755
--- a/linux/keyman-config/run-tests.sh
+++ b/linux/keyman-config/run-tests.sh
@@ -1,6 +1,9 @@
#!/bin/bash
PYTHONPATH=.:$PYTHONPATH
+XDG_CONFIG_HOME=$(mktemp --directory)
+export XDG_CONFIG_HOME
+
if [ -n "$TEAMCITY_VERSION" ]; then
if ! pip3 list --format=columns | grep -q teamcity-messages; then
pip3 install teamcity-messages
@@ -9,3 +12,5 @@ if [ -n "$TEAMCITY_VERSION" ]; then
else
python3 -m unittest discover -v -s tests -p test_*.py
fi
+
+rm -rf "$XDG_CONFIG_HOME"
diff --git a/linux/keyman-config/tests/test_gnome_keyboards_util.py b/linux/keyman-config/tests/test_gnome_keyboards_util.py
index b5a0cac842fff324e018f25672c1b93817ef376b..a53908cdd3296aaee7dfa26b838ac0fe29042f67 100644
--- a/linux/keyman-config/tests/test_gnome_keyboards_util.py
+++ b/linux/keyman-config/tests/test_gnome_keyboards_util.py
@@ -9,14 +9,14 @@ class GnomeKeyboardsUtilTests(unittest.TestCase):
def setUp(self):
_reset_gnome_shell()
- @patch('keyman_config.os.system')
+ @patch('os.system')
def test_IsGnomeShell_RunningGnomeShell(self, mockSystem):
# Setup
mockSystem.return_value = 0
# Execute/Verify
self.assertEqual(is_gnome_shell(), True)
- @patch('keyman_config.os.system')
+ @patch('os.system')
def test_IsGnomeShell_NotRunningGnomeShell(self, mockSystem):
# Setup
mockSystem.return_value = 1
diff --git a/linux/keyman-config/tests/test_install_kmp.py b/linux/keyman-config/tests/test_install_kmp.py
index 2f250550cd3ecef1ea5f1f1a88debe4a292c6178..d1e10bd645b6d9aaa450bc9fed3cc64883b92b31 100644
--- a/linux/keyman-config/tests/test_install_kmp.py
+++ b/linux/keyman-config/tests/test_install_kmp.py
@@ -262,13 +262,11 @@ class InstallKmpTests(unittest.TestCase):
def test_InstallKmp_FutureKeymanVersion(self):
# Setup
- workdir = tempfile.TemporaryDirectory().name
- os.makedirs(workdir)
- packagedir = tempfile.TemporaryDirectory().name
- os.makedirs(packagedir)
- self.mockGetKeyboardDir.return_value = packagedir
- kmpfile = self._createEmptyKmp(workdir)
- self._createKmpJson(packagedir, ', "fileVersion": "99.0"')
+ workdir = tempfile.TemporaryDirectory()
+ packagedir = tempfile.TemporaryDirectory()
+ self.mockGetKeyboardDir.return_value = packagedir.name
+ kmpfile = self._createEmptyKmp(workdir.name)
+ self._createKmpJson(packagedir.name, ', "fileVersion": "99.0"')
# Execute
with self.assertRaises(InstallError) as context:
@@ -278,22 +276,24 @@ class InstallKmpTests(unittest.TestCase):
self.assertTrue('foo.kmp requires Keyman 99.0 or higher' in context.exception.message)
self.mockInstallToIbus.assert_not_called()
+ # Teardown
+ workdir.cleanup()
+ packagedir.cleanup()
+
def test_InstallKmp(self):
for testcase in [
{'name': 'PreviousKeymanVersion', 'fileVersion': ', "fileVersion": "7.0"'},
- {'name': 'SameKeymanVersion', 'fileVersion': ', "fileVersion": "' + __version__ + '"'},
+ {'name': 'SameKeymanVersion', 'fileVersion': f', "fileVersion": "{__version__}"'},
{'name': 'NoFileVersion', 'fileVersion': ''}
]:
with self.subTest(msg=testcase['name'], data=testcase['fileVersion']):
# Setup
self.mockInstallToIbus.reset_mock()
- workdir = tempfile.TemporaryDirectory().name
- os.makedirs(workdir)
- packagedir = tempfile.TemporaryDirectory().name
- os.makedirs(packagedir)
- self.mockGetKeyboardDir.return_value = packagedir
- kmpfile = self._createEmptyKmp(workdir)
- self._createKmpJson(packagedir, testcase['fileVersion'])
+ workdir = tempfile.TemporaryDirectory()
+ packagedir = tempfile.TemporaryDirectory()
+ self.mockGetKeyboardDir.return_value = packagedir.name
+ kmpfile = self._createEmptyKmp(workdir.name)
+ self._createKmpJson(packagedir.name, testcase['fileVersion'])
# Execute
InstallKmp()._install_kmp(kmpfile, 'km', InstallLocation.User)
@@ -301,6 +301,10 @@ class InstallKmpTests(unittest.TestCase):
# Verify
self.mockInstallToIbus.assert_called_once()
+ # Teardown
+ workdir.cleanup()
+ packagedir.cleanup()
+
if __name__ == '__main__':
unittest.main()
diff --git a/linux/keyman-config/tests/test_package_install_completion.py b/linux/keyman-config/tests/test_package_install_completion.py
index 97238e3dc2e57051c63d085d1692018267841f71..61374a6c66cbc5756e22b0c42e42810adf378b03 100644
--- a/linux/keyman-config/tests/test_package_install_completion.py
+++ b/linux/keyman-config/tests/test_package_install_completion.py
@@ -2,7 +2,7 @@
import os
import tempfile
import unittest
-from unittest.mock import patch, ANY
+from unittest.mock import patch
from importlib.machinery import SourceFileLoader
from importlib.util import module_from_spec, spec_from_loader
@@ -10,17 +10,11 @@ from importlib.util import module_from_spec, spec_from_loader
class PackageInstallCompletionTests(unittest.TestCase):
def setUp(self):
- patcher1 = patch('keyman_config.install_kmp.extract_kmp')
- self.mockExtractKmp = patcher1.start()
- self.addCleanup(patcher1.stop)
- patcher2 = patch('keyman_config.kmpmetadata.get_metadata')
- self.mockGetMetadata = patcher2.start()
- self.addCleanup(patcher2.stop)
-
+ self.mockExtractKmp = self._setupMock('keyman_config.install_kmp.extract_kmp')
+ self.mockGetMetadata = self._setupMock('keyman_config.kmpmetadata.get_metadata')
loader = SourceFileLoader('km_package_install', os.path.join(os.path.dirname(
os.path.abspath(__file__)), '../km-package-install'))
- spec = spec_from_loader(loader.name, loader)
- if spec:
+ if spec := spec_from_loader(loader.name, loader):
self.mod = module_from_spec(spec)
loader.exec_module(self.mod)
@@ -29,32 +23,43 @@ class PackageInstallCompletionTests(unittest.TestCase):
self.cacheDir = os.path.join(self.tempDir.name, 'keyman')
os.makedirs(self.cacheDir)
+ def tearDown(self):
+ self.tempDir.cleanup()
+
+ def _setupMock(self, arg0):
+ patcher = patch(arg0)
+ result = patcher.start()
+ self.addCleanup(patcher.stop)
+ return result
+
def _list_languages_for_keyboard_impl(self, packageId):
return self.mod._list_languages_for_keyboard_impl(packageId, 'someDir')
def test_PackageCompletionNoLanguage(self):
- open(os.path.join(self.cacheDir, 'foo'), 'w')
- self.mockGetMetadata.return_value = (None, None, None, [{}], None)
- result = self._list_languages_for_keyboard_impl('foo')
- self.assertEqual(result, "")
+ with open(os.path.join(self.cacheDir, 'foo'), 'w'):
+ self.mockGetMetadata.return_value = (None, None, None, [{}], None)
+ result = self._list_languages_for_keyboard_impl('foo')
+ self.assertEqual(result, "")
def test_PackageCompletionOneLanguage(self):
- open(os.path.join(self.cacheDir, 'khmer_angkor'), 'w')
- self.mockGetMetadata.return_value = (
- None, None, None,
- [{'languages': [{'name': 'Central Khmer (Khmer, Cambodia)', 'id': 'km'}]}],
- None)
- result = self._list_languages_for_keyboard_impl('khmer_angkor')
- self.assertEqual(result, "km")
+ with open(os.path.join(self.cacheDir, 'khmer_angkor'), 'w'):
+ self.mockGetMetadata.return_value = (
+ None, None, None,
+ [{'languages': [{'name': 'Central Khmer (Khmer, Cambodia)', 'id': 'km'}]}],
+ None
+ )
+ result = self._list_languages_for_keyboard_impl('khmer_angkor')
+ self.assertEqual(result, "km")
def test_PackageCompletionMultipleLanguages(self):
- open(os.path.join(self.cacheDir, 'sil_euro_latin'), 'w')
- self.mockGetMetadata.return_value = (
- None, None, None,
- [{'languages': [
- {'name': 'English', 'id': 'en'},
- {'name': 'French', 'id': 'fr'},
- {'name': 'German', 'id': 'de'}]}],
- None)
- result = self._list_languages_for_keyboard_impl('sil_euro_latin')
- self.assertEqual(result, 'en\nfr\nde')
+ with open(os.path.join(self.cacheDir, 'sil_euro_latin'), 'w'):
+ self.mockGetMetadata.return_value = (
+ None, None, None,
+ [{'languages': [
+ {'name': 'English', 'id': 'en'},
+ {'name': 'French', 'id': 'fr'},
+ {'name': 'German', 'id': 'de'}]}],
+ None
+ )
+ result = self._list_languages_for_keyboard_impl('sil_euro_latin')
+ self.assertEqual(result, 'en\nfr\nde')
diff --git a/linux/scripts/install.sh b/linux/scripts/install.sh
index 40f2eb8f5b09bcd7f6571fa272d660391349ebaa..55074f48481915a713d630926f56b3161d76b955 100755
--- a/linux/scripts/install.sh
+++ b/linux/scripts/install.sh
@@ -15,7 +15,7 @@ INSTALLDIR=${INSTALLDIR:-"/usr/local"}
if [[ "${SUDOINSTALL}" != "no" ]]; then
if [ "$EUID" -ne 0 ]
then
- echo "Please run 'make un/install' with sudo"
+ echo "Please run 'build.sh un/install' with sudo"
exit
fi
fi
@@ -24,7 +24,7 @@ if [ -f "/usr/share/ibus/component/keyman.xml" ] && [ "${SUDOINSTALL}" == "yes"
if grep -Fq "/usr/lib/ibus" /usr/share/ibus/component/keyman.xml
then
echo "component file is in ibus-keyman package version so move it"
- echo "run 'sudo make uninstall' to put it back"
+ echo "run 'sudo build.sh uninstall' to put it back"
mv /usr/share/ibus/component/keyman.xml /usr/share/doc/ibus-keyman/
else
echo "component file is local one so overwrite it"
@@ -37,10 +37,10 @@ cd "$BASEDIR"
cd ibus-keyman
if [[ "${SUDOINSTALL}" == "uninstall" ]]; then
- echo "doing make uninstall of ibus-keyman"
+ echo "doing build.sh uninstall of ibus-keyman"
./build.sh uninstall
else
- echo "doing make install of ibus-keyman"
+ echo "doing build.sh install of ibus-keyman"
./build.sh install
fi
cd "$BASEDIR"
@@ -49,20 +49,20 @@ cd keyman-config
echo "SUDOINSTALL: ${SUDOINSTALL}"
if [[ "${SUDOINSTALL}" == "yes" ]]; then
if [ ! -d build ]; then
- echo "keyman-config must be built before it is installed. Run 'make configure' if needed then 'make'"
+ echo "keyman-config must be built before it is installed. Run 'build.sh configure build'."
exit 1
fi
echo "doing sudo glib-compile-schemas for keyman-config"
cp com.keyman.gschema.xml /usr/share/glib-2.0/schemas/
glib-compile-schemas /usr/share/glib-2.0/schemas/
echo "doing sudo install of keyman-config"
- make install
+ ./build.sh install
elif [[ "${SUDOINSTALL}" == "uninstall" ]]; then
echo "doing sudo uninstall of keyman-config"
- make uninstall
+ ./build.sh uninstall
else
echo "doing /tmp install of keyman-config"
- make install-temp
+ ./build.sh install
fi
cd "$BASEDIR"
diff --git a/linux/scripts/package-build.inc.sh b/linux/scripts/package-build.inc.sh
index 39ea70f7d1719859c9bba2a3de0240b013628b52..6c249891c1633a3bcd5da6279f6fc13a316cc29a 100644
--- a/linux/scripts/package-build.inc.sh
+++ b/linux/scripts/package-build.inc.sh
@@ -28,10 +28,7 @@ function downloadSource() {
if [ "${proj:=}" == "keyman" ]; then
cd "${BASEDIR}" || exit
- fi
-
- if [ "${proj}" == "keyman" ]; then
- make clean
+ ./build.sh clean
fi
# Update tier in Debian watch files (replacing any previously set tier) and remove comment
diff --git a/linux/scripts/reconf.sh b/linux/scripts/reconf.sh
index cd74571a4a1dc696934de83f04d395d4850cda05..4e4f5c1b115cbc37e02905dfcdc262ccb9941024 100755
--- a/linux/scripts/reconf.sh
+++ b/linux/scripts/reconf.sh
@@ -21,7 +21,7 @@ cd "$BASEDIR/ibus-keyman"
./build.sh clean configure
cd "$BASEDIR/keyman-config"
-make clean
+./build.sh clean
cd keyman_config
sed \
diff --git a/oem/firstvoices/ios/exportAppStore.plist b/oem/firstvoices/ios/exportAppStore.plist
index 123af31a0c437e0927eddef23f03b3a850374642..c4ac972730f0715629ebd05914f11ab9f1026ca6 100644
--- a/oem/firstvoices/ios/exportAppStore.plist
+++ b/oem/firstvoices/ios/exportAppStore.plist
@@ -7,9 +7,9 @@
teamID
D7TR486TEH
signingCertificate
- EFA9DE793B2A25F38270468AE62060CFF55CD634
+ C4726D4C8BD4C2FE38A551B84CEDB8E4B3FD12D6
installerSigningCertificate
- EFA9DE793B2A25F38270468AE62060CFF55CD634
+ C4726D4C8BD4C2FE38A551B84CEDB8E4B3FD12D6
provisioningProfiles
com.firstvoices.keyboards
diff --git a/package-lock.json b/package-lock.json
index 865cff0557fbc4e0bec564ed631cd3520c658368..d78384e30fc3d19b8a5008e283abf8a4eb4dc4d6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -35,15 +35,16 @@
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.59.1",
"chai": "^4.3.4",
+ "esbuild": "^0.15.16",
"eslint": "^8.39.0",
"eslint-config-standard-with-typescript": "^34.0.1",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-n": "^15.7.0",
"eslint-plugin-promise": "^6.1.1",
- "esbuild": "^0.15.15",
"mocha": "^10.0.0",
"mocha-teamcity-reporter": "^4.0.0",
"ts-node": "^10.9.1",
+ "tslib": "^2.5.2",
"typescript": "^4.9.5"
}
},
@@ -481,6 +482,17 @@
"typescript": "^4.9.5"
}
},
+ "common/web/tslib": {
+ "name": "@keymanapp/tslib",
+ "dependencies": {
+ "tslib": "^2.5.2",
+ "typescript": "^4.9.5"
+ },
+ "devDependencies": {
+ "@keymanapp/resources-gosh": "*",
+ "esbuild": "^0.15.16"
+ }
+ },
"common/web/types": {
"name": "@keymanapp/common-types",
"license": "MIT",
@@ -2252,6 +2264,38 @@
"node": ">=12"
}
},
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz",
+ "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz",
+ "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -2325,38 +2369,6 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
- "node_modules/@esbuild/android-arm": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.15.tgz",
- "integrity": "sha512-JJjZjJi2eBL01QJuWjfCdZxcIgot+VoK6Fq7eKF9w4YHm9hwl7nhBR1o2Wnt/WcANk5l9SkpvrldW1PLuXxcbw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-loong64": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.15.tgz",
- "integrity": "sha512-lhz6UNPMDXUhtXSulw8XlFAtSYO26WmHQnCi2Lg2p+/TMiJKNLtZCYUxV4wG6rZMzXmr8InGpNwk+DLT2Hm0PA==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/@gar/promisify": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
@@ -2530,6 +2542,10 @@
"resolved": "common/tools/sourcemap-path-remapper",
"link": true
},
+ "node_modules/@keymanapp/tslib": {
+ "resolved": "common/web/tslib",
+ "link": true
+ },
"node_modules/@keymanapp/web-sentry-manager": {
"resolved": "common/web/sentry-manager",
"link": true
@@ -2777,6 +2793,11 @@
"node": ">=6"
}
},
+ "node_modules/@sentry/browser/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ },
"node_modules/@sentry/cli": {
"version": "2.2.0",
"dev": true,
@@ -2811,6 +2832,11 @@
"node": ">=6"
}
},
+ "node_modules/@sentry/core/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ },
"node_modules/@sentry/hub": {
"version": "5.30.0",
"license": "BSD-3-Clause",
@@ -2823,6 +2849,11 @@
"node": ">=6"
}
},
+ "node_modules/@sentry/hub/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ },
"node_modules/@sentry/minimal": {
"version": "5.30.0",
"license": "BSD-3-Clause",
@@ -2835,6 +2866,11 @@
"node": ">=6"
}
},
+ "node_modules/@sentry/minimal/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ },
"node_modules/@sentry/node": {
"version": "6.19.6",
"license": "BSD-3-Clause",
@@ -2908,6 +2944,11 @@
"node": ">=6"
}
},
+ "node_modules/@sentry/node/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ },
"node_modules/@sentry/types": {
"version": "5.30.0",
"license": "BSD-3-Clause",
@@ -2926,6 +2967,11 @@
"node": ">=6"
}
},
+ "node_modules/@sentry/utils/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ },
"node_modules/@sindresorhus/is": {
"version": "4.6.0",
"license": "MIT",
@@ -4944,9 +4990,9 @@
"license": "MIT"
},
"node_modules/esbuild": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.15.tgz",
- "integrity": "sha512-TEw/lwK4Zzld9x3FedV6jy8onOUHqcEX3ADFk4k+gzPUwrxn8nWV62tH0udo8jOtjFodlEfc4ypsqX3e+WWO6w==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz",
+ "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==",
"dev": true,
"hasInstallScript": true,
"bin": {
@@ -4956,34 +5002,34 @@
"node": ">=12"
},
"optionalDependencies": {
- "@esbuild/android-arm": "0.15.15",
- "@esbuild/linux-loong64": "0.15.15",
- "esbuild-android-64": "0.15.15",
- "esbuild-android-arm64": "0.15.15",
- "esbuild-darwin-64": "0.15.15",
- "esbuild-darwin-arm64": "0.15.15",
- "esbuild-freebsd-64": "0.15.15",
- "esbuild-freebsd-arm64": "0.15.15",
- "esbuild-linux-32": "0.15.15",
- "esbuild-linux-64": "0.15.15",
- "esbuild-linux-arm": "0.15.15",
- "esbuild-linux-arm64": "0.15.15",
- "esbuild-linux-mips64le": "0.15.15",
- "esbuild-linux-ppc64le": "0.15.15",
- "esbuild-linux-riscv64": "0.15.15",
- "esbuild-linux-s390x": "0.15.15",
- "esbuild-netbsd-64": "0.15.15",
- "esbuild-openbsd-64": "0.15.15",
- "esbuild-sunos-64": "0.15.15",
- "esbuild-windows-32": "0.15.15",
- "esbuild-windows-64": "0.15.15",
- "esbuild-windows-arm64": "0.15.15"
+ "@esbuild/android-arm": "0.15.18",
+ "@esbuild/linux-loong64": "0.15.18",
+ "esbuild-android-64": "0.15.18",
+ "esbuild-android-arm64": "0.15.18",
+ "esbuild-darwin-64": "0.15.18",
+ "esbuild-darwin-arm64": "0.15.18",
+ "esbuild-freebsd-64": "0.15.18",
+ "esbuild-freebsd-arm64": "0.15.18",
+ "esbuild-linux-32": "0.15.18",
+ "esbuild-linux-64": "0.15.18",
+ "esbuild-linux-arm": "0.15.18",
+ "esbuild-linux-arm64": "0.15.18",
+ "esbuild-linux-mips64le": "0.15.18",
+ "esbuild-linux-ppc64le": "0.15.18",
+ "esbuild-linux-riscv64": "0.15.18",
+ "esbuild-linux-s390x": "0.15.18",
+ "esbuild-netbsd-64": "0.15.18",
+ "esbuild-openbsd-64": "0.15.18",
+ "esbuild-sunos-64": "0.15.18",
+ "esbuild-windows-32": "0.15.18",
+ "esbuild-windows-64": "0.15.18",
+ "esbuild-windows-arm64": "0.15.18"
}
},
"node_modules/esbuild-android-64": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.15.tgz",
- "integrity": "sha512-F+WjjQxO+JQOva3tJWNdVjouFMLK6R6i5gjDvgUthLYJnIZJsp1HlF523k73hELY20WPyEO8xcz7aaYBVkeg5Q==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz",
+ "integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==",
"cpu": [
"x64"
],
@@ -4997,9 +5043,9 @@
}
},
"node_modules/esbuild-android-arm64": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.15.tgz",
- "integrity": "sha512-attlyhD6Y22jNyQ0fIIQ7mnPvDWKw7k6FKnsXlBvQE6s3z6s6cuEHcSgoirquQc7TmZgVCK5fD/2uxmRN+ZpcQ==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz",
+ "integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==",
"cpu": [
"arm64"
],
@@ -5013,9 +5059,9 @@
}
},
"node_modules/esbuild-darwin-64": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.15.tgz",
- "integrity": "sha512-ohZtF8W1SHJ4JWldsPVdk8st0r9ExbAOSrBOh5L+Mq47i696GVwv1ab/KlmbUoikSTNoXEhDzVpxUR/WIO19FQ==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz",
+ "integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==",
"cpu": [
"x64"
],
@@ -5029,9 +5075,9 @@
}
},
"node_modules/esbuild-darwin-arm64": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.15.tgz",
- "integrity": "sha512-P8jOZ5zshCNIuGn+9KehKs/cq5uIniC+BeCykvdVhx/rBXSxmtj3CUIKZz4sDCuESMbitK54drf/2QX9QHG5Ag==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz",
+ "integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==",
"cpu": [
"arm64"
],
@@ -5045,9 +5091,9 @@
}
},
"node_modules/esbuild-freebsd-64": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.15.tgz",
- "integrity": "sha512-KkTg+AmDXz1IvA9S1gt8dE24C8Thx0X5oM0KGF322DuP+P3evwTL9YyusHAWNsh4qLsR80nvBr/EIYs29VSwuA==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz",
+ "integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==",
"cpu": [
"x64"
],
@@ -5061,9 +5107,9 @@
}
},
"node_modules/esbuild-freebsd-arm64": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.15.tgz",
- "integrity": "sha512-FUcML0DRsuyqCMfAC+HoeAqvWxMeq0qXvclZZ/lt2kLU6XBnDA5uKTLUd379WYEyVD4KKFctqWd9tTuk8C/96g==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz",
+ "integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==",
"cpu": [
"arm64"
],
@@ -5077,9 +5123,9 @@
}
},
"node_modules/esbuild-linux-32": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.15.tgz",
- "integrity": "sha512-q28Qn5pZgHNqug02aTkzw5sW9OklSo96b5nm17Mq0pDXrdTBcQ+M6Q9A1B+dalFeynunwh/pvfrNucjzwDXj+Q==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz",
+ "integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==",
"cpu": [
"ia32"
],
@@ -5093,9 +5139,9 @@
}
},
"node_modules/esbuild-linux-64": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.15.tgz",
- "integrity": "sha512-217KPmWMirkf8liO+fj2qrPwbIbhNTGNVtvqI1TnOWJgcMjUWvd677Gq3fTzXEjilkx2yWypVnTswM2KbXgoAg==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz",
+ "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==",
"cpu": [
"x64"
],
@@ -5109,9 +5155,9 @@
}
},
"node_modules/esbuild-linux-arm": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.15.tgz",
- "integrity": "sha512-RYVW9o2yN8yM7SB1yaWr378CwrjvGCyGybX3SdzPHpikUHkME2AP55Ma20uNwkNyY2eSYFX9D55kDrfQmQBR4w==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz",
+ "integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==",
"cpu": [
"arm"
],
@@ -5125,9 +5171,9 @@
}
},
"node_modules/esbuild-linux-arm64": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.15.tgz",
- "integrity": "sha512-/ltmNFs0FivZkYsTzAsXIfLQX38lFnwJTWCJts0IbCqWZQe+jjj0vYBNbI0kmXLb3y5NljiM5USVAO1NVkdh2g==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz",
+ "integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==",
"cpu": [
"arm64"
],
@@ -5141,9 +5187,9 @@
}
},
"node_modules/esbuild-linux-mips64le": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.15.tgz",
- "integrity": "sha512-PksEPb321/28GFFxtvL33yVPfnMZihxkEv5zME2zapXGp7fA1X2jYeiTUK+9tJ/EGgcNWuwvtawPxJG7Mmn86A==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz",
+ "integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==",
"cpu": [
"mips64el"
],
@@ -5157,9 +5203,9 @@
}
},
"node_modules/esbuild-linux-ppc64le": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.15.tgz",
- "integrity": "sha512-ek8gJBEIhcpGI327eAZigBOHl58QqrJrYYIZBWQCnH3UnXoeWMrMZLeeZL8BI2XMBhP+sQ6ERctD5X+ajL/AIA==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz",
+ "integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==",
"cpu": [
"ppc64"
],
@@ -5173,9 +5219,9 @@
}
},
"node_modules/esbuild-linux-riscv64": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.15.tgz",
- "integrity": "sha512-H5ilTZb33/GnUBrZMNJtBk7/OXzDHDXjIzoLXHSutwwsLxSNaLxzAaMoDGDd/keZoS+GDBqNVxdCkpuiRW4OSw==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz",
+ "integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==",
"cpu": [
"riscv64"
],
@@ -5189,9 +5235,9 @@
}
},
"node_modules/esbuild-linux-s390x": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.15.tgz",
- "integrity": "sha512-jKaLUg78mua3rrtrkpv4Or2dNTJU7bgHN4bEjT4OX4GR7nLBSA9dfJezQouTxMmIW7opwEC5/iR9mpC18utnxQ==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz",
+ "integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==",
"cpu": [
"s390x"
],
@@ -5205,9 +5251,9 @@
}
},
"node_modules/esbuild-netbsd-64": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.15.tgz",
- "integrity": "sha512-aOvmF/UkjFuW6F36HbIlImJTTx45KUCHJndtKo+KdP8Dhq3mgLRKW9+6Ircpm8bX/RcS3zZMMmaBLkvGY06Gvw==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz",
+ "integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==",
"cpu": [
"x64"
],
@@ -5221,9 +5267,9 @@
}
},
"node_modules/esbuild-openbsd-64": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.15.tgz",
- "integrity": "sha512-HFFX+WYedx1w2yJ1VyR1Dfo8zyYGQZf1cA69bLdrHzu9svj6KH6ZLK0k3A1/LFPhcEY9idSOhsB2UyU0tHPxgQ==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz",
+ "integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==",
"cpu": [
"x64"
],
@@ -5237,9 +5283,9 @@
}
},
"node_modules/esbuild-sunos-64": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.15.tgz",
- "integrity": "sha512-jOPBudffG4HN8yJXcK9rib/ZTFoTA5pvIKbRrt3IKAGMq1EpBi4xoVoSRrq/0d4OgZLaQbmkHp8RO9eZIn5atA==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz",
+ "integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==",
"cpu": [
"x64"
],
@@ -5253,9 +5299,9 @@
}
},
"node_modules/esbuild-windows-32": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.15.tgz",
- "integrity": "sha512-MDkJ3QkjnCetKF0fKxCyYNBnOq6dmidcwstBVeMtXSgGYTy8XSwBeIE4+HuKiSsG6I/mXEb++px3IGSmTN0XiA==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz",
+ "integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==",
"cpu": [
"ia32"
],
@@ -5269,9 +5315,9 @@
}
},
"node_modules/esbuild-windows-64": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.15.tgz",
- "integrity": "sha512-xaAUIB2qllE888SsMU3j9nrqyLbkqqkpQyWVkfwSil6BBPgcPk3zOFitTTncEKCLTQy3XV9RuH7PDj3aJDljWA==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz",
+ "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==",
"cpu": [
"x64"
],
@@ -5285,9 +5331,9 @@
}
},
"node_modules/esbuild-windows-arm64": {
- "version": "0.15.15",
- "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.15.tgz",
- "integrity": "sha512-ttuoCYCIJAFx4UUKKWYnFdrVpoXa3+3WWkXVI6s09U+YjhnyM5h96ewTq/WgQj9LFSIlABQvadHSOQyAVjW5xQ==",
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz",
+ "integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==",
"cpu": [
"arm64"
],
@@ -6577,11 +6623,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/google-closure-compiler-java": {
- "version": "20200224.0.0",
- "dev": true,
- "license": "Apache-2.0"
- },
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
@@ -6910,20 +6951,6 @@
"version": "2.0.4",
"license": "ISC"
},
- "node_modules/internal-slot": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
- "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
- "dev": true,
- "dependencies": {
- "get-intrinsic": "^1.2.0",
- "has": "^1.0.3",
- "side-channel": "^1.0.4"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
"node_modules/inline-source-map": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz",
@@ -6942,6 +6969,20 @@
"node": ">=0.10.0"
}
},
+ "node_modules/internal-slot": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
+ "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.2.0",
+ "has": "^1.0.3",
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/interpret": {
"version": "1.4.0",
"dev": true,
@@ -7743,18 +7784,18 @@
"version": "4.4.2",
"license": "MIT"
},
- "node_modules/lodash.merge": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
- "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
- "dev": true
- },
"node_modules/lodash.memoize": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz",
"integrity": "sha512-eDn9kqrAmVUC1wmZvlQ6Uhde44n+tXpqPrN8olQJbttgh0oKclk+SF54P47VEGE9CEiMeRwAP8BaM7UHvBkz2A==",
"dev": true
},
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
"node_modules/lodash.set": {
"version": "4.3.2",
"license": "MIT"
@@ -10287,8 +10328,9 @@
}
},
"node_modules/tslib": {
- "version": "1.14.1",
- "license": "0BSD"
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.2.tgz",
+ "integrity": "sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA=="
},
"node_modules/tsutils": {
"version": "3.21.0",
@@ -10305,6 +10347,12 @@
"typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
}
},
+ "node_modules/tsutils/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
"node_modules/tunnel": {
"version": "0.0.6",
"license": "MIT",
@@ -11080,16 +11128,17 @@
"@keymanapp/lexical-model-layer": "*",
"@keymanapp/models-types": "*",
"@keymanapp/recorder-core": "*",
+ "@keymanapp/tslib": "*",
"@keymanapp/web-utils": "*",
"@types/node": "^11.9.4",
- "eventemitter3": "^4.0.0"
+ "eventemitter3": "^4.0.0",
+ "tslib": "^2.5.2"
},
"devDependencies": {
"@keymanapp/resources-gosh": "*",
"@keymanapp/web-sentry-manager": "*",
"@sentry/cli": "2.2.0",
"chai": "^4.3.4",
- "google-closure-compiler-java": "^20200224.0.0",
"karma": "^6.4.1",
"karma-browserstack-launcher": "^1.6.0",
"karma-chai": "^0.1.0",
diff --git a/package.json b/package.json
index 11fa521e1863cdf9640f979070ec2f8860e71100..787fe68e5a2b5c08988f0cae658d88de9f0b3fa3 100644
--- a/package.json
+++ b/package.json
@@ -2,18 +2,19 @@
"name": "root",
"private": true,
"devDependencies": {
- "chai": "^4.3.4",
- "esbuild": "^0.15.15",
- "mocha": "^10.0.0",
- "mocha-teamcity-reporter": "^4.0.0",
- "ts-node": "^10.9.1",
- "typescript": "^4.9.5",
"@typescript-eslint/eslint-plugin": "^5.59.1",
+ "chai": "^4.3.4",
+ "esbuild": "^0.15.16",
"eslint": "^8.39.0",
"eslint-config-standard-with-typescript": "^34.0.1",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-n": "^15.7.0",
- "eslint-plugin-promise": "^6.1.1"
+ "eslint-plugin-promise": "^6.1.1",
+ "mocha": "^10.0.0",
+ "mocha-teamcity-reporter": "^4.0.0",
+ "ts-node": "^10.9.1",
+ "tslib": "^2.5.2",
+ "typescript": "^4.9.5"
},
"scripts": {},
"workspaces": [
@@ -37,10 +38,10 @@
"web"
],
"dependencies": {
+ "@keymanapp/common-types": "file:common/web/types",
+ "@keymanapp/developer-test-helpers": "file:developer/src/common/web/test-helpers",
"@keymanapp/hextobin": "file:common/tools/hextobin",
"@keymanapp/keyman-version": "file:common/web/keyman-version",
- "@keymanapp/common-types": "file:common/web/types",
- "@keymanapp/ldml-keyboard-constants": "file:core/include/ldml",
- "@keymanapp/developer-test-helpers": "file:developer/src/common/web/test-helpers"
+ "@keymanapp/ldml-keyboard-constants": "file:core/include/ldml"
}
}
diff --git a/resources/build/build-utils-ci.inc.sh b/resources/build/build-utils-ci.inc.sh
index de36ae9e9b606556869c22f2f7492240825070f7..cab449a4636d59e5f9a388bfad84601d165e0956 100644
--- a/resources/build/build-utils-ci.inc.sh
+++ b/resources/build/build-utils-ci.inc.sh
@@ -100,18 +100,7 @@ function _builder_publish_npm_package() {
dry_run=--dry-run
fi
- # We use --no-git-tag-version because our CI system controls version numbering
- # and already tags releases. We also want to have the version of this match
- # the release of Keyman Developer -- these two versions should be in sync.
- # Because this is a large repo with multiple projects and build systems, it's
- # better for us that individual build systems don't take too much ownership of
- # git tagging. :)
- npm version --allow-same-version --no-git-tag-version --no-commit-hooks "$VERSION_WITH_TAG"
-
- # Update all @keymanapp/* [*]dependencies in package.json to the current
- # version-with-tag, so that the published version has precise dependencies,
- # and we don't accidentally end up with either older or newer deps here
- _builder_npm_set_dependency_version
+ _builder_write_npm_version
# Note: In either case, npm publish MUST be given --access public to publish a
# package in the @keymanapp scope on the public npm package index.
@@ -128,18 +117,33 @@ function _builder_publish_npm_package() {
fi
}
-# Updates all @keymanapp/* [*]dependencies in package.json to the current
-# version-with-tag, so that the published version has precise dependencies, and
-# we don't accidentally end up with either older or newer deps. This overwrites
-# the local package.json, so it does need to be restored afterwards
-function _builder_npm_set_dependency_version() {
- cat package.json | "$JQ" --arg VERSION_WITH_TAG "$VERSION_WITH_TAG" \
- '
- . +
- (try { dependencies: (.dependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {}) +
- (try { devDependencies: (.devDependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {}) +
- (try { bundleDependencies: (.bundleDependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {}) +
- (try { optionalDependencies: (.optionalDependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {})
- ' > package1.json
- mv -f package1.json package.json
+function _builder_write_npm_version() {
+ # We use --no-git-tag-version because our CI system controls version numbering
+ # and already tags releases. We also want to have the version of this match
+ # the release of Keyman Developer -- these two versions should be in sync.
+ # Because this is a large repo with multiple projects and build systems, it's
+ # better for us that individual build systems don't take too much ownership of
+ # git tagging. :)
+ if ! "$JQ" -e '.version' package.json > /dev/null; then
+ pushd "$KEYMAN_ROOT" > /dev/null
+ npm version --allow-same-version --no-git-tag-version --no-commit-hooks --workspaces "$VERSION_WITH_TAG"
+ popd > /dev/null
+ fi
+
+ # Updates all @keymanapp/* [*]dependencies in all package.jsons to the current
+ # version-with-tag, so that the published version has precise dependencies, and
+ # we don't accidentally end up with either older or newer deps. This overwrites
+ # the local package.json files, so they do need to be restored afterwards
+ find "$KEYMAN_ROOT" -name "package.json" -not -path '*/node_modules/*' -print0 | \
+ while IFS= read -r -d '' line; do
+ cat "$line" | "$JQ" --arg VERSION_WITH_TAG "$VERSION_WITH_TAG" \
+ '
+ . +
+ (try { dependencies: (.dependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {}) +
+ (try { devDependencies: (.devDependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {}) +
+ (try { bundleDependencies: (.bundleDependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {}) +
+ (try { optionalDependencies: (.optionalDependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {})
+ ' > "${line}_"
+ mv -f "${line}_" "$line"
+ done
}
\ No newline at end of file
diff --git a/tsconfig.cjs.json b/tsconfig.cjs.json
index 3508de15c7f2bd727c4ebcb7c6567024bb0b4d9d..4695672e90831e10d897d2f540feed5943d3689d 100644
--- a/tsconfig.cjs.json
+++ b/tsconfig.cjs.json
@@ -7,30 +7,14 @@
"references": [
{ "path": "./common/web/gesture-recognizer/tsconfig.json" },
{ "path": "./common/web/gesture-recognizer/src/tools/unit-test-resources/tsconfig.json" },
- { "path": "./common/web/input-processor/tsconfig.json" },
- { "path": "./common/web/keyboard-processor/tsconfig.json" },
- { "path": "./common/web/recorder/tsconfig.json" },
- { "path": "./common/web/sentry-manager/src/tsconfig.json" },
- { "path": "./common/web/utils/tsconfig.json" },
-
- { "path": "./common/models/templates/tsconfig.json" },
- { "path": "./common/models/types/tsconfig.json" },
- { "path": "./common/models/wordbreakers/tsconfig.json" },
{ "path": "./common/predictive-text/testing/one-stage-embedded-webworker/tsconfig.json" },
{ "path": "./common/predictive-text/testing/two-stage-embedded-webworker/tsconfig.json" },
{ "path": "./common/predictive-text/testing/two-stage-embedded-webworker/worker/tsconfig.json" },
- { "path": "./common/predictive-text/tsconfig.json" },
{ "path": "./developer/src/server/tsconfig.json" },
{ "path": "./resources/build/version/tsconfig.json" },
{ "path": "./resources/build/version/tsconfig.production.json" },
// { "path": "./web/bulk_rendering/tsconfig.json" },
- { "path": "./web/src/tsconfig.all.json" },
- // { "path": "./web/tools/recorder/tsconfig.json" },
- // { "path": "./web/tools/sourcemap-root/tsconfig.json" },
- { "path": "./common/web/lm-message-types/" },
- { "path": "./common/web/lm-worker/" },
- { "path": "./common/web/keyman-version/" },
]
}
\ No newline at end of file
diff --git a/tsconfig.esm.json b/tsconfig.esm.json
index 40c63618c94fd80adfbf4dc2e68bb4b8a1a388d9..de47c84fdeafa7cfbf90c721ac86bd3a0abedd2c 100644
--- a/tsconfig.esm.json
+++ b/tsconfig.esm.json
@@ -23,5 +23,22 @@
{ "path": "./common/web/keyman-version" },
{ "path": "./common/web/types/" },
+
+ { "path": "./common/web/input-processor/tsconfig.json" },
+ { "path": "./common/web/keyboard-processor/tsconfig.json" },
+ { "path": "./common/web/recorder/tsconfig.json" },
+ { "path": "./common/web/sentry-manager/src/tsconfig.json" },
+ { "path": "./common/web/utils/tsconfig.json" },
+
+ { "path": "./common/models/templates/tsconfig.json" },
+ { "path": "./common/models/types/tsconfig.json" },
+ { "path": "./common/models/wordbreakers/tsconfig.json" },
+ { "path": "./common/predictive-text/tsconfig.json" },
+
+ { "path": "./web/src/tsconfig.all.json" },
+ // { "path": "./web/tools/recorder/tsconfig.json" },
+ // { "path": "./web/tools/sourcemap-root/tsconfig.json" },
+ { "path": "./common/web/lm-message-types/" },
+ { "path": "./common/web/lm-worker/" },
]
}
\ No newline at end of file
diff --git a/web/build.sh b/web/build.sh
index f7701259be037293c14d40d02ace2f0796a159ab..35ee1b5ebbe943773eacd1c85205715db3390fe1 100755
--- a/web/build.sh
+++ b/web/build.sh
@@ -1,9 +1,6 @@
#!/usr/bin/env bash
#
# Compile keymanweb and copy compiled javascript and resources to output/embedded folder
-#
-
-set -eu
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
@@ -42,9 +39,9 @@ builder_describe "Builds engine modules for Keyman Engine for Web (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" \
- ":engine/paths Subset used to configure KMW"
-
-# ":app/browser The website-integrating, browser-based version of KMW" \
+ ":engine/paths Subset used to configure KMW" \
+ ":samples Builds all needed resources for the KMW sample-page set" \
+ "--ci+ Set to utilize CI-based test configurations & reporting."
# Possible TODO?
# "upload-symbols Uploads build product to Sentry for error report symbolification. Only defined for $DOC_BUILD_EMBED_WEB" \
@@ -52,10 +49,6 @@ builder_describe "Builds engine modules for Keyman Engine for Web (KMW)." \
builder_describe_outputs \
configure /node_modules
-# build:app/webview build/app/webview/lib/index.js \
-
- # TODO: app/ui linkage.
-
builder_parse "$@"
#### Build action definitions ####
@@ -68,11 +61,9 @@ builder_run_child_actions clean
## Clean actions
-# If a full-on general clean was requested, we can nuke the entire build folder.
-if builder_start_action clean; then
- rm -rf ./build
- builder_finish_action success clean
-fi
+###--- Future tie-in: if #8831 gets accepted, uncomment the next two lines. ---###
+# # If a full-on general clean was requested, we can nuke the entire build folder.
+# builder_run_action clean:project rm -rf ./build
builder_run_child_actions configure
@@ -105,22 +96,24 @@ builder_run_child_actions build:app/webview
# Uses literally everything `engine/` above
builder_run_child_actions build:app/browser
-builder_run_child_actions test
+# Uses app/browser above for engine type-defs
+builder_run_child_actions build:app/ui
-if builder_has_action build:app/browser; then
- builder_warn "Modularization work is not yet complete; consumers may find needed API or components to be missing"
-fi
+# Needs both app/browser and app/ui.
+builder_run_child_actions build:samples
+
+builder_run_child_actions test
if builder_start_action test; then
- ./test.sh :engine
+ TEST_OPTS=
+ if builder_has_option --ci; then
+ TEST_OPTS=--ci
+ fi
+ ./test.sh $TEST_OPTS
builder_finish_action success test
fi
-if builder_has_action build:app/ui; then
- builder_die "Modularization work is not yet complete; builds dependent on this will fail."
-fi
-
### Temporary tie-in for pre-modularization version of the gesture-recognizer
if builder_start_action build:engine/gestures; then
diff --git a/web/ci.sh b/web/ci.sh
index d160cc41902f52065f0527963dc3fcafa44387fb..2f038b90b2ca00e650261424096b7a0e50329101 100755
--- a/web/ci.sh
+++ b/web/ci.sh
@@ -25,6 +25,7 @@ cd "$THIS_SCRIPT_PATH"
S_KEYMAN_COM=
builder_describe "Defines and implements the CI build steps for Keyman Engine for Web (KMW)." \
+ "@/web/src/tools/building/sourcemap-root prepare:s.keyman.com" \
"build" \
"test Runs all unit tests." \
"post-test Runs post-test cleanup. Should be run even if a prior step fails." \
@@ -48,7 +49,9 @@ if builder_start_action build; then
# - clean: make extra-sure that no prior build products exist.
# - also useful when validating this script on a local dev machine!
# - build: then do the ACTUAL build.
- ./build.sh configure clean build
+ # one option:
+ # - --ci: For app/browser, outputs 'release' config filesize profiling logs
+ ./build.sh configure clean build --ci
builder_finish_action success build
fi
@@ -56,15 +59,17 @@ fi
if builder_start_action test; then
# Testing step: run ALL unit tests, including those of the submodules.
- # For only top-level build-product tests, specify the :engine target
- # For all others, specify only the :libraries target
- FLAGS=
-
+ OPTIONS=
if ! builder_is_debug_build; then
- FLAGS=--ci
+ OPTIONS=--ci
fi
- ./test.sh $FLAGS
+ # No --reporter option exists yet for the headless modules.
+
+ $KEYMAN_ROOT/common/web/keyboard-processor/build.sh test $OPTIONS
+ $KEYMAN_ROOT/common/web/input-processor/build.sh test $OPTIONS
+
+ ./build.sh test $OPTIONS
builder_finish_action success test
fi
@@ -115,14 +120,14 @@ if builder_start_action prepare:s.keyman.com; then
echo "FOLDER: $BASE_PUBLISH_FOLDER"
mkdir -p "$BASE_PUBLISH_FOLDER"
- cp -Rf build/app/browser/release/* "$BASE_PUBLISH_FOLDER"
- cp -Rf build/app/resources/* "$BASE_PUBLISH_FOLDER/resources"
- cp -Rf build/app/ui/release/* "$BASE_PUBLISH_FOLDER"
+ # s.keyman.com - release-config only. It's notably smaller, thus far more favorable
+ # for distribution via cloud service.
+ cp -Rf build/publish/release/* "$BASE_PUBLISH_FOLDER"
# Third phase: tweak the sourcemaps
# We can use an alt-mode of Web's sourcemap-root tool for this.
for sourcemap in "$BASE_PUBLISH_FOLDER/"*.map; do
- node build/tools/building/sourcemap-root/index.mjs null "$sourcemap" --sourceRoot "https://s.keyman.com/kmw/engine/$VERSION/src"
+ node "$KEYMAN_ROOT/web/build/tools/building/sourcemap-root/index.mjs" null "$sourcemap" --sourceRoot "https://s.keyman.com/kmw/engine/$VERSION/src"
done
# Actual construction of the PR will be left to CI-config scripting for now.
@@ -161,20 +166,9 @@ if builder_start_action prepare:downloads.keyman.com; then
fi
fi
- pushd build/app/browser/release
- "${COMPRESS_CMD}" $COMPRESS_ADD ../../../../$ZIP *
- cd ..
- "${COMPRESS_CMD}" $COMPRESS_ADD ../../../$ZIP debug
- popd
-
- pushd build/app/resources
- "${COMPRESS_CMD}" $COMPRESS_ADD ../../../$ZIP *
- popd
-
- pushd build/app/ui/release
+ pushd build/publish
+ # Zip both the 'debug' and 'release' configurations together.
"${COMPRESS_CMD}" $COMPRESS_ADD ../../../../$ZIP *
- cd ..
- "${COMPRESS_CMD}" $COMPRESS_ADD ../../../$ZIP debug
popd
# --- Second action artifact - the 'static' folder (hosted user testing on downloads.keyman.com) ---
diff --git a/web/common.inc.sh b/web/common.inc.sh
index 0a615962de2a7bb8cdeda65958f3401295c243df..96a3c61bbd8eccf782d9330bbfdd2130fa9fa072 100644
--- a/web/common.inc.sh
+++ b/web/common.inc.sh
@@ -20,14 +20,66 @@ compile ( ) {
fi
local COMPILE_TARGET="$1"
+ local BUNDLE_FLAG="${2:-}"
tsc -b "${KEYMAN_ROOT}/web/src/$COMPILE_TARGET" -v
- # COMPILE_TARGET entries are all prefixed with `engine`, so remove that.
if [ -f "./build-bundler.js" ]; then
- node "./build-bundler.js"
+ node "./build-bundler.js" "$BUNDLE_FLAG"
# So... tsc does declaration-bundling on its own pretty well, at least for local development.
tsc --emitDeclarationOnly --outFile "${KEYMAN_ROOT}/web/build/$COMPILE_TARGET/lib/index.d.ts" -p "${KEYMAN_ROOT}/web/src/$COMPILE_TARGET"
fi
+}
+
+_copy_dir_if_exists ( ) {
+ local SRC=$1
+ local DST=$2
+
+ if [ -d "$SRC" ]; then
+ cp -rf "$SRC/." "$DST"
+ fi
+}
+
+# Copies top-level build artifacts into common 'debug' and 'release' config folders
+# for use in publishing.
+prepare ( ) {
+ local CHILD_BUILD_ROOT="$KEYMAN_ROOT/web/build/app"
+ local PUBLISH_BUILD_ROOT="$KEYMAN_ROOT/web/build/publish"
+
+ mkdir -p "$PUBLISH_BUILD_ROOT/debug"
+ mkdir -p "$PUBLISH_BUILD_ROOT/release"
+
+ _copy_dir_if_exists "$CHILD_BUILD_ROOT/browser/debug" "$PUBLISH_BUILD_ROOT/debug"
+ _copy_dir_if_exists "$CHILD_BUILD_ROOT/browser/release" "$PUBLISH_BUILD_ROOT/release"
+
+ _copy_dir_if_exists "$CHILD_BUILD_ROOT/resources" "$PUBLISH_BUILD_ROOT/debug"
+ _copy_dir_if_exists "$CHILD_BUILD_ROOT/resources" "$PUBLISH_BUILD_ROOT/release"
+
+ _copy_dir_if_exists "$CHILD_BUILD_ROOT/ui/debug" "$PUBLISH_BUILD_ROOT/debug"
+ _copy_dir_if_exists "$CHILD_BUILD_ROOT/ui/release" "$PUBLISH_BUILD_ROOT/release"
+}
+
+# Runs all headless tests corresponding to the specified target.
+# This should be called from the working directory of a child project's
+# build script.
+#
+# ### Parameters
+#
+# * 1: `product` the folder under src/test/auto/headless containing the
+# child project's tests
+#
+# ### Example
+#
+# ```bash
+# # from engine/osk
+# test-headless osk
+# ```
+test-headless ( ) {
+ TEST_OPTS=
+ if builder_has_option --ci; then
+ TEST_OPTS="--reporter mocha-teamcity-reporter"
+ fi
+
+ mocha --recursive "${KEYMAN_ROOT}/web/src/test/auto/headless/$1" $TEST_OPTS
}
\ No newline at end of file
diff --git a/web/package.json b/web/package.json
index 120f5c350c4dafa6fb2510068570fdbc1cafc92f..21b17525a6701bd4ba8336dbfa613a0af00b08f3 100644
--- a/web/package.json
+++ b/web/package.json
@@ -2,6 +2,10 @@
"name": "keyman",
"description": "Facilitates text input in any language.",
"exports": {
+ "./app/browser": {
+ "types": "./build/app/browser/obj/test-index.d.js",
+ "import": "./build/app/browser/obj/test-index.js"
+ },
"./engine/attachment": {
"types": "./build/engine/attachment/obj/index.d.ts",
"import": "./build/engine/attachment/obj/index.js"
@@ -68,7 +72,6 @@
"@keymanapp/web-sentry-manager": "*",
"@sentry/cli": "2.2.0",
"chai": "^4.3.4",
- "google-closure-compiler-java": "^20200224.0.0",
"karma": "^6.4.1",
"karma-browserstack-launcher": "^1.6.0",
"karma-chai": "^0.1.0",
@@ -103,9 +106,11 @@
"@keymanapp/lexical-model-layer": "*",
"@keymanapp/models-types": "*",
"@keymanapp/recorder-core": "*",
+ "@keymanapp/tslib": "*",
"@keymanapp/web-utils": "*",
"@types/node": "^11.9.4",
- "eventemitter3": "^4.0.0"
+ "eventemitter3": "^4.0.0",
+ "tslib": "^2.5.2"
},
"type": "module"
}
diff --git a/web/src/app/browser/build-bundler.js b/web/src/app/browser/build-bundler.js
index 3d855f92ed26fa90cca23c87ecfa472368d7260c..cc81f7c402c301ae3739f5c6e928fca8d7603d7b 100644
--- a/web/src/app/browser/build-bundler.js
+++ b/web/src/app/browser/build-bundler.js
@@ -6,8 +6,28 @@
*/
import esbuild from 'esbuild';
-import { spawn } from 'child_process';
import fs from 'fs';
+import { determineNeededDowncompileHelpers, buildTslibTreeshaker } from '@keymanapp/tslib/esbuild-tools';
+
+let EMIT_FILESIZE_PROFILE = false;
+
+if(process.argv.length > 2) {
+ for(let i = 2; i < process.argv.length; i++) {
+ const arg = process.argv[i];
+
+ switch(arg) {
+ case '':
+ break;
+ case '--ci':
+ EMIT_FILESIZE_PROFILE=true
+ break;
+ // May add other options if desired in the future.
+ default:
+ console.error("Invalid command-line option set for script; only --ci is permitted.");
+ process.exit(1);
+ }
+ }
+}
/*
* Refer to https://github.com/microsoft/TypeScript/issues/13721#issuecomment-307259227 -
@@ -30,7 +50,10 @@ let es5ClassAnnotationAsPurePlugin = {
}
}
-await esbuild.build({
+const commonConfig = {
+ alias: {
+ 'tslib': '@keymanapp/tslib'
+ },
bundle: true,
sourcemap: true,
format: "iife",
@@ -43,38 +66,46 @@ await esbuild.build({
target: "es5",
treeShaking: true,
tsconfig: './tsconfig.json'
-});
+};
-await esbuild.build({
- bundle: true,
- sourcemap: true,
+// Prepare the needed setup for `tslib` treeshaking.
+const unusedHelpers = await determineNeededDowncompileHelpers(commonConfig, /worker-main\.wrapped(?:\.min)?\.js/);
+commonConfig.plugins = [buildTslibTreeshaker(unusedHelpers), ...commonConfig.plugins];
+
+// And now... do the actual builds.
+await esbuild.build(commonConfig);
+
+let result = await esbuild.build({
+ ...commonConfig,
minifyWhitespace: true,
minifySyntax: true,
- minifyIdentifiers: false,
+ minifyIdentifiers: true,
format: "iife",
nodePaths: ['../../../../node_modules'],
entryPoints: {
'index': '../../../build/app/browser/obj/release-main.js',
},
outfile: '../../../build/app/browser/release/keymanweb.js',
- plugins: [ es5ClassAnnotationAsPurePlugin ],
- target: "es5",
- treeShaking: true,
- tsconfig: './tsconfig.json'
+ // Enables source-file output size profiling!
+ metafile: true
});
+let filesizeProfile = await esbuild.analyzeMetafile(result.metafile, { verbose: true });
+fs.writeFileSync('../../../build/app/browser/filesize-profile.log', `
+// Minified Keyman Engine for Web ('app/browser' target), filesize profile
+${filesizeProfile}
+`);
+if(EMIT_FILESIZE_PROFILE) {
+ // Profiles the sourcecode!
+ console.log(filesizeProfile);
+}
+
await esbuild.build({
- bundle: true,
- sourcemap: true,
- minify: false,
+ ...commonConfig,
format: "esm",
- nodePaths: ['../../../../node_modules'],
entryPoints: {
'index': '../../../build/app/browser/obj/test-index.js',
},
outfile: '../../../build/app/browser/lib/index.mjs',
- plugins: [ es5ClassAnnotationAsPurePlugin ],
- target: "es5",
- treeShaking: true,
tsconfig: './tsconfig.json'
});
\ No newline at end of file
diff --git a/web/src/app/browser/build.sh b/web/src/app/browser/build.sh
index e5340bdff86d8fa7417955a533f017cd03d887cd..2cbc51fae5a070028fe3eeba113b5ca88756068d 100755
--- a/web/src/app/browser/build.sh
+++ b/web/src/app/browser/build.sh
@@ -1,8 +1,4 @@
#!/usr/bin/env bash
-#
-
-# set -x
-set -eu
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
@@ -10,15 +6,13 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../../resources/build/build-utils.sh"
## END STANDARD BUILD SCRIPT INCLUDE
+SUBPROJECT_NAME=app/browser
+. "$KEYMAN_ROOT/web/common.inc.sh"
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
# This script runs from its own folder
cd "$THIS_SCRIPT_PATH"
-# Imports common Web build-script definitions & functions
-SUBPROJECT_NAME=app/browser
-. "$KEYMAN_ROOT/web/common.inc.sh"
-
# ################################ Main script ################################
builder_describe "Builds the Keyman Engine for Web's website-integrating version for use in non-puppeted browsers." \
@@ -27,7 +21,8 @@ builder_describe "Builds the Keyman Engine for Web's website-integrating version
"clean" \
"configure" \
"build" \
- "test"
+ "test" \
+ "--ci+ Set to utilize CI-based test configurations & reporting."
# Possible TODO?s
# "upload-symbols Uploads build product to Sentry for error report symbolification. Only defined for $DOC_BUILD_EMBED_WEB" \
@@ -45,27 +40,28 @@ builder_describe_outputs \
#### Build action definitions ####
-if builder_start_action configure; then
- verify_npm_setup
-
- builder_finish_action success configure
-fi
-
-if builder_start_action clean; then
- rm -rf "$KEYMAN_ROOT/web/build/$SUBPROJECT_NAME"
- builder_finish_action success clean
-fi
-
-if builder_start_action build; then
- compile $SUBPROJECT_NAME
+compile_and_copy() {
+ local COMPILE_FLAGS=
+ if builder_has_option --ci; then
+ COMPILE_FLAGS=--ci
+ fi
+ compile $SUBPROJECT_NAME $COMPILE_FLAGS
mkdir -p "$KEYMAN_ROOT/web/build/app/resources/osk"
cp -R "$KEYMAN_ROOT/web/src/resources/osk/." "$KEYMAN_ROOT/web/build/app/resources/osk/"
- builder_finish_action success build
-fi
+ # Update the build/publish copy of our build artifacts
+ prepare
+
+ local PROFILE_DEST="$KEYMAN_ROOT/web/build/profiling/"
+ mkdir -p "$PROFILE_DEST"
+ cp "$KEYMAN_ROOT/web/build/app/browser/filesize-profile.log" "$PROFILE_DEST/web-engine-filesize.log"
+ cp "$KEYMAN_ROOT/common/web/lm-worker/build/filesize-profile.log" "$PROFILE_DEST/lm-worker-filesize.log"
+}
+
+builder_run_action configure verify_npm_setup
+builder_run_action clean rm -rf "$KEYMAN_ROOT/web/build/$SUBPROJECT_NAME"
+builder_run_action build compile_and_copy
-if builder_start_action test; then
- # No headless tests of yet.
- builder_finish_action success test
-fi
\ No newline at end of file
+# No headless tests for this child project. Currently, DOM-based unit &
+# integrated tests are run solely by the top-level $KEYMAN_ROOT/web project.
\ No newline at end of file
diff --git a/web/src/app/browser/src/beepHandler.ts b/web/src/app/browser/src/beepHandler.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5fae87425777442d7faee91df9b092672d65222d
--- /dev/null
+++ b/web/src/app/browser/src/beepHandler.ts
@@ -0,0 +1,87 @@
+import { type KeyboardInterface } from '@keymanapp/keyboard-processor';
+import { DesignIFrame, OutputTarget } from 'keyman/engine/element-wrappers';
+
+// Utility object used to handle beep (keyboard error response) operations.
+class BeepData {
+ e: HTMLElement;
+ c: string;
+
+ constructor(e: HTMLElement) {
+ this.e = e;
+ this.c = e.style.backgroundColor;
+ }
+
+ reset(): void {
+ this.e.style.backgroundColor = this.c;
+ }
+}
+
+export class BeepHandler {
+ readonly keyboardInterface: KeyboardInterface;
+
+ constructor(keyboardInterface: KeyboardInterface) {
+ this.keyboardInterface = keyboardInterface;
+ }
+
+ _BeepObjects: BeepData[] = []; // BeepObjects - maintains a list of active 'beep' visual feedback elements
+ _BeepTimeout: number = 0; // BeepTimeout - a flag indicating if there is an active 'beep'.
+ // Set to 1 if there is an active 'beep', otherwise leave as '0'.
+ /**
+ * Function beep KB (DOM-side implementation)
+ * Scope Public
+ * @param {Object} Pelem element to flash
+ * Description Flash body as substitute for audible beep; notify embedded device to vibrate
+ */
+ beep(outputTarget: OutputTarget) {
+ if(!(outputTarget instanceof OutputTarget)) {
+ return;
+ }
+
+ // All code after this point is DOM-based, triggered by the beep.
+ var Pelem: HTMLElement = outputTarget.getElement();
+ if(outputTarget instanceof DesignIFrame) {
+ Pelem = outputTarget.docRoot; // I1446 - beep sometimes fails to flash when using OSK and rich control
+ }
+
+ if(!Pelem) {
+ return; // There's no way to signal a 'beep' to null, so just cut everything short.
+ }
+
+ if(!Pelem.style || typeof(Pelem.style.backgroundColor)=='undefined') {
+ return;
+ }
+
+ for(var Lbo=0; Lbo {
+ this.keyboardInterface.resetContextCache();
+
+ var Lbo;
+ this._BeepTimeout = 0;
+ for(Lbo=0;Lbo {
const baseReport = super.debugReport();
baseReport.attachType = this.attachType;
diff --git a/web/src/app/browser/src/context/focusAssistant.ts b/web/src/app/browser/src/context/focusAssistant.ts
index bda60dee5dd5fdbbd3d1a1a3b5c6009bc52fb719..c0ae8a6bdb1b7caa8ead25b222cf655fbc5bd470 100644
--- a/web/src/app/browser/src/context/focusAssistant.ts
+++ b/web/src/app/browser/src/context/focusAssistant.ts
@@ -41,6 +41,18 @@ interface EventMap {
export class FocusAssistant extends EventEmitter {
private _maintainingFocus: boolean = false; // ActivatingKeymanWebUI - Does the OSK have active focus / an active interaction?
+ /**
+ * Returns `true` only when the active target has an active `forceScroll` method/state, which deliberately
+ * blurs and then refocuses the same element in order to force a browser-default page scroll to keep the
+ * element and text-caret visible.
+ */
+ readonly isTargetForcingScroll: () => boolean;
+
+ constructor(isTargetForcingScroll: () => boolean) {
+ super();
+ this.isTargetForcingScroll = isTargetForcingScroll;
+ }
+
/*
* Long-term idea here: about all of the relevant OSK events that would interact with this have "enter" and
* "leave" variants - we could take a stack of `Promise`s. On a `Promise` fulfillment, remove it from the
@@ -109,16 +121,6 @@ export class FocusAssistant extends EventEmitter {
*/
_IgnoreNextSelChange = 0;
- /**
- * JH (2023-04-24): Set only by the OutputTarget `forceScroll` method, which deliberately blurs and
- * then refocuses the same element in order to force a browser-default page scroll to keep the element
- * visible.
- *
- * While it feels like this should be possible to merge with the other class fields in some form... it
- * doesn't seem as safe to do on first glance.
- */
- _IgnoreBlurFocus: boolean = false;
-
/**
* Is used as a time-delayed async `restoringFocus` or `maintainingFocus` - could be modeled decently as a Promise.
* Probably more the latter, as it's a touch-OSK interaction like the other `maintainingFocus` cases.
diff --git a/web/src/app/browser/src/context/pageIntegrationHandlers.ts b/web/src/app/browser/src/context/pageIntegrationHandlers.ts
index 98367eb4220b7645d795b524a81a33bd7e18ca30..30b91cb102e654dd46da183407f2176fb389efe8 100644
--- a/web/src/app/browser/src/context/pageIntegrationHandlers.ts
+++ b/web/src/app/browser/src/context/pageIntegrationHandlers.ts
@@ -2,6 +2,7 @@ import { DomEventTracker } from 'keyman/engine/events';
import KeymanEngine from "../keymanEngine.js";
import { FocusAssistant } from './focusAssistant.js';
+import { RotationProcessor } from '../utils/rotationProcessor.js';
// Note: in the future, it'd probably be best to have an instance per iframe window as
// well as the top-level window. This was not done in or before KMW 16.0 though, so
@@ -30,11 +31,35 @@ export class PageIntegrationHandlers {
*/
private deactivateOnScroll: boolean;
+ /**
+ * This component should only ever be applied to a base page: we need the ability to add 'scroll
+ * space' on touch devices so that the OSK doesn't block the bottom.
+ */
+ private mobilePageTrailer: HTMLDivElement;
+
+ private rotationProcessor: RotationProcessor;
+
constructor(window: Window, engine: KeymanEngine) {
this.window = window;
this.engine = engine;
this.attachHandlers();
+
+ if(engine.config.hostDevice.touchable) {
+ this.buildPageTrailer();
+
+ this.rotationProcessor = new RotationProcessor(this.engine);
+ this.rotationProcessor.init();
+ }
+ }
+
+ private buildPageTrailer() {
+ // Add a blank DIV to the bottom of the page to allow the bottom of the page to be shown
+ const dTrailer = this.mobilePageTrailer = document.createElement('div');
+ const ds=dTrailer.style;
+ ds.width='100%';
+ ds.height=(screen.width/2)+'px'; // ... interesting choice, but okay.
+ document.body.appendChild(dTrailer);
}
private get focusAssistant(): FocusAssistant {
@@ -42,8 +67,10 @@ export class PageIntegrationHandlers {
}
private suppressFocusCheck: (e: FocusEvent) => boolean = (e) => {
- if(this.focusAssistant._IgnoreBlurFocus) {
- // Prevent triggering other blur-handling events (as possible)
+ if(this.focusAssistant.isTargetForcingScroll()) {
+ // Prevent triggering other blur-handling events (as possible) - this blur
+ // is programmatic in order to force a browser scroll-position update.
+ // All focus changes should be prevented at this time.
e.stopPropagation();
e.cancelBubble = true;
}
@@ -177,8 +204,7 @@ export class PageIntegrationHandlers {
eventTracker.attachDOMEvent(window, 'load', this._WindowLoad, false);
eventTracker.attachDOMEvent(window, 'unload', this._WindowUnload,false);
- // TODO: Hotkey module stuff. Is not yet modularized.
- // eventTracker.attachDOMEvent(document, 'keyup', this.engine.hotkeyManager._Process, false);
+ eventTracker.attachDOMEvent(document, 'keyup', this.engine.hotkeyManager._Process, false);
}
public shutdown() {
@@ -198,12 +224,13 @@ export class PageIntegrationHandlers {
eventTracker.detachDOMEvent(docBody, 'touchstart', this.touchStartActivationHandler,false);
eventTracker.detachDOMEvent(docBody, 'touchmove', this.touchMoveActivationHandler, false);
eventTracker.detachDOMEvent(docBody, 'touchend', this.touchEndActivationHandler, false);
+
+ this.mobilePageTrailer?.parentElement.removeChild(this.mobilePageTrailer);
}
eventTracker.detachDOMEvent(window, 'load', this._WindowLoad, false);
eventTracker.detachDOMEvent(window, 'unload', this._WindowUnload,false);
- // TODO: Hotkey module stuff.
- // eventTracker.detachDOMEvent(document, 'keyup', this.engine.hotkeyManager._Process, false);
+ eventTracker.detachDOMEvent(document, 'keyup', this.engine.hotkeyManager._Process, false);
}
}
\ No newline at end of file
diff --git a/web/src/app/browser/src/contextManager.ts b/web/src/app/browser/src/contextManager.ts
index 226f62b4c013862303f472fcc4b19ba2ee4a1ab4..42e7edd729f291c3bbef37a8e6ec00dee3d0b6e3 100644
--- a/web/src/app/browser/src/contextManager.ts
+++ b/web/src/app/browser/src/contextManager.ts
@@ -12,7 +12,7 @@ import {
import { BrowserConfiguration } from './configuration.js';
import { FocusAssistant } from './context/focusAssistant.js';
-interface KeyboardCookie {
+export interface KeyboardCookie {
current: string;
}
@@ -43,7 +43,7 @@ function _SetTargDir(Ptarg: HTMLElement, activeKeyboard: Keyboard) {
export default class ContextManager extends ContextManagerBase {
private _activeKeyboard: {keyboard: Keyboard, metadata: KeyboardStub};
private cookieManager = new CookieSerializer('KeymanWeb_Keyboard');
- readonly focusAssistant = new FocusAssistant();
+ readonly focusAssistant = new FocusAssistant(() => this.activeTarget?.isForcingScroll());
readonly page: PageContextAttachment;
private mostRecentTarget: OutputTarget;
private currentTarget: OutputTarget;
@@ -183,8 +183,15 @@ export default class ContextManager extends ContextManagerBase) {
+ public setActiveTarget(target: OutputTarget, sendEvents: boolean) {
const previousTarget = this.mostRecentTarget;
const originalTarget = this.activeTarget; // may differ, depending on focus state.
+ if(target == originalTarget) {
+ /**
+ * If it's already active, we should cancel early.
+ *
+ * The #1 reason - we don't want .resetContext calls in this scenario.
+ * In particular, moving the caret or setting the selection range of an
+ * or