For Suggestion application: OutputTarget.restoreTo

This commit is contained in:
Joshua A. Horton 2019-03-28 10:10:10 +07:00
parent 75fb17fb90
commit 66bf2856f5
10 changed files with 147 additions and 4 deletions

View file

@ -216,6 +216,13 @@ interface Context {
* A concrete suggestion
*/
interface Suggestion {
/**
* Indicates the externally-supplied id of the Transform that prompted
* the Suggestion. Automatically handled by the LMLayer; models should
* not handle this field.
*/
transformId?: number;
/**
* The suggested update to the buffer. Note that this transform should
* be applied AFTER the instigating transform, if any.

View file

@ -233,9 +233,17 @@ class LMLayerWorker {
switch(payload.message) {
case 'predict':
let {transform, context} = payload;
// Let's not rely on the model to copy transform IDs.
let suggestions = model.predict(transform, context);
suggestions.forEach(function(s: Suggestion) {
s.transformId = transform.id;
});
// Now that the suggestions are ready, send them out!
this.cast('suggestions', {
token: payload.token,
suggestions: model.predict(transform, context)
suggestions: suggestions
});
break;
case 'unload':

View file

@ -190,7 +190,7 @@ namespace com.keyman.dom {
var n = start.node.ownerDocument.createTextNode(s);
let range = this.root.ownerDocument.createRange();
range.setStart(start.node, s.length);
range.setStart(start.node, start.offset);
range.collapse(true);
range.insertNode(n);
}
@ -207,5 +207,35 @@ namespace com.keyman.dom {
}
Lsel.collapseToEnd();
}
protected setTextAfterCaret(s: string) {
if(!this.hasSelection()) {
return;
}
let caret = this.getCarets().end;
let delta = s._kmwLength();
let Lsel = this.root.ownerDocument.getSelection();
if(delta == 0) {
return;
}
// This is designed explicitly for use in direct-setting operations; deadkeys
// will be handled after this method.
if(caret.node.nodeType == 3) {
let textStart = <Text> caret.node;
textStart.replaceData(caret.offset, textStart.length, s);
} else {
// Create a new text node - empty control
var n = caret.node.ownerDocument.createTextNode(s);
let range = this.root.ownerDocument.createRange();
range.setStart(caret.node, caret.offset);
range.collapse(true);
range.insertNode(n);
}
}
}
}

View file

@ -196,7 +196,7 @@ namespace com.keyman.dom {
var n = this.doc.createTextNode(s);
let range = this.doc.createRange();
range.setStart(start.node, s.length);
range.setStart(start.node, start.offset);
range.collapse(true);
range.insertNode(n);
}
@ -214,6 +214,36 @@ namespace com.keyman.dom {
Lsel.collapseToEnd();
}
protected setTextAfterCaret(s: string) {
if(!this.hasSelection()) {
return;
}
let caret = this.getCarets().end;
let delta = s._kmwLength();
let Lsel = this.doc.getSelection();
if(delta == 0) {
return;
}
// This is designed explicitly for use in direct-setting operations; deadkeys
// will be handled after this method.
if(caret.node.nodeType == 3) {
let textStart = <Text> caret.node;
textStart.replaceData(caret.offset, textStart.length, s);
} else {
// Create a new text node - empty control
var n = caret.node.ownerDocument.createTextNode(s);
let range = this.root.ownerDocument.createRange();
range.setStart(caret.node, caret.offset);
range.collapse(true);
range.insertNode(n);
}
}
/**
* Function saveProperties
* Scope Private

View file

@ -92,6 +92,13 @@ namespace com.keyman.dom {
this.setCaret(newCaret);
}
protected setTextAfterCaret(s: string) {
let c = this.getCaret();
this.root.value = this.getTextBeforeCaret() + s;
this.setCaret(c);
}
getTextAfterCaret(): string {
this.getCaret();
return this.getText()._kmwSubstring(this.processedSelectionEnd);

View file

@ -99,6 +99,13 @@ namespace com.keyman.dom {
this.setCaret(newCaret);
}
protected setTextAfterCaret(s: string) {
let c = this.getCaret();
this.root.value = this.getTextBeforeCaret() + s;
this.setCaret(c);
}
getTextAfterCaret(): string {
this.getCaret();
return this.getText()._kmwSubstring(this.processedSelectionEnd);

View file

@ -59,5 +59,9 @@ namespace com.keyman.dom {
this.adjustDeadkeys(s._kmwLength());
this.root.setTextBeforeCaret(this.root.getTextBeforeCaret() + s);
}
protected setTextAfterCaret(s: string) {
this.root.setText(this.getTextBeforeCaret() + s, this.getTextBeforeCaret()._kmwLength());
}
}
}

View file

@ -295,7 +295,7 @@ namespace com.keyman.dom {
this.setText(textValue, null);
}
private setText(t?: string, cp?: number): void {
setText(t?: string, cp?: number): void {
var tLen=0;
var t1: string, t2: string;

View file

@ -162,6 +162,37 @@ namespace com.keyman.text {
return new Transcription(keyEvent, transform, Mock.from(original), removedDks, insertedDks);
}
/**
* Restores the `OutputTarget` to the indicated state. Designed for use with `Transcription.preInput`.
* @param original An `OutputTarget` (usually a `Mock`).
*/
restoreTo(original: OutputTarget) {
//
this.setTextBeforeCaret(original.getTextBeforeCaret());
this.setTextAfterCaret(original.getTextAfterCaret());
// Also, restore the deadkeys!
this._dks = original._dks.clone();
}
/**
* Helper to `restoreTo` - allows directly setting the 'before' context to that of another
* `OutputTarget`.
* @param s
*/
protected setTextBeforeCaret(s: string): void {
// This one's easy enough to provide a default implementation for.
this.deleteCharsBeforeCaret(this.getTextBeforeCaret()._kmwLength());
this.insertTextBeforeCaret(s);
}
/**
* Helper to `restoreTo` - allows directly setting the 'after' context to that of another
* `OutputTarget`.
* @param s
*/
protected abstract setTextAfterCaret(s: string): void;
/**
* Returns the underlying element / document modeled by the wrapper.
*/
@ -317,5 +348,9 @@ namespace com.keyman.text {
this.text = this.getTextBeforeCaret() + s + this.getTextAfterCaret();
this.caretIndex += s.kmwLength();
}
protected setTextAfterCaret(s: string): void {
this.text = this.getTextBeforeCaret() + s;
}
}
}

View file

@ -251,6 +251,21 @@ namespace com.keyman.text.prediction {
}
}
/**
* Retrieves the context and output state of KMW immediately before the prediction with
* token `id` was generated. Must correspond to a 'recent' one, as only so many are stored
* in `ModelManager`'s history buffer.
* @param id A unique identifier corresponding to a recent `Transcription`.
* @returns The matching `Transcription`, or `null` none is found.
*/
public getPredictionState(id: number): Transcription {
let match = this.recentTranscriptions.filter(function(t: Transcription) {
return t.token == id;
})
return match.length == 0 ? null : match[0];
}
public shutdown() {
this.lmEngine.shutdown();
}