plugin.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import {
  4. ServiceManager
  5. } from 'jupyter-js-services';
  6. import {
  7. Application
  8. } from 'phosphide/lib/core/application';
  9. import {
  10. MimeData as IClipboard
  11. } from 'phosphor-dragdrop';
  12. import {
  13. Widget
  14. } from 'phosphor-widget';
  15. import {
  16. DocumentRegistry, restartKernel, selectKernelForContext, IWidgetFactoryOptions
  17. } from '../docregistry';
  18. import {
  19. RenderMime
  20. } from '../rendermime';
  21. import {
  22. WidgetTracker
  23. } from '../widgettracker';
  24. import {
  25. NotebookPanel, NotebookModelFactory, NotebookWidgetFactory, NotebookActions
  26. } from './index';
  27. /**
  28. * The class name for all main area portrait tab icons.
  29. */
  30. const PORTRAIT_ICON_CLASS = 'jp-MainAreaPortraitIcon';
  31. /**
  32. * The class name for the notebook icon from the default theme.
  33. */
  34. const NOTEBOOK_ICON_CLASS = 'jp-ImageNotebook';
  35. /**
  36. * The map of command ids used by the notebook.
  37. */
  38. const cmdIds = {
  39. interrupt: 'notebook:interrupt-kernel',
  40. restart: 'notebook:restart-kernel',
  41. restartClear: 'notebook:restart-clear',
  42. restartRunAll: 'notebook:restart-runAll',
  43. switchKernel: 'notebook:switch-kernel',
  44. clearAllOutputs: 'notebook:clear-outputs',
  45. run: 'notebook-cells:run',
  46. runAndAdvance: 'notebook-cells:run-and-advance',
  47. runAndInsert: 'notebook-cells:run-and-insert',
  48. runAll: 'notebook:run-all',
  49. toCode: 'notebook-cells:to-code',
  50. toMarkdown: 'notebook-cells:to-markdown',
  51. toRaw: 'notebook-cells:to-raw',
  52. cut: 'notebook-cells:cut',
  53. copy: 'notebook-cells:copy',
  54. paste: 'notebook-cells:paste',
  55. clearOutputs: 'notebook-cells:clear-output',
  56. deleteCell: 'notebook-cells:delete',
  57. insertAbove: 'notebook-cells:insert-above',
  58. insertBelow: 'notebook-cells:insert-below',
  59. selectAbove: 'notebook-cells:select-above',
  60. selectBelow: 'notebook-cells:select-below',
  61. extendAbove: 'notebook-cells:extend-above',
  62. extendBelow: 'notebook-cells:extend-below',
  63. editMode: 'notebook:edit-mode',
  64. merge: 'notebook-cells:merge',
  65. split: 'notebook-cells:split',
  66. commandMode: 'notebook:command-mode',
  67. toggleLines: 'notebook-cells:toggle-line-numbers',
  68. toggleAllLines: 'notebook-cells:toggle-all-line-numbers',
  69. undo: 'notebook-cells:undo',
  70. redo: 'notebook-cells:redo',
  71. markdown1: 'notebook-cells:markdown-header1',
  72. markdown2: 'notebook-cells:markdown-header2',
  73. markdown3: 'notebook-cells:markdown-header3',
  74. markdown4: 'notebook-cells:markdown-header4',
  75. markdown5: 'notebook-cells:markdown-header5',
  76. markdown6: 'notebook-cells:markdown-header6',
  77. };
  78. /**
  79. * A class that tracks notebook widgets.
  80. */
  81. export
  82. class NotebookTracker extends WidgetTracker<NotebookPanel> { }
  83. /**
  84. * The notebook file handler extension.
  85. */
  86. export
  87. const notebookHandlerExtension = {
  88. id: 'jupyter.extensions.notebookHandler',
  89. requires: [DocumentRegistry, ServiceManager, RenderMime, IClipboard],
  90. activate: activateNotebookHandler
  91. };
  92. /**
  93. * The notebook widget tracker provider.
  94. */
  95. export
  96. const notebookTrackerProvider = {
  97. id: 'jupyter.plugins.notebookTracker',
  98. provides: NotebookTracker,
  99. resolve: () => {
  100. return Private.notebookTracker;
  101. }
  102. };
  103. /**
  104. * Activate the notebook handler extension.
  105. */
  106. function activateNotebookHandler(app: Application, registry: DocumentRegistry, services: ServiceManager, rendermime: RenderMime<Widget>, clipboard: IClipboard): void {
  107. let widgetFactory = new NotebookWidgetFactory(rendermime, clipboard);
  108. let options: IWidgetFactoryOptions = {
  109. fileExtensions: ['.ipynb'],
  110. displayName: 'Notebook',
  111. modelName: 'notebook',
  112. defaultFor: ['.ipynb'],
  113. preferKernel: true,
  114. canStartKernel: true
  115. };
  116. registry.addModelFactory(new NotebookModelFactory());
  117. registry.addWidgetFactory(widgetFactory, options);
  118. // Add the ability to launch notebooks for each kernel type.
  119. let displayNameMap: { [key: string]: string } = Object.create(null);
  120. let specs = services.kernelspecs;
  121. for (let kernelName in specs.kernelspecs) {
  122. let displayName = specs.kernelspecs[kernelName].spec.display_name;
  123. displayNameMap[displayName] = kernelName;
  124. }
  125. let displayNames = Object.keys(displayNameMap).sort((a, b) => {
  126. return a.localeCompare(b);
  127. });
  128. registry.addFileType({
  129. name: 'Notebook',
  130. extension: '.ipynb',
  131. fileType: 'notebook',
  132. fileFormat: 'json'
  133. });
  134. for (let displayName of displayNames) {
  135. registry.addCreator({
  136. name: `${displayName} Notebook`,
  137. fileType: 'Notebook',
  138. widgetName: 'Notebook',
  139. kernelName: displayNameMap[displayName]
  140. });
  141. }
  142. // Track the current active notebook.
  143. let tracker = Private.notebookTracker;
  144. widgetFactory.widgetCreated.connect((sender, widget) => {
  145. widget.title.icon = `${PORTRAIT_ICON_CLASS} ${NOTEBOOK_ICON_CLASS}`;
  146. tracker.addWidget(widget);
  147. });
  148. app.commands.add([
  149. {
  150. id: cmdIds.runAndAdvance,
  151. handler: () => {
  152. if (tracker.activeWidget) {
  153. let nbWidget = tracker.activeWidget;
  154. NotebookActions.runAndAdvance(nbWidget.content, nbWidget.context.kernel);
  155. }
  156. }
  157. },
  158. {
  159. id: cmdIds.run,
  160. handler: () => {
  161. if (tracker.activeWidget) {
  162. let nbWidget = tracker.activeWidget;
  163. NotebookActions.run(nbWidget.content, nbWidget.context.kernel);
  164. }
  165. }
  166. },
  167. {
  168. id: cmdIds.runAndInsert,
  169. handler: () => {
  170. if (tracker.activeWidget) {
  171. let nbWidget = tracker.activeWidget;
  172. NotebookActions.runAndInsert(nbWidget.content, nbWidget.context.kernel);
  173. }
  174. }
  175. },
  176. {
  177. id: cmdIds.runAll,
  178. handler: () => {
  179. if (tracker.activeWidget) {
  180. let nbWidget = tracker.activeWidget;
  181. NotebookActions.runAll(nbWidget.content, nbWidget.context.kernel);
  182. }
  183. }
  184. },
  185. {
  186. id: cmdIds.restart,
  187. handler: () => {
  188. if (tracker.activeWidget) {
  189. let nbWidget = tracker.activeWidget;
  190. restartKernel(nbWidget.kernel, nbWidget.node);
  191. }
  192. }
  193. },
  194. {
  195. id: cmdIds.restartClear,
  196. handler: () => {
  197. if (tracker.activeWidget) {
  198. let nbWidget = tracker.activeWidget;
  199. let promise = restartKernel(nbWidget.kernel, nbWidget.node);
  200. promise.then(result => {
  201. if (result) {
  202. NotebookActions.clearAllOutputs(nbWidget.content);
  203. }
  204. });
  205. }
  206. }
  207. },
  208. {
  209. id: cmdIds.restartRunAll,
  210. handler: () => {
  211. if (tracker.activeWidget) {
  212. let nbWidget = tracker.activeWidget;
  213. let promise = restartKernel(nbWidget.kernel, nbWidget.node);
  214. promise.then(result => {
  215. NotebookActions.runAll(nbWidget.content, nbWidget.context.kernel);
  216. });
  217. }
  218. }
  219. },
  220. {
  221. id: cmdIds.clearAllOutputs,
  222. handler: () => {
  223. if (tracker.activeWidget) {
  224. let nbWidget = tracker.activeWidget;
  225. NotebookActions.clearAllOutputs(nbWidget.content);
  226. }
  227. }
  228. },
  229. {
  230. id: cmdIds.clearOutputs,
  231. handler: () => {
  232. if (tracker.activeWidget) {
  233. let nbWidget = tracker.activeWidget;
  234. NotebookActions.clearOutputs(nbWidget.content);
  235. }
  236. }
  237. },
  238. {
  239. id: cmdIds.interrupt,
  240. handler: () => {
  241. if (tracker.activeWidget) {
  242. let kernel = tracker.activeWidget.context.kernel;
  243. if (kernel) {
  244. kernel.interrupt();
  245. }
  246. }
  247. }
  248. },
  249. {
  250. id: cmdIds.toCode,
  251. handler: () => {
  252. if (tracker.activeWidget) {
  253. let nbWidget = tracker.activeWidget;
  254. NotebookActions.changeCellType(nbWidget.content, 'code');
  255. }
  256. }
  257. },
  258. {
  259. id: cmdIds.toMarkdown,
  260. handler: () => {
  261. if (tracker.activeWidget) {
  262. let nbWidget = tracker.activeWidget;
  263. NotebookActions.changeCellType(nbWidget.content, 'markdown');
  264. }
  265. }
  266. },
  267. {
  268. id: cmdIds.toRaw,
  269. handler: () => {
  270. if (tracker.activeWidget) {
  271. let nbWidget = tracker.activeWidget;
  272. NotebookActions.changeCellType(nbWidget.content, 'raw');
  273. }
  274. }
  275. },
  276. {
  277. id: cmdIds.cut,
  278. handler: () => {
  279. if (tracker.activeWidget) {
  280. let nbWidget = tracker.activeWidget;
  281. NotebookActions.cut(nbWidget.content, nbWidget.clipboard);
  282. }
  283. }
  284. },
  285. {
  286. id: cmdIds.copy,
  287. handler: () => {
  288. if (tracker.activeWidget) {
  289. let nbWidget = tracker.activeWidget;
  290. NotebookActions.copy(nbWidget.content, nbWidget.clipboard);
  291. }
  292. }
  293. },
  294. {
  295. id: cmdIds.paste,
  296. handler: () => {
  297. if (tracker.activeWidget) {
  298. let nbWidget = tracker.activeWidget;
  299. NotebookActions.paste(nbWidget.content, nbWidget.clipboard);
  300. }
  301. }
  302. },
  303. {
  304. id: cmdIds.deleteCell,
  305. handler: () => {
  306. if (tracker.activeWidget) {
  307. let nbWidget = tracker.activeWidget;
  308. NotebookActions.deleteCells(nbWidget.content);
  309. }
  310. }
  311. },
  312. {
  313. id: cmdIds.split,
  314. handler: () => {
  315. if (tracker.activeWidget) {
  316. let nbWidget = tracker.activeWidget;
  317. NotebookActions.splitCell(nbWidget.content);
  318. }
  319. }
  320. },
  321. {
  322. id: cmdIds.merge,
  323. handler: () => {
  324. if (tracker.activeWidget) {
  325. let nbWidget = tracker.activeWidget;
  326. NotebookActions.mergeCells(nbWidget.content);
  327. }
  328. }
  329. },
  330. {
  331. id: cmdIds.insertAbove,
  332. handler: () => {
  333. if (tracker.activeWidget) {
  334. let nbWidget = tracker.activeWidget;
  335. NotebookActions.insertAbove(nbWidget.content);
  336. }
  337. }
  338. },
  339. {
  340. id: cmdIds.insertBelow,
  341. handler: () => {
  342. if (tracker.activeWidget) {
  343. let nbWidget = tracker.activeWidget;
  344. NotebookActions.insertBelow(nbWidget.content);
  345. }
  346. }
  347. },
  348. {
  349. id: cmdIds.selectAbove,
  350. handler: () => {
  351. if (tracker.activeWidget) {
  352. let nbWidget = tracker.activeWidget;
  353. NotebookActions.selectAbove(nbWidget.content);
  354. }
  355. }
  356. },
  357. {
  358. id: cmdIds.selectBelow,
  359. handler: () => {
  360. if (tracker.activeWidget) {
  361. let nbWidget = tracker.activeWidget;
  362. NotebookActions.selectBelow(nbWidget.content);
  363. }
  364. }
  365. },
  366. {
  367. id: cmdIds.extendAbove,
  368. handler: () => {
  369. if (tracker.activeWidget) {
  370. let nbWidget = tracker.activeWidget;
  371. NotebookActions.extendSelectionAbove(nbWidget.content);
  372. }
  373. }
  374. },
  375. {
  376. id: cmdIds.extendBelow,
  377. handler: () => {
  378. if (tracker.activeWidget) {
  379. let nbWidget = tracker.activeWidget;
  380. NotebookActions.extendSelectionBelow(nbWidget.content);
  381. }
  382. }
  383. },
  384. {
  385. id: cmdIds.toggleLines,
  386. handler: () => {
  387. if (tracker.activeWidget) {
  388. let nbWidget = tracker.activeWidget;
  389. NotebookActions.toggleLineNumbers(nbWidget.content);
  390. }
  391. }
  392. },
  393. {
  394. id: cmdIds.toggleAllLines,
  395. handler: () => {
  396. if (tracker.activeWidget) {
  397. let nbWidget = tracker.activeWidget;
  398. NotebookActions.toggleAllLineNumbers(nbWidget.content);
  399. }
  400. }
  401. },
  402. {
  403. id: cmdIds.commandMode,
  404. handler: () => {
  405. if (tracker.activeWidget) {
  406. tracker.activeWidget.content.mode = 'command';
  407. }
  408. }
  409. },
  410. {
  411. id: cmdIds.editMode,
  412. handler: () => {
  413. if (tracker.activeWidget) {
  414. tracker.activeWidget.content.mode = 'edit';
  415. }
  416. }
  417. },
  418. {
  419. id: cmdIds.undo,
  420. handler: () => {
  421. if (tracker.activeWidget) {
  422. NotebookActions.undo(tracker.activeWidget.content);
  423. }
  424. }
  425. },
  426. {
  427. id: cmdIds.redo,
  428. handler: () => {
  429. if (tracker.activeWidget) {
  430. NotebookActions.redo(tracker.activeWidget.content);
  431. }
  432. }
  433. },
  434. {
  435. id: cmdIds.switchKernel,
  436. handler: () => {
  437. if (tracker.activeWidget) {
  438. selectKernelForContext(tracker.activeWidget.context, tracker.activeWidget.node);
  439. }
  440. }
  441. },
  442. {
  443. id: cmdIds.markdown1,
  444. handler: () => {
  445. if (tracker.activeWidget) {
  446. NotebookActions.setMarkdownHeader(tracker.activeWidget.content, 1);
  447. }
  448. }
  449. },
  450. {
  451. id: cmdIds.markdown2,
  452. handler: () => {
  453. if (tracker.activeWidget) {
  454. NotebookActions.setMarkdownHeader(tracker.activeWidget.content, 2);
  455. }
  456. }
  457. },
  458. {
  459. id: cmdIds.markdown3,
  460. handler: () => {
  461. if (tracker.activeWidget) {
  462. NotebookActions.setMarkdownHeader(tracker.activeWidget.content, 3);
  463. }
  464. }
  465. },
  466. {
  467. id: cmdIds.markdown4,
  468. handler: () => {
  469. if (tracker.activeWidget) {
  470. NotebookActions.setMarkdownHeader(tracker.activeWidget.content, 4);
  471. }
  472. }
  473. },
  474. {
  475. id: cmdIds.markdown5,
  476. handler: () => {
  477. if (tracker.activeWidget) {
  478. NotebookActions.setMarkdownHeader(tracker.activeWidget.content, 5);
  479. }
  480. }
  481. },
  482. {
  483. id: cmdIds.markdown6,
  484. handler: () => {
  485. if (tracker.activeWidget) {
  486. NotebookActions.setMarkdownHeader(tracker.activeWidget.content, 6);
  487. }
  488. }
  489. }
  490. ]);
  491. app.palette.add([
  492. {
  493. command: cmdIds.run,
  494. category: 'Notebook Cell Operations',
  495. text: 'Run Cell(s)'
  496. },
  497. {
  498. command: cmdIds.runAndAdvance,
  499. category: 'Notebook Cell Operations',
  500. text: 'Run Cell(s) and Advance'
  501. },
  502. {
  503. command: cmdIds.runAndInsert,
  504. category: 'Notebook Cell Operations',
  505. text: 'Run Cell(s) and Insert'
  506. },
  507. {
  508. command: cmdIds.interrupt,
  509. category: 'Notebook Operations',
  510. text: 'Interrupt Kernel'
  511. },
  512. {
  513. command: cmdIds.restart,
  514. category: 'Notebook Operations',
  515. text: 'Restart Kernel'
  516. },
  517. {
  518. command: cmdIds.restartClear,
  519. category: 'Notebook Operations',
  520. text: 'Restart Kernel & Clear Outputs'
  521. },
  522. {
  523. command: cmdIds.restartRunAll,
  524. category: 'Notebook Operations',
  525. text: 'Restart Kernel & Run All'
  526. },
  527. {
  528. command: cmdIds.runAll,
  529. category: 'Notebook Operations',
  530. text: 'Run All Cells'
  531. },
  532. {
  533. command: cmdIds.clearAllOutputs,
  534. category: 'Notebook Operations',
  535. text: 'Clear All Outputs'
  536. },
  537. {
  538. command: cmdIds.clearOutputs,
  539. category: 'Notebook Cell Operations',
  540. text: 'Clear Output(s)'
  541. },
  542. {
  543. command: cmdIds.toCode,
  544. category: 'Notebook Cell Operations',
  545. text: 'Convert to Code'
  546. },
  547. {
  548. command: cmdIds.toMarkdown,
  549. category: 'Notebook Cell Operations',
  550. text: 'Convert to Markdown'
  551. },
  552. {
  553. command: cmdIds.toRaw,
  554. category: 'Notebook Cell Operations',
  555. text: 'Convert to Raw'
  556. },
  557. {
  558. command: cmdIds.cut,
  559. category: 'Notebook Cell Operations',
  560. text: 'Cut Cell(s)'
  561. },
  562. {
  563. command: cmdIds.copy,
  564. category: 'Notebook Cell Operations',
  565. text: 'Copy Cell(s)'
  566. },
  567. {
  568. command: cmdIds.paste,
  569. category: 'Notebook Cell Operations',
  570. text: 'Paste Cell(s)'
  571. },
  572. {
  573. command: cmdIds.deleteCell,
  574. category: 'Notebook Cell Operations',
  575. text: 'Delete Cell(s)'
  576. },
  577. {
  578. command: cmdIds.split,
  579. category: 'Notebook Cell Operations',
  580. text: 'Split Cell'
  581. },
  582. {
  583. command: cmdIds.merge,
  584. category: 'Notebook Cell Operations',
  585. text: 'Merge Selected Cell(s)'
  586. },
  587. {
  588. command: cmdIds.insertAbove,
  589. category: 'Notebook Cell Operations',
  590. text: 'Insert Cell Above'
  591. },
  592. {
  593. command: cmdIds.insertBelow,
  594. category: 'Notebook Cell Operations',
  595. text: 'Insert Cell Below'
  596. },
  597. {
  598. command: cmdIds.selectAbove,
  599. category: 'Notebook Cell Operations',
  600. text: 'Select Cell Above'
  601. },
  602. {
  603. command: cmdIds.selectBelow,
  604. category: 'Notebook Cell Operations',
  605. text: 'Select Cell Below'
  606. },
  607. {
  608. command: cmdIds.extendAbove,
  609. category: 'Notebook Cell Operations',
  610. text: 'Extend Selection Above'
  611. },
  612. {
  613. command: cmdIds.extendBelow,
  614. category: 'Notebook Cell Operations',
  615. text: 'Extend Selection Below'
  616. },
  617. {
  618. command: cmdIds.toggleLines,
  619. category: 'Notebook Cell Operations',
  620. text: 'Toggle Line Numbers'
  621. },
  622. {
  623. command: cmdIds.toggleAllLines,
  624. category: 'Notebook Operations',
  625. text: 'Toggle All Line Numbers'
  626. },
  627. {
  628. command: cmdIds.editMode,
  629. category: 'Notebook Operations',
  630. text: 'To Edit Mode'
  631. },
  632. {
  633. command: cmdIds.commandMode,
  634. category: 'Notebook Operations',
  635. text: 'To Command Mode'
  636. },
  637. {
  638. command: cmdIds.switchKernel,
  639. category: 'Notebook Operations',
  640. text: 'Switch Kernel'
  641. },
  642. {
  643. command: cmdIds.undo,
  644. category: 'Notebook Cell Operations',
  645. text: 'Undo Cell Operation'
  646. },
  647. {
  648. command: cmdIds.redo,
  649. category: 'Notebook Cell Operations',
  650. text: 'Redo Cell Operation'
  651. },
  652. {
  653. command: cmdIds.markdown1,
  654. category: 'Notebook Cell Operations',
  655. text: 'Markdown Header 1'
  656. },
  657. {
  658. command: cmdIds.markdown2,
  659. category: 'Notebook Cell Operations',
  660. text: 'Markdown Header 2'
  661. },
  662. {
  663. command: cmdIds.markdown3,
  664. category: 'Notebook Cell Operations',
  665. text: 'Markdown Header 3'
  666. },
  667. {
  668. command: cmdIds.markdown4,
  669. category: 'Notebook Cell Operations',
  670. text: 'Markdown Header 4'
  671. },
  672. {
  673. command: cmdIds.markdown5,
  674. category: 'Notebook Cell Operations',
  675. text: 'Markdown Header 5'
  676. },
  677. {
  678. command: cmdIds.markdown6,
  679. category: 'Notebook Cell Operations',
  680. text: 'Markdown Header 6'
  681. }
  682. ]);
  683. }
  684. /**
  685. * A namespace for private data.
  686. */
  687. namespace Private {
  688. /**
  689. * A singleton instance of a notebook tracker.
  690. */
  691. export
  692. const notebookTracker = new NotebookTracker();
  693. }