From ff4e7c98f3a608c5fdfd636cb593157e49066456 Mon Sep 17 00:00:00 2001 From: Sabine Date: Mon, 25 Aug 2025 10:37:08 +0200 Subject: [PATCH 1/4] feat(developer): solve merge conflicts --- .../src/kmc-convert/src/converter-messages.ts | 9 +- developer/src/kmc-convert/src/converter.ts | 4 +- .../keylayout-to-kmn-converter.ts | 65 +++++++----- .../src/keylayout-to-kmn/kmn-file-writer.ts | 44 ++++----- .../kmc-convert/test/kmn-file-writer.tests.ts | 98 ++++--------------- 5 files changed, 84 insertions(+), 136 deletions(-) diff --git a/developer/src/kmc-convert/src/converter-messages.ts b/developer/src/kmc-convert/src/converter-messages.ts index 8e22f4d49e..bc411300cc 100644 --- a/developer/src/kmc-convert/src/converter-messages.ts +++ b/developer/src/kmc-convert/src/converter-messages.ts @@ -6,7 +6,7 @@ import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m, CompilerMessageDef as def } from '@keymanapp/developer-utils'; const Namespace = CompilerErrorNamespace.Converter; - const SevInfo = CompilerErrorSeverity.Info | Namespace; +const SevInfo = CompilerErrorSeverity.Info | Namespace; // const SevHint = CompilerErrorSeverity.Hint | Namespace; // const SevWarn = CompilerErrorSeverity.Warn | Namespace; const SevError = CompilerErrorSeverity.Error | Namespace; @@ -53,8 +53,11 @@ export class ConverterMessages { ); static INFO_UnsupportedCharactersDetected = SevInfo | 0x0007; - static Info_UnsupportedCharactersDetected = (o: { inputFilename: string, keymap_index: string, key: string,KeyName:string, output: string; }) => m( - this.INFO_UnsupportedCharactersDetected, `INFO: Input file ${def(o.inputFilename)} contains unsupported character '${def(o.output)}' at keyMap index ${def(o.keymap_index)} on Keycode ${def(o.key)} (${def(o.KeyName)})` + static Info_UnsupportedCharactersDetected = (o: { inputFilename: string, keymap_index: string, key: string, KeyName: string, output: string; }) => m( + this.INFO_UnsupportedCharactersDetected, `INFO: Input file ${def(o.inputFilename)} + contains unsupported character '${def(o.output)} + ' at keyMap index ${def(o.keymap_index)} + on Keycode ${def(o.key)} (${def(o.KeyName)})` ); static ERROR_InvalidFile = SevError | 0x0008; diff --git a/developer/src/kmc-convert/src/converter.ts b/developer/src/kmc-convert/src/converter.ts index 2e9911073a..646d0de15f 100644 --- a/developer/src/kmc-convert/src/converter.ts +++ b/developer/src/kmc-convert/src/converter.ts @@ -81,9 +81,9 @@ export class Converter implements KeymanCompiler { } const converter = new ConverterClass(this.callbacks, converterOptions); - const artifacts = await converter.run(inputFilename, outputFilename); + const result = await converter.run(inputFilename, outputFilename); // Note: any subsequent errors in conversion will have been reported by the converter - return artifacts ? { artifacts } : null; + return result ? result : null; } /** diff --git a/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-to-kmn-converter.ts b/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-to-kmn-converter.ts index 49be9e039e..8463b1a2a8 100644 --- a/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-to-kmn-converter.ts +++ b/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-to-kmn-converter.ts @@ -7,19 +7,34 @@ * */ -import { CompilerCallbacks, CompilerOptions } from "@keymanapp/developer-utils"; -import { ConverterToKmnArtifacts } from "../converter-artifacts.js"; +import { CompilerCallbacks, CompilerOptions, KeymanCompilerResult, } from "@keymanapp/developer-utils"; import { KmnFileWriter } from './kmn-file-writer.js'; import { KeylayoutFileReader } from './keylayout-file-reader.js'; import { ConverterMessages } from '../converter-messages.js'; -import { KeylayoutXMLSourceFile } from '@keymanapp/developer-utils'; +import { ConverterArtifacts } from "../converter-artifacts.js"; +import { ConverterToKmnArtifacts } from "../converter-artifacts.js"; +import { KeylayoutXMLSourceFile } from '../../../common/web/utils/src/types/keylayout/keylayout-xml.js'; + + +export interface ConverterResult extends KeymanCompilerResult { + /** + * Internal in-memory build artifacts from a successful compilation. Caller + * can write these to disk with {@link Converter.write} + */ + artifacts: ConverterArtifacts; +}; + +export interface ConverterToKmnResult extends ConverterResult { + /** + * Internal in-memory build artifacts from a successful compilation. Caller + * can write these to disk with {@link Converter.write} + */ + artifacts: ConverterToKmnArtifacts; +}; + + + -/** - * Object holding all important data for the conversion between - * input (*.keylayout) format and output (*.kmn) format. - * It contains input and output filenames, an array of all used modifiers - * and all preprocessed key rules for up to 3 key/modifier combinations. - */ export interface ProcesData { keylayout_filename: string, kmn_filename: string, @@ -51,12 +66,12 @@ export interface ActionStateOutput { */ export function find_usedKeysCount(data: any, pos: number): number { - let usedKeyCount = KeylayoutToKmnConverter.MAX_KEY_COUNT; - if (data.keyboard.keyMapSet[0].keyMap[pos].key.length < usedKeyCount ) { - // set the max to n-1 (keys are zero indexed ) - usedKeyCount = data.keyboard.keyMapSet[0].keyMap[pos].key.length - 1; + let usedKeyCount = KeylayoutToKmnConverter.MAX_KEY_COUNT; + if (data.keyboard.keyMapSet[0].keyMap[pos].key.length < usedKeyCount) { + // set max to n-1 (keys are zero indexed ) + usedKeyCount = data.keyboard.keyMapSet[0].keyMap[pos].key.length - 1; } - return usedKeyCount ; + return usedKeyCount; } export class KeylayoutToKmnConverter { @@ -83,8 +98,7 @@ export class KeylayoutToKmnConverter { * @param outputFilename the resulting keyman .kmn-file * @return null on success */ - async run(inputFilename: string, outputFilename?: string): Promise { - + async run(inputFilename: string, outputFilename?: string): Promise { if (!inputFilename) { this.callbacks.reportMessage(ConverterMessages.Error_FileNotFound({ inputFilename })); @@ -123,16 +137,21 @@ export class KeylayoutToKmnConverter { const kmnFileWriter = new KmnFileWriter(this.callbacks, this.options); - const out_text_ok: boolean = kmnFileWriter.write(outArray); - if (!out_text_ok) { + // write to object/ConverterToKmnResult + const out_Uint8: Uint8Array = kmnFileWriter.write(outArray); + const Result_toBeReturned: ConverterToKmnResult = { + artifacts: { + kmn: { data: out_Uint8, filename: outputFilename } + } + }; + + if (!out_Uint8) { this.callbacks.reportMessage(ConverterMessages.Error_UnableToWrite({ outputFilename })); return null; } - - return null; + return Result_toBeReturned; } - /** * @brief member function to read filename and behaviour of a json object into a ProcesData * @param jsonObj containing filename, behaviour and rules of a json object @@ -148,10 +167,8 @@ export class KeylayoutToKmnConverter { arrayOf_Modifiers: [], arrayOf_Rules: [] }; -//_S2 do I need to "validate again here?" - if ((jsonObj !== null) && (jsonObj.hasOwnProperty("keyboard"))) { - //if ((jsonObj !== null) ) { + if ((jsonObj !== null) && (jsonObj.hasOwnProperty("keyboard"))) { data_object.keylayout_filename = outputfilename.replace(/\.kmn$/, '.keylayout'); data_object.kmn_filename = outputfilename; data_object.arrayOf_Modifiers = modifierBehavior; // ukelele uses behaviours e.g. 18 modifiersCombinations in 8 KeyMapSelect(behaviors) diff --git a/developer/src/kmc-convert/src/keylayout-to-kmn/kmn-file-writer.ts b/developer/src/kmc-convert/src/keylayout-to-kmn/kmn-file-writer.ts index c00330e5d1..31ea2fdc85 100644 --- a/developer/src/kmc-convert/src/keylayout-to-kmn/kmn-file-writer.ts +++ b/developer/src/kmc-convert/src/keylayout-to-kmn/kmn-file-writer.ts @@ -23,12 +23,12 @@ export class KmnFileWriter { * @param outputfilename the file that will be written; if no outputfilename is given an outputfilename will be created from data_ukelele.keylayout_filename * @return true if data has been written; false if not */ - public write(data_ukelele: ProcesData): boolean { + public writeToFile(data_ukelele: ProcesData): boolean { let data: string = "\n"; // add top part of kmn file: STORES - data += this.writeData_Stores(data_ukelele); + data += this.write_KmnFileHeader(data_ukelele); // add bottom part of kmn file: RULES data += this.writeData_Rules(data_ukelele); @@ -37,33 +37,21 @@ export class KmnFileWriter { this.callbacks.fs.writeFileSync(data_ukelele.kmn_filename, new TextEncoder().encode(data)); return true; } catch (err) { - this.callbacks.reportMessage(ConverterMessages.Error_OutputFilenameIsRequired()); + this.callbacks.reportMessage(ConverterMessages.Error_UnableToWrite({outputFilename: data_ukelele.kmn_filename})); return false; } } - public writeToString(data_ukelele: ProcesData): string { + /** + * @brief member function to write data from object to a Uint8Array + * @param data_ukelele the array holding all keyboard data + * @return a Uint8Array holding data + */ + public write(data_ukelele: ProcesData): Uint8Array { let data: string = "\n"; // add top part of kmn file: STORES - data += this.writeData_Stores(data_ukelele); - - // add bottom part of kmn file: RULES - data += this.writeData_Rules(data_ukelele); - - try { - return data; - } catch (err) { - this.callbacks.reportMessage(ConverterMessages.Error_OutputFilenameIsRequired()); - return null; - } - } - - public writeToUint8Array(data_ukelele: ProcesData): Uint8Array { - let data: string = "\n"; - - // add top part of kmn file: STORES - data += this.writeData_Stores(data_ukelele); + data += this.write_KmnFileHeader(data_ukelele); // add bottom part of kmn file: RULES data += this.writeData_Rules(data_ukelele); @@ -71,17 +59,17 @@ export class KmnFileWriter { try { return new TextEncoder().encode(data); } catch (err) { - this.callbacks.reportMessage(ConverterMessages.Error_OutputFilenameIsRequired()); + this.callbacks.reportMessage(ConverterMessages.Error_UnableToWrite({outputFilename: data_ukelele.kmn_filename})); return null; } } /** - * @brief member function to create data for stores that will be printed to the resulting kmn file + * @brief member function to create data for the header (stores) that will be printed to the resulting kmn file * @param data_ukelele an object containing all data read from a .keylayout file * @return string - all stores to be printed */ - public writeData_Stores(data_ukelele: ProcesData): string { + public write_KmnFileHeader(data_ukelele: ProcesData): string { let data: string = ""; @@ -114,6 +102,9 @@ export class KmnFileWriter { let data: string = ""; // filter array of all rules and remove duplicates + // during the process of creating Rule[], duplicate rules might occur + // (e.g. when in a keylayout file the same modifiers occur in several behaviors thus producing the same rules). + // This is to filter out those duplicate Rule objects const unique_data_Rules: Rule[] = data_ukelele.arrayOf_Rules.filter((curr) => { return (!(curr.output === new TextEncoder().encode("") || curr.output === undefined) && (curr.key !== "") @@ -416,7 +407,8 @@ export class KmnFileWriter { /** * @brief member function to review rules for acceptable modifiers, duplicate or ambiguous rules and return an array containing possible warnings. - * Definition of comparisons e.g. 1-1, 2-4, 6-6 + * Keyman can not handle duplicate rules so we need to make sure a rule is written only once by either omitting a duplicate rule or commenting out an ambiguous rule. + * Omitting rules and definition of comparisons e.g. 1-1, 2-4, 6-6 * see https://docs.google.com/document/d/12J3NGO6RxIthCpZDTR8FYSRjiMgXJDLwPY2z9xqKzJ0/edit?tab=t.0#heading=h.pcz8rjyrl5ug * @param rule : Rule[] - an array of all rules * @param index the index of a rule in array[rule] diff --git a/developer/src/kmc-convert/test/kmn-file-writer.tests.ts b/developer/src/kmc-convert/test/kmn-file-writer.tests.ts index a7ea37b70f..806558cb57 100644 --- a/developer/src/kmc-convert/test/kmn-file-writer.tests.ts +++ b/developer/src/kmc-convert/test/kmn-file-writer.tests.ts @@ -22,6 +22,7 @@ describe('KmnFileWriter', function () { compilerTestCallbacks.clear(); }); + describe("write() ", function () { const inputFilename = makePathToFixture('../data/Test.keylayout'); const sut = new KeylayoutToKmnConverter(compilerTestCallbacks, compilerTestOptions); @@ -30,77 +31,12 @@ describe('KmnFileWriter', function () { const read = sut_r.read(inputFilename); const converted = sut.convert_bound.convert(read, inputFilename.replace(/\.keylayout$/, '.kmn')); - // empty ProcesData from unavailable file name - const inputFilename_unavailable = makePathToFixture('../data/X.keylayout'); - const read_unavailable = sut_r.read(inputFilename_unavailable); - const converted_unavailable = sut.convert_bound.convert(read_unavailable, inputFilename_unavailable.replace(/\.keylayout$/, '.kmn')); - - it('write() should return true (no error) if written', async function () { - const result = sut_w.write(converted); - assert.isTrue(result); - }); - - it('write() should return false if no inputfile', async function () { - const result = sut_w.write(converted_unavailable); - assert.isFalse(result); - }); - - }); - - describe("writeToString() ", function () { - const inputFilename = makePathToFixture('../data/Test.keylayout'); - const sut = new KeylayoutToKmnConverter(compilerTestCallbacks, compilerTestOptions); - const sut_r = new KeylayoutFileReader(compilerTestCallbacks); - const sut_w = new KmnFileWriter(compilerTestCallbacks, compilerTestOptions); - const read = sut_r.read(inputFilename); - const converted = sut.convert_bound.convert(read, inputFilename.replace(/\.keylayout$/, '.kmn')); - - const out_expected_first: string = "c ..................................................................................................................\n" - + "c ..................................................................................................................\n" - + "c Keyman keyboard generated by kmn-convert version: " + KEYMAN_VERSION.VERSION + "\n" - + "c from Ukelele file: "; - - const out_expected_last: string = "\n" - + "c ..................................................................................................................\n" - + "c ..................................................................................................................\n" - + "\n" - + "store(&TARGETS) 'desktop'\n" - + "\n" - + "begin Unicode > use(main)\n\n" - + "group(main) using keys\n\n" - + "\n"; - - // empty ProcesData from unavailable file name - const inputFilename_unavailable = makePathToFixture('../data/X.keylayout'); - const read_unavailable = sut_r.read(inputFilename_unavailable); - const converted_unavailable = sut.convert_bound.convert(read_unavailable, inputFilename_unavailable.replace(/\.keylayout$/, '.kmn')); - - it('writeToString() should return result', async function () { - const result = sut_w.writeToString(converted); - assert.isNotNull(result); - }); - - it('writeToString() should return header in case of missing inputfile', async function () { - const result = sut_w.writeToString(converted_unavailable); - assert.equal(result, ("\n" + out_expected_first + out_expected_last)); - }); - }); - - describe("writeToUint8Array() ", function () { - const inputFilename = makePathToFixture('../data/Test.keylayout'); - const sut = new KeylayoutToKmnConverter(compilerTestCallbacks, compilerTestOptions); - const sut_r = new KeylayoutFileReader(compilerTestCallbacks); - const sut_w = new KmnFileWriter(compilerTestCallbacks, compilerTestOptions); - const read = sut_r.read(inputFilename); - const converted = sut.convert_bound.convert(read, inputFilename.replace(/\.keylayout$/, '.kmn')); - - const out_expected_first: string = + const out_expected: string = "c ..................................................................................................................\n" + "c ..................................................................................................................\n" + "c Keyman keyboard generated by kmn-convert version: " + KEYMAN_VERSION.VERSION + "\n" - + "c from Ukelele file: "; - - const out_expected_last: string = "\n" + + "c from Ukelele file: " + + "\n" + "c ..................................................................................................................\n" + "c ..................................................................................................................\n" + "\n" @@ -115,13 +51,13 @@ describe('KmnFileWriter', function () { const inputFilename_unavailable = makePathToFixture('../data/X.keylayout'); const read_unavailable = sut_r.read(inputFilename_unavailable); const converted_unavailable = sut.convert_bound.convert(read_unavailable, inputFilename_unavailable.replace(/\.keylayout$/, '.kmn')); - it('writeToUint8Array() should return header in case of missing inputfile', async function () { - const result = sut_w.writeToUint8Array(converted_unavailable); - assert.equal(new TextDecoder().decode(result), ("\n" + out_expected_first + out_expected_last)); + it('write() should return header in case of missing inputfile', async function () { + const result = sut_w.write(converted_unavailable); + assert.equal(new TextDecoder().decode(result), ("\n" + out_expected)); }); - it('writeToUint8Array() should return result', async function () { - const result = sut_w.writeToUint8Array(converted); + it('write() should return result', async function () { + const result = sut_w.write(converted); assert.isNotNull(result); }); }); @@ -151,7 +87,7 @@ describe('KmnFileWriter', function () { }); - describe("writeData_Stores() ", function () { + describe("write_KmnFileHeader() ", function () { const sut = new KeylayoutToKmnConverter(compilerTestCallbacks, compilerTestOptions); const sut_r = new KeylayoutFileReader(compilerTestCallbacks); const sut_w = new KmnFileWriter(compilerTestCallbacks, compilerTestOptions); @@ -186,18 +122,18 @@ describe('KmnFileWriter', function () { + "group(main) using keys\n\n" + "\n"; - it(('writeData_Stores should return store text with filename ').padEnd(62, " ") + 'on correct input', async function () { - const written_correctName = sut_w.writeData_Stores(converted); + it(('write_KmnFileHeader should return store text with filename ').padEnd(62, " ") + 'on correct input', async function () { + const written_correctName = sut_w.write_KmnFileHeader(converted); assert.equal(written_correctName, (out_expected_first + converted.keylayout_filename + out_expected_last)); }); - it(('writeData_Stores should return store text without filename ').padEnd(62, " ") + 'on empty input', async function () { - const written_emptyName = sut_w.writeData_Stores(converted_empty); + it(('write_KmnFileHeader should return store text without filename ').padEnd(62, " ") + 'on empty input', async function () { + const written_emptyName = sut_w.write_KmnFileHeader(converted_empty); assert.equal(written_emptyName, (out_expected_first + out_expected_last)); }); - it(('writeData_Stores should return store text without filename ').padEnd(62, " ") + 'on only filename as input', async function () { - const written_onlyName = sut_w.writeData_Stores(converted_unavailable); + it(('write_KmnFileHeader should return store text without filename ').padEnd(62, " ") + 'on only filename as input', async function () { + const written_onlyName = sut_w.write_KmnFileHeader(converted_unavailable); assert.equal(written_onlyName, (out_expected_first + converted_unavailable.keylayout_filename + out_expected_last)); }); }); @@ -502,7 +438,7 @@ describe('KmnFileWriter', function () { }); }); - describe('write form intermediate data array', function () { + describe('write from intermediate data array', function () { const sut_w = new KmnFileWriter(compilerTestCallbacks, compilerTestOptions); [ [ From 57c01ae0386db61d7fcbf7ad9553e1bdcabbf995 Mon Sep 17 00:00:00 2001 From: Sabine Date: Mon, 25 Aug 2025 10:41:02 +0200 Subject: [PATCH 2/4] remove unused function --- .../src/keylayout-to-kmn/kmn-file-writer.ts | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/developer/src/kmc-convert/src/keylayout-to-kmn/kmn-file-writer.ts b/developer/src/kmc-convert/src/keylayout-to-kmn/kmn-file-writer.ts index 31ea2fdc85..b27734db3d 100644 --- a/developer/src/kmc-convert/src/keylayout-to-kmn/kmn-file-writer.ts +++ b/developer/src/kmc-convert/src/keylayout-to-kmn/kmn-file-writer.ts @@ -17,31 +17,6 @@ export class KmnFileWriter { constructor(private callbacks: CompilerCallbacks, private options: CompilerOptions) { }; - /** - * @brief member function to write data from object to a kmn file - * @param data_ukelele the array holding all keyboard data - * @param outputfilename the file that will be written; if no outputfilename is given an outputfilename will be created from data_ukelele.keylayout_filename - * @return true if data has been written; false if not - */ - public writeToFile(data_ukelele: ProcesData): boolean { - - let data: string = "\n"; - - // add top part of kmn file: STORES - data += this.write_KmnFileHeader(data_ukelele); - - // add bottom part of kmn file: RULES - data += this.writeData_Rules(data_ukelele); - - try { - this.callbacks.fs.writeFileSync(data_ukelele.kmn_filename, new TextEncoder().encode(data)); - return true; - } catch (err) { - this.callbacks.reportMessage(ConverterMessages.Error_UnableToWrite({outputFilename: data_ukelele.kmn_filename})); - return false; - } - } - /** * @brief member function to write data from object to a Uint8Array * @param data_ukelele the array holding all keyboard data From d5996b9b4f755bedf7220a66c92742bd303dd70d Mon Sep 17 00:00:00 2001 From: Sabine Date: Mon, 25 Aug 2025 10:57:38 +0200 Subject: [PATCH 3/4] feat(developer): layout of error-msg --- .../src/kmc-convert/src/converter-messages.ts | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/developer/src/kmc-convert/src/converter-messages.ts b/developer/src/kmc-convert/src/converter-messages.ts index bc411300cc..6a9b78082d 100644 --- a/developer/src/kmc-convert/src/converter-messages.ts +++ b/developer/src/kmc-convert/src/converter-messages.ts @@ -29,39 +29,39 @@ export class ConverterMessages { ); static ERROR_FileNotFound = SevError | 0x0003; - static Error_FileNotFound = (o: { inputFilename: string; }) => m( - this.ERROR_FileNotFound, `Input filename '${def(o.inputFilename)} - ' does not exist or could not be loaded.` - ); + static Error_FileNotFound = + (o: { inputFilename: string; }) => m( + this.ERROR_FileNotFound, + `Input filename '${def(o.inputFilename)} ' does not exist or could not be loaded.` + ); static ERROR_UnableToRead = SevError | 0x0004; static Error_UnableToRead = (o: { inputFilename: string; }) => m( - this.ERROR_UnableToRead, `Input file '${def(o.inputFilename)} - ' could not be read.` + this.ERROR_UnableToRead, + `Input file '${def(o.inputFilename)} ' could not be read.` ); static ERROR_UnableToConvert = SevError | 0x0005; static Error_UnableToConvert = (o: { inputFilename: string; }) => m( - this.ERROR_UnableToConvert, `Input file '${def(o.inputFilename)} - ' could not be converted.` + this.ERROR_UnableToConvert, + `Input file '${def(o.inputFilename)} ' could not be converted.` ); static ERROR_UnableToWrite = SevError | 0x0006; static Error_UnableToWrite = (o: { outputFilename: string; }) => m( - this.ERROR_UnableToWrite, `Output file for '${def(o.outputFilename)} - ' could not be written.` + this.ERROR_UnableToWrite, + `Output file for '${def(o.outputFilename)} ' could not be written.` ); static INFO_UnsupportedCharactersDetected = SevInfo | 0x0007; static Info_UnsupportedCharactersDetected = (o: { inputFilename: string, keymap_index: string, key: string, KeyName: string, output: string; }) => m( - this.INFO_UnsupportedCharactersDetected, `INFO: Input file ${def(o.inputFilename)} - contains unsupported character '${def(o.output)} - ' at keyMap index ${def(o.keymap_index)} - on Keycode ${def(o.key)} (${def(o.KeyName)})` + this.INFO_UnsupportedCharactersDetected, + `INFO: Input file ${def(o.inputFilename)} contains unsupported character '${def(o.output)} ' at keyMap index ${def(o.keymap_index)} on Keycode ${def(o.key)} (${def(o.KeyName)})` ); static ERROR_InvalidFile = SevError | 0x0008; static Error_InvalidFile = (o: { errorText: string; }) => m( - this.ERROR_InvalidFile, `The source file has an invalid structure: ${def(o.errorText)}` + this.ERROR_InvalidFile, + `The source file has an invalid structure: ${def(o.errorText)}` ); } From b94c3367961c7290f472c8dd36e7ae4929ea734d Mon Sep 17 00:00:00 2001 From: Sabine Date: Mon, 25 Aug 2025 15:24:49 +0200 Subject: [PATCH 4/4] feat(developer): move callbacks.reportMessage --- .../keylayout-to-kmn/keylayout-file-reader.ts | 10 ++--- .../keylayout-to-kmn-converter.ts | 42 +++++++------------ .../src/keylayout-to-kmn/kmn-file-writer.ts | 2 +- .../test/keylayout-to-kmn-converter.tests.ts | 27 ++---------- 4 files changed, 25 insertions(+), 56 deletions(-) diff --git a/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-file-reader.ts b/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-file-reader.ts index 9d69218bb7..bd7dd4761c 100644 --- a/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-file-reader.ts +++ b/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-file-reader.ts @@ -39,8 +39,8 @@ export class KeylayoutFileReader { /** * If object contains attribute #text it will be removed. - * @param o Object with possible property #text - * @return objects that do not contain property #text + * @param o Object with possible property #text containing whitespaces + * @return objects that do not contain property #text */ public remove_whitespace(o: any): void { if (o['#text']) { @@ -50,7 +50,7 @@ export class KeylayoutFileReader { /** * @brief wrapper to remove whitespace and box single-entry objects into arrays - * @param o Object with property to box/remove whitespace from + * @param o Object with property to box/remove whitespaces from * @param x Name of element to box * @return objects that contain only boxed arrays */ @@ -63,7 +63,7 @@ export class KeylayoutFileReader { /** * @brief member function to box single-entry objects into arrays * @param source the object to be changed - * @return objects that contain only boxed arrays + * @return object that contain only boxed arrays */ public boxArray(source: any) { @@ -101,7 +101,7 @@ export class KeylayoutFileReader { } /** - * @brief member function to parse data from a .keylayout-file and store to a json object + * @brief member function to parse data from a .keylayout-file and store in a json object * we need to be able to ignore an output character of "", process an output character of " " (space) and allow surrounding whitespace in #text (which will be removed later) * @param inputFilename the ukelele .keylayout-file to be parsed * @return in case of success: json object containing data of the .keylayout file; else null diff --git a/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-to-kmn-converter.ts b/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-to-kmn-converter.ts index 8463b1a2a8..82bb26d9e1 100644 --- a/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-to-kmn-converter.ts +++ b/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-to-kmn-converter.ts @@ -78,7 +78,7 @@ export class KeylayoutToKmnConverter { static readonly INPUT_FILE_EXTENSION = '.keylayout'; static readonly OUTPUT_FILE_EXTENSION = '.kmn'; static readonly SKIP_COMMENTED_LINES = false; - static readonly MAX_CTRL_CHARACTER = 32; + static readonly MAX_CTRL_CHARACTER = 0x20; // the hightest control character we print out as a Unicode CodePoint static readonly MAX_KEY_COUNT = 49; // At most we use key Nr 0 (A) -> key Nr 49 (Space) static USE_KEY_COUNT = KeylayoutToKmnConverter.MAX_KEY_COUNT; // we use key Nr 0 (A) -> highest available key of .keylayout file @@ -122,34 +122,20 @@ export class KeylayoutToKmnConverter { return null; } - if (!jsonO) { - this.callbacks.reportMessage(ConverterMessages.Error_UnableToRead({ inputFilename })); - return null; - } - outputFilename = outputFilename ?? inputFilename.replace(/\.keylayout$/, '.kmn'); const outArray: ProcesData = await this.convert(jsonO, outputFilename); - if (outArray.arrayOf_Rules.length === 0) { - this.callbacks.reportMessage(ConverterMessages.Error_UnableToConvert({ inputFilename })); - return null; - } const kmnFileWriter = new KmnFileWriter(this.callbacks, this.options); // write to object/ConverterToKmnResult const out_Uint8: Uint8Array = kmnFileWriter.write(outArray); - const Result_toBeReturned: ConverterToKmnResult = { + const result: ConverterToKmnResult = { artifacts: { kmn: { data: out_Uint8, filename: outputFilename } } }; - - if (!out_Uint8) { - this.callbacks.reportMessage(ConverterMessages.Error_UnableToWrite({ outputFilename })); - return null; - } - return Result_toBeReturned; + return result; } /** @@ -189,12 +175,16 @@ export class KeylayoutToKmnConverter { // fill rules into arrayOf_Rules of data_object return this.createRuleData(data_object, jsonObj); } - return data_object; + else { + const inputFilename = data_object.keylayout_filename; + this.callbacks.reportMessage(ConverterMessages.Error_UnableToConvert({ inputFilename })); + return data_object; + } } /** * @brief member function to read the rules contained in a json object and add array of Rules[] to an ProcesData - * @param data_ukelele: an object containing the name of the input file, an array of behaviours and an (empty) array of Rules + * @param data_ukelele: an object containing the name of the in/output file, an array of behaviours and an (empty) array of Rules * @param jsonObj: json Object containing all data read from a keylayout file * @return an object containing the name of the input file, an array of behaviours and a populated array of Rules[] */ @@ -215,8 +205,6 @@ export class KeylayoutToKmnConverter { this.callbacks.reportMessage(ConverterMessages.Error_InvalidFile({ errorText })); } - // loop keys 0-49 (= all keys we use) - //for (let j = 0; j <= KeylayoutToKmnConverter.MAX_KEY_COUNT; j++) { for (let j = 0; j <= KeylayoutToKmnConverter.MAX_KEY_COUNT; j++) { // loop behaviors (in ukelele it is possible to define multiple modifier combinations that behave in the same way) @@ -500,7 +488,7 @@ export class KeylayoutToKmnConverter { /** * @brief member function to review data in array of Rules[] of data_ukelele: remove duplicate rules and mark first occurance of a rule in object_array - * @param data_ukelele: an object containing the name of the input file, an array of behaviours and an array of Rules + * @param data_ukelele: an object containing the name of the in/output file, an array of behaviours and an array of Rules * @return an object containing the name of the input file, an array of behaviours and the revised array of Rules[] */ public reviewRuleInputData(data_ukelele: ProcesData): ProcesData { @@ -697,14 +685,14 @@ export class KeylayoutToKmnConverter { /** * @brief member function to check if CAPS is used throughout a keylayout file or not * @param keylayout_modifier the modifier string used in the .keylayout-file - * @return kmn_modifier the modifier string used in the .kmn-file + * @return kmn_modifier: the modifier string used in the .kmn-file */ public checkIfCapsIsUsed(keylayout_modifier: string[][]): boolean { return JSON.stringify(keylayout_modifier).toUpperCase().includes("CAPS"); } /** - * @brief member function to check if a modifier can be used in keyman + * @brief member function to check if a modifier can be used in Keyman * @param keylayout_modifier the modifier string used in the .keylayout-file * @return true if the modifier can be used in keyman; false if not */ @@ -739,7 +727,7 @@ export class KeylayoutToKmnConverter { /** * @brief member function to map Ukelele keycodes to Windows Keycodes * @param pos Ukelele (=mac) keycodes - * @return keycode on a Windows Keyboard + * @return VK */ public map_UkeleleKC_To_VK(pos: number): string { const vk = [ @@ -821,7 +809,7 @@ export class KeylayoutToKmnConverter { * @brief member function to find the actionID of a certain state-next pair * @param data :any an object containing all data read from a .keylayout file * @param search :string value 'next' to be found - * @return a string containing the actionId of a certain state-next pair + * @return a string containing the actionId of a certain state(none)-next pair */ public get_ActionID__From__ActionNext(data: any, search: string): string { if (search !== "none") { @@ -840,7 +828,7 @@ export class KeylayoutToKmnConverter { * @brief member function to create an array of (modifier) behaviours for a given keycode in [{keycode,modifier}] * @param data : any - an object containing all data read from a .keylayout file * @param search : KeylayoutFileData[] - an array[{keycode,modifier}] to be found - * @return an array: string[] containing modifiers + * @return a string[] containing modifiers */ public get_Modifier_array__From__KeyModifier_array(data: any, search: KeylayoutFileData[]): string[] { const returnString1D: string[] = []; diff --git a/developer/src/kmc-convert/src/keylayout-to-kmn/kmn-file-writer.ts b/developer/src/kmc-convert/src/keylayout-to-kmn/kmn-file-writer.ts index b27734db3d..84c1276128 100644 --- a/developer/src/kmc-convert/src/keylayout-to-kmn/kmn-file-writer.ts +++ b/developer/src/kmc-convert/src/keylayout-to-kmn/kmn-file-writer.ts @@ -386,7 +386,7 @@ export class KmnFileWriter { * Omitting rules and definition of comparisons e.g. 1-1, 2-4, 6-6 * see https://docs.google.com/document/d/12J3NGO6RxIthCpZDTR8FYSRjiMgXJDLwPY2z9xqKzJ0/edit?tab=t.0#heading=h.pcz8rjyrl5ug * @param rule : Rule[] - an array of all rules - * @param index the index of a rule in array[rule] + * @param index the index of a rule in Rule[] * @return a string[] containing possible warnings for a rule */ public reviewRules(rule: Rule[], index: number): string[] { diff --git a/developer/src/kmc-convert/test/keylayout-to-kmn-converter.tests.ts b/developer/src/kmc-convert/test/keylayout-to-kmn-converter.tests.ts index 897fe4e935..a546b20b52 100644 --- a/developer/src/kmc-convert/test/keylayout-to-kmn-converter.tests.ts +++ b/developer/src/kmc-convert/test/keylayout-to-kmn-converter.tests.ts @@ -22,27 +22,6 @@ describe('KeylayoutToKmnConverter', function () { compilerTestCallbacks.clear(); }); - // todo remove - describe('RunAllFiles', function () { - const sut = new KeylayoutToKmnConverter(compilerTestCallbacks, compilerTestOptions); - [ - [makePathToFixture('../data/Italian.keylayout')], - [makePathToFixture('../data/Italian_command.keylayout')], - [makePathToFixture('../data/Swiss_French.keylayout')], - [makePathToFixture('../data/Spanish.keylayout')], - [makePathToFixture('../data/Swiss_German.keylayout')], - [makePathToFixture('../data/US.keylayout')], - [makePathToFixture('../data/Polish.keylayout')], - [makePathToFixture('../data/French.keylayout')], - [makePathToFixture('../data/Latin_American.keylayout')], - // [makePathToFixture('../data/German_complete.keylayout')], - [makePathToFixture('../data/German_complete_reduced.keylayout')], - // [makePathToFixture('../data/German_Standard.keylayout')], - ].forEach(function (files_) { - sut.run(files_[0]); - assert.isTrue(true); - }); - }); describe('RunTestFiles resulting in errors ', function () { const sut = new KeylayoutToKmnConverter(compilerTestCallbacks, compilerTestOptions); [ @@ -178,16 +157,18 @@ describe('KeylayoutToKmnConverter', function () { describe('convert() ', function () { const sut = new KeylayoutToKmnConverter(compilerTestCallbacks, compilerTestOptions); const sut_r = new KeylayoutFileReader(compilerTestCallbacks); + + // ProcesData from usable file const inputFilename = makePathToFixture('../data/Test.keylayout'); const read = sut_r.read(inputFilename); const converted = sut.convert_bound.convert(read, inputFilename.replace(/\.keylayout$/, '.kmn')); - // ProcesData from unavailable file name + // ProcesData from unavailable file const inputFilename_unavailable = makePathToFixture('../data/X.keylayout'); const read_unavailable = sut_r.read(inputFilename_unavailable); const converted_unavailable = sut.convert_bound.convert(read_unavailable, inputFilename_unavailable.replace(/\.keylayout$/, '.kmn')); - // ProcesData from empty filename + // ProcesData from empty file const inputFilename_empty = makePathToFixture(''); const read_empty = sut_r.read(inputFilename_empty); const converted_empty = sut.convert_bound.convert(read_empty, inputFilename_empty);