فهرست منبع

Add notebook execution and make example functional

Steven Silvester 9 سال پیش
والد
کامیت
6499b8afbe
6فایلهای تغییر یافته به همراه229 افزوده شده و 89 حذف شده
  1. 94 12
      example/index.css
  2. 1 0
      example/index.html
  3. 1 0
      example/package.json
  4. 16 68
      example/src/index.ts
  5. 97 9
      src/notebook/notebook/model.ts
  6. 20 0
      src/notebook/notebook/serialize.ts

+ 94 - 12
example/index.css

@@ -1,26 +1,108 @@
-.jp-Cell {
-	border: 1px solid gray;
+.p-Widget.jp-NotebookWidget {
+  min-width: 50px;
+  min-height: 50px;
 }
 
-.jp-selected-cell {
-    border: 1px solid #ababab;
+
+.p-Widget.jp-NotebookContainer {
+  overflow: auto;
 }
 
-.jp-Notebook {
-    overflow: auto;
+
+.p-Widget.jp-NotebookWidget-body {
+  padding: 0.5em;
+  font-size: 11px;
 }
 
-.jp-InputAreaWidget .CodeMirror {
-  height: auto;
-  background-color: #f7f7f7;
+
+.jp-InputAreaWidget > .jp-CodeMirrorWidget {
   border: 1px solid #cfcfcf;
+  border-radius: 2px;
+  background: #f7f7f7;
+  line-height: 1.21429em;
+}
+
+
+.jp-MarkdownCell {
+  outline: 0;
+}
+
+
+.jp-OutputArea pre {
+  background: #ffffff;
+  border: none;
+}
+
+
+.jp-Cell.jp-selected-cell {
+  border-color: #66BB6A;
+  border-left-width: 1px;
+  padding-left: 10px;
+  background: linear-gradient(to right, #66BB6A -40px, #66BB6A 5px, transparent 5px, transparent 100%);
 }
 
+
 .jp-Cell {
-  padding: 0.5em;
-  margin: 0.5em;
+  padding-top: 10px;
+  padding-bottom: 10px;
+  padding-left: 10px;
+  padding-right: 10px;
+  border-width: 1px;
+  border-style: solid;
+  border-color: transparent;
 }
 
+
 .jp-MarkdownCell {
   font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
-}
+}
+
+
+
+
+.CodeMirror.cm-s-default {
+  line-height: 1.21429em;
+  /* Changed from 1em to our global default */
+  font-size: 14px;
+  height: auto;
+  /* Changed to auto to autogrow */
+  background: none;
+  /* Changed from white to allow our bg to show through */
+}
+
+
+.CodeMirror-scroll {
+  /*  The CodeMirror docs are a bit fuzzy on if overflow-y should be hidden or visible.*/
+  /*  We have found that if it is visible, vertical scrollbars appear with font size changes.*/
+  overflow-y: hidden;
+  overflow-x: auto;
+}
+
+
+.CodeMirror-lines {
+  /* In CM2, this used to be 0.4em, but in CM3 it went to 4px. We need the em value because */
+  /* we have set a different line-height and want this to scale with that. */
+  padding: 0.4em;
+}
+
+
+.CodeMirror-linenumber {
+  padding: 0 8px 0 4px;
+}
+
+
+
+.CodeMirror-gutters {
+  border-bottom-left-radius: 2px;
+  border-top-left-radius: 2px;
+}
+
+
+.CodeMirror pre {
+  /* In CM3 this went to 4px from 0 in CM2. We need the 0 value because of how we size */
+  /* .CodeMirror-lines */
+  padding: 0;
+  border: 0;
+  border-radius: 0;
+}
+

+ 1 - 0
example/index.html

@@ -2,6 +2,7 @@
 <html lang="en">
   <head>
     <title>Notebook Demo</title>
+    <link rel="stylesheet" type="text/css" href="index.css">
   </head>
   <body>
     <script id='jupyter-config-data' type="application/json">{ "baseUrl": "{{base_url}}" }</script>

+ 1 - 0
example/package.json

@@ -9,6 +9,7 @@
     "phosphor-widget": "^1.0.0-rc.1"
   },
   "scripts": {
+    "build": "cd .. && npm run build:example",
     "postinstall": "npm dedupe"
   }
 }

+ 16 - 68
example/src/index.ts

@@ -14,7 +14,7 @@ import {
 } from 'jupyter-js-services';
 
 import {
-  getConfigOption
+  getBaseUrl
 } from 'jupyter-js-utils';
 
 import {
@@ -27,41 +27,18 @@ import {
 
 
 // jupyter notebook --NotebookApp.allow_origin=* --port 8890
-let SERVER_URL=getConfigOption('baseUrl');
+let SERVER_URL = getBaseUrl();
 let NOTEBOOK = 'test.ipynb';
 
 function bindings(nbModel: NotebookModel) {
-  let bindings: IKeyBinding[] = [
-  {
-    selector: '.jp-InputAreaWidget .CodeMirror',
+  let bindings: IKeyBinding[] = [{
+    selector: '.jp-nbCell',
     sequence: ["Shift Enter"],
     handler: () => {
-      if (nbModel.selectedCellIndex !== void 0) {
-        let cell = nbModel.cells.get(nbModel.selectedCellIndex);
-        if (isMarkdownCellModel(cell) && !cell.rendered) {
-          cell.rendered = true;
-        }
-      }
+      nbModel.runSelectedCell();
       return true;
     }
-  },
-  {
-    selector: '*',
-    sequence: ["ArrowDown"],
-    handler: () => {
-      nbModel.selectNextCell();
-      return true;
-    }
-  },
-  {
-    selector: '*',
-    sequence: ["ArrowUp"],
-    handler: () => {
-      nbModel.selectPreviousCell();
-      return true;
-    }
-  },
-  ];
+  }];
   return bindings;
 }
 
@@ -78,8 +55,12 @@ function main(): void {
   // and make that a separate example.
 
   let contents = new ContentsManager(SERVER_URL);
-  contents.get(NOTEBOOK, {}).then((data) => {
-    let nbdata: NBData = makedata(data);
+  contents.get(NOTEBOOK, {}).then(data => {
+    let nbdata = {
+      content: data.content,
+      name: data.name,
+      path: data.path
+    }
     let nbModel = new NotebookModel();
     populateNotebookModel(nbModel, nbdata);
     let nbWidget = new NotebookWidget(nbModel);
@@ -87,47 +68,14 @@ function main(): void {
     nbWidget.attach(document.body);
 
     // start session
-    /*
     startNewSession({
       notebookPath: NOTEBOOK,
       kernelName: nbdata.content.metadata.kernelspec.name,
       baseUrl: SERVER_URL
-    }).then((session) => {
-
-      // when shift-enter is pressed, the notebook widget emits a signal saying 'execute this'
-      // along with the cell's Model
-
-
-      // when we get a session, we hook up a handler to the signal
-      // with a function that will:
-      //   - add the cell to the 'pending execution' notebook Model list.
-      //   - clear the cell's output area right away?
-      //   - execute the cell's text, take the cell out of 'pending execution' and put it in 'executing'
-      //   - hook up an iopub handler to the kernel future returned that will modify the cell's output area Model
-      //   - when the kernel future is done, ask the notebook to
-
-      // should the 'cell executing' index be a notebook-level property, or a cell-level property?  Probably notebook-level
-    })
-    */
-  })
-
-/**
- * TODO
- * 1. Make cells executable
- *   - [ ] start kernel using jupyter-js-services
- *   - [ ] shift-enter on code cells executes
- *     - [ ] get text from cell
- *     - [ ] form execute_request message
- *     - [ ] set up reply handler which modifies the cell's output area
- */
-}
-
-function makedata(a: IContentsModel): NBData {
-  return {
-    content: a.content,
-    name: a.name,
-    path: a.path
-  }
+    }).then(session => {
+      nbModel.session = session;
+    });
+  });
 }
 
 main();

+ 97 - 9
src/notebook/notebook/model.ts

@@ -1,25 +1,30 @@
+
 import {
-  ICellModel,
-  ICodeCellModel, CodeCellModel,
-  IMarkdownCellModel, MarkdownCellModel,
-  IRawCellModel
-} from '../cells';
+  INotebookSession
+} from 'jupyter-js-services';
 
 import {
   IObservableList, ObservableList, ListChangeType, IListChangedArgs
 } from 'phosphor-observablelist';
 
 import {
-  Widget
-} from 'phosphor-widget';
+  IChangedArgs, Property
+} from 'phosphor-properties';
 
 import {
   ISignal, Signal
 } from 'phosphor-signaling';
 
 import {
-  IChangedArgs, Property
-} from 'phosphor-properties';
+  Widget
+} from 'phosphor-widget';
+
+import {
+  ICellModel,
+  ICodeCellModel, CodeCellModel,
+  IMarkdownCellModel, MarkdownCellModel,
+  IRawCellModel, isCodeCellModel, isMarkdownCellModel
+} from '../cells';
 
 import {
   EditorModel
@@ -36,6 +41,10 @@ import {
   StreamName
 } from '../output-area';
 
+import {
+  messageToModel
+} from './serialize';
+
 
 /**
  * The interactivity modes for a notebook.
@@ -97,6 +106,11 @@ interface INotebookModel {
    */
   cells: IObservableList<ICellModel>;
 
+  /**
+   * The optional notebook session associated with the model. 
+   */
+  session?: INotebookSession;
+
   /**
    * The currently selected cell.
    *
@@ -148,6 +162,11 @@ interface INotebookModel {
    *   new cell will be intialized with the data from the source.
    */
   //createRawCell(source?: ICellModel): IRawCellModel;
+
+  /**
+   * Run the selected cell, taking the appropriate action.
+   */
+  runSelectedCell(): void;
 }
 
 
