mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-02 21:57:41 +00:00
183 lines
6.1 KiB
TypeScript
183 lines
6.1 KiB
TypeScript
// Defines the text processing KMW core.
|
|
///<reference path="../keyboardProcessor.ts" />
|
|
// Defines the LMLayer's outer shell
|
|
///<reference path="../../includes/lmlayer.ts" />
|
|
// Defines the ModelManager and its related types.
|
|
///<reference path="../../keyboards/kmwkeyboards.ts" />
|
|
|
|
namespace com.keyman.text.prediction {
|
|
export interface ModelSpec {
|
|
/**
|
|
* The model's unique identifier.
|
|
*/
|
|
id: string;
|
|
|
|
/**
|
|
* The list of supported BCP-47 language codes. Only one language should be supported,
|
|
* although multiple variants based on region code (at min) may be specified.
|
|
*/
|
|
languages: string[];
|
|
|
|
/**
|
|
* The path/URL to the file that defines the model.
|
|
*/
|
|
path: string;
|
|
}
|
|
|
|
export class TranscriptionContext implements Context {
|
|
left: string;
|
|
right?: string;
|
|
|
|
startOfBuffer: boolean;
|
|
endOfBuffer: boolean;
|
|
|
|
constructor(mock: Mock, config: Configuration) {
|
|
this.left = mock.getTextBeforeCaret();
|
|
this.startOfBuffer = this.left._kmwLength() > config.leftContextCodePoints;
|
|
if(!this.startOfBuffer) {
|
|
// Our custom substring version will return the last n characters if param #1 is given -n.
|
|
this.left = this.left._kmwSubstr(-config.leftContextCodePoints);
|
|
}
|
|
|
|
this.right = mock.getTextAfterCaret();
|
|
this.endOfBuffer = this.right._kmwLength() > config.leftContextCodePoints;
|
|
if(!this.endOfBuffer) {
|
|
this.right = this.right._kmwSubstr(0, config.leftContextCodePoints);
|
|
}
|
|
}
|
|
}
|
|
|
|
type SupportedEventNames = "suggestionsready" | "invalidatesuggestions" | "statechange" | "tryaccept" | "tryrevert";
|
|
type SupportedEventHandler = InvalidateSuggestionsHandler | ReadySuggestionsHandler | StateChangeHandler | TryUIHandler;
|
|
|
|
export class ReadySuggestions {
|
|
suggestions: Suggestion[];
|
|
transcriptionID: number;
|
|
|
|
constructor(suggestions: Suggestion[], id: number) {
|
|
this.suggestions = suggestions;
|
|
this.transcriptionID = id;
|
|
}
|
|
}
|
|
|
|
export class ModelManager {
|
|
// Tracks registered models by ID.
|
|
private registeredModels: {[id: string]: ModelSpec} = {};
|
|
|
|
// Allows for easy model lookup by language code; useful when switching keyboards.
|
|
languageModelMap: {[language:string]: ModelSpec} = {};
|
|
|
|
static EVENT_PREFIX: string = "kmw.mm.";
|
|
|
|
init() {
|
|
let keyman = com.keyman.singleton;
|
|
|
|
// Registers this module for keyboard (language) and model change events.
|
|
keyman['addEventListener']('keyboardchange', this.onKeyboardChange.bind(this));
|
|
}
|
|
|
|
onKeyboardChange(kbdInfo?: keyboards.KeyboardChangeData | string) {
|
|
let keyman = com.keyman.singleton;
|
|
let core = keyman.core;
|
|
|
|
if(typeof kbdInfo == 'string') { // This case refers to the active language code.
|
|
kbdInfo = {
|
|
['internalName']: keyman.keyboardManager.getActiveKeyboardName(),
|
|
['languageCode']: kbdInfo,
|
|
['indirect']: true
|
|
}
|
|
}
|
|
|
|
let lgCode = kbdInfo['languageCode'];
|
|
let model = this.languageModelMap[lgCode];
|
|
var loadPromise: Promise<void>;
|
|
|
|
if(core.activeModel !== model) {
|
|
if(core.activeModel) {
|
|
core.languageProcessor.unloadModel();
|
|
}
|
|
|
|
if(model) {
|
|
loadPromise = core.languageProcessor.loadModel(model);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Accessible publicly as keyman.modelManager.register(model: ModelSpec)
|
|
register(model: ModelSpec): void {
|
|
let keyman = com.keyman.singleton;
|
|
let activeLanguage = keyman.keyboardManager.getActiveLanguage();
|
|
|
|
this.registeredModels[model.id] = model;
|
|
|
|
// Register the model for each targeted language code variant.
|
|
let mm = this;
|
|
model.languages.forEach(function(code: string) {
|
|
mm.languageModelMap[code] = model;
|
|
|
|
// The model's for our active language! Activate it!
|
|
if(code == activeLanguage) {
|
|
// Manually trigger our model-update event function.
|
|
mm.onKeyboardChange(code);
|
|
}
|
|
});
|
|
}
|
|
|
|
deregister(modelId: string): void {
|
|
let keyman = com.keyman.singleton;
|
|
let core = keyman.core;
|
|
let model: ModelSpec;
|
|
|
|
// Remove the model from the id-lookup associative array.
|
|
if(this.registeredModels[modelId]) {
|
|
model = this.registeredModels[modelId];
|
|
delete this.registeredModels[modelId];
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
// Is it the active model?
|
|
if(core.activeModel && core.activeModel.id == modelId) {
|
|
core.languageProcessor.unloadModel();
|
|
}
|
|
|
|
// Ensure the model is deregistered for each targeted language code variant.
|
|
let mm = this;
|
|
model.languages.forEach(function(code: string) {
|
|
if(mm.languageModelMap[code].id == modelId) {
|
|
delete mm.languageModelMap[code];
|
|
}
|
|
});
|
|
}
|
|
|
|
isRegistered(model: ModelSpec): boolean {
|
|
return !! this.registeredModels[model.id];
|
|
}
|
|
|
|
/**
|
|
* Function addEventListener
|
|
* Scope Public
|
|
* @param {string} event event to handle
|
|
* @param {function(Event)} func event handler function
|
|
* @return {boolean} value returned by util.addEventListener
|
|
* Description Wrapper function to add and identify handlers for ModelManager events
|
|
*/
|
|
['addEventListener'](event: SupportedEventNames, func: SupportedEventHandler): boolean {
|
|
let keyman = com.keyman.singleton;
|
|
return keyman.util.addEventListener(ModelManager.EVENT_PREFIX + event, func);
|
|
}
|
|
|
|
/**
|
|
* Function removeEventListener
|
|
* Scope Public
|
|
* @param {string} event event to handle
|
|
* @param {function(Event)} func event handler function
|
|
* @return {boolean} value returned by util.addEventListener
|
|
* Description Wrapper function to remove previously-added handlers for ModelManager events
|
|
*/
|
|
['removeEventListener'](event: SupportedEventNames, func: SupportedEventHandler): boolean {
|
|
let keyman = com.keyman.singleton;
|
|
return keyman.util.removeEventListener(ModelManager.EVENT_PREFIX + event, func);
|
|
}
|
|
}
|
|
}
|