Browse Source

Merge pull request #2700 from blink1073/strict-null-console

Strict null checks in Console
Afshin Darian 7 years ago
parent
commit
6c8fadab5f

+ 8 - 5
packages/console-extension/src/index.ts

@@ -165,7 +165,10 @@ function activateConsole(app: JupyterLab, manager: IServiceManager, mainMenu: IM
   // Add a launcher item if the launcher is available.
   if (launcher) {
     manager.ready.then(() => {
-      let specs = manager.specs;
+      const specs = manager.specs;
+      if (!specs) {
+        return;
+      }
       let baseUrl = PageConfig.getBaseUrl();
       for (let name in specs.kernelspecs) {
         let displayName = specs.kernelspecs[name].display_name;
@@ -224,9 +227,7 @@ function activateConsole(app: JupyterLab, manager: IServiceManager, mainMenu: IM
     execute: (args: Partial<ConsolePanel.IOptions>) => {
       let path = args['path'];
       let widget = tracker.find(value => {
-        if (value.console.session.path === path) {
-          return true;
-        }
+        return value.console.session.path === path;
       });
       if (widget) {
         shell.activateById(widget.id);
@@ -238,6 +239,7 @@ function activateConsole(app: JupyterLab, manager: IServiceManager, mainMenu: IM
           if (model) {
             return createConsole(args);
           }
+          return Promise.reject(`No running console for path: ${path}`);
         });
       }
     },
@@ -354,7 +356,7 @@ function activateConsole(app: JupyterLab, manager: IServiceManager, mainMenu: IM
   commands.addCommand(command, {
     label: 'Close and Shutdown',
     execute: args => {
-      let current = getCurrent(args);
+      const current = getCurrent(args);
       if (!current) {
         return;
       }
@@ -387,6 +389,7 @@ function activateConsole(app: JupyterLab, manager: IServiceManager, mainMenu: IM
           widget.console.inject(args['code'] as string);
           return true;
         }
+        return false;
       });
     },
     isEnabled: hasWidget

+ 16 - 12
packages/console/src/foreign.ts

@@ -68,19 +68,18 @@ class ForeignHandler implements IDisposable {
    * Test whether the handler is disposed.
    */
   get isDisposed(): boolean {
-    return this._cells === null;
+    return this._isDisposed;
   }
 
   /**
    * Dispose the resources held by the handler.
    */
   dispose(): void {
-    if (this._cells === null) {
+    if (this.isDisposed) {
       return;
     }
-    let cells = this._cells;
-    this._cells = null;
-    cells.clear();
+    this._isDisposed = true;
+    this._cells.clear();
     Signal.clearData(this);
   }
 
@@ -97,7 +96,7 @@ class ForeignHandler implements IDisposable {
     }
     let kernel = this.session.kernel;
     if (!kernel) {
-      return;
+      return false;
     }
 
     // Check whether this message came from an external session.
@@ -109,7 +108,7 @@ class ForeignHandler implements IDisposable {
     let msgType = msg.header.msg_type;
     let parentHeader = msg.parent_header as KernelMessage.IHeader;
     let parentMsgId = parentHeader.msg_id as string;
-    let cell: CodeCell;
+    let cell: CodeCell | undefined;
     switch (msgType) {
     case 'execute_input':
       let inputMsg = msg as KernelMessage.IExecuteInputMsg;
@@ -132,14 +131,18 @@ class ForeignHandler implements IDisposable {
       }
       let output = msg.content as nbformat.IOutput;
       cell = this._cells.get(parentMsgId);
-      output.output_type = msgType as nbformat.OutputType;
-      cell.model.outputs.add(output);
+      if (cell) {
+        output.output_type = msgType as nbformat.OutputType;
+        cell.model.outputs.add(output);
+      }
       parent.update();
       return true;
     case 'clear_output':
       let wait = (msg as KernelMessage.IClearOutputMsg).content.wait;
       cell = this._cells.get(parentMsgId);
-      cell.model.outputs.clear(wait);
+      if (cell) {
+        cell.model.outputs.clear(wait);
+      }
       return true;
     default:
       return false;
@@ -158,8 +161,9 @@ class ForeignHandler implements IDisposable {
 
   private _cells = new Map<string, CodeCell>();
   private _enabled = true;
-  private _parent: ForeignHandler.IReceiver = null;
-  private _factory: () => CodeCell = null;
+  private _parent: ForeignHandler.IReceiver;
+  private _factory: () => CodeCell;
+  private _isDisposed = false;
 }
 
 

+ 19 - 13
packages/console/src/history.ts

@@ -35,7 +35,7 @@ interface IConsoleHistory extends IDisposable {
   /**
    * The current editor used by the history widget.
    */
-  editor: CodeEditor.IEditor;
+  editor: CodeEditor.IEditor | null;
 
   /**
    * The placeholder text that a history session began with.
@@ -105,23 +105,26 @@ class ConsoleHistory implements IConsoleHistory {
   /**
    * The current editor used by the history manager.
    */
-  get editor(): CodeEditor.IEditor {
+  get editor(): CodeEditor.IEditor | null {
     return this._editor;
   }
-  set editor(value: CodeEditor.IEditor) {
+  set editor(value: CodeEditor.IEditor | null) {
     if (this._editor === value) {
       return;
     }
 
-    let editor = this._editor;
-    if (editor) {
-      editor.edgeRequested.disconnect(this.onEdgeRequest, this);
-      editor.model.value.changed.disconnect(this.onTextChange, this);
+    let prev = this._editor;
+    if (prev) {
+      prev.edgeRequested.disconnect(this.onEdgeRequest, this);
+      prev.model.value.changed.disconnect(this.onTextChange, this);
     }
 
-    editor = this._editor = value;
-    editor.edgeRequested.connect(this.onEdgeRequest, this);
-    editor.model.value.changed.connect(this.onTextChange, this);
+    this._editor = value;
+
+    if (value) {
+      value.edgeRequested.connect(this.onEdgeRequest, this);
+      value.model.value.changed.connect(this.onTextChange, this);
+    }
   }
 
   /**
@@ -252,7 +255,7 @@ class ConsoleHistory implements IConsoleHistory {
    * Handle an edge requested signal.
    */
   protected onEdgeRequest(editor: CodeEditor.IEditor, location: CodeEditor.EdgeLocation): void {
-    let model = this._editor.model;
+    let model = editor.model;
     let source = model.value.text;
 
     if (location === 'top') {
@@ -278,7 +281,10 @@ class ConsoleHistory implements IConsoleHistory {
         }
         this._setByHistory = true;
         model.value.text = text;
-        editor.setCursorPosition(editor.getPositionAt(text.length));
+        let pos = editor.getPositionAt(text.length);
+        if (pos) {
+          editor.setCursorPosition(pos);
+        }
       });
     }
   }
@@ -304,7 +310,7 @@ class ConsoleHistory implements IConsoleHistory {
   private _placeholder: string = '';
   private _setByHistory = false;
   private _isDisposed = false;
-  private _editor: CodeEditor.IEditor = null;
+  private _editor: CodeEditor.IEditor | null = null;
 }
 
 

+ 10 - 4
packages/console/src/panel.ts

@@ -90,7 +90,6 @@ class ConsolePanel extends Panel {
     session.ready.then(() => {
       this._connected = new Date();
       this._updateTitle();
-      this.console.promptCell.editor.focus();
     });
 
     this._manager = manager;
@@ -133,13 +132,20 @@ class ConsolePanel extends Panel {
    */
   protected onAfterAttach(msg: Message): void {
     this._session.initialize();
+    let prompt = this.console.promptCell;
+    if (prompt) {
+      prompt.editor.focus();
+    }
   }
 
   /**
    * Handle `'activate-request'` messages.
    */
   protected onActivateRequest(msg: Message): void {
-    this.console.promptCell.editor.focus();
+    let prompt = this.console.promptCell;
+    if (prompt) {
+      prompt.editor.focus();
+    }
   }
 
   /**
@@ -166,8 +172,8 @@ class ConsolePanel extends Panel {
   }
 
   private _manager: ServiceManager.IManager;
-  private _executed: Date = null;
-  private _connected: Date = null;
+  private _executed: Date | null = null;
+  private _connected: Date | null = null;
   private _session: ClientSession;
 }
 

+ 47 - 33
packages/console/src/widget.ts

@@ -239,25 +239,12 @@ class CodeConsole extends Widget {
    */
   dispose() {
     // Do nothing if already disposed.
-    if (this._foreignHandler === null) {
+    if (this.isDisposed) {
       return;
     }
-
-    let cells = this._cells;
-    let history = this._history;
-    let foreignHandler = this._foreignHandler;
-
-    this._banner = null;
-    this._cells = null;
-    this._content = null;
-    this._input = null;
-    this._mimeTypeService = null;
-    this._foreignHandler = null;
-    this._history = null;
-
-    cells.clear();
-    history.dispose();
-    foreignHandler.dispose();
+    this._cells.clear();
+    this._history.dispose();
+    this._foreignHandler.dispose();
 
     super.dispose();
   }
@@ -277,7 +264,10 @@ class CodeConsole extends Widget {
       return Promise.resolve(void 0);
     }
 
-    let promptCell = this.promptCell;
+    const promptCell = this.promptCell;
+    if (!promptCell) {
+      return Promise.reject('Cannot execute without a prompt cell');
+    }
     promptCell.model.trusted = true;
 
     if (force) {
@@ -318,6 +308,9 @@ class CodeConsole extends Widget {
    */
   insertLinebreak(): void {
     let promptCell = this.promptCell;
+    if (!promptCell) {
+      return;
+    }
     let model = promptCell.model;
     let editor = promptCell.editor;
     // Insert the line break at the cursor position, and move cursor forward.
@@ -325,8 +318,10 @@ class CodeConsole extends Widget {
     let offset = editor.getOffsetAt(pos);
     let text = model.value.text;
     model.value.text = text.substr(0, offset) + '\n' + text.substr(offset);
-    pos = editor.getPositionAt(offset + 1);
-    editor.setCursorPosition(pos);
+    pos = editor.getPositionAt(offset + 1) as CodeEditor.IPosition;
+    if (pos) {
+      editor.setCursorPosition(pos);
+    }
   }
 
   /**
@@ -339,6 +334,9 @@ class CodeConsole extends Widget {
     let output = map(layout.widgets, widget => {
       return (widget as CodeCell).model.toJSON() as nbformat.ICodeCell;
     });
+    if (!promptCell) {
+      return toArray(output);
+    }
     // Serialize prompt cell and return.
     return toArray(output).concat(promptCell.model.toJSON() as nbformat.ICodeCell);
   }
@@ -390,7 +388,10 @@ class CodeConsole extends Widget {
    * Handle `'activate-request'` messages.
    */
   protected onActivateRequest(msg: Message): void {
-    this.promptCell.editor.focus();
+    let editor = this.promptCell && this.promptCell.editor;
+    if (editor) {
+      editor.focus();
+    }
     this.update();
   }
 
@@ -441,7 +442,10 @@ class CodeConsole extends Widget {
    * Handle the `'keydown'` event for the widget.
    */
   private _evtKeyDown(event: KeyboardEvent): void {
-    let editor = this.promptCell.editor;
+    let editor = this.promptCell && this.promptCell.editor;
+    if (!editor) {
+      return;
+    }
     if (event.keyCode === 13 && !editor.hasFocus()) {
       event.preventDefault();
       editor.focus();
@@ -544,12 +548,20 @@ class CodeConsole extends Widget {
    * Test whether we should execute the prompt cell.
    */
   private _shouldExecute(timeout: number): Promise<boolean> {
-    let promptCell = this.promptCell;
+    const promptCell = this.promptCell;
+    if (!promptCell) {
+      return Promise.resolve(false);
+    }
     let model = promptCell.model;
     let code = model.value.text + '\n';
     return new Promise<boolean>((resolve, reject) => {
       let timer = setTimeout(() => { resolve(true); }, timeout);
-      this.session.kernel.requestIsComplete({ code }).then(isComplete => {
+      let kernel = this.session.kernel;
+      if (!kernel) {
+        resolve(false);
+        return;
+      }
+      kernel.requestIsComplete({ code }).then(isComplete => {
         clearTimeout(timer);
         if (this.isDisposed) {
           resolve(false);
@@ -561,7 +573,9 @@ class CodeConsole extends Widget {
         model.value.text = code + isComplete.content.indent;
         let editor = promptCell.editor;
         let pos = editor.getPositionAt(model.value.text.length);
-        editor.setCursorPosition(pos);
+        if (pos) {
+          editor.setCursorPosition(pos);
+        }
         resolve(false);
       }).catch(() => { resolve(true); });
     });
@@ -585,22 +599,22 @@ class CodeConsole extends Widget {
       return;
     }
     kernel.ready.then(() => {
-      if (this.isDisposed) {
+      if (this.isDisposed || !kernel || !kernel.info) {
         return;
       }
       this._handleInfo(kernel.info);
     });
   }
 
-  private _banner: RawCell = null;
-  private _cells: IObservableList<Cell> = null;
-  private _content: Panel = null;
+  private _banner: RawCell;
+  private _cells: IObservableList<Cell>;
+  private _content: Panel;
   private _executed = new Signal<this, Date>(this);
-  private _foreignHandler: ForeignHandler =  null;
-  private _history: IConsoleHistory = null;
-  private _input: Panel = null;
+  private _foreignHandler: ForeignHandler;
+  private _history: IConsoleHistory ;
+  private _input: Panel;
   private _mimetype = 'text/x-ipython';
-  private _mimeTypeService: IEditorMimeTypeService = null;
+  private _mimeTypeService: IEditorMimeTypeService;
   private _promptCellCreated = new Signal<this, CodeCell>(this);
 }