mirror of
https://github.com/keymanapp/keyman.git
synced 2026-10-08 15:07:33 +00:00
Merge branch 'feat/web/spur-edge-key' into change/web/add-same-edge-detection
This commit is contained in:
commit
db25462aec
5 changed files with 19 additions and 22 deletions
|
|
@ -3,26 +3,24 @@
|
|||
*
|
||||
* Created by jahorton on 2025-10-20
|
||||
*
|
||||
* This file defines the predictive-text engine's SearchSpace class, which is used to
|
||||
* manage the search-space(s) for text corrections within the engine.
|
||||
* This file defines the predictive-text engine's SearchQuotientCluster class,
|
||||
* which is used to manage the search-space(s) for text corrections within the
|
||||
* engine.
|
||||
*/
|
||||
|
||||
import { QueueComparator as Comparator, PriorityQueue } from '@keymanapp/web-utils';
|
||||
import { QueueComparator, PriorityQueue } from '@keymanapp/web-utils';
|
||||
import { LexicalModelTypes } from '@keymanapp/common-types';
|
||||
|
||||
import { SearchNode, SearchResult } from './distance-modeler.js';
|
||||
import { generateSpaceSeed, InputSegment, PathResult, SearchQuotientNode } from './search-quotient-node.js';
|
||||
|
||||
const PATH_QUEUE_COMPARATOR: Comparator<SearchQuotientNode> = (a, b) => {
|
||||
const PATH_QUEUE_COMPARATOR: QueueComparator<SearchQuotientNode> = (a, b) => {
|
||||
return a.currentCost - b.currentCost;
|
||||
}
|
||||
|
||||
// The set of search spaces corresponding to the same 'context' for search.
|
||||
// Whenever a wordbreak boundary is crossed, a new instance should be made.
|
||||
export class SearchQuotientCluster implements SearchQuotientNode {
|
||||
// While most functions can be done directly from SearchSpace, merging and splitting will need access
|
||||
// to SearchPath-specific members. It's also cleaner to not allow nested SearchClusters while we
|
||||
// haven't worked out support for such a scenario.
|
||||
private selectionQueue: PriorityQueue<SearchQuotientNode> = new PriorityQueue(PATH_QUEUE_COMPARATOR);
|
||||
readonly spaceId: number;
|
||||
|
||||
|
|
@ -48,20 +46,19 @@ export class SearchQuotientCluster implements SearchQuotientNode {
|
|||
private _processedEdgeSet?: {[pathKey: string]: boolean} = {};
|
||||
|
||||
/**
|
||||
* Provides a heuristic for the base cost at each depth if the best
|
||||
* individual input were taken at that level.
|
||||
* Provides a heuristic for the base cost at each depth if the best individual
|
||||
* input were taken at that level.
|
||||
*/
|
||||
readonly lowestPossibleSingleCost: number;
|
||||
|
||||
/**
|
||||
* Constructs a fresh SearchSpace instance for used in predictive-text correction
|
||||
* and suggestion searches.
|
||||
* @param baseSpaceId
|
||||
* @param model
|
||||
* Constructs a fresh SearchQuotientCluster instance for use in
|
||||
* predictive-text correction and suggestion searches.
|
||||
* @param inboundPaths
|
||||
*/
|
||||
constructor(inboundPaths: SearchQuotientNode[]) {
|
||||
if(inboundPaths.length == 0) {
|
||||
throw new Error("SearchCluster requires an array with at least one SearchPath");
|
||||
throw new Error("SearchQuotientCluster requires an array with at least one SearchQuotientNode");
|
||||
}
|
||||
|
||||
let lowestPossibleSingleCost = Number.POSITIVE_INFINITY;
|
||||
|
|
@ -72,12 +69,12 @@ export class SearchQuotientCluster implements SearchQuotientNode {
|
|||
|
||||
for(let path of inboundPaths) {
|
||||
if(path.inputCount != inputCount || path.codepointLength != codepointLength) {
|
||||
throw new Error(`SearchPath does not share same properties as others in the cluster: inputCount ${path.inputCount} vs ${inputCount}, codepointLength ${path.codepointLength} vs ${codepointLength}`);
|
||||
throw new Error(`SearchQuotientNode does not share same properties as others in the cluster: inputCount ${path.inputCount} vs ${inputCount}, codepointLength ${path.codepointLength} vs ${codepointLength}`);
|
||||
}
|
||||
|
||||
// If there's a source-range key mismatch - via mismatch in count or in actual ID, we have an error.
|
||||
if(path.sourceRangeKey != sourceRangeKey) {
|
||||
throw new Error(`SearchPath does not share the same source identifiers as others in the cluster`);
|
||||
throw new Error(`SearchQuotientNode does not share the same source identifiers as others in the cluster`);
|
||||
}
|
||||
|
||||
lowestPossibleSingleCost = Math.min(lowestPossibleSingleCost, path.lowestPossibleSingleCost);
|
||||
|
|
@ -121,7 +118,7 @@ export class SearchQuotientCluster implements SearchQuotientNode {
|
|||
* has fat-finger data available, which itself indicates that the user has
|
||||
* corrections enabled.
|
||||
*/
|
||||
get correctionsEnabled(): boolean {
|
||||
get correctionsEnabled(): boolean {
|
||||
const paths = this.selectionQueue.toArray();
|
||||
// When corrections are disabled, the Web engine will only provide individual Transforms
|
||||
// for an input, not a distribution. No distributions means we shouldn't do corrections.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
* engine.
|
||||
*/
|
||||
|
||||
import { QueueComparator as Comparator, KMWString, PriorityQueue } from '@keymanapp/web-utils';
|
||||
import { QueueComparator, KMWString, PriorityQueue } from '@keymanapp/web-utils';
|
||||
import { LexicalModelTypes } from '@keymanapp/common-types';
|
||||
import { buildMergedTransform } from '@keymanapp/models-templates';
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ import LexicalModel = LexicalModelTypes.LexicalModel;
|
|||
import ProbabilityMass = LexicalModelTypes.ProbabilityMass;
|
||||
import Transform = LexicalModelTypes.Transform;
|
||||
|
||||
export const QUEUE_NODE_COMPARATOR: Comparator<SearchNode> = function(arg1, arg2) {
|
||||
export const QUEUE_NODE_COMPARATOR: QueueComparator<SearchNode> = function(arg1, arg2) {
|
||||
return arg1.currentCost - arg2.currentCost;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -409,7 +409,7 @@ describe('ContextState', () => {
|
|||
assert.equal(state.tokenization.tokens[state.tokenization.tokens.length - 1].searchModule.inputCount, 1);
|
||||
});
|
||||
|
||||
it.skip('handles case where tail token is split into three rather than two', function() {
|
||||
it('handles case where tail token is split into three rather than two', function() {
|
||||
let baseContext = models.tokenize(defaultBreaker, {
|
||||
left: "text'", startOfBuffer: true, endOfBuffer: true
|
||||
});
|
||||
|
|
|
|||
|
|
@ -561,7 +561,7 @@ describe('ContextTokenization', function() {
|
|||
}
|
||||
});
|
||||
|
||||
it.skip('handles case that triggers a token merge: can+\'+t', () => {
|
||||
it('handles case that triggers a token merge: can+\'+t', () => {
|
||||
const baseTokens = ['an', ' ', 'apple', ' ', 'a', ' ', 'day', ' ', 'can', '\''];
|
||||
const baseTokenization = new ContextTokenization(baseTokens.map(t => toToken(t)));
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Created by jahorton on 2025-10-29
|
||||
*
|
||||
* This file defines tests for the SearchSpace class of the
|
||||
* This file defines tests for the SearchQuotientCluster class of the
|
||||
* predictive-text correction-search engine.
|
||||
*/
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue