@keymanapp/kmc-ldml 18.0.16-alpha → 18.0.18-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/src/compiler/compiler.js +309 -308
- package/build/src/compiler/compiler.js.map +1 -1
- package/build/src/compiler/disp.js +84 -83
- package/build/src/compiler/disp.js.map +1 -1
- package/build/src/compiler/empty-compiler.js +116 -115
- package/build/src/compiler/empty-compiler.js.map +1 -1
- package/build/src/compiler/keymanweb-compiler.js +97 -96
- package/build/src/compiler/keymanweb-compiler.js.map +1 -1
- package/build/src/compiler/keys.d.ts.map +1 -1
- package/build/src/compiler/keys.js +419 -415
- package/build/src/compiler/keys.js.map +1 -1
- package/build/src/compiler/layr.d.ts.map +1 -1
- package/build/src/compiler/layr.js +83 -76
- package/build/src/compiler/layr.js.map +1 -1
- package/build/src/compiler/ldml-compiler-options.js +5 -4
- package/build/src/compiler/ldml-compiler-options.js.map +1 -1
- package/build/src/compiler/loca.js +61 -60
- package/build/src/compiler/loca.js.map +1 -1
- package/build/src/compiler/messages.d.ts +11 -3
- package/build/src/compiler/messages.d.ts.map +1 -1
- package/build/src/compiler/messages.js +120 -109
- package/build/src/compiler/messages.js.map +1 -1
- package/build/src/compiler/meta.js +57 -56
- package/build/src/compiler/meta.js.map +1 -1
- package/build/src/compiler/metadata-compiler.js +49 -48
- package/build/src/compiler/metadata-compiler.js.map +1 -1
- package/build/src/compiler/section-compiler.js +42 -41
- package/build/src/compiler/section-compiler.js.map +1 -1
- package/build/src/compiler/substitution-tracker.js +105 -104
- package/build/src/compiler/substitution-tracker.js.map +1 -1
- package/build/src/compiler/touch-layout-compiler.js +93 -92
- package/build/src/compiler/touch-layout-compiler.js.map +1 -1
- package/build/src/compiler/tran.d.ts +7 -0
- package/build/src/compiler/tran.d.ts.map +1 -1
- package/build/src/compiler/tran.js +382 -353
- package/build/src/compiler/tran.js.map +1 -1
- package/build/src/compiler/vars.d.ts.map +1 -1
- package/build/src/compiler/vars.js +236 -227
- package/build/src/compiler/vars.js.map +1 -1
- package/build/src/compiler/visual-keyboard-compiler.js +70 -69
- package/build/src/compiler/visual-keyboard-compiler.js.map +1 -1
- package/build/src/main.js +5 -4
- package/build/src/main.js.map +1 -1
- package/build/src/util/util.d.ts +2 -2
- package/build/src/util/util.d.ts.map +1 -1
- package/build/src/util/util.js +185 -179
- package/build/src/util/util.js.map +1 -1
- package/package.json +6 -6
|
@@ -1,416 +1,420 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
var
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
KeysCompiler.
|
|
23
|
-
KeysCompiler.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
constants.section.
|
|
40
|
-
constants.section.
|
|
41
|
-
constants.section.
|
|
42
|
-
constants.section.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
*
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
//
|
|
57
|
-
this
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
flickKeys
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
*
|
|
218
|
-
* @param
|
|
219
|
-
* @param
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
//
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const
|
|
270
|
-
const
|
|
271
|
-
const
|
|
272
|
-
const
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
toCooked = sections.vars.
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
*
|
|
320
|
-
* @param
|
|
321
|
-
* @param
|
|
322
|
-
* @
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
//
|
|
387
|
-
//
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
if
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
-
|
|
1
|
+
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ce44ab4e-cf0b-5e01-b83e-b8818380b183")}catch(e){}}();
|
|
3
|
+
import { constants } from '@keymanapp/ldml-keyboard-constants';
|
|
4
|
+
import { LDMLKeyboard, KMXPlus, Constants } from '@keymanapp/common-types';
|
|
5
|
+
import { CompilerMessages } from './messages.js';
|
|
6
|
+
import { SectionCompiler } from "./section-compiler.js";
|
|
7
|
+
var Keys = KMXPlus.Keys;
|
|
8
|
+
var KeysFlicks = KMXPlus.KeysFlicks;
|
|
9
|
+
import { allUsedKeyIdsInFlick, allUsedKeyIdsInKey, allUsedKeyIdsInLayers, calculateUniqueKeys, hashFlicks, hashKeys, translateLayerAttrToModifier, validModifier } from '../util/util.js';
|
|
10
|
+
import { SubstitutionUse } from './substitution-tracker.js';
|
|
11
|
+
/** reserved name for the special gap key. space is not allowed in key ids. */
|
|
12
|
+
const reserved_gap = "gap (reserved)";
|
|
13
|
+
export class KeysCompiler extends SectionCompiler {
|
|
14
|
+
static validateSubstitutions(keyboard, st) {
|
|
15
|
+
const uniqueKeys = calculateUniqueKeys([...keyboard.keys?.key]);
|
|
16
|
+
const keyBag = hashKeys(uniqueKeys); // for easier lookup
|
|
17
|
+
// will be the set of ALL keys used in this keyboard
|
|
18
|
+
const usedKeys = allUsedKeyIdsInLayers(keyboard?.layers);
|
|
19
|
+
// save off the layer key IDs before we mutate the set
|
|
20
|
+
const layerKeyIds = Array.from(usedKeys.values());
|
|
21
|
+
const flickHash = hashFlicks(keyboard?.flicks?.flick); // for easier lookup
|
|
22
|
+
const usedFlicks = KeysCompiler.getUsedFlicks(layerKeyIds, keyBag);
|
|
23
|
+
KeysCompiler.addKeysFromFlicks(usedFlicks, flickHash, usedKeys);
|
|
24
|
+
KeysCompiler.addUsedGestureKeys(layerKeyIds, keyBag, usedKeys);
|
|
25
|
+
// process each used key. unused keys don't get checked.
|
|
26
|
+
for (let keyId of usedKeys.values()) {
|
|
27
|
+
const key = keyBag.get(keyId);
|
|
28
|
+
if (!key)
|
|
29
|
+
continue; // key not found is handled elsewhere.
|
|
30
|
+
st.addStringAndMarkerSubstitution(SubstitutionUse.emit, key.output);
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
get id() {
|
|
35
|
+
return constants.section.keys;
|
|
36
|
+
}
|
|
37
|
+
get dependencies() {
|
|
38
|
+
const defaults = new Set([
|
|
39
|
+
constants.section.elem,
|
|
40
|
+
constants.section.list,
|
|
41
|
+
constants.section.meta,
|
|
42
|
+
constants.section.strs,
|
|
43
|
+
constants.section.vars,
|
|
44
|
+
]);
|
|
45
|
+
return defaults;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
*
|
|
49
|
+
* @returns just the non-touch layers.
|
|
50
|
+
*/
|
|
51
|
+
hardwareLayers() {
|
|
52
|
+
return this.keyboard3.layers?.filter(({ formId }) => formId !== "touch");
|
|
53
|
+
}
|
|
54
|
+
validate() {
|
|
55
|
+
let valid = true;
|
|
56
|
+
// There's no 'form' compiler.
|
|
57
|
+
// We validate this here so that someone checks it.
|
|
58
|
+
this.keyboard3.forms?.form?.forEach((form) => {
|
|
59
|
+
if (!LDMLKeyboard.ImportStatus.isImpliedImport(form)) {
|
|
60
|
+
// If it's not an implied import, give a warning.
|
|
61
|
+
this.callbacks.reportMessage(CompilerMessages.Warn_CustomForm({ id: form.id }));
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
const keyBag = this.getKeyBag();
|
|
65
|
+
// will be the set of ALL keys used in this keyboard
|
|
66
|
+
const usedKeys = this.getLayerKeyIds();
|
|
67
|
+
// save off the layer key IDs before we mutate the set
|
|
68
|
+
const layerKeyIds = Array.from(usedKeys.values());
|
|
69
|
+
const flickHash = this.getFlicks(); // for easier lookup
|
|
70
|
+
const usedFlicks = KeysCompiler.getUsedFlicks(layerKeyIds, keyBag);
|
|
71
|
+
// go through each layer key and collect flicks and gestures
|
|
72
|
+
for (const keyId of layerKeyIds) {
|
|
73
|
+
const key = keyBag.get(keyId);
|
|
74
|
+
if (!key) {
|
|
75
|
+
// Note: validateHardwareLayerForKmap(), below, will raise an error for hardware keys that are missing, with additional
|
|
76
|
+
// context. For this section, we just skip missing keys.
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const { flickId } = key;
|
|
80
|
+
if (flickId) {
|
|
81
|
+
if (!flickHash.has(flickId)) {
|
|
82
|
+
valid = false;
|
|
83
|
+
this.callbacks.reportMessage(CompilerMessages.Error_MissingFlicks({ flickId, id: keyId }));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const gestureKeys = allUsedKeyIdsInKey(key);
|
|
87
|
+
for (const [gestureKeyId, attrs] of gestureKeys.entries()) {
|
|
88
|
+
const gestureKey = keyBag.get(gestureKeyId);
|
|
89
|
+
if (gestureKey == null) {
|
|
90
|
+
// TODO-LDML: could keep track of already missing keys so we don't warn multiple times on gesture keys
|
|
91
|
+
valid = false;
|
|
92
|
+
this.callbacks.reportMessage(CompilerMessages.Error_GestureKeyNotFoundInKeyBag({ keyId: gestureKeyId, parentKeyId: keyId, attribute: attrs.join(',') }));
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
usedKeys.add(gestureKeyId);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// now, check the flicks
|
|
100
|
+
KeysCompiler.addKeysFromFlicks(usedFlicks, flickHash, usedKeys);
|
|
101
|
+
// TODO-LDML: hint on unused flicks (that aren't imported)
|
|
102
|
+
// Note: the layr compiler does more extensive validation of the layer attributes.
|
|
103
|
+
// Kmap validation
|
|
104
|
+
const hardwareLayers = this.hardwareLayers();
|
|
105
|
+
if (hardwareLayers.length >= 1) {
|
|
106
|
+
// validate all errors
|
|
107
|
+
for (let layers of hardwareLayers) {
|
|
108
|
+
for (let layer of layers.layer) {
|
|
109
|
+
valid =
|
|
110
|
+
this.validateHardwareLayerForKmap(layers.formId, layer, keyBag) && valid; // note: always validate even if previously invalid results found
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// TODO-LDML: } else { touch?
|
|
114
|
+
}
|
|
115
|
+
return valid;
|
|
116
|
+
}
|
|
117
|
+
static addKeysFromFlicks(usedFlicks, flickHash, usedKeys) {
|
|
118
|
+
for (let flickId of usedFlicks.values()) {
|
|
119
|
+
const flick = flickHash.get(flickId);
|
|
120
|
+
if (!flick)
|
|
121
|
+
continue;
|
|
122
|
+
const flickKeys = allUsedKeyIdsInFlick(flick);
|
|
123
|
+
flickKeys.forEach(keyId => usedKeys.add(keyId));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
getFlicks() {
|
|
127
|
+
return hashFlicks(this.keyboard3?.flicks?.flick);
|
|
128
|
+
}
|
|
129
|
+
/** a set with all key ids used in all layers */
|
|
130
|
+
getLayerKeyIds() {
|
|
131
|
+
return allUsedKeyIdsInLayers(this.keyboard3?.layers);
|
|
132
|
+
}
|
|
133
|
+
/** the entire keybag (used or unused) as a hash */
|
|
134
|
+
getKeyBag() {
|
|
135
|
+
const uniqueKeys = calculateUniqueKeys([...this.keyboard3.keys?.key]);
|
|
136
|
+
return hashKeys(uniqueKeys); // for easier lookup
|
|
137
|
+
}
|
|
138
|
+
compile(sections) {
|
|
139
|
+
/* c8 ignore next 4 */
|
|
140
|
+
if (!this.keyboard3?.keys?.key && !this.keyboard3?.keys?.flicks) {
|
|
141
|
+
// short-circuit if no keys or flicks. Doesn't happen in practice due to implied import.
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
let sect = new Keys(sections.strs);
|
|
145
|
+
// TODO-LDML: some duplication with validate()
|
|
146
|
+
const keyBag = this.getKeyBag();
|
|
147
|
+
// We only want to include used keys in .kmx
|
|
148
|
+
const usedKeys = this.getLayerKeyIds();
|
|
149
|
+
// save off the layer key IDs before we mutate the set
|
|
150
|
+
const layerKeyIds = Array.from(usedKeys.values());
|
|
151
|
+
// Load the flicks first
|
|
152
|
+
this.loadFlicks(sections, sect, keyBag, layerKeyIds, usedKeys);
|
|
153
|
+
// add in the gesture keys
|
|
154
|
+
KeysCompiler.addUsedGestureKeys(layerKeyIds, keyBag, usedKeys);
|
|
155
|
+
// Now, load the keys into memory
|
|
156
|
+
this.loadKeys(sections, sect, keyBag, layerKeyIds, usedKeys);
|
|
157
|
+
// Finally, kmap
|
|
158
|
+
// Use LayerMap + keys to generate compiled keys for hardware
|
|
159
|
+
const hardwareLayers = this.hardwareLayers();
|
|
160
|
+
/* c8 ignore next 3 */
|
|
161
|
+
if (hardwareLayers.length > 1) {
|
|
162
|
+
// validation should have already caught this
|
|
163
|
+
throw Error(`Internal error: Expected 0 or 1 hardware layer, not ${hardwareLayers.length}`);
|
|
164
|
+
}
|
|
165
|
+
else if (hardwareLayers.length === 1) {
|
|
166
|
+
const theLayers = hardwareLayers[0];
|
|
167
|
+
const { formId } = theLayers;
|
|
168
|
+
for (let layer of theLayers.layer) {
|
|
169
|
+
this.compileHardwareLayerToKmap(sections, layer, sect, formId);
|
|
170
|
+
}
|
|
171
|
+
} // else: TODO-LDML do nothing if only touch layers
|
|
172
|
+
// Now load the reserved keys and slip them in here
|
|
173
|
+
const reservedKeys = this.getReservedKeys(sections);
|
|
174
|
+
for (const key of reservedKeys.values()) {
|
|
175
|
+
sect.keys.push(key);
|
|
176
|
+
}
|
|
177
|
+
return sect;
|
|
178
|
+
}
|
|
179
|
+
/** list of reserved keys, for tests */
|
|
180
|
+
static reserved_keys = [reserved_gap];
|
|
181
|
+
/** count of reserved keys, for tests */
|
|
182
|
+
static reserved_count = KeysCompiler.reserved_keys.length;
|
|
183
|
+
/** load up all reserved keys */
|
|
184
|
+
getReservedKeys(sections) {
|
|
185
|
+
const r = new Map();
|
|
186
|
+
// set up some constants..
|
|
187
|
+
const no_string = sections.strs.allocString('');
|
|
188
|
+
const no_list = sections.list.allocList([], {}, sections);
|
|
189
|
+
// now add the reserved key(s).
|
|
190
|
+
r.set(reserved_gap, {
|
|
191
|
+
flags: constants.keys_key_flags_gap | constants.keys_key_flags_extend,
|
|
192
|
+
id: sections.strs.allocString(reserved_gap),
|
|
193
|
+
flicks: '',
|
|
194
|
+
longPress: no_list,
|
|
195
|
+
longPressDefault: no_string,
|
|
196
|
+
multiTap: no_list,
|
|
197
|
+
switch: no_string,
|
|
198
|
+
to: no_string,
|
|
199
|
+
width: 10.0, // 10 * .1
|
|
200
|
+
});
|
|
201
|
+
if (r.size !== KeysCompiler.reserved_count) {
|
|
202
|
+
throw Error(`Internal Error: KeysCompiler.reserved_count=${KeysCompiler.reserved_count} != ${r.size} actual reserved keys.`);
|
|
203
|
+
}
|
|
204
|
+
return r;
|
|
205
|
+
}
|
|
206
|
+
static addUsedGestureKeys(layerKeyIds, keyBag, usedKeys) {
|
|
207
|
+
for (let keyId of layerKeyIds) {
|
|
208
|
+
const key = keyBag.get(keyId);
|
|
209
|
+
if (!key)
|
|
210
|
+
continue;
|
|
211
|
+
for (let gestureKeyId of allUsedKeyIdsInKey(key).keys()) {
|
|
212
|
+
usedKeys.add(gestureKeyId);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
*
|
|
218
|
+
* @param keyBag the keybag as a hash
|
|
219
|
+
* @param layerKeyIds list of keys from the layer, to extract used flicks
|
|
220
|
+
* @param usedKeys will be populated with keys used in the flick
|
|
221
|
+
*/
|
|
222
|
+
loadFlicks(sections, sect, keyBag, layerKeyIds, usedKeys) {
|
|
223
|
+
const flickHash = this.getFlicks(); // for easier lookup
|
|
224
|
+
const usedFlicks = KeysCompiler.getUsedFlicks(layerKeyIds, keyBag);
|
|
225
|
+
// only include used flicks in the table
|
|
226
|
+
// this way, extra unused imported flicks are ignored
|
|
227
|
+
// in id order, for now
|
|
228
|
+
for (let flickId of Array.from(usedFlicks.values()).sort()) {
|
|
229
|
+
const flick = flickHash.get(flickId);
|
|
230
|
+
if (!flick)
|
|
231
|
+
continue; // already reported by validate()
|
|
232
|
+
// allocate the in-memory <flick id=…>
|
|
233
|
+
let flicks = new KeysFlicks(sections.strs.allocString(flickId));
|
|
234
|
+
// add data from each segment
|
|
235
|
+
for (let { keyId, directions } of flick.flickSegment) {
|
|
236
|
+
const keyIdStr = sections.strs.allocString(keyId);
|
|
237
|
+
let directionsList = sections.list.allocListFromSpaces(directions, {}, sections);
|
|
238
|
+
flicks.flicks.push({
|
|
239
|
+
directions: directionsList,
|
|
240
|
+
keyId: keyIdStr,
|
|
241
|
+
});
|
|
242
|
+
usedKeys.add(keyId);
|
|
243
|
+
}
|
|
244
|
+
sect.flicks.push(flicks);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
static getUsedFlicks(layerKeyIds, keyBag) {
|
|
248
|
+
const usedFlicks = new Set();
|
|
249
|
+
for (let keyId of layerKeyIds) {
|
|
250
|
+
const key = keyBag.get(keyId);
|
|
251
|
+
if (!key?.flickId)
|
|
252
|
+
continue;
|
|
253
|
+
usedFlicks.add(key.flickId);
|
|
254
|
+
}
|
|
255
|
+
return usedFlicks;
|
|
256
|
+
}
|
|
257
|
+
loadKeys(sections, sect, keyBag, layerKeyIds, usedKeys) {
|
|
258
|
+
// for each used key (whether from layer, gesture, etc.)
|
|
259
|
+
// push these in id order, for tidiness
|
|
260
|
+
for (let keyId of Array.from(usedKeys.values()).sort()) {
|
|
261
|
+
const key = keyBag.get(keyId);
|
|
262
|
+
if (!key)
|
|
263
|
+
continue; // missing key
|
|
264
|
+
let flags = 0;
|
|
265
|
+
const { flickId, gap, longPressDefaultKeyId, longPressKeyIds, multiTapKeyIds, layerId, output } = key;
|
|
266
|
+
if (!!gap) {
|
|
267
|
+
flags |= constants.keys_key_flags_gap;
|
|
268
|
+
}
|
|
269
|
+
const id = sections.strs.allocString(key.id);
|
|
270
|
+
const longPress = sections.list.allocListFromSpaces(longPressKeyIds, {}, sections);
|
|
271
|
+
const longPressDefault = sections.strs.allocString(longPressDefaultKeyId, {}, sections);
|
|
272
|
+
const multiTap = sections.list.allocListFromSpaces(multiTapKeyIds, {}, sections);
|
|
273
|
+
const keySwitch = sections.strs.allocString(layerId); // 'switch' is a reserved word
|
|
274
|
+
const toRaw = output;
|
|
275
|
+
let toCooked = sections.vars.substituteStrings(toRaw, sections);
|
|
276
|
+
toCooked = sections.vars.substituteMarkerString(toCooked);
|
|
277
|
+
const to = sections.strs.allocString(toCooked, {
|
|
278
|
+
stringVariables: true,
|
|
279
|
+
markers: true,
|
|
280
|
+
unescape: true,
|
|
281
|
+
singleOk: true,
|
|
282
|
+
nfd: true,
|
|
283
|
+
}, sections);
|
|
284
|
+
if (!to.isOneChar) {
|
|
285
|
+
flags |= constants.keys_key_flags_extend;
|
|
286
|
+
}
|
|
287
|
+
const width = Math.ceil((key.width || 1) * 10.0); // default, width=1
|
|
288
|
+
sect.keys.push({
|
|
289
|
+
flags,
|
|
290
|
+
flicks: flickId,
|
|
291
|
+
id,
|
|
292
|
+
longPress,
|
|
293
|
+
longPressDefault,
|
|
294
|
+
multiTap,
|
|
295
|
+
switch: keySwitch,
|
|
296
|
+
to,
|
|
297
|
+
width,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
getKeymapFromForm(hardware, badScans) {
|
|
302
|
+
return KeysCompiler.getKeymapFromForms(this.keyboard3?.forms.form, hardware, badScans);
|
|
303
|
+
}
|
|
304
|
+
static getKeymapFromForms(forms, hardware, badScans) {
|
|
305
|
+
// seach in reverse form because of overrides
|
|
306
|
+
const ldmlForm = [...forms].reverse().find((f) => f.id === hardware);
|
|
307
|
+
if (!ldmlForm) {
|
|
308
|
+
return undefined;
|
|
309
|
+
}
|
|
310
|
+
return KeysCompiler.getKeymapFromScancodes(ldmlForm, badScans);
|
|
311
|
+
}
|
|
312
|
+
static getKeymapFromScancodes(ldmlForm, badScans) {
|
|
313
|
+
const { scanCodes } = ldmlForm;
|
|
314
|
+
const ldmlScan = scanCodes.map(o => o.codes.split(" ").map(n => Number.parseInt(n, 16)));
|
|
315
|
+
const ldmlVkey = Constants.CLDRScanToKeyMap(ldmlScan, badScans);
|
|
316
|
+
return ldmlVkey;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Validate for purpose of kmap
|
|
320
|
+
* @param hardware the 'form' parameter
|
|
321
|
+
* @param layer
|
|
322
|
+
* @param keyHash the keybag's hash
|
|
323
|
+
* @returns true if valid
|
|
324
|
+
*/
|
|
325
|
+
validateHardwareLayerForKmap(hardware, layer, keyHash) {
|
|
326
|
+
let valid = true;
|
|
327
|
+
const { modifiers } = layer;
|
|
328
|
+
if (!validModifier(modifiers)) {
|
|
329
|
+
this.callbacks.reportMessage(CompilerMessages.Error_InvalidModifier({ modifiers, layer: layer.id }));
|
|
330
|
+
valid = false;
|
|
331
|
+
}
|
|
332
|
+
const badScans = new Set();
|
|
333
|
+
const keymap = this.getKeymapFromForm(hardware, badScans);
|
|
334
|
+
if (!keymap) {
|
|
335
|
+
this.callbacks.reportMessage(CompilerMessages.Error_InvalidHardware({ formId: hardware }));
|
|
336
|
+
valid = false;
|
|
337
|
+
return valid;
|
|
338
|
+
}
|
|
339
|
+
else if (badScans.size !== 0) {
|
|
340
|
+
const codes = Array.from(badScans.values()).map(n => Number(n).toString(16)).sort();
|
|
341
|
+
this.callbacks.reportMessage(CompilerMessages.Error_InvalidScanCode({ form: hardware, codes }));
|
|
342
|
+
valid = false;
|
|
343
|
+
return valid;
|
|
344
|
+
}
|
|
345
|
+
if (layer.row.length > keymap.length) {
|
|
346
|
+
this.callbacks.reportMessage(CompilerMessages.Error_HardwareLayerHasTooManyRows());
|
|
347
|
+
valid = false;
|
|
348
|
+
}
|
|
349
|
+
for (let y = 0; y < layer.row.length && y < keymap.length; y++) {
|
|
350
|
+
const keys = layer.row[y].keys.split(" ");
|
|
351
|
+
if (keys.length > keymap[y].length) {
|
|
352
|
+
this.callbacks.reportMessage(CompilerMessages.Error_RowOnHardwareLayerHasTooManyKeys({
|
|
353
|
+
row: y + 1,
|
|
354
|
+
hardware,
|
|
355
|
+
modifiers,
|
|
356
|
+
}));
|
|
357
|
+
valid = false;
|
|
358
|
+
}
|
|
359
|
+
let x = -1;
|
|
360
|
+
for (let key of keys) {
|
|
361
|
+
x++;
|
|
362
|
+
let keydef = keyHash.get(key);
|
|
363
|
+
if (!keydef) {
|
|
364
|
+
this.callbacks.reportMessage(CompilerMessages.Error_KeyNotFoundInKeyBag({
|
|
365
|
+
keyId: key,
|
|
366
|
+
col: x + 1,
|
|
367
|
+
row: y + 1,
|
|
368
|
+
layer: layer.id,
|
|
369
|
+
form: "hardware",
|
|
370
|
+
}));
|
|
371
|
+
valid = false;
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (!keydef.output && !keydef.gap && !keydef.layerId) {
|
|
375
|
+
this.callbacks.reportMessage(CompilerMessages.Error_KeyMissingToGapOrSwitch({ keyId: key }));
|
|
376
|
+
valid = false;
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return valid;
|
|
382
|
+
}
|
|
383
|
+
compileHardwareLayerToKmap(sections, layer, sect, hardware) {
|
|
384
|
+
const mods = translateLayerAttrToModifier(layer);
|
|
385
|
+
const keymap = this.getKeymapFromForm(hardware);
|
|
386
|
+
// Iterate over rows (y) and cols (x) of the scancodes table.
|
|
387
|
+
// Any assigned keys will be used until we run out of keys in each row,
|
|
388
|
+
// and run out of rows. The rest will be reserved_gap.
|
|
389
|
+
for (let y = 0; y < keymap.length; y++) {
|
|
390
|
+
let keys;
|
|
391
|
+
// if there are keys, use them.
|
|
392
|
+
if (y < layer.row.length) {
|
|
393
|
+
const row = layer.row[y];
|
|
394
|
+
keys = row.keys.split(" ");
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
keys = [];
|
|
398
|
+
}
|
|
399
|
+
// all columns in this row
|
|
400
|
+
for (let x = 0; x < keymap[y].length; x++) {
|
|
401
|
+
const vkey = keymap[y][x]; // from the scan table
|
|
402
|
+
let key = reserved_gap; // unless there's a key in this row
|
|
403
|
+
if (x < keys.length) {
|
|
404
|
+
key = keys[x];
|
|
405
|
+
}
|
|
406
|
+
// push every combination
|
|
407
|
+
for (const mod of mods) {
|
|
408
|
+
sect.kmap.push({
|
|
409
|
+
vkey,
|
|
410
|
+
mod,
|
|
411
|
+
key, // key id, to be changed into key index at finalization
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return sect;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
416
419
|
//# sourceMappingURL=keys.js.map
|
|
420
|
+
//# debugId=ce44ab4e-cf0b-5e01-b83e-b8818380b183
|