mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-08 09:55:32 +00:00
refactor(web): transcription cache needed for multitaps, no longer prediction-only
This commit is contained in:
parent
2836a16650
commit
ad07855239
3 changed files with 52 additions and 15 deletions
|
|
@ -22,6 +22,7 @@ import {
|
|||
SystemStoreIDs,
|
||||
type TextTransform
|
||||
} from "@keymanapp/keyboard-processor";
|
||||
import { TranscriptionCache } from "../transcriptionCache.js";
|
||||
|
||||
export default class InputProcessor {
|
||||
public static readonly DEFAULT_OPTIONS: ProcessorInitOptions = {
|
||||
|
|
@ -37,6 +38,8 @@ export default class InputProcessor {
|
|||
private kbdProcessor: KeyboardProcessor;
|
||||
private lngProcessor: LanguageProcessor;
|
||||
|
||||
private readonly contextCache = new TranscriptionCache();
|
||||
|
||||
constructor(device: DeviceSpec, predictiveTextWorker: Worker, options?: ProcessorInitOptions) {
|
||||
if(!device) {
|
||||
throw new Error('device must be defined');
|
||||
|
|
@ -48,7 +51,7 @@ export default class InputProcessor {
|
|||
|
||||
this.contextDevice = device;
|
||||
this.kbdProcessor = new KeyboardProcessor(device, options);
|
||||
this.lngProcessor = new LanguageProcessor(predictiveTextWorker);
|
||||
this.lngProcessor = new LanguageProcessor(predictiveTextWorker, this.contextCache);
|
||||
}
|
||||
|
||||
public get languageProcessor(): LanguageProcessor {
|
||||
|
|
@ -215,6 +218,10 @@ export default class InputProcessor {
|
|||
ruleBehavior.triggersDefaultCommand = true;
|
||||
}
|
||||
|
||||
// Multitaps operate in part by referencing 'committed' Transcriptions to rewind
|
||||
// the context as necessary.
|
||||
this.contextCache.save(ruleBehavior.transcription);
|
||||
|
||||
// The keyboard may want to take an action after all other keystroke processing is
|
||||
// finished, for example to switch layers. This action may not have any output
|
||||
// but may change system store or variable store values. Given this, we don't need to
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { LMLayer } from "@keymanapp/lexical-model-layer/web";
|
|||
import { OutputTarget, Transcription, Mock } from "@keymanapp/keyboard-processor";
|
||||
import ContextWindow from "../contextWindow.js";
|
||||
import ModelSpec from "./modelSpec.js"
|
||||
import { TranscriptionCache } from "../../transcriptionCache.js";
|
||||
|
||||
/**
|
||||
* Corresponds to the 'suggestionsready' LanguageProcessor event.
|
||||
|
|
@ -60,18 +61,18 @@ export default class LanguageProcessor extends EventEmitter<LanguageProcessorEve
|
|||
private configuration?: Configuration;
|
||||
private currentPromise?: Promise<Suggestion[]>;
|
||||
|
||||
private recentTranscriptions: Transcription[] = [];
|
||||
private readonly recentTranscriptions: TranscriptionCache;
|
||||
|
||||
private _mayPredict: boolean = true;
|
||||
private _mayCorrect: boolean = true;
|
||||
|
||||
private _state: StateChangeEnum = 'inactive';
|
||||
|
||||
private static readonly TRANSCRIPTION_BUFFER: 10 = 10;
|
||||
|
||||
public constructor(predictiveTextWorker: Worker, supportsRightDeletions: boolean = false) {
|
||||
public constructor(predictiveTextWorker: Worker, transcriptionCache: TranscriptionCache, supportsRightDeletions: boolean = false) {
|
||||
super();
|
||||
|
||||
this.recentTranscriptions = transcriptionCache;
|
||||
|
||||
// Establishes KMW's platform 'capabilities', which limit the range of context a LMLayer
|
||||
// model may expect.
|
||||
let capabilities: Capabilities = {
|
||||
|
|
@ -357,11 +358,7 @@ export default class LanguageProcessor extends EventEmitter<LanguageProcessorEve
|
|||
}
|
||||
|
||||
private recordTranscription(transcription: Transcription) {
|
||||
this.recentTranscriptions.push(transcription);
|
||||
|
||||
if(this.recentTranscriptions.length > LanguageProcessor.TRANSCRIPTION_BUFFER) {
|
||||
this.recentTranscriptions.splice(0, 1);
|
||||
}
|
||||
this.recentTranscriptions.save(transcription);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -372,11 +369,7 @@ export default class LanguageProcessor extends EventEmitter<LanguageProcessorEve
|
|||
* @returns The matching `Transcription`, or `null` none is found.
|
||||
*/
|
||||
public getPredictionState(id: number): Transcription {
|
||||
let match = this.recentTranscriptions.filter((t: Transcription) => {
|
||||
return t.token == id;
|
||||
})
|
||||
|
||||
return match.length == 0 ? null : match[0];
|
||||
return this.recentTranscriptions.get(id);
|
||||
}
|
||||
|
||||
public shutdown() {
|
||||
|
|
|
|||
37
common/web/input-processor/src/transcriptionCache.ts
Normal file
37
common/web/input-processor/src/transcriptionCache.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import { Transcription } from "@keymanapp/keyboard-processor";
|
||||
|
||||
const TRANSCRIPTION_BUFFER_SIZE = 10;
|
||||
|
||||
export class TranscriptionCache {
|
||||
private readonly map = new Map<number, Transcription>();
|
||||
|
||||
public get(key: number) {
|
||||
const value = this.map.get(key);
|
||||
|
||||
// Update the entry's 'age' / position in the keys() ordering.
|
||||
if(value) {
|
||||
this.save(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public save(value: Transcription) {
|
||||
const key = value.token >= 0 ? value.token : -value.token;
|
||||
|
||||
// Resets the key's ordering in Map.keys.
|
||||
this.map.delete(key);
|
||||
this.map.set(key, value);
|
||||
|
||||
if(this.map.size > TRANSCRIPTION_BUFFER_SIZE) {
|
||||
/* Deletes the oldest entry. As per the specification of `Map.keys()`, the keys are in
|
||||
* insertion order. The earlier `map.delete` call resets a key's position in the list,
|
||||
* ensuring index 0 corresponds to the entry least-recently referenced.
|
||||
*
|
||||
* See also:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys
|
||||
*/
|
||||
this.map.delete(this.map.keys().next().value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue