From 7644d386fc69f38099e69e0c0133274ff27a73af Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Thu, 4 Jul 2024 13:02:36 +0700 Subject: [PATCH 01/26] refactor(common/models): split trie-model implementation into separate components --- common/models/templates/src/common.ts | 18 ++ common/models/templates/src/trie-model.ts | 303 ++-------------------- common/models/templates/src/trie.ts | 244 +++++++++++++++++ 3 files changed, 282 insertions(+), 283 deletions(-) create mode 100644 common/models/templates/src/trie.ts diff --git a/common/models/templates/src/common.ts b/common/models/templates/src/common.ts index 853bc7c18d..80c4470cb5 100644 --- a/common/models/templates/src/common.ts +++ b/common/models/templates/src/common.ts @@ -152,3 +152,21 @@ export function defaultApplyCasing(casing: CasingForm, text: string): string { return text.substring(0, headUnitLength).toUpperCase() .concat(text.substring(headUnitLength)); } } + +/** + * An **opaque** type for a string that is exclusively used as a search key in + * the trie. There should be a function that converts arbitrary strings + * (queries) and converts them into a standard search key for a given language + * model. + * + * Fun fact: This opaque type has ALREADY saved my bacon and found a bug! + */ +export type SearchKey = string & { _: 'SearchKey'}; + +/** + * A function that converts a string (word form or query) into a search key + * (secretly, this is also a string). + */ +export interface Wordform2Key { + (wordform: string): SearchKey; +} diff --git a/common/models/templates/src/trie-model.ts b/common/models/templates/src/trie-model.ts index 9de2fb9050..0a48d673a3 100644 --- a/common/models/templates/src/trie-model.ts +++ b/common/models/templates/src/trie-model.ts @@ -29,7 +29,8 @@ import { extendString, PriorityQueue } from "@keymanapp/web-utils"; import { default as defaultWordBreaker } from "@keymanapp/models-wordbreakers"; -import { applyTransform, isHighSurrogate, isSentinel, SENTINEL_CODE_UNIT, transformToSuggestion } from "./common.js"; +import { applyTransform, SearchKey, transformToSuggestion, Wordform2Key } from "./common.js"; +import { Node, Trie } from './trie.js'; import { getLastPreCaretToken } from "./tokenization.js"; extendString(); @@ -73,186 +74,6 @@ export interface TrieModelOptions { punctuation?: LexicalModelPunctuation; } -class Traversal implements LexiconTraversal { - /** - * The lexical prefix corresponding to the current traversal state. - */ - prefix: String; - - /** - * The current traversal node. Serves as the 'root' of its own sub-Trie, - * and we cannot navigate back to its parent. - */ - root: Node; - - /** - * The max weight for the Trie being 'traversed'. Needed for probability - * calculations. - */ - totalWeight: number; - - constructor(root: Node, prefix: string, totalWeight: number) { - this.root = root; - this.prefix = prefix; - this.totalWeight = totalWeight; - } - - child(char: USVString): LexiconTraversal | undefined { - /* - Note: would otherwise return the current instance if `char == ''`. If - such a call is happening, it's probably indicative of an implementation - issue elsewhere - let's signal now in order to catch such stuff early. - */ - if(char == '') { - return undefined; - } - - // Split into individual code units. - let steps = char.split(''); - let traversal: Traversal | undefined = this; - - while(steps.length > 0 && traversal) { - const step: string = steps.shift()!; - traversal = traversal._child(step); - } - - return traversal; - } - - // Handles one code unit at a time. - private _child(char: USVString): Traversal | undefined { - const root = this.root; - const totalWeight = this.totalWeight; - const nextPrefix = this.prefix + char; - - if(root.type == 'internal') { - let childNode = root.children[char]; - if(!childNode) { - return undefined; - } - - return new Traversal(childNode, nextPrefix, totalWeight); - } else { - // root.type == 'leaf'; - const legalChildren = root.entries.filter(function(entry) { - return entry.key.indexOf(nextPrefix) == 0; - }); - - if(!legalChildren.length) { - return undefined; - } - - return new Traversal(root, nextPrefix, totalWeight); - } - } - - *children(): Generator<{char: USVString, traversal: () => LexiconTraversal}> { - let root = this.root; - - // We refer to the field multiple times in this method, and it doesn't change. - // This also assists minification a bit, since we can't minify when re-accessing - // through `this.`. - const totalWeight = this.totalWeight; - - if(root.type == 'internal') { - for(let entry of root.values) { - let entryNode = root.children[entry]; - - // UTF-16 astral plane check. - if(isHighSurrogate(entry)) { - // First code unit of a UTF-16 code point. - // For now, we'll just assume the second always completes such a char. - // - // Note: Things get nasty here if this is only sometimes true; in the future, - // we should compile-time enforce that this assumption is always true if possible. - if(entryNode.type == 'internal') { - let internalNode = entryNode; - for(let lowSurrogate of internalNode.values) { - let prefix = this.prefix + entry + lowSurrogate; - yield { - char: entry + lowSurrogate, - traversal: function() { return new Traversal(internalNode.children[lowSurrogate], prefix, totalWeight) } - } - } - } else { - // Determine how much of the 'leaf' entry has no Trie nodes, emulate them. - let fullText = entryNode.entries[0].key; - entry = entry + fullText[this.prefix.length + 1]; // The other half of the non-BMP char. - let prefix = this.prefix + entry; - - yield { - char: entry, - traversal: function () {return new Traversal(entryNode, prefix, totalWeight)} - } - } - } else if(isSentinel(entry)) { - continue; - } else if(!entry) { - // Prevent any accidental 'null' or 'undefined' entries from having an effect. - continue; - } else { - let prefix = this.prefix + entry; - yield { - char: entry, - traversal: function() { return new Traversal(entryNode, prefix, totalWeight)} - } - } - } - - return; - } else { // type == 'leaf' - let prefix = this.prefix; - - let children = root.entries.filter(function(entry) { - return entry.key != prefix && prefix.length < entry.key.length; - }) - - for(let {key} of children) { - let nodeKey = key[prefix.length]; - - if(isHighSurrogate(nodeKey)) { - // Merge the other half of an SMP char in! - nodeKey = nodeKey + key[prefix.length+1]; - } - yield { - char: nodeKey, - traversal: function() { return new Traversal(root, prefix + nodeKey, totalWeight)} - } - }; - return; - } - } - - get entries() { - const entryMapper = (value: Entry) => { - return { - text: value.content, - p: value.weight / this.totalWeight - } - } - - if(this.root.type == 'leaf') { - let prefix = this.prefix; - let matches = this.root.entries.filter(function(entry) { - return entry.key == prefix; - }); - - return matches.map(entryMapper); - } else { - let matchingLeaf = this.root.children[SENTINEL_CODE_UNIT]; - if(matchingLeaf && matchingLeaf.type == 'leaf') { - return matchingLeaf.entries.map(entryMapper); - } else { - return []; - } - } - } - - get p(): number { - return this.root.weight / this.totalWeight; - } -} - /** * @class TrieModel * @@ -298,7 +119,7 @@ export default class TrieModel implements LexicalModel { predict(transform: Transform, context: Context): Distribution { // Special-case the empty buffer/transform: return the top suggestions. if (!transform.insert && !context.left && !context.right && context.startOfBuffer && context.endOfBuffer) { - return makeDistribution(this._trie.firstN(MAX_SUGGESTIONS).map(({text, p}) => ({ + return makeDistribution(this.firstN(MAX_SUGGESTIONS).map(({text, p}) => ({ transform: { insert: text, deleteLeft: 0 @@ -319,7 +140,7 @@ export default class TrieModel implements LexicalModel { let prefix = getLastPreCaretToken(this.breakWords, newContext); // Return suggestions from the trie. - return makeDistribution(this._trie.lookup(prefix).map(({text, p}) => + return makeDistribution(this.lookup(prefix).map(({text, p}) => transformToSuggestion({ insert: text, // Delete whatever the prefix that the user wrote. @@ -350,101 +171,13 @@ export default class TrieModel implements LexicalModel { public traverseFromRoot(): LexiconTraversal { return this._trie.traverseFromRoot(); } -}; -///////////////////////////////////////////////////////////////////////////////// -// What remains in this file is the trie implementation proper. Note: to // -// reduce bundle size, any functions/methods related to creating the trie have // -// been removed. // -///////////////////////////////////////////////////////////////////////////////// - -/** - * An **opaque** type for a string that is exclusively used as a search key in - * the trie. There should be a function that converts arbitrary strings - * (queries) and converts them into a standard search key for a given language - * model. - * - * Fun fact: This opaque type has ALREADY saved my bacon and found a bug! - */ -type SearchKey = string & { _: 'SearchKey'}; - -/** - * The priority queue will always pop the most probable item - be it a Traversal - * state or a lexical entry reached via Traversal. - */ -type TraversableWithProb = TextWithProbability | LexiconTraversal; - -/** - * A function that converts a string (word form or query) into a search key - * (secretly, this is also a string). - */ -interface Wordform2Key { - (wordform: string): SearchKey; -} - -// The following trie implementation has been (heavily) derived from trie-ing -// by Conrad Irwin. -// trie-ing is copyright (C) 2015–2017 Conrad Irwin. -// Distributed under the terms of the MIT license: -// https://github.com/ConradIrwin/trie-ing/blob/df55d7af7068d357829db9e0a7faa8a38add1d1d/LICENSE - -type Node = InternalNode | Leaf; -/** - * An internal node in the trie. Internal nodes NEVER contain entries; if an - * internal node should contain an entry, then it has a dummy leaf node (see - * below), that can be accessed by node.children["\uFDD0"]. - */ -interface InternalNode { - type: 'internal'; - weight: number; - /** Maintains the keys of children in descending order of weight. */ - values: string[]; // TODO: As an optimization, "values" can be a single string! /** - * Maps a single UTF-16 code unit to a child node in the trie. This child - * node may be a leaf or an internal node. The keys of this object are kept - * in sorted order in the .values array. + * Returns the top N suggestions from the trie. + * @param n How many suggestions, maximum, to return. */ - children: { [codeunit: string]: Node }; -} -/** Only leaf nodes actually contain entries (i.e., the words proper). */ -interface Leaf { - type: 'leaf'; - weight: number; - entries: Entry[]; -} - -/** - * An entry in the prefix trie (stored in leaf nodes exclusively!) - */ -interface Entry { - /** The actual word form, stored in the trie. */ - content: string; - /** A search key that usually simplifies the word form, for ease of search. */ - key: SearchKey; - weight: number; -} - -/** - * Wrapper class for the trie and its nodes. - */ -class Trie { - public readonly root: Node; - /** The total weight of the entire trie. */ - readonly totalWeight: number; - /** - * Converts arbitrary strings to a search key. The trie is built up of - * search keys; not each entry's word form! - */ - toKey: Wordform2Key; - - constructor(root: Node, totalWeight: number, wordform2key: Wordform2Key) { - this.root = root; - this.toKey = wordform2key; - this.totalWeight = totalWeight; - } - - public traverseFromRoot(): LexiconTraversal { - return new Traversal(this.root, '', this.totalWeight); + firstN(n: number): TextWithProbability[] { + return getSortedResults(this._trie.traverseFromRoot(), n); } /** @@ -475,15 +208,19 @@ class Trie { // priority over anything from its descendants. return directEntries.concat(deduplicated); } +}; - /** - * Returns the top N suggestions from the trie. - * @param n How many suggestions, maximum, to return. - */ - firstN(n: number): TextWithProbability[] { - return getSortedResults(this.traverseFromRoot(), n); - } -} +///////////////////////////////////////////////////////////////////////////////// +// What remains in this file is the trie implementation proper. Note: to // +// reduce bundle size, any functions/methods related to creating the trie have // +// been removed. // +///////////////////////////////////////////////////////////////////////////////// + +/** + * The priority queue will always pop the most probable item - be it a Traversal + * state or a lexical entry reached via Traversal. + */ +type TraversableWithProb = TextWithProbability | LexiconTraversal; /** * Returns all entries matching the given prefix, in descending order of diff --git a/common/models/templates/src/trie.ts b/common/models/templates/src/trie.ts new file mode 100644 index 0000000000..fc3ab7d5bc --- /dev/null +++ b/common/models/templates/src/trie.ts @@ -0,0 +1,244 @@ +import { isHighSurrogate, isSentinel, SearchKey, SENTINEL_CODE_UNIT, Wordform2Key } from "./common.js"; + +// The following trie implementation has been (heavily) derived from trie-ing +// by Conrad Irwin. +// trie-ing is copyright (C) 2015–2017 Conrad Irwin. +// Distributed under the terms of the MIT license: +// https://github.com/ConradIrwin/trie-ing/blob/df55d7af7068d357829db9e0a7faa8a38add1d1d/LICENSE + +export type Node = InternalNode | Leaf; +/** + * An internal node in the trie. Internal nodes NEVER contain entries; if an + * internal node should contain an entry, then it has a dummy leaf node (see + * below), that can be accessed by node.children["\uFDD0"]. + */ +export interface InternalNode { + type: 'internal'; + weight: number; + /** Maintains the keys of children in descending order of weight. */ + values: string[]; // TODO: As an optimization, "values" can be a single string! + /** + * Maps a single UTF-16 code unit to a child node in the trie. This child + * node may be a leaf or an internal node. The keys of this object are kept + * in sorted order in the .values array. + */ + children: { [codeunit: string]: Node }; +} +/** Only leaf nodes actually contain entries (i.e., the words proper). */ +export interface Leaf { + type: 'leaf'; + weight: number; + entries: Entry[]; +} + +/** + * An entry in the prefix trie (stored in leaf nodes exclusively!) + */ +export interface Entry { + /** The actual word form, stored in the trie. */ + content: string; + /** A search key that usually simplifies the word form, for ease of search. */ + key: SearchKey; + weight: number; +} + +export class TrieTraversal implements LexiconTraversal { + /** + * The lexical prefix corresponding to the current traversal state. + */ + prefix: String; + + /** + * The current traversal node. Serves as the 'root' of its own sub-Trie, + * and we cannot navigate back to its parent. + */ + root: Node; + + /** + * The max weight for the Trie being 'traversed'. Needed for probability + * calculations. + */ + totalWeight: number; + + constructor(root: Node, prefix: string, totalWeight: number) { + this.root = root; + this.prefix = prefix; + this.totalWeight = totalWeight; + } + + child(char: USVString): LexiconTraversal | undefined { + /* + Note: would otherwise return the current instance if `char == ''`. If + such a call is happening, it's probably indicative of an implementation + issue elsewhere - let's signal now in order to catch such stuff early. + */ + if(char == '') { + return undefined; + } + + // Split into individual code units. + let steps = char.split(''); + let traversal: ReturnType = this; + + while(steps.length > 0 && traversal) { + const step: string = steps.shift()!; + traversal = traversal._child(step); + } + + return traversal; + } + + // Handles one code unit at a time. + private _child(char: USVString): TrieTraversal | undefined { + const root = this.root; + const totalWeight = this.totalWeight; + const nextPrefix = this.prefix + char; + + if(root.type == 'internal') { + let childNode = root.children[char]; + if(!childNode) { + return undefined; + } + + return new TrieTraversal(childNode, nextPrefix, totalWeight); + } else { + // root.type == 'leaf'; + const legalChildren = root.entries.filter(function(entry) { + return entry.key.indexOf(nextPrefix) == 0; + }); + + if(!legalChildren.length) { + return undefined; + } + + return new TrieTraversal(root, nextPrefix, totalWeight); + } + } + + *children(): Generator<{char: USVString, traversal: () => LexiconTraversal}> { + let root = this.root; + const totalWeight = this.totalWeight; + + if(root.type == 'internal') { + for(let entry of root.values) { + let entryNode = root.children[entry]; + + // UTF-16 astral plane check. + if(isHighSurrogate(entry)) { + // First code unit of a UTF-16 code point. + // For now, we'll just assume the second always completes such a char. + // + // Note: Things get nasty here if this is only sometimes true; in the future, + // we should compile-time enforce that this assumption is always true if possible. + if(entryNode.type == 'internal') { + let internalNode = entryNode; + for(let lowSurrogate of internalNode.values) { + let prefix = this.prefix + entry + lowSurrogate; + yield { + char: entry + lowSurrogate, + traversal: function() { return new TrieTraversal(internalNode.children[lowSurrogate], prefix, totalWeight) } + } + } + } else { + // Determine how much of the 'leaf' entry has no Trie nodes, emulate them. + let fullText = entryNode.entries[0].key; + entry = entry + fullText[this.prefix.length + 1]; // The other half of the non-BMP char. + let prefix = this.prefix + entry; + + yield { + char: entry, + traversal: function () {return new TrieTraversal(entryNode, prefix, totalWeight)} + } + } + } else if(isSentinel(entry)) { + continue; + } else if(!entry) { + // Prevent any accidental 'null' or 'undefined' entries from having an effect. + continue; + } else { + let prefix = this.prefix + entry; + yield { + char: entry, + traversal: function() { return new TrieTraversal(entryNode, prefix, totalWeight)} + } + } + } + + return; + } else { // type == 'leaf' + let prefix = this.prefix; + + let children = root.entries.filter(function(entry) { + return entry.key != prefix && prefix.length < entry.key.length; + }) + + for(let {key} of children) { + let nodeKey = key[prefix.length]; + + if(isHighSurrogate(nodeKey)) { + // Merge the other half of an SMP char in! + nodeKey = nodeKey + key[prefix.length+1]; + } + yield { + char: nodeKey, + traversal: function() { return new TrieTraversal(root, prefix + nodeKey, totalWeight)} + } + }; + return; + } + } + + get entries() { + const totalWeight = this.totalWeight; + const entryMapper = function(value: Entry) { + return { + text: value.content, + p: value.weight / totalWeight + } + } + + if(this.root.type == 'leaf') { + let prefix = this.prefix; + let matches = this.root.entries.filter(function(entry) { + return entry.key == prefix; + }); + + return matches.map(entryMapper); + } else { + let matchingLeaf = this.root.children[SENTINEL_CODE_UNIT]; + if(matchingLeaf && matchingLeaf.type == 'leaf') { + return matchingLeaf.entries.map(entryMapper); + } else { + return []; + } + } + } + + get p(): number { + return this.root.weight / this.totalWeight; + } +} + +/** + * Wrapper class for the trie and its nodes. + */ +export class Trie { + private root: Node; + /** The total weight of the entire trie. */ + readonly totalWeight: number; + /** + * Converts arbitrary strings to a search key. The trie is built up of + * search keys; not each entry's word form! + */ + toKey: Wordform2Key; + + constructor(root: Node, totalWeight: number, wordform2key: Wordform2Key) { + this.root = root; + this.toKey = wordform2key; + this.totalWeight = totalWeight; + } + + public traverseFromRoot(): LexiconTraversal { + return new TrieTraversal(this.root, '', this.totalWeight); + } +} \ No newline at end of file From c16ae2724a312132b63c946fcb16508907c6922e Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Fri, 2 Aug 2024 12:31:24 +0700 Subject: [PATCH 02/26] refactor(developer/compilers): prepare for trie-compiler split --- developer/src/kmc-model/src/build-trie.ts | 30 ++++++++++++----------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/developer/src/kmc-model/src/build-trie.ts b/developer/src/kmc-model/src/build-trie.ts index 8bbc6aaa53..76da23662f 100644 --- a/developer/src/kmc-model/src/build-trie.ts +++ b/developer/src/kmc-model/src/build-trie.ts @@ -291,13 +291,28 @@ namespace Trie { * @returns A JSON-serialiable object that can be given to the TrieModel constructor. */ export function buildTrie(wordlist: WordList, keyFunction: SearchTermToKey): object { - let root = new Trie(keyFunction).buildFromWordList(wordlist).root; + let trie = new Trie(keyFunction); + buildFromWordList(trie, wordlist); + const root = trie.root; return { totalWeight: sumWeights(root), root: root } } + /** + * Populates the trie with the contents of an entire wordlist. + * @param words a list of word and count pairs. + */ + function buildFromWordList(trie: Trie, words: WordList): Trie { + for (let [wordform, weight] of Object.entries(words)) { + let key = trie.toKey(wordform); + addUnsorted(trie.root, { key, weight, content: wordform }, 0); + } + sortTrie(trie.root); + return trie; + } + /** * Wrapper class for the trie and its nodes and wordform to search */ @@ -307,19 +322,6 @@ namespace Trie { constructor(wordform2key: SearchTermToKey) { this.toKey = wordform2key; } - - /** - * Populates the trie with the contents of an entire wordlist. - * @param words a list of word and count pairs. - */ - buildFromWordList(words: WordList): Trie { - for (let [wordform, weight] of Object.entries(words)) { - let key = this.toKey(wordform); - addUnsorted(this.root, { key, weight, content: wordform }, 0); - } - sortTrie(this.root); - return this; - } } // "Constructors" From 54bdf73c93b958df609c1103b07efc98aa5a8374 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Fri, 2 Aug 2024 12:42:54 +0700 Subject: [PATCH 03/26] refactor(common/models): move primary trie-compilation code into common/models/templates We'll likely want to dynamically build a Trie to represent user-specific entries made available by the active OS. We'll then blend _that_ with the 'static' distributed model. --- common/models/templates/src/index.ts | 4 + common/models/templates/src/trie-builder.ts | 155 +++++++++ common/models/templates/src/trie.ts | 28 +- developer/src/kmc-model/src/build-trie.ts | 365 ++++---------------- 4 files changed, 256 insertions(+), 296 deletions(-) create mode 100644 common/models/templates/src/trie-builder.ts diff --git a/common/models/templates/src/index.ts b/common/models/templates/src/index.ts index 8563004fef..b32c37d45a 100644 --- a/common/models/templates/src/index.ts +++ b/common/models/templates/src/index.ts @@ -4,4 +4,8 @@ export { } from "./common.js"; export { default as QuoteBehavior } from "./quote-behavior.js"; export { Tokenization, tokenize, getLastPreCaretToken, wordbreak } from "./tokenization.js"; +export { + Entry, InternalNode, Leaf, Node +} from './trie.js'; +export { addUnsorted, TrieBuilder } from './trie-builder.js'; export { default as TrieModel, TrieModelOptions } from "./trie-model.js"; \ No newline at end of file diff --git a/common/models/templates/src/trie-builder.ts b/common/models/templates/src/trie-builder.ts new file mode 100644 index 0000000000..8b90b5f28d --- /dev/null +++ b/common/models/templates/src/trie-builder.ts @@ -0,0 +1,155 @@ +import { SENTINEL_CODE_UNIT, Wordform2Key } from "./common.js"; +import { Entry, InternalNode, Leaf, Node, Trie } from "./trie.js"; + +function createRootNode(): Node { + return { + type: 'leaf', + weight: 0, + entries: [] + }; +} + +/** + * Adds an entry to the trie. + * + * Note that the trie will likely be unsorted after the add occurs. Before + * performing a lookup on the trie, use call sortTrie() on the root note! + * + * @param node Which node should the entry be added to? + * @param entry the wordform/weight/key to add to the trie + * @param index the index in the key and also the trie depth. Should be set to + * zero when adding onto the root node of the trie. + */ +export function addUnsorted(node: Node, entry: Entry, index: number = 0) { + // Each node stores the MAXIMUM weight out of all of its decesdents, to + // enable a greedy search through the trie. + node.weight = Math.max(node.weight, entry.weight); + + // When should a leaf become an interior node? + // When it already has a value, but the key of the current value is longer + // than the prefix. + if (node.type === 'leaf' && index < entry.key.length && node.entries.length >= 1) { + convertLeafToInternalNode(node, index); + } + + if (node.type === 'leaf') { + // The key matches this leaf node, so add yet another entry. + addItemToLeaf(node, entry); + } else { + // Push the node down to a lower node. + addItemToInternalNode(node, entry, index); + } + + node.unsorted = true; +} + +/** + * Adds an item to the internal node at a given depth. + * @param item + * @param index + */ +function addItemToInternalNode(node: InternalNode, item: Entry, index: number) { + let char = item.key[index]; + // If an internal node is the proper site for item, it belongs under the + // corresponding (sentinel, internal-use) child node signifying this. + if(char == undefined) { + char = SENTINEL_CODE_UNIT; + } + if (!node.children[char]) { + node.children[char] = createRootNode(); + node.values.push(char); + } + addUnsorted(node.children[char], item, index + 1); +} + +function addItemToLeaf(leaf: Leaf, item: Entry) { + leaf.entries.push(item); +} + +/** + * Mutates the given Leaf to turn it into an InternalNode. + * + * NOTE: the node passed in will be DESTRUCTIVELY CHANGED into a different + * type when passed into this function! + * + * @param depth depth of the trie at this level. + */ +function convertLeafToInternalNode(leaf: Leaf, depth: number): void { + let entries = leaf.entries; + + // Alias the current node, as the desired type. + let internal = ( leaf) as InternalNode; + internal.type = 'internal'; + + delete (leaf as Partial).entries; + internal.values = []; + internal.children = {}; + + // Convert the old values array into the format for interior nodes. + for (let item of entries) { + let char: string; + if (depth < item.key.length) { + char = item.key[depth]; + } else { + char = SENTINEL_CODE_UNIT; + } + + if (!internal.children[char]) { + internal.children[char] = createRootNode(); + internal.values.push(char); + } + addUnsorted(internal.children[char], item, depth + 1); + } + + internal.unsorted = true; +} + +/** + * Recursively sort the trie, in descending order of weight. + * @param node any node in the trie + */ +function sortTrie(node: Node) { + if (node.type === 'leaf') { + if (!node.unsorted) { + return; + } + + node.entries.sort(function (a, b) { return b.weight - a.weight; }); + } else { + // We MUST recurse and sort children before returning. + for (let char of node.values) { + sortTrie(node.children[char]); + } + + if (!node.unsorted) { + return; + } + + node.values.sort((a, b) => { + return node.children[b].weight - node.children[a].weight; + }); + } + + delete node.unsorted; +} + +/** + * Wrapper class for the trie and its nodes. + */ +export class TrieBuilder extends Trie { + /** The total weight of the entire trie. */ + totalWeight: number; + + constructor(toKey: Wordform2Key) { + super(createRootNode(), 0, toKey); + this.totalWeight = 0; + } + + sort() { + sortTrie(this.root); + } + + getRoot(): Node { + return this.root; + } +} \ No newline at end of file diff --git a/common/models/templates/src/trie.ts b/common/models/templates/src/trie.ts index fc3ab7d5bc..47dc6fe5dc 100644 --- a/common/models/templates/src/trie.ts +++ b/common/models/templates/src/trie.ts @@ -23,12 +23,22 @@ export interface InternalNode { * in sorted order in the .values array. */ children: { [codeunit: string]: Node }; + + /** + * Used during compilation. + */ + unsorted?: boolean; } /** Only leaf nodes actually contain entries (i.e., the words proper). */ export interface Leaf { type: 'leaf'; weight: number; entries: Entry[]; + + /** + * Used during compilation. + */ + unsorted?: boolean; } /** @@ -88,12 +98,26 @@ export class TrieTraversal implements LexiconTraversal { return traversal; } + private sortNodeIfNeeded(node: Node) { + if(node.unsorted) { + if(node.type == 'leaf') { + node.entries.sort((a, b) => b.weight - a.weight) + } else { + node.values.sort((a, b) => node.children[b].weight - node.children[a].weight); + } + + node.unsorted = false; + } + } + // Handles one code unit at a time. private _child(char: USVString): TrieTraversal | undefined { const root = this.root; const totalWeight = this.totalWeight; const nextPrefix = this.prefix + char; + this.sortNodeIfNeeded(root); + if(root.type == 'internal') { let childNode = root.children[char]; if(!childNode) { @@ -119,6 +143,8 @@ export class TrieTraversal implements LexiconTraversal { let root = this.root; const totalWeight = this.totalWeight; + this.sortNodeIfNeeded(root); + if(root.type == 'internal') { for(let entry of root.values) { let entryNode = root.children[entry]; @@ -223,7 +249,7 @@ export class TrieTraversal implements LexiconTraversal { * Wrapper class for the trie and its nodes. */ export class Trie { - private root: Node; + protected root: Node; /** The total weight of the entire trie. */ readonly totalWeight: number; /** diff --git a/developer/src/kmc-model/src/build-trie.ts b/developer/src/kmc-model/src/build-trie.ts index 76da23662f..efcda3d38c 100644 --- a/developer/src/kmc-model/src/build-trie.ts +++ b/developer/src/kmc-model/src/build-trie.ts @@ -1,6 +1,8 @@ import { ModelCompilerError, ModelCompilerMessageContext, ModelCompilerMessages } from "./model-compiler-messages.js"; import { callbacks } from "./compiler-callbacks.js"; +import { addUnsorted, Node, TrieBuilder } from '@keymanapp/models-templates'; + // Supports LF or CRLF line terminators. const NEWLINE_SEPARATOR = /\u000d?\u000a/; @@ -29,7 +31,7 @@ export function createTrieDataStructure(filenames: string[], searchTermToKey?: ( let wordlist: WordList = {}; filenames.forEach(filename => parseWordListFromFilename(wordlist, filename)); - let trie = Trie.buildTrie(wordlist, searchTermToKey as Trie.SearchTermToKey); + let trie = buildTrie(wordlist, searchTermToKey as SearchTermToKey); return JSON.stringify(trie); } @@ -185,304 +187,77 @@ function* enumerateLines(lines: string[]): Generator { } } -namespace Trie { - /** - * An **opaque** type for a string that is exclusively used as a search key in - * the trie. There should be a function that converts arbitrary strings - * (queries) and converts them into a standard search key for a given language - * model. - * - * Fun fact: This opaque type has ALREADY saved my bacon and found a bug! - */ - type SearchKey = string & { _: 'SearchKey'}; +/** + * An **opaque** type for a string that is exclusively used as a search key in + * the trie. There should be a function that converts arbitrary strings + * (queries) and converts them into a standard search key for a given language + * model. + * + * Fun fact: This opaque type has ALREADY saved my bacon and found a bug! + */ +type SearchKey = string & { _: 'SearchKey'}; - /** - * A function that converts a string (word form or query) into a search key - * (secretly, this is also a string). - */ - export interface SearchTermToKey { - (wordform: string): SearchKey; +/** + * A function that converts a string (word form or query) into a search key + * (secretly, this is also a string). + */ +export interface SearchTermToKey { + (wordform: string): SearchKey; +} + +/** + * Builds a trie from a word list. + * + * @param wordlist The wordlist with non-negative weights. + * @param keyFunction Function that converts word forms into indexed search keys + * @returns A JSON-serialiable object that can be given to the TrieModel constructor. + */ +export function buildTrie(wordlist: WordList, keyFunction: SearchTermToKey): object { + let collater = new TrieBuilder(keyFunction); + + buildFromWordList(collater, wordlist); + return { + totalWeight: sumWeights(collater.getRoot()), + root: collater.getRoot() + } +} + +/** + * Populates the trie with the contents of an entire wordlist. + * @param words a list of word and count pairs. + */ +function buildFromWordList(trieCollator: TrieBuilder, words: WordList): TrieBuilder { + for (let [wordform, weight] of Object.entries(words)) { + let key = trieCollator.toKey(wordform); + addUnsorted(trieCollator.getRoot(), { content: wordform, key, weight }); + } + trieCollator.sort(); + return trieCollator; +} + +/** + * O(n) recursive traversal to sum the total weight of all leaves in the + * trie, starting at the provided node. + * + * @param node The node to start summing weights. + */ +function sumWeights(node: Node): number { + let val: number; + if (node.type === 'leaf') { + val = node.entries + .map(entry => entry.weight) + //.map(entry => isNaN(entry.weight) ? 1 : entry.weight) + .reduce((acc, count) => acc + count, 0); + } else { + val = Object.keys(node.children) + .map((key) => sumWeights(node.children[key])) + .reduce((acc, count) => acc + count, 0); } - // The following trie implementation has been (heavily) derived from trie-ing - // by Conrad Irwin. - // - // trie-ing is distributed under the terms of the MIT license, reproduced here: - // - // The MIT License - // Copyright (c) 2015-2017 Conrad Irwin - // Copyright (c) 2011 Marc Campbell - // - // Permission is hereby granted, free of charge, to any person obtaining a copy - // of this software and associated documentation files (the "Software"), to deal - // in the Software without restriction, including without limitation the rights - // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - // copies of the Software, and to permit persons to whom the Software is - // furnished to do so, subject to the following conditions: - // - // The above copyright notice and this permission notice shall be included in - // all copies or substantial portions of the Software. - // - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - // THE SOFTWARE. - // - // See: https://github.com/ConradIrwin/trie-ing/blob/df55d7af7068d357829db9e0a7faa8a38add1d1d/LICENSE - - /** - * An entry in the prefix trie. The matched word is "content". - */ - interface Entry { - content: string; - key: SearchKey; - weight: number; - } - - /** - * The trie is made up of nodes. A node can be EITHER an internal node (whose - * only children are other nodes) OR a leaf, which actually contains the word - * form entries. - */ - type Node = InternalNode | Leaf; - - /** - * An internal node. - */ - interface InternalNode { - type: 'internal'; - weight: number; - // TODO: As an optimization, "values" can be a single string! - values: string[]; - children: { [codeunit: string]: Node }; - unsorted?: true; - } - - /** - * A leaf node. - */ - interface Leaf { - type: 'leaf'; - weight: number; - entries: Entry[]; - unsorted?: true; - } - - /** - * A sentinel value for when an internal node has contents and requires an - * "internal" leaf. That is, this internal node has content. Instead of placing - * entries as children in an internal node, a "fake" leaf is created, and its - * key is this special internal value. - * - * The value is a valid Unicode BMP code point, but it is a "non-character". - * Unicode will never assign semantics to these characters, as they are - * intended to be used internally as sentinel values. - */ - const INTERNAL_VALUE = '\uFDD0'; - - /** - * Builds a trie from a word list. - * - * @param wordlist The wordlist with non-negative weights. - * @param keyFunction Function that converts word forms into indexed search keys - * @returns A JSON-serialiable object that can be given to the TrieModel constructor. - */ - export function buildTrie(wordlist: WordList, keyFunction: SearchTermToKey): object { - let trie = new Trie(keyFunction); - buildFromWordList(trie, wordlist); - const root = trie.root; - return { - totalWeight: sumWeights(root), - root: root - } - } - - /** - * Populates the trie with the contents of an entire wordlist. - * @param words a list of word and count pairs. - */ - function buildFromWordList(trie: Trie, words: WordList): Trie { - for (let [wordform, weight] of Object.entries(words)) { - let key = trie.toKey(wordform); - addUnsorted(trie.root, { key, weight, content: wordform }, 0); - } - sortTrie(trie.root); - return trie; - } - - /** - * Wrapper class for the trie and its nodes and wordform to search - */ - class Trie { - readonly root = createRootNode(); - toKey: SearchTermToKey; - constructor(wordform2key: SearchTermToKey) { - this.toKey = wordform2key; - } - } - - // "Constructors" - function createRootNode(): Node { - return { - type: 'leaf', - weight: 0, - entries: [] - }; - } - - // Implement Trie creation. - - /** - * Adds an entry to the trie. - * - * Note that the trie will likely be unsorted after the add occurs. Before - * performing a lookup on the trie, use call sortTrie() on the root note! - * - * @param node Which node should the entry be added to? - * @param entry the wordform/weight/key to add to the trie - * @param index the index in the key and also the trie depth. Should be set to - * zero when adding onto the root node of the trie. - */ - function addUnsorted(node: Node, entry: Entry, index: number = 0) { - // Each node stores the MAXIMUM weight out of all of its decesdents, to - // enable a greedy search through the trie. - node.weight = Math.max(node.weight, entry.weight); - - // When should a leaf become an interior node? - // When it already has a value, but the key of the current value is longer - // than the prefix. - if (node.type === 'leaf' && index < entry.key.length && node.entries.length >= 1) { - convertLeafToInternalNode(node, index); - } - - if (node.type === 'leaf') { - // The key matches this leaf node, so add yet another entry. - addItemToLeaf(node, entry); - } else { - // Push the node down to a lower node. - addItemToInternalNode(node, entry, index); - } - - node.unsorted = true; - } - - /** - * Adds an item to the internal node at a given depth. - * @param item - * @param index - */ - function addItemToInternalNode(node: InternalNode, item: Entry, index: number) { - let char = item.key[index]; - // If an internal node is the proper site for item, it belongs under the - // corresponding (sentinel, internal-use) child node signifying this. - if(char == undefined) { - char = INTERNAL_VALUE; - } - if (!node.children[char]) { - node.children[char] = createRootNode(); - node.values.push(char); - } - addUnsorted(node.children[char], item, index + 1); - } - - function addItemToLeaf(leaf: Leaf, item: Entry) { - leaf.entries.push(item); - } - - /** - * Mutates the given Leaf to turn it into an InternalNode. - * - * NOTE: the node passed in will be DESTRUCTIVELY CHANGED into a different - * type when passed into this function! - * - * @param depth depth of the trie at this level. - */ - function convertLeafToInternalNode(leaf: Leaf, depth: number): void { - let entries = leaf.entries; - - // Alias the current node, as the desired type. - let internal = ( leaf) as InternalNode; - internal.type = 'internal'; - - delete leaf.entries; - internal.values = []; - internal.children = {}; - - // Convert the old values array into the format for interior nodes. - for (let item of entries) { - let char: string; - if (depth < item.key.length) { - char = item.key[depth]; - } else { - char = INTERNAL_VALUE; - } - - if (!internal.children[char]) { - internal.children[char] = createRootNode(); - internal.values.push(char); - } - addUnsorted(internal.children[char], item, depth + 1); - } - - internal.unsorted = true; - } - - /** - * Recursively sort the trie, in descending order of weight. - * @param node any node in the trie - */ - function sortTrie(node: Node) { - if (node.type === 'leaf') { - if (!node.unsorted) { - return; - } - - node.entries.sort(function (a, b) { return b.weight - a.weight; }); - } else { - // We MUST recurse and sort children before returning. - for (let char of node.values) { - sortTrie(node.children[char]); - } - - if (!node.unsorted) { - return; - } - - node.values.sort((a, b) => { - return node.children[b].weight - node.children[a].weight; - }); - } - - delete node.unsorted; - } - - /** - * O(n) recursive traversal to sum the total weight of all leaves in the - * trie, starting at the provided node. - * - * @param node The node to start summing weights. - */ - function sumWeights(node: Node): number { - let val: number; - if (node.type === 'leaf') { - val = node.entries - .map(entry => entry.weight) - //.map(entry => isNaN(entry.weight) ? 1 : entry.weight) - .reduce((acc, count) => acc + count, 0); - } else { - val = Object.keys(node.children) - .map((key) => sumWeights(node.children[key])) - .reduce((acc, count) => acc + count, 0); - } - - if(isNaN(val)) { - throw new Error("Unexpected NaN has appeared!"); - } - return val; + if(isNaN(val)) { + throw new Error("Unexpected NaN has appeared!"); } + return val; } /** From 768435365cab644f4e677e2b132c006e0c201f5f Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Thu, 4 Jul 2024 15:14:51 +0700 Subject: [PATCH 04/26] refactor(common/models): refactor migrated trie-construction code for improved encapsulation --- common/models/templates/src/index.ts | 2 +- common/models/templates/src/trie-builder.ts | 20 ++++++++++++- developer/src/kmc-model/src/build-trie.ts | 32 ++------------------- 3 files changed, 23 insertions(+), 31 deletions(-) diff --git a/common/models/templates/src/index.ts b/common/models/templates/src/index.ts index b32c37d45a..b67528a355 100644 --- a/common/models/templates/src/index.ts +++ b/common/models/templates/src/index.ts @@ -7,5 +7,5 @@ export { Tokenization, tokenize, getLastPreCaretToken, wordbreak } from "./token export { Entry, InternalNode, Leaf, Node } from './trie.js'; -export { addUnsorted, TrieBuilder } from './trie-builder.js'; +export { TrieBuilder } from './trie-builder.js'; export { default as TrieModel, TrieModelOptions } from "./trie-model.js"; \ No newline at end of file diff --git a/common/models/templates/src/trie-builder.ts b/common/models/templates/src/trie-builder.ts index 8b90b5f28d..0ac5cfa575 100644 --- a/common/models/templates/src/trie-builder.ts +++ b/common/models/templates/src/trie-builder.ts @@ -20,7 +20,7 @@ function createRootNode(): Node { * @param index the index in the key and also the trie depth. Should be set to * zero when adding onto the root node of the trie. */ -export function addUnsorted(node: Node, entry: Entry, index: number = 0) { +function addUnsorted(node: Node, entry: Entry, index: number = 0) { // Each node stores the MAXIMUM weight out of all of its decesdents, to // enable a greedy search through the trie. node.weight = Math.max(node.weight, entry.weight); @@ -145,6 +145,20 @@ export class TrieBuilder extends Trie { this.totalWeight = 0; } + addEntry(word: string, weight?: number) { + weight = (isNaN(weight ?? NaN) || !weight) ? 1 : weight; + this.totalWeight += weight; + + addUnsorted( + this.root, { + key: this.toKey(word), + content: word, + weight: weight + }, + 0 + ); + } + sort() { sortTrie(this.root); } @@ -152,4 +166,8 @@ export class TrieBuilder extends Trie { getRoot(): Node { return this.root; } + + getTotalWeight(): number { + return this.totalWeight; + } } \ No newline at end of file diff --git a/developer/src/kmc-model/src/build-trie.ts b/developer/src/kmc-model/src/build-trie.ts index efcda3d38c..9e34cd5469 100644 --- a/developer/src/kmc-model/src/build-trie.ts +++ b/developer/src/kmc-model/src/build-trie.ts @@ -1,7 +1,7 @@ import { ModelCompilerError, ModelCompilerMessageContext, ModelCompilerMessages } from "./model-compiler-messages.js"; import { callbacks } from "./compiler-callbacks.js"; -import { addUnsorted, Node, TrieBuilder } from '@keymanapp/models-templates'; +import { TrieBuilder } from '@keymanapp/models-templates'; // Supports LF or CRLF line terminators. const NEWLINE_SEPARATOR = /\u000d?\u000a/; @@ -217,7 +217,7 @@ export function buildTrie(wordlist: WordList, keyFunction: SearchTermToKey): obj buildFromWordList(collater, wordlist); return { - totalWeight: sumWeights(collater.getRoot()), + totalWeight: collater.getTotalWeight(), root: collater.getRoot() } } @@ -228,38 +228,12 @@ export function buildTrie(wordlist: WordList, keyFunction: SearchTermToKey): obj */ function buildFromWordList(trieCollator: TrieBuilder, words: WordList): TrieBuilder { for (let [wordform, weight] of Object.entries(words)) { - let key = trieCollator.toKey(wordform); - addUnsorted(trieCollator.getRoot(), { content: wordform, key, weight }); + trieCollator.addEntry(wordform, weight); } trieCollator.sort(); return trieCollator; } -/** - * O(n) recursive traversal to sum the total weight of all leaves in the - * trie, starting at the provided node. - * - * @param node The node to start summing weights. - */ -function sumWeights(node: Node): number { - let val: number; - if (node.type === 'leaf') { - val = node.entries - .map(entry => entry.weight) - //.map(entry => isNaN(entry.weight) ? 1 : entry.weight) - .reduce((acc, count) => acc + count, 0); - } else { - val = Object.keys(node.children) - .map((key) => sumWeights(node.children[key])) - .reduce((acc, count) => acc + count, 0); - } - - if(isNaN(val)) { - throw new Error("Unexpected NaN has appeared!"); - } - return val; -} - /** * Detects the encoding of a text file. * From f7d2d63e9e7767dd040816b4d4b98aaa8f4a0431 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Fri, 5 Jul 2024 14:54:07 +0700 Subject: [PATCH 05/26] change(common/models): clean up trie construction code, add unit tests --- common/models/templates/src/index.ts | 1 + common/models/templates/src/trie-builder.ts | 26 +- common/models/templates/src/trie.ts | 23 +- .../templates/test/test-trie-builder.js | 257 ++++++++++++++++++ 4 files changed, 281 insertions(+), 26 deletions(-) create mode 100644 common/models/templates/test/test-trie-builder.js diff --git a/common/models/templates/src/index.ts b/common/models/templates/src/index.ts index b67528a355..d4d9e64574 100644 --- a/common/models/templates/src/index.ts +++ b/common/models/templates/src/index.ts @@ -8,4 +8,5 @@ export { Entry, InternalNode, Leaf, Node } from './trie.js'; export { TrieBuilder } from './trie-builder.js'; +export * as trieConstruction from './trie-builder.js'; export { default as TrieModel, TrieModelOptions } from "./trie-model.js"; \ No newline at end of file diff --git a/common/models/templates/src/trie-builder.ts b/common/models/templates/src/trie-builder.ts index 0ac5cfa575..f88956dcbf 100644 --- a/common/models/templates/src/trie-builder.ts +++ b/common/models/templates/src/trie-builder.ts @@ -1,7 +1,7 @@ import { SENTINEL_CODE_UNIT, Wordform2Key } from "./common.js"; import { Entry, InternalNode, Leaf, Node, Trie } from "./trie.js"; -function createRootNode(): Node { +export function createRootNode(): Node { return { type: 'leaf', weight: 0, @@ -10,7 +10,8 @@ function createRootNode(): Node { } /** - * Adds an entry to the trie. + * Adds an entry to the trie. Currently assumes there is no pre-existing match + * for the entry. * * Note that the trie will likely be unsorted after the add occurs. Before * performing a lookup on the trie, use call sortTrie() on the root note! @@ -20,7 +21,7 @@ function createRootNode(): Node { * @param index the index in the key and also the trie depth. Should be set to * zero when adding onto the root node of the trie. */ -function addUnsorted(node: Node, entry: Entry, index: number = 0) { +export function addUnsorted(node: Node, entry: Entry, index: number = 0) { // Each node stores the MAXIMUM weight out of all of its decesdents, to // enable a greedy search through the trie. node.weight = Math.max(node.weight, entry.weight); @@ -48,7 +49,7 @@ function addUnsorted(node: Node, entry: Entry, index: number = 0) { * @param item * @param index */ -function addItemToInternalNode(node: InternalNode, item: Entry, index: number) { +export function addItemToInternalNode(node: InternalNode, item: Entry, index: number) { let char = item.key[index]; // If an internal node is the proper site for item, it belongs under the // corresponding (sentinel, internal-use) child node signifying this. @@ -62,7 +63,7 @@ function addItemToInternalNode(node: InternalNode, item: Entry, index: number) { addUnsorted(node.children[char], item, index + 1); } -function addItemToLeaf(leaf: Leaf, item: Entry) { +export function addItemToLeaf(leaf: Leaf, item: Entry) { leaf.entries.push(item); } @@ -74,7 +75,7 @@ function addItemToLeaf(leaf: Leaf, item: Entry) { * * @param depth depth of the trie at this level. */ -function convertLeafToInternalNode(leaf: Leaf, depth: number): void { +export function convertLeafToInternalNode(leaf: Leaf, depth: number): void { let entries = leaf.entries; // Alias the current node, as the desired type. @@ -108,7 +109,7 @@ function convertLeafToInternalNode(leaf: Leaf, depth: number): void { * Recursively sort the trie, in descending order of weight. * @param node any node in the trie */ -function sortTrie(node: Node) { +export function sortNode(node: Node, onlyLocal?: boolean) { if (node.type === 'leaf') { if (!node.unsorted) { return; @@ -116,9 +117,11 @@ function sortTrie(node: Node) { node.entries.sort(function (a, b) { return b.weight - a.weight; }); } else { - // We MUST recurse and sort children before returning. - for (let char of node.values) { - sortTrie(node.children[char]); + if(!onlyLocal) { + // We recurse and sort children before returning if sorting the full Trie. + for (let char of node.values) { + sortNode(node.children[char], onlyLocal); + } } if (!node.unsorted) { @@ -160,7 +163,8 @@ export class TrieBuilder extends Trie { } sort() { - sortTrie(this.root); + // Sorts the full Trie, not just a part of it. + sortNode(this.root); } getRoot(): Node { diff --git a/common/models/templates/src/trie.ts b/common/models/templates/src/trie.ts index 47dc6fe5dc..7fda0b86b8 100644 --- a/common/models/templates/src/trie.ts +++ b/common/models/templates/src/trie.ts @@ -1,4 +1,5 @@ import { isHighSurrogate, isSentinel, SearchKey, SENTINEL_CODE_UNIT, Wordform2Key } from "./common.js"; +import { sortNode } from "./trie-builder.js"; // The following trie implementation has been (heavily) derived from trie-ing // by Conrad Irwin. @@ -62,7 +63,7 @@ export class TrieTraversal implements LexiconTraversal { * The current traversal node. Serves as the 'root' of its own sub-Trie, * and we cannot navigate back to its parent. */ - root: Node; + readonly root: Node; /** * The max weight for the Trie being 'traversed'. Needed for probability @@ -98,25 +99,15 @@ export class TrieTraversal implements LexiconTraversal { return traversal; } - private sortNodeIfNeeded(node: Node) { - if(node.unsorted) { - if(node.type == 'leaf') { - node.entries.sort((a, b) => b.weight - a.weight) - } else { - node.values.sort((a, b) => node.children[b].weight - node.children[a].weight); - } - - node.unsorted = false; - } - } - // Handles one code unit at a time. private _child(char: USVString): TrieTraversal | undefined { const root = this.root; const totalWeight = this.totalWeight; const nextPrefix = this.prefix + char; - this.sortNodeIfNeeded(root); + // Sorts _just_ the current level, and only if needed. + // We only care about sorting parts that we're actually accessing. + sortNode(root, true); if(root.type == 'internal') { let childNode = root.children[char]; @@ -143,7 +134,9 @@ export class TrieTraversal implements LexiconTraversal { let root = this.root; const totalWeight = this.totalWeight; - this.sortNodeIfNeeded(root); + // Sorts _just_ the current level, and only if needed. + // We only care about sorting parts that we're actually accessing. + sortNode(root, true); if(root.type == 'internal') { for(let entry of root.values) { diff --git a/common/models/templates/test/test-trie-builder.js b/common/models/templates/test/test-trie-builder.js new file mode 100644 index 0000000000..ddf03178af --- /dev/null +++ b/common/models/templates/test/test-trie-builder.js @@ -0,0 +1,257 @@ +/* + * Unit tests for the Trie prediction model. + */ + +import { assert } from 'chai'; +import { SENTINEL_CODE_UNIT, trieConstruction, TrieBuilder } from '@keymanapp/models-templates'; + +describe('trie construction', () => { + it('default root node', () => { + const defaultRoot = trieConstruction.createRootNode(); + assert.equal(defaultRoot.type, 'leaf'); + assert.equal(defaultRoot.weight, 0); + assert.sameMembers(defaultRoot.entries, []); + }); + + it('addItemToLeaf', () => { + // `weight` is managed by addUnsorted, not by addItemToLeaf. + // As a result, we don't test for it here. + const defaultRoot = trieConstruction.createRootNode(); + trieConstruction.addItemToLeaf(defaultRoot, { + content: 'cafe', + weight: 2, + key: 'cafe' + }); + + assert.equal(defaultRoot.type, 'leaf'); + assert.equal(defaultRoot.entries.length, 1); + + trieConstruction.addItemToLeaf(defaultRoot, { + content: 'café', + weight: 1, + key: 'cafe' + }); + + assert.equal(defaultRoot.type, 'leaf'); + assert.equal(defaultRoot.entries.length, 2); + }); + + it('convertLeafToInternalNode', () => { + const defaultRoot = trieConstruction.createRootNode(); + const cafeEntry = { + content: 'cafe', + weight: 5, + key: 'cafe' + }; + trieConstruction.addItemToLeaf(defaultRoot, cafeEntry); + + defaultRoot.weight = 5; + trieConstruction.convertLeafToInternalNode(defaultRoot, 4); + + assert.equal(defaultRoot.type, 'internal'); + assert.sameMembers(defaultRoot.values, [SENTINEL_CODE_UNIT]); + assert.sameMembers(Object.keys(defaultRoot.children), defaultRoot.values); + assert.equal(defaultRoot.weight, 5); + + const sentinelChild = defaultRoot.children[SENTINEL_CODE_UNIT]; + assert.equal(sentinelChild.type, 'leaf'); + assert.equal(sentinelChild.weight, 5); + assert.equal(sentinelChild.entries.length, 1); + // Should be strict-equal too, but we only really care if it's deep-equal. + assert.deepEqual(sentinelChild.entries[0], cafeEntry); + }); + + it('addUnsorted - simple initial entry', () => { + // `weight` is managed by addUnsorted, not by addItemToLeaf. + // As a result, we don't test for it here. + const defaultRoot = trieConstruction.createRootNode(); + trieConstruction.addUnsorted(defaultRoot, { + content: 'cafe', + weight: 2, + key: 'cafe' + }, 4); + + assert.equal(defaultRoot.type, 'leaf'); + assert.equal(defaultRoot.weight, 2); + assert.equal(defaultRoot.entries.length, 1); + + // smaller weight, will not override. Is not additive. + trieConstruction.addUnsorted(defaultRoot, { + content: 'café', + weight: 1, + key: 'cafe' + }, 4); + + assert.equal(defaultRoot.type, 'leaf'); + assert.equal(defaultRoot.weight, 2); + assert.equal(defaultRoot.entries.length, 2); + }); + + it('addUnsorted - short word, then longer word with same prefix', () => { + const defaultRoot = trieConstruction.createRootNode(); + trieConstruction.addUnsorted(defaultRoot, { + content: 'cafe', + weight: 5, + key: 'cafe' + }, 4); + + assert.equal(defaultRoot.type, 'leaf'); + assert.equal(defaultRoot.weight, 5); + assert.equal(defaultRoot.entries.length, 1); + + // smaller weight, will not override. Is not additive. + trieConstruction.addUnsorted(defaultRoot, { + content: 'cafeteria', + weight: 3, + key: 'cafeteria' + }, 4); + + assert.equal(defaultRoot.type, 'internal'); + assert.equal(defaultRoot.weight, 5); + assert.deepEqual(defaultRoot.values, [SENTINEL_CODE_UNIT, 't']); + assert.equal(defaultRoot.children[SENTINEL_CODE_UNIT].weight, 5); + assert.equal(defaultRoot.children['t'].weight, 3); + }); + + it('addUnsorted', () => { + // Perspective: current root is actually representing `ca`. + const defaultRoot = trieConstruction.createRootNode(); + trieConstruction.addUnsorted(defaultRoot, { + content: 'cafe', + weight: 5, + key: 'cafe' + }, 2); + + // There's only one child, so there's no reason to start making + // internal structure just yet. + assert.equal(defaultRoot.type, 'leaf'); + assert.equal(defaultRoot.weight, 5); + + // Adds a second child; that child has more than one letter not-in-common with the first. + trieConstruction.addUnsorted(defaultRoot, { + content: 'call', + weight: 8, + key: 'call' + }, 2); + + assert.equal(defaultRoot.type, 'internal'); + assert.equal(defaultRoot.weight, 8); + assert.sameMembers(defaultRoot.values, ['f', 'l']); + assert.equal(defaultRoot.children['f'].weight, 5); + assert.equal(defaultRoot.children['l'].weight, 8); + }); + + it('addUnsorted - lower frequency prefix', () => { + // Will correspond to 'thin'. + const root = trieConstruction.createRootNode(); + trieConstruction.addUnsorted(root, { + key: 'think', + content: 'think', + weight: 100 + }, 4); + trieConstruction.addUnsorted(root, { + key: 'thing', + content: 'thing', + weight: 40 + }, 4); + + assert.equal(root.type, 'internal'); + assert.sameMembers(root.values, ['k', 'g']); + + // Interesting noted behavior: if just 'think' into 'thin', and nothing else... + // it remains a leaf! That doesn't seem... entirely proper. With 'thing', it + // will at least be an 'internal' node already. + trieConstruction.addUnsorted(root, { + key: 'thin', + content: 'thin', + weight: 20 + }, 4); + + assert.sameMembers(root.values, ['k', 'g', SENTINEL_CODE_UNIT]); + assert.isOk(root.children[SENTINEL_CODE_UNIT]); + assert.equal(root.children[SENTINEL_CODE_UNIT].weight, 20); + }); + + describe('TrieBuilder', () => { + it('standard Trie construction', () => { + const builder = new TrieBuilder((text) => text); + builder.addEntry('caffeine', 2); + builder.addEntry('cafe', 5); + builder.addEntry('calm', 3); + builder.addEntry('calf', 4); + builder.addEntry('call', 6); // total: 20 + builder.addEntry('can', 10); // total: 30 + builder.addEntry('and', 20); // total: 50 + + assert.equal(builder.getTotalWeight(), 50); + + const root = builder.getRoot(); + const aNode = root.children['c'].children['a']; + + // As the nodes were not added in sorted order, they should not currently be ordered. + assert.sameMembers(aNode.values, ['n', 'l', 'f']); + assert.notSameOrderedMembers(aNode.values, ['n', 'l', 'f']); + + const lNode = aNode.children['l']; + assert.sameMembers(lNode.values, ['l', 'f', 'm']); + assert.notSameOrderedMembers(lNode.values, ['l', 'f', 'm']); + + builder.sort(); + + assert.sameOrderedMembers(aNode.values, ['n', 'l', 'f']); + assert.sameOrderedMembers(lNode.values, ['l', 'f', 'm']); + }); + + // In case of high startup-time for user-dictionary processing, this could allow use + // of a 'partially' processed user wordlist. (We'd prioritize predicting when the + // user is interacting with text, then resume processing once 'idle'.) + it('interspersed construction + lookup', () => { + const builder = new TrieBuilder((text) => text); + builder.addEntry('caffeine', 2); + builder.addEntry('cafe', 5); + builder.addEntry('calm', 3); + builder.addEntry('calf', 4); + builder.addEntry('call', 6); // total: 20 + + // ------------------------------------------ + // Pause construction; actually use the Trie. + + assert.equal(builder.getTotalWeight(), 20); + const root = builder.getRoot(); + const caNode = root.children['c'].children['a']; + + // As the nodes were not added in sorted order, they should not currently be ordered. + assert.sameMembers(caNode.values, ['l', 'f']); + assert.notSameOrderedMembers(caNode.values, ['l', 'f']); + + const cal = builder.traverseFromRoot().child('cal'); + // Actually traversing through the node should auto-sort the entries. + assert.sameOrderedMembers(caNode.values, ['l', 'f']); + // Including the reached node's children. + assert.sameOrderedMembers([...cal.children()].map((entry) => entry.char), ['l', 'f', 'm']); + + const cafNode = caNode.children['f']; + + // 'caffeine' was added before 'cafe'. + // Parts not 'traversed' should not be unnecessarily sorted. + assert.notSameOrderedMembers(cafNode.values, ['e', 'f']); + + // ------------------- + // Resume construction + + builder.addEntry('can', 10); // total: 30 + builder.addEntry('and', 20); // total: 50 + + assert.equal(builder.getTotalWeight(), 50); + + // As the nodes were not added in sorted order, they should not currently be ordered. + assert.sameMembers(caNode.values, ['n', 'l', 'f']); + // 'n' was added later, thus will be out of sorted order. + assert.notSameOrderedMembers(caNode.values, ['n', 'l', 'f']); + + builder.sort(); + + assert.sameOrderedMembers(caNode.values, ['n', 'l', 'f']); + }); + }); +}); \ No newline at end of file From 32a31bc693003b84d553e035b88cc5da5c028f91 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Fri, 2 Aug 2024 13:36:07 +0700 Subject: [PATCH 06/26] fix(developer): add 'build' dependency link to models/templates --- developer/src/kmc-model/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/developer/src/kmc-model/build.sh b/developer/src/kmc-model/build.sh index c549648b45..d0cf48817b 100755 --- a/developer/src/kmc-model/build.sh +++ b/developer/src/kmc-model/build.sh @@ -13,7 +13,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" builder_describe "Keyman kmc Lexical Model Compiler module" \ "@/common/web/keyman-version" \ "@/developer/src/common/web/test-helpers" \ - "@/common/models/templates test" \ + "@/common/models/templates" \ "clean" \ "configure" \ "build" \ From 3b563361284f61b76cd09533d514d85a0b10502e Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Fri, 2 Aug 2024 14:11:25 +0700 Subject: [PATCH 07/26] fix(common/models): relocates sortNode to avoid cyclic imports --- common/models/templates/src/trie-builder.ts | 33 +-------------------- common/models/templates/src/trie.ts | 32 +++++++++++++++++++- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/common/models/templates/src/trie-builder.ts b/common/models/templates/src/trie-builder.ts index f88956dcbf..790923a1e4 100644 --- a/common/models/templates/src/trie-builder.ts +++ b/common/models/templates/src/trie-builder.ts @@ -1,5 +1,5 @@ import { SENTINEL_CODE_UNIT, Wordform2Key } from "./common.js"; -import { Entry, InternalNode, Leaf, Node, Trie } from "./trie.js"; +import { Entry, InternalNode, Leaf, Node, Trie, sortNode } from "./trie.js"; export function createRootNode(): Node { return { @@ -105,37 +105,6 @@ export function convertLeafToInternalNode(leaf: Leaf, depth: number): void { internal.unsorted = true; } -/** - * Recursively sort the trie, in descending order of weight. - * @param node any node in the trie - */ -export function sortNode(node: Node, onlyLocal?: boolean) { - if (node.type === 'leaf') { - if (!node.unsorted) { - return; - } - - node.entries.sort(function (a, b) { return b.weight - a.weight; }); - } else { - if(!onlyLocal) { - // We recurse and sort children before returning if sorting the full Trie. - for (let char of node.values) { - sortNode(node.children[char], onlyLocal); - } - } - - if (!node.unsorted) { - return; - } - - node.values.sort((a, b) => { - return node.children[b].weight - node.children[a].weight; - }); - } - - delete node.unsorted; -} - /** * Wrapper class for the trie and its nodes. */ diff --git a/common/models/templates/src/trie.ts b/common/models/templates/src/trie.ts index 7fda0b86b8..7918c8023b 100644 --- a/common/models/templates/src/trie.ts +++ b/common/models/templates/src/trie.ts @@ -1,5 +1,4 @@ import { isHighSurrogate, isSentinel, SearchKey, SENTINEL_CODE_UNIT, Wordform2Key } from "./common.js"; -import { sortNode } from "./trie-builder.js"; // The following trie implementation has been (heavily) derived from trie-ing // by Conrad Irwin. @@ -53,6 +52,37 @@ export interface Entry { weight: number; } +/** + * Recursively sort the trie, in descending order of weight. + * @param node any node in the trie + */ +export function sortNode(node: Node, onlyLocal?: boolean) { + if (node.type === 'leaf') { + if (!node.unsorted) { + return; + } + + node.entries.sort(function (a, b) { return b.weight - a.weight; }); + } else { + if(!onlyLocal) { + // We recurse and sort children before returning if sorting the full Trie. + for (let char of node.values) { + sortNode(node.children[char], onlyLocal); + } + } + + if (!node.unsorted) { + return; + } + + node.values.sort((a, b) => { + return node.children[b].weight - node.children[a].weight; + }); + } + + delete node.unsorted; +} + export class TrieTraversal implements LexiconTraversal { /** * The lexical prefix corresponding to the current traversal state. From 23ffec0c26c1776cc304174b18e63b343a9066e2 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Fri, 12 Jul 2024 10:34:31 +0700 Subject: [PATCH 08/26] refactor(common/models): spin off model template based directly on LexiconTraversals The new `TraversalModel` type inherits almost all code from `TrieModel`, serving as its base class. That said, `TrieModel` will still be responsible for `Trie` loading. --- .../models/templates/src/traversal-model.ts | 287 ++++++++++++++++++ common/models/templates/src/trie-model.ts | 179 +---------- 2 files changed, 295 insertions(+), 171 deletions(-) create mode 100644 common/models/templates/src/traversal-model.ts diff --git a/common/models/templates/src/traversal-model.ts b/common/models/templates/src/traversal-model.ts new file mode 100644 index 0000000000..4d55db1a9d --- /dev/null +++ b/common/models/templates/src/traversal-model.ts @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2019 National Research Council Canada (author: Eddie A. Santos) + * Copyright (c) 2019 SIL International + * Copyright (c) 2015–2017 Conrad Irwin + * Copyright (c) 2011–2015 Marc Campbell + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +// Allows the kmwstring bindings to resolve. +import { extendString, PriorityQueue } from "@keymanapp/web-utils"; +import { default as defaultWordBreaker } from "@keymanapp/models-wordbreakers"; + +import { applyTransform, SearchKey, transformToSuggestion, Wordform2Key } from "./common.js"; +import { getLastPreCaretToken } from "./tokenization.js"; + +extendString(); + +/** + * @file traversal-model.ts + * + * Defines a lexical model based upon the implementation of a `LexiconTraversal`. + */ + +/** Upper bound on the amount of suggestions to generate. */ +const MAX_SUGGESTIONS = 12; + +/** + * Additional arguments to pass into the model, in addition to the model + * parameters themselves. + */ +export interface ModelOptions { + /** + * How to break words in a phrase. + */ + wordBreaker?: WordBreakingFunction; + /** + * This should simplify a search term into a key. + */ + searchTermToKey?: (searchTerm: string) => string; + + /** + * Indicates that the model's written form has 'casing' behavior. + */ + languageUsesCasing?: boolean; + + /** + * Specifies a function used to apply the language's casing rules to a word. + */ + applyCasing?: CasingFunction; + + /** + * Any punctuation to expose to the user. + */ + punctuation?: LexicalModelPunctuation; +} + +/** + * @class TraversalModel + * + * Defines a lexical model based upon the implementation of a `LexiconTraversal`. + */ +export class TraversalModel implements LexicalModel { + configuration?: Configuration; + private rootTraversal: LexiconTraversal; + readonly breakWords: WordBreakingFunction; + readonly punctuation?: LexicalModelPunctuation; + readonly languageUsesCasing?: boolean; + readonly _toKey: Wordform2Key; + + readonly applyCasing?: CasingFunction; + + constructor(rootTraversal: LexiconTraversal, options: ModelOptions = {}) { + this.languageUsesCasing = options.languageUsesCasing; + this.applyCasing = options.applyCasing; + + this.rootTraversal = rootTraversal; + this._toKey = options.searchTermToKey as Wordform2Key || defaultSearchTermToKey + this.breakWords = options.wordBreaker || defaultWordBreaker; + this.punctuation = options.punctuation; + } + + configure(capabilities: Capabilities): Configuration { + return this.configuration = { + leftContextCodePoints: capabilities.maxLeftContextCodePoints, + rightContextCodePoints: capabilities.maxRightContextCodePoints ?? 0 + }; + } + + toKey(text: USVString): USVString { + return this._toKey(text); + } + + predict(transform: Transform, context: Context): Distribution { + // Special-case the empty buffer/transform: return the top suggestions. + if (!transform.insert && !context.left && !context.right && context.startOfBuffer && context.endOfBuffer) { + return makeDistribution(this.firstN(MAX_SUGGESTIONS).map(({text, p}) => ({ + transform: { + insert: text, + deleteLeft: 0 + }, + displayAs: text, + p: p + }))); + } + + // Compute the results of the keystroke: + let newContext = applyTransform(transform, context); + + // Computes the different in word length after applying the transform above. + let leftDelOffset = transform.deleteLeft - transform.insert.kmwLength(); + + // All text to the left of the cursor INCLUDING anything that has + // just been typed. + let prefix = getLastPreCaretToken(this.breakWords, newContext); + + // Return suggestions from the trie. + return makeDistribution(this.lookup(prefix).map(({text, p}) => + transformToSuggestion({ + insert: text, + // Delete whatever the prefix that the user wrote. + deleteLeft: leftDelOffset + prefix.kmwLength() + // Note: a separate capitalization/orthography engine can take this + // result and transform it as needed. + }, + p + ))); + + /* Helper */ + + function makeDistribution(suggestions: WithOutcome[]): Distribution { + let distribution: Distribution = []; + + for(let s of suggestions) { + distribution.push({sample: s, p: s.p}); + } + + return distribution; + } + } + + get wordbreaker(): WordBreakingFunction { + return this.breakWords; + } + + public traverseFromRoot(): LexiconTraversal { + return this.rootTraversal; + } + + /** + * Returns the top N suggestions from the trie. + * @param n How many suggestions, maximum, to return. + */ + firstN(n: number): TextWithProbability[] { + return getSortedResults(this.rootTraversal, n); + } + + /** + * Lookups an arbitrary prefix (a query) in the trie. Returns the top 3 + * results in sorted order. + * + * @param prefix + */ + lookup(prefix: string): TextWithProbability[] { + const searchKey = this.toKey(prefix); + const rootTraversal = this.traverseFromRoot().child(searchKey); + + if(!rootTraversal) { + return []; + } + + const directEntries = rootTraversal.entries; + // `Set` requires Chrome 38+, which is more recent than Chrome 35. + const directSet: Record = {}; + for(const entry of directEntries) { + directSet[entry.text] = entry.text; + } + + const bestEntries = getSortedResults(rootTraversal); + const deduplicated = bestEntries.filter((entry) => !directSet[entry.text]); + + // Any entries directly hosted on the current node should get full display + // priority over anything from its descendants. + return directEntries.concat(deduplicated); + } +}; + +/** + * The priority queue will always pop the most probable item - be it a Traversal + * state or a lexical entry reached via Traversal. + */ +type TraversableWithProb = TextWithProbability | LexiconTraversal; + +/** + * Returns all entries matching the given prefix, in descending order of + * weight. + * + * @param prefix the prefix to match. + * @param results the current results + * @param queue + */ +function getSortedResults(traversal: LexiconTraversal, limit = MAX_SUGGESTIONS): TextWithProbability[] { + let queue = new PriorityQueue(function(a: TraversableWithProb, b: TraversableWithProb) { + // In case of Trie compilation issues that emit `null` or `undefined` + return (b ? b.p : 0) - (a ? a.p : 0); + }); + let results: TextWithProbability[] = []; + + queue.enqueue(traversal); + + while(queue.count > 0) { + const entry = queue.dequeue(); + + if((entry as TextWithProbability)!.text !== undefined) { + const lexicalEntry = entry as TextWithProbability; + results.push(lexicalEntry); + if(results.length >= limit) { + return results; + } + } else { + const traversal = entry as LexiconTraversal; + queue.enqueueAll(traversal.entries); + let children: LexiconTraversal[] = [] + for(let child of traversal.children()) { + children.push(child.traversal()); + } + queue.enqueueAll(children); + } + } + + return results; +} + +/** + * Converts wordforms into an indexable form. It does this by + * normalizing into NFD, removing diacritics, and then converting + * the result to lowercase. + * + * This is a very naïve implementation, that I only think will work on + * some languages that use the Latin script. As of 2020-04-08, only + * 4 out of 11 (36%) of published language models use the Latin script, + * so this might not actually be a great default. + * + * This uses String.prototype.normalize() to convert normalize into NFD. + * NFD is an easy way to separate a Latin character from its diacritics; + * Even then, some Latin-based orthographies use code points that, + * under NFD normalization, do NOT decompose into an ASCII letter and a + * combining diacritical mark (e.g., SENĆOŦEN). + * + * Use this only in early iterations of the model. For a production lexical + * model, you SHOULD write/generate your own key function, tailored to your + * language. + */ +function defaultSearchTermToKey(wordform: string): SearchKey { + /** + * N.B.: this is (slightly) DIFFERENT than the version in + * keymanapp/lexical-model-compiler/build-trie + * as this is for compatibility for models built + * BEFORE the searchTermToKey function was bundled with + * all models. + * + * This compatibility version lowercases AFTER removing diacritics; + * the new version (bundled in future models) lowercases, + * NFD normalizes, THEN removes diacritics. + */ + return wordform + .normalize('NFD') + // Remove all combining diacritics (if input is in NFD) + // common to Latin-orthographies. + .replace(/[\u0300-\u036f]/g, '') + .toLowerCase() as SearchKey; +} diff --git a/common/models/templates/src/trie-model.ts b/common/models/templates/src/trie-model.ts index 0a48d673a3..9e5ee09318 100644 --- a/common/models/templates/src/trie-model.ts +++ b/common/models/templates/src/trie-model.ts @@ -26,12 +26,11 @@ // Should probably make a 'lm-utils' submodule. // Allows the kmwstring bindings to resolve. -import { extendString, PriorityQueue } from "@keymanapp/web-utils"; -import { default as defaultWordBreaker } from "@keymanapp/models-wordbreakers"; +import { extendString } from "@keymanapp/web-utils"; -import { applyTransform, SearchKey, transformToSuggestion, Wordform2Key } from "./common.js"; +import { SearchKey, Wordform2Key } from "./common.js"; import { Node, Trie } from './trie.js'; -import { getLastPreCaretToken } from "./tokenization.js"; +import { TraversalModel } from './traversal-model.js'; extendString(); @@ -41,9 +40,6 @@ extendString(); * Defines a simple word list (unigram) model. */ -/** Upper bound on the amount of suggestions to generate. */ -const MAX_SUGGESTIONS = 12; - /** * Additional arguments to pass into the model, in addition to the model * parameters themselves. @@ -83,185 +79,26 @@ export interface TrieModelOptions { * prefix searches within words, however they are not very good * at predicting the next word. */ -export default class TrieModel implements LexicalModel { +export default class TrieModel extends TraversalModel implements LexicalModel { configuration?: Configuration; - private _trie: Trie; - readonly breakWords: WordBreakingFunction; + public readonly trie: Trie; readonly punctuation?: LexicalModelPunctuation; readonly languageUsesCasing?: boolean; readonly applyCasing?: CasingFunction; constructor(trieData: {root: Node, totalWeight: number}, options: TrieModelOptions = {}) { - this.languageUsesCasing = options.languageUsesCasing; - this.applyCasing = options.applyCasing; - - this._trie = new Trie( + const trie = new Trie( trieData['root'], trieData['totalWeight'], options.searchTermToKey as Wordform2Key || defaultSearchTermToKey ); - this.breakWords = options.wordBreaker || defaultWordBreaker; - this.punctuation = options.punctuation; - } - configure(capabilities: Capabilities): Configuration { - return this.configuration = { - leftContextCodePoints: capabilities.maxLeftContextCodePoints, - rightContextCodePoints: capabilities.maxRightContextCodePoints ?? 0 - }; - } - - toKey(text: USVString): USVString { - return this._trie.toKey(text); - } - - predict(transform: Transform, context: Context): Distribution { - // Special-case the empty buffer/transform: return the top suggestions. - if (!transform.insert && !context.left && !context.right && context.startOfBuffer && context.endOfBuffer) { - return makeDistribution(this.firstN(MAX_SUGGESTIONS).map(({text, p}) => ({ - transform: { - insert: text, - deleteLeft: 0 - }, - displayAs: text, - p: p - }))); - } - - // Compute the results of the keystroke: - let newContext = applyTransform(transform, context); - - // Computes the different in word length after applying the transform above. - let leftDelOffset = transform.deleteLeft - transform.insert.kmwLength(); - - // All text to the left of the cursor INCLUDING anything that has - // just been typed. - let prefix = getLastPreCaretToken(this.breakWords, newContext); - - // Return suggestions from the trie. - return makeDistribution(this.lookup(prefix).map(({text, p}) => - transformToSuggestion({ - insert: text, - // Delete whatever the prefix that the user wrote. - deleteLeft: leftDelOffset + prefix.kmwLength() - // Note: a separate capitalization/orthography engine can take this - // result and transform it as needed. - }, - p - ))); - - /* Helper */ - - function makeDistribution(suggestions: WithOutcome[]): Distribution { - let distribution: Distribution = []; - - for(let s of suggestions) { - distribution.push({sample: s, p: s.p}); - } - - return distribution; - } - } - - get wordbreaker(): WordBreakingFunction { - return this.breakWords; - } - - public traverseFromRoot(): LexiconTraversal { - return this._trie.traverseFromRoot(); - } - - /** - * Returns the top N suggestions from the trie. - * @param n How many suggestions, maximum, to return. - */ - firstN(n: number): TextWithProbability[] { - return getSortedResults(this._trie.traverseFromRoot(), n); - } - - /** - * Lookups an arbitrary prefix (a query) in the trie. Returns the top 3 - * results in sorted order. - * - * @param prefix - */ - lookup(prefix: string): TextWithProbability[] { - const searchKey = this.toKey(prefix); - const rootTraversal = this.traverseFromRoot().child(searchKey); - - if(!rootTraversal) { - return []; - } - - const directEntries = rootTraversal.entries; - // `Set` requires Chrome 38+, which is more recent than Chrome 35. - const directSet: Record = {}; - for(const entry of directEntries) { - directSet[entry.text] = entry.text; - } - - const bestEntries = getSortedResults(rootTraversal); - const deduplicated = bestEntries.filter((entry) => !directSet[entry.text]); - - // Any entries directly hosted on the current node should get full display - // priority over anything from its descendants. - return directEntries.concat(deduplicated); + super(trie.traverseFromRoot(), options); + this.trie = trie; } }; -///////////////////////////////////////////////////////////////////////////////// -// What remains in this file is the trie implementation proper. Note: to // -// reduce bundle size, any functions/methods related to creating the trie have // -// been removed. // -///////////////////////////////////////////////////////////////////////////////// - -/** - * The priority queue will always pop the most probable item - be it a Traversal - * state or a lexical entry reached via Traversal. - */ -type TraversableWithProb = TextWithProbability | LexiconTraversal; - -/** - * Returns all entries matching the given prefix, in descending order of - * weight. - * - * @param prefix the prefix to match. - * @param results the current results - * @param queue - */ -function getSortedResults(traversal: LexiconTraversal, limit = MAX_SUGGESTIONS): TextWithProbability[] { - let queue = new PriorityQueue(function(a: TraversableWithProb, b: TraversableWithProb) { - // In case of Trie compilation issues that emit `null` or `undefined` - return (b ? b.p : 0) - (a ? a.p : 0); - }); - let results: TextWithProbability[] = []; - - queue.enqueue(traversal); - - while(queue.count > 0) { - const entry = queue.dequeue(); - - if((entry as TextWithProbability)!.text !== undefined) { - const lexicalEntry = entry as TextWithProbability; - results.push(lexicalEntry); - if(results.length >= limit) { - return results; - } - } else { - const traversal = entry as LexiconTraversal; - queue.enqueueAll(traversal.entries); - let children: LexiconTraversal[] = [] - for(let child of traversal.children()) { - children.push(child.traversal()); - } - queue.enqueueAll(children); - } - } - - return results; -} - /** * Converts wordforms into an indexable form. It does this by * normalizing into NFD, removing diacritics, and then converting From 04d74fa9fab65e0e4fba04a449edb510a5b2a357 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Thu, 25 Jul 2024 14:38:10 +0700 Subject: [PATCH 09/26] refactor(common/models): convert traversal.children method to return array --- common/models/templates/src/trie.ts | 42 ++++++++++++------- .../templates/test/test-trie-traversal.js | 19 +++------ common/models/types/index.d.ts | 2 +- 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/common/models/templates/src/trie.ts b/common/models/templates/src/trie.ts index 7918c8023b..134dce788a 100644 --- a/common/models/templates/src/trie.ts +++ b/common/models/templates/src/trie.ts @@ -160,10 +160,12 @@ export class TrieTraversal implements LexiconTraversal { } } - *children(): Generator<{char: USVString, traversal: () => LexiconTraversal}> { + children(): {char: USVString, p: number, traversal: () => LexiconTraversal}[] { let root = this.root; const totalWeight = this.totalWeight; + const results: {char: USVString, p: number, traversal: () => LexiconTraversal}[] = []; + // Sorts _just_ the current level, and only if needed. // We only care about sorting parts that we're actually accessing. sortNode(root, true); @@ -183,21 +185,26 @@ export class TrieTraversal implements LexiconTraversal { let internalNode = entryNode; for(let lowSurrogate of internalNode.values) { let prefix = this.prefix + entry + lowSurrogate; - yield { + results.push({ char: entry + lowSurrogate, + p: internalNode.children[lowSurrogate].weight / totalWeight, traversal: function() { return new TrieTraversal(internalNode.children[lowSurrogate], prefix, totalWeight) } - } + }); } } else { - // Determine how much of the 'leaf' entry has no Trie nodes, emulate them. + // No deduplication needed. If there were multiple keys available, + // we wouldn't be in a leaf node. let fullText = entryNode.entries[0].key; entry = entry + fullText[this.prefix.length + 1]; // The other half of the non-BMP char. let prefix = this.prefix + entry; - yield { + const weight = entryNode.entries.reduce((best, current) => Math.max(best, current.weight), 0); + + results.push({ char: entry, + p: weight / totalWeight, traversal: function () {return new TrieTraversal(entryNode, prefix, totalWeight)} - } + }); } } else if(isSentinel(entry)) { continue; @@ -206,34 +213,41 @@ export class TrieTraversal implements LexiconTraversal { continue; } else { let prefix = this.prefix + entry; - yield { + results.push({ char: entry, + p: entryNode.weight / totalWeight, traversal: function() { return new TrieTraversal(entryNode, prefix, totalWeight)} - } + }); } } - return; + return results; } else { // type == 'leaf' let prefix = this.prefix; + // No actual deduplication needed. If there were multiple keys available, + // we wouldn't be in a leaf node. let children = root.entries.filter(function(entry) { return entry.key != prefix && prefix.length < entry.key.length; - }) + }); - for(let {key} of children) { + let weight = children.reduce((best, current) => Math.max(current.weight, best), 0); + + if(children.length > 0) { + const key = children[0].content; let nodeKey = key[prefix.length]; if(isHighSurrogate(nodeKey)) { // Merge the other half of an SMP char in! nodeKey = nodeKey + key[prefix.length+1]; } - yield { + results.push({ char: nodeKey, + p: weight / totalWeight, traversal: function() { return new TrieTraversal(root, prefix + nodeKey, totalWeight)} - } + }); }; - return; + return results; } } diff --git a/common/models/templates/test/test-trie-traversal.js b/common/models/templates/test/test-trie-traversal.js index 09237ee9a0..891a9da67b 100644 --- a/common/models/templates/test/test-trie-traversal.js +++ b/common/models/templates/test/test-trie-traversal.js @@ -174,15 +174,13 @@ describe('Trie traversal abstractions', function() { do { assert.isNotEmpty(leafChildSequence); - let iter = curChild.traversal().children(); - let curr = iter.next(); - curChild = curr.value; + let children = curChild.traversal().children(); + assert.equal(children.length, 1); + curChild = children[0]; // Test generator behavior - there should be one child, then the 'done' state. assert.isDefined(curChild); assert.equal(curChild.char, leafChildSequence[0]); - curr = iter.next(); - assert.isTrue(curr.done); // Prepare for iteration. leafChildSequence.shift(); @@ -275,15 +273,13 @@ describe('Trie traversal abstractions', function() { do { assert.isNotEmpty(leafChildSequence); - let iter = curChild.traversal().children(); - let curr = iter.next(); - curChild = curr.value; + let children = curChild.traversal().children(); + assert.equal(children.length, 1); + curChild = children[0]; // Test generator behavior - there should be one child, then the 'done' state. assert.isDefined(curChild); assert.equal(curChild.char, leafChildSequence[0]); - curr = iter.next(); - assert.isTrue(curr.done); // Prepare for iteration. leafChildSequence.shift(); @@ -337,9 +333,6 @@ describe('Trie traversal abstractions', function() { // Just to be sure our utility function is working right. assert.equal(smpA + smpP + 'pl' + smpE, "𝖺𝗉pl𝖾"); - let pKeys = ['p', smpP]; - let leafChildSequence = ['l', smpE]; - const aNode = rootTraversal.child(smpA); assert.isOk(aNode); assert.isNotOk(rootTraversal.child('a')); diff --git a/common/models/types/index.d.ts b/common/models/types/index.d.ts index 0d6223bc64..b082413f5d 100644 --- a/common/models/types/index.d.ts +++ b/common/models/types/index.d.ts @@ -67,7 +67,7 @@ declare interface LexiconTraversal { * - If `char` = 'e', the child represents a prefix of 'the'. * - Then `traversal` allows traversing the part of the lexicon prefixed by 'the'. */ - children(): Generator<{char: USVString, traversal: () => LexiconTraversal}>; + children(): {char: USVString, p: number, traversal: () => LexiconTraversal}[]; /** * Allows direct access to the traversal state that results when appending one From daaf6b16746c11582f7f6074aa1e4a5bda566726 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Mon, 15 Jul 2024 12:02:00 +0700 Subject: [PATCH 10/26] feat(web): start implementation for traversal compositing --- common/models/types/index.d.ts | 31 +++++---- .../src/main/models/composited-traversal.ts | 67 +++++++++++++++++++ 2 files changed, 86 insertions(+), 12 deletions(-) create mode 100644 common/web/lm-worker/src/main/models/composited-traversal.ts diff --git a/common/models/types/index.d.ts b/common/models/types/index.d.ts index b082413f5d..b5bb977aed 100644 --- a/common/models/types/index.d.ts +++ b/common/models/types/index.d.ts @@ -44,28 +44,35 @@ type TextWithProbability = { */ declare interface LexiconTraversal { /** - * Provides an iterable pattern used to search for words with a 'keyed' prefix matching - * the current traversal state's prefix when a new character is appended. Iterating - * across `children` provides 'breadth' to a lexical search. + * Provides an iterable pattern used to search for words with a 'keyed' prefix + * matching the current traversal state's prefix when a new character is + * appended. Iterating across `children` provides 'breadth' to a lexical + * search. * - * For an example with English, if the current traversal state corresponds to 'th', - * children() may return an iterator with states corresponding 'e' - * (for 'the', 'then', 'there'), 'a' (for 'than', 'that'), etc. + * For an example with English, if the current traversal state corresponds to + * 'th', children() may return an iterator with states corresponding 'e' (for + * 'the', 'then', 'there'), 'a' (for 'than', 'that'), etc. * - * @param char A full character (a UTF-16 code point, which may be comprised of two - * code units) corresponding to the child node, appended to the current - * node's prefix to produce the child node's prefix. + * @param char A full character (a UTF-16 code point, which may be + * comprised of two code units) corresponding to the child + * node, appended to the current node's prefix to produce the + * child node's prefix. *

* Example: if the current traversal's represented prefix is 'th', * char = 'e' would indicate a child node with prefix = 'the'. - * @param traversal a LexiconTraversal starting from (or "rooted at") the child node. Use - * of the returned object provides 'depth' to a lexical search. + * @param traversal a LexiconTraversal starting from (or "rooted at") the + * child node. Use of the returned object provides 'depth' + * to a lexical search. *

* Example: * * - Suppose our current LexiconTraversal represents a prefix of 'th'. * - If `char` = 'e', the child represents a prefix of 'the'. - * - Then `traversal` allows traversing the part of the lexicon prefixed by 'the'. + * - Then `traversal` allows traversing the part of the lexicon prefixed by + * 'the'. + * + * Note that there is no requirement for the ordering of the returned entries; + * no sorting of any kind is required. */ children(): {char: USVString, p: number, traversal: () => LexiconTraversal}[]; diff --git a/common/web/lm-worker/src/main/models/composited-traversal.ts b/common/web/lm-worker/src/main/models/composited-traversal.ts new file mode 100644 index 0000000000..deb83b8614 --- /dev/null +++ b/common/web/lm-worker/src/main/models/composited-traversal.ts @@ -0,0 +1,67 @@ +export class CompositedTraversal implements LexiconTraversal { + private readonly componentTraversals: Distribution; + + constructor(traversalsWithWeighting: Distribution) { + let totalP = traversalsWithWeighting.reduce((accum, current) => accum + current.p, 0); + this.componentTraversals = traversalsWithWeighting.map((entry) => { + // Shallow-copy the traversals while normalizing the associated weightings. + // The sum of all `p` values should sum to 1. + return { + sample: entry.sample, + p: entry.p / totalP + }; + }); + } + + // Eww... mixing these in generator-mode might be an 'issue'. Esp if trying + // to guarantee sorted order among them. + children(): Generator<{ char: string; traversal: () => LexiconTraversal; }, any, unknown> { + throw new Error("Method not implemented."); + } + + child(char: string): LexiconTraversal { + const children = this.componentTraversals.map((traversalTuple) => { + return { + sample: traversalTuple.sample.child(char), + p: traversalTuple.p + }; + }); + + children.filter((entry) => !!entry.sample); + + if(children.length == 0) { + return undefined; + } else { + return new CompositedTraversal(children); + } + } + + get entries(): TextWithProbability[] { + // TODO: Optimization? + // - if each is already sorted, just... zipper-merge the two arrays. + // - Why O(N log N) when we can just O(N)? + // - granted, it's not like we expect large arrays here. + const results = this.componentTraversals.flatMap((traversalTuple) => { + return traversalTuple.sample.entries.map((entry) => { + const reweightedEntry: TextWithProbability = { + text: entry.text, + p: entry.p * traversalTuple.p + }; + + return reweightedEntry; + }); + }); + + results.sort((a, b) => b.p - a.p); + + return results; + } + + get p(): number { + return this.componentTraversals + // to 'traversal weighting' * 'traversal node's max probability' + .map((entry) => entry.p * entry.sample.p) + // find the best entry post-weighting + .reduce((best, current) => (current > best) ? current : best, 0); + } +} \ No newline at end of file From f67dceb4189d20a41539476f71e24d8340b4f11f Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Mon, 15 Jul 2024 12:04:46 +0700 Subject: [PATCH 11/26] feat(web): initial implementation for traversal compositing - blending multiple models into one --- .../src/main/models/composited-traversal.ts | 217 ++++++++++++++++-- .../test/mocha/cases/composited-traversal.js | 172 ++++++++++++++ 2 files changed, 375 insertions(+), 14 deletions(-) create mode 100644 common/web/lm-worker/src/test/mocha/cases/composited-traversal.js diff --git a/common/web/lm-worker/src/main/models/composited-traversal.ts b/common/web/lm-worker/src/main/models/composited-traversal.ts index deb83b8614..7db796641b 100644 --- a/common/web/lm-worker/src/main/models/composited-traversal.ts +++ b/common/web/lm-worker/src/main/models/composited-traversal.ts @@ -1,47 +1,146 @@ +import { PriorityQueue } from "@keymanapp/web-utils"; + export class CompositedTraversal implements LexiconTraversal { private readonly componentTraversals: Distribution; + // Used both internally and externally - and we don't want to re-weight internal calls! constructor(traversalsWithWeighting: Distribution) { - let totalP = traversalsWithWeighting.reduce((accum, current) => accum + current.p, 0); + // let totalP = traversalsWithWeighting.reduce((accum, current) => accum + current.p, 0); this.componentTraversals = traversalsWithWeighting.map((entry) => { // Shallow-copy the traversals while normalizing the associated weightings. // The sum of all `p` values should sum to 1. return { sample: entry.sample, - p: entry.p / totalP + p: entry.p // totalP }; }); } - // Eww... mixing these in generator-mode might be an 'issue'. Esp if trying - // to guarantee sorted order among them. - children(): Generator<{ char: string; traversal: () => LexiconTraversal; }, any, unknown> { - throw new Error("Method not implemented."); + children(): { char: string; p: number, traversal: () => LexiconTraversal }[] { + // Easy case: when there's no source on the current Traversal path, there + // are no children. + // + // Though... we shouldn't even reach this point; if a child path lacks + // support; `undefined` should have been returned instead of a class + // instance. + + /* c8 ignore start */ + if(this.componentTraversals.length == 0) { + return []; + } + /* c8 ignore stop */ + + // If there were no duplicated children, merging the arrays returned from the + // following statement, as is, would be the correct answer. + const unblendedComponents = this.componentTraversals.map((component) => { + const weight = component.p; + return { + sample: component.sample.children().map((entry) => { + return { + char: entry.char, + p: entry.p, + traversal: () => entry.traversal() + }; + }), + // Make sure to track the model-source's weighting! + weight: weight + }; + }); + + // If there's only a single active component, well, there actually are + // no duplicates - thus it is the right answer. + if(unblendedComponents.length == 1) { + const singleSource = unblendedComponents[0]; + return singleSource.sample.map((child) => { + return { + char: child.char, + p: singleSource.weight * child.p, + traversal: () => new CompositedTraversal([{ sample: child.traversal(), p: singleSource.weight }]) + }; + }); + } + + // Now, the tricky part: actual blending. + + // Find sets of traversals that traverse the same char path after the represented prefix. + const componentBuckets: Record LexiconTraversal}, weight: number }[]> = {}; + unblendedComponents.forEach((componentChildren) => { + componentChildren.sample.forEach((entry) => { + componentBuckets[entry.char] ??= []; + componentBuckets[entry.char].push({ + component: entry, + weight: componentChildren.weight + }); + }); + }); + + // For each char path... + const keys = Object.keys(componentBuckets); + return keys.map((key) => { + const bucket = componentBuckets[key]; + // Build the blended traversal for that path + const traversalFunc = () => { + const traversalsWithWeighting = bucket.map((entry) => { + return { + sample: entry.component.traversal(), + // Apply the model's weighting! + // (This is not the `p` for that model's traversal path.) + p: entry.weight + }; + }); + return new CompositedTraversal(traversalsWithWeighting); + }; + + // If there's only one contributing model, there's no need to worry about duplicates + // affecting the "highest probability" value for the path; no blending needed. + // + // It's thus safe to just return the model's probability for the path. + if(bucket.length == 1) { + return { + char: key, + p: bucket[0].weight * bucket[0].component.p, + traversal: traversalFunc + }; + } else { + // Here, there's a non-zero chance that duplicate entries may exist. + // We thus need to worry about the potential for blending... + // which means we're best off delegating "highest probability" calculation + // to the child, which is the level at which the issue may be resolved. + const traversal = traversalFunc(); + + return { + char: key, + // Use the traversal early to ensure duplicate entries aren't improperly + // neglected due to their probability sources being split. + p: traversal.p, + // We've actually built it already, so just re-use the instance. + traversal: () => traversal + }; + } + }); } child(char: string): LexiconTraversal { - const children = this.componentTraversals.map((traversalTuple) => { + let children = this.componentTraversals.map((traversalTuple) => { return { sample: traversalTuple.sample.child(char), p: traversalTuple.p }; }); - children.filter((entry) => !!entry.sample); + children = children.filter((entry) => !!entry.sample); if(children.length == 0) { return undefined; } else { + // Will sort out blending on its own. return new CompositedTraversal(children); } } get entries(): TextWithProbability[] { - // TODO: Optimization? - // - if each is already sorted, just... zipper-merge the two arrays. - // - Why O(N log N) when we can just O(N)? - // - granted, it's not like we expect large arrays here. - const results = this.componentTraversals.flatMap((traversalTuple) => { + // If there were guaranteed to be no duplicates, .flatMap instead of .map would be fine here. + const entrySets = this.componentTraversals.map((traversalTuple) => { return traversalTuple.sample.entries.map((entry) => { const reweightedEntry: TextWithProbability = { text: entry.text, @@ -52,16 +151,106 @@ export class CompositedTraversal implements LexiconTraversal { }); }); + // Combine any duplicated entries. + const resultMap: Record = {}; + entrySets.forEach((set) => { + set.forEach((entry) => { + if(resultMap[entry.text]) { + resultMap[entry.text].p += entry.p; + } else { + resultMap[entry.text] = entry; + } + }); + }); + + // Finalize as array + sort by descending probability + const results = Object.values(resultMap); results.sort((a, b) => b.p - a.p); return results; } get p(): number { - return this.componentTraversals + // If it is impossible for a blended traversal path to yield a higher- + // probability entry than the best unblended result, there's no point + // in considering it here. + const bestUnblendedProb = this.componentTraversals // to 'traversal weighting' * 'traversal node's max probability' .map((entry) => entry.p * entry.sample.p) // find the best entry post-weighting .reduce((best, current) => (current > best) ? current : best, 0); + + // Easy case: 1 component at this level? No blending is possible, + // so just return the best entry found this way. + if(this.componentTraversals.length == 1) { + return bestUnblendedProb; + } + + // Check for traversal children that travel the same path. O(sum of child + // nodes across all components) + // + // If we wanted to try optimizing this... + // - priority queue for each componentTraversal, descending prob order + // - track which sources have contributed while accumulating path probs + // - if best entry from sources not contributed + current accumulation can't + // win, we can stop blending early. + // - further iterations can no longer exceed bestUnblendedProb. + // + // This likely avoids iterating over all paths... but invoking priority + // queues means worst-case O(sum N_i log N_i), which is worse. (With N_i = + // path count per component traversal) + // - constructable "worst case": fully balanced pMaxes? + // - for "good cases"... would the complexity even be worth it? + // - suppose 3 sources + English: worst-case iteration count with current + // setup is 4 * 26 = 104. That's not "terrible"; fairly low to be + // considering big-O optimizations. + // - +1 multiplier due to the following `filter` block. + const childBuckets: Record = {}; + const entryBuckets: Record = {}; + this.componentTraversals.forEach((entry) => { + const weight = entry.p; + const component = entry.sample; + const entries = component.entries; + + component.children().forEach((child) => { + const record = childBuckets[child.char] ??= { pMax: 0, char: child.char }; + record.pMax += weight * child.p; + }); + + entries.forEach((entry) => entryBuckets[entry.text] = (entryBuckets[entry.text] ?? 0) + entry.p * weight); + }); + + // If a path's maximum potential probability does not exceed the best entry, + // it can't affect our answer; skip it. + // Is O(alphabet count) + const potentialBlendedChildren = Object.keys(childBuckets).map((char) => { + const record = childBuckets[char]; + if(record.pMax <= bestUnblendedProb) { + return null; + } else { + return record; + } + }).filter((record) => !!record); + + let bestBlendedEntry = Object.values(entryBuckets).reduce((best, current) => Math.max(best, current), 0); + + // Prep the path-search for a potential winner. + let bestProb = Math.max(bestUnblendedProb, bestBlendedEntry); + // May have zero entries if no blended path can exceed the best non-blended path's probability. + // (Assumption: this is the common case.) + const queue = new PriorityQueue((a, b) => b.pMax - a.pMax, potentialBlendedChildren); + + while(queue.peek()?.pMax > bestProb) { + const entry = queue.dequeue(); + const childPath = this.child(entry.char); + + // Note: this pattern, as-is, does not allow cross-node optimizations + // (such as seeing a better potential here once we've started evaluating + // potentials within the child) + // + // It's a reasonable "first pass" though. + bestProb = Math.max(childPath.p, bestProb); + } + return bestProb; } } \ No newline at end of file diff --git a/common/web/lm-worker/src/test/mocha/cases/composited-traversal.js b/common/web/lm-worker/src/test/mocha/cases/composited-traversal.js new file mode 100644 index 0000000000..6772aaea57 --- /dev/null +++ b/common/web/lm-worker/src/test/mocha/cases/composited-traversal.js @@ -0,0 +1,172 @@ +import { assert } from 'chai'; + +import { CompositedTraversal } from '#./models/composited-traversal.js'; +import { TrieBuilder } from '@keymanapp/models-templates'; + +const simpleLexicalModel = (function() { + const builder = new TrieBuilder((str) => str); + + // Sum total: 10. + builder.addEntry('are', 1); + builder.addEntry('and', 2); + + builder.addEntry('not', 1); + + builder.addEntry('the', 3); + builder.addEntry('try', 1); + builder.addEntry('to', 1); + builder.addEntry('tom', 1); + + return builder; +}()); + +const namesModel = (function() { + const builder = new TrieBuilder((str) => str); + + builder.addEntry('amy', 1); + builder.addEntry('bob', 1); + builder.addEntry('mark', 1); + builder.addEntry('tom', 1); + + return builder; +}()); + +const blendedModel = new CompositedTraversal([ + { sample: simpleLexicalModel.traverseFromRoot(), p: 0.5 }, + { sample: namesModel.traverseFromRoot(), p: 0.5 } +]); + +describe('CompositedTraversal', () => { + describe('direct prefix lookup', () => { + it('returns undefined for non-existent paths', () => { + assert.isUndefined(blendedModel.child('tomcat')); + assert.isUndefined(blendedModel.child('tone')); + assert.isUndefined(blendedModel.child('ton')); + assert.isUndefined(blendedModel.child('apple')); + assert.isUndefined(blendedModel.child('aim')); + assert.isUndefined(blendedModel.child('xylophone')); + }); + + it('reweighs entries found in only one source', () => { + const andNode = blendedModel.child('and'); + assert.equal(andNode.p, 0.1); + + assert.equal(andNode.entries.length, 1); + const and = andNode.entries[0]; + assert.equal(and.text, 'and'); + assert.equal(and.p, 0.1); + }); + + it('merges entries found in multiple sources, combining their probability and entries', () => { + const tomNode = blendedModel.child('tom'); + assert.equal(tomNode.p, 0.175); + + assert.equal(tomNode.entries.length, 1); + const tom = tomNode.entries[0]; + assert.equal(tom.text, 'tom'); + assert.equal(tom.p, 0.175); + }); + + it('correctly returns the probability of intermediate child paths', () => { + // simpleLexical: best is 'and', 0.2 + // names: best is 'amy', 0.25 + // Weight of each is equal at 0.5; 0.25 * 0.5 = 0.125. + // Should not combine. + const aNode = blendedModel.child('a'); + assert.equal(aNode.p, 0.125, "probability of paths were merged despite lack of duplicate"); + assert.equal(aNode.entries.length, 0); + + // 'tom' is on both paths, and is highest-frequency entry when merged. + // We want the combined, net probability to show up on its predecessor paths. + const toNode = blendedModel.child('to'); + assert.equal(toNode.p, 0.175, "probability of path with winning duplicate not merged"); + + // simpleLexical - p('to') = 0.1; is then multiplied by model weight of 0.5. + assert.equal(toNode.entries.length, 1); + const to = toNode.entries[0]; + assert.equal(to.text, 'to'); + assert.equal(to.p, 0.05); + }); + }); + + describe('iteration for correction-search', () => { + it('correctly enumerates child paths from root', () => { + const topChildren = blendedModel.children(); + // 'n' from "simpleLexical", 'b' and 'm' from "names" + assert.sameMembers(topChildren.map((child) => child.char), ['a', 'b', 'm', 'n', 't']); + assert.sameDeepMembers(topChildren.map((child) => { + return { + char: child.char, + p: child.p + } + }), [ + { char: 'a', p: .125 }, // highest: 'amy' + { char: 'b', p: .125 }, // highest: 'bob' + { char: 'm', p: .125 }, // highest: 'mark' + { char: 'n', p: .05 }, // highest: 'not' + { char: 't', p: .175 } // highest: 'tom' + ]); + topChildren.forEach((entry) => assert.equal(entry.p, entry.traversal().p)); + }); + + it('correctly enumerates child paths from section supported by a single member', () => { + const topChildren = blendedModel.children(); + const bNode = topChildren.find((entry) => entry.char == 'b').traversal(); + const bChildren = bNode.children(); + assert.sameDeepMembers(bChildren.map((child) => { + return { + char: child.char, + p: child.p + }; + }), [ + { char: 'o', p: 0.125 } + ]); + bChildren.forEach((entry) => assert.equal(entry.p, entry.traversal().p)); + + const boNode = bChildren.find((entry) => entry.char == 'o').traversal(); + const boChildren = boNode.children(); + assert.sameDeepMembers(boChildren.map((child) => { + return { + char: child.char, + p: child.p + }; + }), [ + { char: 'b', p: 0.125 } + ]); + boChildren.forEach((entry) => assert.equal(entry.p, entry.traversal().p)); + }); + + it('correctly enumerates child paths from section supported by multiple members', () => { + const topChildren = blendedModel.children(); + const tNode = topChildren.find((entry) => entry.char == 't').traversal(); + const tChildren = tNode.children(); + + assert.sameMembers(tChildren.map((child) => child.char), ['h', 'o', 'r']); + assert.sameDeepMembers(tChildren.map((child) => { + return { + char: child.char, + p: child.p + } + }), [ + { char: 'h', p: 0.15 }, + { char: 'o', p: 0.175 }, + { char: 'r', p: 0.05 } + ]); + tChildren.forEach((entry) => assert.equal(entry.p, entry.traversal().p)); + + const toNode = tChildren.find((entry) => entry.char == 'o').traversal(); + const toChildren = toNode.children(); + + assert.sameMembers(toChildren.map((child) => child.char), ['m']); + assert.sameDeepMembers(toChildren.map((child) => { + return { + char: child.char, + p: child.p + }; + }), [ + { char: 'm', p: 0.175 } + ]); + toChildren.forEach((entry) => assert.equal(entry.p, entry.traversal().p)); + }); + }); +}); \ No newline at end of file From 98ccdef181a695419a4a7cf415c4e6aa5aa1d8c7 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Tue, 6 Aug 2024 08:42:53 +0700 Subject: [PATCH 12/26] chore(common/models): Apply suggestions from code review Co-authored-by: Marc Durdin --- common/models/templates/src/common.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/common/models/templates/src/common.ts b/common/models/templates/src/common.ts index 80c4470cb5..3e261e5ca7 100644 --- a/common/models/templates/src/common.ts +++ b/common/models/templates/src/common.ts @@ -158,14 +158,12 @@ export function defaultApplyCasing(casing: CasingForm, text: string): string { * the trie. There should be a function that converts arbitrary strings * (queries) and converts them into a standard search key for a given language * model. - * - * Fun fact: This opaque type has ALREADY saved my bacon and found a bug! */ export type SearchKey = string & { _: 'SearchKey'}; /** * A function that converts a string (word form or query) into a search key - * (secretly, this is also a string). + * (which, internally, is also a string type). */ export interface Wordform2Key { (wordform: string): SearchKey; From 8504a96a4e830fa19e30842145efa45f61c7b8fa Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 6 Aug 2024 08:45:19 +0700 Subject: [PATCH 13/26] chore(developer): drops redundant buildFromWordList return --- developer/src/kmc-model/src/build-trie.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/developer/src/kmc-model/src/build-trie.ts b/developer/src/kmc-model/src/build-trie.ts index 76da23662f..53f4539acc 100644 --- a/developer/src/kmc-model/src/build-trie.ts +++ b/developer/src/kmc-model/src/build-trie.ts @@ -304,13 +304,12 @@ namespace Trie { * Populates the trie with the contents of an entire wordlist. * @param words a list of word and count pairs. */ - function buildFromWordList(trie: Trie, words: WordList): Trie { + function buildFromWordList(trie: Trie, words: WordList): void { for (let [wordform, weight] of Object.entries(words)) { - let key = trie.toKey(wordform); + const key = trie.toKey(wordform); addUnsorted(trie.root, { key, weight, content: wordform }, 0); } sortTrie(trie.root); - return trie; } /** From 6ce82f1a95ccb54a51a955d3f100d7f874db0a00 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 6 Aug 2024 08:45:42 +0700 Subject: [PATCH 14/26] chore(developer): a simple let -> const --- developer/src/kmc-model/src/build-trie.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/developer/src/kmc-model/src/build-trie.ts b/developer/src/kmc-model/src/build-trie.ts index 53f4539acc..c26fd91843 100644 --- a/developer/src/kmc-model/src/build-trie.ts +++ b/developer/src/kmc-model/src/build-trie.ts @@ -291,7 +291,7 @@ namespace Trie { * @returns A JSON-serialiable object that can be given to the TrieModel constructor. */ export function buildTrie(wordlist: WordList, keyFunction: SearchTermToKey): object { - let trie = new Trie(keyFunction); + const trie = new Trie(keyFunction); buildFromWordList(trie, wordlist); const root = trie.root; return { From 1e87e9c9b6c834a8e1a17d8263be00119ac26efb Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 6 Aug 2024 08:46:43 +0700 Subject: [PATCH 15/26] docs(developer): adds @param entry in doc-comment --- developer/src/kmc-model/src/build-trie.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/developer/src/kmc-model/src/build-trie.ts b/developer/src/kmc-model/src/build-trie.ts index c26fd91843..aa9686c123 100644 --- a/developer/src/kmc-model/src/build-trie.ts +++ b/developer/src/kmc-model/src/build-trie.ts @@ -302,6 +302,7 @@ namespace Trie { /** * Populates the trie with the contents of an entire wordlist. + * @param trie The Trie object under construction * @param words a list of word and count pairs. */ function buildFromWordList(trie: Trie, words: WordList): void { From 34a052725497fd397d0bbd73782eaf3e9aea779a Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Thu, 8 Aug 2024 07:22:46 +0700 Subject: [PATCH 16/26] chore: establish user-dict epic --- common/predictive-text/README.md | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/common/predictive-text/README.md b/common/predictive-text/README.md index b44aba5016..0271650580 100644 --- a/common/predictive-text/README.md +++ b/common/predictive-text/README.md @@ -17,11 +17,13 @@ Build Run `build.sh`. This will also automatically install dependencies with `npm`. ```sh -./build.sh +./build.sh configure build ``` ### Two-stage compilation process +TODO: this is well and truly out of date... + Since the primary LMLayer code runs within a [Web Worker][], `build.sh` compiles the LMLayer in two stages: @@ -43,19 +45,7 @@ This will run both headless unit tests, and in-browser unit tests and integratio tests: ```sh -./build.sh -test +./build.sh test ``` -### Test-Driven Development - -I like to use [entr]() to automatically build and re-run the unit tests anytime I -change a source code file. Here's the command I run in separate window: - -```sh -git ls-files | entr -c ./build.sh -tdd -``` - -Importantly, `./build.sh -tdd` skips running the in-browser tests, and skips -downloading/updating `npm` dependencies. - -[entr]: http://eradman.com/entrproject/ +### TODO: epic/user-dict \ No newline at end of file From f6e87f8c74d48c9ea4a4c49676dadc75a71682e7 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Fri, 9 Aug 2024 13:18:41 +0700 Subject: [PATCH 17/26] fix(developer): sets models-templates as package.json dependency --- developer/src/kmc-model/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/developer/src/kmc-model/package.json b/developer/src/kmc-model/package.json index da73aabe7b..eb60642fb1 100644 --- a/developer/src/kmc-model/package.json +++ b/developer/src/kmc-model/package.json @@ -32,12 +32,12 @@ "dependencies": { "@keymanapp/common-types": "*", "@keymanapp/keyman-version": "*", + "@keymanapp/models-templates": "*", "@keymanapp/models-types": "*", "typescript": "^5.4.5" }, "devDependencies": { "@keymanapp/developer-test-helpers": "*", - "@keymanapp/models-templates": "*", "@types/mocha": "^5.2.7", "@types/node": "^20.4.1", "c8": "^7.12.0", From 894721dafca48a9ec76288f6b13e369c0bdcbdd1 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Fri, 9 Aug 2024 14:01:31 +0700 Subject: [PATCH 18/26] chore(common): update package-lock.json --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 098c99700f..fa7423246e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1419,12 +1419,12 @@ "dependencies": { "@keymanapp/common-types": "*", "@keymanapp/keyman-version": "*", + "@keymanapp/models-templates": "*", "@keymanapp/models-types": "*", "typescript": "^5.4.5" }, "devDependencies": { "@keymanapp/developer-test-helpers": "*", - "@keymanapp/models-templates": "*", "@types/mocha": "^5.2.7", "@types/node": "^20.4.1", "c8": "^7.12.0", From 637682d1f994386691b84ac006da8d55c8fe40a9 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 27 Aug 2024 08:30:22 +0700 Subject: [PATCH 19/26] change(web): refactors complex type into CompositedTraversal local type 'ChildEdge' --- .../worker-thread/src/main/models/composited-traversal.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/web/src/engine/predictive-text/worker-thread/src/main/models/composited-traversal.ts b/web/src/engine/predictive-text/worker-thread/src/main/models/composited-traversal.ts index 7db796641b..af56a5c27d 100644 --- a/web/src/engine/predictive-text/worker-thread/src/main/models/composited-traversal.ts +++ b/web/src/engine/predictive-text/worker-thread/src/main/models/composited-traversal.ts @@ -1,5 +1,7 @@ import { PriorityQueue } from "@keymanapp/web-utils"; +type ChildEdge = { char: string; p: number, traversal: () => LexiconTraversal }; + export class CompositedTraversal implements LexiconTraversal { private readonly componentTraversals: Distribution; @@ -16,7 +18,7 @@ export class CompositedTraversal implements LexiconTraversal { }); } - children(): { char: string; p: number, traversal: () => LexiconTraversal }[] { + children(): ChildEdge[] { // Easy case: when there's no source on the current Traversal path, there // are no children. // @@ -63,7 +65,7 @@ export class CompositedTraversal implements LexiconTraversal { // Now, the tricky part: actual blending. // Find sets of traversals that traverse the same char path after the represented prefix. - const componentBuckets: Record LexiconTraversal}, weight: number }[]> = {}; + const componentBuckets: Record = {}; unblendedComponents.forEach((componentChildren) => { componentChildren.sample.forEach((entry) => { componentBuckets[entry.char] ??= []; From e3226f80a82675a925f4926c7da21e0c2ea8f67c Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Fri, 13 Sep 2024 14:23:24 +0700 Subject: [PATCH 20/26] chore: fixup imports --- .../engine/predictive-text/templates/src/traversal-model.ts | 1 + web/src/engine/predictive-text/templates/src/trie-model.ts | 4 +--- web/src/engine/predictive-text/templates/src/trie.ts | 1 + .../worker-thread/src/main/models/composited-traversal.ts | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/web/src/engine/predictive-text/templates/src/traversal-model.ts b/web/src/engine/predictive-text/templates/src/traversal-model.ts index 4d55db1a9d..76205da36c 100644 --- a/web/src/engine/predictive-text/templates/src/traversal-model.ts +++ b/web/src/engine/predictive-text/templates/src/traversal-model.ts @@ -28,6 +28,7 @@ import { default as defaultWordBreaker } from "@keymanapp/models-wordbreakers"; import { applyTransform, SearchKey, transformToSuggestion, Wordform2Key } from "./common.js"; import { getLastPreCaretToken } from "./tokenization.js"; +import { Capabilities, CasingFunction, Configuration, Context, Distribution, LexicalModel, LexicalModelPunctuation, LexiconTraversal, Suggestion, TextWithProbability, Transform, USVString, WithOutcome, WordBreakingFunction } from "@keymanapp/common-types"; extendString(); diff --git a/web/src/engine/predictive-text/templates/src/trie-model.ts b/web/src/engine/predictive-text/templates/src/trie-model.ts index 0fd1f29ed1..12b597b1ef 100644 --- a/web/src/engine/predictive-text/templates/src/trie-model.ts +++ b/web/src/engine/predictive-text/templates/src/trie-model.ts @@ -28,9 +28,7 @@ // Allows the kmwstring bindings to resolve. import { extendString } from "@keymanapp/web-utils"; -import { applyTransform, isHighSurrogate, isSentinel, SENTINEL_CODE_UNIT, transformToSuggestion } from "./common.js"; -import { getLastPreCaretToken } from "./tokenization.js"; -import { Capabilities, CasingFunction, Configuration, Context, Distribution, LexicalModel, LexicalModelPunctuation, LexiconTraversal, Suggestion, TextWithProbability, Transform, USVString, WithOutcome, WordBreakingFunction } from '@keymanapp/common-types'; +import { CasingFunction, Configuration, LexicalModel, LexicalModelPunctuation, WordBreakingFunction } from '@keymanapp/common-types'; import { SearchKey, Wordform2Key } from "./common.js"; import { Node, Trie } from './trie.js'; import { TraversalModel } from './traversal-model.js'; diff --git a/web/src/engine/predictive-text/templates/src/trie.ts b/web/src/engine/predictive-text/templates/src/trie.ts index cc47206b6e..3199d98386 100644 --- a/web/src/engine/predictive-text/templates/src/trie.ts +++ b/web/src/engine/predictive-text/templates/src/trie.ts @@ -1,4 +1,5 @@ import { isHighSurrogate, isSentinel, SearchKey, SENTINEL_CODE_UNIT, Wordform2Key } from "./common.js"; +import { LexiconTraversal, USVString } from "@keymanapp/common-types"; // The following trie implementation has been (heavily) derived from trie-ing // by Conrad Irwin. diff --git a/web/src/engine/predictive-text/worker-thread/src/main/models/composited-traversal.ts b/web/src/engine/predictive-text/worker-thread/src/main/models/composited-traversal.ts index af56a5c27d..bf7f29182b 100644 --- a/web/src/engine/predictive-text/worker-thread/src/main/models/composited-traversal.ts +++ b/web/src/engine/predictive-text/worker-thread/src/main/models/composited-traversal.ts @@ -1,3 +1,4 @@ +import { Distribution, LexiconTraversal, TextWithProbability } from "@keymanapp/common-types"; import { PriorityQueue } from "@keymanapp/web-utils"; type ChildEdge = { char: string; p: number, traversal: () => LexiconTraversal }; From 05e2c8aec104062fa5c1527199a73534bd331497 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Mon, 16 Sep 2024 04:11:52 +0700 Subject: [PATCH 21/26] chore: missing deps --- developer/src/kmc-model/build.sh | 4 +++- developer/src/kmc-model/package.json | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/developer/src/kmc-model/build.sh b/developer/src/kmc-model/build.sh index 372a145312..1e202e098a 100755 --- a/developer/src/kmc-model/build.sh +++ b/developer/src/kmc-model/build.sh @@ -10,8 +10,10 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" builder_describe "Keyman kmc Lexical Model Compiler module" \ "@/common/web/keyman-version" \ + "@/common/web/types" \ "@/developer/src/common/web/test-helpers" \ - "@/web/src/engine/predictive-text/templates/ test" \ + "@/developer/src/common/web/utils" \ + "@/web/src/engine/predictive-text/templates/" \ "clean" \ "configure" \ "build" \ diff --git a/developer/src/kmc-model/package.json b/developer/src/kmc-model/package.json index 396094a1d4..566f816c8a 100644 --- a/developer/src/kmc-model/package.json +++ b/developer/src/kmc-model/package.json @@ -32,6 +32,7 @@ "dependencies": { "@keymanapp/common-types": "*", "@keymanapp/keyman-version": "*", + "@keymanapp/models-templates": "*", "typescript": "^5.4.5" }, "devDependencies": { From 10e922d0c3670daf0f766417b195e230840ba0c5 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Fri, 6 Dec 2024 05:20:22 +0700 Subject: [PATCH 22/26] chore: fixup LexicalModelTypes import --- .../templates/src/traversal-model.ts | 16 +++++++++++++++- .../engine/predictive-text/templates/src/trie.ts | 4 +++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/web/src/engine/predictive-text/templates/src/traversal-model.ts b/web/src/engine/predictive-text/templates/src/traversal-model.ts index 76205da36c..5ef4d7a325 100644 --- a/web/src/engine/predictive-text/templates/src/traversal-model.ts +++ b/web/src/engine/predictive-text/templates/src/traversal-model.ts @@ -28,7 +28,21 @@ import { default as defaultWordBreaker } from "@keymanapp/models-wordbreakers"; import { applyTransform, SearchKey, transformToSuggestion, Wordform2Key } from "./common.js"; import { getLastPreCaretToken } from "./tokenization.js"; -import { Capabilities, CasingFunction, Configuration, Context, Distribution, LexicalModel, LexicalModelPunctuation, LexiconTraversal, Suggestion, TextWithProbability, Transform, USVString, WithOutcome, WordBreakingFunction } from "@keymanapp/common-types"; +import { LexicalModelTypes } from "@keymanapp/common-types"; +import Capabilities = LexicalModelTypes.Capabilities; +import CasingFunction = LexicalModelTypes.CasingFunction; +import Configuration = LexicalModelTypes.Configuration; +import Context = LexicalModelTypes.Context; +import Distribution = LexicalModelTypes.Distribution; +import LexicalModel = LexicalModelTypes.LexicalModel; +import LexicalModelPunctuation = LexicalModelTypes.LexicalModelPunctuation; +import LexiconTraversal = LexicalModelTypes.LexiconTraversal; +import Suggestion = LexicalModelTypes.Suggestion; +import TextWithProbability = LexicalModelTypes.TextWithProbability; +import Transform = LexicalModelTypes.Transform; +import USVString = LexicalModelTypes.USVString; +import WithOutcome = LexicalModelTypes.WithOutcome; +import WordBreakingFunction = LexicalModelTypes.WordBreakingFunction; extendString(); diff --git a/web/src/engine/predictive-text/templates/src/trie.ts b/web/src/engine/predictive-text/templates/src/trie.ts index 3199d98386..841ce348cb 100644 --- a/web/src/engine/predictive-text/templates/src/trie.ts +++ b/web/src/engine/predictive-text/templates/src/trie.ts @@ -1,5 +1,7 @@ import { isHighSurrogate, isSentinel, SearchKey, SENTINEL_CODE_UNIT, Wordform2Key } from "./common.js"; -import { LexiconTraversal, USVString } from "@keymanapp/common-types"; +import { LexicalModelTypes } from "@keymanapp/common-types"; +import LexiconTraversal = LexicalModelTypes.LexiconTraversal; +import USVString = LexicalModelTypes.USVString; // The following trie implementation has been (heavily) derived from trie-ing // by Conrad Irwin. From 88ac9e90b6c746d3ed89d268dd8edf5e00c7463f Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Fri, 6 Dec 2024 08:50:56 +0700 Subject: [PATCH 23/26] chore: fixup LexicalModelTypes import --- .../worker-thread/src/main/models/composited-traversal.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/src/engine/predictive-text/worker-thread/src/main/models/composited-traversal.ts b/web/src/engine/predictive-text/worker-thread/src/main/models/composited-traversal.ts index bf7f29182b..e743bbb638 100644 --- a/web/src/engine/predictive-text/worker-thread/src/main/models/composited-traversal.ts +++ b/web/src/engine/predictive-text/worker-thread/src/main/models/composited-traversal.ts @@ -1,4 +1,7 @@ -import { Distribution, LexiconTraversal, TextWithProbability } from "@keymanapp/common-types"; +import { LexicalModelTypes } from "@keymanapp/common-types"; +import Distribution = LexicalModelTypes.Distribution; +import LexiconTraversal = LexicalModelTypes.LexiconTraversal; +import TextWithProbability = LexicalModelTypes.TextWithProbability; import { PriorityQueue } from "@keymanapp/web-utils"; type ChildEdge = { char: string; p: number, traversal: () => LexiconTraversal }; From 1f907b0f52f37b3958714bd37d0667eddbd0b035 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Fri, 16 May 2025 08:17:44 +0700 Subject: [PATCH 24/26] maint: resolve merge-conflict sections --- common/web/types/src/lexical-model-types.ts | 33 +- developer/src/kmc-model/src/build-trie.ts | 337 ++----------- .../predictive-text/templates/src/common.ts | 16 + .../predictive-text/templates/src/index.ts | 5 + .../templates/src/traversal-model.ts | 299 ++++++++++++ .../templates/src/trie-builder.ts | 146 ++++++ .../templates/src/trie-model.ts | 448 +----------------- .../predictive-text/templates/src/trie.ts | 308 ++++++++++++ 8 files changed, 846 insertions(+), 746 deletions(-) create mode 100644 web/src/engine/predictive-text/templates/src/traversal-model.ts create mode 100644 web/src/engine/predictive-text/templates/src/trie-builder.ts create mode 100644 web/src/engine/predictive-text/templates/src/trie.ts diff --git a/common/web/types/src/lexical-model-types.ts b/common/web/types/src/lexical-model-types.ts index e2670d2e21..f6f2a65411 100644 --- a/common/web/types/src/lexical-model-types.ts +++ b/common/web/types/src/lexical-model-types.ts @@ -32,30 +32,37 @@ export type TextWithProbability = { */ export interface LexiconTraversal { /** - * Provides an iterable pattern used to search for words with a 'keyed' prefix matching - * the current traversal state's prefix when a new character is appended. Iterating - * across `children` provides 'breadth' to a lexical search. + * Provides an iterable pattern used to search for words with a 'keyed' prefix + * matching the current traversal state's prefix when a new character is + * appended. Iterating across `children` provides 'breadth' to a lexical + * search. * - * For an example with English, if the current traversal state corresponds to 'th', - * children() may return an iterator with states corresponding 'e' - * (for 'the', 'then', 'there'), 'a' (for 'than', 'that'), etc. + * For an example with English, if the current traversal state corresponds to + * 'th', children() may return an iterator with states corresponding 'e' (for + * 'the', 'then', 'there'), 'a' (for 'than', 'that'), etc. * - * @param char A full character (a UTF-16 code point, which may be comprised of two - * code units) corresponding to the child node, appended to the current - * node's prefix to produce the child node's prefix. + * @param char A full character (a UTF-16 code point, which may be + * comprised of two code units) corresponding to the child + * node, appended to the current node's prefix to produce the + * child node's prefix. *

* Example: if the current traversal's represented prefix is 'th', * char = 'e' would indicate a child node with prefix = 'the'. - * @param traversal a LexiconTraversal starting from (or "rooted at") the child node. Use - * of the returned object provides 'depth' to a lexical search. + * @param traversal a LexiconTraversal starting from (or "rooted at") the + * child node. Use of the returned object provides 'depth' + * to a lexical search. *

* Example: * * - Suppose our current LexiconTraversal represents a prefix of 'th'. * - If `char` = 'e', the child represents a prefix of 'the'. - * - Then `traversal` allows traversing the part of the lexicon prefixed by 'the'. + * - Then `traversal` allows traversing the part of the lexicon prefixed by + * 'the'. + * + * Note that there is no requirement for the ordering of the returned entries; + * no sorting of any kind is required. */ - children(): Generator<{char: string, traversal: () => LexiconTraversal}>; + children(): {char: string, p: number, traversal: () => LexiconTraversal}[]; /** * Allows direct access to the traversal state that results when appending one diff --git a/developer/src/kmc-model/src/build-trie.ts b/developer/src/kmc-model/src/build-trie.ts index e65b2fc42c..1075c4884f 100644 --- a/developer/src/kmc-model/src/build-trie.ts +++ b/developer/src/kmc-model/src/build-trie.ts @@ -1,6 +1,8 @@ import { ModelCompilerError, ModelCompilerMessageContext, ModelCompilerMessages } from "./model-compiler-messages.js"; import { callbacks } from "./compiler-callbacks.js"; +import { TrieBuilder } from '@keymanapp/models-templates'; + // Supports LF or CRLF line terminators. const NEWLINE_SEPARATOR = /\u000d?\u000a/; @@ -29,7 +31,7 @@ export function createTrieDataStructure(filenames: string[], searchTermToKey?: ( const wordlist: WordList = {}; filenames.forEach(filename => parseWordListFromFilename(wordlist, filename)); - const trie = Trie.buildTrie(wordlist, searchTermToKey as Trie.SearchTermToKey); + const trie = buildTrie(wordlist, searchTermToKey as SearchTermToKey); return JSON.stringify(trie); } @@ -188,302 +190,51 @@ function* enumerateLines(lines: string[]): Generator { } } -namespace Trie { - /** - * An **opaque** type for a string that is exclusively used as a search key in - * the trie. There should be a function that converts arbitrary strings - * (queries) and converts them into a standard search key for a given language - * model. - * - * Fun fact: This opaque type has ALREADY saved my bacon and found a bug! - */ - type SearchKey = string & { _: 'SearchKey'}; +/** + * An **opaque** type for a string that is exclusively used as a search key in + * the trie. There should be a function that converts arbitrary strings + * (queries) and converts them into a standard search key for a given language + * model. + * + * Fun fact: This opaque type has ALREADY saved my bacon and found a bug! + */ +type SearchKey = string & { _: 'SearchKey'}; - /** - * A function that converts a string (word form or query) into a search key - * (secretly, this is also a string). - */ - export interface SearchTermToKey { - (wordform: string): SearchKey; +/** + * A function that converts a string (word form or query) into a search key + * (secretly, this is also a string). + */ +export interface SearchTermToKey { + (wordform: string): SearchKey; +} + +/** + * Builds a trie from a word list. + * + * @param wordlist The wordlist with non-negative weights. + * @param keyFunction Function that converts word forms into indexed search keys + * @returns A JSON-serialiable object that can be given to the TrieModel constructor. + */ +export function buildTrie(wordlist: WordList, keyFunction: SearchTermToKey): object { + let collater = new TrieBuilder(keyFunction); + + buildFromWordList(collater, wordlist); + return { + totalWeight: collater.getTotalWeight(), + root: collater.getRoot() } +} - // The following trie implementation has been (heavily) derived from trie-ing - // by Conrad Irwin. - // - // trie-ing is distributed under the terms of the MIT license, reproduced here: - // - // The MIT License - // Copyright (c) 2015-2017 Conrad Irwin - // Copyright (c) 2011 Marc Campbell - // - // Permission is hereby granted, free of charge, to any person obtaining a copy - // of this software and associated documentation files (the "Software"), to deal - // in the Software without restriction, including without limitation the rights - // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - // copies of the Software, and to permit persons to whom the Software is - // furnished to do so, subject to the following conditions: - // - // The above copyright notice and this permission notice shall be included in - // all copies or substantial portions of the Software. - // - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - // THE SOFTWARE. - // - // See: https://github.com/ConradIrwin/trie-ing/blob/df55d7af7068d357829db9e0a7faa8a38add1d1d/LICENSE - - /** - * An entry in the prefix trie. The matched word is "content". - */ - interface Entry { - content: string; - key: SearchKey; - weight: number; - } - - /** - * The trie is made up of nodes. A node can be EITHER an internal node (whose - * only children are other nodes) OR a leaf, which actually contains the word - * form entries. - */ - type Node = InternalNode | Leaf; - - /** - * An internal node. - */ - interface InternalNode { - type: 'internal'; - weight: number; - // TODO: As an optimization, "values" can be a single string! - values: string[]; - children: { [codeunit: string]: Node }; - unsorted?: true; - } - - /** - * A leaf node. - */ - interface Leaf { - type: 'leaf'; - weight: number; - entries: Entry[]; - unsorted?: true; - } - - /** - * A sentinel value for when an internal node has contents and requires an - * "internal" leaf. That is, this internal node has content. Instead of placing - * entries as children in an internal node, a "fake" leaf is created, and its - * key is this special internal value. - * - * The value is a valid Unicode BMP code point, but it is a "non-character". - * Unicode will never assign semantics to these characters, as they are - * intended to be used internally as sentinel values. - */ - const INTERNAL_VALUE = '\uFDD0'; - - /** - * Builds a trie from a word list. - * - * @param wordlist The wordlist with non-negative weights. - * @param keyFunction Function that converts word forms into indexed search keys - * @returns A JSON-serialiable object that can be given to the TrieModel constructor. - */ - export function buildTrie(wordlist: WordList, keyFunction: SearchTermToKey): object { - const root = new Trie(keyFunction).buildFromWordList(wordlist).root; - return { - totalWeight: sumWeights(root), - root: root - } - } - - /** - * Wrapper class for the trie and its nodes and wordform to search - */ - class Trie { - readonly root = createRootNode(); - toKey: SearchTermToKey; - constructor(wordform2key: SearchTermToKey) { - this.toKey = wordform2key; - } - - /** - * Populates the trie with the contents of an entire wordlist. - * @param words a list of word and count pairs. - */ - buildFromWordList(words: WordList): Trie { - for (const [wordform, weight] of Object.entries(words)) { - const key = this.toKey(wordform); - addUnsorted(this.root, { key, weight, content: wordform }, 0); - } - sortTrie(this.root); - return this; - } - } - - // "Constructors" - function createRootNode(): Node { - return { - type: 'leaf', - weight: 0, - entries: [] - }; - } - - // Implement Trie creation. - - /** - * Adds an entry to the trie. - * - * Note that the trie will likely be unsorted after the add occurs. Before - * performing a lookup on the trie, use call sortTrie() on the root note! - * - * @param node Which node should the entry be added to? - * @param entry the wordform/weight/key to add to the trie - * @param index the index in the key and also the trie depth. Should be set to - * zero when adding onto the root node of the trie. - */ - function addUnsorted(node: Node, entry: Entry, index: number = 0) { - // Each node stores the MAXIMUM weight out of all of its decesdents, to - // enable a greedy search through the trie. - node.weight = Math.max(node.weight, entry.weight); - - // When should a leaf become an interior node? - // When it already has a value, but the key of the current value is longer - // than the prefix. - if (node.type === 'leaf' && index < entry.key.length && node.entries.length >= 1) { - convertLeafToInternalNode(node, index); - } - - if (node.type === 'leaf') { - // The key matches this leaf node, so add yet another entry. - addItemToLeaf(node, entry); - } else { - // Push the node down to a lower node. - addItemToInternalNode(node, entry, index); - } - - node.unsorted = true; - } - - /** - * Adds an item to the internal node at a given depth. - * @param item - * @param index - */ - function addItemToInternalNode(node: InternalNode, item: Entry, index: number) { - let char = item.key[index]; - // If an internal node is the proper site for item, it belongs under the - // corresponding (sentinel, internal-use) child node signifying this. - if(char == undefined) { - char = INTERNAL_VALUE; - } - if (!node.children[char]) { - node.children[char] = createRootNode(); - node.values.push(char); - } - addUnsorted(node.children[char], item, index + 1); - } - - function addItemToLeaf(leaf: Leaf, item: Entry) { - leaf.entries.push(item); - } - - /** - * Mutates the given Leaf to turn it into an InternalNode. - * - * NOTE: the node passed in will be DESTRUCTIVELY CHANGED into a different - * type when passed into this function! - * - * @param depth depth of the trie at this level. - */ - function convertLeafToInternalNode(leaf: Leaf, depth: number): void { - const entries = leaf.entries; - - // Alias the current node, as the desired type. - const internal = ( leaf) as InternalNode; - internal.type = 'internal'; - - delete leaf.entries; - internal.values = []; - internal.children = {}; - - // Convert the old values array into the format for interior nodes. - for (const item of entries) { - let char: string; - if (depth < item.key.length) { - char = item.key[depth]; - } else { - char = INTERNAL_VALUE; - } - - if (!internal.children[char]) { - internal.children[char] = createRootNode(); - internal.values.push(char); - } - addUnsorted(internal.children[char], item, depth + 1); - } - - internal.unsorted = true; - } - - /** - * Recursively sort the trie, in descending order of weight. - * @param node any node in the trie - */ - function sortTrie(node: Node) { - if (node.type === 'leaf') { - if (!node.unsorted) { - return; - } - - node.entries.sort(function (a, b) { return b.weight - a.weight; }); - } else { - // We MUST recurse and sort children before returning. - for (const char of node.values) { - sortTrie(node.children[char]); - } - - if (!node.unsorted) { - return; - } - - node.values.sort((a, b) => { - return node.children[b].weight - node.children[a].weight; - }); - } - - delete node.unsorted; - } - - /** - * O(n) recursive traversal to sum the total weight of all leaves in the - * trie, starting at the provided node. - * - * @param node The node to start summing weights. - */ - function sumWeights(node: Node): number { - let val: number; - if (node.type === 'leaf') { - val = node.entries - .map(entry => entry.weight) - //.map(entry => isNaN(entry.weight) ? 1 : entry.weight) - .reduce((acc, count) => acc + count, 0); - } else { - val = Object.keys(node.children) - .map((key) => sumWeights(node.children[key])) - .reduce((acc, count) => acc + count, 0); - } - - if(isNaN(val)) { - throw new Error("Unexpected NaN has appeared!"); - } - return val; +/** + * Populates the trie with the contents of an entire wordlist. + * @param words a list of word and count pairs. + */ +function buildFromWordList(trieCollator: TrieBuilder, words: WordList): TrieBuilder { + for (let [wordform, weight] of Object.entries(words)) { + trieCollator.addEntry(wordform, weight); } + trieCollator.sort(); + return trieCollator; } /** diff --git a/web/src/engine/predictive-text/templates/src/common.ts b/web/src/engine/predictive-text/templates/src/common.ts index f23ad6095b..1595b3f2e9 100644 --- a/web/src/engine/predictive-text/templates/src/common.ts +++ b/web/src/engine/predictive-text/templates/src/common.ts @@ -157,3 +157,19 @@ export function defaultApplyCasing(casing: CasingForm, text: string): string { return text.substring(0, headUnitLength).toUpperCase() .concat(text.substring(headUnitLength)); } } + +/** + * An **opaque** type for a string that is exclusively used as a search key in + * the trie. There should be a function that converts arbitrary strings + * (queries) and converts them into a standard search key for a given language + * model. + */ +export type SearchKey = string & { _: 'SearchKey'}; + +/** + * A function that converts a string (word form or query) into a search key + * (which, internally, is also a string type). + */ +export interface Wordform2Key { + (wordform: string): SearchKey; +} diff --git a/web/src/engine/predictive-text/templates/src/index.ts b/web/src/engine/predictive-text/templates/src/index.ts index 0966fb4494..5684875c7d 100644 --- a/web/src/engine/predictive-text/templates/src/index.ts +++ b/web/src/engine/predictive-text/templates/src/index.ts @@ -4,4 +4,9 @@ export { } from "./common.js"; export { default as QuoteBehavior } from "./quote-behavior.js"; export { getLastPreCaretToken, Token, Tokenization, tokenize, wordbreak } from "./tokenization.js"; +export { + Entry, InternalNode, Leaf, Node +} from './trie.js'; +export { TrieBuilder } from './trie-builder.js'; +export * as trieConstruction from './trie-builder.js'; export { default as TrieModel, TrieModelOptions } from "./trie-model.js"; \ No newline at end of file diff --git a/web/src/engine/predictive-text/templates/src/traversal-model.ts b/web/src/engine/predictive-text/templates/src/traversal-model.ts new file mode 100644 index 0000000000..95ec3d33d5 --- /dev/null +++ b/web/src/engine/predictive-text/templates/src/traversal-model.ts @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2019 National Research Council Canada (author: Eddie A. Santos) + * Copyright (c) 2019 SIL International + * Copyright (c) 2015–2017 Conrad Irwin + * Copyright (c) 2011–2015 Marc Campbell + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +// Allows the kmwstring bindings to resolve. +import { KMWString, PriorityQueue } from "@keymanapp/web-utils"; +import { default as defaultWordBreaker } from "@keymanapp/models-wordbreakers"; + +import { applyTransform, SearchKey, transformToSuggestion, Wordform2Key } from "./common.js"; +import { getLastPreCaretToken } from "./tokenization.js"; +import { LexicalModelTypes } from "@keymanapp/common-types"; +import Capabilities = LexicalModelTypes.Capabilities; +import CasingFunction = LexicalModelTypes.CasingFunction; +import Configuration = LexicalModelTypes.Configuration; +import Context = LexicalModelTypes.Context; +import Distribution = LexicalModelTypes.Distribution; +import LexicalModel = LexicalModelTypes.LexicalModel; +import LexicalModelPunctuation = LexicalModelTypes.LexicalModelPunctuation; +import LexiconTraversal = LexicalModelTypes.LexiconTraversal; +import Suggestion = LexicalModelTypes.Suggestion; +import TextWithProbability = LexicalModelTypes.TextWithProbability; +import Transform = LexicalModelTypes.Transform; +import WithOutcome = LexicalModelTypes.WithOutcome; +import WordBreakingFunction = LexicalModelTypes.WordBreakingFunction; + +/** + * @file traversal-model.ts + * + * Defines a lexical model based upon the implementation of a `LexiconTraversal`. + */ + +/** Upper bound on the amount of suggestions to generate. */ +const MAX_SUGGESTIONS = 12; + +/** + * Additional arguments to pass into the model, in addition to the model + * parameters themselves. + */ +export interface ModelOptions { + /** + * How to break words in a phrase. + */ + wordBreaker?: WordBreakingFunction; + /** + * This should simplify a search term into a key. + */ + searchTermToKey?: (searchTerm: string) => string; + + /** + * Indicates that the model's written form has 'casing' behavior. + */ + languageUsesCasing?: boolean; + + /** + * Specifies a function used to apply the language's casing rules to a word. + */ + applyCasing?: CasingFunction; + + /** + * Any punctuation to expose to the user. + */ + punctuation?: LexicalModelPunctuation; +} + +/** + * @class TraversalModel + * + * Defines a lexical model based upon the implementation of a `LexiconTraversal`. + */ +export class TraversalModel implements LexicalModel { + configuration?: Configuration; + private rootTraversal: LexiconTraversal; + readonly breakWords: WordBreakingFunction; + readonly punctuation?: LexicalModelPunctuation; + readonly languageUsesCasing?: boolean; + readonly _toKey: Wordform2Key; + + readonly applyCasing?: CasingFunction; + + constructor(rootTraversal: LexiconTraversal, options: ModelOptions = {}) { + this.languageUsesCasing = options.languageUsesCasing; + this.applyCasing = options.applyCasing; + + this.rootTraversal = rootTraversal; + this._toKey = options.searchTermToKey as Wordform2Key || defaultSearchTermToKey + this.breakWords = options.wordBreaker || defaultWordBreaker; + this.punctuation = options.punctuation; + } + + configure(capabilities: Capabilities): Configuration { + return this.configuration = { + leftContextCodePoints: capabilities.maxLeftContextCodePoints, + rightContextCodePoints: capabilities.maxRightContextCodePoints ?? 0 + }; + } + + toKey(text: string): string { + return this._toKey(text); + } + + predict(transform: Transform, context: Context): Distribution { + // Special-case the empty buffer/transform: return the top suggestions. + if (!transform.insert && !context.left && !context.right && context.startOfBuffer && context.endOfBuffer) { + return makeDistribution(this.firstN(MAX_SUGGESTIONS).map(({text, p}) => ({ + transform: { + insert: text, + deleteLeft: 0 + }, + displayAs: text, + p: p + }))); + } + + // Compute the results of the keystroke: + let newContext = applyTransform(transform, context); + + // Computes the different in word length after applying the transform above. + let leftDelOffset = transform.deleteLeft - KMWString.length(transform.insert); + + // All text to the left of the cursor INCLUDING anything that has + // just been typed. + let prefix = getLastPreCaretToken(this.breakWords, newContext); + + // Return suggestions from the trie. + return makeDistribution(this.lookup(prefix).map(({text, p}) => + transformToSuggestion({ + insert: text, + // Delete whatever the prefix that the user wrote. + deleteLeft: leftDelOffset + KMWString.length(prefix) + // Note: a separate capitalization/orthography engine can take this + // result and transform it as needed. + }, + p + ))); + + /* Helper */ + + function makeDistribution(suggestions: WithOutcome[]): Distribution { + let distribution: Distribution = []; + + for(let s of suggestions) { + distribution.push({sample: s, p: s.p}); + } + + return distribution; + } + } + + get wordbreaker(): WordBreakingFunction { + return this.breakWords; + } + + public traverseFromRoot(): LexiconTraversal { + return this.rootTraversal; + } + + /** + * Returns the top N suggestions from the trie. + * @param n How many suggestions, maximum, to return. + */ + firstN(n: number): TextWithProbability[] { + return getSortedResults(this.rootTraversal, n); + } + + /** + * Lookups an arbitrary prefix (a query) in the trie. Returns the top 3 + * results in sorted order. + * + * @param prefix + */ + lookup(prefix: string): TextWithProbability[] { + const searchKey = this.toKey(prefix); + const rootTraversal = this.traverseFromRoot().child(searchKey); + + if(!rootTraversal) { + return []; + } + + const directEntries = rootTraversal.entries; + // `Set` requires Chrome 38+, which is more recent than Chrome 35. + const directSet: Record = {}; + for(const entry of directEntries) { + directSet[entry.text] = entry.text; + } + + const bestEntries = getSortedResults(rootTraversal); + const deduplicated = bestEntries.filter((entry) => !directSet[entry.text]); + + // Any entries directly hosted on the current node should get full display + // priority over anything from its descendants. + return directEntries.concat(deduplicated); + } +}; + +/** + * The priority queue will always pop the most probable item - be it a Traversal + * state or a lexical entry reached via Traversal. + */ +type TraversableWithProb = TextWithProbability | LexiconTraversal; + +/** + * Returns all entries matching the given prefix, in descending order of + * weight. + * + * @param prefix the prefix to match. + * @param results the current results + * @param queue + */ +function getSortedResults(traversal: LexiconTraversal, limit = MAX_SUGGESTIONS): TextWithProbability[] { + let queue = new PriorityQueue(function(a: TraversableWithProb, b: TraversableWithProb) { + // In case of Trie compilation issues that emit `null` or `undefined` + return (b ? b.p : 0) - (a ? a.p : 0); + }); + let results: TextWithProbability[] = []; + + queue.enqueue(traversal); + + while(queue.count > 0) { + const entry = queue.dequeue(); + + if((entry as TextWithProbability)!.text !== undefined) { + const lexicalEntry = entry as TextWithProbability; + results.push(lexicalEntry); + if(results.length >= limit) { + return results; + } + } else { + const traversal = entry as LexiconTraversal; + queue.enqueueAll(traversal.entries); + let children: LexiconTraversal[] = [] + for(let child of traversal.children()) { + children.push(child.traversal()); + } + queue.enqueueAll(children); + } + } + + return results; +} + +/** + * Converts wordforms into an indexable form. It does this by + * normalizing into NFD, removing diacritics, and then converting + * the result to lowercase. + * + * This is a very naïve implementation, that I only think will work on + * some languages that use the Latin script. As of 2020-04-08, only + * 4 out of 11 (36%) of published language models use the Latin script, + * so this might not actually be a great default. + * + * This uses String.prototype.normalize() to convert normalize into NFD. + * NFD is an easy way to separate a Latin character from its diacritics; + * Even then, some Latin-based orthographies use code points that, + * under NFD normalization, do NOT decompose into an ASCII letter and a + * combining diacritical mark (e.g., SENĆOŦEN). + * + * Use this only in early iterations of the model. For a production lexical + * model, you SHOULD write/generate your own key function, tailored to your + * language. + */ +function defaultSearchTermToKey(wordform: string): SearchKey { + /** + * N.B.: this is (slightly) DIFFERENT than the version in + * keymanapp/lexical-model-compiler/build-trie + * as this is for compatibility for models built + * BEFORE the searchTermToKey function was bundled with + * all models. + * + * This compatibility version lowercases AFTER removing diacritics; + * the new version (bundled in future models) lowercases, + * NFD normalizes, THEN removes diacritics. + */ + return wordform + .normalize('NFD') + // Remove all combining diacritics (if input is in NFD) + // common to Latin-orthographies. + .replace(/[\u0300-\u036f]/g, '') + .toLowerCase() as SearchKey; +} diff --git a/web/src/engine/predictive-text/templates/src/trie-builder.ts b/web/src/engine/predictive-text/templates/src/trie-builder.ts new file mode 100644 index 0000000000..790923a1e4 --- /dev/null +++ b/web/src/engine/predictive-text/templates/src/trie-builder.ts @@ -0,0 +1,146 @@ +import { SENTINEL_CODE_UNIT, Wordform2Key } from "./common.js"; +import { Entry, InternalNode, Leaf, Node, Trie, sortNode } from "./trie.js"; + +export function createRootNode(): Node { + return { + type: 'leaf', + weight: 0, + entries: [] + }; +} + +/** + * Adds an entry to the trie. Currently assumes there is no pre-existing match + * for the entry. + * + * Note that the trie will likely be unsorted after the add occurs. Before + * performing a lookup on the trie, use call sortTrie() on the root note! + * + * @param node Which node should the entry be added to? + * @param entry the wordform/weight/key to add to the trie + * @param index the index in the key and also the trie depth. Should be set to + * zero when adding onto the root node of the trie. + */ +export function addUnsorted(node: Node, entry: Entry, index: number = 0) { + // Each node stores the MAXIMUM weight out of all of its decesdents, to + // enable a greedy search through the trie. + node.weight = Math.max(node.weight, entry.weight); + + // When should a leaf become an interior node? + // When it already has a value, but the key of the current value is longer + // than the prefix. + if (node.type === 'leaf' && index < entry.key.length && node.entries.length >= 1) { + convertLeafToInternalNode(node, index); + } + + if (node.type === 'leaf') { + // The key matches this leaf node, so add yet another entry. + addItemToLeaf(node, entry); + } else { + // Push the node down to a lower node. + addItemToInternalNode(node, entry, index); + } + + node.unsorted = true; +} + +/** + * Adds an item to the internal node at a given depth. + * @param item + * @param index + */ +export function addItemToInternalNode(node: InternalNode, item: Entry, index: number) { + let char = item.key[index]; + // If an internal node is the proper site for item, it belongs under the + // corresponding (sentinel, internal-use) child node signifying this. + if(char == undefined) { + char = SENTINEL_CODE_UNIT; + } + if (!node.children[char]) { + node.children[char] = createRootNode(); + node.values.push(char); + } + addUnsorted(node.children[char], item, index + 1); +} + +export function addItemToLeaf(leaf: Leaf, item: Entry) { + leaf.entries.push(item); +} + +/** + * Mutates the given Leaf to turn it into an InternalNode. + * + * NOTE: the node passed in will be DESTRUCTIVELY CHANGED into a different + * type when passed into this function! + * + * @param depth depth of the trie at this level. + */ +export function convertLeafToInternalNode(leaf: Leaf, depth: number): void { + let entries = leaf.entries; + + // Alias the current node, as the desired type. + let internal = ( leaf) as InternalNode; + internal.type = 'internal'; + + delete (leaf as Partial).entries; + internal.values = []; + internal.children = {}; + + // Convert the old values array into the format for interior nodes. + for (let item of entries) { + let char: string; + if (depth < item.key.length) { + char = item.key[depth]; + } else { + char = SENTINEL_CODE_UNIT; + } + + if (!internal.children[char]) { + internal.children[char] = createRootNode(); + internal.values.push(char); + } + addUnsorted(internal.children[char], item, depth + 1); + } + + internal.unsorted = true; +} + +/** + * Wrapper class for the trie and its nodes. + */ +export class TrieBuilder extends Trie { + /** The total weight of the entire trie. */ + totalWeight: number; + + constructor(toKey: Wordform2Key) { + super(createRootNode(), 0, toKey); + this.totalWeight = 0; + } + + addEntry(word: string, weight?: number) { + weight = (isNaN(weight ?? NaN) || !weight) ? 1 : weight; + this.totalWeight += weight; + + addUnsorted( + this.root, { + key: this.toKey(word), + content: word, + weight: weight + }, + 0 + ); + } + + sort() { + // Sorts the full Trie, not just a part of it. + sortNode(this.root); + } + + getRoot(): Node { + return this.root; + } + + getTotalWeight(): number { + return this.totalWeight; + } +} \ No newline at end of file diff --git a/web/src/engine/predictive-text/templates/src/trie-model.ts b/web/src/engine/predictive-text/templates/src/trie-model.ts index e33c47ce2b..8b1287b991 100644 --- a/web/src/engine/predictive-text/templates/src/trie-model.ts +++ b/web/src/engine/predictive-text/templates/src/trie-model.ts @@ -25,25 +25,15 @@ // Worth noting: we're starting to get quite a 'library' of common model/LMLayer functionality. // Should probably make a 'lm-utils' submodule. -import { KMWString, PriorityQueue } from "@keymanapp/web-utils"; -import { default as defaultWordBreaker } from "@keymanapp/models-wordbreakers"; - -import { applyTransform, isHighSurrogate, isSentinel, SENTINEL_CODE_UNIT, transformToSuggestion } from "./common.js"; -import { getLastPreCaretToken } from "./tokenization.js"; import { LexicalModelTypes } from "@keymanapp/common-types"; -import Capabilities = LexicalModelTypes.Capabilities; import CasingFunction = LexicalModelTypes.CasingFunction; import Configuration = LexicalModelTypes.Configuration; -import Context = LexicalModelTypes.Context; -import Distribution = LexicalModelTypes.Distribution; import LexicalModel = LexicalModelTypes.LexicalModel; import LexicalModelPunctuation = LexicalModelTypes.LexicalModelPunctuation; -import LexiconTraversal = LexicalModelTypes.LexiconTraversal; -import Suggestion = LexicalModelTypes.Suggestion; -import TextWithProbability = LexicalModelTypes.TextWithProbability; -import Transform = LexicalModelTypes.Transform; -import WithOutcome = LexicalModelTypes.WithOutcome; import WordBreakingFunction = LexicalModelTypes.WordBreakingFunction; +import { SearchKey, Wordform2Key } from "./common.js"; +import { Node, Trie } from './trie.js'; +import { TraversalModel } from './traversal-model.js'; /** * @file trie-model.ts @@ -51,9 +41,6 @@ import WordBreakingFunction = LexicalModelTypes.WordBreakingFunction; * Defines a simple word list (unigram) model. */ -/** Upper bound on the amount of suggestions to generate. */ -const MAX_SUGGESTIONS = 12; - /** * Additional arguments to pass into the model, in addition to the model * parameters themselves. @@ -84,182 +71,6 @@ export interface TrieModelOptions { punctuation?: LexicalModelPunctuation; } -class Traversal implements LexiconTraversal { - /** - * The lexical prefix corresponding to the current traversal state. - */ - prefix: String; - - /** - * The current traversal node. Serves as the 'root' of its own sub-Trie, - * and we cannot navigate back to its parent. - */ - root: Node; - - /** - * The max weight for the Trie being 'traversed'. Needed for probability - * calculations. - */ - totalWeight: number; - - constructor(root: Node, prefix: string, totalWeight: number) { - this.root = root; - this.prefix = prefix; - this.totalWeight = totalWeight; - } - - child(char: string): LexiconTraversal | undefined { - // May result for blank tokens resulting immediately after whitespace. - if(char == '') { - return this; - } - - // Split into individual code units. - let steps = char.split(''); - let traversal: Traversal | undefined = this; - - while(steps.length > 0 && traversal) { - const step: string = steps.shift()!; - traversal = traversal._child(step); - } - - return traversal; - } - - // Handles one code unit at a time. - private _child(char: string): Traversal | undefined { - const root = this.root; - const totalWeight = this.totalWeight; - const nextPrefix = this.prefix + char; - - if(root.type == 'internal') { - let childNode = root.children[char]; - if(!childNode) { - return undefined; - } - - return new Traversal(childNode, nextPrefix, totalWeight); - } else { - // root.type == 'leaf'; - const legalChildren = root.entries.filter(function(entry) { - return entry.key.indexOf(nextPrefix) == 0; - }); - - if(!legalChildren.length) { - return undefined; - } - - return new Traversal(root, nextPrefix, totalWeight); - } - } - - *children(): Generator<{char: string, traversal: () => LexiconTraversal}> { - let root = this.root; - - // We refer to the field multiple times in this method, and it doesn't change. - // This also assists minification a bit, since we can't minify when re-accessing - // through `this.`. - const totalWeight = this.totalWeight; - - if(root.type == 'internal') { - for(let entry of root.values) { - let entryNode = root.children[entry]; - - // UTF-16 astral plane check. - if(isHighSurrogate(entry)) { - // First code unit of a UTF-16 code point. - // For now, we'll just assume the second always completes such a char. - // - // Note: Things get nasty here if this is only sometimes true; in the future, - // we should compile-time enforce that this assumption is always true if possible. - if(entryNode.type == 'internal') { - let internalNode = entryNode; - for(let lowSurrogate of internalNode.values) { - let prefix = this.prefix + entry + lowSurrogate; - yield { - char: entry + lowSurrogate, - traversal: function() { return new Traversal(internalNode.children[lowSurrogate], prefix, totalWeight) } - } - } - } else { - // Determine how much of the 'leaf' entry has no Trie nodes, emulate them. - let fullText = entryNode.entries[0].key; - entry = entry + fullText[this.prefix.length + 1]; // The other half of the non-BMP char. - let prefix = this.prefix + entry; - - yield { - char: entry, - traversal: function () {return new Traversal(entryNode, prefix, totalWeight)} - } - } - } else if(isSentinel(entry)) { - continue; - } else if(!entry) { - // Prevent any accidental 'null' or 'undefined' entries from having an effect. - continue; - } else { - let prefix = this.prefix + entry; - yield { - char: entry, - traversal: function() { return new Traversal(entryNode, prefix, totalWeight)} - } - } - } - - return; - } else { // type == 'leaf' - let prefix = this.prefix; - - let children = root.entries.filter(function(entry) { - return entry.key != prefix && prefix.length < entry.key.length; - }) - - for(let {key} of children) { - let nodeKey = key[prefix.length]; - - if(isHighSurrogate(nodeKey)) { - // Merge the other half of an SMP char in! - nodeKey = nodeKey + key[prefix.length+1]; - } - yield { - char: nodeKey, - traversal: function() { return new Traversal(root, prefix + nodeKey, totalWeight)} - } - }; - return; - } - } - - get entries() { - const entryMapper = (value: Entry) => { - return { - text: value.content, - p: value.weight / this.totalWeight - } - } - - if(this.root.type == 'leaf') { - let prefix = this.prefix; - let matches = this.root.entries.filter(function(entry) { - return entry.key == prefix; - }); - - return matches.map(entryMapper); - } else { - let matchingLeaf = this.root.children[SENTINEL_CODE_UNIT]; - if(matchingLeaf && matchingLeaf.type == 'leaf') { - return matchingLeaf.entries.map(entryMapper); - } else { - return []; - } - } - } - - get p(): number { - return this.root.weight / this.totalWeight; - } -} - /** * @class TrieModel * @@ -269,269 +80,26 @@ class Traversal implements LexiconTraversal { * prefix searches within words, however they are not very good * at predicting the next word. */ -export default class TrieModel implements LexicalModel { +export default class TrieModel extends TraversalModel implements LexicalModel { configuration?: Configuration; - private _trie: Trie; - readonly breakWords: WordBreakingFunction; + public readonly trie: Trie; readonly punctuation?: LexicalModelPunctuation; readonly languageUsesCasing?: boolean; readonly applyCasing?: CasingFunction; constructor(trieData: {root: Node, totalWeight: number}, options: TrieModelOptions = {}) { - this.languageUsesCasing = options.languageUsesCasing; - this.applyCasing = options.applyCasing; - - this._trie = new Trie( + const trie = new Trie( trieData['root'], trieData['totalWeight'], options.searchTermToKey as Wordform2Key || defaultSearchTermToKey ); - this.breakWords = options.wordBreaker || defaultWordBreaker; - this.punctuation = options.punctuation; - } - configure(capabilities: Capabilities): Configuration { - return this.configuration = { - leftContextCodePoints: capabilities.maxLeftContextCodePoints, - rightContextCodePoints: capabilities.maxRightContextCodePoints ?? 0 - }; - } - - toKey(text: string): string { - return this._trie.toKey(text); - } - - predict(transform: Transform, context: Context): Distribution { - // Special-case the empty buffer/transform: return the top suggestions. - if (!transform.insert && !context.left && !context.right && context.startOfBuffer && context.endOfBuffer) { - return makeDistribution(this._trie.firstN(MAX_SUGGESTIONS).map(({text, p}) => ({ - transform: { - insert: text, - deleteLeft: 0 - }, - displayAs: text, - p: p - }))); - } - - // Compute the results of the keystroke: - let newContext = applyTransform(transform, context); - - // Computes the different in word length after applying the transform above. - let leftDelOffset = transform.deleteLeft - KMWString.length(transform.insert); - - // All text to the left of the cursor INCLUDING anything that has - // just been typed. - let prefix = getLastPreCaretToken(this.breakWords, newContext); - - // Return suggestions from the trie. - return makeDistribution(this._trie.lookup(prefix).map(({text, p}) => - transformToSuggestion({ - insert: text, - // Delete whatever the prefix that the user wrote. - deleteLeft: leftDelOffset + KMWString.length(prefix) - // Note: a separate capitalization/orthography engine can take this - // result and transform it as needed. - }, - p - ))); - - /* Helper */ - - function makeDistribution(suggestions: WithOutcome[]): Distribution { - let distribution: Distribution = []; - - for(let s of suggestions) { - distribution.push({sample: s, p: s.p}); - } - - return distribution; - } - } - - get wordbreaker(): WordBreakingFunction { - return this.breakWords; - } - - public traverseFromRoot(): LexiconTraversal { - return this._trie.traverseFromRoot(); + super(trie.traverseFromRoot(), options); + this.trie = trie; } }; -///////////////////////////////////////////////////////////////////////////////// -// What remains in this file is the trie implementation proper. Note: to // -// reduce bundle size, any functions/methods related to creating the trie have // -// been removed. // -///////////////////////////////////////////////////////////////////////////////// - -/** - * An **opaque** type for a string that is exclusively used as a search key in - * the trie. There should be a function that converts arbitrary strings - * (queries) and converts them into a standard search key for a given language - * model. - * - * Fun fact: This opaque type has ALREADY saved my bacon and found a bug! - */ -type SearchKey = string & { _: 'SearchKey'}; - -/** - * The priority queue will always pop the most probable item - be it a Traversal - * state or a lexical entry reached via Traversal. - */ -type TraversableWithProb = TextWithProbability | LexiconTraversal; - -/** - * A function that converts a string (word form or query) into a search key - * (secretly, this is also a string). - */ -interface Wordform2Key { - (wordform: string): SearchKey; -} - -// The following trie implementation has been (heavily) derived from trie-ing -// by Conrad Irwin. -// trie-ing is copyright (C) 2015–2017 Conrad Irwin. -// Distributed under the terms of the MIT license: -// https://github.com/ConradIrwin/trie-ing/blob/df55d7af7068d357829db9e0a7faa8a38add1d1d/LICENSE - -type Node = InternalNode | Leaf; -/** - * An internal node in the trie. Internal nodes NEVER contain entries; if an - * internal node should contain an entry, then it has a dummy leaf node (see - * below), that can be accessed by node.children["\uFDD0"]. - */ -interface InternalNode { - type: 'internal'; - weight: number; - /** Maintains the keys of children in descending order of weight. */ - values: string[]; // TODO: As an optimization, "values" can be a single string! - /** - * Maps a single UTF-16 code unit to a child node in the trie. This child - * node may be a leaf or an internal node. The keys of this object are kept - * in sorted order in the .values array. - */ - children: { [codeunit: string]: Node }; -} -/** Only leaf nodes actually contain entries (i.e., the words proper). */ -interface Leaf { - type: 'leaf'; - weight: number; - entries: Entry[]; -} - -/** - * An entry in the prefix trie (stored in leaf nodes exclusively!) - */ -interface Entry { - /** The actual word form, stored in the trie. */ - content: string; - /** A search key that usually simplifies the word form, for ease of search. */ - key: SearchKey; - weight: number; -} - -/** - * Wrapper class for the trie and its nodes. - */ -class Trie { - public readonly root: Node; - /** The total weight of the entire trie. */ - readonly totalWeight: number; - /** - * Converts arbitrary strings to a search key. The trie is built up of - * search keys; not each entry's word form! - */ - toKey: Wordform2Key; - - constructor(root: Node, totalWeight: number, wordform2key: Wordform2Key) { - this.root = root; - this.toKey = wordform2key; - this.totalWeight = totalWeight; - } - - public traverseFromRoot(): LexiconTraversal { - return new Traversal(this.root, '', this.totalWeight); - } - - /** - * Lookups an arbitrary prefix (a query) in the trie. Returns the top 3 - * results in sorted order. - * - * @param prefix - */ - lookup(prefix: string): TextWithProbability[] { - const searchKey = this.toKey(prefix); - const rootTraversal = this.traverseFromRoot().child(searchKey); - - if(!rootTraversal) { - return []; - } - - const directEntries = rootTraversal.entries; - // `Set` requires Chrome 38+, which is more recent than Chrome 35. - const directSet: Record = {}; - for(const entry of directEntries) { - directSet[entry.text] = entry.text; - } - - const bestEntries = getSortedResults(rootTraversal); - const deduplicated = bestEntries.filter((entry) => !directSet[entry.text]); - - // Any entries directly hosted on the current node should get full display - // priority over anything from its descendants. - return directEntries.concat(deduplicated); - } - - /** - * Returns the top N suggestions from the trie. - * @param n How many suggestions, maximum, to return. - */ - firstN(n: number): TextWithProbability[] { - return getSortedResults(this.traverseFromRoot(), n); - } -} - -/** - * Returns all entries matching the given prefix, in descending order of - * weight. - * - * @param prefix the prefix to match. - * @param results the current results - * @param queue - */ -function getSortedResults(traversal: LexiconTraversal, limit = MAX_SUGGESTIONS): TextWithProbability[] { - let queue = new PriorityQueue(function(a: TraversableWithProb, b: TraversableWithProb) { - // In case of Trie compilation issues that emit `null` or `undefined` - return (b ? b.p : 0) - (a ? a.p : 0); - }); - let results: TextWithProbability[] = []; - - queue.enqueue(traversal); - - while(queue.count > 0) { - const entry = queue.dequeue(); - - if((entry as TextWithProbability)!.text !== undefined) { - const lexicalEntry = entry as TextWithProbability; - results.push(lexicalEntry); - if(results.length >= limit) { - return results; - } - } else { - const traversal = entry as LexiconTraversal; - queue.enqueueAll(traversal.entries); - let children: LexiconTraversal[] = [] - for(let child of traversal.children()) { - children.push(child.traversal()); - } - queue.enqueueAll(children); - } - } - - return results; -} - /** * Converts wordforms into an indexable form. It does this by * normalizing into NFD, removing diacritics, and then converting diff --git a/web/src/engine/predictive-text/templates/src/trie.ts b/web/src/engine/predictive-text/templates/src/trie.ts new file mode 100644 index 0000000000..5eccf645f8 --- /dev/null +++ b/web/src/engine/predictive-text/templates/src/trie.ts @@ -0,0 +1,308 @@ +import { isHighSurrogate, isSentinel, SearchKey, SENTINEL_CODE_UNIT, Wordform2Key } from "./common.js"; +import { LexicalModelTypes } from "@keymanapp/common-types"; +import LexiconTraversal = LexicalModelTypes.LexiconTraversal; + +// The following trie implementation has been (heavily) derived from trie-ing +// by Conrad Irwin. +// trie-ing is copyright (C) 2015–2017 Conrad Irwin. +// Distributed under the terms of the MIT license: +// https://github.com/ConradIrwin/trie-ing/blob/df55d7af7068d357829db9e0a7faa8a38add1d1d/LICENSE + +export type Node = InternalNode | Leaf; +/** + * An internal node in the trie. Internal nodes NEVER contain entries; if an + * internal node should contain an entry, then it has a dummy leaf node (see + * below), that can be accessed by node.children["\uFDD0"]. + */ +export interface InternalNode { + type: 'internal'; + weight: number; + /** Maintains the keys of children in descending order of weight. */ + values: string[]; // TODO: As an optimization, "values" can be a single string! + /** + * Maps a single UTF-16 code unit to a child node in the trie. This child + * node may be a leaf or an internal node. The keys of this object are kept + * in sorted order in the .values array. + */ + children: { [codeunit: string]: Node }; + + /** + * Used during compilation. + */ + unsorted?: boolean; +} +/** Only leaf nodes actually contain entries (i.e., the words proper). */ +export interface Leaf { + type: 'leaf'; + weight: number; + entries: Entry[]; + + /** + * Used during compilation. + */ + unsorted?: boolean; +} + +/** + * An entry in the prefix trie (stored in leaf nodes exclusively!) + */ +export interface Entry { + /** The actual word form, stored in the trie. */ + content: string; + /** A search key that usually simplifies the word form, for ease of search. */ + key: SearchKey; + weight: number; +} + +/** + * Recursively sort the trie, in descending order of weight. + * @param node any node in the trie + */ +export function sortNode(node: Node, onlyLocal?: boolean) { + if (node.type === 'leaf') { + if (!node.unsorted) { + return; + } + + node.entries.sort(function (a, b) { return b.weight - a.weight; }); + } else { + if(!onlyLocal) { + // We recurse and sort children before returning if sorting the full Trie. + for (let char of node.values) { + sortNode(node.children[char], onlyLocal); + } + } + + if (!node.unsorted) { + return; + } + + node.values.sort((a, b) => { + return node.children[b].weight - node.children[a].weight; + }); + } + + delete node.unsorted; +} + +export class TrieTraversal implements LexiconTraversal { + /** + * The lexical prefix corresponding to the current traversal state. + */ + prefix: String; + + /** + * The current traversal node. Serves as the 'root' of its own sub-Trie, + * and we cannot navigate back to its parent. + */ + readonly root: Node; + + /** + * The max weight for the Trie being 'traversed'. Needed for probability + * calculations. + */ + totalWeight: number; + + constructor(root: Node, prefix: string, totalWeight: number) { + this.root = root; + this.prefix = prefix; + this.totalWeight = totalWeight; + } + + child(char: string): LexiconTraversal | undefined { + // May result for blank tokens resulting immediately after whitespace. + if(char == '') { + return this; + } + + // Split into individual code units. + let steps = char.split(''); + let traversal: ReturnType = this; + + while(steps.length > 0 && traversal) { + const step: string = steps.shift()!; + traversal = traversal._child(step); + } + + return traversal; + } + + // Handles one code unit at a time. + private _child(char: string): TrieTraversal | undefined { + const root = this.root; + const totalWeight = this.totalWeight; + const nextPrefix = this.prefix + char; + + // Sorts _just_ the current level, and only if needed. + // We only care about sorting parts that we're actually accessing. + sortNode(root, true); + + if(root.type == 'internal') { + let childNode = root.children[char]; + if(!childNode) { + return undefined; + } + + return new TrieTraversal(childNode, nextPrefix, totalWeight); + } else { + // root.type == 'leaf'; + const legalChildren = root.entries.filter(function(entry) { + return entry.key.indexOf(nextPrefix) == 0; + }); + + if(!legalChildren.length) { + return undefined; + } + + return new TrieTraversal(root, nextPrefix, totalWeight); + } + } + + children(): {char: string, p: number, traversal: () => LexiconTraversal}[] { + let root = this.root; + + // We refer to the field multiple times in this method, and it doesn't change. + // This also assists minification a bit, since we can't minify when re-accessing + // through `this.`. + const totalWeight = this.totalWeight; + + const results: {char: string, p: number, traversal: () => LexiconTraversal}[] = []; + + // Sorts _just_ the current level, and only if needed. + // We only care about sorting parts that we're actually accessing. + sortNode(root, true); + + if(root.type == 'internal') { + for(let entry of root.values) { + let entryNode = root.children[entry]; + + // UTF-16 astral plane check. + if(isHighSurrogate(entry)) { + // First code unit of a UTF-16 code point. + // For now, we'll just assume the second always completes such a char. + // + // Note: Things get nasty here if this is only sometimes true; in the future, + // we should compile-time enforce that this assumption is always true if possible. + if(entryNode.type == 'internal') { + let internalNode = entryNode; + for(let lowSurrogate of internalNode.values) { + let prefix = this.prefix + entry + lowSurrogate; + results.push({ + char: entry + lowSurrogate, + p: internalNode.children[lowSurrogate].weight / totalWeight, + traversal: function() { return new TrieTraversal(internalNode.children[lowSurrogate], prefix, totalWeight) } + }); + } + } else { + // No deduplication needed. If there were multiple keys available, + // we wouldn't be in a leaf node. + let fullText = entryNode.entries[0].key; + entry = entry + fullText[this.prefix.length + 1]; // The other half of the non-BMP char. + let prefix = this.prefix + entry; + + const weight = entryNode.entries.reduce((best, current) => Math.max(best, current.weight), 0); + + results.push({ + char: entry, + p: weight / totalWeight, + traversal: function () {return new TrieTraversal(entryNode, prefix, totalWeight)} + }); + } + } else if(isSentinel(entry)) { + continue; + } else if(!entry) { + // Prevent any accidental 'null' or 'undefined' entries from having an effect. + continue; + } else { + let prefix = this.prefix + entry; + results.push({ + char: entry, + p: entryNode.weight / totalWeight, + traversal: function() { return new TrieTraversal(entryNode, prefix, totalWeight)} + }); + } + } + + return results; + } else { // type == 'leaf' + let prefix = this.prefix; + + // No actual deduplication needed. If there were multiple keys available, + // we wouldn't be in a leaf node. + let children = root.entries.filter(function(entry) { + return entry.key != prefix && prefix.length < entry.key.length; + }); + + let weight = children.reduce((best, current) => Math.max(current.weight, best), 0); + + if(children.length > 0) { + const key = children[0].content; + let nodeKey = key[prefix.length]; + + if(isHighSurrogate(nodeKey)) { + // Merge the other half of an SMP char in! + nodeKey = nodeKey + key[prefix.length+1]; + } + results.push({ + char: nodeKey, + p: weight / totalWeight, + traversal: function() { return new TrieTraversal(root, prefix + nodeKey, totalWeight)} + }); + }; + return results; + } + } + + get entries() { + const entryMapper = (value: Entry) => { + return { + text: value.content, + p: value.weight / this.totalWeight + } + } + + if(this.root.type == 'leaf') { + let prefix = this.prefix; + let matches = this.root.entries.filter(function(entry) { + return entry.key == prefix; + }); + + return matches.map(entryMapper); + } else { + let matchingLeaf = this.root.children[SENTINEL_CODE_UNIT]; + if(matchingLeaf && matchingLeaf.type == 'leaf') { + return matchingLeaf.entries.map(entryMapper); + } else { + return []; + } + } + } + + get p(): number { + return this.root.weight / this.totalWeight; + } +} + +/** + * Wrapper class for the trie and its nodes. + */ +export class Trie { + protected root: Node; + /** The total weight of the entire trie. */ + readonly totalWeight: number; + /** + * Converts arbitrary strings to a search key. The trie is built up of + * search keys; not each entry's word form! + */ + toKey: Wordform2Key; + + constructor(root: Node, totalWeight: number, wordform2key: Wordform2Key) { + this.root = root; + this.toKey = wordform2key; + this.totalWeight = totalWeight; + } + + public traverseFromRoot(): LexiconTraversal { + return new TrieTraversal(this.root, '', this.totalWeight); + } +} \ No newline at end of file From d59756e3277ab1fbc8365ba89f0befede0a074ae Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Fri, 16 May 2025 08:50:24 +0200 Subject: [PATCH 25/26] chore(developer): use const instead of let --- developer/src/kmc-model/src/build-trie.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/developer/src/kmc-model/src/build-trie.ts b/developer/src/kmc-model/src/build-trie.ts index 1075c4884f..8473ef5c23 100644 --- a/developer/src/kmc-model/src/build-trie.ts +++ b/developer/src/kmc-model/src/build-trie.ts @@ -216,7 +216,7 @@ export interface SearchTermToKey { * @returns A JSON-serialiable object that can be given to the TrieModel constructor. */ export function buildTrie(wordlist: WordList, keyFunction: SearchTermToKey): object { - let collater = new TrieBuilder(keyFunction); + const collater = new TrieBuilder(keyFunction); buildFromWordList(collater, wordlist); return { @@ -230,7 +230,7 @@ export function buildTrie(wordlist: WordList, keyFunction: SearchTermToKey): obj * @param words a list of word and count pairs. */ function buildFromWordList(trieCollator: TrieBuilder, words: WordList): TrieBuilder { - for (let [wordform, weight] of Object.entries(words)) { + for (const [wordform, weight] of Object.entries(words)) { trieCollator.addEntry(wordform, weight); } trieCollator.sort(); From bd98f4bfb90d29ac9ece1e22360235a004f289a3 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Thu, 30 Oct 2025 16:31:45 +0100 Subject: [PATCH 26/26] chore(windows): upgrade VC++ projects to v143 (VS2022) Test-bot: skip Build-bot: skip release:developer,windows --- .../delphi/ext/tds2dbg/tds2dbg.vcxproj | 236 ++--- developer/src/kmanalyze/kmanalyze.vcxproj | 418 ++++---- developer/src/kmdecomp/kmdecomp.vcxproj | 340 +++---- .../src/samples/imsample/imsample.vcxproj | 530 +++++----- .../src/engine/keyman32/keyman-engine.vcxproj | 802 +++++++-------- windows/src/engine/keyman32/keyman32.vcxproj | 930 +++++++++--------- .../engine/keyman32/keyman32.vcxproj.filters | 534 +++++----- .../keyman-engine-tests.vcxproj | 320 +++--- .../src/engine/keymanx64/keymanx64.vcxproj | 304 +++--- .../src/engine/kmrefresh/kmrefresh.vcxproj | 312 +++--- windows/src/engine/kmtip/kmtip.vcxproj | 862 ++++++++-------- windows/src/engine/mcompile/mcompile.vcxproj | 266 ++--- windows/src/engine/testhost/testhost.vcxproj | 342 +++---- windows/src/support/etl2log/etl2log.vcxproj | 362 +++---- windows/src/support/texteditor/Editor.vcxproj | 348 +++---- .../shared-data/cpp/cppshareddata.vcxproj | 350 +++---- 16 files changed, 3631 insertions(+), 3625 deletions(-) diff --git a/common/windows/delphi/ext/tds2dbg/tds2dbg.vcxproj b/common/windows/delphi/ext/tds2dbg/tds2dbg.vcxproj index 8d57ae5c66..5183081136 100644 --- a/common/windows/delphi/ext/tds2dbg/tds2dbg.vcxproj +++ b/common/windows/delphi/ext/tds2dbg/tds2dbg.vcxproj @@ -1,119 +1,119 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {40BFECE3-7FC0-4649-A3DC-E017FF84C75B} - Win32Proj - 10.0.17763.0 - - - - Application - true - v141 - - - Application - false - v141 - - - Application - true - v141 - - - Application - false - v141 - - - - - - - - - - - - - - - - - - - - - true - - - true - - - - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - Level3 - ProgramDatabase - Disabled - - - MachineX86 - true - Console - - - - - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDLL - Level3 - ProgramDatabase - - - MachineX86 - true - Console - true - true - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {40BFECE3-7FC0-4649-A3DC-E017FF84C75B} + Win32Proj + 10.0 + + + + Application + true + v143 + + + Application + false + v143 + + + Application + true + v143 + + + Application + false + v143 + + + + + + + + + + + + + + + + + + + + + true + + + true + + + + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Level3 + ProgramDatabase + Disabled + + + MachineX86 + true + Console + + + + + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + ProgramDatabase + + + MachineX86 + true + Console + true + true + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/developer/src/kmanalyze/kmanalyze.vcxproj b/developer/src/kmanalyze/kmanalyze.vcxproj index 20fd857a35..da59aaf833 100644 --- a/developer/src/kmanalyze/kmanalyze.vcxproj +++ b/developer/src/kmanalyze/kmanalyze.vcxproj @@ -1,210 +1,210 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {4EAE6DFD-E18A-493D-A00F-4846A2593F56} - Win32Proj - kmanalyze - 10.0 - - - - Application - true - v142 - MultiByte - - - Application - false - v142 - true - MultiByte - - - Application - true - v142 - MultiByte - - - Application - false - v142 - true - MultiByte - - - - - - - - - - - - - - - - - - - - - true - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\windows\src\global\inc;$(KEYMAN_ROOT)\common\windows\delphi\ext\sentry - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - true - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\windows\src\global\inc;$(KEYMAN_ROOT)\common\windows\delphi\ext\sentry - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - false - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\windows\src\global\inc;$(KEYMAN_ROOT)\common\windows\delphi\ext\sentry - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - false - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\windows\src\global\inc;$(KEYMAN_ROOT)\common\windows\delphi\ext\sentry - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - - Use - Level3 - Disabled - true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - pch.h - MultiThreadedDebug - $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry;%(AdditionalIncludeDirectories) - - - Console - true - $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry\sentry.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - sentry.dll - - - - - Use - Level3 - Disabled - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - pch.h - MultiThreaded - $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry;%(AdditionalIncludeDirectories) - - - Console - true - $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry\sentry.x64.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - sentry.dll - - - - - Use - Level3 - MaxSpeed - true - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - pch.h - MultiThreaded - $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry;%(AdditionalIncludeDirectories) - - - Console - true - true - true - $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry\sentry.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - sentry.dll - - - - - Use - Level3 - MaxSpeed - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - pch.h - MultiThreaded - $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry;%(AdditionalIncludeDirectories) - - - Console - true - true - true - $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry\sentry.x64.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - sentry.dll - - - - - - - - - - - NotUsing - NotUsing - NotUsing - NotUsing - - - - - - Create - Create - Create - Create - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {4EAE6DFD-E18A-493D-A00F-4846A2593F56} + Win32Proj + kmanalyze + 10.0 + + + + Application + true + v143 + MultiByte + + + Application + false + v143 + true + MultiByte + + + Application + true + v143 + MultiByte + + + Application + false + v143 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + true + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\windows\src\global\inc;$(KEYMAN_ROOT)\common\windows\delphi\ext\sentry + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + true + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\windows\src\global\inc;$(KEYMAN_ROOT)\common\windows\delphi\ext\sentry + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + false + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\windows\src\global\inc;$(KEYMAN_ROOT)\common\windows\delphi\ext\sentry + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + false + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\windows\src\global\inc;$(KEYMAN_ROOT)\common\windows\delphi\ext\sentry + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + + Use + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + pch.h + MultiThreadedDebug + $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry;%(AdditionalIncludeDirectories) + + + Console + true + $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry\sentry.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + sentry.dll + + + + + Use + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + pch.h + MultiThreaded + $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry;%(AdditionalIncludeDirectories) + + + Console + true + $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry\sentry.x64.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + sentry.dll + + + + + Use + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + pch.h + MultiThreaded + $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry;%(AdditionalIncludeDirectories) + + + Console + true + true + true + $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry\sentry.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + sentry.dll + + + + + Use + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + pch.h + MultiThreaded + $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry;%(AdditionalIncludeDirectories) + + + Console + true + true + true + $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry\sentry.x64.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + sentry.dll + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + Create + Create + Create + Create + + + + + + + + \ No newline at end of file diff --git a/developer/src/kmdecomp/kmdecomp.vcxproj b/developer/src/kmdecomp/kmdecomp.vcxproj index 223ae72199..36cea5f3ff 100644 --- a/developer/src/kmdecomp/kmdecomp.vcxproj +++ b/developer/src/kmdecomp/kmdecomp.vcxproj @@ -1,171 +1,171 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {963D608A-6689-469C-AE42-F56696DD42CC} - 10.0 - - - - Application - false - MultiByte - v142 - - - Application - false - MultiByte - v142 - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - true - false - AllRules.ruleset - - - AllRules.ruleset - - - - - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - $(IncludePath) - - - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(IncludePath) - - - - .\Debug/kmdecomp.tlb - - - - - Disabled - $(KEYMAN_ROOT)\windows\src\global\inc;$(KEYMAN_ROOT)\common\windows\delphi\ext\sentry;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - Level3 - true - EditAndContinue - stdcpp17 - Use - pch.h - - - _DEBUG;%(PreprocessorDefinitions) - 0x0c09 - - - true - true - Console - MachineX86 - true - $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry\sentry.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - sentry.dll - - - true - .\Debug/kmdecomp.bsc - - - - - .\Release/kmdecomp.tlb - - - - - MaxSpeed - OnlyExplicitInline - $(KEYMAN_ROOT)\windows\src\global\inc;$(KEYMAN_ROOT)\common\windows\delphi\ext\sentry;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - MultiThreaded - true - Level3 - true - true - stdcpp17 - Use - pch.h - - - NDEBUG;%(PreprocessorDefinitions) - 0x0c09 - - - true - Console - MachineX86 - true - $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry\sentry.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - sentry.dll - - - true - .\Release/kmdecomp.bsc - - - - - NotUsing - NotUsing - - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - Create - Create - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - - - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + + {963D608A-6689-469C-AE42-F56696DD42CC} + 10.0 + + + + Application + false + MultiByte + v143 + + + Application + false + MultiByte + v143 + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + true + false + AllRules.ruleset + + + AllRules.ruleset + + + + + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(IncludePath) + + + + .\Debug/kmdecomp.tlb + + + + + Disabled + $(KEYMAN_ROOT)\windows\src\global\inc;$(KEYMAN_ROOT)\common\windows\delphi\ext\sentry;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + Level3 + true + EditAndContinue + stdcpp17 + Use + pch.h + + + _DEBUG;%(PreprocessorDefinitions) + 0x0c09 + + + true + true + Console + MachineX86 + true + $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry\sentry.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + sentry.dll + + + true + .\Debug/kmdecomp.bsc + + + + + .\Release/kmdecomp.tlb + + + + + MaxSpeed + OnlyExplicitInline + $(KEYMAN_ROOT)\windows\src\global\inc;$(KEYMAN_ROOT)\common\windows\delphi\ext\sentry;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + true + Level3 + true + true + stdcpp17 + Use + pch.h + + + NDEBUG;%(PreprocessorDefinitions) + 0x0c09 + + + true + Console + MachineX86 + true + $(KEYMAN_ROOT)\common\windows\delphi\ext\sentry\sentry.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + sentry.dll + + + true + .\Release/kmdecomp.bsc + + + + + NotUsing + NotUsing + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + Create + Create + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + + + + + + + + + \ No newline at end of file diff --git a/developer/src/samples/imsample/imsample.vcxproj b/developer/src/samples/imsample/imsample.vcxproj index 9abee9aeff..93413dbe8f 100644 --- a/developer/src/samples/imsample/imsample.vcxproj +++ b/developer/src/samples/imsample/imsample.vcxproj @@ -1,266 +1,266 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - - - {0C9DB8F9-B788-8782-A97D-4F8294AA0B4E} - 10.0 - imsample - - - - DynamicLibrary - false - v142 - - - DynamicLibrary - false - v142 - - - DynamicLibrary - false - v142 - - - DynamicLibrary - false - v142 - - - - - - - - - - - - - - - - - - - - - - - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - true - - - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - true - .dll - $(ProjectName).x64 - - - false - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - false - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - .dll - $(ProjectName).x64 - - - - MultiThreadedDebug - Default - true - Disabled - true - Level3 - true - EditAndContinue - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) - .\IMSample.pch - true - - - true - _DEBUG;%(PreprocessorDefinitions) - .\IMSample.tlb - true - NUL - Win32 - - - 0x0c09 - _DEBUG;%(PreprocessorDefinitions) - - - true - .\IMSample.bsc - - - true - true - true - Windows - $(OutDir)$(TargetName)$(TargetExt) - .\IMSample.lib - odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - .\imsample.def - true - - - - - MultiThreadedDebug - Default - false - Disabled - true - Level3 - ProgramDatabase - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) - .\IMSample.pch - true - - - true - _DEBUG;%(PreprocessorDefinitions) - .\IMSample.tlb - true - NUL - - - 0x0c09 - _DEBUG;%(PreprocessorDefinitions) - - - true - .\IMSample.bsc - - - true - true - true - Windows - $(OutDir)$(TargetName)$(TargetExt) - $(OutDir)$(TargetName).lib - odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - .\imsample.def - true - - - - - MultiThreaded - OnlyExplicitInline - true - true - MaxSpeed - true - Level3 - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - .\IMSample.pch - true - - - true - NDEBUG;%(PreprocessorDefinitions) - .\IMSample.tlb - true - NUL - Win32 - - - 0x0c09 - NDEBUG;%(PreprocessorDefinitions) - - - true - .\IMSample.bsc - - - true - true - Windows - odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - .\imsample.def - true - - - - - MultiThreaded - OnlyExplicitInline - true - true - MaxSpeed - true - Level3 - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - .\IMSample.pch - true - - - true - NDEBUG;%(PreprocessorDefinitions) - .\IMSample.tlb - true - NUL - - - 0x0c09 - NDEBUG;%(PreprocessorDefinitions) - - - true - .\IMSample.bsc - - - true - true - Windows - odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - .\imsample.def - true - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + {0C9DB8F9-B788-8782-A97D-4F8294AA0B4E} + 10.0 + imsample + + + + DynamicLibrary + false + v143 + + + DynamicLibrary + false + v143 + + + DynamicLibrary + false + v143 + + + DynamicLibrary + false + v143 + + + + + + + + + + + + + + + + + + + + + + + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + true + + + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + true + .dll + $(ProjectName).x64 + + + false + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + false + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + .dll + $(ProjectName).x64 + + + + MultiThreadedDebug + Default + true + Disabled + true + Level3 + true + EditAndContinue + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + .\IMSample.pch + true + + + true + _DEBUG;%(PreprocessorDefinitions) + .\IMSample.tlb + true + NUL + Win32 + + + 0x0c09 + _DEBUG;%(PreprocessorDefinitions) + + + true + .\IMSample.bsc + + + true + true + true + Windows + $(OutDir)$(TargetName)$(TargetExt) + .\IMSample.lib + odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + .\imsample.def + true + + + + + MultiThreadedDebug + Default + false + Disabled + true + Level3 + ProgramDatabase + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + .\IMSample.pch + true + + + true + _DEBUG;%(PreprocessorDefinitions) + .\IMSample.tlb + true + NUL + + + 0x0c09 + _DEBUG;%(PreprocessorDefinitions) + + + true + .\IMSample.bsc + + + true + true + true + Windows + $(OutDir)$(TargetName)$(TargetExt) + $(OutDir)$(TargetName).lib + odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + .\imsample.def + true + + + + + MultiThreaded + OnlyExplicitInline + true + true + MaxSpeed + true + Level3 + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + .\IMSample.pch + true + + + true + NDEBUG;%(PreprocessorDefinitions) + .\IMSample.tlb + true + NUL + Win32 + + + 0x0c09 + NDEBUG;%(PreprocessorDefinitions) + + + true + .\IMSample.bsc + + + true + true + Windows + odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + .\imsample.def + true + + + + + MultiThreaded + OnlyExplicitInline + true + true + MaxSpeed + true + Level3 + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + .\IMSample.pch + true + + + true + NDEBUG;%(PreprocessorDefinitions) + .\IMSample.tlb + true + NUL + + + 0x0c09 + NDEBUG;%(PreprocessorDefinitions) + + + true + .\IMSample.bsc + + + true + true + Windows + odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + .\imsample.def + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/windows/src/engine/keyman32/keyman-engine.vcxproj b/windows/src/engine/keyman32/keyman-engine.vcxproj index f958f9a797..e268b10e22 100644 --- a/windows/src/engine/keyman32/keyman-engine.vcxproj +++ b/windows/src/engine/keyman32/keyman-engine.vcxproj @@ -1,402 +1,402 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {A7069186-5AE7-4504-8926-861B7A34BA9E} - Keyman32 - 10.0 - - - - StaticLibrary - false - v142 - - - StaticLibrary - false - v142 - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - false - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - true - AllRules.ruleset - - - AllRules.ruleset - - - - - $(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\src;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\i18n;$(ProjectDir)..\..\..\..\core\build\rust\x86\$(Configuration);$(LibraryPath) - $(ProjectDir)..\..\..\..\common\include;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\include;$(ProjectDir)..\..\..\..\core\include;$(IncludePath) - - - $(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\src;$(ProjectDir)..\..\..\..\core\build\rust\x86\$(Configuration);$(LibraryPath) - $(ProjectDir)..\..\..\..\common\include;$(ProjectDir)..\..\..\..\core\include;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\include;$(IncludePath) - - - - NDEBUG;%(PreprocessorDefinitions) - true - true - Win32 - ./Keyman32.tlb - - - - - MaxSpeed - OnlyExplicitInline - .\;..\..\global\inc;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - - - MultiThreaded - true - Use - pch.h - All - ./ - ./ - ./ - Level4 - true - true - ProgramDatabase - - - NDEBUG;%(PreprocessorDefinitions) - 0x0409 - - - /verbose:lib /section:.SHARDATA,rws %(AdditionalOptions) - keymancore.lib;libicuuc.a;libicuin.a;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) - ./Keyman32.dll - true - keyman32.def - true - ./Keyman32.pdb - true - ./Keyman32.map - false - Windows - true - true - 0x1c100000 - ./Keyman32.lib - MachineX86 - true - true - - - true - ./Keyman32.bsc - - - - - - - - - _DEBUG;%(PreprocessorDefinitions) - true - true - Win32 - ./Keyman32.tlb - - - - - Disabled - .\;..\..\global\inc;.;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) - - - MultiThreadedDebug - Use - pch.h - All - ./ - ./ - ./ - true - Level4 - true - true - EditAndContinue - - - _DEBUG;%(PreprocessorDefinitions) - 0x0409 - - - /verbose:lib /section:.SHARDATA,rws %(AdditionalOptions) - keymancore.lib;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) - ./Keyman32.dll - true - keyman32.def - true - ./Keyman32.pdb - true - ./Keyman32.map - Windows - 0x1C100000 - ./Keyman32.lib - MachineX86 - true - true - - - true - ./Keyman32.bsc - - - - - - - - - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - - - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - - Create - Create - - - - - - - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - - - - - Document - false - false - mc %(FullPath) - Compiling keyman-debug-etw - %(Filename).rc;%(Filename.h);MSG00001.bin - mc %(FullPath) - Compiling keyman-debug-etw - %(Filename).rc;%(Filename.h);MSG00001.bin - Designer - - - - - - - - - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + + {A7069186-5AE7-4504-8926-861B7A34BA9E} + Keyman32 + 10.0 + + + + StaticLibrary + false + v143 + + + StaticLibrary + false + v143 + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + false + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + true + AllRules.ruleset + + + AllRules.ruleset + + + + + $(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\src;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\i18n;$(ProjectDir)..\..\..\..\core\build\rust\x86\$(Configuration);$(LibraryPath) + $(ProjectDir)..\..\..\..\common\include;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\include;$(ProjectDir)..\..\..\..\core\include;$(IncludePath) + + + $(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\src;$(ProjectDir)..\..\..\..\core\build\rust\x86\$(Configuration);$(LibraryPath) + $(ProjectDir)..\..\..\..\common\include;$(ProjectDir)..\..\..\..\core\include;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\include;$(IncludePath) + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + ./Keyman32.tlb + + + + + MaxSpeed + OnlyExplicitInline + .\;..\..\global\inc;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + MultiThreaded + true + Use + pch.h + All + ./ + ./ + ./ + Level4 + true + true + ProgramDatabase + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /verbose:lib /section:.SHARDATA,rws %(AdditionalOptions) + keymancore.lib;libicuuc.a;libicuin.a;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) + ./Keyman32.dll + true + keyman32.def + true + ./Keyman32.pdb + true + ./Keyman32.map + false + Windows + true + true + 0x1c100000 + ./Keyman32.lib + MachineX86 + true + true + + + true + ./Keyman32.bsc + + + + + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + ./Keyman32.tlb + + + + + Disabled + .\;..\..\global\inc;.;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + MultiThreadedDebug + Use + pch.h + All + ./ + ./ + ./ + true + Level4 + true + true + EditAndContinue + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /verbose:lib /section:.SHARDATA,rws %(AdditionalOptions) + keymancore.lib;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) + ./Keyman32.dll + true + keyman32.def + true + ./Keyman32.pdb + true + ./Keyman32.map + Windows + 0x1C100000 + ./Keyman32.lib + MachineX86 + true + true + + + true + ./Keyman32.bsc + + + + + + + + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + Create + Create + + + + + + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + + Document + false + false + mc %(FullPath) + Compiling keyman-debug-etw + %(Filename).rc;%(Filename.h);MSG00001.bin + mc %(FullPath) + Compiling keyman-debug-etw + %(Filename).rc;%(Filename.h);MSG00001.bin + Designer + + + + + + + + + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/windows/src/engine/keyman32/keyman32.vcxproj b/windows/src/engine/keyman32/keyman32.vcxproj index 71ae7ec501..ba15db6516 100644 --- a/windows/src/engine/keyman32/keyman32.vcxproj +++ b/windows/src/engine/keyman32/keyman32.vcxproj @@ -1,466 +1,466 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {BD5564FB-35A5-4A3C-B96A-4A6578E2B593} - Keyman32 - 10.0 - keyman32 - - - - DynamicLibrary - false - v142 - - - DynamicLibrary - false - v142 - - - DynamicLibrary - false - v142 - - - DynamicLibrary - false - v142 - - - - - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - false - false - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - true - true - AllRules.ruleset - AllRules.ruleset - - - - - AllRules.ruleset - AllRules.ruleset - - - - - - - $(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\src;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\i18n;$(ProjectDir)..\..\..\..\core\build\rust\x86\$(Configuration);$(LibraryPath) - $(ProjectDir)..\..\..\..\common\include;$(ProjectDir)..\..\..\..\core\include;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\include;$(IncludePath) - keyman32 - - - $(ProjectDir)..\..\..\..\core\build\x64\$(Configuration)\src;$(ProjectDir)..\..\..\..\core\build\x64\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\core\build\x64\$(Configuration)\subprojects\icu\source\i18n;$(ProjectDir)..\..\..\..\core\build\rust\x64\$(Configuration);$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) - $(ProjectDir)..\..\..\..\common\include;$(ProjectDir)..\..\..\..\core\include;$(ProjectDir)..\..\..\..\core\build\x64\$(Configuration)\include;$(IncludePath) - keyman64 - - - $(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\src;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\i18n;$(ProjectDir)..\..\..\..\core\build\rust\x86\$(Configuration);$(LibraryPath) - $(ProjectDir)..\..\..\..\common\include;$(ProjectDir)..\..\..\..\core\include;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\include;$(IncludePath) - keyman32 - - - $(ProjectDir)..\..\..\..\core\build\x64\$(Configuration)\src;$(ProjectDir)..\..\..\..\core\build\x64\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\core\build\x64\$(Configuration)\subprojects\icu\source\i18n;$(ProjectDir)..\..\..\..\core\build\rust\x64\$(Configuration);$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) - $(ProjectDir)..\..\..\..\common\include;$(ProjectDir)..\..\..\..\core\include;$(ProjectDir)..\..\..\..\core\build\x64\$(Configuration)\include;$(IncludePath) - keyman64 - - - - NDEBUG;%(PreprocessorDefinitions) - true - true - Win32 - ./Keyman32.tlb - - - - - MaxSpeed - OnlyExplicitInline - .\;..\..\global\inc;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - - - MultiThreaded - true - Use - pch.h - All - Level4 - true - true - ProgramDatabase - - - NDEBUG;%(PreprocessorDefinitions) - 0x0409 - - - /verbose:lib /section:.SHARDATA,rws %(AdditionalOptions) - libicuuc.a;libicuin.a;libkeymancore.a;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) - true - keyman32.def - true - true - false - Windows - true - true - 0x1c100000 - MachineX86 - true - true - ..\..\..\lib\keyman32.lib - - - true - ./Keyman32.bsc - - - - - - - - - NDEBUG;%(PreprocessorDefinitions) - true - true - ./Keyman32.tlb - - - - - MaxSpeed - OnlyExplicitInline - .\;..\..\global\inc;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - - - MultiThreaded - true - Use - pch.h - All - Level4 - true - true - ProgramDatabase - - - NDEBUG;%(PreprocessorDefinitions) - 0x0409 - - - /verbose:lib /section:.SHARDATA,rws %(AdditionalOptions) - libicuuc.a;libicuin.a;libkeymancore.a;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) - true - keyman64.def - true - true - false - Windows - true - true - 0x1c100000 - true - true - ..\..\..\lib\keyman64.lib - - - true - ./Keyman64.bsc - - - - - - - - - _DEBUG;%(PreprocessorDefinitions) - true - true - Win32 - ./Keyman32.tlb - - - - - Disabled - .\;..\..\global\inc;.;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) - - - MultiThreadedDebug - Use - pch.h - All - true - Level4 - true - true - EditAndContinue - - - _DEBUG;%(PreprocessorDefinitions) - 0x0409 - - - /verbose:lib /section:.SHARDATA,rws %(AdditionalOptions) - libicuuc.a;libicuin.a;libkeymancore.a;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) - true - keyman32.def - true - true - Windows - 0x1C100000 - MachineX86 - true - true - ..\..\..\lib\keyman32.lib - - - true - ./Keyman32.bsc - - - - - - - - - _DEBUG;%(PreprocessorDefinitions) - true - true - ./Keyman32.tlb - - - - - Disabled - .\;..\..\global\inc;.;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) - - - MultiThreadedDebug - Use - pch.h - All - true - Level4 - true - true - ProgramDatabase - - - _DEBUG;%(PreprocessorDefinitions) - 0x0409 - - - /verbose:lib /section:.SHARDATA,rws %(AdditionalOptions) - libicuuc.a;libicuin.a;libkeymancore.a;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) - true - keyman64.def - true - true - Windows - 0x1C100000 - true - true - ..\..\..\lib\keyman64.lib - - - true - ./Keyman32.bsc - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Create - Create - Create - Create - - - - - - - - - - - - - - - - - Document - false - false - false - false - mc %(FullPath) - mc %(FullPath) - Compiling keyman-debug-etw - Compiling keyman-debug-etw - %(Filename).rc;%(Filename.h);MSG00001.bin - %(Filename).rc;%(Filename.h);MSG00001.bin - mc %(FullPath) - mc %(FullPath) - Compiling keyman-debug-etw - Compiling keyman-debug-etw - %(Filename).rc;%(Filename.h);MSG00001.bin - %(Filename).rc;%(Filename.h);MSG00001.bin - Designer - - - true - true - - - - - true - true - - - - - - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - true - true - - - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {BD5564FB-35A5-4A3C-B96A-4A6578E2B593} + Keyman32 + 10.0 + keyman32 + + + + DynamicLibrary + false + v143 + + + DynamicLibrary + false + v143 + + + DynamicLibrary + false + v143 + + + DynamicLibrary + false + v143 + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + false + false + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + true + true + AllRules.ruleset + AllRules.ruleset + + + + + AllRules.ruleset + AllRules.ruleset + + + + + + + $(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\src;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\i18n;$(ProjectDir)..\..\..\..\core\build\rust\x86\$(Configuration);$(LibraryPath) + $(ProjectDir)..\..\..\..\common\include;$(ProjectDir)..\..\..\..\core\include;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\include;$(IncludePath) + keyman32 + + + $(ProjectDir)..\..\..\..\core\build\x64\$(Configuration)\src;$(ProjectDir)..\..\..\..\core\build\x64\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\core\build\x64\$(Configuration)\subprojects\icu\source\i18n;$(ProjectDir)..\..\..\..\core\build\rust\x64\$(Configuration);$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) + $(ProjectDir)..\..\..\..\common\include;$(ProjectDir)..\..\..\..\core\include;$(ProjectDir)..\..\..\..\core\build\x64\$(Configuration)\include;$(IncludePath) + keyman64 + + + $(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\src;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\i18n;$(ProjectDir)..\..\..\..\core\build\rust\x86\$(Configuration);$(LibraryPath) + $(ProjectDir)..\..\..\..\common\include;$(ProjectDir)..\..\..\..\core\include;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\include;$(IncludePath) + keyman32 + + + $(ProjectDir)..\..\..\..\core\build\x64\$(Configuration)\src;$(ProjectDir)..\..\..\..\core\build\x64\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\core\build\x64\$(Configuration)\subprojects\icu\source\i18n;$(ProjectDir)..\..\..\..\core\build\rust\x64\$(Configuration);$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) + $(ProjectDir)..\..\..\..\common\include;$(ProjectDir)..\..\..\..\core\include;$(ProjectDir)..\..\..\..\core\build\x64\$(Configuration)\include;$(IncludePath) + keyman64 + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + ./Keyman32.tlb + + + + + MaxSpeed + OnlyExplicitInline + .\;..\..\global\inc;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + MultiThreaded + true + Use + pch.h + All + Level4 + true + true + ProgramDatabase + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /verbose:lib /section:.SHARDATA,rws %(AdditionalOptions) + libicuuc.a;libicuin.a;libkeymancore.a;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) + true + keyman32.def + true + true + false + Windows + true + true + 0x1c100000 + MachineX86 + true + true + ..\..\..\lib\keyman32.lib + + + true + ./Keyman32.bsc + + + + + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + ./Keyman32.tlb + + + + + MaxSpeed + OnlyExplicitInline + .\;..\..\global\inc;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + MultiThreaded + true + Use + pch.h + All + Level4 + true + true + ProgramDatabase + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /verbose:lib /section:.SHARDATA,rws %(AdditionalOptions) + libicuuc.a;libicuin.a;libkeymancore.a;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) + true + keyman64.def + true + true + false + Windows + true + true + 0x1c100000 + true + true + ..\..\..\lib\keyman64.lib + + + true + ./Keyman64.bsc + + + + + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + ./Keyman32.tlb + + + + + Disabled + .\;..\..\global\inc;.;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + MultiThreadedDebug + Use + pch.h + All + true + Level4 + true + true + EditAndContinue + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /verbose:lib /section:.SHARDATA,rws %(AdditionalOptions) + libicuuc.a;libicuin.a;libkeymancore.a;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) + true + keyman32.def + true + true + Windows + 0x1C100000 + MachineX86 + true + true + ..\..\..\lib\keyman32.lib + + + true + ./Keyman32.bsc + + + + + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + ./Keyman32.tlb + + + + + Disabled + .\;..\..\global\inc;.;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + MultiThreadedDebug + Use + pch.h + All + true + Level4 + true + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /verbose:lib /section:.SHARDATA,rws %(AdditionalOptions) + libicuuc.a;libicuin.a;libkeymancore.a;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) + true + keyman64.def + true + true + Windows + 0x1C100000 + true + true + ..\..\..\lib\keyman64.lib + + + true + ./Keyman32.bsc + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + + + + + + Document + false + false + false + false + mc %(FullPath) + mc %(FullPath) + Compiling keyman-debug-etw + Compiling keyman-debug-etw + %(Filename).rc;%(Filename.h);MSG00001.bin + %(Filename).rc;%(Filename.h);MSG00001.bin + mc %(FullPath) + mc %(FullPath) + Compiling keyman-debug-etw + Compiling keyman-debug-etw + %(Filename).rc;%(Filename.h);MSG00001.bin + %(Filename).rc;%(Filename.h);MSG00001.bin + Designer + + + true + true + + + + + true + true + + + + + + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + true + true + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/windows/src/engine/keyman32/keyman32.vcxproj.filters b/windows/src/engine/keyman32/keyman32.vcxproj.filters index 5627c72b04..cffae6a908 100644 --- a/windows/src/engine/keyman32/keyman32.vcxproj.filters +++ b/windows/src/engine/keyman32/keyman32.vcxproj.filters @@ -1,265 +1,271 @@ - - - - - {2dc5904b-4033-47e6-88ac-ebe442c06c2e} - cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90 - - - {27baf0f2-3926-4720-b5f9-045e47cd975c} - h;hpp;hxx;hm;inl;fi;fd - - - {088a2b68-c9db-41f4-9f45-37fdf758931e} - ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Source Files - - - Resource Files - - - Resource Files - - - Source Files - - - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Source Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - + + + + + {2dc5904b-4033-47e6-88ac-ebe442c06c2e} + cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90 + + + {27baf0f2-3926-4720-b5f9-045e47cd975c} + h;hpp;hxx;hm;inl;fi;fd + + + {088a2b68-c9db-41f4-9f45-37fdf758931e} + ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + Resource Files + + + Resource Files + + + Source Files + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Source Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + \ No newline at end of file diff --git a/windows/src/engine/keyman32/tests/keyman-engine-tests/keyman-engine-tests.vcxproj b/windows/src/engine/keyman32/tests/keyman-engine-tests/keyman-engine-tests.vcxproj index 48afed6fdd..5bf2365a59 100644 --- a/windows/src/engine/keyman32/tests/keyman-engine-tests/keyman-engine-tests.vcxproj +++ b/windows/src/engine/keyman32/tests/keyman-engine-tests/keyman-engine-tests.vcxproj @@ -1,160 +1,160 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - {46399ed7-79a1-4e7e-bd96-4b1aeec838ae} - Win32Proj - 10.0.19041.0 - Application - v142 - Unicode - keyman-engine-tests - - - - - - - - - $(ProjectDir)..\..\..\..\..\..\common\include;$(ProjectDir)..\..\..\..\..\..\core\include;$(ProjectDir)..\..\..\..\..\..\core\build\x86\$(Configuration)\include;$(ProjectDir)..\..\..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\i18n;$(ProjectDir)..\..\..\..\global\inc;$(ProjectDir)..\..;$(IncludePath) - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - $(ProjectDir)..\..\..\..\..\..\core\build\x86\$(Configuration)\src;$(ProjectDir)..\..\..\..\kmtip\bin\Win32\$(Configuration);$(ProjectDir)..\..\..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\i18n;$(LibraryPath) - true - - - $(ProjectDir)..\..\..\..\..\..\common\include;$(ProjectDir)..\..\..\..\..\..\core\include;$(ProjectDir)..\..\..\..\..\..\core\build\x86\$(Configuration)\include;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\i18n;$(ProjectDir)..\..\..\..\global\inc;$(ProjectDir)..\..;$(IncludePath) - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - $(ProjectDir)..\..\..\..\..\..\common\include;$(ProjectDir)..\..\..\..\..\..\core\build\x64\$(Configuration)\include;$(ProjectDir)..\..\..\..\..\..\core\include;$(ProjectDir)..\..\..\..\..\..\core\build\x64\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\..\..\core\build\x64\$(Configuration)\subprojects\icu\source\i18n;$(ProjectDir)..\..\..\..\global\inc;$(ProjectDir)..\..;$(IncludePath) - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - $(ProjectDir)..\..\..\..\..\..\common\include;$(ProjectDir)..\..\..\..\..\..\core\build\x64\$(Configuration)\include;$(ProjectDir)..\..\..\..\..\..\core\include;$(ProjectDir)..\..\..\..\..\..\core\build\x64\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\..\..\core\build\x64\$(Configuration)\subprojects\icu\source\i18n;$(ProjectDir)..\..\..\..\global\inc;$(ProjectDir)..\..;$(IncludePath) - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - - - - - - - - - - Create - Create - Create - Create - - - - - - - - {a7069186-5ae7-4504-8926-861b7a34ba9e} - - - - - - - - - - Use - pch.h - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - Level3 - %(AdditionalIncludeDirectories) - - - true - Console - version.lib;psapi.lib;imm32.lib;libkeymancore.a;libicuuc.a;libicuin.a;%(AdditionalDependencies) - - - false - - - - - Use - pch.h - Disabled - X64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - Level3 - - - true - Console - - - - - Use - pch.h - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - Level3 - ProgramDatabase - - - true - Console - true - true - - - - - Use - pch.h - X64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - Level3 - ProgramDatabase - - - true - Console - true - true - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {46399ed7-79a1-4e7e-bd96-4b1aeec838ae} + Win32Proj + 10.0 + Application + v143 + Unicode + keyman-engine-tests + + + + + + + + + $(ProjectDir)..\..\..\..\..\..\common\include;$(ProjectDir)..\..\..\..\..\..\core\include;$(ProjectDir)..\..\..\..\..\..\core\build\x86\$(Configuration)\include;$(ProjectDir)..\..\..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\i18n;$(ProjectDir)..\..\..\..\global\inc;$(ProjectDir)..\..;$(IncludePath) + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\..\..\..\..\core\build\x86\$(Configuration)\src;$(ProjectDir)..\..\..\..\kmtip\bin\Win32\$(Configuration);$(ProjectDir)..\..\..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\i18n;$(LibraryPath) + true + + + $(ProjectDir)..\..\..\..\..\..\common\include;$(ProjectDir)..\..\..\..\..\..\core\include;$(ProjectDir)..\..\..\..\..\..\core\build\x86\$(Configuration)\include;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\subprojects\icu\source\i18n;$(ProjectDir)..\..\..\..\global\inc;$(ProjectDir)..\..;$(IncludePath) + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + $(ProjectDir)..\..\..\..\..\..\common\include;$(ProjectDir)..\..\..\..\..\..\core\build\x64\$(Configuration)\include;$(ProjectDir)..\..\..\..\..\..\core\include;$(ProjectDir)..\..\..\..\..\..\core\build\x64\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\..\..\core\build\x64\$(Configuration)\subprojects\icu\source\i18n;$(ProjectDir)..\..\..\..\global\inc;$(ProjectDir)..\..;$(IncludePath) + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + $(ProjectDir)..\..\..\..\..\..\common\include;$(ProjectDir)..\..\..\..\..\..\core\build\x64\$(Configuration)\include;$(ProjectDir)..\..\..\..\..\..\core\include;$(ProjectDir)..\..\..\..\..\..\core\build\x64\$(Configuration)\subprojects\icu\source\common;$(ProjectDir)..\..\..\..\..\..\core\build\x64\$(Configuration)\subprojects\icu\source\i18n;$(ProjectDir)..\..\..\..\global\inc;$(ProjectDir)..\..;$(IncludePath) + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + {a7069186-5ae7-4504-8926-861b7a34ba9e} + + + + + + + + + + Use + pch.h + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + Level3 + %(AdditionalIncludeDirectories) + + + true + Console + version.lib;psapi.lib;imm32.lib;libkeymancore.a;libicuuc.a;libicuin.a;%(AdditionalDependencies) + + + false + + + + + Use + pch.h + Disabled + X64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + Level3 + + + true + Console + + + + + Use + pch.h + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreaded + Level3 + ProgramDatabase + + + true + Console + true + true + + + + + Use + pch.h + X64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreaded + Level3 + ProgramDatabase + + + true + Console + true + true + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/windows/src/engine/keymanx64/keymanx64.vcxproj b/windows/src/engine/keymanx64/keymanx64.vcxproj index 82b3bcb59f..2fd5955560 100644 --- a/windows/src/engine/keymanx64/keymanx64.vcxproj +++ b/windows/src/engine/keymanx64/keymanx64.vcxproj @@ -1,153 +1,153 @@ - - - - - Debug - x64 - - - Release - x64 - - - - {A34650EA-D6E8-4229-8091-6BF1443565B5} - keymanx64 - Win32Proj - 10.0 - - - - Application - Unicode - true - v142 - - - Application - Unicode - v142 - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - true - false - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - false - false - AllRules.ruleset - - - AllRules.ruleset - - - *.cdf;*.cache;*.obj;*.ilk;*.resources;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;*.tlog;*.manifest;*.res;*.pch;*.exp;*.idb;*.rep;*.xdc;*.pdb;*_manifest.rc;*.bsc;*.sbr;*.metagen;*.bi - *.cdf;*.cache;*.obj;*.ilk;*.resources;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;*.tlog;*.manifest;*.res;*.pch;*.exp;*.idb;*.rep;*.xdc;*.pdb;*_manifest.rc;*.bsc;*.sbr;*.metagen;*.bi - - - - X64 - - - Disabled - ..\..\global\inc;..\..\..\..\common\windows\delphi\ext\sentry;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) - false - true - EnableFastChecks - MultiThreadedDebug - - - Level4 - true - ProgramDatabase - true - - - ..\..\..\..\common\windows\delphi\ext\sentry\sentry.x64.lib;keyman64.lib;libcmtd.lib;%(AdditionalDependencies) - ..\..\..\lib;%(AdditionalLibraryDirectories) - false - true - Windows - MachineX64 - true - sentry.dll - - - - - - - - - - - - - - - X64 - - - ..\..\global\inc;..\..\..\..\common\windows\delphi\ext\sentry;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - false - MultiThreaded - - - Level4 - true - ProgramDatabase - true - - - ..\..\..\..\common\windows\delphi\ext\sentry\sentry.x64.lib;keyman64.lib;libcmt.lib;%(AdditionalDependencies) - ..\..\..\lib;%(AdditionalLibraryDirectories) - false - true - Windows - true - true - MachineX64 - true - true - sentry.dll - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + x64 + + + Release + x64 + + + + {A34650EA-D6E8-4229-8091-6BF1443565B5} + keymanx64 + Win32Proj + 10.0 + + + + Application + Unicode + true + v143 + + + Application + Unicode + v143 + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + true + false + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + false + false + AllRules.ruleset + + + AllRules.ruleset + + + *.cdf;*.cache;*.obj;*.ilk;*.resources;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;*.tlog;*.manifest;*.res;*.pch;*.exp;*.idb;*.rep;*.xdc;*.pdb;*_manifest.rc;*.bsc;*.sbr;*.metagen;*.bi + *.cdf;*.cache;*.obj;*.ilk;*.resources;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;*.tlog;*.manifest;*.res;*.pch;*.exp;*.idb;*.rep;*.xdc;*.pdb;*_manifest.rc;*.bsc;*.sbr;*.metagen;*.bi + + + + X64 + + + Disabled + ..\..\global\inc;..\..\..\..\common\windows\delphi\ext\sentry;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebug + + + Level4 + true + ProgramDatabase + true + + + ..\..\..\..\common\windows\delphi\ext\sentry\sentry.x64.lib;keyman64.lib;libcmtd.lib;%(AdditionalDependencies) + ..\..\..\lib;%(AdditionalLibraryDirectories) + false + true + Windows + MachineX64 + true + sentry.dll + + + + + + + + + + + + + + + X64 + + + ..\..\global\inc;..\..\..\..\common\windows\delphi\ext\sentry;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + false + MultiThreaded + + + Level4 + true + ProgramDatabase + true + + + ..\..\..\..\common\windows\delphi\ext\sentry\sentry.x64.lib;keyman64.lib;libcmt.lib;%(AdditionalDependencies) + ..\..\..\lib;%(AdditionalLibraryDirectories) + false + true + Windows + true + true + MachineX64 + true + true + sentry.dll + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/windows/src/engine/kmrefresh/kmrefresh.vcxproj b/windows/src/engine/kmrefresh/kmrefresh.vcxproj index a978cb947e..0b252895f7 100644 --- a/windows/src/engine/kmrefresh/kmrefresh.vcxproj +++ b/windows/src/engine/kmrefresh/kmrefresh.vcxproj @@ -1,157 +1,157 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {A3B8F75F-E63E-434E-8A2A-034AD9B66571} - kmrefresh - 10.0 - - - - Application - true - Unicode - v142 - - - Application - false - true - Unicode - v142 - - - Application - true - Unicode - v142 - - - Application - false - true - Unicode - v142 - - - - - - - - - - - - - - - - - - - - - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectName).x64 - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - $(ProjectName).x86 - - - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - $(ProjectName).x86 - - - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectName).x64 - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - - Level3 - Disabled - true - true - MultiThreadedDebug - - - Windows - - - - - Level3 - Disabled - true - true - MultiThreadedDebug - - - Windows - - - - - Level3 - MaxSpeed - true - true - true - true - MultiThreaded - - - Windows - true - true - - - - - Level3 - MaxSpeed - true - true - true - true - MultiThreaded - - - Windows - true - true - - - - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {A3B8F75F-E63E-434E-8A2A-034AD9B66571} + kmrefresh + 10.0 + + + + Application + true + Unicode + v143 + + + Application + false + true + Unicode + v143 + + + Application + true + Unicode + v143 + + + Application + false + true + Unicode + v143 + + + + + + + + + + + + + + + + + + + + + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectName).x64 + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + $(ProjectName).x86 + + + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + $(ProjectName).x86 + + + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectName).x64 + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + + Level3 + Disabled + true + true + MultiThreadedDebug + + + Windows + + + + + Level3 + Disabled + true + true + MultiThreadedDebug + + + Windows + + + + + Level3 + MaxSpeed + true + true + true + true + MultiThreaded + + + Windows + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + MultiThreaded + + + Windows + true + true + + + + + + + + + + + \ No newline at end of file diff --git a/windows/src/engine/kmtip/kmtip.vcxproj b/windows/src/engine/kmtip/kmtip.vcxproj index f3fb74116f..173853f507 100644 --- a/windows/src/engine/kmtip/kmtip.vcxproj +++ b/windows/src/engine/kmtip/kmtip.vcxproj @@ -1,432 +1,432 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {93391ECB-E2F1-4D23-B85E-6FDEB7ACF9B4} - 10.0 - - - - DynamicLibrary - false - MultiByte - v142 - - - DynamicLibrary - false - MultiByte - v142 - - - DynamicLibrary - false - MultiByte - v142 - - - DynamicLibrary - false - MultiByte - v142 - - - - - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - true - true - false - false - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - true - true - true - true - AllRules.ruleset - AllRules.ruleset - - - - - AllRules.ruleset - AllRules.ruleset - - - - - .dll - .dll - .dll - .dll - kmtip64 - kmtip64 - - - - NDEBUG;%(PreprocessorDefinitions) - true - true - Win32 - ./kmtip.tlb - - - - - MaxSpeed - OnlyExplicitInline - .\;..\..\global\inc;..\..\resellers;C:\Program Files\Microsoft Platform SDK\Include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;_USRDLL;KMTIP_EXPORTS;%(PreprocessorDefinitions) - true - MultiThreaded - true - All - Level3 - true - true - Use - pch.h - - - NDEBUG;%(PreprocessorDefinitions) - 0x0c09 - - - ..\..\..\lib\keyman32.lib;version.lib;wintrust.lib;crypt32.lib;imagehlp.lib;%(AdditionalDependencies) - true - .\kmtip.def - true - 0x4C100000 - MachineX86 - true - true - - - true - ./kmtip.bsc - - - - - NDEBUG;%(PreprocessorDefinitions) - true - true - ./kmtip.tlb - - - - - MaxSpeed - OnlyExplicitInline - .\;..\..\global\inc;..\..\resellers;C:\Program Files\Microsoft Platform SDK\Include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;_USRDLL;KMTIP_EXPORTS;%(PreprocessorDefinitions) - true - MultiThreaded - true - All - Level3 - true - true - Use - pch.h - - - NDEBUG;%(PreprocessorDefinitions) - 0x0c09 - - - ..\..\..\lib\keyman64.lib;version.lib;wintrust.lib;crypt32.lib;imagehlp.lib;%(AdditionalDependencies) - true - .\kmtip64.def - true - 0x4C100000 - true - true - - - true - ./kmtip.bsc - - - - - _DEBUG;%(PreprocessorDefinitions) - true - true - Win32 - ./kmtip.tlb - - - - - Disabled - .\;..\..\global\inc;..\..\resellers;C:\Program Files\Microsoft Platform SDK\Include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;_USRDLL;KMTIP_EXPORTS;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - true - Level3 - true - EditAndContinue - true - Use - pch.h - - - _DEBUG;%(PreprocessorDefinitions) - 0x0c09 - - - ..\..\..\lib\keyman32.lib;version.lib;wintrust.lib;crypt32.lib;imagehlp.lib;%(AdditionalDependencies) - true - .\kmtip.def - true - true - 0x4C100000 - MachineX86 - true - - - true - ./kmtip.bsc - - - - - _DEBUG;%(PreprocessorDefinitions) - true - true - ./kmtip.tlb - - - - - Disabled - .\;..\..\global\inc;..\..\resellers;C:\Program Files\Microsoft Platform SDK\Include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;_USRDLL;KMTIP_EXPORTS;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - true - Level3 - true - ProgramDatabase - true - Use - pch.h - - - _DEBUG;%(PreprocessorDefinitions) - 0x0c09 - - - ..\..\..\lib\keyman64.lib;version.lib;wintrust.lib;crypt32.lib;imagehlp.lib;%(AdditionalDependencies) - true - .\kmtip64.def - true - true - 0x4C100000 - true - - - true - ./kmtip.bsc - - - - - - - - - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - Create - Create - Create - Create - - - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - - - true - true - - - true - true - - - - - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {93391ECB-E2F1-4D23-B85E-6FDEB7ACF9B4} + 10.0 + + + + DynamicLibrary + false + MultiByte + v143 + + + DynamicLibrary + false + MultiByte + v143 + + + DynamicLibrary + false + MultiByte + v143 + + + DynamicLibrary + false + MultiByte + v143 + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + true + true + false + false + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + true + true + true + true + AllRules.ruleset + AllRules.ruleset + + + + + AllRules.ruleset + AllRules.ruleset + + + + + .dll + .dll + .dll + .dll + kmtip64 + kmtip64 + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + ./kmtip.tlb + + + + + MaxSpeed + OnlyExplicitInline + .\;..\..\global\inc;..\..\resellers;C:\Program Files\Microsoft Platform SDK\Include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;KMTIP_EXPORTS;%(PreprocessorDefinitions) + true + MultiThreaded + true + All + Level3 + true + true + Use + pch.h + + + NDEBUG;%(PreprocessorDefinitions) + 0x0c09 + + + ..\..\..\lib\keyman32.lib;version.lib;wintrust.lib;crypt32.lib;imagehlp.lib;%(AdditionalDependencies) + true + .\kmtip.def + true + 0x4C100000 + MachineX86 + true + true + + + true + ./kmtip.bsc + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + ./kmtip.tlb + + + + + MaxSpeed + OnlyExplicitInline + .\;..\..\global\inc;..\..\resellers;C:\Program Files\Microsoft Platform SDK\Include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;KMTIP_EXPORTS;%(PreprocessorDefinitions) + true + MultiThreaded + true + All + Level3 + true + true + Use + pch.h + + + NDEBUG;%(PreprocessorDefinitions) + 0x0c09 + + + ..\..\..\lib\keyman64.lib;version.lib;wintrust.lib;crypt32.lib;imagehlp.lib;%(AdditionalDependencies) + true + .\kmtip64.def + true + 0x4C100000 + true + true + + + true + ./kmtip.bsc + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + ./kmtip.tlb + + + + + Disabled + .\;..\..\global\inc;..\..\resellers;C:\Program Files\Microsoft Platform SDK\Include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;KMTIP_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + true + Level3 + true + EditAndContinue + true + Use + pch.h + + + _DEBUG;%(PreprocessorDefinitions) + 0x0c09 + + + ..\..\..\lib\keyman32.lib;version.lib;wintrust.lib;crypt32.lib;imagehlp.lib;%(AdditionalDependencies) + true + .\kmtip.def + true + true + 0x4C100000 + MachineX86 + true + + + true + ./kmtip.bsc + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + ./kmtip.tlb + + + + + Disabled + .\;..\..\global\inc;..\..\resellers;C:\Program Files\Microsoft Platform SDK\Include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;KMTIP_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + true + Level3 + true + ProgramDatabase + true + Use + pch.h + + + _DEBUG;%(PreprocessorDefinitions) + 0x0c09 + + + ..\..\..\lib\keyman64.lib;version.lib;wintrust.lib;crypt32.lib;imagehlp.lib;%(AdditionalDependencies) + true + .\kmtip64.def + true + true + 0x4C100000 + true + + + true + ./kmtip.bsc + + + + + + + + + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + + + + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + + + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + + + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + Create + Create + Create + Create + + + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + + + + + true + true + + + true + true + + + + + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/windows/src/engine/mcompile/mcompile.vcxproj b/windows/src/engine/mcompile/mcompile.vcxproj index e9c426a330..99f60fa722 100644 --- a/windows/src/engine/mcompile/mcompile.vcxproj +++ b/windows/src/engine/mcompile/mcompile.vcxproj @@ -1,134 +1,134 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {5C22DDDA-CEAD-45F1-96B0-1473111156AA} - Win32Proj - mtop - mcompile - 10.0 - - - - Application - true - Unicode - v142 - - - Application - false - true - Unicode - v142 - - - - - - - - - - - - - true - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - false - *.cdf;*.cache;*.obj;*.ilk;*.resources;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;*.tlog;*.manifest;*.res;*.pch;*.exp;*.idb;*.rep;*.xdc;*.pdb;*_manifest.rc;*.bsc;*.sbr;*.metagen;*.bi - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - false - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - false - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - - Use - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDebug - ..\..\..\..\common\windows\delphi\ext\sentry;..\..\global\inc - pch.h - - - Console - true - ..\..\..\..\common\windows\delphi\ext\sentry\sentry.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - sentry.dll - - - - - Level3 - Use - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - ..\..\..\..\common\windows\delphi\ext\sentry;..\..\global\inc - pch.h - - - Console - true - true - true - ..\..\..\..\common\windows\delphi\ext\sentry\sentry.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - sentry.dll - - - - - - - - - - - - - - - - NotUsing - NotUsing - - - - - - - - - - Create - Create - pch.h - pch.h - - - - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + + {5C22DDDA-CEAD-45F1-96B0-1473111156AA} + Win32Proj + mtop + mcompile + 10.0 + + + + Application + true + Unicode + v143 + + + Application + false + true + Unicode + v143 + + + + + + + + + + + + + true + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + false + *.cdf;*.cache;*.obj;*.ilk;*.resources;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;*.tlog;*.manifest;*.res;*.pch;*.exp;*.idb;*.rep;*.xdc;*.pdb;*_manifest.rc;*.bsc;*.sbr;*.metagen;*.bi + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + false + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + false + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDebug + ..\..\..\..\common\windows\delphi\ext\sentry;..\..\global\inc + pch.h + + + Console + true + ..\..\..\..\common\windows\delphi\ext\sentry\sentry.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + sentry.dll + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreaded + ..\..\..\..\common\windows\delphi\ext\sentry;..\..\global\inc + pch.h + + + Console + true + true + true + ..\..\..\..\common\windows\delphi\ext\sentry\sentry.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + sentry.dll + + + + + + + + + + + + + + + + NotUsing + NotUsing + + + + + + + + + + Create + Create + pch.h + pch.h + + + + + + + + + + + \ No newline at end of file diff --git a/windows/src/engine/testhost/testhost.vcxproj b/windows/src/engine/testhost/testhost.vcxproj index 8b965e942c..e9d06a87a6 100644 --- a/windows/src/engine/testhost/testhost.vcxproj +++ b/windows/src/engine/testhost/testhost.vcxproj @@ -1,172 +1,172 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - Win32Proj - {12818da3-0472-49a5-96fc-a4808a1a77fa} - testhost - 10.0 - - - - Application - true - v142 - Unicode - - - Application - false - v142 - true - Unicode - - - Application - true - v142 - Unicode - - - Application - false - v142 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - false - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - true - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - false - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - - Level3 - true - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - stdcpp17 - - - Windows - true - - - - - Level3 - true - true - true - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - stdcpp17 - - - Windows - true - true - true - - - - - Level3 - true - _DEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - stdcpp17 - - - Windows - true - - - - - Level3 - true - true - true - NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - stdcpp17 - - - Windows - true - true - true - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {12818da3-0472-49a5-96fc-a4808a1a77fa} + testhost + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + false + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + true + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + false + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + + Level3 + true + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + true + true + + + + + Level3 + true + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + + + + + Level3 + true + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/windows/src/support/etl2log/etl2log.vcxproj b/windows/src/support/etl2log/etl2log.vcxproj index d8e0372cb2..839d6f2886 100644 --- a/windows/src/support/etl2log/etl2log.vcxproj +++ b/windows/src/support/etl2log/etl2log.vcxproj @@ -1,182 +1,182 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {8754791A-42D7-4E93-84F1-97DEF9D644EB} - Win32Proj - etl2log - 10.0 - - - - Application - true - v142 - Unicode - - - Application - false - v142 - true - Unicode - - - Application - true - v142 - Unicode - - - Application - false - v142 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - true - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - false - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - false - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - - Use - Level3 - Disabled - true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - - - Console - true - tdh.lib;%(AdditionalDependencies) - - - - - Use - Level3 - Disabled - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - - - Console - true - tdh.lib;%(AdditionalDependencies) - - - - - Use - Level3 - MaxSpeed - true - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - - - Console - true - true - true - tdh.lib;%(AdditionalDependencies) - - - - - Use - Level3 - MaxSpeed - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - - - Console - true - true - true - tdh.lib;%(AdditionalDependencies) - - - - - - - - Create - Create - Create - Create - - - - - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {8754791A-42D7-4E93-84F1-97DEF9D644EB} + Win32Proj + etl2log + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + true + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + false + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + false + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + + Use + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + tdh.lib;%(AdditionalDependencies) + + + + + Use + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + tdh.lib;%(AdditionalDependencies) + + + + + Use + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + tdh.lib;%(AdditionalDependencies) + + + + + Use + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + tdh.lib;%(AdditionalDependencies) + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + \ No newline at end of file diff --git a/windows/src/support/texteditor/Editor.vcxproj b/windows/src/support/texteditor/Editor.vcxproj index 4b01793829..30879810b3 100644 --- a/windows/src/support/texteditor/Editor.vcxproj +++ b/windows/src/support/texteditor/Editor.vcxproj @@ -1,175 +1,175 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - Win32Proj - {6804767e-857f-48cc-b843-22bf357a668c} - Editor - 10.0 - - - - Application - true - v142 - Unicode - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - Application - false - v142 - true - Unicode - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - Application - true - v142 - Unicode - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - Application - false - v142 - true - Unicode - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - - - - - - - - - - - - - - - - - - - true - $(ProjectName)32 - - - false - $(ProjectName)32 - - - true - $(ProjectName)64 - - - false - $(ProjectName)64 - - - - Level3 - true - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - - - Windows - true - $(OutDir)$(TargetName)$(TargetExt) - - - - - Level3 - true - true - true - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - - - Windows - true - true - true - - - - - Level3 - true - _DEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - - - Windows - true - - - - - Level3 - true - true - true - NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - - - Windows - true - true - true - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {6804767e-857f-48cc-b843-22bf357a668c} + Editor + 10.0 + + + + Application + true + v143 + Unicode + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + Application + false + v143 + true + Unicode + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + Application + true + v143 + Unicode + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + Application + false + v143 + true + Unicode + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + + + + + + + + + + + + + + + + + + + true + $(ProjectName)32 + + + false + $(ProjectName)32 + + + true + $(ProjectName)64 + + + false + $(ProjectName)64 + + + + Level3 + true + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + $(OutDir)$(TargetName)$(TargetExt) + + + + + Level3 + true + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + + + + + Level3 + true + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + Level3 + true + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/windows/src/test/unit-tests/shared-data/cpp/cppshareddata.vcxproj b/windows/src/test/unit-tests/shared-data/cpp/cppshareddata.vcxproj index 77f601a5c5..8c76a595a8 100644 --- a/windows/src/test/unit-tests/shared-data/cpp/cppshareddata.vcxproj +++ b/windows/src/test/unit-tests/shared-data/cpp/cppshareddata.vcxproj @@ -1,176 +1,176 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {748219AC-3BDE-40FC-8E93-7516C4BCE81D} - Win32Proj - cppshareddata - 10.0 - - - - Application - true - v142 - Unicode - - - Application - false - v142 - true - Unicode - - - Application - true - v142 - Unicode - - - Application - false - v142 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - true - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - false - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - false - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - - - - Use - Level3 - Disabled - true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - false - pch.h - - - Console - true - - - - - Use - Level3 - Disabled - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - false - pch.h - - - Console - true - - - - - Use - Level3 - MaxSpeed - true - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - false - pch.h - - - Console - true - true - true - - - - - Use - Level3 - MaxSpeed - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - false - pch.h - - - Console - true - true - true - - - - - - - - - Create - Create - Create - Create - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {748219AC-3BDE-40FC-8E93-7516C4BCE81D} + Win32Proj + cppshareddata + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + true + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + false + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + false + $(ProjectDir)bin\$(Platform)\$(Configuration)\ + $(ProjectDir)obj\$(Platform)\$(Configuration)\ + + + + Use + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + pch.h + + + Console + true + + + + + Use + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + pch.h + + + Console + true + + + + + Use + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + pch.h + + + Console + true + true + true + + + + + Use + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + pch.h + + + Console + true + true + true + + + + + + + + + Create + Create + Create + Create + + + + + \ No newline at end of file