From d6f0e155450d01be15d7ee0c36cff2f24e11d268 Mon Sep 17 00:00:00 2001 From: jahorton Date: Tue, 20 Jul 2021 09:02:50 +0700 Subject: [PATCH 1/6] fix(common/core/web): adds empty-array check for alternates --- .../src/text/prediction/languageProcessor.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/common/core/web/input-processor/src/text/prediction/languageProcessor.ts b/common/core/web/input-processor/src/text/prediction/languageProcessor.ts index 17faf9e15de..30f0d070106 100644 --- a/common/core/web/input-processor/src/text/prediction/languageProcessor.ts +++ b/common/core/web/input-processor/src/text/prediction/languageProcessor.ts @@ -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 || transcription.transform, context); let lp = this; return promise.then(function(suggestions: Suggestion[]) { From d4620dc22105e9626ab6b6a6d8b66d78c867fbe2 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 21 Jul 2021 11:40:21 +0700 Subject: [PATCH 2/6] fix(common/models): adds worker null guard for predict --- common/predictive-text/worker/model-compositor.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/common/predictive-text/worker/model-compositor.ts b/common/predictive-text/worker/model-compositor.ts index 5c41e919576..7004388f78f 100644 --- a/common/predictive-text/worker/model-compositor.ts +++ b/common/predictive-text/worker/model-compositor.ts @@ -73,6 +73,14 @@ class ModelCompositor { if(!(transformDistribution instanceof Array)) { transformDistribution = [ {sample: transformDistribution, p: 1.0} ]; + } else if(transformDistribution.length == 0) { + transformDistribution.push({ + sample: { + insert: '', + deleteLeft: 0 + }, + p: 1.0 + }) } let inputTransform = transformDistribution.sort(function(a, b) { From 2cb64baf42309284b3a9be60fa568b2d79bbbb81 Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 22 Jul 2021 08:22:28 +0700 Subject: [PATCH 3/6] fix(common/core/web): fixes likely root of problem --- common/core/web/input-processor/src/text/inputProcessor.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/core/web/input-processor/src/text/inputProcessor.ts b/common/core/web/input-processor/src/text/inputProcessor.ts index 314d1e8c5ef..fa429961646 100644 --- a/common/core/web/input-processor/src/text/inputProcessor.ts +++ b/common/core/web/input-processor/src/text/inputProcessor.ts @@ -172,6 +172,11 @@ namespace com.keyman.text { if(pair.p < KEYSTROKE_EPSILON) { break; } else if(timer && timer() >= TIMEOUT_THRESHOLD) { + // It's always possible that the thread _executing_ our JS got paused, + // even if JS itself is single-threaded. + if(alternates.length == 0) { + alternates = undefined; + } break; } From bc05dc4c107d782c47e6727365ed6b8fede84406 Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 22 Jul 2021 10:36:36 +0700 Subject: [PATCH 4/6] change(common/core/web): adjustments per review --- .../input-processor/src/text/inputProcessor.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/common/core/web/input-processor/src/text/inputProcessor.ts b/common/core/web/input-processor/src/text/inputProcessor.ts index fa429961646..d4374b0210b 100644 --- a/common/core/web/input-processor/src/text/inputProcessor.ts +++ b/common/core/web/input-processor/src/text/inputProcessor.ts @@ -172,11 +172,11 @@ namespace com.keyman.text { if(pair.p < KEYSTROKE_EPSILON) { break; } else if(timer && timer() >= TIMEOUT_THRESHOLD) { - // It's always possible that the thread _executing_ our JS got paused, - // even if JS itself is single-threaded. - if(alternates.length == 0) { - alternates = undefined; - } + // 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; } @@ -193,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; @@ -221,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); From e8c3786e42d2d9ec4fcdbcb821ba93e1e4227067 Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 22 Jul 2021 10:39:32 +0700 Subject: [PATCH 5/6] change(common/core/web): removes conditional replacement of a now always-truthy --- .../input-processor/src/text/prediction/languageProcessor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/core/web/input-processor/src/text/prediction/languageProcessor.ts b/common/core/web/input-processor/src/text/prediction/languageProcessor.ts index 30f0d070106..d9a74b3a877 100644 --- a/common/core/web/input-processor/src/text/prediction/languageProcessor.ts +++ b/common/core/web/input-processor/src/text/prediction/languageProcessor.ts @@ -332,7 +332,7 @@ namespace com.keyman.text.prediction { } let transform = transcription.transform; - var promise = this.currentPromise = this.lmEngine.predict(alternates || transcription.transform, context); + var promise = this.currentPromise = this.lmEngine.predict(alternates, context); let lp = this; return promise.then(function(suggestions: Suggestion[]) { From abc96ad91f53ccefc9d8e73c067e144eed79897d Mon Sep 17 00:00:00 2001 From: jahorton Date: Tue, 27 Jul 2021 08:40:47 +0700 Subject: [PATCH 6/6] docs(common/models): restores non-cherried comment --- common/predictive-text/worker/model-compositor.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/common/predictive-text/worker/model-compositor.ts b/common/predictive-text/worker/model-compositor.ts index 7004388f78f..83937bec51d 100644 --- a/common/predictive-text/worker/model-compositor.ts +++ b/common/predictive-text/worker/model-compositor.ts @@ -74,7 +74,18 @@ class ModelCompositor { if(!(transformDistribution instanceof Array)) { transformDistribution = [ {sample: transformDistribution, p: 1.0} ]; } else if(transformDistribution.length == 0) { - transformDistribution.push({ + /* + 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