plugin.ts 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. 'use strict';
  4. import {
  5. AbstractFileHandler
  6. } from 'jupyter-js-docmanager';
  7. import {
  8. NotebookWidget, NotebookModel, NBData, populateNotebookModel, buildOutputModel, Output, INotebookModel
  9. } from 'jupyter-js-notebook';
  10. import {
  11. isCodeCellModel, isMarkdownCellModel
  12. } from 'jupyter-js-notebook/lib/cells';
  13. import {
  14. IContentsModel, IContentsManager,
  15. NotebookSessionManager, INotebookSessionManager,
  16. INotebookSession, IKernelMessage
  17. } from 'jupyter-js-services';
  18. import {
  19. ICommandRegistry, ICommandPalette
  20. } from 'phosphide';
  21. import {
  22. Container
  23. } from 'phosphor-di';
  24. import {
  25. Panel
  26. } from 'phosphor-panel';
  27. import {
  28. IChangedArgs, Property
  29. } from 'phosphor-properties';
  30. import {
  31. Widget
  32. } from 'phosphor-widget';
  33. import {
  34. IServicesProvider, IDocumentManager
  35. } from '../index';
  36. import {
  37. WidgetManager
  38. } from './widgetmanager';
  39. let executeCellCommandId = 'notebook:execute-selected-cell';
  40. let renderCellCommandId = 'notebook:render-selected-cell';
  41. let selectNextCellCommandId = 'notebook:select-next-cell';
  42. let selectPreviousCellCommandId = 'notebook:select-previous-cell';
  43. let notebookContainerClass = 'jp-NotebookContainer';
  44. /**
  45. * The class name added to a dirty documents.
  46. */
  47. const DIRTY_CLASS = 'jp-mod-dirty';
  48. /**
  49. * Register the plugin contributions.
  50. *
  51. * @param container - The di container for type registration.
  52. *
  53. * #### Notes
  54. * This is called automatically when the plugin is loaded.
  55. */
  56. export
  57. function resolve(container: Container): Promise<AbstractFileHandler> {
  58. return container.resolve({
  59. requires: [IServicesProvider, IDocumentManager, ICommandRegistry, ICommandPalette],
  60. create: (services: IServicesProvider, manager: IDocumentManager,
  61. registry: ICommandRegistry,
  62. palette: ICommandPalette) => {
  63. let handler = new NotebookFileHandler(
  64. services.contentsManager,
  65. services.notebookSessionManager
  66. );
  67. manager.register(handler);
  68. registry.add([{
  69. id: executeCellCommandId,
  70. handler: () => handler.executeSelectedCell()
  71. }, {
  72. id: renderCellCommandId,
  73. handler: () => handler.renderSelectedCell()
  74. }, {
  75. id: selectNextCellCommandId,
  76. handler: () => handler.selectNextCell()
  77. }, {
  78. id: selectPreviousCellCommandId,
  79. handler: () => handler.selectPreviousCell()
  80. }]);
  81. palette.add([{
  82. id: executeCellCommandId,
  83. category: 'Notebook Operations',
  84. args: void 0,
  85. text: 'Execute current cell',
  86. caption: 'Execute the current cell'
  87. }, {
  88. id: renderCellCommandId,
  89. category: 'Notebook Operations',
  90. args: void 0,
  91. text: 'Render current markdown cell',
  92. caption: 'Render the current markdown cell'
  93. }, {
  94. id: selectNextCellCommandId,
  95. category: 'Notebook Operations',
  96. args: void 0,
  97. text: 'Select next cell',
  98. caption: 'Select next cell'
  99. }, {
  100. id: selectPreviousCellCommandId,
  101. category: 'Notebook Operations',
  102. args: void 0,
  103. text: 'Select previous cell',
  104. caption: 'Select previous cell'
  105. }]);
  106. return handler;
  107. }
  108. });
  109. }
  110. /**
  111. * Convert a kernel message to an output model.
  112. */
  113. function messageToModel(msg: IKernelMessage) {
  114. let m: Output = msg.content;
  115. let type = msg.header.msg_type;
  116. if (type === 'execute_result') {
  117. m.output_type = 'display_data';
  118. } else {
  119. m.output_type = type;
  120. }
  121. return buildOutputModel(m);
  122. }
  123. /**
  124. * Execute the selected cell in a notebook.
  125. */
  126. function executeSelectedCell(model: INotebookModel, session: INotebookSession) {
  127. let cell = model.cells.get(model.selectedCellIndex);
  128. if (isCodeCellModel(cell)) {
  129. let exRequest = {
  130. code: cell.input.textEditor.text,
  131. silent: false,
  132. store_history: true,
  133. stop_on_error: true,
  134. allow_stdin: true
  135. };
  136. let output = cell.output;
  137. console.log(`executing`, exRequest)
  138. let ex = session.kernel.execute(exRequest);
  139. output.clear(false);
  140. ex.onIOPub = (msg => {
  141. let model = messageToModel(msg);
  142. console.log('iopub', msg);
  143. if (model !== void 0) {
  144. output.add(model)
  145. }
  146. });
  147. if (model.selectedCellIndex === model.cells.length - 1) {
  148. let cell = model.createCodeCell();
  149. model.cells.add(cell);
  150. }
  151. model.selectNextCell();
  152. ex.onReply = (msg => {console.log('a', msg)});
  153. ex.onDone = (msg => {console.log('b', msg)});
  154. }
  155. }
  156. /**
  157. * Render the selected cell in a notebook.
  158. */
  159. function renderSelectedCell(model: INotebookModel) {
  160. let cell = model.cells.get(model.selectedCellIndex);
  161. if (isMarkdownCellModel(cell)) {
  162. cell.rendered = true;
  163. }
  164. if (model.selectedCellIndex === model.cells.length - 1) {
  165. let cell = model.createCodeCell();
  166. model.cells.add(cell);
  167. }
  168. model.selectNextCell();
  169. }
  170. /**
  171. * A container which manages a notebook and widgets.
  172. */
  173. class NotebookContainer extends Panel {
  174. /**
  175. * Construct a new NotebookContainer.
  176. */
  177. constructor() {
  178. super();
  179. this._model = new NotebookModel();
  180. this._model.stateChanged.connect(this._onModelChanged, this);
  181. let widgetarea = new Widget();
  182. this._manager = new WidgetManager(widgetarea.node);
  183. let widget = new NotebookWidget(this._model);
  184. this.addChild(widgetarea);
  185. this.addChild(widget);
  186. }
  187. /**
  188. * Get the notebook model used by the widget.
  189. *
  190. * #### Notes
  191. * This is a read-only property.
  192. */
  193. get model(): INotebookModel {
  194. return this._model;
  195. }
  196. /**
  197. * Get the notebook session used by the widget.
  198. *
  199. * #### Notes
  200. * This is a read-only property.
  201. */
  202. get session(): INotebookSession {
  203. return this._session;
  204. }
  205. /**
  206. * Set the session and set up widget handling.
  207. */
  208. setSession(value: INotebookSession) {
  209. this._session = value;
  210. this._session.kernel.registerCommTarget('ipython.widget', (comm, msg) => {
  211. console.log('comm message', msg);
  212. let modelPromise = this._manager.handle_comm_open(comm, msg);
  213. comm.onMsg = (msg) => {
  214. this._manager.handle_comm_open(comm, msg)
  215. // create the widget model and (if needed) the view
  216. console.log('comm widget message', msg);
  217. }
  218. comm.onClose = (msg) => {
  219. console.log('comm widget close', msg);
  220. }
  221. });
  222. }
  223. private _onModelChanged(model: INotebookModel, args: IChangedArgs<INotebookModel>): void {
  224. if (args.name === 'dirty') {
  225. if (args.newValue) {
  226. this.addClass(DIRTY_CLASS);
  227. } else {
  228. this.removeClass(DIRTY_CLASS);
  229. }
  230. }
  231. }
  232. private _manager: WidgetManager = null;
  233. private _model: INotebookModel = null;
  234. private _session: INotebookSession = null;
  235. }
  236. /**
  237. * An implementation of a file handler.
  238. */
  239. export
  240. class NotebookFileHandler extends AbstractFileHandler {
  241. constructor(contents: IContentsManager, session: INotebookSessionManager) {
  242. super(contents);
  243. this.session = session;
  244. }
  245. /**
  246. * Get the list of file extensions supported by the handler.
  247. */
  248. get fileExtensions(): string[] {
  249. return ['.ipynb']
  250. }
  251. /**
  252. * Execute the selected cell on the active widget.
  253. */
  254. executeSelectedCell(): void {
  255. let w = this.activeWidget as NotebookContainer;
  256. if (w) executeSelectedCell(w.model, w.session);
  257. }
  258. /**
  259. * Render the selected cell on the active widget.
  260. */
  261. renderSelectedCell(): void {
  262. let w = this.activeWidget as NotebookContainer;
  263. if (w) renderSelectedCell(w.model);
  264. }
  265. /**
  266. * Select the next cell on the active widget.
  267. */
  268. selectNextCell(): void {
  269. let w = this.activeWidget as NotebookContainer;
  270. if (w) w.model.selectNextCell();
  271. }
  272. /**
  273. * Select the previous cell on the active widget.
  274. */
  275. selectPreviousCell(): void {
  276. let w = this.activeWidget as NotebookContainer;
  277. if (w) w.model.selectPreviousCell();
  278. }
  279. /**
  280. * Get file contents given a contents model.
  281. */
  282. protected getContents(model: IContentsModel): Promise<IContentsModel> {
  283. return this.manager.get(model.path, { type: 'notebook' });
  284. }
  285. /**
  286. * Create the widget from an `IContentsModel`.
  287. */
  288. protected createWidget(contents: IContentsModel): Widget {
  289. let panel = new NotebookContainer();
  290. panel.title.text = contents.name;
  291. panel.addClass(notebookContainerClass);
  292. this.session.startNew({notebookPath: contents.path}).then(s => {
  293. panel.setSession(s);
  294. })
  295. return panel;
  296. }
  297. /**
  298. * Populate the notebook widget with the contents of the notebook.
  299. */
  300. protected setState(widget: Widget, model: IContentsModel): Promise<void> {
  301. let nbData: NBData = {
  302. content: model.content,
  303. name: model.name,
  304. path: model.path
  305. }
  306. let nbWidget: NotebookWidget = ((widget as Panel).childAt(1)) as NotebookWidget;
  307. populateNotebookModel(nbWidget.model, nbData);
  308. if (nbWidget.model.cells.length === 0) {
  309. let cell = nbWidget.model.createCodeCell();
  310. nbWidget.model.cells.add(cell);
  311. }
  312. nbWidget.model.selectedCellIndex = 0;
  313. return Promise.resolve();
  314. }
  315. /**
  316. * Get the current state of the notebook.
  317. */
  318. protected getState(widget: Widget, model: IContentsModel): Promise<IContentsModel> {
  319. return Promise.resolve(void 0);
  320. }
  321. session: INotebookSessionManager;
  322. }