@@ -209,6 +228,20 @@ class NotebookModel implements INotebookModel {
     NotebookModelPrivate.modeProperty.set(this, value);
   }
 
+  /**
+   * Get the session for the notebook.
+   */
+  get session() {
+    return NotebookModelPrivate.sessionProperty.get(this);
+  }
+
+  /**
+   * Set the session for the notebook.
+   */
+  set session(value: INotebookSession) {
+    NotebookModelPrivate.sessionProperty.set(this, value);
+  }
+
   /**
    * Get the selected cell index.
    */
@@ -289,6 +322,52 @@ class NotebookModel implements INotebookModel {
     return cell;
   }
 
+  /**
+   * Run the selected cell, taking the appropriate action.
+   */
+  runSelectedCell(): void {
+    let cell = this.cells.get(this.selectedCellIndex);
+    if (!cell) {
+      return;
+    }
+    if (isMarkdownCellModel(cell)) {
+      cell.rendered = true;
+    } else if (isCodeCellModel(cell)) {
+      this.executeCell(cell as CodeCellModel);
+    }
+    if (this.selectedCellIndex === this.cells.length - 1) {
+      let cell = this.createCodeCell();
+      this.cells.add(cell);
+    }
+    this.selectNextCell();
+  }
+
+  /**
+   * Execute the current cell. 
+   */
+  protected executeCell(cell: CodeCellModel): void {
+    let session = this.session;
+    if (!session) {
+      return;
+    }
+    let exRequest = {
+      code: cell.input.textEditor.text,
+      silent: false,
+      store_history: true,
+      stop_on_error: true,
+      allow_stdin: true
+    };
+    let output = cell.output;
+    let ex = this.session.kernel.execute(exRequest);
+    output.clear(false);
+    ex.onIOPub = (msg => {
+      let model = messageToModel(msg);
+      if (model !== void 0) {
+        output.add(model)
+      }
+    });
+  }
+
   /**
    * Handle a change in the cells list.
    */
@@ -341,6 +420,15 @@ namespace NotebookModelPrivate {
     notify: stateChangedSignal,
   });
 
+  /**
+  * A property descriptor which holds the session of the notebook.
+  */
+  export
+  const sessionProperty = new Property<NotebookModel, INotebookSession>({
+    name: 'session',
+    notify: stateChangedSignal,
+  });
+
   /**
   * A property descriptor for the selected cell index.
   */

+ 20 - 0
src/notebook/notebook/serialize.ts

@@ -2,6 +2,10 @@
 // Distributed under the terms of the Modified BSD License.
 'use strict';
 
+import {
+  IKernelMessage
+} from 'jupyter-js-services';
+
 import {
   NotebookModel, INotebookModel
 } from './model';
@@ -109,3 +113,19 @@ function buildOutputModel(out: Output): OutputModel {
     return outmodel;
   }
 }
+
+
+/**
+ * Convert a kernel message to an output model.
+ */
+export
+function messageToModel(msg: IKernelMessage) {
+  let m: Output = msg.content;
+  let type = msg.header.msg_type;
+  if (type === 'execute_result') {
+    m.output_type = 'display_data';
+  } else {
+    m.output_type = type;
+  }
+  return buildOutputModel(m);
+}