spiegel-keyman/common/web/keyboard-processor/src/text/ruleBehavior.ts

133 lines
No EOL
4.8 KiB
TypeScript

namespace com.keyman.text {
/**
* Represents the commands and state changes that result from a matched keyboard rule.
*/
export class RuleBehavior {
/**
* The before-and-after Transform from matching a keyboard rule. May be `null`
* if no keyboard rules were matched for the keystroke.
*/
transcription: Transcription = null;
/**
* Indicates whether or not a BEEP command was issued by the matched keyboard rule.
*/
beep?: boolean;
/**
* A set of changed store values triggered by the matched keyboard rule.
*/
setStore: {[id: number]: string} = {};
/**
* A set of variable stores with save requests triggered by the matched keyboard rule
*/
saveStore: {[name: string]: VariableStore} = {};
/**
* A set of variable stores with possible changes to be applied during finalization.
*/
variableStores: keyboards.VariableStoreDictionary = {};
/**
* Denotes a non-output default behavior; this should be evaluated later, against the true keystroke.
*/
triggersDefaultCommand: boolean = false;
/**
* Denotes error log messages generated when attempting to generate this behavior.
*/
errorLog?: string;
/**
* Denotes warning log messages generated when attempting to generate this behavior.
*/
warningLog?: string;
/**
* If predictive text is active, contains a Promise returning predictive Suggestions.
*/
predictionPromise?: Promise<Suggestion[]>;
/**
* In reference to https://github.com/keymanapp/keyman/pull/4350#issuecomment-768753852:
*
* If the final group processed is a context and keystroke group (using keys),
* and there is no nomatch rule, and the keystroke is not matched in the group,
* the keystroke's default behavior should trigger, regardless of whether or not any
* rules in prior groups matched.
*/
triggerKeyDefault?: boolean;
finalize(processor: KeyboardProcessor, outputTarget: OutputTarget, readonly: boolean) {
if(!this.transcription) {
throw "Cannot finalize a RuleBehavior with no transcription.";
}
if(processor.beepHandler && this.beep) {
processor.beepHandler(outputTarget);
}
for(let storeID in this.setStore) {
let sysStore = processor.keyboardInterface.systemStores[storeID];
if(sysStore) {
try {
sysStore.set(this.setStore[storeID]);
} catch (error) {
if(processor.errorLogger) {
processor.errorLogger("Rule attempted to perform illegal operation - 'platform' may not be changed.");
}
}
} else if(processor.warningLogger) {
processor.warningLogger("Unknown store affected by keyboard rule: " + storeID);
}
}
processor.keyboardInterface.applyVariableStores(this.variableStores);
if(processor.keyboardInterface.variableStoreSerializer) {
for(let storeID in this.saveStore) {
processor.keyboardInterface.variableStoreSerializer.saveStore(processor.activeKeyboard.id, storeID, this.saveStore[storeID]);
}
}
if(this.triggersDefaultCommand) {
let keyEvent = this.transcription.keystroke;
DefaultOutput.applyCommand(keyEvent, outputTarget);
}
if(processor.warningLogger && this.warningLog) {
processor.warningLogger(this.warningLog);
} else if(processor.errorLogger && this.errorLog) {
processor.errorLogger(this.errorLog);
}
}
/**
* Merges default-related behaviors from another RuleBehavior into this one. Assumes that the current instance
* "came first" chronologically. Both RuleBehaviors must be sourced from the same keystroke.
*
* Intended use: merging rule-based behavior with default key behavior during scenarios like those described
* at https://github.com/keymanapp/keyman/pull/4350#issuecomment-768753852.
*
* This function does not attempt a "complete" merge for two fully-constructed RuleBehaviors! Things
* WILL break for unintended uses.
* @param other
*/
mergeInDefaults(other: RuleBehavior) {
let keystroke = this.transcription.keystroke;
let keyFromOther = other.transcription.keystroke;
if(keystroke.Lcode != keyFromOther.Lcode || keystroke.Lmodifiers != keyFromOther.Lmodifiers) {
throw "RuleBehavior default-merge not supported unless keystrokes are identical!";
}
this.triggersDefaultCommand = this.triggersDefaultCommand || other.triggersDefaultCommand;
let mergingMock = Mock.from(this.transcription.preInput, false);
mergingMock.apply(this.transcription.transform);
mergingMock.apply(other.transcription.transform);
this.transcription = mergingMock.buildTranscriptionFrom(this.transcription.preInput, keystroke, false, this.transcription.alternates);
}
}
}