From 7b198eb4a4441becdab1462aa7393fa69d33c9db Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Fri, 23 Feb 2024 13:17:11 +0700 Subject: [PATCH 1/2] fix(developer): repeated options in kmc must now be fully specified Fixes #10396. Instead of `--message 20ae 5006`, which is ambiguous, because 5006 could be a filename, you must now specify the parameter option flag each time: `--message 20ae --message 5006` (or `-m 20ae -m 5006`). --- common/web/types/src/util/compiler-interfaces.ts | 2 +- developer/src/kmc/src/commands/build.ts | 3 ++- developer/src/kmc/src/messages/infrastructureMessages.ts | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/common/web/types/src/util/compiler-interfaces.ts b/common/web/types/src/util/compiler-interfaces.ts index a7e2fba841..9101cf5965 100644 --- a/common/web/types/src/util/compiler-interfaces.ts +++ b/common/web/types/src/util/compiler-interfaces.ts @@ -89,7 +89,7 @@ export class CompilerError { * ``` */ static formatCode(code: number): string { - return 'KM' + CompilerError.error(code).toString(16).toUpperCase().padStart(5, '0'); + return Number.isInteger(code) ? 'KM' + CompilerError.error(code).toString(16).toUpperCase().padStart(5, '0') : 'KM?????'; } /** diff --git a/developer/src/kmc/src/commands/build.ts b/developer/src/kmc/src/commands/build.ts index 5150e76f33..8548d96317 100644 --- a/developer/src/kmc/src/commands/build.ts +++ b/developer/src/kmc/src/commands/build.ts @@ -26,7 +26,8 @@ export function declareBuild(program: Command) { .option('-d, --debug', 'Include debug information in output') .option('-w, --compiler-warnings-as-errors', 'Causes warnings to fail the build; overrides project-level warnings-as-errors option') .option('-W, --no-compiler-warnings-as-errors', 'Warnings do not fail the build; overrides project-level warnings-as-errors option') - .option('-m, --message ', 'Adjust severity of info, hint or warning message to Disable (default), Info, Hint, Warn or Error') + .option('-m, --message ', 'Adjust severity of info, hint or warning message to Disable (default), Info, Hint, Warn or Error (option can be repeated)', + (value, previous) => previous.concat([value]), []) .option('--no-compiler-version', 'Exclude compiler version metadata from output') .option('--no-warn-deprecated-code', 'Turn off warnings for deprecated code styles'); diff --git a/developer/src/kmc/src/messages/infrastructureMessages.ts b/developer/src/kmc/src/messages/infrastructureMessages.ts index 1288698e00..5816dc815b 100644 --- a/developer/src/kmc/src/messages/infrastructureMessages.ts +++ b/developer/src/kmc/src/messages/infrastructureMessages.ts @@ -1,4 +1,4 @@ -import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m } from "@keymanapp/common-types"; +import { CompilerError, CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m } from "@keymanapp/common-types"; const Namespace = CompilerErrorNamespace.Infrastructure; const SevInfo = CompilerErrorSeverity.Info | Namespace; @@ -101,15 +101,15 @@ export class InfrastructureMessages { static ERROR_InvalidMessageFormat = SevError | 0x0016; static Error_MessageNamespaceNotFound = (o:{code: number}) => m(this.ERROR_MessageNamespaceNotFound, - `Invalid parameter: --message KM${o.code?.toString(16)} does not have a recognized namespace`); + `Invalid parameter: --message ${CompilerError.formatCode(o.code)} does not have a recognized namespace`); static ERROR_MessageNamespaceNotFound = SevError | 0x0017; static Error_MessageCodeNotFound = (o:{code: number}) => m(this.ERROR_MessageCodeNotFound, - `Invalid parameter: --message KM${o.code?.toString(16)} is not a recognized code`); + `Invalid parameter: --message ${CompilerError.formatCode(o.code)} is not a recognized code`); static ERROR_MessageCodeNotFound = SevError | 0x0018; static Error_MessageCannotBeCoerced = (o:{code: number}) => m(this.ERROR_MessageCannotBeCoerced, - `Invalid parameter: --message KM${o.code?.toString(16)} is not a info, hint or warn message type and cannot be coerced`); + `Invalid parameter: --message ${CompilerError.formatCode(o.code)} is not of type 'info', 'hint' or 'warn', and cannot be coerced`); static ERROR_MessageCannotBeCoerced = SevError | 0x0019; } From 413c26c9341b668d7615632e4d657c8c30888185 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Tue, 27 Feb 2024 12:06:11 +0700 Subject: [PATCH 2/2] docs(developer): add documentation for kmc-analyze Relates to #10207. --- developer/docs/api/etc/kmc-analyze.api.md | 31 +++++----- developer/docs/api/etc/kmc-kmn.api.md | 2 +- developer/src/kmc-analyze/src/index.ts | 5 +- developer/src/kmc-analyze/src/messages.ts | 18 +++++- .../src/osk-character-use/index.ts | 61 +++++++++++++++++++ .../kmc-analyze/src/osk-rewrite-pua/index.ts | 25 ++++++++ 6 files changed, 121 insertions(+), 21 deletions(-) diff --git a/developer/docs/api/etc/kmc-analyze.api.md b/developer/docs/api/etc/kmc-analyze.api.md index c2b911a450..91fbf7f1f1 100644 --- a/developer/docs/api/etc/kmc-analyze.api.md +++ b/developer/docs/api/etc/kmc-analyze.api.md @@ -6,47 +6,46 @@ import { CompilerCallbacks } from '@keymanapp/common-types'; import { CompilerEvent } from '@keymanapp/common-types'; -import { Osk } from '@keymanapp/common-types'; +import { Osk } from '@keymanapp/developer-utils'; -// @public (undocumented) +// @public export class AnalyzeOskCharacterUse { - // Warning: (ae-forgotten-export) The symbol "AnalyzeOskCharacterUseOptions" needs to be exported by the entry point index.d.ts constructor(callbacks: CompilerCallbacks, options?: AnalyzeOskCharacterUseOptions); analyze(file: string): Promise; clear(): void; getStrings(format?: '.txt' | '.md' | '.json'): string[]; } -// @public (undocumented) +// @public +export interface AnalyzeOskCharacterUseOptions { + includeCounts?: boolean; + puaBase?: number; + stripDottedCircle?: boolean; +} + +// @public export class AnalyzeOskRewritePua { constructor(callbacks: CompilerCallbacks); - // (undocumented) analyze(file: string, mapping: Osk.StringResult[]): Promise; - // (undocumented) clear(): void; - // (undocumented) get data(): { [index: string]: Uint8Array; }; } -// @public (undocumented) +// @public export class AnalyzerMessages { - // (undocumented) - static FATAL_UnexpectedException: number; - // (undocumented) + static readonly FATAL_UnexpectedException: number; + // @internal (undocumented) static Fatal_UnexpectedException: (o: { e: any; }) => CompilerEvent; - // (undocumented) - static INFO_ScanningFile: number; - // (undocumented) + static readonly INFO_ScanningFile: number; + // @internal (undocumented) static Info_ScanningFile: (o: { type: string; name: string; }) => CompilerEvent; } -// (No @packageDocumentation comment for this package) - ``` diff --git a/developer/docs/api/etc/kmc-kmn.api.md b/developer/docs/api/etc/kmc-kmn.api.md index b1faead083..5950430d0b 100644 --- a/developer/docs/api/etc/kmc-kmn.api.md +++ b/developer/docs/api/etc/kmc-kmn.api.md @@ -11,7 +11,7 @@ import { KeymanCompiler } from '@keymanapp/common-types'; import { KeymanCompilerArtifactOptional } from '@keymanapp/common-types'; import { KeymanCompilerArtifacts } from '@keymanapp/common-types'; import { KeymanCompilerResult } from '@keymanapp/common-types'; -import { Osk } from '@keymanapp/common-types'; +import { Osk } from '@keymanapp/developer-utils'; import { UnicodeSet } from '@keymanapp/common-types'; import { UnicodeSetParser } from '@keymanapp/common-types'; diff --git a/developer/src/kmc-analyze/src/index.ts b/developer/src/kmc-analyze/src/index.ts index 6717654ee0..c0999f4316 100644 --- a/developer/src/kmc-analyze/src/index.ts +++ b/developer/src/kmc-analyze/src/index.ts @@ -1,7 +1,8 @@ /** - * osk-char-use - get list of characters used in on screen keyboard + touch layout + * @packageDocumentation + * kmc-analyze - keyboard analysis classes, including tools for `&displayMap`. */ -export { AnalyzeOskCharacterUse } from './osk-character-use/index.js'; +export { AnalyzeOskCharacterUse, AnalyzeOskCharacterUseOptions } from './osk-character-use/index.js'; export { AnalyzeOskRewritePua } from './osk-rewrite-pua/index.js'; export { AnalyzerMessages } from './messages.js'; \ No newline at end of file diff --git a/developer/src/kmc-analyze/src/messages.ts b/developer/src/kmc-analyze/src/messages.ts index a800fb944a..d3596099d3 100644 --- a/developer/src/kmc-analyze/src/messages.ts +++ b/developer/src/kmc-analyze/src/messages.ts @@ -7,11 +7,25 @@ const SevInfo = CompilerErrorSeverity.Info | Namespace; // const SevError = CompilerErrorSeverity.Error | Namespace; const SevFatal = CompilerErrorSeverity.Fatal | Namespace; +/** + * @public + * Compiler messages for `kmc analyze` + */ export class AnalyzerMessages { + /** @internal */ static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, null, o.e ?? 'unknown error'); - static FATAL_UnexpectedException = SevFatal | 0x0001; + /** + * Raised when the compiler experiences an internal error. These should be + * reported to the Keyman team for resolution via + * https://github.com/keymanapp/keyman/issues/new + */ + static readonly FATAL_UnexpectedException = SevFatal | 0x0001; + /** @internal */ static Info_ScanningFile = (o:{type: string, name: string}) => m(this.INFO_ScanningFile, `Scanning ${o.type} file ${o.name}`); - static INFO_ScanningFile = SevInfo | 0x0002; + /** + * Informative message reporting on the current file being scanned + */ + static readonly INFO_ScanningFile = SevInfo | 0x0002; }; diff --git a/developer/src/kmc-analyze/src/osk-character-use/index.ts b/developer/src/kmc-analyze/src/osk-character-use/index.ts index 55832b25a1..7ad1a76e56 100644 --- a/developer/src/kmc-analyze/src/osk-character-use/index.ts +++ b/developer/src/kmc-analyze/src/osk-character-use/index.ts @@ -7,9 +7,24 @@ import { AnalyzerMessages } from "../messages.js"; type StringRefUsageMap = {[index:string]: Osk.StringRefUsage[]}; +/** + * @public + * Options for character analysis + */ export interface AnalyzeOskCharacterUseOptions { + /** + * First character to use in PUA for remapping with &displayMap, defaults to + * U+F100 + */ puaBase?: number; + /** + * If true, strips U+25CC from the key cap before further analysis + */ stripDottedCircle?: boolean; + /** + * If true, reports number of references to each character found in each + * source file + */ includeCounts?: boolean; } @@ -19,6 +34,11 @@ const defaultOptions: AnalyzeOskCharacterUseOptions = { includeCounts: false, } +/** + * @public + * Analyze the characters used in On Screen Keyboard files (.kvks, + * .keyman-touch-layout) for use with `&displayMap`. + */ export class AnalyzeOskCharacterUse { private _strings: StringRefUsageMap = {}; private options: AnalyzeOskCharacterUseOptions; @@ -27,6 +47,10 @@ export class AnalyzeOskCharacterUse { this.options = {...defaultOptions, ...options}; } + /** + * Clears analysis data collected from previous calls to + * {@link AnalyzeOskCharacterUse.analyze} + */ public clear() { this._strings = {}; } @@ -35,6 +59,19 @@ export class AnalyzeOskCharacterUse { // Analyze a single file // + /** + * Analyzes a single source file for Unicode character usage. Can parse .kmn, + * .kvks, .keyman-touch-layout file formats. Can be called multiple times to + * collect results from more than one file. Use + * {@link AnalyzeOskCharacterUse.getStrings} to retrieve results. + * + * Note: `analyze()` collects key cap data, so calling this for a .kmn file + * will retrieve the key caps from the .kvks and .keyman-touch-layout files + * that it references, rather than key cap data from the .kmn file itself. + * + * @param file - relative or absolute path to a Keyman source file + * @returns true if the file is successfully loaded and parsed + */ public async analyze(file: string): Promise { switch(KeymanFileTypes.sourceTypeFromFilename(file)) { case KeymanFileTypes.Source.VisualKeyboard: { @@ -211,6 +248,30 @@ export class AnalyzeOskCharacterUse { return result; } + /** + * Returns the collected results from earlier calls to + * {@link AnalyzeOskCharacterUse.analyze}. This generates a mapping from a key + * cap (one or more characters) to a PUA code, for use with `&displayMap`. + * + * Three output formats are supported: + * + * - .txt: tab-separated string format, with three columns: PUA, Key Cap, and + * plain string. The PUA and Key Cap columns are formatted as Unicode Scalar + * Values, e.g. U+0061, and the plain string is the original key cap string. + * + * - .md: formatted for documentation purposes. Generates a Markdown table + * (GFM) with PUA, Key Cap, and plain string. The PUA and Key Cap columns + * are formatted as Unicode Scalar Values, e.g. U+0061, and the plain string + * is the original key cap string. + * + * - .json: returns the final aggregated data as an array of strings, which + * can be joined to form a JSON blob of an object with a single member, + * `map`, which is an array of {@link Osk.StringResult} objects. + * + * @param format - file format to return - can be '.txt', '.md', or '.json' + * @returns an array of strings, formatted according to the `format` + * parameter. + */ public getStrings(format?: '.txt'|'.md'|'.json'): string[] { const final = this.prepareResults(this._strings); switch(format) { diff --git a/developer/src/kmc-analyze/src/osk-rewrite-pua/index.ts b/developer/src/kmc-analyze/src/osk-rewrite-pua/index.ts index f748f80964..2d24619546 100644 --- a/developer/src/kmc-analyze/src/osk-rewrite-pua/index.ts +++ b/developer/src/kmc-analyze/src/osk-rewrite-pua/index.ts @@ -4,12 +4,22 @@ import { CompilerMessages } from '@keymanapp/kmc-kmn'; import { getOskFromKmnFile } from "../util/get-osk-from-kmn-file.js"; import { AnalyzerMessages } from "../messages.js"; +/** + * @public + * Rewrite On Screen Keyboard files (.kvks, .keyman-touch-layout) with PUA + * codepoints, based on analysis provided by {@link AnalyzeOskCharacterUse} + * class. + */ export class AnalyzeOskRewritePua { private _data: {[index:string]: Uint8Array} = {}; constructor(private callbacks: CompilerCallbacks) { } + /** + * Clears data collected from previous calls to + * {@link AnalyzeOskRewritePua.analyze} + */ public clear() { this._data = {}; } @@ -18,6 +28,17 @@ export class AnalyzeOskRewritePua { // Analyze a single file // + /** + * Analyze a keyboard file or files, and provide a remapped output. Accepts a + * .kmn, .kvks, .keyman-touch-layout file formats. For .kmn, will rewrite + * associated On Screen Keyboard file formats. Can be called multiple times to + * rewrite multiple files. Use the {@link AnalyzeOskRewritePua.data} property + * to retrieve the output file content for writing. This does not modify the + * source file. + * @param file - relative or absolute path to a Keyman source file + * @param mapping - OSK keycap map provided by {@link AnalyzeOskCharacterUse} + * @returns true if the file is successfully loaded and rewritten + */ public async analyze(file: string, mapping: Osk.StringResult[]): Promise { let map: Osk.PuaMap = Osk.parseMapping(mapping); @@ -50,6 +71,10 @@ export class AnalyzeOskRewritePua { return true; } + /** + * Returns the file data for OSK files rewritten with PUA characters, for use + * with `&displayMap`. + */ public get data() { return this._data; }