From 0e6aa6566f3b61cce56a9f7e76fc8098c0ecf49a Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Fri, 4 Nov 2022 15:15:43 -0500 Subject: [PATCH 01/33] =?UTF-8?q?feat(core):=20ldml:=20First=20steps=20for?= =?UTF-8?q?=20layr=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 - dummy Layr class for compilation - fleshed out Layr class - CPP validation side --- common/web/types/src/kmx/kmx-plus.ts | 51 +++++++ .../src/ldml-keyboard/ldml-keyboard-xml.ts | 12 ++ core/include/ldml/keyboardprocessor_ldml.h | 10 ++ core/include/ldml/keyboardprocessor_ldml.ts | 41 ++++++ core/src/kmx/kmx_plus.cpp | 132 ++++++++++++++++++ core/src/kmx/kmx_plus.h | 94 ++++++++++++- 6 files changed, 339 insertions(+), 1 deletion(-) diff --git a/common/web/types/src/kmx/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus.ts index aa04094b3a..5509d7845d 100644 --- a/common/web/types/src/kmx/kmx-plus.ts +++ b/common/web/types/src/kmx/kmx-plus.ts @@ -190,6 +190,7 @@ export class Vkey extends Section { vkeys: VkeyItem[] = []; }; +// 'disp' export class DispItem { to: StrsItem; display: StrsItem; @@ -200,6 +201,55 @@ export class Disp extends Section { disps: DispItem[] = []; }; +// 'layr' + +/** + * In-memory `` + */ +export class LayrList { + flags: number; + hardware: StrsItem; + /** + * Index into Layr.layers + */ + layerIndex: number; + count: number; +}; + +/** + * In-memory `` + */ + export class LayrEntry { + id: StrsItem; + modifier: StrsItem; + /** + * index into Layr.rows + */ + rowIndex: number; + count: number; +}; + +/** + * In-memory `` + */ + export class LayrRow { + /** + * index into Layr.vkeys + */ + keyIndex: number; + count: number; +}; + +export class Layr extends Section { + lists: LayrList[] = []; + layers: LayrEntry[] = []; + rows: LayrRow[] = []; + /** + * each item is a vkey id + */ + vkeys: number[] = []; +}; + export interface KMXPlusData { sect?: Strs; // sect is ignored in-memory bksp?: Bksp; @@ -207,6 +257,7 @@ export interface KMXPlusData { elem?: Elem; // elem is ignored in-memory finl?: Finl; keys?: Keys; + layr?: Layr; loca?: Loca; meta?: Meta; name?: Name; diff --git a/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts b/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts index 568d186ae6..aa44621222 100644 --- a/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts +++ b/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts @@ -76,12 +76,24 @@ export interface LKKey { }; export interface LKLayers { + /** + * `hardware` or `touch` + */ form?: string; + /** + * `us`, `iso`, `jis`, or `abnt2` + */ + hardware?: string; + /** + * Minimum width in millimeters + */ + minDeviceWidth?: number; layer?: LKLayer[]; }; export interface LKLayer { id?: string; + modifier?: string; row?: LKRow[]; }; diff --git a/core/include/ldml/keyboardprocessor_ldml.h b/core/include/ldml/keyboardprocessor_ldml.h index aa5db68abc..53db980f92 100644 --- a/core/include/ldml/keyboardprocessor_ldml.h +++ b/core/include/ldml/keyboardprocessor_ldml.h @@ -25,6 +25,9 @@ #define LDML_ELEM_FLAGS_UNICODE_SET 0x1 #define LDML_FINL_FLAGS_ERROR 0x1 #define LDML_KEYS_FLAGS_EXTEND 0x1 +#define LDML_LAYR_LIST_FLAGS_HARDWARE 0x0 +#define LDML_LAYR_LIST_FLAGS_MASK_FORM 0x1 +#define LDML_LAYR_LIST_FLAGS_TOUCH 0x1 #define LDML_LENGTH_BKSP 0x10 #define LDML_LENGTH_BKSP_ITEM 0x10 #define LDML_LENGTH_DISP 0x20 @@ -37,6 +40,11 @@ #define LDML_LENGTH_HEADER 0x8 #define LDML_LENGTH_KEYS 0x10 #define LDML_LENGTH_KEYS_ITEM 0x10 +#define LDML_LENGTH_LAYR 0x20 +#define LDML_LENGTH_LAYR_ENTRY 0x10 +#define LDML_LENGTH_LAYR_KEY 0x4 +#define LDML_LENGTH_LAYR_LIST 0x10 +#define LDML_LENGTH_LAYR_ROW 0x8 #define LDML_LENGTH_LOCA 0x10 #define LDML_LENGTH_LOCA_ITEM 0x4 #define LDML_LENGTH_META 0x24 @@ -65,6 +73,8 @@ #define LDML_SECTIONNAME_FINL "finl" #define LDML_SECTIONID_KEYS 0x7379656B /* "keys" */ #define LDML_SECTIONNAME_KEYS "keys" +#define LDML_SECTIONID_LAYR 0x7279616C /* "layr" */ +#define LDML_SECTIONNAME_LAYR "layr" #define LDML_SECTIONID_LOCA 0x61636F6C /* "loca" */ #define LDML_SECTIONNAME_LOCA "loca" #define LDML_SECTIONID_META 0x6174656D /* "meta" */ diff --git a/core/include/ldml/keyboardprocessor_ldml.ts b/core/include/ldml/keyboardprocessor_ldml.ts index 9404a4e668..f2eaea0c50 100644 --- a/core/include/ldml/keyboardprocessor_ldml.ts +++ b/core/include/ldml/keyboardprocessor_ldml.ts @@ -23,12 +23,14 @@ * through. */ export type SectionIdent = +// Keep this sorted, but with `sect` as the first entry. 'sect' | 'bksp' | 'disp' | 'elem' | 'finl' | 'keys' | + 'layr' | 'loca' | 'meta' | 'name' | @@ -225,6 +227,43 @@ class Constants { */ readonly keys_flags_extend = 1; + /* ------------------------------------------------------------------ + * layr section + ------------------------------------------------------------------ */ + + /** + * Minimum length of the 'layr' section not including variable parts + */ + readonly length_layr = 32; + /** + * Length of each layer list in the 'layr' section variable part + */ + readonly length_layr_list = 16; + /** + * bitmask for the 'form' field of the layr.list[].flags bitfield + */ + readonly layr_list_flags_mask_form = 1; + /** + * hardware layout: value for the 'form' field of the layr.list[].flags + */ + readonly layr_list_flags_hardware = 0; + /** + * touch layout: value for the 'form' field of the layr.list[].flags + */ + readonly layr_list_flags_touch = 1; + /** + * Length of each layer entry in the 'layr' section variable part + */ + readonly length_layr_entry = 16; + /** + * Length of each row entry in the 'layr' section variable part + */ + readonly length_layr_row = 8; + /** + * Length of each key entry in the 'layr' section variable part + */ + readonly length_layr_key = 4; + /* ------------------------------------------------------------------ * loca section ------------------------------------------------------------------ */ @@ -332,11 +371,13 @@ class Constants { * All section IDs. */ readonly section: SectionMap = { + // keep this sorted bksp: 'bksp', disp: 'disp', elem: 'elem', finl: 'finl', keys: 'keys', + layr: 'layr', loca: 'loca', meta: 'meta', name: 'name', diff --git a/core/src/kmx/kmx_plus.cpp b/core/src/kmx/kmx_plus.cpp index 03bbb0e559..87fbaa12d2 100644 --- a/core/src/kmx/kmx_plus.cpp +++ b/core/src/kmx/kmx_plus.cpp @@ -352,6 +352,134 @@ COMP_KMXPLUS_TRAN::valid(KMX_DWORD _kmn_unused(length)) const { return true; } +bool +COMP_KMXPLUS_LAYR::valid(KMX_DWORD _kmn_unused(length)) const { + if (header.size < sizeof(*this) + + (listCount * sizeof(COMP_KMXPLUS_LAYR_LIST)) + + (layerCount * sizeof(COMP_KMXPLUS_LAYR_ENTRY)) + + (rowCount * sizeof(COMP_KMXPLUS_LAYR_ROW)) + + (keyCount * sizeof(COMP_KMXPLUS_LAYR_KEY))) { + DebugLog("header.size < expected size"); + return false; + } + // TODO-LDML + DebugLog("!! More to do here."); + return true; +} + +COMP_KMXPLUS_LAYR_Helper::COMP_KMXPLUS_LAYR_Helper() : layr(nullptr), is_valid(false) { +} + +bool +COMP_KMXPLUS_LAYR_Helper::setLayr(const COMP_KMXPLUS_LAYR *newLayr) { + is_valid = true; + if (newLayr == nullptr) { + // null = invalid + is_valid = false; + return false; + } + const uint8_t *rawdata = reinterpret_cast(this); + rawdata += LDML_LENGTH_LAYR; // skip past non-dynamic portion + // lists + if (layr->listCount > 0) { + lists = reinterpret_cast(rawdata); + } else { + lists = nullptr; + is_valid = false; + } + rawdata += sizeof(COMP_KMXPLUS_LAYR_LIST) * layr->listCount; + // entries + if (layr->layerCount > 0) { + entries = reinterpret_cast(rawdata); + } else { + entries = nullptr; + is_valid = false; + } + rawdata += sizeof(COMP_KMXPLUS_LAYR_ENTRY) * layr->layerCount; + // rows + if (layr->rowCount > 0) { + rows = reinterpret_cast(rawdata); + } else { + rows = nullptr; + is_valid = false; + } + rawdata += sizeof(COMP_KMXPLUS_LAYR_ROW) * layr->rowCount; + // keys + if (layr->keyCount > 0) { + keys = reinterpret_cast(rawdata); + } else { + keys = nullptr; + is_valid = false; + } + // rawdata += sizeof(COMP_KMXPLUS_LAYR_KEY) * layr->keyCount; + + // Now, validate offsets by walking + if (is_valid) { + for(KMX_DWORD i = 0; is_valid && i < layr->listCount; i++) { + const COMP_KMXPLUS_LAYR_LIST &list = lists[i]; + // is the count off the end? + if ((list.layer >= layr->layerCount) || (list.layer + list.count > layr->layerCount)) { + DebugLog("COMP_KMXPLUS_LAYR_Helper: list[%d] would access layer %d+%d, > count %d", + i, list.layer, list.count, layr->layerCount); + is_valid = false; + } + } + for(KMX_DWORD i = 0; is_valid && i < layr->layerCount; i++) { + const COMP_KMXPLUS_LAYR_ENTRY &entry = entries[i]; + // is the count off the end? + if ((entry.row >= layr->rowCount) || (entry.row + entry.count > layr->rowCount)) { + DebugLog("COMP_KMXPLUS_LAYR_Helper: entry[%d] would access row %d+%d, > count %d", + i, entry.row, entry.count, layr->rowCount); + is_valid = false; + } + } + for(KMX_DWORD i = 0; is_valid && i < layr->rowCount; i++) { + const COMP_KMXPLUS_LAYR_ROW &row = rows[i]; + // is the count off the end? + if ((row.key >= layr->keyCount) || (row.key + row.count > layr->keyCount)) { + DebugLog("COMP_KMXPLUS_LAYR_Helper: row[%d] would access key %d+%d, > count %d", + i, row.key, row.count, layr->keyCount); + is_valid = false; + } + } + } + // Return results + DebugLog("COMP_KMXPLUS_LAYR_Helper.setLayr(): %s", is_valid ? "valid" : "invalid"); + return is_valid; +} + +bool COMP_KMXPLUS_LAYR_Helper::valid() const { + return is_valid; +} + +const COMP_KMXPLUS_LAYR_LIST * +COMP_KMXPLUS_LAYR_Helper::getList(KMX_DWORD list) const { + if (!valid() || list >= layr->listCount) + return nullptr; + return lists + list; +} + +const COMP_KMXPLUS_LAYR_ENTRY * +COMP_KMXPLUS_LAYR_Helper::getEntry(KMX_DWORD entry) const { + if (!valid() || entry >= layr->layerCount) + return nullptr; + return entries + entry; +} + +const COMP_KMXPLUS_LAYR_ROW * +COMP_KMXPLUS_LAYR_Helper::getRow(KMX_DWORD row) const { + if (!valid() || row >= layr->rowCount) + return nullptr; + return rows + row; +} + +const COMP_KMXPLUS_LAYR_KEY * +COMP_KMXPLUS_LAYR_Helper::getKey(KMX_DWORD key) const { + if (!valid() || key >= layr->keyCount) + return nullptr; + return keys + key; +} + // ---- constructor kmx_plus::kmx_plus(const COMP_KEYBOARD *keyboard, size_t length) @@ -391,11 +519,15 @@ kmx_plus::kmx_plus(const COMP_KEYBOARD *keyboard, size_t length) disp = section_from_sect(sect); elem = section_from_sect(sect); keys = section_from_sect(sect); + layr = section_from_sect(sect); loca = section_from_sect(sect); meta = section_from_sect(sect); strs = section_from_sect(sect); tran = section_from_sect(sect); vkey = section_from_sect(sect); + + // calculate and validate the layer dynamic parts + (void)layrHelper.setLayr(layr); } } diff --git a/core/src/kmx/kmx_plus.h b/core/src/kmx/kmx_plus.h index c5ebb94fab..971d0067c6 100644 --- a/core/src/kmx/kmx_plus.h +++ b/core/src/kmx/kmx_plus.h @@ -371,8 +371,97 @@ struct COMP_KMXPLUS_DISP { static_assert(sizeof(struct COMP_KMXPLUS_DISP) % 0x10 == 0, "Structs prior to entries[] should align to 128-bit boundary"); static_assert(sizeof(struct COMP_KMXPLUS_DISP) == LDML_LENGTH_DISP, "mismatched size of section disp"); + + +/* ------------------------------------------------------------------ + * layr section + ------------------------------------------------------------------ */ + +struct COMP_KMXPLUS_LAYR_LIST { + KMX_DWORD flags; + KMXPLUS_STR hardware; + KMX_DWORD layer; + KMX_DWORD count; +}; + +static_assert(sizeof(struct COMP_KMXPLUS_LAYR_LIST) == LDML_LENGTH_LAYR_LIST, "mismatched size of COMP_KMXPLUS_LAYR_LIST"); + +struct COMP_KMXPLUS_LAYR_ENTRY { + KMXPLUS_STR id; + KMXPLUS_STR modifier; + KMX_DWORD row; + KMX_DWORD count; +}; + +static_assert(sizeof(struct COMP_KMXPLUS_LAYR_ENTRY) == LDML_LENGTH_LAYR_ENTRY, "mismatched size of COMP_KMXPLUS_LAYR_ENTRY"); + +struct COMP_KMXPLUS_LAYR_ROW { + KMX_DWORD key; + KMX_DWORD count; +}; + +static_assert(sizeof(struct COMP_KMXPLUS_LAYR_ROW) == LDML_LENGTH_LAYR_ROW, "mismatched size of COMP_KMXPLUS_LAYR_ROW"); + + +struct COMP_KMXPLUS_LAYR_KEY { + KMX_DWORD vkey; +}; + +static_assert(sizeof(struct COMP_KMXPLUS_LAYR_KEY) == LDML_LENGTH_LAYR_KEY, "mismatched size of COMP_KMXPLUS_LAYR_KEY"); + +struct COMP_KMXPLUS_LAYR { + static const KMX_DWORD IDENT = LDML_SECTIONID_LAYR; + COMP_KMXPLUS_HEADER header; + KMX_DWORD listCount; + KMX_DWORD layerCount; + KMX_DWORD rowCount; + KMX_DWORD keyCount; + KMX_DWORD reserved[2]; + // entries, rows, and keys have a dynamic offset + // use COMP_KMXPLUS_LAYR_Helper to access. + // + // COMP_KMXPLUS_LAYR_LIST lists[]; + // COMP_KMXPLUS_LAYR_ENTRY entries[]; + // COMP_KMXPLUS_LAYR_ROW rows[]; + // COMP_KMXPLUS_LAYR_KEY keys[]; + /** + * @brief True if section is valid. + */ + bool valid(KMX_DWORD length) const; +}; + /** - * @brief helper accessor object for + * Helper accessor for the dynamic part of a layr section. + */ +class COMP_KMXPLUS_LAYR_Helper { +public: + COMP_KMXPLUS_LAYR_Helper(); + /** + * Initialize the helper to point at a layr section. + * @return true if valid + */ + bool setLayr(const COMP_KMXPLUS_LAYR *newLayr); + bool valid() const; + + const COMP_KMXPLUS_LAYR_LIST *getList(KMX_DWORD list) const; + const COMP_KMXPLUS_LAYR_ENTRY *getEntry(KMX_DWORD entry) const; + const COMP_KMXPLUS_LAYR_ROW *getRow(KMX_DWORD row) const; + const COMP_KMXPLUS_LAYR_KEY *getKey(KMX_DWORD key) const; + +private: + const COMP_KMXPLUS_LAYR *layr; + bool is_valid; + const COMP_KMXPLUS_LAYR_LIST *lists; + const COMP_KMXPLUS_LAYR_ENTRY *entries; + const COMP_KMXPLUS_LAYR_ROW *rows; + const COMP_KMXPLUS_LAYR_KEY *keys; +}; + +static_assert(sizeof(struct COMP_KMXPLUS_LAYR) % 0x10 == 0, "Structs prior to entries[] should align to 128-bit boundary"); +static_assert(sizeof(struct COMP_KMXPLUS_LAYR) == LDML_LENGTH_LAYR, "mismatched size of section layr"); + +/** + * @brief helper accessor object for KMX Plus data * */ class kmx_plus { @@ -386,9 +475,11 @@ class kmx_plus { * @param length length of the entire KMX file */ kmx_plus(const COMP_KEYBOARD *keyboard, size_t length); + // keep the next elements sorted const COMP_KMXPLUS_DISP *disp; const COMP_KMXPLUS_ELEM *elem; const COMP_KMXPLUS_KEYS *keys; + const COMP_KMXPLUS_LAYR *layr; const COMP_KMXPLUS_LOCA *loca; const COMP_KMXPLUS_META *meta; const COMP_KMXPLUS_SECT *sect; @@ -398,6 +489,7 @@ class kmx_plus { inline bool is_valid() { return valid; } private: bool valid; // true if valid + COMP_KMXPLUS_LAYR_Helper layrHelper; }; /** From b639d80a014c4d8beb549ae179be1286e7cd3de0 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Wed, 16 Nov 2022 18:07:52 -0600 Subject: [PATCH 02/33] =?UTF-8?q?feat(core):=20ldml:=20spec:=20correct=20p?= =?UTF-8?q?adding=20key2=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - also document key2.flicks.flags For #7532 --- core/src/ldml/C7043_ldml.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/core/src/ldml/C7043_ldml.md b/core/src/ldml/C7043_ldml.md index 065899b4dd..89df7634dc 100644 --- a/core/src/ldml/C7043_ldml.md +++ b/core/src/ldml/C7043_ldml.md @@ -419,8 +419,9 @@ Entries are sorted in a binary codepoint sort on the `to` field. | 4 | 32 | size | int: Length of section | | 8 | 32 | keyCount | int: Number of keys | |12 | 32 | flicksCount | int: Number of flick lists | -|12 | 32 | flickCount | int: Number of flick elements | -|16 | var | keys | keys sub-table | +|16 | 32 | flickCount | int: Number of flick elements | +|20 | 96 | reserved | padding | +|32 | var | keys | keys sub-table | | - | var | flicks | flick lists sub-table | | - | var | flick | flick elements sub-table | @@ -485,6 +486,13 @@ There is not a 'null' flick element at the end of each list. Elements are ordered by the `flicks.id`, and secondarily by the directions list id. +- `flags`: Flags is a 32-bit bitfield defined as below: + +| Bit position | Meaning | Description | +|--------------|-----------|---------------------------------------------| +| 0 | extend | 0: `to` is a char, 1: `to` is a string | + + ### C7043.2.16 `list`—String lists | ∆ | Bits | Name | Description | From 64c5c796a16c922d3a5087db67efd0b4f376598e Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Wed, 16 Nov 2022 18:20:43 -0600 Subject: [PATCH 03/33] =?UTF-8?q?feat(core):=20ldml:=20steps=20for=20list/?= =?UTF-8?q?key2=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - update .ts constants - update builder to support digits in section id - update XML and stub KMXPlusData For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- common/web/types/src/kmx/kmx-plus.ts | 10 +++ .../src/ldml-keyboard/ldml-keyboard-xml.ts | 20 ++++++ core/include/ldml/keyboardprocessor_ldml.h | 15 +++++ core/include/ldml/keyboardprocessor_ldml.ts | 66 ++++++++++++++++++- 4 files changed, 109 insertions(+), 2 deletions(-) diff --git a/common/web/types/src/kmx/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus.ts index 5509d7845d..2690f10d46 100644 --- a/common/web/types/src/kmx/kmx-plus.ts +++ b/common/web/types/src/kmx/kmx-plus.ts @@ -250,14 +250,24 @@ export class Layr extends Section { vkeys: number[] = []; }; +export class Key2 { + // TODO-LDML +}; + +export class List { + // TODO-LDML +}; + export interface KMXPlusData { sect?: Strs; // sect is ignored in-memory bksp?: Bksp; disp?: Disp; elem?: Elem; // elem is ignored in-memory finl?: Finl; + key2?: Key2; keys?: Keys; layr?: Layr; + list?: List; loca?: Loca; meta?: Meta; name?: Name; diff --git a/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts b/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts index aa44621222..0261e2671f 100644 --- a/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts +++ b/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts @@ -66,13 +66,33 @@ export interface LKSettings { export interface LKKeys { key: LKKey[]; + flicks: LKFlicks[]; }; export interface LKKey { id?: string; + flicks?: string; to?: string; gap?: boolean; switch?: string; + longPress?: string; + longPressDefault?: string; + multiTap?: string; + /** + * "no" or falsy + */ + transform?: string; + width?: number; +}; + +export interface LKFlicks { + id?: string; + flick?: LKFlick[]; +}; + +export interface LKFlick { + directions?: string; + to?: string; }; export interface LKLayers { diff --git a/core/include/ldml/keyboardprocessor_ldml.h b/core/include/ldml/keyboardprocessor_ldml.h index 53db980f92..f042865ec9 100644 --- a/core/include/ldml/keyboardprocessor_ldml.h +++ b/core/include/ldml/keyboardprocessor_ldml.h @@ -24,6 +24,10 @@ #define LDML_ELEM_FLAGS_TERTIARY_MASK 0xFF000000 #define LDML_ELEM_FLAGS_UNICODE_SET 0x1 #define LDML_FINL_FLAGS_ERROR 0x1 +#define LDML_KEY2_FLICK_FLAGS_EXTEND 0x1 +#define LDML_KEY2_KEY_FLAGS_EXTEND 0x1 +#define LDML_KEY2_KEY_FLAGS_GAP 0x2 +#define LDML_KEY2_KEY_FLAGS_NOTRANSFORM 0x4 #define LDML_KEYS_FLAGS_EXTEND 0x1 #define LDML_LAYR_LIST_FLAGS_HARDWARE 0x0 #define LDML_LAYR_LIST_FLAGS_MASK_FORM 0x1 @@ -38,6 +42,10 @@ #define LDML_LENGTH_FINL 0x10 #define LDML_LENGTH_FINL_ITEM 0x10 #define LDML_LENGTH_HEADER 0x8 +#define LDML_LENGTH_KEY2 0x20 +#define LDML_LENGTH_KEY2_FLICK_ELEMENT 0xC +#define LDML_LENGTH_KEY2_FLICK_LIST 0xC +#define LDML_LENGTH_KEY2_KEY 0x14 #define LDML_LENGTH_KEYS 0x10 #define LDML_LENGTH_KEYS_ITEM 0x10 #define LDML_LENGTH_LAYR 0x20 @@ -45,6 +53,9 @@ #define LDML_LENGTH_LAYR_KEY 0x4 #define LDML_LENGTH_LAYR_LIST 0x10 #define LDML_LENGTH_LAYR_ROW 0x8 +#define LDML_LENGTH_LIST 0x20 +#define LDML_LENGTH_LIST_INDEX 0x4 +#define LDML_LENGTH_LIST_ITEM 0x8 #define LDML_LENGTH_LOCA 0x10 #define LDML_LENGTH_LOCA_ITEM 0x4 #define LDML_LENGTH_META 0x24 @@ -71,10 +82,14 @@ #define LDML_SECTIONNAME_ELEM "elem" #define LDML_SECTIONID_FINL 0x6C6E6966 /* "finl" */ #define LDML_SECTIONNAME_FINL "finl" +#define LDML_SECTIONID_KEY2 0x3279656B /* "key2" */ +#define LDML_SECTIONNAME_KEY2 "key2" #define LDML_SECTIONID_KEYS 0x7379656B /* "keys" */ #define LDML_SECTIONNAME_KEYS "keys" #define LDML_SECTIONID_LAYR 0x7279616C /* "layr" */ #define LDML_SECTIONNAME_LAYR "layr" +#define LDML_SECTIONID_LIST 0x7473696C /* "list" */ +#define LDML_SECTIONNAME_LIST "list" #define LDML_SECTIONID_LOCA 0x61636F6C /* "loca" */ #define LDML_SECTIONNAME_LOCA "loca" #define LDML_SECTIONID_META 0x6174656D /* "meta" */ diff --git a/core/include/ldml/keyboardprocessor_ldml.ts b/core/include/ldml/keyboardprocessor_ldml.ts index f2eaea0c50..0c33c21d56 100644 --- a/core/include/ldml/keyboardprocessor_ldml.ts +++ b/core/include/ldml/keyboardprocessor_ldml.ts @@ -29,8 +29,10 @@ export type SectionIdent = 'disp' | 'elem' | 'finl' | + 'key2' | 'keys' | 'layr' | + 'list' | 'loca' | 'meta' | 'name' | @@ -227,6 +229,47 @@ class Constants { */ readonly keys_flags_extend = 1; + /* ------------------------------------------------------------------ + * key2 section + ------------------------------------------------------------------ */ + + /** + * Minimum length of the 'key2' section not including variable parts + */ + readonly length_key2 = 32; + /** + * Length of each item in the 'key2' keys sub-table + */ + readonly length_key2_key = 20; + /** + * Length of each item in the 'key2' flick lists sub-table + */ + readonly length_key2_flick_list = 12; + /** + * Length of each item in the 'key2' flick elements sub-table + */ + readonly length_key2_flick_element = 12; + + /** + * 0 if to is a char, 1 if it is a string + */ + readonly key2_key_flags_extend = 0x00000001; + + /** + * 1 if the key is a gap + */ + readonly key2_key_flags_gap = 0x00000002; + + /** + * 1 if the key is transform=no + */ + readonly key2_key_flags_notransform = 0x00000004; + + /** + * 0 if to is a char, 1 if it is a string + */ + readonly key2_flick_flags_extend = 0x00000001; + /* ------------------------------------------------------------------ * layr section ------------------------------------------------------------------ */ @@ -265,9 +308,26 @@ class Constants { readonly length_layr_key = 4; /* ------------------------------------------------------------------ - * loca section + * list section ------------------------------------------------------------------ */ + /** + * Minimum length of the 'list' section not including variable parts + */ + readonly length_list = 32; + /** + * Length of each list item in the 'list' list section variable part + */ + readonly length_list_item = 8; + /** + * Length of each list item in the 'list' indices section variable part + */ + readonly length_list_index = 4; + + /* ------------------------------------------------------------------ + * loca section + ------------------------------------------------------------------ */ + /** * Minimum length of the 'loca' section not including variable parts */ @@ -376,8 +436,10 @@ class Constants { disp: 'disp', elem: 'elem', finl: 'finl', + key2: 'key2', keys: 'keys', layr: 'layr', + list: 'list', loca: 'loca', meta: 'meta', name: 'name', @@ -394,7 +456,7 @@ class Constants { * @returns hex ID such as 0x74636573 */ hex_section_id(id:string) { - if(!id || typeof id !== 'string' || !id.match(/[a-z][a-z][a-z][a-z]/)) { + if(!id || typeof id !== 'string' || !id.match(/[a-z0-9][a-z0-9][a-z0-9][a-z0-9]/)) { throw Error(`hex_section_id(${id}) - need a 4-character string`); } let r = 0; From 06308a26da1b8b18efe18b86a68ab66d3497a163 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Thu, 17 Nov 2022 14:17:07 -0600 Subject: [PATCH 04/33] =?UTF-8?q?feat(core):=20ldml:=20spec:=20correct=20l?= =?UTF-8?q?ength=20of=20list=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For #7532 --- core/src/ldml/C7043_ldml.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/ldml/C7043_ldml.md b/core/src/ldml/C7043_ldml.md index 89df7634dc..f8db54b0a6 100644 --- a/core/src/ldml/C7043_ldml.md +++ b/core/src/ldml/C7043_ldml.md @@ -501,7 +501,7 @@ Elements are ordered by the `flicks.id`, and secondarily by the directions list | 4 | 32 | size | int: Length of section | | 8 | 32 | listCount | int: Total number of lists elements | |12 | 32 | indexCount | int: Total number of index elements | -|32 | var | lists | list sub-table | +|16 | var | lists | list sub-table | | - | var | indices | index sub-table | #### `list.lists` sub-table From 02ab5b5851264721c7c4e4f4478ee11f57f77c96 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Thu, 17 Nov 2022 14:17:27 -0600 Subject: [PATCH 05/33] =?UTF-8?q?feat(core):=20ldml:=20correct=20length=20?= =?UTF-8?q?of=20list=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For #7532 --- core/include/ldml/keyboardprocessor_ldml.h | 2 +- core/include/ldml/keyboardprocessor_ldml.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/include/ldml/keyboardprocessor_ldml.h b/core/include/ldml/keyboardprocessor_ldml.h index f042865ec9..0462ae9850 100644 --- a/core/include/ldml/keyboardprocessor_ldml.h +++ b/core/include/ldml/keyboardprocessor_ldml.h @@ -53,7 +53,7 @@ #define LDML_LENGTH_LAYR_KEY 0x4 #define LDML_LENGTH_LAYR_LIST 0x10 #define LDML_LENGTH_LAYR_ROW 0x8 -#define LDML_LENGTH_LIST 0x20 +#define LDML_LENGTH_LIST 0x10 #define LDML_LENGTH_LIST_INDEX 0x4 #define LDML_LENGTH_LIST_ITEM 0x8 #define LDML_LENGTH_LOCA 0x10 diff --git a/core/include/ldml/keyboardprocessor_ldml.ts b/core/include/ldml/keyboardprocessor_ldml.ts index 0c33c21d56..8eada52084 100644 --- a/core/include/ldml/keyboardprocessor_ldml.ts +++ b/core/include/ldml/keyboardprocessor_ldml.ts @@ -314,7 +314,7 @@ class Constants { /** * Minimum length of the 'list' section not including variable parts */ - readonly length_list = 32; + readonly length_list = 16; /** * Length of each list item in the 'list' list section variable part */ From 208a6e40a0713ec75bf0e1a62c4be51981df500a Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Thu, 17 Nov 2022 14:17:55 -0600 Subject: [PATCH 06/33] =?UTF-8?q?feat(core):=20ldml:=20C++=20builds=20?= =?UTF-8?q?=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For #7532 --- common/web/types/src/kmx/kmx-plus.ts | 2 +- core/src/kmx/kmx_plus.cpp | 14 +++++++++ core/src/kmx/kmx_plus.h | 47 +++++++++++++++++++++++++++- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/common/web/types/src/kmx/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus.ts index 2690f10d46..448876938f 100644 --- a/common/web/types/src/kmx/kmx-plus.ts +++ b/common/web/types/src/kmx/kmx-plus.ts @@ -262,7 +262,7 @@ export interface KMXPlusData { sect?: Strs; // sect is ignored in-memory bksp?: Bksp; disp?: Disp; - elem?: Elem; // elem is ignored in-memory + elem?: Elem; // elem is ignored in-mxemory finl?: Finl; key2?: Key2; keys?: Keys; diff --git a/core/src/kmx/kmx_plus.cpp b/core/src/kmx/kmx_plus.cpp index 87fbaa12d2..5be21fba67 100644 --- a/core/src/kmx/kmx_plus.cpp +++ b/core/src/kmx/kmx_plus.cpp @@ -480,6 +480,20 @@ COMP_KMXPLUS_LAYR_Helper::getKey(KMX_DWORD key) const { return keys + key; } +bool +COMP_KMXPLUS_KEY2::valid(KMX_DWORD _kmn_unused(length)) const { + // TODO-LDML more to do here + DebugLog("TODO-LDML: key2"); + return true; +} + +bool +COMP_KMXPLUS_LIST::valid(KMX_DWORD _kmn_unused(length)) const { + // TODO-LDML more to do here + DebugLog("TODO-LDML: list"); + return true; +} + // ---- constructor kmx_plus::kmx_plus(const COMP_KEYBOARD *keyboard, size_t length) diff --git a/core/src/kmx/kmx_plus.h b/core/src/kmx/kmx_plus.h index 971d0067c6..69ab05c6f1 100644 --- a/core/src/kmx/kmx_plus.h +++ b/core/src/kmx/kmx_plus.h @@ -404,7 +404,7 @@ static_assert(sizeof(struct COMP_KMXPLUS_LAYR_ROW) == LDML_LENGTH_LAYR_ROW, "mis struct COMP_KMXPLUS_LAYR_KEY { - KMX_DWORD vkey; + KMX_DWORD key; // index into key2 section }; static_assert(sizeof(struct COMP_KMXPLUS_LAYR_KEY) == LDML_LENGTH_LAYR_KEY, "mismatched size of COMP_KMXPLUS_LAYR_KEY"); @@ -460,6 +460,51 @@ private: static_assert(sizeof(struct COMP_KMXPLUS_LAYR) % 0x10 == 0, "Structs prior to entries[] should align to 128-bit boundary"); static_assert(sizeof(struct COMP_KMXPLUS_LAYR) == LDML_LENGTH_LAYR, "mismatched size of section layr"); +/* ------------------------------------------------------------------ + * key2 section + ------------------------------------------------------------------ */ +struct COMP_KMXPLUS_KEY2 { + static const KMX_DWORD IDENT = LDML_SECTIONID_KEY2; + COMP_KMXPLUS_HEADER header; + KMX_DWORD keyCount; + KMX_DWORD flicksCount; + KMX_DWORD flickCount; + KMX_DWORD reserved[3]; + // TODO-LDML: keys sub-table + // TODO-LDML: flick lists sub-table + // TODO-LDML: flick elements sub-table + + /** + * @brief True if section is valid. + */ + bool valid(KMX_DWORD length) const; +}; + +static_assert(sizeof(struct COMP_KMXPLUS_KEY2) % 0x10 == 0, "Structs prior to entries[] should align to 128-bit boundary"); +static_assert(sizeof(struct COMP_KMXPLUS_KEY2) == LDML_LENGTH_KEY2, "mismatched size of section key2"); + + +/* ------------------------------------------------------------------ + * list section + ------------------------------------------------------------------ */ +struct COMP_KMXPLUS_LIST { + static const KMX_DWORD IDENT = LDML_SECTIONID_LIST; + COMP_KMXPLUS_HEADER header; + KMX_DWORD listCount; + KMX_DWORD indexCount; + // TODO-LDML: lists sub-table + // TODO-LDML: indices sub-table + + /** + * @brief True if section is valid. + */ + bool valid(KMX_DWORD length) const; +}; + +static_assert(sizeof(struct COMP_KMXPLUS_LIST) % 0x10 == 0, "Structs prior to entries[] should align to 128-bit boundary"); +static_assert(sizeof(struct COMP_KMXPLUS_LIST) == LDML_LENGTH_LIST, "mismatched size of section list"); + + /** * @brief helper accessor object for KMX Plus data * From bccb64c9115df4d522574f06bff145adc4104a2e Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Thu, 17 Nov 2022 15:37:25 -0600 Subject: [PATCH 07/33] =?UTF-8?q?feat(core):=20ldml:=20kmx-plus.ts=20for?= =?UTF-8?q?=20layr/key2/list=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - update kmx-plus for layr/key2/list For #7532 --- common/web/types/src/kmx/kmx-plus.ts | 163 +++++++++++++++++- common/web/types/src/kmx/string-list.ts | 35 ++++ .../ldml-keyboard/ldml-keyboard-xml-reader.ts | 6 + core/src/kmx/kmx_plus.cpp | 2 + core/src/kmx/kmx_plus.h | 2 + core/src/ldml/C7043_ldml.md | 5 +- .../src/kmc-keyboard/src/compiler/compiler.ts | 2 +- .../src/kmc-keyboard/test/helpers/index.ts | 3 +- 8 files changed, 211 insertions(+), 7 deletions(-) create mode 100644 common/web/types/src/kmx/string-list.ts diff --git a/common/web/types/src/kmx/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus.ts index 448876938f..bf7f62be9b 100644 --- a/common/web/types/src/kmx/kmx-plus.ts +++ b/common/web/types/src/kmx/kmx-plus.ts @@ -1,6 +1,7 @@ import { constants } from '@keymanapp/ldml-keyboard-constants'; import * as r from 'restructure'; import { ElementString } from './element-string.js'; +import { StringList } from './string-list.js'; import { KMXFile } from './kmx.js'; @@ -15,6 +16,7 @@ export class GlobalSections { // These sections are used by other sections during compilation strs: Strs; elem: Elem; + list: List; } // 'sect' @@ -114,7 +116,7 @@ export class StrsItem { constructor(value: string) { this.value = value; } -} +}; export class Strs extends Section { strings: StrsItem[] = [ new StrsItem('') ]; // C7043: The null string is always requierd @@ -250,14 +252,52 @@ export class Layr extends Section { vkeys: number[] = []; }; -export class Key2 { +export class Key2Keys { + vkey: number; + to: StrsItem; + flags: number; + id: StrsItem; + switch: StrsItem; + width: number; + longPress: StringList; + longPressDefault: StrsItem; + multiTap: StringList; + flicks: number; +}; + +export class Key2Flicks { + count: number; + flick: number; + id: StrsItem; +}; + +export class Key2Flick { + directions: StringList; + flags: number; + to: StrsItem; +}; + +export class Key2 extends Section { + keyCount: number; + flicksCount: number; + flickCount: number; + keys: Key2Keys[] = []; + flicks: Key2Flicks[] = []; + flick: Key2Flick[] = []; +}; + +export class List extends Section { // TODO-LDML }; -export class List { - // TODO-LDML +export class ListItem { + readonly value: string[]; + constructor(value: string[]) { + this.value = value; + } }; + export interface KMXPlusData { sect?: Strs; // sect is ignored in-memory bksp?: Bksp; @@ -302,6 +342,21 @@ export class KMXPlusFile extends KMXFile { public readonly COMP_PLUS_KEYS_ITEM: any; public readonly COMP_PLUS_KEYS: any; + public readonly COMP_PLUS_LAYR_ENTRY: any; + public readonly COMP_PLUS_LAYR_KEY: any; + public readonly COMP_PLUS_LAYR_LIST: any; + public readonly COMP_PLUS_LAYR_ROW: any; + public readonly COMP_PLUS_LAYR: any; + + public readonly COMP_PLUS_KEY2_FLICK: any; + public readonly COMP_PLUS_KEY2_FLICKS: any; + public readonly COMP_PLUS_KEY2_KEY: any; + public readonly COMP_PLUS_KEY2: any; + + public readonly COMP_PLUS_LIST_LIST: any; + public readonly COMP_PLUS_LIST_INDEX: any; + public readonly COMP_PLUS_LIST: any; + public readonly COMP_PLUS_LOCA_ITEM: any; public readonly COMP_PLUS_LOCA: any; @@ -406,6 +461,106 @@ export class KMXPlusFile extends KMXFile { items: new r.Array(this.COMP_PLUS_KEYS_ITEM, 'count') }); + // 'layr' + + this.COMP_PLUS_LAYR_ENTRY = new r.Struct({ + id: r.uint32le, // str + modifier: r.uint32le, // str + row: r.uint32le, // index into rows + count: r.uint32le, + }); + + this.COMP_PLUS_LAYR_KEY = new r.Struct({ + key: r.uint32le, // index into key2 + }); + + this.COMP_PLUS_LAYR_LIST = new r.Struct({ + flags: r.uint32le, + hardware: r.uint32le, //str + layer: r.uint32le, // index into layers + count: r.uint32le, + }); + + this.COMP_PLUS_LAYR_ROW = new r.Struct({ + key: r.uint32le, + count: r.uint32le, + }); + + this.COMP_PLUS_LAYR = new r.Struct({ + ident: r.uint32le, + size: r.uint32le, + listCount: r.uint32le, + layerCount: r.uint32le, + rowCount: r.uint32le, + keyCount: r.uint32le, + reserved0: new r.Reserved(r.uint32le), + reserved1: new r.Reserved(r.uint32le), + lists: new r.Array(this.COMP_PLUS_LAYR_LIST, 'listCount'), + layers: new r.Array(this.COMP_PLUS_LAYR_ENTRY, 'layerCount'), + rows: new r.Array(this.COMP_PLUS_LAYR_ROW, 'rowCount'), + keys: new r.Array(this.COMP_PLUS_LAYR_KEY, 'keyCount'), + }); + + this.COMP_PLUS_KEY2_FLICK = new r.Struct({ + directions: r.uint32le, // list + flags: r.uint32le, + to: r.uint32le, // str | codepoint + }); + + this.COMP_PLUS_KEY2_FLICKS = new r.Struct({ + count: r.uint32le, + flick: r.uint32le, + id: r.uint32le, // str + }); + + this.COMP_PLUS_KEY2_KEY = new r.Struct({ + vkey: r.uint32le, + to: r.uint32le, // str | codepoint + flags: r.uint32le, + id: r.uint32le, // str + switch: r.uint32le, // str + width: r.uint32le, // width*10 ( 1 = 0.1 keys) + longPress: r.uint32le, // list index + longPressDefault: r.uint32le, // str + multiTap: r.uint32le, // list index + flicks: r.uint32le, // index into flicks table + }); + + this.COMP_PLUS_KEY2 = new r.Struct({ + ident: r.uint32le, + size: r.uint32le, + keyCount: r.uint32le, + flicksCount: r.uint32le, + flickCount: r.uint32le, + reserved0: new r.Reserved(r.uint32le), + reserved1: new r.Reserved(r.uint32le), + reserved2: new r.Reserved(r.uint32le), + keys: new r.Array(this.COMP_PLUS_KEY2_KEY, 'keyCount'), + flicks: new r.Array(this.COMP_PLUS_KEY2_FLICKS, 'flicksCount'), + flick: new r.Array(this.COMP_PLUS_KEY2_FLICK, 'flickCount'), + }); + + // 'list' + + this.COMP_PLUS_LIST_LIST = new r.Struct({ + index: r.uint32le, + count: r.uint32le, + }); + + this.COMP_PLUS_LIST_INDEX = new r.Struct({ + str: r.uint32le, // str + }); + + this.COMP_PLUS_LIST = new r.Struct({ + ident: r.uint32le, + size: r.uint32le, + listCount: r.uint32le, + indexCount: r.uint32le, + lists: new r.Array(this.COMP_PLUS_LIST_LIST, 'listCount'), + indices: new r.Array(this.COMP_PLUS_LIST_INDEX, 'indexCount'), + }); + + // 'loca' this.COMP_PLUS_LOCA_ITEM = r.uint32le; //str diff --git a/common/web/types/src/kmx/string-list.ts b/common/web/types/src/kmx/string-list.ts new file mode 100644 index 0000000000..0f52d4eb3e --- /dev/null +++ b/common/web/types/src/kmx/string-list.ts @@ -0,0 +1,35 @@ +// import { constants } from '@keymanapp/ldml-keyboard-constants'; +import { Strs, StrsItem } from './kmx-plus.js'; + +export class ListIndex { + value: StrsItem; // will become index into Strs table + isEqual(a: ListIndex) { + return a.value === this.value; + } +}; + +export class StringList extends Array { + constructor(strs: Strs, source: Array) { + super(); + if(!source) { + return; + } + + for (const str of source) { + let index = new ListIndex(); + index.value = strs.allocString(str); + this.push(index); + } + } + isEqual(a: StringList): boolean { + if (a.length != this.length) { + return false; + } + for (let i = 0; i < a.length; i++) { + if (!this[i].isEqual(a[i])) { + return false; + } + } + return true; + } +}; 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 6d34a8a309..33c30e8bfc 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 @@ -15,6 +15,7 @@ export default class LDMLKeyboardXMLSourceFileReader { boxXmlArray(source?.keyboard?.names, 'name'); boxXmlArray(source?.keyboard?.vkeys, 'vkey'); boxXmlArray(source?.keyboard?.keys, 'key'); + boxXmlArray(source?.keyboard?.keys, 'flicks'); boxXmlArray(source?.keyboard?.locales, 'locale'); boxXmlArray(source?.keyboard, 'transforms'); if(source?.keyboard?.layers) { @@ -27,6 +28,11 @@ export default class LDMLKeyboardXMLSourceFileReader { } } } + if(source?.keyboard?.keys?.flicks) { + for(let flicks of source?.keyboard?.keys?.flicks) { + boxXmlArray(flicks, 'flick'); + } + } if(source?.keyboard?.transforms) { for(let transform of source.keyboard.transforms) { boxXmlArray(transform, 'transform'); diff --git a/core/src/kmx/kmx_plus.cpp b/core/src/kmx/kmx_plus.cpp index 5be21fba67..7e810f542f 100644 --- a/core/src/kmx/kmx_plus.cpp +++ b/core/src/kmx/kmx_plus.cpp @@ -532,8 +532,10 @@ kmx_plus::kmx_plus(const COMP_KEYBOARD *keyboard, size_t length) // these will be nullptr if they don't validate disp = section_from_sect(sect); elem = section_from_sect(sect); + key2 = section_from_sect(sect); keys = section_from_sect(sect); layr = section_from_sect(sect); + list = section_from_sect(sect); loca = section_from_sect(sect); meta = section_from_sect(sect); strs = section_from_sect(sect); diff --git a/core/src/kmx/kmx_plus.h b/core/src/kmx/kmx_plus.h index 69ab05c6f1..9a2450a10b 100644 --- a/core/src/kmx/kmx_plus.h +++ b/core/src/kmx/kmx_plus.h @@ -523,8 +523,10 @@ class kmx_plus { // keep the next elements sorted const COMP_KMXPLUS_DISP *disp; const COMP_KMXPLUS_ELEM *elem; + const COMP_KMXPLUS_KEY2 *key2; const COMP_KMXPLUS_KEYS *keys; const COMP_KMXPLUS_LAYR *layr; + const COMP_KMXPLUS_LIST *list; const COMP_KMXPLUS_LOCA *loca; const COMP_KMXPLUS_META *meta; const COMP_KMXPLUS_SECT *sect; diff --git a/core/src/ldml/C7043_ldml.md b/core/src/ldml/C7043_ldml.md index f8db54b0a6..55f0585c6e 100644 --- a/core/src/ldml/C7043_ldml.md +++ b/core/src/ldml/C7043_ldml.md @@ -338,10 +338,13 @@ Represents layers on the keyboard. |16 | 32 | rowCount | int: number of row entries | |20 | 32 | keyCount | int: number of key entries | |24 | 64 | reserved | padding | -|32 | var | layers | layers sub-table | +|32 | var | lists | layer list sub-table | +| - | var | layers | layers sub-table | | - | var | rows | rows sub-table | | - | var | keys | keys sub-table | +### `layr.lists` subtable + Each layer list corresponds to one `` element. There are `listCount` total lists. diff --git a/developer/src/kmc-keyboard/src/compiler/compiler.ts b/developer/src/kmc-keyboard/src/compiler/compiler.ts index 092403b27a..aa958f41b0 100644 --- a/developer/src/kmc-keyboard/src/compiler/compiler.ts +++ b/developer/src/kmc-keyboard/src/compiler/compiler.ts @@ -103,7 +103,7 @@ export default class Compiler { // errors for the keyboard developer. continue; } - const sect = section.compile({strs: kmx.kmxplus.strs, elem: kmx.kmxplus.elem}); + const sect = section.compile({strs: kmx.kmxplus.strs, elem: kmx.kmxplus.elem, list: kmx.kmxplus.list}); /* istanbul ignore if */ if(!sect) { diff --git a/developer/src/kmc-keyboard/test/helpers/index.ts b/developer/src/kmc-keyboard/test/helpers/index.ts index 4453fe1ef1..c546539d8c 100644 --- a/developer/src/kmc-keyboard/test/helpers/index.ts +++ b/developer/src/kmc-keyboard/test/helpers/index.ts @@ -82,7 +82,8 @@ export function loadSectionFixture(compilerClass: typeof SectionCompiler, filena let globalSections: GlobalSections = { strs: new Strs(), - elem: null + elem: null, + list: null, }; globalSections.elem = new Elem(globalSections.strs); From 3496939073fc0fde46bd9ba11b8bd6c6f19c7dfa Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Fri, 18 Nov 2022 17:21:37 -0600 Subject: [PATCH 08/33] =?UTF-8?q?feat(core):=20ldml:=20steps=20for=20list/?= =?UTF-8?q?key2=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - update kmx-plus.ts for key2 and list sections - add tests for key2 - TODO: binary write, basic fixture, layr For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- common/web/types/src/kmx/kmx-plus.ts | 56 ++++++----- common/web/types/src/kmx/string-list.ts | 12 ++- core/src/ldml/C7043_ldml.md | 3 + .../src/kmc-keyboard/src/compiler/compiler.ts | 3 + .../src/kmc-keyboard/src/compiler/key2.ts | 98 +++++++++++++++++++ .../test/fixtures/sections/key2/flicks.xml | 0 .../test/fixtures/sections/key2/maximal.xml | 37 +++++++ .../src/kmc-keyboard/test/helpers/index.ts | 2 + developer/src/kmc-keyboard/test/test-key2.ts | 66 +++++++++++++ 9 files changed, 249 insertions(+), 28 deletions(-) create mode 100644 developer/src/kmc-keyboard/src/compiler/key2.ts create mode 100644 developer/src/kmc-keyboard/test/fixtures/sections/key2/flicks.xml create mode 100644 developer/src/kmc-keyboard/test/fixtures/sections/key2/maximal.xml create mode 100644 developer/src/kmc-keyboard/test/test-key2.ts diff --git a/common/web/types/src/kmx/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus.ts index bf7f62be9b..6810f5343c 100644 --- a/common/web/types/src/kmx/kmx-plus.ts +++ b/common/web/types/src/kmx/kmx-plus.ts @@ -1,7 +1,7 @@ import { constants } from '@keymanapp/ldml-keyboard-constants'; import * as r from 'restructure'; import { ElementString } from './element-string.js'; -import { StringList } from './string-list.js'; +import { ListItem } from './string-list.js'; import { KMXFile } from './kmx.js'; @@ -253,61 +253,69 @@ export class Layr extends Section { }; export class Key2Keys { - vkey: number; - to: StrsItem; flags: number; + flicks: string; // for in-memory only id: StrsItem; - switch: StrsItem; - width: number; - longPress: StringList; + longPress: ListItem; longPressDefault: StrsItem; - multiTap: StringList; - flicks: number; + multiTap: ListItem; + switch: StrsItem; + to: StrsItem; + vkey: number; + width: number; }; export class Key2Flicks { - count: number; - flick: number; + flicks: Key2Flick[] = []; id: StrsItem; }; export class Key2Flick { - directions: StringList; + directions: ListItem; flags: number; to: StrsItem; }; export class Key2 extends Section { - keyCount: number; - flicksCount: number; - flickCount: number; keys: Key2Keys[] = []; flicks: Key2Flicks[] = []; - flick: Key2Flick[] = []; }; export class List extends Section { - // TODO-LDML -}; - -export class ListItem { - readonly value: string[]; - constructor(value: string[]) { - this.value = value; + allocListFromSpaces(strs: Strs, s?: string): ListItem { + if(s === undefined || s === null) { + s = ''; + } + // TODO-LDML: support unicode escaping etc + return this.allocList(strs, s.split(' ')); } + allocList(strs: Strs, s?: string[]): ListItem { + let result = this.lists.find(item => item.isEqual(s)); + if(result === undefined) { + result = new ListItem(strs, s); + this.lists.push(result); + } + return result; + } + constructor(strs: Strs) { + super(); + this.lists.push(new ListItem(strs, [])); // C7043: null element string + } + lists: ListItem[] = []; }; +export { ListItem as ListItem }; export interface KMXPlusData { sect?: Strs; // sect is ignored in-memory bksp?: Bksp; disp?: Disp; - elem?: Elem; // elem is ignored in-mxemory + elem?: Elem; // elem is ignored in-memory finl?: Finl; key2?: Key2; keys?: Keys; layr?: Layr; - list?: List; + list?: List; // list is ignored in-memory loca?: Loca; meta?: Meta; name?: Name; diff --git a/common/web/types/src/kmx/string-list.ts b/common/web/types/src/kmx/string-list.ts index 0f52d4eb3e..456d0766c5 100644 --- a/common/web/types/src/kmx/string-list.ts +++ b/common/web/types/src/kmx/string-list.ts @@ -3,12 +3,16 @@ import { Strs, StrsItem } from './kmx-plus.js'; export class ListIndex { value: StrsItem; // will become index into Strs table - isEqual(a: ListIndex) { - return a.value === this.value; + isEqual(a: ListIndex | string) { + // so we can compare this to a string + return a.toString() === this.toString(); + } + toString(): string { + return this.value.value; } }; -export class StringList extends Array { +export class ListItem extends Array { constructor(strs: Strs, source: Array) { super(); if(!source) { @@ -21,7 +25,7 @@ export class StringList extends Array { this.push(index); } } - isEqual(a: StringList): boolean { + isEqual(a: ListItem | string[]): boolean { if (a.length != this.length) { return false; } diff --git a/core/src/ldml/C7043_ldml.md b/core/src/ldml/C7043_ldml.md index 55f0585c6e..77a78ac6b5 100644 --- a/core/src/ldml/C7043_ldml.md +++ b/core/src/ldml/C7043_ldml.md @@ -475,6 +475,7 @@ For each flicks in the flick list: Elements are ordered by the string id. +If this section is present, it must have a 'flicks' in the list at position zero with count=0, index=0 and id=0 meaning 'no flicks'. #### `key2.flick` flick element subtable For each flick element: @@ -485,6 +486,8 @@ For each flick element: | 8+| 32 | flags | int: per-key flags | |12+| 32 | to | str: output string | +If this section is present, it must have a 'flick element' at position zero with directions=0, flags=0, and to=0 meaning 'no flick'. + There is not a 'null' flick element at the end of each list. Elements are ordered by the `flicks.id`, and secondarily by the directions list id. diff --git a/developer/src/kmc-keyboard/src/compiler/compiler.ts b/developer/src/kmc-keyboard/src/compiler/compiler.ts index aa958f41b0..765df112d4 100644 --- a/developer/src/kmc-keyboard/src/compiler/compiler.ts +++ b/developer/src/kmc-keyboard/src/compiler/compiler.ts @@ -3,6 +3,7 @@ import CompilerCallbacks from './callbacks.js'; import CompilerOptions from './compiler-options.js'; import { DispCompiler } from './disp.js'; import { KeysCompiler } from './keys.js'; +import { Key2Compiler } from './key2.js'; import { LocaCompiler } from './loca.js'; import { CompilerMessages } from './messages.js'; import { MetaCompiler } from './meta.js'; @@ -17,6 +18,7 @@ import KMXPlusFile = KMXPlus.KMXPlusFile; const SECTION_COMPILERS = [ BkspCompiler, DispCompiler, + Key2Compiler, KeysCompiler, FinlCompiler, LocaCompiler, @@ -95,6 +97,7 @@ export default class Compiler { // These two sections are required by other sections kmx.kmxplus.strs = new KMXPlus.Strs(); kmx.kmxplus.elem = new KMXPlus.Elem(kmx.kmxplus.strs); + kmx.kmxplus.list = new KMXPlus.List(kmx.kmxplus.strs); for(let section of sections) { if(!section.validate()) { diff --git a/developer/src/kmc-keyboard/src/compiler/key2.ts b/developer/src/kmc-keyboard/src/compiler/key2.ts new file mode 100644 index 0000000000..544722676b --- /dev/null +++ b/developer/src/kmc-keyboard/src/compiler/key2.ts @@ -0,0 +1,98 @@ +import { constants } from '@keymanapp/ldml-keyboard-constants'; +import { /*LDMLKeyboard,*/ KMXPlus, /*Constants*/ } from '@keymanapp/common-types'; +// import { CompilerMessages } from './messages.js'; +import { SectionCompiler } from "./section-compiler.js"; + +import GlobalSections = KMXPlus.GlobalSections; +import Key2 = KMXPlus.Key2; +import ListItem = KMXPlus.ListItem; +import { Key2Flicks } from '@keymanapp/common-types/src/kmx/kmx-plus.js'; +// import USVirtualKeyMap = Constants.USVirtualKeyMap; + +export class Key2Compiler extends SectionCompiler { + + public get id() { + return constants.section.key2; + } + + public validate() { + let valid = true; + return valid; + } + + public compile(sections: GlobalSections): Key2 { + if (!this.keyboard.keys.key && !this.keyboard.keys.flicks) { + // short-circuit if no keys or flicks + return null; + } + + let sect = new Key2(); + + // Load the flicks first + this.loadFlicks(sections, sect); + + // Now, load the keys + this.loadKeys(sections, sect); + + return sect; + } + + public loadFlicks(sections: GlobalSections, sect: Key2) { + for (let lkflicks of this.keyboard.keys.flicks) { + let flicks: Key2Flicks = { + id: sections.strs.allocString(lkflicks.id), + flicks: [] + }; + + for (let lkflick of lkflicks.flick) { + let flags = 0; + const to = sections.strs.allocString(lkflick.to); + flags |= constants.key2_flick_flags_extend; + let directions : ListItem = sections.list.allocListFromSpaces(sections.strs, lkflick.directions); + flicks.flicks.push({ + directions, + flags, + to, + }); + } + + sect.flicks.push(flicks); + } + } + + public loadKeys(sections: GlobalSections, sect: Key2) { + for (let key of this.keyboard.keys.key) { + let flags = 0; + const flicks = key.flicks; + // TODO-LDML: verify that this flick id exists + if (!!key.gap) { + flags |= constants.key2_key_flags_gap; + } + if (key.transform === 'no') { + flags |= constants.key2_key_flags_notransform; + } + const id = sections.strs.allocString(key.id); + const longPress : ListItem = sections.list.allocListFromSpaces(sections.strs, key.longPress); + const longPressDefault = sections.strs.allocString(key.longPressDefault); + const multiTap : ListItem = sections.list.allocListFromSpaces(sections.strs, key.multiTap); + const keySwitch = sections.strs.allocString(key.switch); // 'switch' is a reserved word + flags |= constants.key2_key_flags_extend; + const to = sections.strs.allocString(key.to); // TODO-LDML: single char + const width = Math.ceil(key.width * 10.0); + const vkey: any = null; // TODO-LDML: fill in later + sect.keys.push({ + flags, + flicks, + id, + longPress, + longPressDefault, + multiTap, + switch: keySwitch, // 'switch' is a reserved word + to, + vkey, + width, + }); + } + } + +} diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/key2/flicks.xml b/developer/src/kmc-keyboard/test/fixtures/sections/key2/flicks.xml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/key2/maximal.xml b/developer/src/kmc-keyboard/test/fixtures/sections/key2/maximal.xml new file mode 100644 index 0000000000..cb1dbd0573 --- /dev/null +++ b/developer/src/kmc-keyboard/test/fixtures/sections/key2/maximal.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/developer/src/kmc-keyboard/test/helpers/index.ts b/developer/src/kmc-keyboard/test/helpers/index.ts index c546539d8c..c3a33755e4 100644 --- a/developer/src/kmc-keyboard/test/helpers/index.ts +++ b/developer/src/kmc-keyboard/test/helpers/index.ts @@ -18,6 +18,7 @@ import Elem = KMXPlus.Elem; import GlobalSections = KMXPlus.GlobalSections; import Section = KMXPlus.Section; import Strs = KMXPlus.Strs; +import List = KMXPlus.List; /** * Builds a path to the fixture with the given path components. @@ -86,6 +87,7 @@ export function loadSectionFixture(compilerClass: typeof SectionCompiler, filena list: null, }; globalSections.elem = new Elem(globalSections.strs); + globalSections.list = new List(globalSections.strs); return compiler.compile(globalSections); } diff --git a/developer/src/kmc-keyboard/test/test-key2.ts b/developer/src/kmc-keyboard/test/test-key2.ts new file mode 100644 index 0000000000..61bb0881d5 --- /dev/null +++ b/developer/src/kmc-keyboard/test/test-key2.ts @@ -0,0 +1,66 @@ +import 'mocha'; +import { assert } from 'chai'; +import { Key2Compiler } from '../src/compiler/key2.js'; +import { compilerTestCallbacks, loadSectionFixture } from './helpers/index.js'; +import { KMXPlus } from '@keymanapp/common-types'; +// import { CompilerMessages } from '../src/compiler/messages.js'; +import { constants } from '@keymanapp/ldml-keyboard-constants'; + +import Key2 = KMXPlus.Key2; + +describe('key2', function () { + this.slow(500); // 0.5 sec -- json schema validation takes a while + + it('should compile minimal keys data', function () { + let key2 = loadSectionFixture(Key2Compiler, 'sections/keys/minimal.xml', compilerTestCallbacks) as Key2; + assert.ok(key2); + // assert.equal(compilerTestCallbacks.messages.length, 0); + assert.equal(key2.keys.length, 1); + assert.equal(key2.flicks.length, 0); + assert.equal(key2.keys[0].to.value, '🪦'); + assert.equal(key2.keys[0].id.value, 'grave'); + }); + + it('should compile maximal key2 data', function () { + let key2 = loadSectionFixture(Key2Compiler, 'sections/key2/maximal.xml', compilerTestCallbacks) as Key2; + assert.ok(key2); + assert.equal(compilerTestCallbacks.messages.length, 0); + assert.equal(key2.keys.length, 4); + + const [q] = key2.keys.filter(({ id }) => id.value === 'q'); + assert.ok(q); + assert.isFalse(!!(q.flags & constants.key2_key_flags_gap)); + assert.equal(q.width, 32); // ceil(3.1 * 10) + assert.equal(q.flicks, 'flick0'); // note this is a string, not a StrsItem + + const [flick0] = key2.flicks.filter(({ id }) => id.value === 'flick0'); + assert.ok(flick0); + assert.equal(flick0.flicks.length, 2); + + const [flick0_nw_se] = flick0.flicks.filter(({ directions }) => directions && directions.isEqual('nw se'.split(' '))); + assert.ok(flick0_nw_se); + assert.equal(flick0_nw_se.to?.value, 'ç'); + + const [flick0_ne_sw] = flick0.flicks.filter(({ directions }) => directions && directions.isEqual('ne sw'.split(' '))); + assert.ok(flick0_ne_sw); + assert.equal(flick0_ne_sw.to?.value, 'ê'); + }); + + it('should accept layouts with gap/switch keys', function () { + let key2 = loadSectionFixture(Key2Compiler, 'sections/keys/gap-switch.xml', compilerTestCallbacks) as Key2; + assert.ok(key2); + assert.equal(compilerTestCallbacks.messages.length, 0); + assert.equal(key2.keys.length, 4); + + const [Qgap] = key2.keys.filter(({ id }) => id.value === 'Q'); + assert.ok(Qgap); + assert.isTrue(!!(Qgap.flags & constants.key2_key_flags_gap)); + + const [Wshift] = key2.keys.filter(({ id }) => id.value === 'W'); + assert.isNotNull(Wshift); + assert.isFalse(!!(Wshift.flags & constants.key2_key_flags_gap)); + assert.equal(Wshift.switch.value, 'shift'); + + }); + +}); From 2d05c8f379f5dca54aafc75db33ddb123d4d3d83 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Tue, 22 Nov 2022 18:45:33 -0600 Subject: [PATCH 09/33] =?UTF-8?q?spec(core):=20ldml:=20document=20KMXPlus?= =?UTF-8?q?=20update=20process=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- core/src/ldml/C7532_ldml_updating.md | 59 ++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 core/src/ldml/C7532_ldml_updating.md diff --git a/core/src/ldml/C7532_ldml_updating.md b/core/src/ldml/C7532_ldml_updating.md new file mode 100644 index 0000000000..fc89add74a --- /dev/null +++ b/core/src/ldml/C7532_ldml_updating.md @@ -0,0 +1,59 @@ +# How to update KMXPlus sections + +A diary. +By Steven R. Loomis + +Keyman Section Update Journal + +working on ‘layr’, using ‘disp’ as a model from https://github.com/keymanapp/keyman/pull/7568 + +## Constants and Scaffolding + +- *Edit/Commit*: `core/include/ldml/keyboardprocessor_ldml.ts` + - update `SectionIdent` and keep in order + - update `SectionMap` and keep in order + - add a comment block in order `layr section` + - add `length_layr` with the nonvariable length + - add a `length_layr_*` for each subitem + - add parameters for each flag/bitfield + - Check indentation, check for copypasta errs! +- Run: `./core/tools/ldml-const-builder/build.sh clean build run` +- Verify/Commit: `core/include/ldml/keyboardprocessor_ldml.h` + +## XML changes + +- `resources/standards-data/ldml-keyboards/techpreview/` : update / reimport / fix fixup script if needed +- E/C: `common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts` + - add to `LKKeyboard` and subproperties as needed to support the structure on the XML side +- Now would be a good time to stop and make sure everything compiles. It didn’t, there was an unrelated issue with snprintf! + +## In-memory data: Phase 1 + +- `common/web/types/src/kmx/kmx-plus.ts` + - update `KMXPlusData` to include new section + - add the new section and any in-memory data for the compiler + - It’s enough temporarily to add `export class Sect extends Section{ /* TODO-LDML */};` for now so that it compiles, and come back to it +- `core/src/kmx/kmx_plus.h` + - add new structs +- `core/src/kmx/kmx_plus.cpp` + - add validate implementation for the section and any new structs + - update `kmx_plus::kmx_plus()` to include the loader +- `common/web/types/src/kmx/kmx-plus.ts` + - Also update class KMXPlusFile to include the actual binary format (coordinate with `kmx_plus.h`) +- E/C `common/web/types/src/ldml-keyboard/ldml-keyboard-xml-reader.ts` if there’s any changing to the boxing needed + +## In-memory data: Phase 2 + +- first the compiler tests + - add a section in `developer/src/kmc-keyboard/test/fixtures/sections` if needed + - add a test case such as `developer/src/kmc-keyboard/src/compiler/layr.ts` +- add a compiler + - `developer/src/kmc-keyboard/compiler/key2.ts` + +## Writing out + +- update basic.xml and basic.txt + - TODO-LDML: regen fixtures + - … BUT DO NOT CHECK THEM IN! + +## more to come From e0dcb30b511d5a8336b28671bba3efa2a8eb4845 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Tue, 22 Nov 2022 19:11:53 -0600 Subject: [PATCH 10/33] =?UTF-8?q?spec(core):=20ldml:=20more=20document=20K?= =?UTF-8?q?MXPlus=20update=20process=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - add some comments to kmx-plus.ts For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- common/web/types/src/kmx/kmx-plus.ts | 2 ++ core/src/ldml/C7532_ldml_updating.md | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/common/web/types/src/kmx/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus.ts index 6810f5343c..008b70007f 100644 --- a/common/web/types/src/kmx/kmx-plus.ts +++ b/common/web/types/src/kmx/kmx-plus.ts @@ -448,6 +448,7 @@ export class KMXPlusFile extends KMXFile { count: r.uint32le, reserved: new r.Reserved(r.uint32le), // padding strings: new r.Array(this.COMP_PLUS_ELEM_STRING, 'count') + // + variable subtable: Element data (see KMXPlusBuilder.emitElements()) }); // 'finl' - see 'tran' @@ -637,6 +638,7 @@ export class KMXPlusFile extends KMXFile { count: r.uint32le, reserved: new r.Reserved(r.uint32le), // padding items: new r.Array(this.COMP_PLUS_STRS_ITEM, 'count') + // + variable subtable: String data (see KMXPlusBuilder.emitStrings()) }); // 'tran' diff --git a/core/src/ldml/C7532_ldml_updating.md b/core/src/ldml/C7532_ldml_updating.md index fc89add74a..13c57089cb 100644 --- a/core/src/ldml/C7532_ldml_updating.md +++ b/core/src/ldml/C7532_ldml_updating.md @@ -49,11 +49,21 @@ working on ‘layr’, using ‘disp’ as a model from https://github.com/keyma - add a test case such as `developer/src/kmc-keyboard/src/compiler/layr.ts` - add a compiler - `developer/src/kmc-keyboard/compiler/key2.ts` + - Note: the in-memory compiler can affect `basic.kmx` even _before_ you add the section writing code. How? Simple… `Strs.allocString()` is called for the in-memory structures, so the string table will start growing even before those strings are actually used by the new sections. This is why it's fine to ignore the basic failure until you actually do the KMXPlus write. ## Writing out +- The moment you've been waiting for! Crack open `common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts` and do it. + - Add `import { BUILDER_DISP, build_disp } from './build-disp.js';` to the top (and a new file to go with it) — and, in order + - Add `private sect_disp: BUILDER_DISP` to `class KMXPlusBuilder {` — and, in order + - add `this.sect_disp = build_disp(this.file.kmxplus, this.sect_strs);` to the `build()` function. Include any other sections that need to be cross referenced. + - Update `finalize_sect` to get the section count right, and add `offset = this.finalize_sect_item(this.sect_disp, offset);` + - Finally, add `this.emitSection(file, this.file.COMP_PLUS_DISP, this.sect_disp);` to `compile()` — and, in order. + - Note that some variable length parts (such as the actual text data in `strs`) are sometimes in a separate emit function. Anything that's not in the `COMP_PLUS_STRS` `r.Struct` definition needs one of these. + - update basic.xml and basic.txt - - TODO-LDML: regen fixtures - - … BUT DO NOT CHECK THEM IN! + - Tweak `basic.xml` as needed + - You can use `developer/src/kmc-keyboard/build.sh build-fixtures` which will generate `build/test/fixtures/basic-xml.kmx` as well as `.kvk` + - … BUT DO NOT CHECK IT IN! ## more to come From 8b77d1f764cc981dc7b68298f498b5a790263c96 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Wed, 23 Nov 2022 15:25:51 -0600 Subject: [PATCH 11/33] =?UTF-8?q?feat(core):=20ldml:=20make=20sect=20build?= =?UTF-8?q?er=20an=20object=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - remove one hardcoded list For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- .../kmx/kmx-plus-builder/kmx-plus-builder.ts | 192 +++++++++--------- core/src/ldml/C7532_ldml_updating.md | 2 +- 2 files changed, 94 insertions(+), 100 deletions(-) diff --git a/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts b/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts index 9c8e30aee7..4f4f1ec216 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts @@ -1,39 +1,50 @@ import * as r from 'restructure'; import { KMXPlusFile } from "../kmx-plus.js"; -import { constants } from '@keymanapp/ldml-keyboard-constants'; +import { constants, SectionIdent } from '@keymanapp/ldml-keyboard-constants'; import { BUILDER_SECTION } from './builder-section.js'; import { BUILDER_SECT, build_sect } from './build-sect.js'; +import { BUILDER_DISP, build_disp } from './build-disp.js'; +import { BUILDER_ELEM, build_elem } from './build-elem.js'; +import { BUILDER_KEY2, build_key2 } from './build-key2.js'; import { BUILDER_KEYS, build_keys } from './build-keys.js'; +import { BUILDER_LAYR, build_layr } from './build-layr.js'; +import { BUILDER_LIST, build_list } from './build-list.js'; import { BUILDER_LOCA, build_loca } from './build-loca.js'; import { BUILDER_META, build_meta } from './build-meta.js'; import { BUILDER_NAME, build_name } from './build-name.js'; -import { BUILDER_STRS, build_strs } from './build-strs.js'; -import { BUILDER_VKEY, build_vkey } from './build-vkey.js'; -import { BUILDER_TRAN, build_tran } from './build-tran.js'; -import { BUILDER_ELEM, build_elem } from './build-elem.js'; import { BUILDER_ORDR, build_ordr } from './build-ordr.js'; -import { BUILDER_DISP, build_disp } from './build-disp.js'; +import { BUILDER_STRS, build_strs } from './build-strs.js'; +import { BUILDER_TRAN, build_tran } from './build-tran.js'; +import { BUILDER_VKEY, build_vkey } from './build-vkey.js'; type BUILDER_BKSP = BUILDER_TRAN; type BUILDER_FINL = BUILDER_TRAN; +type SectionBuilders = { + // [id in SectionIdent]: BUILDER_SECTION; + sect?: BUILDER_SECT; + bksp?: BUILDER_BKSP; + disp?: BUILDER_DISP; + elem?: BUILDER_ELEM; + finl?: BUILDER_FINL; + key2?: BUILDER_KEY2; + keys?: BUILDER_KEYS; + layr?: BUILDER_LAYR; + list?: BUILDER_LIST; + loca?: BUILDER_LOCA; + meta?: BUILDER_META; + name?: BUILDER_NAME; + ordr?: BUILDER_ORDR; + strs?: BUILDER_STRS; + tran?: BUILDER_TRAN; + vkey?: BUILDER_VKEY; +}; + export default class KMXPlusBuilder { private file: KMXPlusFile; //private writeDebug: boolean; - private sect_sect: BUILDER_SECT; - private sect_bksp: BUILDER_BKSP; - private sect_disp: BUILDER_DISP; - private sect_elem: BUILDER_ELEM; - private sect_finl: BUILDER_FINL; - private sect_keys: BUILDER_KEYS; - private sect_loca: BUILDER_LOCA; - private sect_meta: BUILDER_META; - private sect_name: BUILDER_NAME; - private sect_ordr: BUILDER_ORDR; - private sect_strs: BUILDER_STRS; - private sect_tran: BUILDER_TRAN; - private sect_vkey: BUILDER_VKEY; + sect : SectionBuilders; constructor(file: KMXPlusFile, _writeDebug: boolean) { this.file = file; @@ -44,21 +55,25 @@ export default class KMXPlusBuilder { const fileSize = this.build(); let file: Uint8Array = new Uint8Array(fileSize); - this.emitSection(file, this.file.COMP_PLUS_SECT, this.sect_sect); - this.emitSection(file, this.file.COMP_PLUS_BKSP, this.sect_bksp); - this.emitSection(file, this.file.COMP_PLUS_DISP, this.sect_disp); - this.emitSection(file, this.file.COMP_PLUS_ELEM, this.sect_elem); + this.emitSection(file, this.file.COMP_PLUS_SECT, this.sect.sect); + // Keep the rest of these in order. + this.emitSection(file, this.file.COMP_PLUS_BKSP, this.sect.bksp); + this.emitSection(file, this.file.COMP_PLUS_DISP, this.sect.disp); + this.emitSection(file, this.file.COMP_PLUS_ELEM, this.sect.elem); this.emitElements(file); - this.emitSection(file, this.file.COMP_PLUS_FINL, this.sect_finl); - this.emitSection(file, this.file.COMP_PLUS_KEYS, this.sect_keys); - this.emitSection(file, this.file.COMP_PLUS_LOCA, this.sect_loca); - this.emitSection(file, this.file.COMP_PLUS_META, this.sect_meta); - this.emitSection(file, this.file.COMP_PLUS_NAME, this.sect_name); - this.emitSection(file, this.file.COMP_PLUS_ORDR, this.sect_ordr); - this.emitSection(file, this.file.COMP_PLUS_STRS, this.sect_strs); + this.emitSection(file, this.file.COMP_PLUS_FINL, this.sect.finl); + this.emitSection(file, this.file.COMP_PLUS_KEY2, this.sect.key2); + this.emitSection(file, this.file.COMP_PLUS_KEYS, this.sect.keys); + this.emitSection(file, this.file.COMP_PLUS_LAYR, this.sect.layr); + this.emitSection(file, this.file.COMP_PLUS_LIST, this.sect.list); + this.emitSection(file, this.file.COMP_PLUS_LOCA, this.sect.loca); + this.emitSection(file, this.file.COMP_PLUS_META, this.sect.meta); + this.emitSection(file, this.file.COMP_PLUS_NAME, this.sect.name); + this.emitSection(file, this.file.COMP_PLUS_ORDR, this.sect.ordr); + this.emitSection(file, this.file.COMP_PLUS_STRS, this.sect.strs); this.emitStrings(file); - this.emitSection(file, this.file.COMP_PLUS_TRAN, this.sect_tran); - this.emitSection(file, this.file.COMP_PLUS_VKEY, this.sect_vkey); + this.emitSection(file, this.file.COMP_PLUS_TRAN, this.sect.tran); + this.emitSection(file, this.file.COMP_PLUS_VKEY, this.sect.vkey); return file; } @@ -68,83 +83,62 @@ export default class KMXPlusBuilder { // We must prepare the strs and elem sections early so that other sections can // reference them. However, they will be emitted in alpha order. - this.sect_strs = build_strs(this.file.kmxplus.strs); - this.sect_elem = build_elem(this.file.kmxplus.elem, this.sect_strs); + this.sect.strs = build_strs(this.file.kmxplus.strs); + this.sect.elem = build_elem(this.file.kmxplus.elem, this.sect.strs); const build_bksp = build_tran; const build_finl = build_tran; - this.sect_bksp = build_bksp(this.file.kmxplus.bksp, this.sect_strs, this.sect_elem); - this.sect_disp = build_disp(this.file.kmxplus, this.sect_strs); - this.sect_finl = build_finl(this.file.kmxplus.finl, this.sect_strs, this.sect_elem); - this.sect_keys = build_keys(this.file.kmxplus, this.sect_strs); - this.sect_loca = build_loca(this.file.kmxplus, this.sect_strs); - this.sect_meta = build_meta(this.file.kmxplus, this.sect_strs); - this.sect_name = build_name(this.file.kmxplus, this.sect_strs); - this.sect_ordr = build_ordr(this.file.kmxplus, this.sect_strs, this.sect_elem); - this.sect_tran = build_tran(this.file.kmxplus.tran, this.sect_strs, this.sect_elem); - this.sect_vkey = build_vkey(this.file.kmxplus); + this.sect.bksp = build_bksp(this.file.kmxplus.bksp, this.sect.strs, this.sect.elem); + this.sect.disp = build_disp(this.file.kmxplus, this.sect.strs); + this.sect.finl = build_finl(this.file.kmxplus.finl, this.sect.strs, this.sect.elem); + this.sect.key2 = build_key2(this.file.kmxplus, this.sect.strs, this.sect.list); + this.sect.keys = build_keys(this.file.kmxplus, this.sect.strs); + this.sect.layr = build_layr(this.file.kmxplus, this.sect.strs, this.sect.list); + this.sect.list = build_list(this.file.kmxplus, this.sect.strs); + this.sect.loca = build_loca(this.file.kmxplus, this.sect.strs); + this.sect.meta = build_meta(this.file.kmxplus, this.sect.strs); + this.sect.name = build_name(this.file.kmxplus, this.sect.strs); + this.sect.ordr = build_ordr(this.file.kmxplus, this.sect.strs, this.sect.elem); + this.sect.tran = build_tran(this.file.kmxplus.tran, this.sect.strs, this.sect.elem); + this.sect.vkey = build_vkey(this.file.kmxplus); // Finalize the sect (index) section - this.sect_sect = build_sect(); + this.sect.sect = build_sect(); this.finalize_sect(); // must be done last - return this.sect_sect.total; + return this.sect.sect.total; } private finalize_sect() { // 'sect' section - // We always have 'loca', 'meta' and 'strs' - this.sect_sect.count = 3; + this.sect.sect.count = 0; - // Handle optional sections - // TODO: use a loop... - if(this.sect_bksp) { - this.sect_sect.count++; - } - if(this.sect_disp) { - this.sect_sect.count++; - } - if(this.sect_elem) { - this.sect_sect.count++; - } - if(this.sect_finl) { - this.sect_sect.count++; - } - if(this.sect_keys) { - this.sect_sect.count++; - } - if(this.sect_name) { - this.sect_sect.count++; - } - if(this.sect_ordr) { - this.sect_sect.count++; - } - if(this.sect_tran) { - this.sect_sect.count++; - } - if(this.sect_vkey) { - this.sect_sect.count++; - } + Object.keys(constants.section).forEach((sectstr : string) => { + const sect : SectionIdent = constants.section[sectstr]; + if(this.sect[sect]) { + this.sect.sect.count++; + } + }); - this.sect_sect.size = constants.length_sect + constants.length_sect_item * this.sect_sect.count; + this.sect.sect.size = constants.length_sect + constants.length_sect_item * this.sect.sect.count; - let offset = this.sect_sect.size; - offset = this.finalize_sect_item(this.sect_bksp, offset); - offset = this.finalize_sect_item(this.sect_disp, offset); - offset = this.finalize_sect_item(this.sect_elem, offset); - offset = this.finalize_sect_item(this.sect_finl, offset); - offset = this.finalize_sect_item(this.sect_keys, offset); - offset = this.finalize_sect_item(this.sect_loca, offset); - offset = this.finalize_sect_item(this.sect_meta, offset); - offset = this.finalize_sect_item(this.sect_name, offset); - offset = this.finalize_sect_item(this.sect_ordr, offset); - offset = this.finalize_sect_item(this.sect_strs, offset); - offset = this.finalize_sect_item(this.sect_tran, offset); - offset = this.finalize_sect_item(this.sect_vkey, offset); + let offset = this.sect.sect.size; + offset = this.finalize_sect_item(this.sect.bksp, offset); + offset = this.finalize_sect_item(this.sect.disp, offset); + offset = this.finalize_sect_item(this.sect.elem, offset); + offset = this.finalize_sect_item(this.sect.finl, offset); + offset = this.finalize_sect_item(this.sect.keys, offset); + offset = this.finalize_sect_item(this.sect.loca, offset); + offset = this.finalize_sect_item(this.sect.meta, offset); + offset = this.finalize_sect_item(this.sect.name, offset); + offset = this.finalize_sect_item(this.sect.ordr, offset); + offset = this.finalize_sect_item(this.sect.strs, offset); + offset = this.finalize_sect_item(this.sect.tran, offset); + offset = this.finalize_sect_item(this.sect.vkey, offset); - this.sect_sect.total = offset; + this.sect.sect.total = offset; } private finalize_sect_item(sect: BUILDER_SECTION, offset: number): number { @@ -153,7 +147,7 @@ export default class KMXPlusBuilder { return offset; } sect._offset = offset; - this.sect_sect.items.push({sect: sect.ident, offset: offset}); + this.sect.sect.items.push({sect: sect.ident, offset: offset}); // TODO: padding return offset + sect.size; } @@ -165,24 +159,24 @@ export default class KMXPlusBuilder { } private emitStrings(file: Uint8Array) { - for(let item of this.sect_strs.items) { + for(let item of this.sect.strs.items) { if(item._value === '') { // We have a special case for the zero-length string let sbuf = r.uint16le; - file.set(sbuf.toBuffer(0), item.offset + this.sect_strs._offset); + file.set(sbuf.toBuffer(0), item.offset + this.sect.strs._offset); } else { let sbuf = new r.String(null, 'utf16le'); - file.set(sbuf.toBuffer(item._value), item.offset + this.sect_strs._offset); + file.set(sbuf.toBuffer(item._value), item.offset + this.sect.strs._offset); } } } private emitElements(file: Uint8Array) { - if(this.sect_elem) { - for(let str of this.sect_elem.strings) { + if(this.sect.elem) { + for(let str of this.sect.elem.strings) { if(str.items.length > 0) { let COMP_PLUS_ELEM_ELEMENTS = new r.Array(this.file.COMP_PLUS_ELEM_ELEMENT, str.items.length); - file.set(COMP_PLUS_ELEM_ELEMENTS.toBuffer(str.items), str.offset + this.sect_elem._offset); + file.set(COMP_PLUS_ELEM_ELEMENTS.toBuffer(str.items), str.offset + this.sect.elem._offset); } } } diff --git a/core/src/ldml/C7532_ldml_updating.md b/core/src/ldml/C7532_ldml_updating.md index 13c57089cb..5336392d0a 100644 --- a/core/src/ldml/C7532_ldml_updating.md +++ b/core/src/ldml/C7532_ldml_updating.md @@ -57,7 +57,7 @@ working on ‘layr’, using ‘disp’ as a model from https://github.com/keyma - Add `import { BUILDER_DISP, build_disp } from './build-disp.js';` to the top (and a new file to go with it) — and, in order - Add `private sect_disp: BUILDER_DISP` to `class KMXPlusBuilder {` — and, in order - add `this.sect_disp = build_disp(this.file.kmxplus, this.sect_strs);` to the `build()` function. Include any other sections that need to be cross referenced. - - Update `finalize_sect` to get the section count right, and add `offset = this.finalize_sect_item(this.sect_disp, offset);` + - Update `finalize_sect` and add `offset = this.finalize_sect_item(this.sect_disp, offset);` - Finally, add `this.emitSection(file, this.file.COMP_PLUS_DISP, this.sect_disp);` to `compile()` — and, in order. - Note that some variable length parts (such as the actual text data in `strs`) are sometimes in a separate emit function. Anything that's not in the `COMP_PLUS_STRS` `r.Struct` definition needs one of these. From 9e21fe1a74ce06ea87d060a4f91c395849033130 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Wed, 23 Nov 2022 16:47:00 -0600 Subject: [PATCH 12/33] =?UTF-8?q?feat(core):=20ldml:=20updates=20to=20list?= =?UTF-8?q?=20and=20other=20stuff=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - list is probably in good shape for the builder (not yet the writer). Other stuff TBD. For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- .../src/kmx/kmx-plus-builder/build-key2.ts | 54 ++++++++++++ .../src/kmx/kmx-plus-builder/build-layr.ts | 41 +++++++++ .../src/kmx/kmx-plus-builder/build-list.ts | 87 +++++++++++++++++++ .../src/kmx/kmx-plus-builder/build-strs.ts | 12 +-- .../kmx/kmx-plus-builder/kmx-plus-builder.ts | 4 +- common/web/types/src/kmx/kmx-plus.ts | 12 +++ common/web/types/src/kmx/string-list.ts | 19 ++++ .../src/kmc-keyboard/src/compiler/disp.ts | 13 +-- 8 files changed, 217 insertions(+), 25 deletions(-) create mode 100644 common/web/types/src/kmx/kmx-plus-builder/build-key2.ts create mode 100644 common/web/types/src/kmx/kmx-plus-builder/build-layr.ts create mode 100644 common/web/types/src/kmx/kmx-plus-builder/build-list.ts diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts b/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts new file mode 100644 index 0000000000..2b8e1fd702 --- /dev/null +++ b/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts @@ -0,0 +1,54 @@ + +import { constants } from "@keymanapp/ldml-keyboard-constants"; +import { KeyFlags, KMXPlusData } from "../kmx-plus.js"; +import { build_strs_index, BUILDER_STRS } from "./build-strs.js"; +import { BUILDER_SECTION } from "./builder-section.js"; + +/* ------------------------------------------------------------------ + * keys section + ------------------------------------------------------------------ */ + + +// interface BUILDER_KEY2_ITEM { +// vkey: number; +// mod: number; +// to: number; //str or UTF-32 char depending on value of 'extend' +// flags: number; //bitfield +// }; + +/** + * Builder for the 'keys' section + */ +export interface BUILDER_KEY2 extends BUILDER_SECTION { +// count: number; +// reserved: number; +// items: BUILDER_KEYS_ITEM[]; +}; + + +export function build_key2(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_list: BUILDER_LIST): BUILDER_KEY2 { + if(!kmxplus.keys.keys.length) { + return null; + } + + let key2: BUILDER_KEY2 = { + // ident: constants.hex_section_id(constants.section.keys), + // size: constants.length_keys + constants.length_keys_item * kmxplus.keys.keys.length, + // _offset: 0, + // count: kmxplus.keys.keys.length, + // reserved: 0, + // items: [] + }; + +// for(let item of kmxplus.keys.keys) { +// keys.items.push({ +// vkey: item.vkey, +// mod: item.mod, +// // todo: support 'extend' +// to: build_strs_index(sect_strs, item.to), +// flags: KeyFlags.extend // todo: support non-extended +// }); +// } + + return key2; +} diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts new file mode 100644 index 0000000000..3948cbb517 --- /dev/null +++ b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts @@ -0,0 +1,41 @@ + +import { constants } from "@keymanapp/ldml-keyboard-constants"; +import { KeyFlags, KMXPlusData } from "../kmx-plus.js"; +import { build_strs_index, BUILDER_STRS } from "./build-strs.js"; +import { BUILDER_SECTION } from "./builder-section.js"; + +/* ------------------------------------------------------------------ + * keys section + ------------------------------------------------------------------ */ + + +// interface BUILDER_KEY2_ITEM { +// vkey: number; +// mod: number; +// to: number; //str or UTF-32 char depending on value of 'extend' +// flags: number; //bitfield +// }; + +/** + * Builder for the 'keys' section + */ +export interface BUILDER_LAYR extends BUILDER_SECTION { +// count: number; +// reserved: number; +// items: BUILDER_KEYS_ITEM[]; +}; + + +export function build_layr(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_list: BUILDER_LIST): BUILDER_LAYR { + if(!kmxplus.keys.keys.length) { + return null; + } + + let layr: BUILDER_LAYR = { + ident: 0, + size: 0, + _offset: 0 + }; + + return layr; +} diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-list.ts b/common/web/types/src/kmx/kmx-plus-builder/build-list.ts new file mode 100644 index 0000000000..08efc8b171 --- /dev/null +++ b/common/web/types/src/kmx/kmx-plus-builder/build-list.ts @@ -0,0 +1,87 @@ +import { constants } from "@keymanapp/ldml-keyboard-constants"; +import { List, ListItem } from "../kmx-plus.js"; +import { build_strs_index, BUILDER_STRS } from "./build-strs.js"; +import { BUILDER_SECTION } from "./builder-section.js"; + +/* ------------------------------------------------------------------ + * list section + ------------------------------------------------------------------ */ + +interface BUILDER_LIST_LIST { + index: number; // index into indices + count: number; // number of strings + _value: ListItem; // for findability +}; + +interface BUILDER_LIST_INDEX { + index: number; // str + _value: string; // for findability? +}; + +/** + * Builder for the 'list' section + */ +export interface BUILDER_LIST extends BUILDER_SECTION { + listCount: number; + indexCount: number; + lists: BUILDER_LIST_LIST[]; + indices: BUILDER_LIST_INDEX[]; +}; + +export function build_list(source_list: List, sect_strs: BUILDER_STRS): BUILDER_LIST { + let result: BUILDER_LIST = { + listCount: source_list.lists.length, + indexCount: 0, + lists: [], + indices: [], + ident: constants.hex_section_id(constants.section.list), + size: 0, + _offset: 0 + }; + + result.lists = source_list.lists.map(array => { + let list : BUILDER_LIST_LIST = { + index: result.indices.length, // the next indexcount + count: array.length, + _value: array + }; + array.forEach((i) => { + let index : BUILDER_LIST_INDEX = { + // Get the final string index + index: build_strs_index(sect_strs, i.value), + _value: i.value.value, // unwrap the actual string value + }; + result.indices.push(index); // increment the indexCount + result.indexCount++; + }); + return list; + }); + + // Sort the lists. + result.lists.sort((a,b) => a._value.compareTo(b._value)); + + let offset = constants.length_list + + (constants.length_list_item * result.listCount) + + (constants.length_list_index * result.indexCount); + result.size = offset; + + return result; +} + +/** + * Returns the index into the list, analagous to build_strs_index + * @param sect_strs + * @param value + * @returns + */ +export function build_list_index(sect_list: BUILDER_LIST, value: ListItem) { + if(!(value instanceof ListItem)) { + throw new Error('unexpected value '+ value); + } + + let result = sect_list.lists.findIndex(v => v._value === value); + if(result < 0) { + throw new Error('unexpectedly missing ListItem ' + value); // TODO-LDML: it's an array of strs + } + return result; +} diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-strs.ts b/common/web/types/src/kmx/kmx-plus-builder/build-strs.ts index 7cb931fbaa..2c9a43eef8 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-strs.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-strs.ts @@ -23,16 +23,6 @@ export interface BUILDER_STRS extends BUILDER_SECTION { items: BUILDER_STRS_ITEM[]; }; -function binaryStringCompare(a: string, b: string): number { - // https://tc39.es/ecma262/multipage/abstract-operations.html#sec-islessthan - if(typeof a != 'string' || typeof b != 'string') { - throw new Error('binaryStringCompare: inputs must be strings'); - } - if(a < b) return -1; - if(a > b) return 1; - return 0; -} - export function build_strs(source_strs: Strs): BUILDER_STRS { let result: BUILDER_STRS = { ident: constants.hex_section_id(constants.section.strs), @@ -44,7 +34,7 @@ export function build_strs(source_strs: Strs): BUILDER_STRS { }; result.items = source_strs.strings.map(item => { return {_value: item.value, length: item.value.length, offset: 0}; }); - result.items.sort((a,b) => binaryStringCompare(a._value, b._value)); + result.items.sort((a,b) => StrsItem.binaryStringCompare(a._value, b._value)); let offset = constants.length_strs + constants.length_strs_item * result.count; // TODO: consider padding diff --git a/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts b/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts index 4f4f1ec216..c2fb069f8a 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts @@ -81,9 +81,10 @@ export default class KMXPlusBuilder { private build() { // Required sections: sect, strs, loca, meta - // We must prepare the strs and elem sections early so that other sections can + // We must prepare the strs, list, and elem sections early so that other sections can // reference them. However, they will be emitted in alpha order. this.sect.strs = build_strs(this.file.kmxplus.strs); + this.sect.list = build_list(this.file.kmxplus.list, this.sect.strs); this.sect.elem = build_elem(this.file.kmxplus.elem, this.sect.strs); const build_bksp = build_tran; @@ -95,7 +96,6 @@ export default class KMXPlusBuilder { this.sect.key2 = build_key2(this.file.kmxplus, this.sect.strs, this.sect.list); this.sect.keys = build_keys(this.file.kmxplus, this.sect.strs); this.sect.layr = build_layr(this.file.kmxplus, this.sect.strs, this.sect.list); - this.sect.list = build_list(this.file.kmxplus, this.sect.strs); this.sect.loca = build_loca(this.file.kmxplus, this.sect.strs); this.sect.meta = build_meta(this.file.kmxplus, this.sect.strs); this.sect.name = build_name(this.file.kmxplus, this.sect.strs); diff --git a/common/web/types/src/kmx/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus.ts index 008b70007f..d207a3fe16 100644 --- a/common/web/types/src/kmx/kmx-plus.ts +++ b/common/web/types/src/kmx/kmx-plus.ts @@ -116,6 +116,18 @@ export class StrsItem { constructor(value: string) { this.value = value; } + compareTo(o: StrsItem): number { + return StrsItem.binaryStringCompare(this.value, o.value); + } + static binaryStringCompare(a: string, b: string): number { + // https://tc39.es/ecma262/multipage/abstract-operations.html#sec-islessthan + if(typeof a != 'string' || typeof b != 'string') { + throw new Error('binaryStringCompare: inputs must be strings'); + } + if(a < b) return -1; + if(a > b) return 1; + return 0; + } }; export class Strs extends Section { diff --git a/common/web/types/src/kmx/string-list.ts b/common/web/types/src/kmx/string-list.ts index 456d0766c5..d70204ff54 100644 --- a/common/web/types/src/kmx/string-list.ts +++ b/common/web/types/src/kmx/string-list.ts @@ -36,4 +36,23 @@ export class ListItem extends Array { } return true; } + compareTo(o: ListItem): number { + for (let i = 0; i < Math.min(this.length, o.length); i++) { + const r = this[i].value.compareTo(o[i].value); + if (r !== 0) { + return r; + } + } + // prefix is the same, so go by length: shortest is first. + if (this.length < o.length) { + return -1; + } else if (this.length > o.length) { + return 1; + } else { + return 0; + } + } + toString(): string { + return this.map(v => v.value.value).toString(); + } }; diff --git a/developer/src/kmc-keyboard/src/compiler/disp.ts b/developer/src/kmc-keyboard/src/compiler/disp.ts index 3f3260447a..5eb7defa80 100644 --- a/developer/src/kmc-keyboard/src/compiler/disp.ts +++ b/developer/src/kmc-keyboard/src/compiler/disp.ts @@ -44,18 +44,7 @@ export class DispCompiler extends SectionCompiler { display: sections.strs.allocString(display.display), })) || []; - // TODO-LDML: Same function in comon/web/types/src - function binaryStringCompare(a: string, b: string) : number { - if(a < b) { - return -1; - } else if(a > b) { - return 1; - } else { - return 0; - } - } - - result.disps.sort((a: DispItem, b: DispItem) => binaryStringCompare(a.to.value, b.to.value)); + result.disps.sort((a: DispItem, b: DispItem) => a.to.compareTo(b.to)); return result; } From 156c737a9a6faa4fc4db7cea5b93da6ea45cd653 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Wed, 23 Nov 2022 18:33:09 -0600 Subject: [PATCH 13/33] =?UTF-8?q?feat(core):=20ldml:=20updates=20to=20key2?= =?UTF-8?q?=20and=20other=20stuff=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - adding compareTo to some classes For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- .../src/kmx/kmx-plus-builder/build-key2.ts | 122 ++++++++++++++---- .../src/kmx/kmx-plus-builder/build-layr.ts | 12 +- .../src/kmx/kmx-plus-builder/build-list.ts | 4 + common/web/types/src/kmx/kmx-plus.ts | 11 ++ .../src/kmc-keyboard/src/compiler/key2.ts | 9 +- 5 files changed, 119 insertions(+), 39 deletions(-) diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts b/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts index 2b8e1fd702..7bf2380031 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts @@ -1,54 +1,120 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; -import { KeyFlags, KMXPlusData } from "../kmx-plus.js"; +import { KMXPlusData } from "../kmx-plus.js"; import { build_strs_index, BUILDER_STRS } from "./build-strs.js"; +import { build_list_index, BUILDER_LIST } from "./build-list.js"; import { BUILDER_SECTION } from "./builder-section.js"; /* ------------------------------------------------------------------ - * keys section + * key2 section ------------------------------------------------------------------ */ +interface BUILDER_KEY2_KEY { + vkey: number; + to: number; // str + flags: number; + id: number; // str + switch: number; // str + width: number; // width*10 + longPress: number; // list + longPressDefault: number; // str + multiTap: number; // list + flicks: number; // index into flicks[] +}; -// interface BUILDER_KEY2_ITEM { -// vkey: number; -// mod: number; -// to: number; //str or UTF-32 char depending on value of 'extend' -// flags: number; //bitfield -// }; +interface BUILDER_KEY2_FLICK { + directions: number; // list + flags: number; + to: number; // str +}; + +interface BUILDER_KEY2_FLICKS { + count: number; + flick: number; // index into flick[] + id: number; //str + _id: string; +}; /** * Builder for the 'keys' section */ export interface BUILDER_KEY2 extends BUILDER_SECTION { -// count: number; -// reserved: number; -// items: BUILDER_KEYS_ITEM[]; + ident: number; + size: number; + keyCount: number; + flicksCount: number; + flickCount: number; + keys: BUILDER_KEY2_KEY[]; + flicks: BUILDER_KEY2_FLICKS[]; + flick: BUILDER_KEY2_FLICK[]; }; - export function build_key2(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_list: BUILDER_LIST): BUILDER_KEY2 { - if(!kmxplus.keys.keys.length) { + if(!kmxplus.key2.keys.length && !kmxplus.key2.flicks.length) { return null; } let key2: BUILDER_KEY2 = { - // ident: constants.hex_section_id(constants.section.keys), - // size: constants.length_keys + constants.length_keys_item * kmxplus.keys.keys.length, - // _offset: 0, - // count: kmxplus.keys.keys.length, - // reserved: 0, - // items: [] + ident: constants.hex_section_id(constants.section.key2), + size: 0, + keyCount: kmxplus.key2.keys.length, + flicksCount: kmxplus.key2.flicks.length, + flickCount: 0, + keys: [], + flicks: [], + flick: [], + _offset: 0 }; -// for(let item of kmxplus.keys.keys) { -// keys.items.push({ -// vkey: item.vkey, -// mod: item.mod, -// // todo: support 'extend' -// to: build_strs_index(sect_strs, item.to), -// flags: KeyFlags.extend // todo: support non-extended -// }); -// } + // flicks first + // sort the input, to simplify bookkeeping later + kmxplus.key2.flicks.sort((a, b) => a.compareTo(b)); + // we always need a flicks=0 to mena 'no flicks' + key2.flicks = kmxplus.key2.flicks.map((flicks) => { + let result : BUILDER_KEY2_FLICKS = { + count: flicks.flicks.length, + flick: key2.flick.length, // index of first flick + id: build_strs_index(sect_strs, flicks.id), + _id: flicks.id.value, + }; + flicks.flicks.forEach((flick) => { + key2.flick.push({ + directions: build_list_index(sect_list, flick.directions), + flags: flick.flags, + to: build_strs_index(sect_strs, flick.to), + }); + key2.flickCount++; + }); + return result; + }); + + // now keys + kmxplus.key2.keys.sort((a, b) => a.id.compareTo(b.id)); + key2.keys = kmxplus.key2.keys.map((key) => { + let result : BUILDER_KEY2_KEY = { + vkey: key.vkey, + to: build_strs_index(sect_strs, key.to), + flags: key.flags, + id: build_strs_index(sect_strs, key.id), + switch: build_strs_index(sect_strs, key.switch), + width: key.width, + longPress: build_list_index(sect_list, key.longPress), + longPressDefault: build_strs_index(sect_strs, key.longPressDefault), + multiTap: build_list_index(sect_list, key.multiTap), + flicks: key2.flicks.findIndex(v => v._id === key.flicks), + }; + // Make sure the flicks were was found + if (key.flicks && !result.flicks) { + throw new Error(`Key2: Could not find flicks id=${key.flicks} for key=${key.id.value}`); + } + return result; + }); + + let offset = constants.length_key2 + + (constants.length_key2_key * key2.keyCount) + + (constants.length_key2_flick_element * key2.flickCount) + + (constants.length_key2_flick_list * key2.flicksCount); + key2.size = offset; return key2; } diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts index 3948cbb517..e04e5f55e9 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts @@ -1,7 +1,8 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; -import { KeyFlags, KMXPlusData } from "../kmx-plus.js"; -import { build_strs_index, BUILDER_STRS } from "./build-strs.js"; +import { /*KeyFlags,*/ KMXPlusData } from "../kmx-plus.js"; +import { /*build_strs_index,*/ BUILDER_STRS } from "./build-strs.js"; +import { /*build_list_index,*/ BUILDER_LIST } from "./build-list.js"; import { BUILDER_SECTION } from "./builder-section.js"; /* ------------------------------------------------------------------ @@ -27,13 +28,14 @@ export interface BUILDER_LAYR extends BUILDER_SECTION { export function build_layr(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_list: BUILDER_LIST): BUILDER_LAYR { - if(!kmxplus.keys.keys.length) { + if(!kmxplus.layr.layers && !kmxplus.layr.lists && !kmxplus.layr.rows && !kmxplus.layr.vkeys) { return null; } let layr: BUILDER_LAYR = { - ident: 0, - size: 0, + ident: constants.hex_section_id(constants.section.key2), + size: constants.length_layr, + // TODO TODO TODO _offset: 0 }; diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-list.ts b/common/web/types/src/kmx/kmx-plus-builder/build-list.ts index 08efc8b171..45e9ac163f 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-list.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-list.ts @@ -29,6 +29,10 @@ export interface BUILDER_LIST extends BUILDER_SECTION { }; export function build_list(source_list: List, sect_strs: BUILDER_STRS): BUILDER_LIST { + if(!source_list?.lists?.length) { + return null; + } + let result: BUILDER_LIST = { listCount: source_list.lists.length, indexCount: 0, diff --git a/common/web/types/src/kmx/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus.ts index d207a3fe16..bde2f56377 100644 --- a/common/web/types/src/kmx/kmx-plus.ts +++ b/common/web/types/src/kmx/kmx-plus.ts @@ -280,6 +280,12 @@ export class Key2Keys { export class Key2Flicks { flicks: Key2Flick[] = []; id: StrsItem; + compareTo(b: Key2Flicks): number { + return this.id.compareTo(b.id); + } + constructor(id: StrsItem) { + this.id = id; + } }; export class Key2Flick { @@ -291,6 +297,11 @@ export class Key2Flick { export class Key2 extends Section { keys: Key2Keys[] = []; flicks: Key2Flicks[] = []; + constructor(strs: Strs) { + super(); + let nullFlicks = new Key2Flicks(strs.allocString('')); + this.flicks.push(nullFlicks); // C7043: null element string + } }; export class List extends Section { diff --git a/developer/src/kmc-keyboard/src/compiler/key2.ts b/developer/src/kmc-keyboard/src/compiler/key2.ts index 544722676b..beff5c849b 100644 --- a/developer/src/kmc-keyboard/src/compiler/key2.ts +++ b/developer/src/kmc-keyboard/src/compiler/key2.ts @@ -20,13 +20,14 @@ export class Key2Compiler extends SectionCompiler { return valid; } + public compile(sections: GlobalSections): Key2 { if (!this.keyboard.keys.key && !this.keyboard.keys.flicks) { // short-circuit if no keys or flicks return null; } - let sect = new Key2(); + let sect = new Key2(sections.strs); // Load the flicks first this.loadFlicks(sections, sect); @@ -39,10 +40,7 @@ export class Key2Compiler extends SectionCompiler { public loadFlicks(sections: GlobalSections, sect: Key2) { for (let lkflicks of this.keyboard.keys.flicks) { - let flicks: Key2Flicks = { - id: sections.strs.allocString(lkflicks.id), - flicks: [] - }; + let flicks: Key2Flicks = new Key2Flicks(sections.strs.allocString(lkflicks.id)); for (let lkflick of lkflicks.flick) { let flags = 0; @@ -94,5 +92,4 @@ export class Key2Compiler extends SectionCompiler { }); } } - } From c6b1be4ed713f808bc5874a3b7a66d90438f73ca Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Wed, 23 Nov 2022 18:43:35 -0600 Subject: [PATCH 14/33] =?UTF-8?q?feat(core):=20ldml:=20updates=20to=20key2?= =?UTF-8?q?=20and=20other=20stuff=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - compiles again For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- developer/src/kmc-keyboard/src/compiler/key2.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/developer/src/kmc-keyboard/src/compiler/key2.ts b/developer/src/kmc-keyboard/src/compiler/key2.ts index beff5c849b..3629d549f6 100644 --- a/developer/src/kmc-keyboard/src/compiler/key2.ts +++ b/developer/src/kmc-keyboard/src/compiler/key2.ts @@ -6,7 +6,8 @@ import { SectionCompiler } from "./section-compiler.js"; import GlobalSections = KMXPlus.GlobalSections; import Key2 = KMXPlus.Key2; import ListItem = KMXPlus.ListItem; -import { Key2Flicks } from '@keymanapp/common-types/src/kmx/kmx-plus.js'; +import Key2Flicks = KMXPlus.Key2Flicks; + // import USVirtualKeyMap = Constants.USVirtualKeyMap; export class Key2Compiler extends SectionCompiler { From 00442f5edd9431d1ffc3133ad7bd1a4dd88b2edb Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Wed, 23 Nov 2022 18:48:45 -0600 Subject: [PATCH 15/33] =?UTF-8?q?feat(core):=20ldml:=20updates=20to=20key2?= =?UTF-8?q?=20and=20other=20stuff=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - passes all but fixtures For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- common/web/types/src/kmx/kmx-plus-builder/build-key2.ts | 3 ++- common/web/types/src/kmx/kmx-plus-builder/build-layr.ts | 2 +- common/web/types/src/kmx/kmx-plus-builder/build-list.ts | 1 + common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts | 4 +++- developer/src/kmc-keyboard/test/test-key2.ts | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts b/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts index 7bf2380031..044921cbdf 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts @@ -50,7 +50,8 @@ export interface BUILDER_KEY2 extends BUILDER_SECTION { }; export function build_key2(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_list: BUILDER_LIST): BUILDER_KEY2 { - if(!kmxplus.key2.keys.length && !kmxplus.key2.flicks.length) { + if(!kmxplus.key2.keys.length && + (kmxplus.key2.flicks.length <= 1)) { // if no keys and only the 'null' flick. return null; } diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts index e04e5f55e9..ee51dc71f2 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts @@ -28,7 +28,7 @@ export interface BUILDER_LAYR extends BUILDER_SECTION { export function build_layr(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_list: BUILDER_LIST): BUILDER_LAYR { - if(!kmxplus.layr.layers && !kmxplus.layr.lists && !kmxplus.layr.rows && !kmxplus.layr.vkeys) { + if(!kmxplus.layr?.layers && !kmxplus.layr?.lists && !kmxplus.layr?.rows && !kmxplus.layr?.vkeys) { return null; } diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-list.ts b/common/web/types/src/kmx/kmx-plus-builder/build-list.ts index 45e9ac163f..4a3c9f3fdc 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-list.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-list.ts @@ -30,6 +30,7 @@ export interface BUILDER_LIST extends BUILDER_SECTION { export function build_list(source_list: List, sect_strs: BUILDER_STRS): BUILDER_LIST { if(!source_list?.lists?.length) { + // there's always the null list return null; } diff --git a/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts b/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts index c2fb069f8a..b1026aa035 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts @@ -44,7 +44,9 @@ export default class KMXPlusBuilder { private file: KMXPlusFile; //private writeDebug: boolean; - sect : SectionBuilders; + sect : SectionBuilders = { + + }; constructor(file: KMXPlusFile, _writeDebug: boolean) { this.file = file; diff --git a/developer/src/kmc-keyboard/test/test-key2.ts b/developer/src/kmc-keyboard/test/test-key2.ts index 61bb0881d5..d99b3eb382 100644 --- a/developer/src/kmc-keyboard/test/test-key2.ts +++ b/developer/src/kmc-keyboard/test/test-key2.ts @@ -16,7 +16,7 @@ describe('key2', function () { assert.ok(key2); // assert.equal(compilerTestCallbacks.messages.length, 0); assert.equal(key2.keys.length, 1); - assert.equal(key2.flicks.length, 0); + assert.equal(key2.flicks.length, 1); // there's always a 'null' flick assert.equal(key2.keys[0].to.value, '🪦'); assert.equal(key2.keys[0].id.value, 'grave'); }); From deceda169c6e54f1898eb475e3fe90eb66f81506 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Wed, 23 Nov 2022 19:22:03 -0600 Subject: [PATCH 16/33] =?UTF-8?q?feat(core):=20ldml:=20updates=20to=20key2?= =?UTF-8?q?=20and=20other=20stuff=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - finalizers - build-key2 should be complete now - stub for build-layr For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- .../src/kmx/kmx-plus-builder/build-key2.ts | 8 ++- .../src/kmx/kmx-plus-builder/build-layr.ts | 60 +++++++++++++++---- .../kmx/kmx-plus-builder/kmx-plus-builder.ts | 4 ++ 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts b/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts index 044921cbdf..26933d8c2e 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts @@ -44,6 +44,9 @@ export interface BUILDER_KEY2 extends BUILDER_SECTION { keyCount: number; flicksCount: number; flickCount: number; + reserved0: number; + reserved1: number; + reserved2: number; keys: BUILDER_KEY2_KEY[]; flicks: BUILDER_KEY2_FLICKS[]; flick: BUILDER_KEY2_FLICK[]; @@ -61,10 +64,13 @@ export function build_key2(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_l keyCount: kmxplus.key2.keys.length, flicksCount: kmxplus.key2.flicks.length, flickCount: 0, + reserved0: 0, + reserved1: 0, + reserved2: 0, keys: [], flicks: [], flick: [], - _offset: 0 + _offset: 0, }; // flicks first diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts index ee51dc71f2..8b43a75aef 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts @@ -6,24 +6,47 @@ import { /*build_list_index,*/ BUILDER_LIST } from "./build-list.js"; import { BUILDER_SECTION } from "./builder-section.js"; /* ------------------------------------------------------------------ - * keys section + * layr section ------------------------------------------------------------------ */ -// interface BUILDER_KEY2_ITEM { -// vkey: number; -// mod: number; -// to: number; //str or UTF-32 char depending on value of 'extend' -// flags: number; //bitfield -// }; +interface BUILDER_LAYR_LIST { + flags: number; + hardware: number; // str + layer: number; // index + count: number; +}; + +interface BUILDER_LAYR_LAYER { + id: number; // str + modifier: number; // str + row: number; // row index + count: number; +}; + +interface BUILDER_LAYR_ROW { + key: number; + count: number; +}; + +interface BUILDER_LAYR_KEY { + key: number; +}; /** * Builder for the 'keys' section */ export interface BUILDER_LAYR extends BUILDER_SECTION { -// count: number; -// reserved: number; -// items: BUILDER_KEYS_ITEM[]; + listCount: number, + layerCount: number, + rowCount: number, + keyCount: number, + reserved0: number, + reserved1: number, + lists: BUILDER_LAYR_LIST[], + layers: BUILDER_LAYR_LAYER[], + rows: BUILDER_LAYR_ROW[], + keys: BUILDER_LAYR_KEY[], }; @@ -33,11 +56,22 @@ export function build_layr(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_l } let layr: BUILDER_LAYR = { - ident: constants.hex_section_id(constants.section.key2), + ident: constants.hex_section_id(constants.section.key2), size: constants.length_layr, - // TODO TODO TODO - _offset: 0 + _offset: 0, + listCount: 0, + layerCount: 0, + rowCount: 0, + keyCount: 0, + reserved0: 0, + reserved1: 0, + lists: [], + layers: [], + rows: [], + keys: [] }; + // TODO-LDML + return layr; } diff --git a/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts b/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts index b1026aa035..ee88c5f904 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts @@ -127,11 +127,15 @@ export default class KMXPlusBuilder { this.sect.sect.size = constants.length_sect + constants.length_sect_item * this.sect.sect.count; let offset = this.sect.sect.size; + // Note: in order! Everyone's here except 'sect' which is at offset 0 offset = this.finalize_sect_item(this.sect.bksp, offset); offset = this.finalize_sect_item(this.sect.disp, offset); offset = this.finalize_sect_item(this.sect.elem, offset); offset = this.finalize_sect_item(this.sect.finl, offset); + offset = this.finalize_sect_item(this.sect.key2, offset); offset = this.finalize_sect_item(this.sect.keys, offset); + offset = this.finalize_sect_item(this.sect.layr, offset); + offset = this.finalize_sect_item(this.sect.list, offset); offset = this.finalize_sect_item(this.sect.loca, offset); offset = this.finalize_sect_item(this.sect.meta, offset); offset = this.finalize_sect_item(this.sect.name, offset); From 273075c1f38d821ba178f15c293d420597d1fde0 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Fri, 25 Nov 2022 15:55:11 -0600 Subject: [PATCH 17/33] =?UTF-8?q?feat(core):=20ldml:=20updates=20to=20layr?= =?UTF-8?q?=20compiler=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - layr compiler working For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- .../src/kmx/kmx-plus-builder/build-layr.ts | 2 +- common/web/types/src/kmx/kmx-plus.ts | 4 +- core/src/ldml/C7532_ldml_updating.md | 6 +- .../src/kmc-keyboard/src/compiler/compiler.ts | 16 ++- .../src/kmc-keyboard/src/compiler/layr.ts | 121 ++++++++++++++++++ .../test/fixtures/sections/key2/maximal.xml | 2 +- developer/src/kmc-keyboard/test/test-layr.ts | 103 +++++++++++++++ 7 files changed, 242 insertions(+), 12 deletions(-) create mode 100644 developer/src/kmc-keyboard/src/compiler/layr.ts create mode 100644 developer/src/kmc-keyboard/test/test-layr.ts diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts index 8b43a75aef..f8891ee68a 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts @@ -51,7 +51,7 @@ export interface BUILDER_LAYR extends BUILDER_SECTION { export function build_layr(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_list: BUILDER_LIST): BUILDER_LAYR { - if(!kmxplus.layr?.layers && !kmxplus.layr?.lists && !kmxplus.layr?.rows && !kmxplus.layr?.vkeys) { + if(!kmxplus.layr?.layers && !kmxplus.layr?.lists && !kmxplus.layr?.rows && !kmxplus.layr?.keys) { return null; } diff --git a/common/web/types/src/kmx/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus.ts index bde2f56377..c55d36f9c1 100644 --- a/common/web/types/src/kmx/kmx-plus.ts +++ b/common/web/types/src/kmx/kmx-plus.ts @@ -259,9 +259,9 @@ export class Layr extends Section { layers: LayrEntry[] = []; rows: LayrRow[] = []; /** - * each item is a vkey id + * each item is a key id for in-memory */ - vkeys: number[] = []; + keys: string[] = []; }; export class Key2Keys { diff --git a/core/src/ldml/C7532_ldml_updating.md b/core/src/ldml/C7532_ldml_updating.md index 5336392d0a..b3ce8d9251 100644 --- a/core/src/ldml/C7532_ldml_updating.md +++ b/core/src/ldml/C7532_ldml_updating.md @@ -46,9 +46,13 @@ working on ‘layr’, using ‘disp’ as a model from https://github.com/keyma - first the compiler tests - add a section in `developer/src/kmc-keyboard/test/fixtures/sections` if needed - - add a test case such as `developer/src/kmc-keyboard/src/compiler/layr.ts` + - add a test case such as `developer/src/kmc-keyboard/test/test-key2.ts` - add a compiler - `developer/src/kmc-keyboard/compiler/key2.ts` + - link it in to `developer/src/kmc-keyboard/src/compiler/compiler.ts` + - add the import + - add to `SECTION_COMPILERS` + - Note: the in-memory compiler can affect `basic.kmx` even _before_ you add the section writing code. How? Simple… `Strs.allocString()` is called for the in-memory structures, so the string table will start growing even before those strings are actually used by the new sections. This is why it's fine to ignore the basic failure until you actually do the KMXPlus write. ## Writing out diff --git a/developer/src/kmc-keyboard/src/compiler/compiler.ts b/developer/src/kmc-keyboard/src/compiler/compiler.ts index 765df112d4..d176ab6dad 100644 --- a/developer/src/kmc-keyboard/src/compiler/compiler.ts +++ b/developer/src/kmc-keyboard/src/compiler/compiler.ts @@ -1,15 +1,16 @@ import { LDMLKeyboardXMLSourceFileReader, LDMLKeyboard, KMXPlus } from '@keymanapp/common-types'; import CompilerCallbacks from './callbacks.js'; import CompilerOptions from './compiler-options.js'; -import { DispCompiler } from './disp.js'; -import { KeysCompiler } from './keys.js'; -import { Key2Compiler } from './key2.js'; -import { LocaCompiler } from './loca.js'; import { CompilerMessages } from './messages.js'; +import { BkspCompiler, FinlCompiler, TranCompiler } from './tran.js'; +import { DispCompiler } from './disp.js'; +import { Key2Compiler } from './key2.js'; +import { KeysCompiler } from './keys.js'; +import { LayrCompiler } from './layr.js'; +import { LocaCompiler } from './loca.js'; import { MetaCompiler } from './meta.js'; import { NameCompiler } from './name.js'; import { OrdrCompiler } from './ordr.js'; -import { BkspCompiler, FinlCompiler, TranCompiler } from './tran.js'; import { VkeyCompiler } from './vkey.js'; import LDMLKeyboardXMLSourceFile = LDMLKeyboard.LDMLKeyboardXMLSourceFile; @@ -18,15 +19,16 @@ import KMXPlusFile = KMXPlus.KMXPlusFile; const SECTION_COMPILERS = [ BkspCompiler, DispCompiler, + FinlCompiler, Key2Compiler, KeysCompiler, - FinlCompiler, + LayrCompiler, LocaCompiler, MetaCompiler, NameCompiler, OrdrCompiler, TranCompiler, - VkeyCompiler + VkeyCompiler, ]; export default class Compiler { diff --git a/developer/src/kmc-keyboard/src/compiler/layr.ts b/developer/src/kmc-keyboard/src/compiler/layr.ts new file mode 100644 index 0000000000..d53b5780f0 --- /dev/null +++ b/developer/src/kmc-keyboard/src/compiler/layr.ts @@ -0,0 +1,121 @@ +import { constants } from '@keymanapp/ldml-keyboard-constants'; +import { /*LDMLKeyboard,*/ KMXPlus,/* Constants*/ } from '@keymanapp/common-types'; +import { CompilerMessages } from './messages.js'; +import { SectionCompiler } from "./section-compiler.js"; + +import GlobalSections = KMXPlus.GlobalSections; +import Layr = KMXPlus.Layr; +import LayrEntry = KMXPlus.LayrEntry; +import LayrList = KMXPlus.LayrList; +import LayrRow = KMXPlus.LayrRow; +// import USVirtualKeyMap = Constants.USVirtualKeyMap; + +export class LayrCompiler extends SectionCompiler { + + public get id() { + return constants.section.layr; + } + + public validate() { + let valid = true; + if(!this.keyboard.layers?.[0]?.layer?.length) { + valid = false; + this.callbacks.reportMessage(CompilerMessages.Error_MustBeAtLeastOneLayerElement()); + } + // TODO-LDML + + // if(this.keyboard.layers?.[0]?.form == 'hardware') { + // for(let layer of this.keyboard.layers[0].layer) { + // valid = this.validateHardwareLayer(layer) && valid; // note: always validate even if previously invalid results found + // } + // } + return valid; + } + + public compile(sections: GlobalSections): Layr { + // Use LayerMap + keys to generate compiled keys for hardware + + const sect = new Layr(); + // if(this.keyboard.layers?.[0]?.form == 'hardware') { + // for(let layer of this.keyboard.layers[0].layer) { + // let sect = this.compileHardwareLayer(sections, layer); + // return sect; + // } + // } + + sect.lists = this.keyboard.layers.map((layers) => { + const list : LayrList = { + flags: 0, // flag + hardware: sections.strs.allocString(layers.hardware), + layerIndex: sect.layers.length, + count: layers.layer.length, + }; + if (layers.form === 'touch') { + list.flags |= constants.layr_list_flags_touch; + } + // TODO-LDML: minDeviceWidth? modifiers? + // push all layers + layers.layer.forEach((layer) => { + const entry : LayrEntry = { + id: sections.strs.allocString(layer.id), + modifier: sections.strs.allocString(layer.modifier), + rowIndex: sect.rows.length, + count: layer.row.length, + }; + layer.row.forEach((row) => { + const lrow : LayrRow = { + keyIndex: sect.keys.length, + count: 0, + }; + row.keys.split(' ').forEach((keyid) => { + sect.keys.push(keyid); // just the id… for now + lrow.count ++; // increment key count + }); + sect.rows.push(lrow); + }); + sect.layers.push(entry); + }); + return list; + }); + + return sect; + } + + // private compileHardwareLayer( + // sections: GlobalSections, + // layer: LDMLKeyboard.LKLayer + // ): Layr { + // let result = new Layr(); + // const mod = this.translateLayerIdToModifier(layer.id); + + // let y = -1; + // for(let row of layer.row) { + // y++; + + // const keys = row.keys.split(' '); + // let x = -1; + // for(let key of keys) { + // x++; + + // let keydef = this.keyboard.keys?.key?.find(x => x.id == key); + + // result.keys.push({ + // vkey: USVirtualKeyMap[y][x], + // mod: mod, + // to: sections.strs.allocString(keydef.to), + // flags: 0 // Note: 'expand' is never set here, only by the .kmx builder + // }); + // } + // } + + // return result; + // } + + // private translateLayerIdToModifier(id: string) { + // if(id == 'base') { + // return 0; + // } + // // TODO: other modifiers + // return 0; + // } +} diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/key2/maximal.xml b/developer/src/kmc-keyboard/test/fixtures/sections/key2/maximal.xml index cb1dbd0573..10ed505c8b 100644 --- a/developer/src/kmc-keyboard/test/fixtures/sections/key2/maximal.xml +++ b/developer/src/kmc-keyboard/test/fixtures/sections/key2/maximal.xml @@ -24,7 +24,7 @@ - + diff --git a/developer/src/kmc-keyboard/test/test-layr.ts b/developer/src/kmc-keyboard/test/test-layr.ts new file mode 100644 index 0000000000..4c39690894 --- /dev/null +++ b/developer/src/kmc-keyboard/test/test-layr.ts @@ -0,0 +1,103 @@ +import 'mocha'; +import { assert } from 'chai'; +import { LayrCompiler } from '../src/compiler/layr.js'; +import { compilerTestCallbacks, loadSectionFixture } from './helpers/index.js'; +import { KMXPlus } from '@keymanapp/common-types'; +// import { CompilerMessages } from '../src/compiler/messages.js'; +import { constants } from '@keymanapp/ldml-keyboard-constants'; + +import Layr = KMXPlus.Layr; + +describe('layr', function () { + this.slow(500); // 0.5 sec -- json schema validation takes a while + + // reuse the keys minimal file + it('should compile minimal keys data', function () { + let layr = loadSectionFixture(LayrCompiler, 'sections/keys/minimal.xml', compilerTestCallbacks) as Layr; + assert.ok(layr); + assert.equal(compilerTestCallbacks.messages.length, 0); + + assert.equal(layr.lists?.length, 1); + assert.equal(layr.layers?.length, 1); + assert.equal(layr.rows?.length, 1); + assert.equal(layr.keys?.length, 1); + + assert.ok(layr.lists[0]); + assert.equal(layr.lists[0].count, 1); + assert.equal(layr.lists[0].flags & constants.layr_list_flags_mask_form, constants.layr_list_flags_hardware); + assert.equal(layr.lists[0].hardware?.value, ''); + assert.equal(layr.lists[0].layerIndex, 0); + + assert.ok(layr.layers[0]); + assert.equal(layr.layers[0].count, 1); + assert.equal(layr.layers[0].id.value, 'base'); + // assert.equal(layr.layers[0].modifier, ?); // TODO-LDML + assert.equal(layr.layers[0].rowIndex, 0); + + assert.ok(layr.rows[0]); + assert.equal(layr.rows[0].count, 1); + assert.equal(layr.rows[0].keyIndex, 0); + + assert.ok(layr.keys[0]); + // assert.equal(layr.vkeys[0], 0); // TODO-LDML + }); + + // reuse key2 maximal + it('should compile maximal key2 data', function () { + let layr = loadSectionFixture(LayrCompiler, 'sections/key2/maximal.xml', compilerTestCallbacks) as Layr; + assert.ok(layr); + assert.equal(compilerTestCallbacks.messages.length, 0); + + assert.equal(layr.lists?.length, 2); + assert.equal(layr.layers?.length, 3); + assert.equal(layr.rows?.length, 3); + assert.equal(layr.keys?.length, 8); + + const listHardware = layr.lists.find(v => v.hardware.value === 'abnt2'); + assert.ok(listHardware); + assert.equal(listHardware.count, 2); + assert.equal(listHardware.flags & constants.layr_list_flags_mask_form, constants.layr_list_flags_hardware); + assert.equal(listHardware.hardware?.value, 'abnt2'); + const hardware0 = layr.layers[listHardware.layerIndex + 0]; + assert.ok(hardware0); + assert.equal(hardware0.count, 1); + assert.equal(hardware0.id.value, 'base'); + // assert.equal(hardware0.modifier, ?); // TODO-LDML + const hardware0row0 = layr.rows[hardware0.rowIndex + 0]; + assert.ok(hardware0row0); + assert.equal(hardware0row0.count, 2); + assert.sameMembers(layr.keys.slice(hardware0row0.keyIndex, + hardware0row0.keyIndex + hardware0row0.count), + 'Q W'.split(' ') + ); + const hardware1 = layr.layers[listHardware.layerIndex + 1]; + assert.ok(hardware1); + assert.equal(hardware1.count, 1); + assert.equal(hardware1.id.value, 'shift'); + // assert.equal(hardware0.modifier, ?); // TODO-LDML + const hardware1row0 = layr.rows[hardware1.rowIndex + 0]; + assert.ok(hardware1row0); + assert.equal(hardware1row0.count, 2); + assert.sameMembers(layr.keys.slice(hardware1row0.keyIndex, + hardware1row0.keyIndex + hardware1row0.count), + 'q w'.split(' ') + ); + + const listTouch = layr.lists.find(v => v.hardware.value !== 'abnt2'); // TODO-LDML: need to add some more fields!!! + assert.ok(listTouch); + assert.equal(listTouch.count, 1); + assert.equal(listTouch.flags & constants.layr_list_flags_mask_form, constants.layr_list_flags_touch); + const touch0 = layr.layers[listTouch.layerIndex + 0]; + assert.ok(touch0); + assert.equal(touch0.count, 1); + assert.equal(touch0.id.value, 'base'); + // assert.equal(touch0.modifier, ?); // TODO-LDML + const touch0row0 = layr.rows[touch0.rowIndex + 0]; + assert.ok(touch0row0); + assert.equal(touch0row0.count, 4); + assert.sameMembers(layr.keys.slice(touch0row0.keyIndex, + touch0row0.keyIndex + touch0row0.count), + 'Q q W w'.split(' ') + ); + }); +}); From e5ec3cbb6a1f0a6ad89d6bb749abad63060cca9e Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Fri, 25 Nov 2022 18:01:41 -0600 Subject: [PATCH 18/33] =?UTF-8?q?feat(core):=20ldml:=20spec=20updates=20?= =?UTF-8?q?=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit layr.keys is now a list of string ids For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- core/src/ldml/C7043_ldml.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/core/src/ldml/C7043_ldml.md b/core/src/ldml/C7043_ldml.md index 77a78ac6b5..0de36adc42 100644 --- a/core/src/ldml/C7043_ldml.md +++ b/core/src/ldml/C7043_ldml.md @@ -348,12 +348,13 @@ Represents layers on the keyboard. Each layer list corresponds to one `` element. There are `listCount` total lists. -| ∆ | Bits | Name | Description | -|---|------|------------|--------------------------------------------| -| 0+| 32 | flags | int: per-layers options | -| 4+| 32 | hardware | str: layout (`us`,`iso`,`jis`,`abnt2`) | -| 8+| 32 | layer | int: index to first layer element | -|12+| 32 | count | int: number of layer elements in this list | +| ∆ | Bits | Name | Description | +|---|------|------------------|--------------------------------------------| +| 0+| 32 | flags | int: per-layers options | +| 4+| 32 | hardware | str: layout (`us`,`iso`,`jis`,`abnt2`) | +| 8+| 32 | layer | int: index to first layer element | +|12+| 32 | count | int: number of layer elements in this list | +|16+| 32 | minDeviceWidth | int: min device width in millimeters, or 0 | - `flags`: a 32-bit bitfield defined as below: @@ -362,6 +363,8 @@ There are `listCount` total lists. | 0 | form | 0: hardware  | | 0 | form | 1: touch | +Layers are sorted hardware-first, then by minimum width ascending. + ### `layr.layers` subtable Each layer entry corresponds to one `` element @@ -391,7 +394,7 @@ There are `keyCount` total key entries. | ∆ | Bits | Name | Description | |---|------|---------|------------------------------------------| -| 0+| 32 | key | int: index into `key2` section | +| 0+| 32 | key | str: key id | ### C7043.2.14 `disp`—Display list From e98835d35bf2f128662bc479c69aae7dc3c6eac6 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Fri, 25 Nov 2022 18:17:45 -0600 Subject: [PATCH 19/33] =?UTF-8?q?feat(core):=20ldml:=20updates=20to=20layr?= =?UTF-8?q?=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - re-redo of layr - most tests now pass - updated layr builder For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- .../src/kmx/kmx-plus-builder/build-layr.ts | 70 +++++++++++++- common/web/types/src/kmx/kmx-plus.ts | 26 +---- .../src/kmc-keyboard/src/compiler/layr.ts | 94 ++++--------------- developer/src/kmc-keyboard/test/test-layr.ts | 89 ++++++++---------- 4 files changed, 126 insertions(+), 153 deletions(-) diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts index f8891ee68a..15fa636d16 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts @@ -1,8 +1,8 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; import { /*KeyFlags,*/ KMXPlusData } from "../kmx-plus.js"; -import { /*build_strs_index,*/ BUILDER_STRS } from "./build-strs.js"; -import { /*build_list_index,*/ BUILDER_LIST } from "./build-list.js"; +import { build_strs_index, BUILDER_STRS } from "./build-strs.js"; +import { /*build_list_index*/ BUILDER_LIST } from "./build-list.js"; import { BUILDER_SECTION } from "./builder-section.js"; /* ------------------------------------------------------------------ @@ -15,6 +15,7 @@ interface BUILDER_LAYR_LIST { hardware: number; // str layer: number; // index count: number; + minDeviceWidth: number; }; interface BUILDER_LAYR_LAYER { @@ -51,7 +52,7 @@ export interface BUILDER_LAYR extends BUILDER_SECTION { export function build_layr(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_list: BUILDER_LIST): BUILDER_LAYR { - if(!kmxplus.layr?.layers && !kmxplus.layr?.lists && !kmxplus.layr?.rows && !kmxplus.layr?.keys) { + if (!kmxplus.layr?.lists) { return null; } @@ -59,7 +60,7 @@ export function build_layr(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_l ident: constants.hex_section_id(constants.section.key2), size: constants.length_layr, _offset: 0, - listCount: 0, + listCount: kmxplus.layr.lists.length, layerCount: 0, rowCount: 0, keyCount: 0, @@ -71,7 +72,66 @@ export function build_layr(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_l keys: [] }; - // TODO-LDML + // pre-sort layers + kmxplus.layr.lists.sort((a, b) => { + const aform = a.flags & constants.layr_list_flags_mask_form; + const bform = b.flags & constants.layr_list_flags_mask_form; + if (aform < bform) { + return -1; + } else if(aform > bform) { + return 1; + } + if (a.minDeviceWidth < b.minDeviceWidth) { + return -1; + } else if(a.minDeviceWidth > b.minDeviceWidth) { + return 1; + } else { + return 0; // same + } + }); + layr.lists = kmxplus.layr.lists.map((list) => { + const blist : BUILDER_LAYR_LIST = { + flags: list.flags, + hardware: build_strs_index(sect_strs, list.hardware), + layer: layr.layers.length, + count: list.layers.length, + minDeviceWidth: list.minDeviceWidth, + }; + list.layers.forEach((layer) => { + const blayer : BUILDER_LAYR_LAYER = { + id: build_strs_index(sect_strs, layer.id), + modifier: build_strs_index(sect_strs, layer.modifier), + row: layr.rows.length, + count: layer.rows.length, + }; + layer.rows.forEach((row) => { + const brow : BUILDER_LAYR_ROW = { + key: layr.keys.length, + count: row.keys.length, + }; + row.keys.forEach((key) => { + const bkey : BUILDER_LAYR_KEY = { + key: build_strs_index(sect_strs, key), + }; + layr.keys.push(bkey); + layr.keyCount++; + }); + layr.rows.push(brow); + layr.rowCount++; + }); + layr.layers.push(blayer); + layr.layerCount++; + }); + + return blist; + }); + + let offset = constants.length_layr + + (constants.length_layr_list * layr.listCount) + + (constants.length_layr_entry * layr.layerCount) + + (constants.length_layr_row * layr.rowCount) + + (constants.length_layr_key * layr.keyCount); + layr.size = offset; return layr; } diff --git a/common/web/types/src/kmx/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus.ts index c55d36f9c1..b5ab008350 100644 --- a/common/web/types/src/kmx/kmx-plus.ts +++ b/common/web/types/src/kmx/kmx-plus.ts @@ -223,11 +223,8 @@ export class Disp extends Section { export class LayrList { flags: number; hardware: StrsItem; - /** - * Index into Layr.layers - */ - layerIndex: number; - count: number; + layers: LayrEntry[] = []; + minDeviceWidth: number; // millimeters }; /** @@ -236,32 +233,18 @@ export class LayrList { export class LayrEntry { id: StrsItem; modifier: StrsItem; - /** - * index into Layr.rows - */ - rowIndex: number; - count: number; + rows: LayrRow[] = []; }; /** * In-memory `` */ export class LayrRow { - /** - * index into Layr.vkeys - */ - keyIndex: number; - count: number; + keys: StrsItem[] = []; }; export class Layr extends Section { lists: LayrList[] = []; - layers: LayrEntry[] = []; - rows: LayrRow[] = []; - /** - * each item is a key id for in-memory - */ - keys: string[] = []; }; export class Key2Keys { @@ -511,6 +494,7 @@ export class KMXPlusFile extends KMXFile { hardware: r.uint32le, //str layer: r.uint32le, // index into layers count: r.uint32le, + minDeviceWidth: r.uint32le, // integer: millimeters }); this.COMP_PLUS_LAYR_ROW = new r.Struct({ diff --git a/developer/src/kmc-keyboard/src/compiler/layr.ts b/developer/src/kmc-keyboard/src/compiler/layr.ts index d53b5780f0..2aa94f09fe 100644 --- a/developer/src/kmc-keyboard/src/compiler/layr.ts +++ b/developer/src/kmc-keyboard/src/compiler/layr.ts @@ -23,99 +23,37 @@ export class LayrCompiler extends SectionCompiler { this.callbacks.reportMessage(CompilerMessages.Error_MustBeAtLeastOneLayerElement()); } // TODO-LDML - - // if(this.keyboard.layers?.[0]?.form == 'hardware') { - // for(let layer of this.keyboard.layers[0].layer) { - // valid = this.validateHardwareLayer(layer) && valid; // note: always validate even if previously invalid results found - // } - // } return valid; } public compile(sections: GlobalSections): Layr { - // Use LayerMap + keys to generate compiled keys for hardware - const sect = new Layr(); - // if(this.keyboard.layers?.[0]?.form == 'hardware') { - // for(let layer of this.keyboard.layers[0].layer) { - // let sect = this.compileHardwareLayer(sections, layer); - // return sect; - // } - // } sect.lists = this.keyboard.layers.map((layers) => { const list : LayrList = { - flags: 0, // flag + flags: 0, hardware: sections.strs.allocString(layers.hardware), - layerIndex: sect.layers.length, - count: layers.layer.length, + minDeviceWidth: layers.minDeviceWidth || 0, + layers: layers.layer.map((layer) => { + const entry : LayrEntry = { + id: sections.strs.allocString(layer.id), + modifier: sections.strs.allocString(layer.modifier), + rows: layer.row.map((row) => { + const erow : LayrRow = { + keys: row.keys.split(' ').map((id) => sections.strs.allocString(id)), + }; + return erow; + }), + }; + // TODO-LDML: modifiers + return entry; + }), }; if (layers.form === 'touch') { list.flags |= constants.layr_list_flags_touch; } - // TODO-LDML: minDeviceWidth? modifiers? - // push all layers - layers.layer.forEach((layer) => { - const entry : LayrEntry = { - id: sections.strs.allocString(layer.id), - modifier: sections.strs.allocString(layer.modifier), - rowIndex: sect.rows.length, - count: layer.row.length, - }; - layer.row.forEach((row) => { - const lrow : LayrRow = { - keyIndex: sect.keys.length, - count: 0, - }; - row.keys.split(' ').forEach((keyid) => { - sect.keys.push(keyid); // just the id… for now - lrow.count ++; // increment key count - }); - sect.rows.push(lrow); - }); - sect.layers.push(entry); - }); return list; }); - return sect; } - - // private compileHardwareLayer( - // sections: GlobalSections, - // layer: LDMLKeyboard.LKLayer - // ): Layr { - // let result = new Layr(); - // const mod = this.translateLayerIdToModifier(layer.id); - - // let y = -1; - // for(let row of layer.row) { - // y++; - - // const keys = row.keys.split(' '); - // let x = -1; - // for(let key of keys) { - // x++; - - // let keydef = this.keyboard.keys?.key?.find(x => x.id == key); - - // result.keys.push({ - // vkey: USVirtualKeyMap[y][x], - // mod: mod, - // to: sections.strs.allocString(keydef.to), - // flags: 0 // Note: 'expand' is never set here, only by the .kmx builder - // }); - // } - // } - - // return result; - // } - - // private translateLayerIdToModifier(id: string) { - // if(id == 'base') { - // return 0; - // } - // // TODO: other modifiers - // return 0; - // } } diff --git a/developer/src/kmc-keyboard/test/test-layr.ts b/developer/src/kmc-keyboard/test/test-layr.ts index 4c39690894..921abd4ba0 100644 --- a/developer/src/kmc-keyboard/test/test-layr.ts +++ b/developer/src/kmc-keyboard/test/test-layr.ts @@ -7,6 +7,15 @@ import { KMXPlus } from '@keymanapp/common-types'; import { constants } from '@keymanapp/ldml-keyboard-constants'; import Layr = KMXPlus.Layr; +import LayrRow = KMXPlus.LayrRow; + +function allKeysOk(row : LayrRow, str : string, msg? : string) { + const split = str.split(' '); + assert.equal(row.keys.length, split.length, msg); + for (let i=0; i v.hardware.value === 'abnt2'); assert.ok(listHardware); - assert.equal(listHardware.count, 2); + assert.equal(listHardware.minDeviceWidth, 0); + assert.equal(listHardware.layers.length, 2); assert.equal(listHardware.flags & constants.layr_list_flags_mask_form, constants.layr_list_flags_hardware); assert.equal(listHardware.hardware?.value, 'abnt2'); - const hardware0 = layr.layers[listHardware.layerIndex + 0]; + const hardware0 = listHardware.layers[0]; assert.ok(hardware0); - assert.equal(hardware0.count, 1); assert.equal(hardware0.id.value, 'base'); // assert.equal(hardware0.modifier, ?); // TODO-LDML - const hardware0row0 = layr.rows[hardware0.rowIndex + 0]; + const hardware0row0 = hardware0.rows[0]; assert.ok(hardware0row0); - assert.equal(hardware0row0.count, 2); - assert.sameMembers(layr.keys.slice(hardware0row0.keyIndex, - hardware0row0.keyIndex + hardware0row0.count), - 'Q W'.split(' ') - ); - const hardware1 = layr.layers[listHardware.layerIndex + 1]; + assert.equal(hardware0row0.keys.length, 2); + allKeysOk(hardware0row0,'Q W', 'hardware0row0'); + const hardware1 = listHardware.layers[1]; assert.ok(hardware1); - assert.equal(hardware1.count, 1); + assert.equal(hardware1.rows.length, 1); assert.equal(hardware1.id.value, 'shift'); // assert.equal(hardware0.modifier, ?); // TODO-LDML - const hardware1row0 = layr.rows[hardware1.rowIndex + 0]; + const hardware1row0 = hardware1.rows[0]; assert.ok(hardware1row0); - assert.equal(hardware1row0.count, 2); - assert.sameMembers(layr.keys.slice(hardware1row0.keyIndex, - hardware1row0.keyIndex + hardware1row0.count), - 'q w'.split(' ') - ); + assert.equal(hardware1row0.keys.length, 2); + allKeysOk(hardware1row0,'q w', 'hardware1row0'); const listTouch = layr.lists.find(v => v.hardware.value !== 'abnt2'); // TODO-LDML: need to add some more fields!!! assert.ok(listTouch); - assert.equal(listTouch.count, 1); + assert.equal(listTouch.minDeviceWidth, 300); + assert.equal(listTouch.layers.length, 1); assert.equal(listTouch.flags & constants.layr_list_flags_mask_form, constants.layr_list_flags_touch); - const touch0 = layr.layers[listTouch.layerIndex + 0]; + const touch0 = listTouch.layers[0]; assert.ok(touch0); - assert.equal(touch0.count, 1); + assert.equal(touch0.rows.length, 1); assert.equal(touch0.id.value, 'base'); // assert.equal(touch0.modifier, ?); // TODO-LDML - const touch0row0 = layr.rows[touch0.rowIndex + 0]; + const touch0row0 = touch0.rows[0]; assert.ok(touch0row0); - assert.equal(touch0row0.count, 4); - assert.sameMembers(layr.keys.slice(touch0row0.keyIndex, - touch0row0.keyIndex + touch0row0.count), - 'Q q W w'.split(' ') - ); + assert.equal(touch0row0.keys.length, 4); + allKeysOk(touch0row0,'Q q W w', 'touch0row0'); }); }); From 2d7aa026af925bc2faf71fc6443c6526e54973c2 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Tue, 29 Nov 2022 18:05:04 -0600 Subject: [PATCH 20/33] =?UTF-8?q?feat(core):=20ldml:=20updating=20guide=20?= =?UTF-8?q?updates=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - update notes on updating fixtures For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- core/src/ldml/C7532_ldml_updating.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/src/ldml/C7532_ldml_updating.md b/core/src/ldml/C7532_ldml_updating.md index b3ce8d9251..7251b4e182 100644 --- a/core/src/ldml/C7532_ldml_updating.md +++ b/core/src/ldml/C7532_ldml_updating.md @@ -66,8 +66,10 @@ working on ‘layr’, using ‘disp’ as a model from https://github.com/keyma - Note that some variable length parts (such as the actual text data in `strs`) are sometimes in a separate emit function. Anything that's not in the `COMP_PLUS_STRS` `r.Struct` definition needs one of these. - update basic.xml and basic.txt - - Tweak `basic.xml` as needed - - You can use `developer/src/kmc-keyboard/build.sh build-fixtures` which will generate `build/test/fixtures/basic-xml.kmx` as well as `.kvk` - - … BUT DO NOT CHECK IT IN! + - Tweak `eveloper/src/kmc-keyboard/test/fixtures/basic.xml` as needed + - You can use `developer/src/kmc-keyboard/build.sh build-fixtures` which will generate these. The two .kmx files are supposed to match: if not, fix `basic.txt` or fix other bugs. + - `developer/src/kmc-keyboard/build/test/fixtures/basic-txt.kmx` - KMX generated from basic.txt. + - `developer/src/kmc-keyboard/build/test/fixtures/basic-xml.kmx` - KMX generated from basic.xml. + - `developer/src/kmc-keyboard/build/test/fixtures/basic-xml.kvk` - KVK generated from basic.xml. ## more to come From 66f01f4cf549a2766303815e81e2952e836ef501 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Tue, 29 Nov 2022 18:33:04 -0600 Subject: [PATCH 21/33] =?UTF-8?q?feat(core):=20ldml:=20fix=20to=20build-la?= =?UTF-8?q?yr=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- common/web/types/src/kmx/kmx-plus-builder/build-layr.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts index 15fa636d16..7705903840 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts @@ -57,7 +57,7 @@ export function build_layr(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_l } let layr: BUILDER_LAYR = { - ident: constants.hex_section_id(constants.section.key2), + ident: constants.hex_section_id(constants.section.layr), size: constants.length_layr, _offset: 0, listCount: kmxplus.layr.lists.length, From f150a96a632d9f80842a0481fd352aa26aa611e9 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Tue, 29 Nov 2022 18:33:13 -0600 Subject: [PATCH 22/33] =?UTF-8?q?feat(core):=20ldml:=20basic:=20WIP=20?= =?UTF-8?q?=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- .../src/kmc-keyboard/test/fixtures/basic.txt | 28 +++++++++++++++++++ .../src/kmc-keyboard/test/fixtures/basic.xml | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/developer/src/kmc-keyboard/test/fixtures/basic.txt b/developer/src/kmc-keyboard/test/fixtures/basic.txt index 37a6c83dcc..40fcb15504 100644 --- a/developer/src/kmc-keyboard/test/fixtures/basic.txt +++ b/developer/src/kmc-keyboard/test/fixtures/basic.txt @@ -107,9 +107,18 @@ block(sectitems) 66 69 6e 6c diff(sect,finl) + 6b 65 79 32 + diff(sect,key2) + 6b 65 79 73 diff(sect,keys) + 6c 61 79 72 + diff(sect,layr) + + 6c 69 73 74 + diff(sect,list) + 6c 6f 63 61 diff(sect,loca) @@ -256,6 +265,11 @@ block(finl) 01 00 00 00 # KMX_DWORD flags; # }; +block(key2) # struct COMP_KMXPLUS_KEY2 { + 5b 54 79 32 # KMX_DWORD header.ident; // 0000 Section name - key2 + sizeof(key2) # KMX_DWORD header.size; // 0004 Section length +# TODO: key2 + # ---------------------------------------------------------------------------------------------------- # keys # ---------------------------------------------------------------------------------------------------- @@ -273,6 +287,16 @@ block(keys) # struct COMP_KMXPLUS_KEYS { c0 00 00 00 00 00 00 00 index(strNull,strKey1,2) 01 00 00 00 # KMX_DWORD vkey, mod, to, flags; 31 00 00 00 00 00 00 00 index(strNull,strKey2,2) 01 00 00 00 # KMX_DWORD vkey, mod, to, flags; +block(layr) # struct COMP_KMXPLUS_LAYR { + 6c 61 79 72 # KMX_DWORD header.ident; // 0000 Section name - layr + sizeof(layr) # KMX_DWORD header.size; // 0004 Section length +# TODO: layr + +block(list) # struct COMP_KMXPLUS_LAYR_LIST { + 6c 69 73 74 # KMX_DWORD header.ident; // 0000 Section name - list + sizeof(list) # KMX_DWORD header.size; // 0004 Section length +# TODO: list + block(loca) # struct COMP_KMXPLUS_LOCA { 6c 6f 63 61 # KMX_DWORD header.ident; // 0000 Section name - loca sizeof(loca) # KMX_DWORD header.size; // 0004 Section length @@ -336,11 +360,13 @@ block(strs) # struct COMP_KMXPLUS_STRS { diff(strs,strName) sizeof(strName,2) diff(strs,strElemTranFrom1) sizeof(strElemTranFrom1,2) diff(strs,strElemTranFrom2) sizeof(strElemTranFrom2,2) + diff(strs,strBase) sizeof(strBase,2) diff(strs,strElemBkspFrom2) sizeof(strElemBkspFrom2,2) diff(strs,strLocale) sizeof(strLocale,2) diff(strs,strLayout) sizeof(strLayout,2) diff(strs,strAuthor) sizeof(strAuthor,2) diff(strs,strConformsTo) sizeof(strConformsTo,2) + diff(strs,strThat) sizeof(strThat,2) diff(strs,strTranTo) sizeof(strTranTo,2) diff(strs,strKey1) sizeof(strKey1,2) diff(strs,strKey2) sizeof(strKey2,2) @@ -360,12 +386,14 @@ block(strs) # struct COMP_KMXPLUS_STRS { block(strName) 54 00 65 00 73 00 74 00 4b 00 62 00 64 00 block(x) 00 00 # 'TestKbd' block(strElemTranFrom1) 5E 00 block(x) 00 00 # '^' block(strElemTranFrom2) 61 00 block(x) 00 00 # 'a' + block(strBase) 62 00 61 00 73 00 65 00 block(x) 00 00 # 'base' block(strElemBkspFrom2) 65 00 block(x) 00 00 # 'e' block(strLocale) 6d 00 74 00 block(x) 00 00 # 'mt' block(strLayout) 71 00 77 00 65 00 72 00 74 00 79 00 block(x) 00 00 # 'qwerty' block(strAuthor) 73 00 72 00 6c 00 32 00 39 00 35 00 block(x) 00 00 # 'srl295' block(strConformsTo) 74 00 65 00 63 00 68 00 70 00 72 00 65 00 76 00 69 00 65 00 77 00 block(x) 00 00 # 'techpreview' + block(strThat) 74 00 68 00 61 00 74 00 block(x) 00 00 # 'that' block(strTranTo) E2 00 block(x) 00 00 # 'â' block(strKey1) 27 01 block(x) 00 00 # 'ħ' block(strKey2) 90 17 b6 17 block(x) 00 00 # 'ថា' diff --git a/developer/src/kmc-keyboard/test/fixtures/basic.xml b/developer/src/kmc-keyboard/test/fixtures/basic.xml index f22a4ff605..9cb1582962 100644 --- a/developer/src/kmc-keyboard/test/fixtures/basic.xml +++ b/developer/src/kmc-keyboard/test/fixtures/basic.xml @@ -25,7 +25,7 @@ - + From 30761fec01bd32c60b9a30a2b66b9aea862a71f2 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Sat, 3 Dec 2022 04:55:52 +0800 Subject: [PATCH 23/33] =?UTF-8?q?feat(core):=20ldml:=20basic:=20updates=20?= =?UTF-8?q?to=20basic.txt=20for=20layr=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - doc fix also - key2 still to go For feat(developer): ldml: include all LDML data in kmx+ 🙀 #753 --- common/web/types/src/kmx/kmx-plus.ts | 2 +- .../src/kmc-keyboard/test/fixtures/basic.txt | 32 ++++++++++++++++++- .../src/kmc-keyboard/test/fixtures/basic.xml | 2 +- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/common/web/types/src/kmx/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus.ts index b5ab008350..ba607f32db 100644 --- a/common/web/types/src/kmx/kmx-plus.ts +++ b/common/web/types/src/kmx/kmx-plus.ts @@ -486,7 +486,7 @@ export class KMXPlusFile extends KMXFile { }); this.COMP_PLUS_LAYR_KEY = new r.Struct({ - key: r.uint32le, // index into key2 + key: r.uint32le, // str: key id }); this.COMP_PLUS_LAYR_LIST = new r.Struct({ diff --git a/developer/src/kmc-keyboard/test/fixtures/basic.txt b/developer/src/kmc-keyboard/test/fixtures/basic.txt index 40fcb15504..9f34a6c241 100644 --- a/developer/src/kmc-keyboard/test/fixtures/basic.txt +++ b/developer/src/kmc-keyboard/test/fixtures/basic.txt @@ -291,11 +291,39 @@ block(layr) # struct COMP_KMXPLUS_LAYR { 6c 61 79 72 # KMX_DWORD header.ident; // 0000 Section name - layr sizeof(layr) # KMX_DWORD header.size; // 0004 Section length # TODO: layr + 01 00 00 00 # KMX_DWORD listCount + 01 00 00 00 # KMX_DWORD rowCount + 02 00 00 00 # KMX_DWORD keyCount + 00 00 00 00 # KMX_DWORD reserved0 + 00 00 00 00 # KMX_DWORD reserved1 + # list 0 + # flags + index(strNull,strUs,2) # KMXPLUS_STR hardware; + 00 00 00 00 # KMX_DWORD layer; + 01 00 00 00 # count + 7B 00 00 00 # KMX_DWORD minDeviceWidth; // 123 + # layers 0 + index(strNull,strBase,2) # KMXPLUS_STR id; + 00 00 00 00 # KMXPLUS_STR mod str + 00 00 00 00 # KMX_DWORD row index + 01 00 00 00 # KMX_DWORD count + # rows 0 + 00 00 00 00 # KMX_DWORD key index + 02 00 00 00 # KMX_DWORD count + # keys + index(strNull,strHmaqtua,2) # KMXPLUS_STR locale; // 'hmaqtua' + index(strNull,strThat,2) # KMXPLUS_STR locale; // 'that' block(list) # struct COMP_KMXPLUS_LAYR_LIST { 6c 69 73 74 # KMX_DWORD header.ident; // 0000 Section name - list sizeof(list) # KMX_DWORD header.size; // 0004 Section length -# TODO: list + 01 00 00 00 # KMX_DWORD listCount (should be 3) + 01 00 00 00 # KMX_DWORD indexCount (should be 3) + # first the null list + 00 00 00 00 # KMX_DWORD lists[0].index + 00 00 00 00 # KMX_DWORD lists[0].count + # now the null index + 00 00 00 00 # KMX_DWORD indices[0] (null) block(loca) # struct COMP_KMXPLUS_LOCA { 6c 6f 63 61 # KMX_DWORD header.ident; // 0000 Section name - loca @@ -388,12 +416,14 @@ block(strs) # struct COMP_KMXPLUS_STRS { block(strElemTranFrom2) 61 00 block(x) 00 00 # 'a' block(strBase) 62 00 61 00 73 00 65 00 block(x) 00 00 # 'base' block(strElemBkspFrom2) 65 00 block(x) 00 00 # 'e' + block(strHmaqtua) 68 00 6d 00 61 00 71 00 74 00 75 00 61 00 block(x) 00 00 # 'hmaqtua' block(strLocale) 6d 00 74 00 block(x) 00 00 # 'mt' block(strLayout) 71 00 77 00 65 00 72 00 74 00 79 00 block(x) 00 00 # 'qwerty' block(strAuthor) 73 00 72 00 6c 00 32 00 39 00 35 00 block(x) 00 00 # 'srl295' block(strConformsTo) 74 00 65 00 63 00 68 00 70 00 72 00 65 00 76 00 69 00 65 00 77 00 block(x) 00 00 # 'techpreview' block(strThat) 74 00 68 00 61 00 74 00 block(x) 00 00 # 'that' + block(strUs) 75 73 block(x) 00 00 block(strTranTo) E2 00 block(x) 00 00 # 'â' block(strKey1) 27 01 block(x) 00 00 # 'ħ' block(strKey2) 90 17 b6 17 block(x) 00 00 # 'ថា' diff --git a/developer/src/kmc-keyboard/test/fixtures/basic.xml b/developer/src/kmc-keyboard/test/fixtures/basic.xml index 9cb1582962..fdc8bdfa30 100644 --- a/developer/src/kmc-keyboard/test/fixtures/basic.xml +++ b/developer/src/kmc-keyboard/test/fixtures/basic.xml @@ -29,7 +29,7 @@ - + From eb8c4dea7aeff47eddcd25edb4d9680b2b0b3b84 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Thu, 15 Dec 2022 15:27:58 -0600 Subject: [PATCH 24/33] =?UTF-8?q?feat(core):=20ldml:=20basic:=20updates=20?= =?UTF-8?q?to=20basic.txt=20for=20layr=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - more key2 chan ges For feat(developer): ldml: include all LDML data in kmx+ 🙀 #753 --- .../src/kmx/kmx-plus-builder/build-key2.ts | 6 +- common/web/types/src/kmx/kmx-plus.ts | 21 ++++++ .../src/kmc-keyboard/src/compiler/key2.ts | 4 +- .../src/kmc-keyboard/test/fixtures/basic.txt | 65 ++++++++++++++++--- 4 files changed, 83 insertions(+), 13 deletions(-) diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts b/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts index 26933d8c2e..ec071a9de4 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts @@ -108,10 +108,10 @@ export function build_key2(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_l longPress: build_list_index(sect_list, key.longPress), longPressDefault: build_strs_index(sect_strs, key.longPressDefault), multiTap: build_list_index(sect_list, key.multiTap), - flicks: key2.flicks.findIndex(v => v._id === key.flicks), + flicks: key2.flicks.findIndex(v => v._id === (key.flicks || '')), // flicks id='' is the 'null' flicks }; - // Make sure the flicks were was found - if (key.flicks && !result.flicks) { + // Make sure the flicks were found + if (result.flicks === -1) { throw new Error(`Key2: Could not find flicks id=${key.flicks} for key=${key.id.value}`); } return result; diff --git a/common/web/types/src/kmx/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus.ts index ba607f32db..18f2134566 100644 --- a/common/web/types/src/kmx/kmx-plus.ts +++ b/common/web/types/src/kmx/kmx-plus.ts @@ -288,6 +288,14 @@ export class Key2 extends Section { }; export class List extends Section { + /** + * Allocate a list from a space-separated list of items. + * Note that passing undefined or null or `''` will + * end up being the same as the empty list `[]` + * @param strs Strs section for allocation + * @param s space-separated list of items + * @returns a List object + */ allocListFromSpaces(strs: Strs, s?: string): ListItem { if(s === undefined || s === null) { s = ''; @@ -295,9 +303,22 @@ export class List extends Section { // TODO-LDML: support unicode escaping etc return this.allocList(strs, s.split(' ')); } + /** + * Return a List object referring to the string list. + * Note that a falsy list, or a list containing only an empty string + * `['']` will be stored as an empty list `[]`. + * @param strs Strs section for allocation + * @param s string list to allocate + * @returns + */ allocList(strs: Strs, s?: string[]): ListItem { + // Special case the 'null' list for [] or [''] + if (!s || (s.length === 1 && s[0] === '')) { + return this.lists[0]; + } let result = this.lists.find(item => item.isEqual(s)); if(result === undefined) { + // allocate a new ListItem result = new ListItem(strs, s); this.lists.push(result); } diff --git a/developer/src/kmc-keyboard/src/compiler/key2.ts b/developer/src/kmc-keyboard/src/compiler/key2.ts index 3629d549f6..21214a55c9 100644 --- a/developer/src/kmc-keyboard/src/compiler/key2.ts +++ b/developer/src/kmc-keyboard/src/compiler/key2.ts @@ -77,8 +77,8 @@ export class Key2Compiler extends SectionCompiler { const keySwitch = sections.strs.allocString(key.switch); // 'switch' is a reserved word flags |= constants.key2_key_flags_extend; const to = sections.strs.allocString(key.to); // TODO-LDML: single char - const width = Math.ceil(key.width * 10.0); - const vkey: any = null; // TODO-LDML: fill in later + const width = Math.ceil((key.width || 1) * 10.0); // default, width=1 + const vkey: any = 0; // TODO-LDML: fill in later sect.keys.push({ flags, flicks, diff --git a/developer/src/kmc-keyboard/test/fixtures/basic.txt b/developer/src/kmc-keyboard/test/fixtures/basic.txt index 9f34a6c241..5fda6a471f 100644 --- a/developer/src/kmc-keyboard/test/fixtures/basic.txt +++ b/developer/src/kmc-keyboard/test/fixtures/basic.txt @@ -140,6 +140,10 @@ block(sectitems) 76 6b 65 79 diff(sect,vkey) +# ???? TODO +00 00 00 00 +00 00 00 00 + block(endsect) # ---------------------------------------------------------------------------------------------------- @@ -264,12 +268,49 @@ block(finl) 00 00 00 00 # KMXPLUS_ELEM before; 01 00 00 00 # KMX_DWORD flags; # }; +# ---------------------------------------------------------------------------------------------------- +# key2 +# ---------------------------------------------------------------------------------------------------- block(key2) # struct COMP_KMXPLUS_KEY2 { - 5b 54 79 32 # KMX_DWORD header.ident; // 0000 Section name - key2 + 6b 65 79 32 # KMX_DWORD header.ident; // 0000 Section name - key2 sizeof(key2) # KMX_DWORD header.size; // 0004 Section length -# TODO: key2 - + 02 00 00 00 # KMX_DWORD keyCount + 01 00 00 00 # KMX_DWORD flicksCount + 00 00 00 00 # KMX_DWORD flickCount + 00 00 00 00 00 00 00 00 00 00 00 00 # Reserved[3] + # keys + # hmaqtua + 00 00 00 00 # KMX_DWORD vkey + index(strNull,strKey1,2) # KMXPLUS_STR 'U+0127' + 01 00 00 00 # KMX_DWORD (flags: extend) + index(strNull,strHmaqtua,2) # KMXPLUS_STR 'hmaqtua' + 00 00 00 00 # KMXPLUS_STR switch + 0A 00 00 00 # KMX_DWORD width*10 + 02 00 00 00 # LIST longPress (TODO: should be 0) + 00 00 00 00 # STR longPressDefault + 01 00 00 00 # LIST multiTap (TODO: should be0) + 00 00 00 00 # flicks 0 + # that + 00 00 00 00 # KMX_DWORD vkey + index(strNull,strKey2,2) # KMXPLUS_STR 'U+0127' + 01 00 00 00 # KMX_DWORD flags = extend + index(strNull,strThat,2) # KMXPLUS_STR 'hmaqtua' + 00 00 00 00 # KMXPLUS_STR switch + 0A 00 00 00 # KMX_DWORD width*10 + 00 00 00 00 # LIST longPress + 00 00 00 00 # STR longPressDefault + 00 00 00 00 # LIST multiTap + 00 00 00 00 # flicks 0 + # flicks + # flicks 0 - null + 00 00 00 00 # KMX_DWORD count + 00 00 00 00 # KMX_DWORD flick + 00 00 00 00 # KMX_STR id + # flick + #00 00 00 00 # LIST directions + #00 00 00 01 # flags + #00 00 00 00 # str: to # ---------------------------------------------------------------------------------------------------- # keys # ---------------------------------------------------------------------------------------------------- @@ -287,10 +328,14 @@ block(keys) # struct COMP_KMXPLUS_KEYS { c0 00 00 00 00 00 00 00 index(strNull,strKey1,2) 01 00 00 00 # KMX_DWORD vkey, mod, to, flags; 31 00 00 00 00 00 00 00 index(strNull,strKey2,2) 01 00 00 00 # KMX_DWORD vkey, mod, to, flags; +# ---------------------------------------------------------------------------------------------------- +# layr +# ---------------------------------------------------------------------------------------------------- + + block(layr) # struct COMP_KMXPLUS_LAYR { 6c 61 79 72 # KMX_DWORD header.ident; // 0000 Section name - layr sizeof(layr) # KMX_DWORD header.size; // 0004 Section length -# TODO: layr 01 00 00 00 # KMX_DWORD listCount 01 00 00 00 # KMX_DWORD rowCount 02 00 00 00 # KMX_DWORD keyCount @@ -319,11 +364,15 @@ block(list) # struct COMP_KMXPLUS_LAYR_LIST { sizeof(list) # KMX_DWORD header.size; // 0004 Section length 01 00 00 00 # KMX_DWORD listCount (should be 3) 01 00 00 00 # KMX_DWORD indexCount (should be 3) - # first the null list - 00 00 00 00 # KMX_DWORD lists[0].index + # lists, first the null list + block(listNull) + index(indexNull,indexNull,2) # KMX_DWORD list index 00 00 00 00 # KMX_DWORD lists[0].count - # now the null index - 00 00 00 00 # KMX_DWORD indices[0] (null) + block(endLists) + # indices + block(indexNull) + index(strNull,strNull,2) # KMXPLUS_STR string index + block(endIndices) block(loca) # struct COMP_KMXPLUS_LOCA { 6c 6f 63 61 # KMX_DWORD header.ident; // 0000 Section name - loca From b036fc58403d235249d956c13cfec0fe94bf13b8 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Fri, 16 Dec 2022 17:53:14 -0600 Subject: [PATCH 25/33] =?UTF-8?q?feat(core):=20ldml:=20basic:=20updates=20?= =?UTF-8?q?to=20basic.txt=20for=20layr=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - key2 now passes! - starting on layr For feat(developer): ldml: include all LDML data in kmx+ 🙀 #753 --- core/include/ldml/keyboardprocessor_ldml.h | 2 +- core/include/ldml/keyboardprocessor_ldml.ts | 2 +- .../src/kmc-keyboard/test/fixtures/basic.txt | 17 +++++++++++++---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/core/include/ldml/keyboardprocessor_ldml.h b/core/include/ldml/keyboardprocessor_ldml.h index 0462ae9850..051470ee6c 100644 --- a/core/include/ldml/keyboardprocessor_ldml.h +++ b/core/include/ldml/keyboardprocessor_ldml.h @@ -45,7 +45,7 @@ #define LDML_LENGTH_KEY2 0x20 #define LDML_LENGTH_KEY2_FLICK_ELEMENT 0xC #define LDML_LENGTH_KEY2_FLICK_LIST 0xC -#define LDML_LENGTH_KEY2_KEY 0x14 +#define LDML_LENGTH_KEY2_KEY 0x28 #define LDML_LENGTH_KEYS 0x10 #define LDML_LENGTH_KEYS_ITEM 0x10 #define LDML_LENGTH_LAYR 0x20 diff --git a/core/include/ldml/keyboardprocessor_ldml.ts b/core/include/ldml/keyboardprocessor_ldml.ts index 8eada52084..ecb2bf78c6 100644 --- a/core/include/ldml/keyboardprocessor_ldml.ts +++ b/core/include/ldml/keyboardprocessor_ldml.ts @@ -240,7 +240,7 @@ class Constants { /** * Length of each item in the 'key2' keys sub-table */ - readonly length_key2_key = 20; + readonly length_key2_key = 40; /** * Length of each item in the 'key2' flick lists sub-table */ diff --git a/developer/src/kmc-keyboard/test/fixtures/basic.txt b/developer/src/kmc-keyboard/test/fixtures/basic.txt index 5fda6a471f..d88bdc0934 100644 --- a/developer/src/kmc-keyboard/test/fixtures/basic.txt +++ b/developer/src/kmc-keyboard/test/fixtures/basic.txt @@ -287,9 +287,9 @@ block(key2) # struct COMP_KMXPLUS_KEY2 { index(strNull,strHmaqtua,2) # KMXPLUS_STR 'hmaqtua' 00 00 00 00 # KMXPLUS_STR switch 0A 00 00 00 # KMX_DWORD width*10 - 02 00 00 00 # LIST longPress (TODO: should be 0) + index(listNull,indexAe,4) # LIST longPress 'a e' 00 00 00 00 # STR longPressDefault - 01 00 00 00 # LIST multiTap (TODO: should be0) + index(listNull,listNull,4) # LIST multiTap 00 00 00 00 # flicks 0 # that 00 00 00 00 # KMX_DWORD vkey @@ -298,9 +298,9 @@ block(key2) # struct COMP_KMXPLUS_KEY2 { index(strNull,strThat,2) # KMXPLUS_STR 'hmaqtua' 00 00 00 00 # KMXPLUS_STR switch 0A 00 00 00 # KMX_DWORD width*10 - 00 00 00 00 # LIST longPress + index(listNull,listNull,4) # LIST longPress 00 00 00 00 # STR longPressDefault - 00 00 00 00 # LIST multiTap + index(listNull,listNull,4) # LIST multiTap 00 00 00 00 # flicks 0 # flicks # flicks 0 - null @@ -308,9 +308,11 @@ block(key2) # struct COMP_KMXPLUS_KEY2 { 00 00 00 00 # KMX_DWORD flick 00 00 00 00 # KMX_STR id # flick + # Right now there aren't any flick elements. #00 00 00 00 # LIST directions #00 00 00 01 # flags #00 00 00 00 # str: to + # ---------------------------------------------------------------------------------------------------- # keys # ---------------------------------------------------------------------------------------------------- @@ -337,6 +339,7 @@ block(layr) # struct COMP_KMXPLUS_LAYR { 6c 61 79 72 # KMX_DWORD header.ident; // 0000 Section name - layr sizeof(layr) # KMX_DWORD header.size; // 0004 Section length 01 00 00 00 # KMX_DWORD listCount + 01 00 00 00 # KMX_DWORD layerCount 01 00 00 00 # KMX_DWORD rowCount 02 00 00 00 # KMX_DWORD keyCount 00 00 00 00 # KMX_DWORD reserved0 @@ -368,10 +371,16 @@ block(list) # struct COMP_KMXPLUS_LAYR_LIST { block(listNull) index(indexNull,indexNull,2) # KMX_DWORD list index 00 00 00 00 # KMX_DWORD lists[0].count + block(listAe) + index(indexAe,indexNull,2) + 02 00 00 00 # KMX_DWORD count block(endLists) # indices block(indexNull) index(strNull,strNull,2) # KMXPLUS_STR string index + block(indexAe) + index(strNull,strElemTranFrom2,2) # KMXPLUS_STR a + index(strNull,strElemBkspFrom2,2) # KMXPLUS_STR e block(endIndices) block(loca) # struct COMP_KMXPLUS_LOCA { From 63fa53a028b2eabb051e560dbe14af02c49f1331 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Tue, 20 Dec 2022 17:05:29 -0600 Subject: [PATCH 26/33] =?UTF-8?q?feat(core):=20ldml:=20updates=20to=20basi?= =?UTF-8?q?c.txt=20and=20builders=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - layr now passes comparison For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- core/include/ldml/keyboardprocessor_ldml.h | 2 +- core/include/ldml/keyboardprocessor_ldml.ts | 2 +- developer/src/kmc-keyboard/test/fixtures/basic.txt | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/include/ldml/keyboardprocessor_ldml.h b/core/include/ldml/keyboardprocessor_ldml.h index 051470ee6c..ce319c8176 100644 --- a/core/include/ldml/keyboardprocessor_ldml.h +++ b/core/include/ldml/keyboardprocessor_ldml.h @@ -51,7 +51,7 @@ #define LDML_LENGTH_LAYR 0x20 #define LDML_LENGTH_LAYR_ENTRY 0x10 #define LDML_LENGTH_LAYR_KEY 0x4 -#define LDML_LENGTH_LAYR_LIST 0x10 +#define LDML_LENGTH_LAYR_LIST 0x14 #define LDML_LENGTH_LAYR_ROW 0x8 #define LDML_LENGTH_LIST 0x10 #define LDML_LENGTH_LIST_INDEX 0x4 diff --git a/core/include/ldml/keyboardprocessor_ldml.ts b/core/include/ldml/keyboardprocessor_ldml.ts index ecb2bf78c6..b0efbe19ac 100644 --- a/core/include/ldml/keyboardprocessor_ldml.ts +++ b/core/include/ldml/keyboardprocessor_ldml.ts @@ -281,7 +281,7 @@ class Constants { /** * Length of each layer list in the 'layr' section variable part */ - readonly length_layr_list = 16; + readonly length_layr_list = 20; /** * bitmask for the 'form' field of the layr.list[].flags bitfield */ diff --git a/developer/src/kmc-keyboard/test/fixtures/basic.txt b/developer/src/kmc-keyboard/test/fixtures/basic.txt index d88bdc0934..1d0d3c6261 100644 --- a/developer/src/kmc-keyboard/test/fixtures/basic.txt +++ b/developer/src/kmc-keyboard/test/fixtures/basic.txt @@ -286,11 +286,11 @@ block(key2) # struct COMP_KMXPLUS_KEY2 { 01 00 00 00 # KMX_DWORD (flags: extend) index(strNull,strHmaqtua,2) # KMXPLUS_STR 'hmaqtua' 00 00 00 00 # KMXPLUS_STR switch - 0A 00 00 00 # KMX_DWORD width*10 + 0A 00 00 00 # KMX_DWORD width*10 index(listNull,indexAe,4) # LIST longPress 'a e' 00 00 00 00 # STR longPressDefault index(listNull,listNull,4) # LIST multiTap - 00 00 00 00 # flicks 0 + 00 00 00 00 # flicks 0 # that 00 00 00 00 # KMX_DWORD vkey index(strNull,strKey2,2) # KMXPLUS_STR 'U+0127' @@ -339,25 +339,25 @@ block(layr) # struct COMP_KMXPLUS_LAYR { 6c 61 79 72 # KMX_DWORD header.ident; // 0000 Section name - layr sizeof(layr) # KMX_DWORD header.size; // 0004 Section length 01 00 00 00 # KMX_DWORD listCount - 01 00 00 00 # KMX_DWORD layerCount + 01 00 00 00 # KMX_DWORD layerCount 01 00 00 00 # KMX_DWORD rowCount 02 00 00 00 # KMX_DWORD keyCount 00 00 00 00 # KMX_DWORD reserved0 00 00 00 00 # KMX_DWORD reserved1 # list 0 - # flags + 00 00 00 00 # KMX_DWORD flags index(strNull,strUs,2) # KMXPLUS_STR hardware; 00 00 00 00 # KMX_DWORD layer; - 01 00 00 00 # count + 01 00 00 00 # count 7B 00 00 00 # KMX_DWORD minDeviceWidth; // 123 # layers 0 index(strNull,strBase,2) # KMXPLUS_STR id; 00 00 00 00 # KMXPLUS_STR mod str 00 00 00 00 # KMX_DWORD row index - 01 00 00 00 # KMX_DWORD count + 01 00 00 00 # KMX_DWORD count # rows 0 00 00 00 00 # KMX_DWORD key index - 02 00 00 00 # KMX_DWORD count + 02 00 00 00 # KMX_DWORD count # keys index(strNull,strHmaqtua,2) # KMXPLUS_STR locale; // 'hmaqtua' index(strNull,strThat,2) # KMXPLUS_STR locale; // 'that' From 343f234a8e658cc8acdcc3ba33355241f68cf8e5 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Tue, 20 Dec 2022 19:16:41 -0600 Subject: [PATCH 27/33] =?UTF-8?q?feat(core):=20ldml:=20updates=20to=20basi?= =?UTF-8?q?c.txt=20and=20builders=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - strs and ALL TESTS pass For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- .../src/kmx/kmx-plus-builder/build-list.ts | 4 +- common/web/types/src/kmx/kmx-plus.ts | 5 +- core/src/ldml/C7532_ldml_updating.md | 1 + .../src/kmc-keyboard/test/fixtures/basic.txt | 56 +++++++++++++------ 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-list.ts b/common/web/types/src/kmx/kmx-plus-builder/build-list.ts index 4a3c9f3fdc..56c2299cb5 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-list.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-list.ts @@ -14,7 +14,7 @@ interface BUILDER_LIST_LIST { }; interface BUILDER_LIST_INDEX { - index: number; // str + str: number; // str _value: string; // for findability? }; @@ -53,7 +53,7 @@ export function build_list(source_list: List, sect_strs: BUILDER_STRS): BUILDER_ array.forEach((i) => { let index : BUILDER_LIST_INDEX = { // Get the final string index - index: build_strs_index(sect_strs, i.value), + str: build_strs_index(sect_strs, i.value), _value: i.value.value, // unwrap the actual string value }; result.indices.push(index); // increment the indexCount diff --git a/common/web/types/src/kmx/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus.ts index 18f2134566..aba7cd10ba 100644 --- a/common/web/types/src/kmx/kmx-plus.ts +++ b/common/web/types/src/kmx/kmx-plus.ts @@ -295,7 +295,7 @@ export class List extends Section { * @param strs Strs section for allocation * @param s space-separated list of items * @returns a List object - */ + */ allocListFromSpaces(strs: Strs, s?: string): ListItem { if(s === undefined || s === null) { s = ''; @@ -309,7 +309,7 @@ export class List extends Section { * `['']` will be stored as an empty list `[]`. * @param strs Strs section for allocation * @param s string list to allocate - * @returns + * @returns */ allocList(strs: Strs, s?: string[]): ListItem { // Special case the 'null' list for [] or [''] @@ -597,7 +597,6 @@ export class KMXPlusFile extends KMXFile { indices: new r.Array(this.COMP_PLUS_LIST_INDEX, 'indexCount'), }); - // 'loca' this.COMP_PLUS_LOCA_ITEM = r.uint32le; //str diff --git a/core/src/ldml/C7532_ldml_updating.md b/core/src/ldml/C7532_ldml_updating.md index 7251b4e182..d5c75188b0 100644 --- a/core/src/ldml/C7532_ldml_updating.md +++ b/core/src/ldml/C7532_ldml_updating.md @@ -64,6 +64,7 @@ working on ‘layr’, using ‘disp’ as a model from https://github.com/keyma - Update `finalize_sect` and add `offset = this.finalize_sect_item(this.sect_disp, offset);` - Finally, add `this.emitSection(file, this.file.COMP_PLUS_DISP, this.sect_disp);` to `compile()` — and, in order. - Note that some variable length parts (such as the actual text data in `strs`) are sometimes in a separate emit function. Anything that's not in the `COMP_PLUS_STRS` `r.Struct` definition needs one of these. + - Also note that restructure will happily ignore (write zeros for) any fields where the BUILDER_* fields don't match the COMP_PLUS_* fields. - update basic.xml and basic.txt - Tweak `eveloper/src/kmc-keyboard/test/fixtures/basic.xml` as needed diff --git a/developer/src/kmc-keyboard/test/fixtures/basic.txt b/developer/src/kmc-keyboard/test/fixtures/basic.txt index 1d0d3c6261..d91ccba7cf 100644 --- a/developer/src/kmc-keyboard/test/fixtures/basic.txt +++ b/developer/src/kmc-keyboard/test/fixtures/basic.txt @@ -287,9 +287,9 @@ block(key2) # struct COMP_KMXPLUS_KEY2 { index(strNull,strHmaqtua,2) # KMXPLUS_STR 'hmaqtua' 00 00 00 00 # KMXPLUS_STR switch 0A 00 00 00 # KMX_DWORD width*10 - index(listNull,indexAe,4) # LIST longPress 'a e' + 01 00 00 00 # TODO: index(listNull,indexAe,4) # LIST longPress 'a e' 00 00 00 00 # STR longPressDefault - index(listNull,listNull,4) # LIST multiTap + 00 00 00 00 # TODO: index(listNull,listNull,4) # LIST multiTap 00 00 00 00 # flicks 0 # that 00 00 00 00 # KMX_DWORD vkey @@ -298,9 +298,9 @@ block(key2) # struct COMP_KMXPLUS_KEY2 { index(strNull,strThat,2) # KMXPLUS_STR 'hmaqtua' 00 00 00 00 # KMXPLUS_STR switch 0A 00 00 00 # KMX_DWORD width*10 - index(listNull,listNull,4) # LIST longPress + 00 00 00 00 # TODO: index(listNull,listNull,4) # LIST longPress 00 00 00 00 # STR longPressDefault - index(listNull,listNull,4) # LIST multiTap + 00 00 00 00 # TODO: index(listNull,listNull,4) # LIST multiTap 00 00 00 00 # flicks 0 # flicks # flicks 0 - null @@ -334,7 +334,6 @@ block(keys) # struct COMP_KMXPLUS_KEYS { # layr # ---------------------------------------------------------------------------------------------------- - block(layr) # struct COMP_KMXPLUS_LAYR { 6c 61 79 72 # KMX_DWORD header.ident; // 0000 Section name - layr sizeof(layr) # KMX_DWORD header.size; // 0004 Section length @@ -362,26 +361,39 @@ block(layr) # struct COMP_KMXPLUS_LAYR { index(strNull,strHmaqtua,2) # KMXPLUS_STR locale; // 'hmaqtua' index(strNull,strThat,2) # KMXPLUS_STR locale; // 'that' +# ---------------------------------------------------------------------------------------------------- +# list +# ---------------------------------------------------------------------------------------------------- + +# TODO-LDML: lots of comment-out ahead. Need to revisit. + block(list) # struct COMP_KMXPLUS_LAYR_LIST { 6c 69 73 74 # KMX_DWORD header.ident; // 0000 Section name - list - sizeof(list) # KMX_DWORD header.size; // 0004 Section length - 01 00 00 00 # KMX_DWORD listCount (should be 3) - 01 00 00 00 # KMX_DWORD indexCount (should be 3) - # lists, first the null list + diff(list,endList) # KMX_DWORD header.size; // 0004 Section length + 02 00 00 00 # KMX_DWORD listCount (should be 2) + 02 00 00 00 # KMX_DWORD indexCount (should be 2) + # list #0 the null list block(listNull) - index(indexNull,indexNull,2) # KMX_DWORD list index + 00 00 00 00 #index(indexNull,indexNull,2) # KMX_DWORD list index (0) 00 00 00 00 # KMX_DWORD lists[0].count + # list #1 the ae list block(listAe) - index(indexAe,indexNull,2) + 00 00 00 00 # index(indexAe,indexNull,2) # KMX_DWORD list index (also 0) 02 00 00 00 # KMX_DWORD count block(endLists) # indices - block(indexNull) - index(strNull,strNull,2) # KMXPLUS_STR string index + #block(indexNull) + # No null index + # index(strNull,strNull,2) # KMXPLUS_STR string index block(indexAe) index(strNull,strElemTranFrom2,2) # KMXPLUS_STR a index(strNull,strElemBkspFrom2,2) # KMXPLUS_STR e block(endIndices) + block(endList) + +# ---------------------------------------------------------------------------------------------------- +# loca +# ---------------------------------------------------------------------------------------------------- block(loca) # struct COMP_KMXPLUS_LOCA { 6c 6f 63 61 # KMX_DWORD header.ident; // 0000 Section name - loca @@ -390,6 +402,9 @@ block(loca) # struct COMP_KMXPLUS_LOCA { 00 00 00 00 # KMX_DWORD reserved; // 000C padding index(strNull,strLocale,2) # KMXPLUS_STR locale; // 0010+ locale string entry = 'mt' # }; +# ---------------------------------------------------------------------------------------------------- +# meta +# ---------------------------------------------------------------------------------------------------- block(meta) # struct COMP_KMXPLUS_META { 6d 65 74 61 # KMX_DWORD header.ident; // 0000 Section name - meta @@ -402,6 +417,9 @@ block(meta) # struct COMP_KMXPLUS_META { index(strNull,strVersion,2) # KMXPLUS_STR version; 00 00 00 00 # KMX_DWORD settings; # }; +# ---------------------------------------------------------------------------------------------------- +# store_targets_name +# ---------------------------------------------------------------------------------------------------- block(name) # struct COMP_KMXPLUS_META { 6e 61 6d 65 # KMX_DWORD header.ident; // 0000 Section name - name @@ -448,11 +466,13 @@ block(strs) # struct COMP_KMXPLUS_STRS { diff(strs,strElemTranFrom2) sizeof(strElemTranFrom2,2) diff(strs,strBase) sizeof(strBase,2) diff(strs,strElemBkspFrom2) sizeof(strElemBkspFrom2,2) + diff(strs,strHmaqtua) sizeof(strHmaqtua,2) diff(strs,strLocale) sizeof(strLocale,2) diff(strs,strLayout) sizeof(strLayout,2) diff(strs,strAuthor) sizeof(strAuthor,2) diff(strs,strConformsTo) sizeof(strConformsTo,2) diff(strs,strThat) sizeof(strThat,2) + diff(strs,strUs) sizeof(strUs,2) diff(strs,strTranTo) sizeof(strTranTo,2) diff(strs,strKey1) sizeof(strKey1,2) diff(strs,strKey2) sizeof(strKey2,2) @@ -479,18 +499,18 @@ block(strs) # struct COMP_KMXPLUS_STRS { block(strLayout) 71 00 77 00 65 00 72 00 74 00 79 00 block(x) 00 00 # 'qwerty' block(strAuthor) 73 00 72 00 6c 00 32 00 39 00 35 00 block(x) 00 00 # 'srl295' block(strConformsTo) 74 00 65 00 63 00 68 00 70 00 72 00 65 00 76 00 - 69 00 65 00 77 00 block(x) 00 00 # 'techpreview' + 69 00 65 00 77 00 block(x) 00 00 # 'techpreview' block(strThat) 74 00 68 00 61 00 74 00 block(x) 00 00 # 'that' - block(strUs) 75 73 block(x) 00 00 + block(strUs) 75 00 73 00 block(x) 00 00 block(strTranTo) E2 00 block(x) 00 00 # 'â' - block(strKey1) 27 01 block(x) 00 00 # 'ħ' - block(strKey2) 90 17 b6 17 block(x) 00 00 # 'ថា' + block(strKey1) 27 01 block(x) 00 00 # 'ħ' + block(strKey2) 90 17 b6 17 block(x) 00 00 # 'ថា' # block(strElemOrdrFrom3) 45 1a block(x) 00 00 # 'ᩅ' block(strElemOrdrFrom1) 60 1a block(x) 00 00 # '᩠' block(strElemOrdrBefore) 6b 1a block(x) 00 00 # 'ᩫ' block(strElemOrdrFrom2) 75 1a block(x) 00 00 # '᩵' - block(strIndicator) 3d d8 40 de block(x) 00 00 # '🙀' + block(strIndicator) 3d d8 40 de block(x) 00 00 # '🙀' From 513f4581da62370c4049d77d3813584f11a9a79e Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Tue, 20 Dec 2022 21:37:41 -0600 Subject: [PATCH 28/33] =?UTF-8?q?feat(core):=20ldml:=20updates=20to=20C++?= =?UTF-8?q?=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fix c++ to match spec For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- core/src/kmx/kmx_plus.h | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/kmx/kmx_plus.h b/core/src/kmx/kmx_plus.h index 9a2450a10b..b15fad77f1 100644 --- a/core/src/kmx/kmx_plus.h +++ b/core/src/kmx/kmx_plus.h @@ -382,6 +382,7 @@ struct COMP_KMXPLUS_LAYR_LIST { KMXPLUS_STR hardware; KMX_DWORD layer; KMX_DWORD count; + KMX_DWORD minDeviceWidth; }; static_assert(sizeof(struct COMP_KMXPLUS_LAYR_LIST) == LDML_LENGTH_LAYR_LIST, "mismatched size of COMP_KMXPLUS_LAYR_LIST"); From 05656ac49952f8fc4bcfe62409d5e7e3009712c5 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Thu, 22 Dec 2022 13:12:08 -0600 Subject: [PATCH 29/33] =?UTF-8?q?feat(core):=20ldml:=20fix=20off-by-one=20?= =?UTF-8?q?on=20section=20count=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - yes, sect was counting itself. Too much automation! - disable layr/key2/list loading in C++ for now For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- .../web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts | 2 +- core/src/kmx/kmx_plus.cpp | 6 +++--- developer/src/kmc-keyboard/test/fixtures/basic.txt | 4 ---- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts b/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts index ee88c5f904..275db8a904 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts @@ -119,7 +119,7 @@ export default class KMXPlusBuilder { Object.keys(constants.section).forEach((sectstr : string) => { const sect : SectionIdent = constants.section[sectstr]; - if(this.sect[sect]) { + if(this.sect[sect] && sect !== 'sect') { this.sect.sect.count++; } }); diff --git a/core/src/kmx/kmx_plus.cpp b/core/src/kmx/kmx_plus.cpp index 7e810f542f..026b852271 100644 --- a/core/src/kmx/kmx_plus.cpp +++ b/core/src/kmx/kmx_plus.cpp @@ -532,10 +532,10 @@ kmx_plus::kmx_plus(const COMP_KEYBOARD *keyboard, size_t length) // these will be nullptr if they don't validate disp = section_from_sect(sect); elem = section_from_sect(sect); - key2 = section_from_sect(sect); + // TODO-LDML: // key2 = section_from_sect(sect); keys = section_from_sect(sect); - layr = section_from_sect(sect); - list = section_from_sect(sect); + // TODO-LDML: // layr = section_from_sect(sect); + // TODO-LDML: // list = section_from_sect(sect); loca = section_from_sect(sect); meta = section_from_sect(sect); strs = section_from_sect(sect); diff --git a/developer/src/kmc-keyboard/test/fixtures/basic.txt b/developer/src/kmc-keyboard/test/fixtures/basic.txt index d91ccba7cf..8b848136ec 100644 --- a/developer/src/kmc-keyboard/test/fixtures/basic.txt +++ b/developer/src/kmc-keyboard/test/fixtures/basic.txt @@ -140,10 +140,6 @@ block(sectitems) 76 6b 65 79 diff(sect,vkey) -# ???? TODO -00 00 00 00 -00 00 00 00 - block(endsect) # ---------------------------------------------------------------------------------------------------- From efef796d75072a07fc7f910cc300b916b6b8c88d Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Thu, 22 Dec 2022 13:43:53 -0600 Subject: [PATCH 30/33] =?UTF-8?q?feat(core):=20ldml:=20=20fix=20C++=20layr?= =?UTF-8?q?=20helper=20=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - reenable layr/key2/list in c++ - minor update to layr helper, missed ptr assignment For feat(developer): ldml: include all LDML data in kmx+ 🙀 #7532 --- core/src/kmx/kmx_plus.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/kmx/kmx_plus.cpp b/core/src/kmx/kmx_plus.cpp index 026b852271..26d07bbb47 100644 --- a/core/src/kmx/kmx_plus.cpp +++ b/core/src/kmx/kmx_plus.cpp @@ -378,6 +378,7 @@ COMP_KMXPLUS_LAYR_Helper::setLayr(const COMP_KMXPLUS_LAYR *newLayr) { is_valid = false; return false; } + layr = newLayr; const uint8_t *rawdata = reinterpret_cast(this); rawdata += LDML_LENGTH_LAYR; // skip past non-dynamic portion // lists @@ -532,10 +533,10 @@ kmx_plus::kmx_plus(const COMP_KEYBOARD *keyboard, size_t length) // these will be nullptr if they don't validate disp = section_from_sect(sect); elem = section_from_sect(sect); - // TODO-LDML: // key2 = section_from_sect(sect); + key2 = section_from_sect(sect); keys = section_from_sect(sect); - // TODO-LDML: // layr = section_from_sect(sect); - // TODO-LDML: // list = section_from_sect(sect); + layr = section_from_sect(sect); + list = section_from_sect(sect); loca = section_from_sect(sect); meta = section_from_sect(sect); strs = section_from_sect(sect); From 564f98cc30ee23b0ae3f79869a383374f1a8aae7 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Fri, 6 Jan 2023 11:46:17 -0600 Subject: [PATCH 31/33] Apply suggestions from code review Co-authored-by: Marc Durdin --- .../web/types/src/kmx/kmx-plus-builder/build-key2.ts | 4 ++-- .../web/types/src/kmx/kmx-plus-builder/build-layr.ts | 4 ++-- common/web/types/src/kmx/kmx-plus.ts | 4 +--- common/web/types/src/kmx/string-list.ts | 9 +++++---- core/include/ldml/keyboardprocessor_ldml.ts | 4 ++-- core/src/kmx/kmx_plus.cpp | 1 - core/src/ldml/C7043_ldml.md | 2 +- developer/src/kmc-keyboard/src/compiler/key2.ts | 10 +++++----- developer/src/kmc-keyboard/src/compiler/layr.ts | 2 +- developer/src/kmc-keyboard/test/test-key2.ts | 3 +-- developer/src/kmc-keyboard/test/test-layr.ts | 1 - 11 files changed, 20 insertions(+), 24 deletions(-) diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts b/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts index ec071a9de4..94577c21fc 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts @@ -53,7 +53,7 @@ export interface BUILDER_KEY2 extends BUILDER_SECTION { }; export function build_key2(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_list: BUILDER_LIST): BUILDER_KEY2 { - if(!kmxplus.key2.keys.length && + if(kmxplus.key2.keys.length == 0 && (kmxplus.key2.flicks.length <= 1)) { // if no keys and only the 'null' flick. return null; } @@ -76,7 +76,7 @@ export function build_key2(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_l // flicks first // sort the input, to simplify bookkeeping later kmxplus.key2.flicks.sort((a, b) => a.compareTo(b)); - // we always need a flicks=0 to mena 'no flicks' + // we always need a flicks=0 to mean 'no flicks' key2.flicks = kmxplus.key2.flicks.map((flicks) => { let result : BUILDER_KEY2_FLICKS = { count: flicks.flicks.length, diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts index 7705903840..9594a7b8b6 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts @@ -1,8 +1,8 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; -import { /*KeyFlags,*/ KMXPlusData } from "../kmx-plus.js"; +import { KMXPlusData } from "../kmx-plus.js"; import { build_strs_index, BUILDER_STRS } from "./build-strs.js"; -import { /*build_list_index*/ BUILDER_LIST } from "./build-list.js"; +import { BUILDER_LIST } from "./build-list.js"; import { BUILDER_SECTION } from "./builder-section.js"; /* ------------------------------------------------------------------ diff --git a/common/web/types/src/kmx/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus.ts index aba7cd10ba..aae235d2d6 100644 --- a/common/web/types/src/kmx/kmx-plus.ts +++ b/common/web/types/src/kmx/kmx-plus.ts @@ -297,9 +297,7 @@ export class List extends Section { * @returns a List object */ allocListFromSpaces(strs: Strs, s?: string): ListItem { - if(s === undefined || s === null) { - s = ''; - } + s = s ?? ''; // TODO-LDML: support unicode escaping etc return this.allocList(strs, s.split(' ')); } diff --git a/common/web/types/src/kmx/string-list.ts b/common/web/types/src/kmx/string-list.ts index d70204ff54..436c761582 100644 --- a/common/web/types/src/kmx/string-list.ts +++ b/common/web/types/src/kmx/string-list.ts @@ -1,8 +1,10 @@ -// import { constants } from '@keymanapp/ldml-keyboard-constants'; import { Strs, StrsItem } from './kmx-plus.js'; export class ListIndex { - value: StrsItem; // will become index into Strs table + readonly value: StrsItem; // will become index into Strs table + constructor(value: StrsItem) { + this.value = value; + } isEqual(a: ListIndex | string) { // so we can compare this to a string return a.toString() === this.toString(); @@ -20,8 +22,7 @@ export class ListItem extends Array { } for (const str of source) { - let index = new ListIndex(); - index.value = strs.allocString(str); + let index = new ListIndex(strs.allocString(str)); this.push(index); } } diff --git a/core/include/ldml/keyboardprocessor_ldml.ts b/core/include/ldml/keyboardprocessor_ldml.ts index b0efbe19ac..1d3513860a 100644 --- a/core/include/ldml/keyboardprocessor_ldml.ts +++ b/core/include/ldml/keyboardprocessor_ldml.ts @@ -456,8 +456,8 @@ class Constants { * @returns hex ID such as 0x74636573 */ hex_section_id(id:string) { - if(!id || typeof id !== 'string' || !id.match(/[a-z0-9][a-z0-9][a-z0-9][a-z0-9]/)) { - throw Error(`hex_section_id(${id}) - need a 4-character string`); + if(!id || typeof id !== 'string' || !id.match(/^[a-z0-9]{4}$/)) { + throw Error(`hex_section_id(${id}) - need a 4-character alphanumeric lower-case string`); } let r = 0; for (let i = 3; i>=0; i--) { diff --git a/core/src/kmx/kmx_plus.cpp b/core/src/kmx/kmx_plus.cpp index 26d07bbb47..6a39ecadb3 100644 --- a/core/src/kmx/kmx_plus.cpp +++ b/core/src/kmx/kmx_plus.cpp @@ -412,7 +412,6 @@ COMP_KMXPLUS_LAYR_Helper::setLayr(const COMP_KMXPLUS_LAYR *newLayr) { keys = nullptr; is_valid = false; } - // rawdata += sizeof(COMP_KMXPLUS_LAYR_KEY) * layr->keyCount; // Now, validate offsets by walking if (is_valid) { diff --git a/core/src/ldml/C7043_ldml.md b/core/src/ldml/C7043_ldml.md index 0de36adc42..3bfc9d62b9 100644 --- a/core/src/ldml/C7043_ldml.md +++ b/core/src/ldml/C7043_ldml.md @@ -487,7 +487,7 @@ For each flick element: |---|------|---------------- |----------------------------------------------------------| | 0+| 32 | directions | list: index into `list` section with direction list | | 8+| 32 | flags | int: per-key flags | -|12+| 32 | to | str: output string | +|12+| 32 | to | str: output string, or ucs32: output char, see flags | If this section is present, it must have a 'flick element' at position zero with directions=0, flags=0, and to=0 meaning 'no flick'. diff --git a/developer/src/kmc-keyboard/src/compiler/key2.ts b/developer/src/kmc-keyboard/src/compiler/key2.ts index 21214a55c9..f1f9680428 100644 --- a/developer/src/kmc-keyboard/src/compiler/key2.ts +++ b/developer/src/kmc-keyboard/src/compiler/key2.ts @@ -1,6 +1,5 @@ import { constants } from '@keymanapp/ldml-keyboard-constants'; -import { /*LDMLKeyboard,*/ KMXPlus, /*Constants*/ } from '@keymanapp/common-types'; -// import { CompilerMessages } from './messages.js'; +import { KMXPlus } from '@keymanapp/common-types'; import { SectionCompiler } from "./section-compiler.js"; import GlobalSections = KMXPlus.GlobalSections; @@ -8,7 +7,6 @@ import Key2 = KMXPlus.Key2; import ListItem = KMXPlus.ListItem; import Key2Flicks = KMXPlus.Key2Flicks; -// import USVirtualKeyMap = Constants.USVirtualKeyMap; export class Key2Compiler extends SectionCompiler { @@ -18,6 +16,7 @@ export class Key2Compiler extends SectionCompiler { public validate() { let valid = true; + // TODO-LDML: some validation needed here? return valid; } @@ -45,6 +44,7 @@ export class Key2Compiler extends SectionCompiler { for (let lkflick of lkflicks.flick) { let flags = 0; + // TODO-LDML: single char const to = sections.strs.allocString(lkflick.to); flags |= constants.key2_flick_flags_extend; let directions : ListItem = sections.list.allocListFromSpaces(sections.strs, lkflick.directions); @@ -71,9 +71,9 @@ export class Key2Compiler extends SectionCompiler { flags |= constants.key2_key_flags_notransform; } const id = sections.strs.allocString(key.id); - const longPress : ListItem = sections.list.allocListFromSpaces(sections.strs, key.longPress); + const longPress: ListItem = sections.list.allocListFromSpaces(sections.strs, key.longPress); const longPressDefault = sections.strs.allocString(key.longPressDefault); - const multiTap : ListItem = sections.list.allocListFromSpaces(sections.strs, key.multiTap); + const multiTap: ListItem = sections.list.allocListFromSpaces(sections.strs, key.multiTap); const keySwitch = sections.strs.allocString(key.switch); // 'switch' is a reserved word flags |= constants.key2_key_flags_extend; const to = sections.strs.allocString(key.to); // TODO-LDML: single char diff --git a/developer/src/kmc-keyboard/src/compiler/layr.ts b/developer/src/kmc-keyboard/src/compiler/layr.ts index 2aa94f09fe..b143a86c3c 100644 --- a/developer/src/kmc-keyboard/src/compiler/layr.ts +++ b/developer/src/kmc-keyboard/src/compiler/layr.ts @@ -1,5 +1,5 @@ import { constants } from '@keymanapp/ldml-keyboard-constants'; -import { /*LDMLKeyboard,*/ KMXPlus,/* Constants*/ } from '@keymanapp/common-types'; +import { KMXPlus } from '@keymanapp/common-types'; import { CompilerMessages } from './messages.js'; import { SectionCompiler } from "./section-compiler.js"; diff --git a/developer/src/kmc-keyboard/test/test-key2.ts b/developer/src/kmc-keyboard/test/test-key2.ts index d99b3eb382..a222d670c6 100644 --- a/developer/src/kmc-keyboard/test/test-key2.ts +++ b/developer/src/kmc-keyboard/test/test-key2.ts @@ -3,7 +3,6 @@ import { assert } from 'chai'; import { Key2Compiler } from '../src/compiler/key2.js'; import { compilerTestCallbacks, loadSectionFixture } from './helpers/index.js'; import { KMXPlus } from '@keymanapp/common-types'; -// import { CompilerMessages } from '../src/compiler/messages.js'; import { constants } from '@keymanapp/ldml-keyboard-constants'; import Key2 = KMXPlus.Key2; @@ -30,7 +29,7 @@ describe('key2', function () { const [q] = key2.keys.filter(({ id }) => id.value === 'q'); assert.ok(q); assert.isFalse(!!(q.flags & constants.key2_key_flags_gap)); - assert.equal(q.width, 32); // ceil(3.1 * 10) + assert.equal(q.width, 32); // ceil(3.14159 * 10.0) assert.equal(q.flicks, 'flick0'); // note this is a string, not a StrsItem const [flick0] = key2.flicks.filter(({ id }) => id.value === 'flick0'); diff --git a/developer/src/kmc-keyboard/test/test-layr.ts b/developer/src/kmc-keyboard/test/test-layr.ts index 921abd4ba0..2828fdd1b6 100644 --- a/developer/src/kmc-keyboard/test/test-layr.ts +++ b/developer/src/kmc-keyboard/test/test-layr.ts @@ -3,7 +3,6 @@ import { assert } from 'chai'; import { LayrCompiler } from '../src/compiler/layr.js'; import { compilerTestCallbacks, loadSectionFixture } from './helpers/index.js'; import { KMXPlus } from '@keymanapp/common-types'; -// import { CompilerMessages } from '../src/compiler/messages.js'; import { constants } from '@keymanapp/ldml-keyboard-constants'; import Layr = KMXPlus.Layr; From 3d154540e735e4eae85b5c3dbe6b87e341eddac1 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Fri, 6 Jan 2023 15:42:05 -0600 Subject: [PATCH 32/33] =?UTF-8?q?fix(common):=20ldml:=20updates=20per=20co?= =?UTF-8?q?de=20review=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - build-key2.ts: comments, and treat the kmxplus data as const - build-layr.ts: comments, reordering of structs, and treat the kmxplus data as const - build-list.ts: comments, reordering - string-list.ts: comments - kmx-plus.ts: comments - ldml-keyboard-xml.ts: update definition of LKKey.transform - For #7532 --- .../src/kmx/kmx-plus-builder/build-key2.ts | 74 ++++--- .../src/kmx/kmx-plus-builder/build-layr.ts | 195 ++++++++++-------- .../src/kmx/kmx-plus-builder/build-list.ts | 23 ++- common/web/types/src/kmx/kmx-plus.ts | 4 + common/web/types/src/kmx/string-list.ts | 15 ++ .../src/ldml-keyboard/ldml-keyboard-xml.ts | 5 +- 6 files changed, 189 insertions(+), 127 deletions(-) diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts b/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts index 94577c21fc..edff5520b0 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-key2.ts @@ -1,6 +1,6 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; -import { KMXPlusData } from "../kmx-plus.js"; +import { Key2Flick, KMXPlusData, StrsItem } from "../kmx-plus.js"; import { build_strs_index, BUILDER_STRS } from "./build-strs.js"; import { build_list_index, BUILDER_LIST } from "./build-list.js"; import { BUILDER_SECTION } from "./builder-section.js"; @@ -9,30 +9,41 @@ import { BUILDER_SECTION } from "./builder-section.js"; * key2 section ------------------------------------------------------------------ */ +/** + * This struct is a single in the key2 keybag + */ interface BUILDER_KEY2_KEY { - vkey: number; - to: number; // str + vkey: number; // Scan code for the key + to: number; // str or single codepoint flags: number; - id: number; // str - switch: number; // str - width: number; // width*10 - longPress: number; // list - longPressDefault: number; // str - multiTap: number; // list - flicks: number; // index into flicks[] -}; - -interface BUILDER_KEY2_FLICK { - directions: number; // list - flags: number; - to: number; // str + id: number; // str with original key id + _id: string; // original key id, for sorting + switch: number; // str with layer of new l + width: number; // ceil((width||1)*10), so 12 for width 1.2 + longPress: number; // list of longPress sequences + longPressDefault: number; // str with the default longPress target + multiTap: number; // list of multiTap sequences + flicks: number; // index into the flicks[] subtable for this flick list }; +/** + * This is a , a list of elements. + */ interface BUILDER_KEY2_FLICKS { - count: number; - flick: number; // index into flick[] - id: number; //str - _id: string; + count: number; // number of BUILDER_KEY2_FLICK entries in this flick list + flick: number; // index into the flick[] subtable of the first flick in the list + id: number; // str with the original id of this flicks + _id: string; // copy of the flicks id, used for sorting during build + _flicks: Key2Flick[]; // temporary copy of Key2Flick object +}; + +/** + * This is a single element. + */ +interface BUILDER_KEY2_FLICK { + directions: number; // list of cardinal/intercardinal directions + flags: number; // + to: number; // str or single codepoint }; /** @@ -73,18 +84,24 @@ export function build_key2(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_l _offset: 0, }; - // flicks first - // sort the input, to simplify bookkeeping later - kmxplus.key2.flicks.sort((a, b) => a.compareTo(b)); - // we always need a flicks=0 to mean 'no flicks' + // flicks first: the keys will need to index into the flicks table. + + // Note that per the Key2 class and spec, there is always a flicks=0 meaning 'no flicks' key2.flicks = kmxplus.key2.flicks.map((flicks) => { let result : BUILDER_KEY2_FLICKS = { count: flicks.flicks.length, flick: key2.flick.length, // index of first flick id: build_strs_index(sect_strs, flicks.id), _id: flicks.id.value, + _flicks: flicks.flicks, }; - flicks.flicks.forEach((flick) => { + return result; + }); + // Sort the flicks array by id + key2.flicks.sort((a, b) => StrsItem.binaryStringCompare(a._id, b._id)); + // now, allocate 'flick' entries for each 'flicks' + key2.flicks.forEach((flicks) => { + flicks._flicks.forEach((flick) => { key2.flick.push({ directions: build_list_index(sect_list, flick.directions), flags: flick.flags, @@ -92,17 +109,16 @@ export function build_key2(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_l }); key2.flickCount++; }); - return result; }); - // now keys - kmxplus.key2.keys.sort((a, b) => a.id.compareTo(b.id)); + // now, keys key2.keys = kmxplus.key2.keys.map((key) => { let result : BUILDER_KEY2_KEY = { vkey: key.vkey, to: build_strs_index(sect_strs, key.to), flags: key.flags, id: build_strs_index(sect_strs, key.id), + _id: key.id.value, switch: build_strs_index(sect_strs, key.switch), width: key.width, longPress: build_list_index(sect_list, key.longPress), @@ -116,6 +132,8 @@ export function build_key2(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_l } return result; }); + // sort the keys by id + key2.keys.sort((a, b) => StrsItem.binaryStringCompare(a._id, b._id)); let offset = constants.length_key2 + (constants.length_key2_key * key2.keyCount) + diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts index 9594a7b8b6..fe35c5d2ef 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts @@ -1,132 +1,157 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; -import { KMXPlusData } from "../kmx-plus.js"; +import { KMXPlusData, LayrEntry, LayrRow, StrsItem } from "../kmx-plus.js"; import { build_strs_index, BUILDER_STRS } from "./build-strs.js"; import { BUILDER_LIST } from "./build-list.js"; import { BUILDER_SECTION } from "./builder-section.js"; /* ------------------------------------------------------------------ - * layr section - ------------------------------------------------------------------ */ - + * layr section - + ------------------------------------------------------------------ */ +/** + * List of layers, the element + */ interface BUILDER_LAYR_LIST { - flags: number; - hardware: number; // str - layer: number; // index - count: number; - minDeviceWidth: number; + flags: number; + hardware: number; // str - hardware name, see #7986 + layer: number; // index of first layer in the list, in the + count: number; // number of layer entries in the list + minDeviceWidth: number; // width in millimeters + _layers: LayrEntry[]; // original layer entry, for in-memory only }; +/** + * element + */ interface BUILDER_LAYR_LAYER { - id: number; // str - modifier: number; // str - row: number; // row index - count: number; + id: number; // str of layer id + _id: string; // original layer id, for sorting + modifier: number; // str of modifier string + row: number; // row index into row subtable + _rows: LayrRow[]; // original rows, for in-memory only + count: number; // number of row entries }; +/** + * element + */ interface BUILDER_LAYR_ROW { - key: number; - count: number; + key: number; // index into key subtable + count: number; // number of keys }; +/** + * portion of keys attribute of + */ interface BUILDER_LAYR_KEY { - key: number; + key: number; }; /** * Builder for the 'keys' section */ export interface BUILDER_LAYR extends BUILDER_SECTION { - listCount: number, - layerCount: number, - rowCount: number, - keyCount: number, - reserved0: number, - reserved1: number, - lists: BUILDER_LAYR_LIST[], - layers: BUILDER_LAYR_LAYER[], - rows: BUILDER_LAYR_ROW[], - keys: BUILDER_LAYR_KEY[], + listCount: number, // number of entries in lists subtable + layerCount: number, // number of entries in layers subtable + rowCount: number, // number of entries in rows subtable + keyCount: number, // number of entries in keys subtable + reserved0: number, // padding + reserved1: number, // padding + lists: BUILDER_LAYR_LIST[], // subtable of elements + layers: BUILDER_LAYR_LAYER[], // subtable of elements + rows: BUILDER_LAYR_ROW[], // subtable of elements + keys: BUILDER_LAYR_KEY[], // subtable of key entries }; - export function build_layr(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_list: BUILDER_LIST): BUILDER_LAYR { if (!kmxplus.layr?.lists) { - return null; + return null; // if there aren't any layers at all (which should be an invalid keyboard) } let layr: BUILDER_LAYR = { - ident: constants.hex_section_id(constants.section.layr), - size: constants.length_layr, - _offset: 0, - listCount: kmxplus.layr.lists.length, - layerCount: 0, - rowCount: 0, - keyCount: 0, - reserved0: 0, - reserved1: 0, - lists: [], - layers: [], - rows: [], - keys: [] + ident: constants.hex_section_id(constants.section.layr), + size: constants.length_layr, + _offset: 0, + listCount: kmxplus.layr.lists.length, + layerCount: 0, // calculated below + rowCount: 0, // calculated below + keyCount: 0, // calculated below + reserved0: 0, + reserved1: 0, + lists: [], + layers: [], + rows: [], + keys: [] }; - // pre-sort layers - kmxplus.layr.lists.sort((a, b) => { + layr.lists = kmxplus.layr.lists.map((list) => { + const blist: BUILDER_LAYR_LIST = { + flags: list.flags, + hardware: build_strs_index(sect_strs, list.hardware), + layer: null, // to be set below + _layers: list.layers, + count: list.layers.length, + minDeviceWidth: list.minDeviceWidth, + }; + return blist; + }); + // now sort the lists + layr.lists.sort((a, b) => { const aform = a.flags & constants.layr_list_flags_mask_form; const bform = b.flags & constants.layr_list_flags_mask_form; if (aform < bform) { - return -1; - } else if(aform > bform) { - return 1; + return -1; + } else if (aform > bform) { + return 1; } if (a.minDeviceWidth < b.minDeviceWidth) { - return -1; - } else if(a.minDeviceWidth > b.minDeviceWidth) { - return 1; + return -1; + } else if (a.minDeviceWidth > b.minDeviceWidth) { + return 1; } else { - return 0; // same + return 0; // same } }); - - layr.lists = kmxplus.layr.lists.map((list) => { - const blist : BUILDER_LAYR_LIST = { - flags: list.flags, - hardware: build_strs_index(sect_strs, list.hardware), - layer: layr.layers.length, - count: list.layers.length, - minDeviceWidth: list.minDeviceWidth, - }; - list.layers.forEach((layer) => { - const blayer : BUILDER_LAYR_LAYER = { - id: build_strs_index(sect_strs, layer.id), - modifier: build_strs_index(sect_strs, layer.modifier), - row: layr.rows.length, - count: layer.rows.length, - }; - layer.rows.forEach((row) => { - const brow : BUILDER_LAYR_ROW = { - key: layr.keys.length, - count: row.keys.length, - }; - row.keys.forEach((key) => { - const bkey : BUILDER_LAYR_KEY = { - key: build_strs_index(sect_strs, key), - }; - layr.keys.push(bkey); - layr.keyCount++; - }); - layr.rows.push(brow); - layr.rowCount++; - }); - layr.layers.push(blayer); - layr.layerCount++; + // Now allocate the layers, rows, and keys + layr.lists.forEach((list) => { + list.layer = layr.layers.length; // index to first layer in list + const blayers = list._layers.map((layer) => { + const blayer: BUILDER_LAYR_LAYER = { + _id: layer.id.value, // original id + id: build_strs_index(sect_strs, layer.id), + modifier: build_strs_index(sect_strs, layer.modifier), + row: null, // row ID, to be filled in + _rows: layer.rows, // temporary + count: layer.rows.length, // number of rows + }; + return blayer; + }); + // sort the new layers + blayers.sort((a, b) => StrsItem.binaryStringCompare(a._id, b._id)); + blayers.forEach((layer) => { + layer.row = layr.rows.length; // index to first row in list + layer._rows.forEach((row) => { + const brow: BUILDER_LAYR_ROW = { + key: layr.keys.length, + count: row.keys.length, + }; + row.keys.forEach((key) => { + const bkey: BUILDER_LAYR_KEY = { + key: build_strs_index(sect_strs, key), + }; + layr.keys.push(bkey); + }); + layr.rows.push(brow); + }); + layr.layers.push(layer); }); - - return blist; }); + layr.layerCount = layr.layers.length; + layr.rowCount = layr.rows.length; + layr.keyCount = layr.keys.length; + let offset = constants.length_layr + (constants.length_layr_list * layr.listCount) + (constants.length_layr_entry * layr.layerCount) + diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-list.ts b/common/web/types/src/kmx/kmx-plus-builder/build-list.ts index 56c2299cb5..a1ed950463 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-list.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-list.ts @@ -7,23 +7,26 @@ import { BUILDER_SECTION } from "./builder-section.js"; * list section ------------------------------------------------------------------ */ +/** + * A list entry. + */ interface BUILDER_LIST_LIST { - index: number; // index into indices - count: number; // number of strings - _value: ListItem; // for findability + index: number; // index into indices[] subtable + count: number; // number of strings in this list + _value: ListItem; // for locating the list during finalization }; interface BUILDER_LIST_INDEX { - str: number; // str - _value: string; // for findability? + str: number; // str for this string + _value: string; // for locating this string during finalization }; /** * Builder for the 'list' section */ export interface BUILDER_LIST extends BUILDER_SECTION { - listCount: number; - indexCount: number; + listCount: number; // Number of lists total in the subtable + indexCount: number; // Total number of indices in the subtable lists: BUILDER_LIST_LIST[]; indices: BUILDER_LIST_INDEX[]; }; @@ -35,13 +38,13 @@ export function build_list(source_list: List, sect_strs: BUILDER_STRS): BUILDER_ } let result: BUILDER_LIST = { + ident: constants.hex_section_id(constants.section.list), + size: 0, + _offset: 0, listCount: source_list.lists.length, indexCount: 0, lists: [], indices: [], - ident: constants.hex_section_id(constants.section.list), - size: 0, - _offset: 0 }; result.lists = source_list.lists.map(array => { diff --git a/common/web/types/src/kmx/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus.ts index aae235d2d6..1bb205333a 100644 --- a/common/web/types/src/kmx/kmx-plus.ts +++ b/common/web/types/src/kmx/kmx-plus.ts @@ -111,6 +111,10 @@ export class Ordr extends Section { // 'strs' +/** + * A string item in memory. This will be replaced with an index + * into the string table at finalization. + */ export class StrsItem { readonly value: string; constructor(value: string) { diff --git a/common/web/types/src/kmx/string-list.ts b/common/web/types/src/kmx/string-list.ts index 436c761582..9805073ff9 100644 --- a/common/web/types/src/kmx/string-list.ts +++ b/common/web/types/src/kmx/string-list.ts @@ -1,5 +1,9 @@ import { Strs, StrsItem } from './kmx-plus.js'; +/** + * A single entry in a ListItem. + * Contains a StrsItem as its value. + */ export class ListIndex { readonly value: StrsItem; // will become index into Strs table constructor(value: StrsItem) { @@ -14,7 +18,18 @@ export class ListIndex { } }; +/** + * A string list in memory. This will be replaced with an index + * into the string table at finalization. + */ export class ListItem extends Array { + /** + * Construct a new list from an array of strings. + * Use List. This is meant to be called by the List.allocString*() functions. + * @param strs the Strs section is needed to construct this object. + * @param source array of strings + * @returns + */ constructor(strs: Strs, source: Array) { super(); if(!source) { diff --git a/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts b/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts index 0261e2671f..9f83ad17d5 100644 --- a/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts +++ b/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts @@ -78,10 +78,7 @@ export interface LKKey { longPress?: string; longPressDefault?: string; multiTap?: string; - /** - * "no" or falsy - */ - transform?: string; + transform?: "no"; width?: number; }; From 9f8b0352a26f774cbbec528c1495dac2478b5ae8 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Fri, 6 Jan 2023 15:51:56 -0600 Subject: [PATCH 33/33] =?UTF-8?q?fix(developer):=20ldml:=20updates=20per?= =?UTF-8?q?=20code=20review=20=F0=9F=99=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - …/key2/flicks.xml deleted empty test file - test-key2.ts: reinstated an assert For #7532 --- .../src/kmc-keyboard/test/fixtures/sections/key2/flicks.xml | 0 developer/src/kmc-keyboard/test/test-key2.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 developer/src/kmc-keyboard/test/fixtures/sections/key2/flicks.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/key2/flicks.xml b/developer/src/kmc-keyboard/test/fixtures/sections/key2/flicks.xml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/developer/src/kmc-keyboard/test/test-key2.ts b/developer/src/kmc-keyboard/test/test-key2.ts index a222d670c6..4ea12e9075 100644 --- a/developer/src/kmc-keyboard/test/test-key2.ts +++ b/developer/src/kmc-keyboard/test/test-key2.ts @@ -13,7 +13,7 @@ describe('key2', function () { it('should compile minimal keys data', function () { let key2 = loadSectionFixture(Key2Compiler, 'sections/keys/minimal.xml', compilerTestCallbacks) as Key2; assert.ok(key2); - // assert.equal(compilerTestCallbacks.messages.length, 0); + assert.equal(compilerTestCallbacks.messages.length, 0); assert.equal(key2.keys.length, 1); assert.equal(key2.flicks.length, 1); // there's always a 'null' flick assert.equal(key2.keys[0].to.value, '🪦');