Browse Source

Compute the text diff upon completion rather than replacing the whole
text field.

Ian Rose 6 years ago
parent
commit
0326f9c9d8

+ 5 - 9
packages/completer/src/handler.ts

@@ -166,26 +166,22 @@ export class CompletionHandler implements IDisposable {
   /**
    * Handle a completion selected signal from the completion widget.
    */
-  protected onCompletionSelected(completer: Completer, value: string): void {
+  protected onCompletionSelected(completer: Completer, val: string): void {
     const model = completer.model;
     const editor = this._editor;
     if (!editor || !model) {
       return;
     }
 
-    const patch = model.createPatch(value);
+    const patch = model.createPatch(val);
 
     if (!patch) {
       return;
     }
 
-    const { offset, text } = patch;
-    editor.model.value.text = text;
-
-    const position = editor.getPositionAt(offset);
-    if (position) {
-      editor.setCursorPosition(position);
-    }
+    const { start, end, value } = patch;
+    editor.model.value.remove(start, end);
+    editor.model.value.insert(start, value);
   }
 
   /**

+ 4 - 13
packages/completer/src/model.ts

@@ -96,7 +96,7 @@ export class CompleterModel implements Completer.IModel {
 
     // If the text change means that the original start point has been preceded,
     // then the completion is no longer valid and should be reset.
-    if (currentLine.length < originalLine.length) {
+    if (!this._subsetMatch && currentLine.length < originalLine.length) {
       this.reset(true);
       return;
     }
@@ -301,13 +301,6 @@ export class CompleterModel implements Completer.IModel {
       return;
     }
 
-    // When the completer detects a common subset prefix for all options,
-    // it updates the model and sets the model source to that value, but this
-    // text change should be ignored.
-    if (this._subsetMatch) {
-      return;
-    }
-
     const { text, column, line } = change;
     const last = text.split('\n')[line][column - 1];
 
@@ -337,12 +330,10 @@ export class CompleterModel implements Completer.IModel {
       return undefined;
     }
 
-    const { start, end } = cursor;
-    const { text } = original;
-    const prefix = text.substring(0, start);
-    const suffix = text.substring(end);
+    let { start, end } = cursor;
+    end = end + (this.current.text.length - this.original.text.length);
 
-    return { offset: (prefix + patch).length, text: prefix + patch + suffix };
+    return { start, end, value: patch };
   }
 
   /**

+ 13 - 8
packages/completer/src/widget.ts

@@ -1,6 +1,10 @@
 // Copyright (c) Jupyter Development Team.
 // Distributed under the terms of the Modified BSD License.
 
+import { HoverBox, defaultSanitizer } from '@jupyterlab/apputils';
+
+import { CodeEditor } from '@jupyterlab/codeeditor';
+
 import { IIterator, IterableOrArrayLike, toArray } from '@phosphor/algorithm';
 
 import { JSONObject, JSONExt } from '@phosphor/coreutils';
@@ -15,10 +19,6 @@ import { ISignal, Signal } from '@phosphor/signaling';
 
 import { Widget } from '@phosphor/widgets';
 
-import { HoverBox, defaultSanitizer } from '@jupyterlab/apputils';
-
-import { CodeEditor } from '@jupyterlab/codeeditor';
-
 /**
  * The class name added to completer menu items.
  */
@@ -662,14 +662,19 @@ export namespace Completer {
    */
   export interface IPatch {
     /**
-     * The patched text.
+     * The start of the range to be patched.
      */
-    text: string;
+    start: number;
+
+    /**
+     * The end of the range to be patched.
+     */
+    end: number;
 
     /**
-     * The offset of the cursor.
+     * The value to be patched in.
      */
-    offset: number;
+    value: string;
   }
 
   /**