Skip to content

Commit

Permalink
fix(developer): LDML compiler, add TSS_VISUALKEYBOARD store when comp…
Browse files Browse the repository at this point in the history
…iling visual keyboard

Fixes: #12395
Cherry-pick-of: #12402
  • Loading branch information
mcdurdin committed Sep 16, 2024
1 parent 91cd736 commit 94a4ac4
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 16 deletions.
33 changes: 23 additions & 10 deletions developer/src/kmc-ldml/src/compiler/compiler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { LDMLKeyboardXMLSourceFileReader, LDMLKeyboard, KMXPlus, CompilerCallbacks, LDMLKeyboardTestDataXMLSourceFile, UnicodeSetParser, KeymanCompiler, KeymanCompilerResult, KeymanCompilerArtifacts, defaultCompilerOptions, KMXBuilder, KvkFileWriter, KeymanCompilerArtifactOptional } from '@keymanapp/common-types';
import { KMX, LDMLKeyboardXMLSourceFileReader, LDMLKeyboard, KMXPlus, CompilerCallbacks, LDMLKeyboardTestDataXMLSourceFile, UnicodeSetParser, KeymanCompiler, KeymanCompilerResult, KeymanCompilerArtifacts, defaultCompilerOptions, KMXBuilder, KvkFileWriter, KeymanCompilerArtifactOptional } from '@keymanapp/common-types';
import { LdmlCompilerOptions } from './ldml-compiler-options.js';
import { CompilerMessages } from './messages.js';
import { BkspCompiler, TranCompiler } from './tran.js';
Expand Down Expand Up @@ -132,13 +132,27 @@ export class LdmlKeyboardCompiler implements KeymanCompiler {
KMXPlusMetadataCompiler.addKmxMetadata(kmx.kmxplus, kmx.keyboard, compilerOptions);

// Use the builder to generate the binary output file
const builder = new KMXBuilder(kmx, compilerOptions.saveDebug);
const kmx_binary = builder.compile();
const kmxBuilder = new KMXBuilder(kmx, compilerOptions.saveDebug);
const keyboardId = this.callbacks.path.basename(outputFilename, '.kmx');
const vkCompiler = new LdmlKeyboardVisualKeyboardCompiler(this.callbacks);
const vkCompilerResult = vkCompiler.compile(kmx.kmxplus, keyboardId);
if(vkCompilerResult === null) {
return null;
}
const vkData = typeof vkCompilerResult == 'object' ? vkCompilerResult : null;

const vkcompiler = new LdmlKeyboardVisualKeyboardCompiler(this.callbacks);
const vk = vkcompiler.compile(kmx.kmxplus, this.callbacks.path.basename(outputFilename, '.kmx'));
const writer = new KvkFileWriter();
const kvk_binary = writer.write(vk);
if(vkData) {
kmx.keyboard.stores.push({
dpName: '',
dpString: keyboardId + '.kvk',
dwSystemID: KMX.KMXFile.TSS_VISUALKEYBOARD
});
}

const kmxBinary = kmxBuilder.compile();

const kvkWriter = new KvkFileWriter();
const kvkBinary = vkData ? kvkWriter.write(vkData) : null;

// Note: we could have a step of generating source files here
// KvksFileWriter()...
Expand All @@ -151,11 +165,10 @@ export class LdmlKeyboardCompiler implements KeymanCompiler {
//KMW17.0: const encoder = new TextEncoder();
//KMW17.0: const kmw_binary = encoder.encode(kmw_string);


return {
artifacts: {
kmx: { data: kmx_binary, filename: outputFilename },
kvk: { data: kvk_binary, filename: outputFilename.replace(/\.kmx$/, '.kvk') },
kmx: { data: kmxBinary, filename: outputFilename },
kvk: kvkBinary ? { data: kvkBinary, filename: outputFilename.replace(/\.kmx$/, '.kvk') } : null,
//KMW17.0: js: { data: kmw_binary, filename: outputFilename.replace(/\.kmx$/, '.js') },
}
};
Expand Down
2 changes: 1 addition & 1 deletion developer/src/kmc-ldml/src/compiler/empty-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { VarsCompiler } from './vars.js';
import { CompilerMessages } from './messages.js';

/**
* Compiler for typrs that don't actually consume input XML
* Compiler for types that don't actually consume input XML
*/
export abstract class EmptyCompiler extends SectionCompiler {
private _id: SectionIdent;
Expand Down
33 changes: 30 additions & 3 deletions developer/src/kmc-ldml/src/compiler/visual-keyboard-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,15 @@ export class LdmlKeyboardVisualKeyboardCompiler {
public constructor(private callbacks: CompilerCallbacks) {
}

public compile(source: KMXPlus.KMXPlusData, keyboardId: string): VisualKeyboard.VisualKeyboard {
/**
* Generate a visual keyboard
* @param source Compiled KMX+ data; note that this is modified to add
* &VISUALKEYBOARD system store on success
* @param keyboardId Basename of keyboard, without file extension
* @returns Visual keyboard data on success, null on failure, or
* false if no VK was generated for this keyboard
*/
public compile(source: KMXPlus.KMXPlusData, keyboardId: string): VisualKeyboard.VisualKeyboard | boolean | null {
let result = new VisualKeyboard.VisualKeyboard();

/* TODO-LDML: consider VisualKeyboardHeaderFlags.kvkhUseUnderlying kvkhDisplayUnderlying kvkhAltGr kvkh102 */
Expand All @@ -50,16 +58,32 @@ export class LdmlKeyboardVisualKeyboardCompiler {
result.header.ansiFont = {...VisualKeyboard.DEFAULT_KVK_FONT};
result.header.unicodeFont = {...VisualKeyboard.DEFAULT_KVK_FONT};

let hasVisualKeyboard = false;

for(let layersList of source.layr.lists) {
const formId = layersList.hardware.value;
if(formId == 'touch') {
continue;
}

for(let layer of layersList.layers) {
this.compileHardwareLayer(source, result, layer, formId);
const res = this.compileHardwareLayer(source, result, layer, formId);
if(res === false) {
// failed to compile the layer
return null;
}
if(res === null) {
// not a supported layer type, but not an error
continue;
}
hasVisualKeyboard = true;
}
}

if(!hasVisualKeyboard) {
return false;
}

return result;
}

Expand All @@ -76,9 +100,10 @@ export class LdmlKeyboardVisualKeyboardCompiler {
const shift = this.translateLayerModifiersToVisualKeyboardShift(layer.mod);
if(shift === null) {
// Caps (num, scroll) is not a supported shift state in .kvk
return;
return null;
}

let result = true;
let y = -1;
for(let row of layer.rows) {
y++;
Expand All @@ -94,6 +119,7 @@ export class LdmlKeyboardVisualKeyboardCompiler {
this.callbacks.reportMessage(
CompilerMessages.Error_KeyNotFoundInKeyBag({ keyId: key.value, layer: layerId, row: y, col: x, form: hardware })
);
result = false;
} else {
vk.keys.push({
flags: VisualKeyboard.VisualKeyboardKeyFlags.kvkkUnicode,
Expand All @@ -104,6 +130,7 @@ export class LdmlKeyboardVisualKeyboardCompiler {
}
}
}
return result;
}

private getDisplayFromKey(keydef: KMXPlus.KeysKeys, source: KMXPlus.KMXPlusData) {
Expand Down
12 changes: 10 additions & 2 deletions developer/src/kmc-ldml/test/test-visual-keyboard-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as path from 'path';
import {assert} from 'chai';
import { stripIndent } from 'common-tags';

import { KvkFileWriter, LDMLKeyboardXMLSourceFileReader, VisualKeyboard } from '@keymanapp/common-types';
import { KMX, KvkFileWriter, LDMLKeyboardXMLSourceFileReader, VisualKeyboard } from '@keymanapp/common-types';
import hextobin from '@keymanapp/hextobin';

import { checkMessages, compilerTestCallbacks, compilerTestOptions, makePathToFixture } from './helpers/index.js';
Expand Down Expand Up @@ -38,9 +38,16 @@ describe('visual-keyboard-compiler', function() {
let kmx = await k.compile(source);
assert(kmx, 'k.compile should not have failed');

const vk = (new LdmlKeyboardVisualKeyboardCompiler(compilerTestCallbacks)).compile(kmx.kmxplus, path.basename(inputFilename, '.xml'));
const keyboardId = path.basename(inputFilename, '.xml');

const vk = (new LdmlKeyboardVisualKeyboardCompiler(compilerTestCallbacks)).compile(kmx.kmxplus, keyboardId);
checkMessages();
assert.isNotNull(vk, 'LdmlKeyboardVisualKeyboardCompiler.compile should not have returned null');
assert.isNotNull(kmx.keyboard.stores.find(store =>
store.dwSystemID == KMX.KMXFile.TSS_VISUALKEYBOARD &&
store.dpString == keyboardId + '.kvk'
));
assert(typeof vk == 'object');

// Use the builder to generate the binary output file
const writer = new KvkFileWriter();
Expand Down Expand Up @@ -220,6 +227,7 @@ async function loadVisualKeyboardFromXml(xml: string, id: string) {
assert(kmx, 'k.compile should not have failed');

const vk = (new LdmlKeyboardVisualKeyboardCompiler(compilerTestCallbacks)).compile(kmx.kmxplus, id);
assert(typeof vk == 'object');
assert.isEmpty(compilerTestCallbacks.messages);
assert.isOk(vk);

Expand Down

0 comments on commit 94a4ac4

Please sign in to comment.