Merge pull request #5491 from keymanapp/fix/common/core/web/cherry/empty-alternates

fix(common/core/web): error from early fat-finger termination due to OS interruptions 🍒
This commit is contained in:
Joshua Horton 2021-07-28 17:06:21 +07:00 committed by GitHub
commit 7f12d5a7ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 38 additions and 2 deletions

View file

@ -172,6 +172,11 @@ namespace com.keyman.text {
if(pair.p < KEYSTROKE_EPSILON) {
break;
} else if(timer && timer() >= TIMEOUT_THRESHOLD) {
// Note: it's always possible that the thread _executing_ our JS
// got paused by the OS, even if JS itself is single-threaded.
//
// The case where `alternates` is initialized (line 167) but empty
// (because of net-zero loop iterations) MUST be handled.
break;
}
@ -188,6 +193,8 @@ namespace com.keyman.text {
// If alternateBehavior.beep == true, ignore it. It's a disallowed key sequence,
// so we expect users to never intend their use.
//
// Also possible that this set of conditions fail for all evaluated alternates.
if(alternateBehavior && !alternateBehavior.beep && pair.p > 0) {
let transform: Transform = alternateBehavior.transcription.transform;
@ -216,7 +223,9 @@ namespace com.keyman.text {
// -- All keystroke (and 'alternate') processing is now complete. Time to finalize everything! --
// Notify the ModelManager of new input - it's predictive text time!
ruleBehavior.transcription.alternates = alternates;
if(alternates && alternates.length > 0) {
ruleBehavior.transcription.alternates = alternates;
}
// Yes, even for ruleBehavior.triggersDefaultCommand. Those tend to change the context.
ruleBehavior.predictionPromise = this.languageProcessor.predict(ruleBehavior.transcription);

View file

@ -323,8 +323,16 @@ namespace com.keyman.text.prediction {
this.lmEngine.resetContext(context);
}
let alternates = transcription.alternates;
if(!alternates || alternates.length == 0) {
alternates = [{
sample: transcription.transform,
p: 1.0
}];
}
let transform = transcription.transform;
var promise = this.currentPromise = this.lmEngine.predict(transcription.alternates || transcription.transform, context);
var promise = this.currentPromise = this.lmEngine.predict(alternates, context);
let lp = this;
return promise.then(function(suggestions: Suggestion[]) {

View file

@ -73,6 +73,25 @@ class ModelCompositor {
if(!(transformDistribution instanceof Array)) {
transformDistribution = [ {sample: transformDistribution, p: 1.0} ];
} else if(transformDistribution.length == 0) {
/*
Robust stop-gap: if our other filters somehow fail, this fixes the
zero-length array by making it match the form of the array that
would result if it were instead called with the other legal
parameter type - a single Transform.
Unfortunately, the method will lack all data about even
the original keystroke that resulted in the call... but this way,
we can at least get some predictions rather than shortcutting
and producing none whatsoever.
*/
transformDistribution.push({
sample: {
insert: '',
deleteLeft: 0
},
p: 1.0
})
}
let inputTransform = transformDistribution.sort(function(a, b) {