index.ts 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import {
  4. ILayoutRestorer, JupyterLab, JupyterLabPlugin
  5. } from '@jupyterlab/application';
  6. import {
  7. Dialog, ICommandPalette, IMainMenu, showDialog
  8. } from '@jupyterlab/apputils';
  9. import {
  10. IEditorServices
  11. } from '@jupyterlab/codeeditor';
  12. import {
  13. IStateDB, PageConfig, URLExt
  14. } from '@jupyterlab/coreutils';
  15. import {
  16. ILauncher
  17. } from '@jupyterlab/launcher';
  18. import {
  19. CellTools, ICellTools, INotebookTracker, NotebookActions,
  20. NotebookModelFactory, NotebookPanel, NotebookTracker, NotebookWidgetFactory
  21. } from '@jupyterlab/notebook';
  22. import {
  23. ServiceManager
  24. } from '@jupyterlab/services';
  25. import {
  26. ReadonlyJSONObject
  27. } from '@phosphor/coreutils';
  28. import {
  29. Message, MessageLoop
  30. } from '@phosphor/messaging';
  31. import {
  32. Menu, Widget
  33. } from '@phosphor/widgets';
  34. /**
  35. * The command IDs used by the notebook plugin.
  36. */
  37. namespace CommandIDs {
  38. export
  39. const interrupt = 'notebook:interrupt-kernel';
  40. export
  41. const restart = 'notebook:restart-kernel';
  42. export
  43. const restartClear = 'notebook:restart-clear-output';
  44. export
  45. const restartRunAll = 'notebook:restart-run-all';
  46. export
  47. const reconnectToKernel = 'notebook:reconnect-to-kernel';
  48. export
  49. const changeKernel = 'notebook:change-kernel';
  50. export
  51. const createConsole = 'notebook:create-console';
  52. export
  53. const clearAllOutputs = 'notebook:clear-all-cell-outputs';
  54. export
  55. const closeAndShutdown = 'notebook:close-and-shutdown';
  56. export
  57. const trust = 'notebook:trust';
  58. export
  59. const exportToFormat = 'notebook:export-to-format';
  60. export
  61. const run = 'notebook:run-cell';
  62. export
  63. const runAndAdvance = 'notebook:run-cell-and-select-next';
  64. export
  65. const runAndInsert = 'notebook:run-cell-and-insert-below';
  66. export
  67. const runAll = 'notebook:run-all-cells';
  68. export
  69. const toCode = 'notebook:change-cell-to-code';
  70. export
  71. const toMarkdown = 'notebook:change-cell-to-markdown';
  72. export
  73. const toRaw = 'notebook:change-cell-to-raw';
  74. export
  75. const cut = 'notebook:cut-cell';
  76. export
  77. const copy = 'notebook:copy-cell';
  78. export
  79. const paste = 'notebook:paste-cell';
  80. export
  81. const moveUp = 'notebook:move-cell-up';
  82. export
  83. const moveDown = 'notebook:move-cell-down';
  84. export
  85. const clearOutputs = 'notebook:clear-cell-output';
  86. export
  87. const deleteCell = 'notebook:delete-cell';
  88. export
  89. const insertAbove = 'notebook:insert-cell-above';
  90. export
  91. const insertBelow = 'notebook:insert-cell-below';
  92. export
  93. const selectAbove = 'notebook:move-cursor-up';
  94. export
  95. const selectBelow = 'notebook:move-cursor-down';
  96. export
  97. const extendAbove = 'notebook:extend-marked-cells-above';
  98. export
  99. const extendBelow = 'notebook:extend-marked-cells-below';
  100. export
  101. const editMode = 'notebook:enter-edit-mode';
  102. export
  103. const merge = 'notebook:merge-cells';
  104. export
  105. const split = 'notebook:split-cell-at-cursor';
  106. export
  107. const commandMode = 'notebook:enter-command-mode';
  108. export
  109. const toggleLines = 'notebook:toggle-cell-line-numbers';
  110. export
  111. const toggleAllLines = 'notebook:toggle-all-cell-line-numbers';
  112. export
  113. const undo = 'notebook:undo-cell-action';
  114. export
  115. const redo = 'notebook:redo-cell-action';
  116. export
  117. const markdown1 = 'notebook:change-cell-to-heading-1';
  118. export
  119. const markdown2 = 'notebook:change-cell-to-heading-2';
  120. export
  121. const markdown3 = 'notebook:change-cell-to-heading-3';
  122. export
  123. const markdown4 = 'notebook:change-cell-to-heading-4';
  124. export
  125. const markdown5 = 'notebook:change-cell-to-heading-5';
  126. export
  127. const markdown6 = 'notebook:change-cell-to-heading-6';
  128. export
  129. const hideCode = 'notebook:hide-cell-code';
  130. export
  131. const showCode = 'notebook:show-cell-code';
  132. export
  133. const hideAllCode = 'notebook:hide-all-cell-code';
  134. export
  135. const showAllCode = 'notebook:show-all-cell-code';
  136. export
  137. const hideOutput = 'notebook:hide-cell-outputs';
  138. export
  139. const showOutput = 'notebook:show-cell-outputs';
  140. export
  141. const hideAllOutputs = 'notebook:hide-all-cell-outputs';
  142. export
  143. const showAllOutputs = 'notebook:show-all-cell-outputs';
  144. }
  145. /**
  146. * The class name for the notebook icon from the default theme.
  147. */
  148. const NOTEBOOK_ICON_CLASS = 'jp-NotebookRunningIcon';
  149. /**
  150. * The name of the factory that creates notebooks.
  151. */
  152. const FACTORY = 'Notebook';
  153. /**
  154. * The allowed Export To ... formats and their human readable labels.
  155. */
  156. const EXPORT_TO_FORMATS = [
  157. { 'format': 'html', 'label': 'HTML' },
  158. { 'format': 'latex', 'label': 'LaTeX' },
  159. { 'format': 'markdown', 'label': 'Markdown' },
  160. { 'format': 'pdf', 'label': 'PDF' },
  161. { 'format': 'rst', 'label': 'ReStructured Text' },
  162. { 'format': 'script', 'label': 'Executable Script' },
  163. { 'format': 'slides', 'label': 'Reveal JS' }
  164. ];
  165. /**
  166. * The notebook widget tracker provider.
  167. */
  168. export
  169. const trackerPlugin: JupyterLabPlugin<INotebookTracker> = {
  170. id: 'jupyter.services.notebook-tracker',
  171. provides: INotebookTracker,
  172. requires: [
  173. IMainMenu,
  174. ICommandPalette,
  175. NotebookPanel.IContentFactory,
  176. IEditorServices,
  177. ILayoutRestorer
  178. ],
  179. optional: [ILauncher],
  180. activate: activateNotebookHandler,
  181. autoStart: true
  182. };
  183. /**
  184. * The notebook cell factory provider.
  185. */
  186. export
  187. const contentFactoryPlugin: JupyterLabPlugin<NotebookPanel.IContentFactory> = {
  188. id: 'jupyter.services.notebook-renderer',
  189. provides: NotebookPanel.IContentFactory,
  190. requires: [IEditorServices],
  191. autoStart: true,
  192. activate: (app: JupyterLab, editorServices: IEditorServices) => {
  193. let editorFactory = editorServices.factoryService.newInlineEditor.bind(
  194. editorServices.factoryService);
  195. return new NotebookPanel.ContentFactory({ editorFactory });
  196. }
  197. };
  198. /**
  199. * The cell tools extension.
  200. */
  201. const cellToolsPlugin: JupyterLabPlugin<ICellTools> = {
  202. activate: activateCellTools,
  203. provides: ICellTools,
  204. id: 'jupyter.extensions.cell-tools',
  205. autoStart: true,
  206. requires: [INotebookTracker, IEditorServices, IStateDB]
  207. };
  208. /**
  209. * Export the plugins as default.
  210. */
  211. const plugins: JupyterLabPlugin<any>[] = [contentFactoryPlugin, trackerPlugin, cellToolsPlugin];
  212. export default plugins;
  213. /**
  214. * Activate the cell tools extension.
  215. */
  216. function activateCellTools(app: JupyterLab, tracker: INotebookTracker, editorServices: IEditorServices, state: IStateDB): Promise<ICellTools> {
  217. const id = 'cell-tools';
  218. const celltools = new CellTools({ tracker });
  219. const activeCellTool = new CellTools.ActiveCellTool();
  220. const slideShow = CellTools.createSlideShowSelector();
  221. const nbConvert = CellTools.createNBConvertSelector();
  222. const editorFactory = editorServices.factoryService.newInlineEditor
  223. .bind(editorServices.factoryService);
  224. const metadataEditor = new CellTools.MetadataEditorTool({ editorFactory });
  225. // Create message hook for triggers to save to the database.
  226. const hook = (sender: any, message: Message): boolean => {
  227. switch (message) {
  228. case Widget.Msg.ActivateRequest:
  229. state.save(id, { open: true });
  230. break;
  231. case Widget.Msg.AfterHide:
  232. case Widget.Msg.CloseRequest:
  233. state.remove(id);
  234. break;
  235. default:
  236. break;
  237. }
  238. return true;
  239. };
  240. celltools.title.label = 'Cell Tools';
  241. celltools.id = id;
  242. celltools.addItem({ tool: activeCellTool, rank: 1 });
  243. celltools.addItem({ tool: slideShow, rank: 2 });
  244. celltools.addItem({ tool: nbConvert, rank: 3 });
  245. celltools.addItem({ tool: metadataEditor, rank: 4 });
  246. MessageLoop.installMessageHook(celltools, hook);
  247. // Wait until the application has finished restoring before rendering.
  248. Promise.all([state.fetch(id), app.restored]).then(([args]) => {
  249. const open = (args && args['open'] as boolean) || false;
  250. // After initial restoration, check if the cell tools should render.
  251. if (tracker.size) {
  252. app.shell.addToLeftArea(celltools);
  253. if (open) {
  254. app.shell.activateById(celltools.id);
  255. }
  256. }
  257. // For all subsequent widget changes, check if the cell tools should render.
  258. app.shell.currentChanged.connect((sender, args) => {
  259. // If there are any open notebooks, add cell tools to the side panel if
  260. // it is not already there.
  261. if (tracker.size) {
  262. if (!celltools.isAttached) {
  263. app.shell.addToLeftArea(celltools);
  264. }
  265. return;
  266. }
  267. // If there are no notebooks, close cell tools.
  268. celltools.close();
  269. });
  270. });
  271. return Promise.resolve(celltools);
  272. }
  273. /**
  274. * Activate the notebook handler extension.
  275. */
  276. function activateNotebookHandler(app: JupyterLab, mainMenu: IMainMenu, palette: ICommandPalette, contentFactory: NotebookPanel.IContentFactory, editorServices: IEditorServices, restorer: ILayoutRestorer, launcher: ILauncher | null): INotebookTracker {
  277. const services = app.serviceManager;
  278. const factory = new NotebookWidgetFactory({
  279. name: FACTORY,
  280. fileTypes: ['notebook'],
  281. modelName: 'notebook',
  282. defaultFor: ['notebook'],
  283. preferKernel: true,
  284. canStartKernel: true,
  285. rendermime: app.rendermime,
  286. contentFactory,
  287. mimeTypeService: editorServices.mimeTypeService
  288. });
  289. const { commands } = app;
  290. const tracker = new NotebookTracker({ namespace: 'notebook' });
  291. // Handle state restoration.
  292. restorer.restore(tracker, {
  293. command: 'docmanager:open',
  294. args: panel => ({ path: panel.context.path, factory: FACTORY }),
  295. name: panel => panel.context.path,
  296. when: services.ready
  297. });
  298. // Update the command registry when the notebook state changes.
  299. tracker.currentChanged.connect(() => {
  300. if (tracker.size <= 1) {
  301. commands.notifyCommandChanged(CommandIDs.interrupt);
  302. }
  303. });
  304. let registry = app.docRegistry;
  305. registry.addModelFactory(new NotebookModelFactory({}));
  306. registry.addWidgetFactory(factory);
  307. registry.addCreator({
  308. name: 'Notebook',
  309. fileType: 'Notebook',
  310. widgetName: 'Notebook'
  311. });
  312. addCommands(app, services, tracker);
  313. populatePalette(palette);
  314. let id = 0; // The ID counter for notebook panels.
  315. factory.widgetCreated.connect((sender, widget) => {
  316. // If the notebook panel does not have an ID, assign it one.
  317. widget.id = widget.id || `notebook-${++id}`;
  318. widget.title.icon = NOTEBOOK_ICON_CLASS;
  319. // Notify the instance tracker if restore data needs to update.
  320. widget.context.pathChanged.connect(() => { tracker.save(widget); });
  321. // Add the notebook panel to the tracker.
  322. tracker.add(widget);
  323. });
  324. // Add main menu notebook menu.
  325. mainMenu.addMenu(createMenu(app), { rank: 20 });
  326. // The launcher callback.
  327. let callback = (cwd: string, name: string) => {
  328. return commands.execute(
  329. 'docmanager:new-untitled', { path: cwd, type: 'notebook' }
  330. ).then(model => {
  331. return commands.execute('docmanager:open', {
  332. path: model.path, factory: FACTORY,
  333. kernel: { name }
  334. });
  335. });
  336. };
  337. // Add a launcher item if the launcher is available.
  338. if (launcher) {
  339. services.ready.then(() => {
  340. const specs = services.specs;
  341. const baseUrl = PageConfig.getBaseUrl();
  342. for (let name in specs.kernelspecs) {
  343. let displayName = specs.kernelspecs[name].display_name;
  344. let rank = name === specs.default ? 0 : Infinity;
  345. let kernelIconUrl = specs.kernelspecs[name].resources['logo-64x64'];
  346. if (kernelIconUrl) {
  347. let index = kernelIconUrl.indexOf('kernelspecs');
  348. kernelIconUrl = baseUrl + kernelIconUrl.slice(index);
  349. }
  350. launcher.add({
  351. displayName,
  352. category: 'Notebook',
  353. name,
  354. iconClass: 'jp-NotebookRunningIcon',
  355. callback,
  356. rank,
  357. kernelIconUrl
  358. });
  359. }
  360. });
  361. }
  362. app.contextMenu.addItem({
  363. command: CommandIDs.clearOutputs,
  364. selector: '.jp-Notebook .jp-Cell'
  365. });
  366. app.contextMenu.addItem({
  367. command: CommandIDs.split,
  368. selector: '.jp-Notebook .jp-Cell'
  369. });
  370. app.contextMenu.addItem({
  371. type: 'separator',
  372. selector: '.jp-Notebook',
  373. rank: 0
  374. });
  375. app.contextMenu.addItem({
  376. command: CommandIDs.undo,
  377. selector: '.jp-Notebook',
  378. rank: 1
  379. });
  380. app.contextMenu.addItem({
  381. command: CommandIDs.redo,
  382. selector: '.jp-Notebook',
  383. rank: 2
  384. });
  385. app.contextMenu.addItem({
  386. type: 'separator',
  387. selector: '.jp-Notebook',
  388. rank: 0
  389. });
  390. app.contextMenu.addItem({
  391. command: CommandIDs.createConsole,
  392. selector: '.jp-Notebook',
  393. rank: 3
  394. });
  395. app.contextMenu.addItem({
  396. command: CommandIDs.clearAllOutputs,
  397. selector: '.jp-Notebook',
  398. rank: 3
  399. });
  400. return tracker;
  401. }
  402. /**
  403. * Add the notebook commands to the application's command registry.
  404. */
  405. function addCommands(app: JupyterLab, services: ServiceManager, tracker: NotebookTracker): void {
  406. const { commands, shell } = app;
  407. // Get the current widget and activate unless the args specify otherwise.
  408. function getCurrent(args: ReadonlyJSONObject): NotebookPanel | null {
  409. const widget = tracker.currentWidget;
  410. const activate = args['activate'] !== false;
  411. if (activate && widget) {
  412. shell.activateById(widget.id);
  413. }
  414. return widget;
  415. }
  416. /**
  417. * Whether there is an active notebook.
  418. */
  419. function hasWidget(): boolean {
  420. return tracker.currentWidget !== null;
  421. }
  422. commands.addCommand(CommandIDs.runAndAdvance, {
  423. label: 'Run Cell(s) and Select Below',
  424. execute: args => {
  425. const current = getCurrent(args);
  426. if (current) {
  427. const { context, notebook } = current;
  428. return NotebookActions.runAndAdvance(notebook, context.session);
  429. }
  430. },
  431. isEnabled: hasWidget
  432. });
  433. commands.addCommand(CommandIDs.run, {
  434. label: 'Run Cell(s)',
  435. execute: args => {
  436. const current = getCurrent(args);
  437. if (current) {
  438. const { context, notebook } = current;
  439. return NotebookActions.run(notebook, context.session);
  440. }
  441. },
  442. isEnabled: hasWidget
  443. });
  444. commands.addCommand(CommandIDs.runAndInsert, {
  445. label: 'Run Cell(s) and Insert Below',
  446. execute: args => {
  447. const current = getCurrent(args);
  448. if (current) {
  449. const { context, notebook } = current;
  450. return NotebookActions.runAndInsert(notebook, context.session);
  451. }
  452. },
  453. isEnabled: hasWidget
  454. });
  455. commands.addCommand(CommandIDs.runAll, {
  456. label: 'Run All Cells',
  457. execute: args => {
  458. const current = getCurrent(args);
  459. if (current) {
  460. const { context, notebook } = current;
  461. return NotebookActions.runAll(notebook, context.session);
  462. }
  463. },
  464. isEnabled: hasWidget
  465. });
  466. commands.addCommand(CommandIDs.restart, {
  467. label: 'Restart Kernel',
  468. execute: args => {
  469. const current = getCurrent(args);
  470. if (current) {
  471. return current.session.restart();
  472. }
  473. },
  474. isEnabled: hasWidget
  475. });
  476. commands.addCommand(CommandIDs.closeAndShutdown, {
  477. label: 'Close and Shutdown',
  478. execute: args => {
  479. const current = getCurrent(args);
  480. if (!current) {
  481. return;
  482. }
  483. const fileName = current.title.label;
  484. return showDialog({
  485. title: 'Shutdown the notebook?',
  486. body: `Are you sure you want to close "${fileName}"?`,
  487. buttons: [Dialog.cancelButton(), Dialog.warnButton()]
  488. }).then(result => {
  489. if (result.button.accept) {
  490. return current.context.session.shutdown()
  491. .then(() => { current.dispose(); });
  492. }
  493. });
  494. },
  495. isEnabled: hasWidget
  496. });
  497. commands.addCommand(CommandIDs.trust, {
  498. label: 'Trust Notebook',
  499. execute: args => {
  500. const current = getCurrent(args);
  501. if (current) {
  502. const { context, notebook } = current;
  503. return NotebookActions.trust(notebook).then(() => context.save());
  504. }
  505. },
  506. isEnabled: hasWidget
  507. });
  508. commands.addCommand(CommandIDs.exportToFormat, {
  509. label: args => {
  510. const formatLabel = (args['label']) as string;
  511. return (args['isPalette'] ? 'Export To ' : '') + formatLabel;
  512. },
  513. execute: args => {
  514. const current = getCurrent(args);
  515. if (!current) {
  516. return;
  517. }
  518. const notebookPath = URLExt.encodeParts(current.context.path);
  519. const url = URLExt.join(
  520. services.serverSettings.baseUrl,
  521. 'nbconvert',
  522. (args['format']) as string,
  523. notebookPath
  524. ) + '?download=true';
  525. const child = window.open('', '_blank');
  526. const { context } = current;
  527. if (context.model.dirty && !context.model.readOnly) {
  528. return context.save().then(() => { child.location.assign(url); });
  529. }
  530. return new Promise<void>((resolve) => {
  531. child.location.assign(url);
  532. resolve(undefined);
  533. });
  534. },
  535. isEnabled: hasWidget
  536. });
  537. commands.addCommand(CommandIDs.restartClear, {
  538. label: 'Restart Kernel & Clear Outputs',
  539. execute: args => {
  540. const current = getCurrent(args);
  541. if (current) {
  542. const { notebook, session } = current;
  543. return session.restart()
  544. .then(() => { NotebookActions.clearAllOutputs(notebook); });
  545. }
  546. },
  547. isEnabled: hasWidget
  548. });
  549. commands.addCommand(CommandIDs.restartRunAll, {
  550. label: 'Restart Kernel & Run All',
  551. execute: args => {
  552. const current = getCurrent(args);
  553. if (current) {
  554. const { context, notebook, session } = current;
  555. return session.restart()
  556. .then(() => { NotebookActions.runAll(notebook, context.session); });
  557. }
  558. },
  559. isEnabled: hasWidget
  560. });
  561. commands.addCommand(CommandIDs.clearAllOutputs, {
  562. label: 'Clear All Outputs',
  563. execute: args => {
  564. const current = getCurrent(args);
  565. if (current) {
  566. return NotebookActions.clearAllOutputs(current.notebook);
  567. }
  568. },
  569. isEnabled: hasWidget
  570. });
  571. commands.addCommand(CommandIDs.clearOutputs, {
  572. label: 'Clear Output(s)',
  573. execute: args => {
  574. const current = getCurrent(args);
  575. if (current) {
  576. return NotebookActions.clearOutputs(current.notebook);
  577. }
  578. },
  579. isEnabled: hasWidget
  580. });
  581. commands.addCommand(CommandIDs.interrupt, {
  582. label: 'Interrupt Kernel',
  583. execute: args => {
  584. const current = getCurrent(args);
  585. if (!current) {
  586. return;
  587. }
  588. const kernel = current.context.session.kernel;
  589. if (kernel) {
  590. return kernel.interrupt();
  591. }
  592. },
  593. isEnabled: hasWidget
  594. });
  595. commands.addCommand(CommandIDs.toCode, {
  596. label: 'Change to Code Cell Type',
  597. execute: args => {
  598. const current = getCurrent(args);
  599. if (current) {
  600. return NotebookActions.changeCellType(current.notebook, 'code');
  601. }
  602. },
  603. isEnabled: hasWidget
  604. });
  605. commands.addCommand(CommandIDs.toMarkdown, {
  606. label: 'Change to Markdown Cell Type',
  607. execute: args => {
  608. const current = getCurrent(args);
  609. if (current) {
  610. return NotebookActions.changeCellType(current.notebook, 'markdown');
  611. }
  612. },
  613. isEnabled: hasWidget
  614. });
  615. commands.addCommand(CommandIDs.toRaw, {
  616. label: 'Change to Raw Cell Type',
  617. execute: args => {
  618. const current = getCurrent(args);
  619. if (current) {
  620. return NotebookActions.changeCellType(current.notebook, 'raw');
  621. }
  622. },
  623. isEnabled: hasWidget
  624. });
  625. commands.addCommand(CommandIDs.cut, {
  626. label: 'Cut Cell(s)',
  627. execute: args => {
  628. const current = getCurrent(args);
  629. if (current) {
  630. return NotebookActions.cut(current.notebook);
  631. }
  632. },
  633. isEnabled: hasWidget
  634. });
  635. commands.addCommand(CommandIDs.copy, {
  636. label: 'Copy Cell(s)',
  637. execute: args => {
  638. const current = getCurrent(args);
  639. if (current) {
  640. return NotebookActions.copy(current.notebook);
  641. }
  642. },
  643. isEnabled: hasWidget
  644. });
  645. commands.addCommand(CommandIDs.paste, {
  646. label: 'Paste Cell(s) Below',
  647. execute: args => {
  648. const current = getCurrent(args);
  649. if (current) {
  650. return NotebookActions.paste(current.notebook);
  651. }
  652. },
  653. isEnabled: hasWidget
  654. });
  655. commands.addCommand(CommandIDs.deleteCell, {
  656. label: 'Delete Cell(s)',
  657. execute: args => {
  658. const current = getCurrent(args);
  659. if (current) {
  660. return NotebookActions.deleteCells(current.notebook);
  661. }
  662. },
  663. isEnabled: hasWidget
  664. });
  665. commands.addCommand(CommandIDs.split, {
  666. label: 'Split Cell',
  667. execute: args => {
  668. const current = getCurrent(args);
  669. if (current) {
  670. return NotebookActions.splitCell(current.notebook);
  671. }
  672. },
  673. isEnabled: hasWidget
  674. });
  675. commands.addCommand(CommandIDs.merge, {
  676. label: 'Merge Selected Cell(s)',
  677. execute: args => {
  678. const current = getCurrent(args);
  679. if (current) {
  680. return NotebookActions.mergeCells(current.notebook);
  681. }
  682. },
  683. isEnabled: hasWidget
  684. });
  685. commands.addCommand(CommandIDs.insertAbove, {
  686. label: 'Insert Cell Above',
  687. execute: args => {
  688. const current = getCurrent(args);
  689. if (current) {
  690. return NotebookActions.insertAbove(current.notebook);
  691. }
  692. },
  693. isEnabled: hasWidget
  694. });
  695. commands.addCommand(CommandIDs.insertBelow, {
  696. label: 'Insert Cell Below',
  697. execute: args => {
  698. const current = getCurrent(args);
  699. if (current) {
  700. return NotebookActions.insertBelow(current.notebook);
  701. }
  702. },
  703. isEnabled: hasWidget
  704. });
  705. commands.addCommand(CommandIDs.selectAbove, {
  706. label: 'Select Cell Above',
  707. execute: args => {
  708. const current = getCurrent(args);
  709. if (current) {
  710. return NotebookActions.selectAbove(current.notebook);
  711. }
  712. },
  713. isEnabled: hasWidget
  714. });
  715. commands.addCommand(CommandIDs.selectBelow, {
  716. label: 'Select Cell Below',
  717. execute: args => {
  718. const current = getCurrent(args);
  719. if (current) {
  720. return NotebookActions.selectBelow(current.notebook);
  721. }
  722. },
  723. isEnabled: hasWidget
  724. });
  725. commands.addCommand(CommandIDs.extendAbove, {
  726. label: 'Extend Selection Above',
  727. execute: args => {
  728. const current = getCurrent(args);
  729. if (current) {
  730. return NotebookActions.extendSelectionAbove(current.notebook);
  731. }
  732. },
  733. isEnabled: hasWidget
  734. });
  735. commands.addCommand(CommandIDs.extendBelow, {
  736. label: 'Extend Selection Below',
  737. execute: args => {
  738. const current = getCurrent(args);
  739. if (current) {
  740. return NotebookActions.extendSelectionBelow(current.notebook);
  741. }
  742. },
  743. isEnabled: hasWidget
  744. });
  745. commands.addCommand(CommandIDs.moveUp, {
  746. label: 'Move Cell(s) Up',
  747. execute: args => {
  748. const current = getCurrent(args);
  749. if (current) {
  750. return NotebookActions.moveUp(current.notebook);
  751. }
  752. },
  753. isEnabled: hasWidget
  754. });
  755. commands.addCommand(CommandIDs.moveDown, {
  756. label: 'Move Cell(s) Down',
  757. execute: args => {
  758. const current = getCurrent(args);
  759. if (current) {
  760. return NotebookActions.moveDown(current.notebook);
  761. }
  762. },
  763. isEnabled: hasWidget
  764. });
  765. commands.addCommand(CommandIDs.toggleLines, {
  766. label: 'Toggle Line Numbers',
  767. execute: args => {
  768. const current = getCurrent(args);
  769. if (current) {
  770. return NotebookActions.toggleLineNumbers(current.notebook);
  771. }
  772. },
  773. isEnabled: hasWidget
  774. });
  775. commands.addCommand(CommandIDs.toggleAllLines, {
  776. label: 'Toggle All Line Numbers',
  777. execute: args => {
  778. const current = getCurrent(args);
  779. if (current) {
  780. return NotebookActions.toggleAllLineNumbers(current.notebook);
  781. }
  782. },
  783. isEnabled: hasWidget
  784. });
  785. commands.addCommand(CommandIDs.commandMode, {
  786. label: 'Enter Command Mode',
  787. execute: args => {
  788. const current = getCurrent(args);
  789. if (current) {
  790. current.notebook.mode = 'command';
  791. }
  792. },
  793. isEnabled: hasWidget
  794. });
  795. commands.addCommand(CommandIDs.editMode, {
  796. label: 'Enter Edit Mode',
  797. execute: args => {
  798. const current = getCurrent(args);
  799. if (current) {
  800. current.notebook.mode = 'edit';
  801. }
  802. },
  803. isEnabled: hasWidget
  804. });
  805. commands.addCommand(CommandIDs.undo, {
  806. label: 'Undo Cell Operation',
  807. execute: args => {
  808. const current = getCurrent(args);
  809. if (current) {
  810. return NotebookActions.undo(current.notebook);
  811. }
  812. },
  813. isEnabled: hasWidget
  814. });
  815. commands.addCommand(CommandIDs.redo, {
  816. label: 'Redo Cell Operation',
  817. execute: args => {
  818. const current = getCurrent(args);
  819. if (!current) {
  820. return NotebookActions.redo(current.notebook);
  821. }
  822. },
  823. isEnabled: hasWidget
  824. });
  825. commands.addCommand(CommandIDs.changeKernel, {
  826. label: 'Change Kernel',
  827. execute: args => {
  828. const current = getCurrent(args);
  829. if (current) {
  830. return current.context.session.selectKernel();
  831. }
  832. },
  833. isEnabled: hasWidget
  834. });
  835. commands.addCommand(CommandIDs.reconnectToKernel, {
  836. label: 'Reconnect To Kernel',
  837. execute: args => {
  838. const current = getCurrent(args);
  839. if (!current) {
  840. return;
  841. }
  842. const kernel = current.context.session.kernel;
  843. if (kernel) {
  844. return kernel.reconnect();
  845. }
  846. },
  847. isEnabled: hasWidget
  848. });
  849. commands.addCommand(CommandIDs.createConsole, {
  850. label: 'Create Console for Notebook',
  851. execute: args => {
  852. const current = getCurrent(args);
  853. const widget = tracker.currentWidget;
  854. if (!current || !widget) {
  855. return;
  856. }
  857. const options: ReadonlyJSONObject = {
  858. path: widget.context.path,
  859. preferredLanguage: widget.context.model.defaultKernelLanguage,
  860. activate: args['activate']
  861. };
  862. return commands.execute('console:create', options);
  863. },
  864. isEnabled: hasWidget
  865. });
  866. commands.addCommand(CommandIDs.markdown1, {
  867. label: 'Change to Heading 1',
  868. execute: args => {
  869. const current = getCurrent(args);
  870. if (current) {
  871. return NotebookActions.setMarkdownHeader(current.notebook, 1);
  872. }
  873. },
  874. isEnabled: hasWidget
  875. });
  876. commands.addCommand(CommandIDs.markdown2, {
  877. label: 'Change to Heading 2',
  878. execute: args => {
  879. const current = getCurrent(args);
  880. if (current) {
  881. return NotebookActions.setMarkdownHeader(current.notebook, 2);
  882. }
  883. },
  884. isEnabled: hasWidget
  885. });
  886. commands.addCommand(CommandIDs.markdown3, {
  887. label: 'Change to Heading 3',
  888. execute: args => {
  889. const current = getCurrent(args);
  890. if (current) {
  891. return NotebookActions.setMarkdownHeader(current.notebook, 3);
  892. }
  893. },
  894. isEnabled: hasWidget
  895. });
  896. commands.addCommand(CommandIDs.markdown4, {
  897. label: 'Change to Heading 4',
  898. execute: args => {
  899. const current = getCurrent(args);
  900. if (current) {
  901. return NotebookActions.setMarkdownHeader(current.notebook, 4);
  902. }
  903. },
  904. isEnabled: hasWidget
  905. });
  906. commands.addCommand(CommandIDs.markdown5, {
  907. label: 'Change to Heading 5',
  908. execute: args => {
  909. const current = getCurrent(args);
  910. if (current) {
  911. return NotebookActions.setMarkdownHeader(current.notebook, 5);
  912. }
  913. },
  914. isEnabled: hasWidget
  915. });
  916. commands.addCommand(CommandIDs.markdown6, {
  917. label: 'Change to Heading 6',
  918. execute: args => {
  919. const current = getCurrent(args);
  920. if (current) {
  921. return NotebookActions.setMarkdownHeader(current.notebook, 6);
  922. }
  923. },
  924. isEnabled: hasWidget
  925. });
  926. commands.addCommand(CommandIDs.hideCode, {
  927. label: 'Hide Code',
  928. execute: args => {
  929. const current = getCurrent(args);
  930. if (current) {
  931. return NotebookActions.hideCode(current.notebook);
  932. }
  933. },
  934. isEnabled: hasWidget
  935. });
  936. commands.addCommand(CommandIDs.showCode, {
  937. label: 'Show Code',
  938. execute: args => {
  939. const current = getCurrent(args);
  940. if (current) {
  941. return NotebookActions.showCode(current.notebook);
  942. }
  943. },
  944. isEnabled: hasWidget
  945. });
  946. commands.addCommand(CommandIDs.hideAllCode, {
  947. label: 'Hide All Code',
  948. execute: args => {
  949. const current = getCurrent(args);
  950. if (current) {
  951. return NotebookActions.hideAllCode(current.notebook);
  952. }
  953. },
  954. isEnabled: hasWidget
  955. });
  956. commands.addCommand(CommandIDs.showAllCode, {
  957. label: 'Show All Code',
  958. execute: args => {
  959. const current = getCurrent(args);
  960. if (current) {
  961. return NotebookActions.showAllCode(current.notebook);
  962. }
  963. },
  964. isEnabled: hasWidget
  965. });
  966. commands.addCommand(CommandIDs.hideOutput, {
  967. label: 'Hide Output',
  968. execute: args => {
  969. const current = getCurrent(args);
  970. if (current) {
  971. return NotebookActions.hideOutput(current.notebook);
  972. }
  973. },
  974. isEnabled: hasWidget
  975. });
  976. commands.addCommand(CommandIDs.showOutput, {
  977. label: 'Show Output',
  978. execute: args => {
  979. const current = getCurrent(args);
  980. if (current) {
  981. return NotebookActions.showOutput(current.notebook);
  982. }
  983. },
  984. isEnabled: hasWidget
  985. });
  986. commands.addCommand(CommandIDs.hideAllOutputs, {
  987. label: 'Hide All Outputs',
  988. execute: args => {
  989. const current = getCurrent(args);
  990. if (current) {
  991. return NotebookActions.hideAllOutputs(current.notebook);
  992. }
  993. },
  994. isEnabled: hasWidget
  995. });
  996. commands.addCommand(CommandIDs.showAllOutputs, {
  997. label: 'Show All Outputs',
  998. execute: args => {
  999. const current = getCurrent(args);
  1000. if (current) {
  1001. return NotebookActions.showAllOutputs(current.notebook);
  1002. }
  1003. },
  1004. isEnabled: hasWidget
  1005. });
  1006. }
  1007. /**
  1008. * Populate the application's command palette with notebook commands.
  1009. */
  1010. function populatePalette(palette: ICommandPalette): void {
  1011. let category = 'Notebook Operations';
  1012. [
  1013. CommandIDs.interrupt,
  1014. CommandIDs.restart,
  1015. CommandIDs.restartClear,
  1016. CommandIDs.restartRunAll,
  1017. CommandIDs.runAll,
  1018. CommandIDs.clearAllOutputs,
  1019. CommandIDs.toggleAllLines,
  1020. CommandIDs.editMode,
  1021. CommandIDs.commandMode,
  1022. CommandIDs.changeKernel,
  1023. CommandIDs.reconnectToKernel,
  1024. CommandIDs.createConsole,
  1025. CommandIDs.closeAndShutdown,
  1026. CommandIDs.trust
  1027. ].forEach(command => { palette.addItem({ command, category }); });
  1028. EXPORT_TO_FORMATS.forEach(exportToFormat => {
  1029. let args = { 'format': exportToFormat['format'], 'label': exportToFormat['label'], 'isPalette': true };
  1030. palette.addItem({ command: CommandIDs.exportToFormat, category: category, args: args });
  1031. });
  1032. category = 'Notebook Cell Operations';
  1033. [
  1034. CommandIDs.run,
  1035. CommandIDs.runAndAdvance,
  1036. CommandIDs.runAndInsert,
  1037. CommandIDs.clearOutputs,
  1038. CommandIDs.toCode,
  1039. CommandIDs.toMarkdown,
  1040. CommandIDs.toRaw,
  1041. CommandIDs.cut,
  1042. CommandIDs.copy,
  1043. CommandIDs.paste,
  1044. CommandIDs.deleteCell,
  1045. CommandIDs.split,
  1046. CommandIDs.merge,
  1047. CommandIDs.insertAbove,
  1048. CommandIDs.insertBelow,
  1049. CommandIDs.selectAbove,
  1050. CommandIDs.selectBelow,
  1051. CommandIDs.extendAbove,
  1052. CommandIDs.extendBelow,
  1053. CommandIDs.moveDown,
  1054. CommandIDs.moveUp,
  1055. CommandIDs.toggleLines,
  1056. CommandIDs.undo,
  1057. CommandIDs.redo,
  1058. CommandIDs.markdown1,
  1059. CommandIDs.markdown2,
  1060. CommandIDs.markdown3,
  1061. CommandIDs.markdown4,
  1062. CommandIDs.markdown5,
  1063. CommandIDs.markdown6,
  1064. CommandIDs.hideCode,
  1065. CommandIDs.showCode,
  1066. CommandIDs.hideAllCode,
  1067. CommandIDs.showAllCode,
  1068. CommandIDs.hideOutput,
  1069. CommandIDs.showOutput,
  1070. CommandIDs.hideAllOutputs,
  1071. CommandIDs.showAllOutputs,
  1072. ].forEach(command => { palette.addItem({ command, category }); });
  1073. }
  1074. /**
  1075. * Creates a menu for the notebook.
  1076. */
  1077. function createMenu(app: JupyterLab): Menu {
  1078. let { commands } = app;
  1079. let menu = new Menu({ commands });
  1080. let settings = new Menu({ commands });
  1081. let exportTo = new Menu({ commands } );
  1082. menu.title.label = 'Notebook';
  1083. settings.title.label = 'Settings';
  1084. settings.addItem({ command: CommandIDs.toggleAllLines });
  1085. exportTo.title.label = 'Export to ...';
  1086. EXPORT_TO_FORMATS.forEach(exportToFormat => {
  1087. exportTo.addItem({ command: CommandIDs.exportToFormat, args: exportToFormat });
  1088. });
  1089. menu.addItem({ command: CommandIDs.undo });
  1090. menu.addItem({ command: CommandIDs.redo });
  1091. menu.addItem({ type: 'separator' });
  1092. menu.addItem({ command: CommandIDs.cut });
  1093. menu.addItem({ command: CommandIDs.copy });
  1094. menu.addItem({ command: CommandIDs.paste });
  1095. menu.addItem({ command: CommandIDs.deleteCell });
  1096. menu.addItem({ type: 'separator' });
  1097. menu.addItem({ command: CommandIDs.split });
  1098. menu.addItem({ command: CommandIDs.merge });
  1099. menu.addItem({ type: 'separator' });
  1100. menu.addItem({ command: CommandIDs.hideAllCode });
  1101. menu.addItem({ command: CommandIDs.showAllCode });
  1102. menu.addItem({ command: CommandIDs.hideAllOutputs });
  1103. menu.addItem({ command: CommandIDs.showAllOutputs });
  1104. menu.addItem({ command: CommandIDs.clearAllOutputs });
  1105. menu.addItem({ type: 'separator' });
  1106. menu.addItem({ command: CommandIDs.runAll });
  1107. menu.addItem({ command: CommandIDs.interrupt });
  1108. menu.addItem({ command: CommandIDs.restart });
  1109. menu.addItem({ command: CommandIDs.changeKernel });
  1110. menu.addItem({ type: 'separator' });
  1111. menu.addItem({ command: CommandIDs.createConsole });
  1112. menu.addItem({ type: 'separator' });
  1113. menu.addItem({ command: CommandIDs.closeAndShutdown });
  1114. menu.addItem({ command: CommandIDs.trust });
  1115. menu.addItem({ type: 'submenu', submenu: exportTo });
  1116. menu.addItem({ type: 'separator' });
  1117. menu.addItem({ type: 'submenu', submenu: settings });
  1118. return menu;
  1119. }