Parcourir la source

wip update apis

Steven Silvester il y a 8 ans
Parent
commit
d1174957df

+ 1 - 1
src/completer/handler.ts

@@ -107,7 +107,7 @@ class CellCompleterHandler implements IDisposable {
     };
     let pending = ++this._pending;
 
-    return this._kernel.complete(content).then(msg => {
+    return this._kernel.requestComplete(content).then(msg => {
       this.onReply(pending, request, msg);
     });
   }

+ 2 - 7
src/console/content.ts

@@ -497,12 +497,7 @@ class ConsoleContent extends Widget {
    * Initialize the banner and mimetype.
    */
   private _initialize(): void {
-    let session = this._session;
-    if (session.kernel.info) {
-      this._handleInfo(this._session.kernel.info);
-      return;
-    }
-    session.kernel.kernelInfo().then(msg => this._handleInfo(msg.content));
+    this._session.kernel.info().then(info => this._handleInfo(info));
   }
 
   /**
@@ -606,7 +601,7 @@ class ConsoleContent extends Widget {
     let code = prompt.model.source + '\n';
     return new Promise<boolean>((resolve, reject) => {
       let timer = setTimeout(() => { resolve(true); }, timeout);
-      this._session.kernel.isComplete({ code }).then(isComplete => {
+      this._session.kernel.requestIsComplete({ code }).then(isComplete => {
         clearTimeout(timer);
         if (isComplete.content.status !== 'incomplete') {
           resolve(true);

+ 3 - 1
src/console/history.ts

@@ -114,7 +114,9 @@ class ConsoleHistory implements IConsoleHistory {
       return;
     }
 
-    this._kernel.history(Private.initialRequest).then(v => this.onHistory(v));
+    this._kernel.requestHistory(Private.initialRequest).then(v => {
+      this.onHistory(v);
+    });
   }
 
   /**

+ 17 - 23
src/console/plugin.ts

@@ -235,7 +235,6 @@ function activateConsole(app: JupyterLab, services: IServiceManager, rendermime:
           name = session.path.split('/').pop();
           name = `Console ${name.match(CONSOLE_REGEX)[1]}`;
           createConsole(session, name);
-          manager.listRunning();  // Trigger a refresh.
           return session.id;
         });
       }
@@ -261,7 +260,6 @@ function activateConsole(app: JupyterLab, services: IServiceManager, rendermime:
         };
         return manager.startNew(options).then(session => {
           createConsole(session, name);
-          manager.listRunning();  // Trigger a refresh.
           return session.id;
         });
       });
@@ -305,16 +303,14 @@ function activateConsole(app: JupyterLab, services: IServiceManager, rendermime:
     if (args.kernel) {
       return Promise.resolve(args.kernel);
     }
-    return manager.listRunning().then((sessions: Session.IModel[]) => {
-      let options = {
-        name,
-        specs,
-        sessions,
-        preferredLanguage: args.preferredLanguage || '',
-        host: document.body
-      };
-      return selectKernel(options);
-    });
+    let options = {
+      name,
+      specs,
+      sessions: manager.running(),
+      preferredLanguage: args.preferredLanguage || '',
+      host: document.body
+    };
+    return selectKernel(options);
   }
 
   let displayNameMap: { [key: string]: string } = Object.create(null);
@@ -382,17 +378,15 @@ function activateConsole(app: JupyterLab, services: IServiceManager, rendermime:
       if (session.kernel) {
         lang = specs.kernelspecs[session.kernel.name].language;
       }
-      manager.listRunning().then((sessions: Session.IModel[]) => {
-        let options = {
-          name: widget.parent.title.label,
-          specs,
-          sessions,
-          preferredLanguage: lang,
-          kernel: session.kernel.model,
-          host: widget.parent.node
-        };
-        return selectKernel(options);
-      }).then((kernelId: Kernel.IModel) => {
+      let options = {
+        name: widget.parent.title.label,
+        specs,
+        sessions: manager.running(),
+        preferredLanguage: lang,
+        kernel: session.kernel.model,
+        host: widget.parent.node
+      };
+      return selectKernel(options).then(kernelId => {
         // If the user cancels, kernelId will be void and should be ignored.
         if (kernelId) {
           session.changeKernel(kernelId);

+ 5 - 25
src/docmanager/manager.ts

@@ -6,7 +6,7 @@ import {
 } from '@jupyterlab/services';
 
 import {
-  each
+  IIterable, each
 } from 'phosphor/lib/algorithm/iteration';
 
 import {
@@ -165,32 +165,12 @@ class DocumentManager implements IDisposable {
   }
 
   /**
-   * List the running notebook sessions.
-   */
-  listSessions(): Promise<Session.IModel[]> {
-    return this._serviceManager.sessions.listRunning();
-  }
-
-  /**
-   * Handle the renaming of an open document.
-   *
-   * @param oldPath - The previous path.
+   * Create an iterator over the running sessions.
    *
-   * @param newPath - The new path.
-   */
-  handleRename(oldPath: string, newPath: string): void {
-    each(this._contexts, context => {
-      if (context.path === oldPath) {
-        context.setPath(newPath);
-      }
-    });
-  }
-
-  /**
-   * Handle a file deletion.
+   * @returns A new iterator over the running sessions.
    */
-  handleDelete(path: string): void {
-    // TODO: Leave all of the widgets open and flag them as orphaned?
+  sessions(): IIterable<Session.IModel> {
+    return this._serviceManager.sessions.running();
   }
 
   /**

+ 29 - 23
src/docregistry/context.ts

@@ -5,6 +5,10 @@ import {
   ContentsManager, Contents, Kernel, ServiceManager, Session, utils
 } from '@jupyterlab/services';
 
+import {
+  IIterable
+} from 'phosphor/lib/algorithm/iteration';
+
 import {
   JSONObject
 } from 'phosphor/lib/algorithm/json';
@@ -53,32 +57,33 @@ class Context<T extends DocumentRegistry.IModel> implements DocumentRegistry.ICo
     let lang = this._factory.preferredLanguage(ext);
     this._model = this._factory.createNew(lang);
     manager.sessions.runningChanged.connect(this._onSessionsChanged, this);
+    manager.contents.fileChanged.connect(this._onFileChanged, this);
   }
 
   /**
    * A signal emitted when the kernel changes.
    */
-  kernelChanged: ISignal<DocumentRegistry.IContext<T>, Kernel.IKernel>;
+  kernelChanged: ISignal<this, Kernel.IKernel>;
 
   /**
    * A signal emitted when the path changes.
    */
-  pathChanged: ISignal<DocumentRegistry.IContext<T>, string>;
+  pathChanged: ISignal<this, string>;
 
   /**
    * A signal emitted when the model is saved or reverted.
    */
-  fileChanged: ISignal<DocumentRegistry.IContext<T>, Contents.IModel>;
+  fileChanged: ISignal<this, Contents.IModel>;
 
   /**
    * A signal emitted when the context is fully populated for the first time.
    */
-  populated: ISignal<DocumentRegistry.IContext<T>, void>;
+  populated: ISignal<this, void>;
 
   /**
    * A signal emitted when the context is disposed.
    */
-  disposed: ISignal<DocumentRegistry.IContext<T>, void>;
+  disposed: ISignal<this, void>;
 
   /**
    * Get the model associated with the document.
@@ -240,7 +245,7 @@ class Context<T extends DocumentRegistry.IModel> implements DocumentRegistry.ICo
       if (!newPath) {
         return;
       }
-      this.setPath(newPath);
+      this._path = newPath;
       let session = this._session;
       if (session) {
         let options: Session.IOptions = {
@@ -327,10 +332,12 @@ class Context<T extends DocumentRegistry.IModel> implements DocumentRegistry.ICo
   }
 
   /**
-   * Get the list of running sessions.
+   * Create an iterator over the running sessions.
+   *
+   * @returns A new iterator over the running sessions.
    */
-  listSessions(): Promise<Session.IModel[]> {
-    return this._manager.sessions.listRunning();
+  sessions(): IIterable<Session.IModel> {
+    return this._manager.sessions.running();
   }
 
   /**
@@ -360,21 +367,19 @@ class Context<T extends DocumentRegistry.IModel> implements DocumentRegistry.ICo
   }
 
   /**
-   * Set the path of the context.
-   *
-   * #### Notes
-   * This is not part of the `IContext` API and
-   * is not intended to be called by the user.
-   * It is assumed that the file has been renamed on the
-   * contents manager prior to this operation.
+   * Handle a change on the contents manager.
    */
-  setPath(value: string): void {
-    this._path = value;
-    let session = this._session;
-    if (session) {
-      session.rename(value);
+  private _onFileChanged(sender: Contents.IManager, change: Contents.IChangedArgs): void {
+    if (change.type !== 'rename') {
+      return;
+    }
+    if (change.oldValue.path === this._path) {
+      let path = this._path = change.newValue.path;
+      if (this._session) {
+        this._session.rename(path);
+      }
+      this.pathChanged.emit(path);
     }
-    this.pathChanged.emit(value);
   }
 
   /**
@@ -389,7 +394,8 @@ class Context<T extends DocumentRegistry.IModel> implements DocumentRegistry.ICo
       this.kernelChanged.emit(session.kernel);
       session.pathChanged.connect((s, path) => {
         if (path !== this._path) {
-          this.setPath(path);
+          this._path = path;
+          this.pathChanged.emit(path);
         }
       });
       session.kernelChanged.connect((s, kernel) => {

+ 33 - 34
src/docregistry/kernelselector.ts

@@ -5,6 +5,10 @@ import {
   Kernel, Session
 } from '@jupyterlab/services';
 
+import {
+  IterableOrArrayLike, each
+} from 'phosphor/lib/algorithm/iteration';
+
 import {
   showDialog
 } from '../dialog';
@@ -32,7 +36,7 @@ interface IKernelSelection {
   /**
    * The current running sessions.
    */
-  sessions: Session.IModel[];
+  sessions: IterableOrArrayLike<Session.IModel>;
 
   /**
    * The desired kernel language.
@@ -64,7 +68,7 @@ interface IPopulateOptions {
   /**
    * The current running sessions.
    */
-  sessions: Session.IModel[];
+  sessions: IterableOrArrayLike<Session.IModel>;
 
   /**
    * The optional existing kernel model.
@@ -125,17 +129,15 @@ function selectKernel(options: IKernelSelection): Promise<Kernel.IModel> {
  */
 export
 function selectKernelForContext(context: DocumentRegistry.IContext<DocumentRegistry.IModel>, host?: HTMLElement): Promise<void> {
-  return context.listSessions().then(sessions => {
-    let options: IKernelSelection = {
-      name: context.path.split('/').pop(),
-      specs: context.kernelspecs,
-      sessions,
-      preferredLanguage: context.model.defaultKernelLanguage,
-      kernel: context.kernel.model,
-      host
-    };
-    return selectKernel(options);
-  }).then(kernel => {
+  let options: IKernelSelection = {
+    name: context.path.split('/').pop(),
+    specs: context.kernelspecs,
+    sessions: context.sessions(),
+    preferredLanguage: context.model.defaultKernelLanguage,
+    kernel: context.kernel.model,
+    host
+  };
+  return selectKernel(options).then(kernel => {
     if (kernel) {
       context.changeKernel(kernel);
     }
@@ -264,32 +266,29 @@ function populateKernels(node: HTMLSelectElement, options: IPopulateOptions): vo
 
   // Add the sessions using the preferred language first.
   let matchingSessions: Session.IModel[] = [];
-  if (preferredLanguage) {
-    for (let session of sessions) {
-      if (languages[session.kernel.name] === preferredLanguage &&
-          session.kernel.id !== existing) {
-        matchingSessions.push(session);
-      }
-    }
-    if (matchingSessions) {
-      matchingSessions.sort((a, b) => {
-        return a.notebook.path.localeCompare(b.notebook.path);
-      });
-      for (let session of matchingSessions) {
-        let name = displayNames[session.kernel.name];
-        node.appendChild(optionForSession(session, name, maxLength));
-      }
-      node.appendChild(createSeparatorOption(maxLength));
-    }
-  }
-  // Add the other remaining sessions.
   let otherSessions: Session.IModel[] = [];
-  for (let session of sessions) {
-    if (matchingSessions.indexOf(session) === -1 &&
+
+  each(sessions, session => {
+    if (preferredLanguage &&
+        languages[session.kernel.name] === preferredLanguage &&
         session.kernel.id !== existing) {
+      matchingSessions.push(session);
+    } else if (session.kernel.id !== existing) {
       otherSessions.push(session);
     }
+  });
+
+  if (matchingSessions) {
+    matchingSessions.sort((a, b) => {
+      return a.notebook.path.localeCompare(b.notebook.path);
+    });
+    for (let session of matchingSessions) {
+      let name = displayNames[session.kernel.name];
+      node.appendChild(optionForSession(session, name, maxLength));
+    }
+    node.appendChild(createSeparatorOption(maxLength));
   }
+
   if (otherSessions) {
     otherSessions.sort((a, b) => {
       return a.notebook.path.localeCompare(b.notebook.path);

+ 5 - 3
src/docregistry/registry.ts

@@ -6,7 +6,7 @@ import {
 } from '@jupyterlab/services';
 
 import {
-  IIterator, each, empty, map
+  IIterator, IIterable, each, map
 } from 'phosphor/lib/algorithm/iteration';
 
 import {
@@ -698,9 +698,11 @@ namespace DocumentRegistry {
     listCheckpoints(): Promise<Contents.ICheckpointModel[]>;
 
     /**
-     * Get the list of running sessions.
+     * Create an iterator over the running sessions.
+     *
+     * @returns A new iterator over the running sessions.
      */
-    listSessions(): Promise<Session.IModel[]>;
+    sessions(): IIterable<Session.IModel>;
 
     /**
      * Resolve a url to a correct server path.

+ 0 - 9
src/filebrowser/browser.ts

@@ -105,15 +105,6 @@ class FileBrowser extends Widget {
     });
     this._listing = new DirListing({ manager, model, renderer });
 
-    model.fileChanged.connect((fbModel, args) => {
-      let oldPath = args.oldValue && args.oldValue.path || null;
-      if (args.newValue) {
-        manager.handleRename(oldPath, args.newValue.path);
-      } else {
-        manager.handleDelete(oldPath);
-      }
-    });
-
     this._crumbs.addClass(CRUMBS_CLASS);
     this._buttons.addClass(BUTTON_CLASS);
     this._listing.addClass(LISTING_CLASS);

+ 22 - 38
src/filebrowser/dialogs.ts

@@ -6,7 +6,7 @@ import {
 } from '@jupyterlab/services';
 
 import {
-  each
+  IterableOrArrayLike, each
 } from 'phosphor/lib/algorithm/iteration';
 
 import {
@@ -59,13 +59,11 @@ function createFromDialog(model: FileBrowserModel, manager: DocumentManager, cre
 export
 function openWithDialog(path: string, manager: DocumentManager, host?: HTMLElement): Promise<Widget> {
   let handler: OpenWithHandler;
-  return manager.listSessions().then(sessions => {
-    handler = new OpenWithHandler(path, manager, sessions);
-    return showDialog({
-      title: 'Open File',
-      body: handler.node,
-      okText: 'OPEN'
-    });
+  handler = new OpenWithHandler(path, manager);
+  return showDialog({
+    title: 'Open File',
+    body: handler.node,
+    okText: 'OPEN'
   }).then(result => {
     if (result.text === 'OPEN') {
       return handler.open();
@@ -80,14 +78,12 @@ function openWithDialog(path: string, manager: DocumentManager, host?: HTMLEleme
 export
 function createNewDialog(model: FileBrowserModel, manager: DocumentManager, host?: HTMLElement): Promise<Widget> {
   let handler: CreateNewHandler;
-  return manager.listSessions().then(sessions => {
-    handler = new CreateNewHandler(model, manager, sessions);
-    return showDialog({
-      title: 'Create New File',
-      host,
-      body: handler.node,
-      okText: 'CREATE'
-    });
+  handler = new CreateNewHandler(model, manager);
+  return showDialog({
+    title: 'Create New File',
+    host,
+    body: handler.node,
+    okText: 'CREATE'
   }).then(result => {
     if (result.text === 'CREATE') {
       return handler.open();
@@ -132,11 +128,9 @@ class OpenWithHandler extends Widget {
   /**
    * Construct a new "open with" dialog.
    */
-  constructor(path: string, manager: DocumentManager, sessions: Session.IModel[], host?: HTMLElement) {
+  constructor(path: string, manager: DocumentManager) {
     super({ node: Private.createOpenWithNode() });
     this._manager = manager;
-    this._host = host;
-    this._sessions = sessions;
 
     this.inputNode.textContent = path;
     this._ext = path.split('.').pop();
@@ -149,9 +143,7 @@ class OpenWithHandler extends Widget {
    * Dispose of the resources used by the widget.
    */
   dispose(): void {
-    this._host = null;
     this._manager = null;
-    this._sessions = null;
     super.dispose();
   }
 
@@ -213,7 +205,7 @@ class OpenWithHandler extends Widget {
       this._ext, widgetName
     );
     let specs = this._manager.kernelspecs;
-    let sessions = this._sessions;
+    let sessions = this._manager.sessions();
     Private.updateKernels(this.kernelDropdownNode,
       { preference, specs, sessions }
     );
@@ -221,8 +213,6 @@ class OpenWithHandler extends Widget {
 
   private _ext = '';
   private _manager: DocumentManager = null;
-  private _host: HTMLElement = null;
-  private _sessions: Session.IModel[] = null;
 }
 
 
@@ -324,7 +314,7 @@ class CreateFromHandler extends Widget {
     let preference = registry.getKernelPreference(ext, widgetName);
     if (preference.canStartKernel) {
       let specs = this._manager.kernelspecs;
-      let sessions = this._sessions;
+      let sessions = this._manager.sessions();
       let preferredKernel = kernelName;
       Private.updateKernels(this.kernelDropdownNode,
         { specs, sessions, preferredKernel, preference }
@@ -333,10 +323,7 @@ class CreateFromHandler extends Widget {
       this.node.removeChild(this.kernelDropdownNode);
     }
 
-    return manager.listSessions().then(sessions => {
-      this._sessions = sessions;
-      return model.newUntitled({ ext, type });
-    }).then((contents: Contents.IModel) => {
+    return model.newUntitled({ ext, type }).then(contents => {
       this.inputNode.value = contents.name;
       this._orig = contents;
     });
@@ -372,7 +359,6 @@ class CreateFromHandler extends Widget {
   private _widgetName: string;
   private _orig: Contents.IModel = null;
   private _manager: DocumentManager;
-  private _sessions: Session.IModel[] = [];
 }
 
 
@@ -383,11 +369,10 @@ class CreateNewHandler extends Widget {
   /**
    * Construct a new "create new" dialog.
    */
-  constructor(model: FileBrowserModel, manager: DocumentManager, sessions: Session.IModel[]) {
+  constructor(model: FileBrowserModel, manager: DocumentManager) {
     super({ node: Private.createCreateNewNode() });
     this._model = model;
     this._manager = manager;
-    this._sessions = sessions;
 
     // Create a file name based on the current time.
     let time = new Date();
@@ -418,7 +403,6 @@ class CreateNewHandler extends Widget {
    */
   dispose(): void {
     this._model = null;
-    this._sessions = null;
     this._manager = null;
     super.dispose();
   }
@@ -556,9 +540,10 @@ class CreateNewHandler extends Widget {
   protected widgetDropdownChanged(): void {
     let ext = this.ext;
     let widgetName = this.widgetDropdown.value;
-    let preference = this._manager.registry.getKernelPreference(ext, widgetName);
-    let specs = this._manager.kernelspecs;
-    let sessions = this._sessions;
+    let manager = this._manager;
+    let preference = manager.registry.getKernelPreference(ext, widgetName);
+    let specs = manager.kernelspecs;
+    let sessions = manager.sessions();
     Private.updateKernels(this.kernelDropdownNode,
       { preference, sessions, specs }
     );
@@ -566,7 +551,6 @@ class CreateNewHandler extends Widget {
 
   private _model: FileBrowserModel = null;
   private _manager: DocumentManager = null;
-  private _sessions: Session.IModel[] = null;
   private _sentinal = 'UNKNOWN_EXTENSION';
   private _prevExt = '';
   private _extensions: string[] = [];
@@ -664,7 +648,7 @@ namespace Private {
     /**
      * The running sessions.
      */
-    sessions: Session.IModel[];
+    sessions: IterableOrArrayLike<Session.IModel>;
 
     /**
      * The preferred kernel name.

+ 34 - 52
src/filebrowser/model.ts

@@ -6,7 +6,7 @@ import {
 } from '@jupyterlab/services';
 
 import {
-  IIterator, each
+  IterableOrArrayLike, IIterator, each
 } from 'phosphor/lib/algorithm/iteration';
 
 import {
@@ -46,6 +46,7 @@ class FileBrowserModel implements IDisposable, IPathTracker {
     this._manager = options.manager;
     this._model = { path: '', name: '/', type: 'directory' };
     this.cd();
+    this._manager.contents.fileChanged.connect(this._onFileChanged, this);
     this._manager.sessions.runningChanged.connect(this._onRunningChanged, this);
   }
 
@@ -55,14 +56,19 @@ class FileBrowserModel implements IDisposable, IPathTracker {
   pathChanged: ISignal<this, IChangedArgs<string>>;
 
   /**
-   * Get the refreshed signal.
+   * A signal emitted when the directory listing is refreshed.
    */
   refreshed: ISignal<this, void>;
 
+  /**
+   * A signal emitted when the running sessions in the directory changes.
+   */
+  sessionsChanged: ISignal<this, void>;
+
   /**
    * Get the file path changed signal.
    */
-  fileChanged: ISignal<this, IChangedArgs<Contents.IModel>>;
+  fileChanged: ISignal<this, Contents.IChangedArgs>;
 
   /**
    * Get the current path.
@@ -145,12 +151,6 @@ class FileBrowserModel implements IDisposable, IPathTracker {
     this._pending = manager.contents.get(newValue, options).then(contents => {
       this._handleContents(contents);
       this._pendingPath = null;
-      return manager.sessions.listRunning();
-    }).then(models => {
-      if (this.isDisposed) {
-        return;
-      }
-      this._onRunningChanged(manager.sessions, models);
       if (oldValue !== newValue) {
         this.pathChanged.emit({
           name: 'path',
@@ -158,6 +158,7 @@ class FileBrowserModel implements IDisposable, IPathTracker {
           newValue
         });
       }
+      this._onRunningChanged(manager.sessions, manager.sessions.running());
       this.refreshed.emit(void 0);
     });
     return this._pending;
@@ -191,14 +192,7 @@ class FileBrowserModel implements IDisposable, IPathTracker {
     let normalizePath = Private.normalizePath;
     fromFile = normalizePath(this._model.path, fromFile);
     toDir = normalizePath(this._model.path, toDir);
-    return this._manager.contents.copy(fromFile, toDir).then(contents => {
-      this.fileChanged.emit({
-        name: 'file',
-        oldValue: void 0,
-        newValue: contents
-      });
-      return contents;
-    });
+    return this._manager.contents.copy(fromFile, toDir);
   }
 
   /**
@@ -211,13 +205,7 @@ class FileBrowserModel implements IDisposable, IPathTracker {
   deleteFile(path: string): Promise<void> {
     let normalizePath = Private.normalizePath;
     path = normalizePath(this._model.path, path);
-    return this._manager.contents.delete(path).then(() => {
-      this.fileChanged.emit({
-        name: 'file',
-        oldValue: { path: path },
-        newValue: void 0
-      });
-    });
+    return this._manager.contents.delete(path);
   }
 
   /**
@@ -248,16 +236,7 @@ class FileBrowserModel implements IDisposable, IPathTracker {
       options.ext = options.ext || '.txt';
     }
     options.path = options.path || this._model.path;
-
-    let promise = this._manager.contents.newUntitled(options);
-    return promise.then((contents: Contents.IModel) => {
-      this.fileChanged.emit({
-        name: 'file',
-        oldValue: void 0,
-        newValue: contents
-      });
-      return contents;
-    });
+    return this._manager.contents.newUntitled(options);
   }
 
   /**
@@ -274,15 +253,7 @@ class FileBrowserModel implements IDisposable, IPathTracker {
     path = Private.normalizePath(this._model.path, path);
     newPath = Private.normalizePath(this._model.path, newPath);
 
-    let promise = this._manager.contents.rename(path, newPath);
-    return promise.then((contents: Contents.IModel) => {
-      this.fileChanged.emit({
-        name: 'file',
-        oldValue: {type: contents.type , path: path},
-        newValue: contents
-      });
-      return contents;
-    });
+    return this._manager.contents.rename(path, newPath);
   }
 
   /**
@@ -361,15 +332,9 @@ class FileBrowserModel implements IDisposable, IPathTracker {
           content: Private.getContent(reader)
         };
 
-        let promise = this._manager.contents.save(path, model);
-        promise.then((contents: Contents.IModel) => {
-          this.fileChanged.emit({
-            name: 'file',
-            oldValue: void 0,
-            newValue: contents
-          });
+        this._manager.contents.save(path, model).then(contents => {
           resolve(contents);
-        });
+        }).catch(reject);
       };
 
       reader.onerror = (event: Event) => {
@@ -405,7 +370,7 @@ class FileBrowserModel implements IDisposable, IPathTracker {
   /**
    * Handle a change to the running sessions.
    */
-  private _onRunningChanged(sender: Session.IManager, models: Session.IModel[]): void {
+  private _onRunningChanged(sender: Session.IManager, models: IterableOrArrayLike<Session.IModel>): void {
     this._sessions.clear();
     each(models, model => {
       if (this._paths.has(model.notebook.path)) {
@@ -415,6 +380,23 @@ class FileBrowserModel implements IDisposable, IPathTracker {
     this.refreshed.emit(void 0);
   }
 
+  /**
+   * Handle a change on the contents manager.
+   */
+  private _onFileChanged(sender: Contents.IManager, change: Contents.IChangedArgs): void {
+    let path = this._model.path;
+    let value = change.oldValue;
+    if (value && value.path && ContentsManager.dirname(value.path) === path) {
+      this.fileChanged.emit(change);
+      return;
+    }
+    value = change.newValue;
+    if (value && value.path && ContentsManager.dirname(value.path) === path) {
+      this.fileChanged.emit(change);
+      return;
+    }
+  }
+
   private _maxUploadSizeMb = 15;
   private _manager: ServiceManager.IManager = null;
   private _sessions = new Vector<Session.IModel>();

+ 1 - 1
src/inspector/handler.ts

@@ -140,7 +140,7 @@ class InspectionHandler implements IDisposable, Inspector.IInspectable {
     };
     let pending = ++this._pending;
 
-    this._kernel.inspect(contents).then(msg => {
+    this._kernel.requestInspect(contents).then(msg => {
       let value = msg.content;
 
       // If handler has been disposed, bail.

+ 5 - 9
src/notebook/notebook/panel.ts

@@ -320,15 +320,11 @@ class NotebookPanel extends Widget {
     if (!this.model || !kernel) {
       return;
     }
-    if (kernel.info) {
-      this._updateLanguage(kernel.info.language_info);
-    } else {
-      kernel.kernelInfo().then(msg => {
-        if (this.model) {
-          this._updateLanguage(msg.content.language_info);
-        }
-      });
-    }
+    kernel.info().then(msg => {
+      if (this.model) {
+        this._updateLanguage(msg.language_info);
+      }
+    });
     this._updateSpec(kernel);
   }
 

+ 1 - 1
src/notebook/output-area/model.ts

@@ -191,7 +191,7 @@ class OutputAreaModel implements IDisposable {
     };
     this.clear();
     return new Promise<KernelMessage.IExecuteReplyMsg>((resolve, reject) => {
-      let future = kernel.execute(content);
+      let future = kernel.requestExecute(content);
       // Handle published messages.
       future.onIOPub = (msg: KernelMessage.IIOPubMessage) => {
         let msgType = msg.header.msg_type;

+ 0 - 27
src/running/index.css

@@ -29,20 +29,6 @@
 }
 
 
-.jp-RunningSessions-headerRefresh {
-  flex: 1 1 auto;
-  max-width: 100px;
-  padding: 4px 6px;
-  background: var(--jp-layout-color1);
-  color: var(--jp-ui-font-color1);
-  border: none;
-  font-size: var(--jp-ui-font-size1);
-  outline: 0;
-  padding-top: 8px;
-  padding-bottom: 8px;
-}
-
-
 .jp-RunningSessions-section {
   display: flex;
   flex: 0 1 auto;
@@ -50,19 +36,6 @@
 }
 
 
-.jp-RunningSessions-headerRefresh:before {
-  font-family: FontAwesome;
-  content: '\f021'; /* refresh */
-  font-size: var(--jp-ui-icon-font-size);
-}
-
-
-.jp-RunningSessions-headerRefresh:hover {
-  background: var(--jp-layout-color2);
-  z-index: 1; /* raise overlapping border */
-}
-
-
 .jp-RunningSessions-sectionHeader {
   flex: 0 0 auto;
   margin: 4px 0px;

+ 43 - 72
src/running/index.ts

@@ -5,6 +5,14 @@ import {
   ServiceManager, Session, TerminalSession
 } from '@jupyterlab/services';
 
+import {
+  IterableOrArrayLike, each, enumerate
+} from 'phosphor/lib/algorithm/iteration';
+
+import {
+  Vector
+} from 'phosphor/lib/collections/vector';
+
 import {
   Message
 } from 'phosphor/lib/core/messaging';
@@ -36,11 +44,6 @@ const RUNNING_CLASS = 'jp-RunningSessions';
  */
 const HEADER_CLASS = 'jp-RunningSessions-header';
 
-/**
- * The class name added to a running widget header refresh button.
- */
-const REFRESH_CLASS = 'jp-RunningSessions-headerRefresh';
-
 /**
  * The class name added to the running terminal sessions section.
  */
@@ -111,11 +114,6 @@ const FILE_ICON_CLASS = 'jp-mod-file';
  */
 const TERMINAL_ICON_CLASS = 'jp-mod-terminal';
 
-/**
- * The duration of auto-refresh in ms.
- */
-const REFRESH_DURATION = 10000;
-
 /**
  * A regex for console names.
  */
@@ -163,8 +161,14 @@ class RunningSessions extends Widget {
     sessionContainer.appendChild(sessionList);
     sessionNode.appendChild(sessionContainer);
 
-    manager.terminals.runningChanged.connect(this._onTerminalsChanged, this);
-    manager.sessions.runningChanged.connect(this._onSessionsChanged, this);
+    let terminals = manager.terminals;
+    let sessions = manager.sessions;
+
+    terminals.runningChanged.connect(this._onTerminalsChanged, this);
+    sessions.runningChanged.connect(this._onSessionsChanged, this);
+
+    this._onTerminalsChanged(terminals, terminals.running());
+    this._onSessionsChanged(sessions, sessions.running());
   }
 
   /**
@@ -205,29 +209,11 @@ class RunningSessions extends Widget {
       return;
     }
     this._manager = null;
-    this._runningSessions = null;
-    this._runningTerminals = null;
+    this._runningSessions.clear();
+    this._runningTerminals.clear();
     this._renderer = null;
   }
 
-  /**
-   * Refresh the widget.
-   */
-  refresh(): Promise<void> {
-    let terminals = this._manager.terminals;
-    let sessions = this._manager.sessions;
-    clearTimeout(this._refreshId);
-    return terminals.listRunning().then(running => {
-      this._onTerminalsChanged(terminals, running);
-      return sessions.listRunning();
-    }).then(running => {
-      this._onSessionsChanged(sessions, running);
-      this._refreshId = setTimeout(() => {
-        this.refresh();
-      }, REFRESH_DURATION);
-    });
-  }
-
   /**
    * Handle the DOM events for the widget.
    *
@@ -249,7 +235,6 @@ class RunningSessions extends Widget {
    */
   protected onAfterAttach(msg: Message): void {
     this.node.addEventListener('click', this);
-    this.refresh();
   }
 
   /**
@@ -292,16 +277,15 @@ class RunningSessions extends Widget {
     }
 
     // Populate the nodes.
-    for (let i = 0; i < this._runningTerminals.length; i++) {
-      let node = termList.children[i] as HTMLLIElement;
-      renderer.updateTerminalNode(node, this._runningTerminals[i]);
-    }
-    for (let i = 0; i < this._runningSessions.length; i++) {
-      let node = sessionList.children[i] as HTMLLIElement;
-      let model = this._runningSessions[i];
-      let kernelName = kernelspecs[model.kernel.name].display_name;
-      renderer.updateSessionNode(node, model, kernelName);
-    }
+    each(enumerate(this._runningTerminals), ([index, value]) => {
+      let node = termList.children[index] as HTMLLIElement;
+      renderer.updateTerminalNode(node, value);
+    });
+    each(enumerate(this._runningSessions), ([index, value]) => {
+      let node = sessionList.children[index] as HTMLLIElement;
+      let kernelName = kernelspecs[value.kernel.name].display_name;
+      renderer.updateSessionNode(node, value, kernelName);
+    });
   }
 
   /**
@@ -316,27 +300,18 @@ class RunningSessions extends Widget {
     let termList = findElement(termSection, LIST_CLASS);
     let sessionSection = findElement(this.node, SESSIONS_CLASS);
     let sessionList = findElement(sessionSection, LIST_CLASS);
-    let refresh = findElement(this.node, REFRESH_CLASS);
     let renderer = this._renderer;
     let clientX = event.clientX;
     let clientY = event.clientY;
 
-    // Check for a refresh.
-    if (hitTest(refresh, clientX, clientY)) {
-      this.refresh();
-      return;
-    }
-
     // Check for a terminal item click.
     let index = hitTestNodes(termList.children, clientX, clientY);
     if (index !== -1) {
       let node = termList.children[index] as HTMLLIElement;
       let shutdown = renderer.getTerminalShutdown(node);
-      let model = this._runningTerminals[index];
+      let model = this._runningTerminals.at(index);
       if (hitTest(shutdown, clientX, clientY)) {
-        this._manager.terminals.shutdown(model.name).then(() => {
-          this.refresh();
-        });
+        this._manager.terminals.shutdown(model.name);
         return;
       }
       this.terminalOpenRequested.emit(model);
@@ -347,11 +322,9 @@ class RunningSessions extends Widget {
     if (index !== -1) {
       let node = sessionList.children[index] as HTMLLIElement;
       let shutdown = renderer.getSessionShutdown(node);
-      let model = this._runningSessions[index];
+      let model = this._runningSessions.at(index);
       if (hitTest(shutdown, clientX, clientY)) {
-        this._manager.sessions.shutdown(model.id).then(() => {
-          this.refresh();
-        });
+        this._manager.sessions.shutdown(model.id);
         return;
       }
       this.sessionOpenRequested.emit(model);
@@ -361,31 +334,33 @@ class RunningSessions extends Widget {
   /**
    * Handle a change to the running sessions.
    */
-  private _onSessionsChanged(sender: Session.IManager, models: Session.IModel[]): void {
+  private _onSessionsChanged(sender: Session.IManager, models: IterableOrArrayLike<Session.IModel>): void {
     // Strip out non-file backed sessions.
-    this._runningSessions = [];
-    for (let session of models) {
+    this._runningSessions.clear();
+    each(models, session => {
       let name = session.notebook.path.split('/').pop();
       if (name.indexOf('.') !== -1 || CONSOLE_REGEX.test(name)) {
-        this._runningSessions.push(session);
+        this._runningSessions.pushBack(session);
       }
-    }
+    });
     this.update();
   }
 
   /**
    * Handle a change to the running terminals.
    */
-  private _onTerminalsChanged(sender: TerminalSession.IManager, models: TerminalSession.IModel[]): void {
-    this._runningTerminals = models;
+  private _onTerminalsChanged(sender: TerminalSession.IManager, models: IterableOrArrayLike<TerminalSession.IModel>): void {
+    this._runningTerminals.clear();
+    each(models, session => {
+      this._runningTerminals.pushBack(session);
+    });
     this.update();
   }
 
   private _manager: ServiceManager.IManager = null;
   private _renderer: RunningSessions.IRenderer = null;
-  private _runningSessions: Session.IModel[] = [];
-  private _runningTerminals: TerminalSession.IModel[] = [];
-  private _refreshId = -1;
+  private _runningSessions = new Vector<Session.IModel>();
+  private _runningTerminals = new Vector<TerminalSession.IModel>();
 }
 
 
@@ -538,10 +513,6 @@ namespace RunningSessions {
       let sessions = document.createElement('div');
       sessions.className = `${SECTION_CLASS} ${SESSIONS_CLASS}`;
 
-      let refresh = document.createElement('button');
-      refresh.className = REFRESH_CLASS;
-      header.appendChild(refresh);
-
       node.appendChild(header);
       node.appendChild(terminals);
       node.appendChild(sessions);

+ 2 - 2
src/services/plugin.ts

@@ -21,7 +21,7 @@ export
 const servicesProvider: JupyterLabPlugin<IServiceManager> = {
   id: 'jupyter.services.services',
   provides: IServiceManager,
-  activate: (): Promise<IServiceManager> => {
-    return ServiceManager.create() as Promise<IServiceManager>;
+  activate: (): IServiceManager => {
+    return new ServiceManager();
   }
 };

+ 12 - 6
src/terminal/plugin.ts

@@ -1,6 +1,10 @@
 // Copyright (c) Jupyter Development Team.
 // Distributed under the terms of the Modified BSD License.
 
+import {
+  TerminalSession
+} from '@jupyterlab/services';
+
 import {
   InstanceTracker
 } from '../common/instancetracker';
@@ -86,11 +90,13 @@ function activateTerminal(app: JupyterLab, services: IServiceManager, mainMenu:
       term.title.icon = `${LANDSCAPE_ICON_CLASS} ${TERMINAL_ICON_CLASS}`;
       app.shell.addToMainArea(term);
       tracker.add(term);
-      services.terminals.create({ name }).then(session => {
-        term.session = session;
-        // Trigger an update of the running kernels.
-        services.terminals.listRunning();
-      });
+      let promise: Promise<TerminalSession.ISession>;
+      if (name) {
+        promise = services.terminals.connectTo(name);
+      } else {
+        promise = services.terminals.startNew();
+      }
+      promise.then(session => { term.session = session; });
     }
   });
   commands.addCommand(increaseTerminalFontSize, {
@@ -132,7 +138,7 @@ function activateTerminal(app: JupyterLab, services: IServiceManager, mainMenu:
     execute: args => {
       let name = args['name'] as string;
       // Check for a running terminal with the given name.
-      let widget = tracker.find(widget => widget.session.name === name);
+      let widget = tracker.find(value => value.session.name === name);
       if (widget) {
         app.shell.activateMain(widget.id);
       } else {

+ 5 - 9
src/toolbar/kernel.ts

@@ -123,15 +123,11 @@ function updateKernelNameItem(widget: Widget, kernel: Kernel.IKernel): void {
   if (!kernel) {
     return;
   }
-  if (kernel.spec) {
-    widget.node.textContent = kernel.spec.display_name;
-  } else {
-    kernel.getSpec().then(spec => {
-      if (!widget.isDisposed) {
-        widget.node.textContent = kernel.spec.display_name;
-      }
-    });
-  }
+  kernel.spec().then(spec => {
+    if (!widget.isDisposed) {
+      widget.node.textContent = spec.display_name;
+    }
+  });
 }