Преглед изворни кода

Refactor of completion to support more semantic actions, more correct multiline support.

A. Darian пре 8 година
родитељ
комит
2553641c3f

+ 10 - 1
src/notebook/cells/editor.ts

@@ -136,6 +136,11 @@ interface ITextChange extends IEditorState {
  */
 export
 interface ICompletionRequest extends IEditorState {
+  /**
+   * The cursor position of the request, including line breaks.
+   */
+  position: number;
+
   /**
    * The current value of the editor text.
    */
@@ -328,16 +333,20 @@ class CellEditorWidget extends CodeMirrorWidget {
     let chHeight = editor.defaultTextHeight();
     let chWidth = editor.defaultCharWidth();
     let coords = editor.charCoords({ line, ch }, 'page') as ICoords;
+    let position = editor.getDoc().indexFromPos({ line, ch })
 
     // A completion request signal should only be emitted if the final
     // character of the current line is not whitespace. Otherwise, the
     // default tab action of creating a tab character should be allowed to
     // propagate.
     if (currentLine.match(/\S$/)) {
-      let data = { line, ch, chHeight, chWidth, coords, currentValue };
+      let data = {
+        line, ch, chHeight, chWidth, coords, position, currentValue
+      };
       this.completionRequested.emit(data as ICompletionRequest);
       event.preventDefault();
       event.stopPropagation();
+      event.stopImmediatePropagation();
     }
   }
 

+ 2 - 3
src/notebook/completion/handler.ts

@@ -97,9 +97,8 @@ class CellCompletionHandler implements IDisposable {
    */
   private _complete(request: ICompletionRequest): void {
     let content: KernelMessage.ICompleteRequest = {
-      // Only send the current line of code for completion.
-      code: request.currentValue.split('\n')[request.line],
-      cursor_pos: request.ch
+      code: request.currentValue,
+      cursor_pos: request.position
     };
     let pending = ++this._pending;
     this._kernel.complete(content).then(msg => {

+ 5 - 15
src/notebook/completion/model.ts

@@ -336,21 +336,11 @@ class CompletionModel implements ICompletionModel {
     }
 
     let { start, end } = cursor;
-    let lines = original.currentValue.split('\n');
-    let line = lines[original.line];
-    let prefix = line.substring(0, start);
-    let suffix = line.substring(end);
-
-    lines[original.line] = prefix + patch + suffix;
-    let text = lines.join('\n');
-
-    // Add current line to position.
-    let position = prefix.length + patch.length;
-    // Add all the preceding lines' lengths to position.
-    for (let i = 0; i < original.line; i++) {
-      // Add an extra character for the line break.
-      position += lines[i].length + 1;
-    }
+    let value = original.currentValue;
+    let prefix = value.substring(0, start);
+    let suffix = value.substring(end);
+    let text = prefix + patch + suffix;
+    let position = (prefix + patch).length;
 
     return { position, text };
   }

+ 3 - 0
src/notebook/completion/widget.ts

@@ -389,6 +389,9 @@ class CompletionWidget extends Widget {
    */
   private _selectActive(): void {
     let active = this.node.querySelector(`.${ACTIVE_CLASS}`) as HTMLElement;
+    if (!active) {
+      return;
+    }
     this.selected.emit(active.dataset['value']);
     this._reset();
   }

+ 92 - 25
test/src/notebook/completion/model.spec.ts

@@ -56,10 +56,14 @@ describe('notebook/completion/model', () => {
       it('should signal when original request changes', () => {
         let model = new CompletionModel();
         let called = 0;
-        let currentValue = 'foo';
-        let coords: ICoords = null;
         let request: ICompletionRequest = {
-          ch: 0, chHeight: 0, chWidth: 0, line: 0, coords, currentValue
+          ch: 0,
+          chHeight: 0,
+          chWidth: 0,
+          line: 0,
+          coords: null,
+          position: 0,
+          currentValue: 'foo'
         };
         let listener = (sender: any, args: void) => { called++; };
         model.stateChanged.connect(listener);
@@ -73,10 +77,14 @@ describe('notebook/completion/model', () => {
       it('should not signal when original request has not changed', () => {
         let model = new CompletionModel();
         let called = 0;
-        let currentValue = 'foo';
-        let coords: ICoords = null;
         let request: ICompletionRequest = {
-          ch: 0, chHeight: 0, chWidth: 0, line: 0, coords, currentValue
+          ch: 0,
+          chHeight: 0,
+          chWidth: 0,
+          line: 0,
+          coords: null,
+          position: 0,
+          currentValue: 'foo'
         };
         let listener = (sender: any, args: void) => { called++; };
         model.stateChanged.connect(listener);
@@ -98,7 +106,13 @@ describe('notebook/completion/model', () => {
         let coords: ICoords = null;
         let cursor: ICursorSpan = { start: 0, end: 0 };
         let request: ICompletionRequest = {
-          ch: 0, chHeight: 0, chWidth: 0, line: 0, coords, currentValue
+          ch: 0,
+          chHeight: 0,
+          chWidth: 0,
+          line: 0,
+          coords: coords,
+          position: 0,
+          currentValue: currentValue
         };
         let change: ITextChange = {
           ch: 0, chHeight: 0, chWidth: 0, line: 0, coords, oldValue, newValue
@@ -124,7 +138,13 @@ describe('notebook/completion/model', () => {
         let coords: ICoords = null;
         let cursor: ICursorSpan = { start: 0, end: 0 };
         let request: ICompletionRequest = {
-          ch: 0, chHeight: 0, chWidth: 0, line: 0, coords, currentValue
+          ch: 0,
+          chHeight: 0,
+          chWidth: 0,
+          line: 0,
+          coords: coords,
+          position: 0,
+          currentValue: currentValue
         };
         let change: ITextChange = {
           ch: 0, chHeight: 0, chWidth: 0, line: 0, coords, oldValue, newValue
@@ -207,10 +227,14 @@ describe('notebook/completion/model', () => {
 
       it('should return the original request', () => {
         let model = new CompletionModel();
-        let currentValue = 'foo';
-        let coords: ICoords = null;
         let request: ICompletionRequest = {
-          ch: 0, chHeight: 0, chWidth: 0, line: 0, coords, currentValue
+          ch: 0,
+          chHeight: 0,
+          chWidth: 0,
+          line: 0,
+          coords: null,
+          position: 0,
+          currentValue: 'foo'
         };
         model.original = request;
         expect(model.original).to.equal(request);
@@ -233,7 +257,13 @@ describe('notebook/completion/model', () => {
         let coords: ICoords = null;
         let cursor: ICursorSpan = { start: 0, end: 0 };
         let request: ICompletionRequest = {
-          ch: 0, chHeight: 0, chWidth: 0, line: 0, coords, currentValue
+          ch: 0,
+          chHeight: 0,
+          chWidth: 0,
+          line: 0,
+          coords: coords,
+          position: 0,
+          currentValue: currentValue
         };
         let change: ITextChange = {
           ch: 0, chHeight: 0, chWidth: 0, line: 0, coords, oldValue, newValue
@@ -254,7 +284,13 @@ describe('notebook/completion/model', () => {
         let coords: ICoords = null;
         let cursor: ICursorSpan = { start: 0, end: 0 };
         let request: ICompletionRequest = {
-          ch: 0, chHeight: 0, chWidth: 0, line: 0, coords, currentValue
+          ch: 0,
+          chHeight: 0,
+          chWidth: 0,
+          line: 0,
+          coords: coords,
+          position: 0,
+          currentValue: currentValue
         };
         let change: ITextChange = {
           ch: 0, chHeight: 0, chWidth: 0, line: 0, coords, oldValue, newValue
@@ -275,7 +311,13 @@ describe('notebook/completion/model', () => {
         let coords: ICoords = null;
         let cursor: ICursorSpan = { start: 0, end: 0 };
         let request: ICompletionRequest = {
-          ch: 0, chHeight: 0, chWidth: 0, line: 0, coords, currentValue
+          ch: 0,
+          chHeight: 0,
+          chWidth: 0,
+          line: 0,
+          coords: coords,
+          position: 0,
+          currentValue: currentValue
         };
         let change: ITextChange = {
           ch: 0, chHeight: 0, chWidth: 0, line: 0, coords, oldValue, newValue
@@ -299,11 +341,15 @@ describe('notebook/completion/model', () => {
 
       it('should not set if original request is nonexistent', () => {
         let model = new CompletionModel();
-        let currentValue = 'foo';
-        let coords: ICoords = null;
         let cursor: ICursorSpan = { start: 0, end: 0 };
         let request: ICompletionRequest = {
-          ch: 0, chHeight: 0, chWidth: 0, line: 0, coords, currentValue
+          ch: 0,
+          chHeight: 0,
+          chWidth: 0,
+          line: 0,
+          coords: null,
+          position: 0,
+          currentValue: 'foo'
         };
         model.cursor = cursor;
         expect(model.cursor).to.be(null);
@@ -357,7 +403,13 @@ describe('notebook/completion/model', () => {
         let coords: ICoords = null;
         let cursor: ICursorSpan = { start: 0, end: 0 };
         let request: ICompletionRequest = {
-          ch: 0, chHeight: 0, chWidth: 0, line: 0, coords, currentValue
+          ch: 0,
+          chHeight: 0,
+          chWidth: 0,
+          line: 0,
+          coords: coords,
+          position: 0,
+          currentValue: currentValue
         };
         let change: ITextChange = {
           ch: 4, chHeight: 0, chWidth: 0, line: 0, coords, oldValue, newValue
@@ -376,7 +428,13 @@ describe('notebook/completion/model', () => {
         let newValue = 'foo ';
         let coords: ICoords = null;
         let request: ICompletionRequest = {
-          ch: 0, chHeight: 0, chWidth: 0, line: 0, coords, currentValue
+          ch: 0,
+          chHeight: 0,
+          chWidth: 0,
+          line: 0,
+          coords: coords,
+          position: 0,
+          currentValue: currentValue
         };
         let change: ITextChange = {
           ch: 4, chHeight: 0, chWidth: 0, line: 0, coords, oldValue, newValue
@@ -393,13 +451,17 @@ describe('notebook/completion/model', () => {
 
       it('should return a patch value', () => {
         let model = new CompletionModel();
-        let currentValue = 'foo';
         let patch = 'foobar';
         let want: ICompletionPatch = { text: patch, position: patch.length };
-        let coords: ICoords = null;
         let cursor: ICursorSpan = { start: 0, end: 3 };
         let request: ICompletionRequest = {
-          ch: 3, chHeight: 0, chWidth: 0, line: 0, coords, currentValue
+          ch: 0,
+          chHeight: 0,
+          chWidth: 0,
+          line: 0,
+          coords: null,
+          position: 0,
+          currentValue: 'foo'
         };
         model.original = request;
         model.cursor = cursor;
@@ -416,10 +478,15 @@ describe('notebook/completion/model', () => {
         let currentValue = 'foo\nbar';
         let patch = 'barbaz';
         let want: ICompletionPatch = { text: 'foo\nbarbaz', position: 10 };
-        let coords: ICoords = null;
-        let cursor: ICursorSpan = { start: 0, end: 3 };
+        let cursor: ICursorSpan = { start: 4, end: 7 };
         let request: ICompletionRequest = {
-          ch: 3, chHeight: 0, chWidth: 0, line: 1, coords, currentValue
+          ch: 0,
+          chHeight: 0,
+          chWidth: 0,
+          line: 0,
+          coords: null,
+          position: 0,
+          currentValue: currentValue
         };
         model.original = request;
         model.cursor = cursor;