widget.ts 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import {
  4. Kernel, KernelMessage, Session, nbformat
  5. } from '@jupyterlab/services';
  6. import {
  7. map, toArray
  8. } from 'phosphor/lib/algorithm/iteration';
  9. import {
  10. Message
  11. } from 'phosphor/lib/core/messaging';
  12. import {
  13. clearSignalData, defineSignal, ISignal
  14. } from 'phosphor/lib/core/signaling';
  15. import {
  16. Panel, PanelLayout
  17. } from 'phosphor/lib/ui/panel';
  18. import {
  19. Widget
  20. } from 'phosphor/lib/ui/widget';
  21. import {
  22. IEditorMimeTypeService, CodeEditor
  23. } from '../codeeditor';
  24. import {
  25. BaseCellWidget, CodeCellWidget, RawCellWidget,
  26. ICodeCellModel, IRawCellModel, CellModel,
  27. RawCellModel, CodeCellModel
  28. } from '../cells';
  29. import {
  30. OutputAreaWidget
  31. } from '../outputarea';
  32. import {
  33. IRenderMime, RenderMime
  34. } from '../rendermime';
  35. import {
  36. ForeignHandler
  37. } from './foreign';
  38. import {
  39. ConsoleHistory, IConsoleHistory
  40. } from './history';
  41. import {
  42. IObservableVector, ObservableVector
  43. } from '../common/observablevector';
  44. /**
  45. * The class name added to console widgets.
  46. */
  47. const CONSOLE_CLASS = 'jp-CodeConsole';
  48. /**
  49. * The class name added to the console banner.
  50. */
  51. const BANNER_CLASS = 'jp-CodeConsole-banner';
  52. /**
  53. * The class name of a cell whose input originated from a foreign session.
  54. */
  55. const FOREIGN_CELL_CLASS = 'jp-CodeConsole-foreignCell';
  56. /**
  57. * The class name of the active prompt
  58. */
  59. const PROMPT_CLASS = 'jp-CodeConsole-prompt';
  60. /**
  61. * The class name of the panel that holds cell content.
  62. */
  63. const CONTENT_CLASS = 'jp-CodeConsole-content';
  64. /**
  65. * The class name of the panel that holds prompts.
  66. */
  67. const INPUT_CLASS = 'jp-CodeConsole-input';
  68. /**
  69. * The timeout in ms for execution requests to the kernel.
  70. */
  71. const EXECUTION_TIMEOUT = 250;
  72. /**
  73. * A widget containing a Jupyter console.
  74. *
  75. * #### Notes
  76. * The CodeConsole class is intended to be used within a ConsolePanel
  77. * instance. Under most circumstances, it is not instantiated by user code.
  78. */
  79. export
  80. class CodeConsole extends Widget {
  81. /**
  82. * Construct a console widget.
  83. */
  84. constructor(options: CodeConsole.IOptions) {
  85. super();
  86. this.addClass(CONSOLE_CLASS);
  87. // Create the panels that hold the content and input.
  88. let layout = this.layout = new PanelLayout();
  89. this._cells = new ObservableVector<BaseCellWidget>();
  90. this._content = new Panel();
  91. this._input = new Panel();
  92. let factory = this.contentFactory = options.contentFactory;
  93. let modelFactory = this.modelFactory = (
  94. options.modelFactory || CodeConsole.defaultModelFactory
  95. );
  96. this.rendermime = options.rendermime;
  97. this.session = options.session;
  98. this._mimeTypeService = options.mimeTypeService;
  99. // Add top-level CSS classes.
  100. this._content.addClass(CONTENT_CLASS);
  101. this._input.addClass(INPUT_CLASS);
  102. // Insert the content and input panes into the widget.
  103. layout.addWidget(this._content);
  104. layout.addWidget(this._input);
  105. // Create the banner.
  106. let model = modelFactory.createRawCell({});
  107. model.value.text = '...';
  108. let banner = this.banner = factory.createBanner({
  109. model,
  110. contentFactory: factory.rawCellContentFactory
  111. }, this);
  112. banner.addClass(BANNER_CLASS);
  113. banner.readOnly = true;
  114. this._content.addWidget(banner);
  115. // Set the banner text and the mimetype.
  116. this._initialize();
  117. // Set up the foreign iopub handler.
  118. this._foreignHandler = factory.createForeignHandler({
  119. kernel: this.session.kernel,
  120. parent: this,
  121. cellFactory: () => this._createForeignCell(),
  122. });
  123. this._history = factory.createConsoleHistory({
  124. kernel: this.session.kernel
  125. });
  126. this.session.kernelChanged.connect(this._onKernelChanged, this);
  127. }
  128. /**
  129. * A signal emitted when the console finished executing its prompt.
  130. */
  131. readonly executed: ISignal<this, Date>;
  132. /**
  133. * A signal emitted when a new prompt is created.
  134. */
  135. readonly promptCreated: ISignal<this, CodeCellWidget>;
  136. /**
  137. * The content factory used by the console.
  138. */
  139. readonly contentFactory: CodeConsole.IContentFactory;
  140. /**
  141. * The model factory for the console widget.
  142. */
  143. readonly modelFactory: CodeConsole.IModelFactory;
  144. /**
  145. * The rendermime instance used by the console.
  146. */
  147. readonly rendermime: IRenderMime;
  148. /**
  149. * The session used by the console.
  150. */
  151. readonly session: Session.ISession;
  152. /**
  153. * The console banner widget.
  154. */
  155. readonly banner: RawCellWidget;
  156. /**
  157. * The list of content cells in the console.
  158. *
  159. * #### Notes
  160. * This list does not include the banner or the prompt for a console.
  161. */
  162. get cells(): IObservableVector<BaseCellWidget> {
  163. return this._cells;
  164. }
  165. /*
  166. * The console input prompt.
  167. */
  168. get prompt(): CodeCellWidget | null {
  169. let inputLayout = (this._input.layout as PanelLayout);
  170. return inputLayout.widgets.at(0) as CodeCellWidget || null;
  171. }
  172. /**
  173. * Add a new cell to the content panel.
  174. *
  175. * @param cell - The cell widget being added to the content panel.
  176. *
  177. * #### Notes
  178. * This method is meant for use by outside classes that want to inject content
  179. * into a console. It is distinct from the `inject` method in that it requires
  180. * rendered code cell widgets and does not execute them.
  181. */
  182. addCell(cell: BaseCellWidget) {
  183. this._content.addWidget(cell);
  184. this._cells.pushBack(cell);
  185. cell.disposed.connect(this._onCellDisposed, this);
  186. this.update();
  187. }
  188. /**
  189. * Clear the code cells.
  190. */
  191. clear(): void {
  192. // Dispose all the content cells except the first, which is the banner.
  193. let cells = this._content.widgets;
  194. while (cells.length > 1) {
  195. cells.at(1).dispose();
  196. }
  197. }
  198. /**
  199. * Dispose of the resources held by the widget.
  200. */
  201. dispose() {
  202. // Do nothing if already disposed.
  203. if (this._foreignHandler === null) {
  204. return;
  205. }
  206. let foreignHandler = this._foreignHandler;
  207. let history = this._history;
  208. let cells = this._cells;
  209. this._foreignHandler = null;
  210. this._history = null;
  211. this._cells = null;
  212. foreignHandler.dispose();
  213. history.dispose();
  214. cells.clear();
  215. super.dispose();
  216. }
  217. /**
  218. * Execute the current prompt.
  219. *
  220. * @param force - Whether to force execution without checking code
  221. * completeness.
  222. *
  223. * @param timeout - The length of time, in milliseconds, that the execution
  224. * should wait for the API to determine whether code being submitted is
  225. * incomplete before attempting submission anyway. The default value is `250`.
  226. */
  227. execute(force = false, timeout = EXECUTION_TIMEOUT): Promise<void> {
  228. if (this.session.status === 'dead') {
  229. return Promise.resolve(void 0);
  230. }
  231. let prompt = this.prompt;
  232. prompt.trusted = true;
  233. if (force) {
  234. // Create a new prompt before kernel execution to allow typeahead.
  235. this.newPrompt();
  236. return this._execute(prompt);
  237. }
  238. // Check whether we should execute.
  239. return this._shouldExecute(timeout).then(should => {
  240. if (this.isDisposed) {
  241. return;
  242. }
  243. if (should) {
  244. // Create a new prompt before kernel execution to allow typeahead.
  245. this.newPrompt();
  246. return this._execute(prompt);
  247. }
  248. });
  249. }
  250. /**
  251. * Inject arbitrary code for the console to execute immediately.
  252. *
  253. * @param code - The code contents of the cell being injected.
  254. *
  255. * @returns A promise that indicates when the injected cell's execution ends.
  256. */
  257. inject(code: string): Promise<void> {
  258. let cell = this._createForeignCell();
  259. cell.model.value.text = code;
  260. this.addCell(cell);
  261. return this._execute(cell);
  262. }
  263. /**
  264. * Insert a line break in the prompt.
  265. */
  266. insertLinebreak(): void {
  267. let prompt = this.prompt;
  268. let model = prompt.model;
  269. let editor = prompt.editor;
  270. // Insert the line break at the cursor position, and move cursor forward.
  271. let pos = editor.getCursorPosition();
  272. let offset = editor.getOffsetAt(pos);
  273. let text = model.value.text;
  274. model.value.text = text.substr(0, offset) + '\n' + text.substr(offset);
  275. pos = editor.getPositionAt(offset + 1);
  276. editor.setCursorPosition(pos);
  277. }
  278. /**
  279. * Serialize the output.
  280. */
  281. serialize(): nbformat.ICodeCell[] {
  282. let prompt = this.prompt;
  283. let layout = this._content.layout as PanelLayout;
  284. // Serialize content.
  285. let output = map(layout.widgets, widget => {
  286. return (widget as CodeCellWidget).model.toJSON() as nbformat.ICodeCell;
  287. });
  288. // Serialize prompt and return.
  289. return toArray(output).concat(prompt.model.toJSON() as nbformat.ICodeCell);
  290. }
  291. /**
  292. * Handle the DOM events for the widget.
  293. *
  294. * @param event - The DOM event sent to the widget.
  295. *
  296. * #### Notes
  297. * This method implements the DOM `EventListener` interface and is
  298. * called in response to events on the notebook panel's node. It should
  299. * not be called directly by user code.
  300. */
  301. handleEvent(event: Event): void {
  302. switch (event.type) {
  303. case 'keydown':
  304. this._evtKeyDown(event as KeyboardEvent);
  305. break;
  306. default:
  307. break;
  308. }
  309. }
  310. /**
  311. * Handle `after_attach` messages for the widget.
  312. */
  313. protected onAfterAttach(msg: Message): void {
  314. let node = this.node;
  315. node.addEventListener('keydown', this, true);
  316. // Create a prompt if necessary.
  317. if (!this.prompt) {
  318. this.newPrompt();
  319. } else {
  320. this.prompt.editor.focus();
  321. this.update();
  322. }
  323. }
  324. /**
  325. * Handle `before_detach` messages for the widget.
  326. */
  327. protected onBeforeDetach(msg: Message): void {
  328. let node = this.node;
  329. node.removeEventListener('keydown', this, true);
  330. }
  331. /**
  332. * Handle `'activate-request'` messages.
  333. */
  334. protected onActivateRequest(msg: Message): void {
  335. this.prompt.editor.focus();
  336. this.update();
  337. }
  338. /**
  339. * Make a new prompt.
  340. */
  341. protected newPrompt(): void {
  342. let prompt = this.prompt;
  343. let input = this._input;
  344. // Make the last prompt read-only, clear its signals, and move to content.
  345. if (prompt) {
  346. prompt.readOnly = true;
  347. prompt.removeClass(PROMPT_CLASS);
  348. clearSignalData(prompt.editor);
  349. (input.layout as PanelLayout).removeWidgetAt(0);
  350. this.addCell(prompt);
  351. }
  352. // Create the new prompt.
  353. let factory = this.contentFactory;
  354. let options = this._createCodeCellOptions();
  355. prompt = factory.createPrompt(options, this);
  356. prompt.model.mimeType = this._mimetype;
  357. prompt.addClass(PROMPT_CLASS);
  358. this._input.addWidget(prompt);
  359. // Suppress the default "Enter" key handling.
  360. let editor = prompt.editor;
  361. editor.addKeydownHandler(this._onEditorKeydown);
  362. this._history.editor = editor;
  363. if (this.isAttached) {
  364. prompt.editor.focus();
  365. this.update();
  366. }
  367. this.promptCreated.emit(prompt);
  368. }
  369. /**
  370. * Handle `update-request` messages.
  371. */
  372. protected onUpdateRequest(msg: Message): void {
  373. Private.scrollToBottom(this._content.node);
  374. }
  375. /**
  376. * Handle the `'keydown'` event for the widget.
  377. */
  378. private _evtKeyDown(event: KeyboardEvent): void {
  379. let editor = this.prompt.editor;
  380. if (event.keyCode === 13 && !editor.hasFocus()) {
  381. event.preventDefault();
  382. editor.focus();
  383. }
  384. }
  385. /**
  386. * Initialize the banner and mimetype.
  387. */
  388. private _initialize(): void {
  389. let kernel = this.session.kernel;
  390. if (!kernel) {
  391. return;
  392. }
  393. kernel.ready.then(() => {
  394. if (this.isDisposed) {
  395. return;
  396. }
  397. this._handleInfo(kernel.info);
  398. });
  399. }
  400. /**
  401. * Execute the code in the current prompt.
  402. */
  403. private _execute(cell: CodeCellWidget): Promise<void> {
  404. this._history.push(cell.model.value.text);
  405. cell.model.contentChanged.connect(this.update, this);
  406. let onSuccess = (value: KernelMessage.IExecuteReplyMsg) => {
  407. if (this.isDisposed) {
  408. return;
  409. }
  410. if (value && value.content.status === 'ok') {
  411. let content = value.content as KernelMessage.IExecuteOkReply;
  412. // Use deprecated payloads for backwards compatibility.
  413. if (content.payload && content.payload.length) {
  414. let setNextInput = content.payload.filter(i => {
  415. return (i as any).source === 'set_next_input';
  416. })[0];
  417. if (setNextInput) {
  418. let text = (setNextInput as any).text;
  419. // Ignore the `replace` value and always set the next cell.
  420. cell.model.value.text = text;
  421. }
  422. }
  423. }
  424. cell.model.contentChanged.disconnect(this.update, this);
  425. this.update();
  426. this.executed.emit(new Date());
  427. };
  428. let onFailure = () => {
  429. if (this.isDisposed) {
  430. return;
  431. }
  432. cell.model.contentChanged.disconnect(this.update, this);
  433. this.update();
  434. };
  435. return cell.execute(this.session.kernel).then(onSuccess, onFailure);
  436. }
  437. /**
  438. * Update the console based on the kernel info.
  439. */
  440. private _handleInfo(info: KernelMessage.IInfoReply): void {
  441. let layout = this._content.layout as PanelLayout;
  442. let banner = layout.widgets.at(0) as RawCellWidget;
  443. banner.model.value.text = info.banner;
  444. let lang = info.language_info as nbformat.ILanguageInfoMetadata;
  445. this._mimetype = this._mimeTypeService.getMimeTypeByLanguage(lang);
  446. if (this.prompt) {
  447. this.prompt.model.mimeType = this._mimetype;
  448. }
  449. }
  450. /**
  451. * Create a new foreign cell.
  452. */
  453. private _createForeignCell(): CodeCellWidget {
  454. let factory = this.contentFactory;
  455. let options = this._createCodeCellOptions();
  456. let cell = factory.createForeignCell(options, this);
  457. cell.readOnly = true;
  458. cell.model.mimeType = this._mimetype;
  459. cell.addClass(FOREIGN_CELL_CLASS);
  460. return cell;
  461. }
  462. /**
  463. * Create the options used to initialize a code cell widget.
  464. */
  465. private _createCodeCellOptions(): CodeCellWidget.IOptions {
  466. let factory = this.contentFactory;
  467. let contentFactory = factory.codeCellContentFactory;
  468. let modelFactory = this.modelFactory;
  469. let model = modelFactory.createCodeCell({ });
  470. let rendermime = this.rendermime;
  471. return { model, rendermime, contentFactory };
  472. }
  473. /**
  474. * Handle cell disposed signals.
  475. */
  476. private _onCellDisposed(sender: Widget, args: void): void {
  477. if (!this.isDisposed) {
  478. this._cells.remove(sender as CodeCellWidget);
  479. }
  480. }
  481. /**
  482. * Test whether we should execute the prompt.
  483. */
  484. private _shouldExecute(timeout: number): Promise<boolean> {
  485. let prompt = this.prompt;
  486. let model = prompt.model;
  487. let code = model.value.text + '\n';
  488. return new Promise<boolean>((resolve, reject) => {
  489. let timer = setTimeout(() => { resolve(true); }, timeout);
  490. this.session.kernel.requestIsComplete({ code }).then(isComplete => {
  491. clearTimeout(timer);
  492. if (this.isDisposed) {
  493. resolve(false);
  494. }
  495. if (isComplete.content.status !== 'incomplete') {
  496. resolve(true);
  497. return;
  498. }
  499. model.value.text = code + isComplete.content.indent;
  500. let editor = prompt.editor;
  501. let pos = editor.getPositionAt(model.value.text.length);
  502. editor.setCursorPosition(pos);
  503. resolve(false);
  504. }).catch(() => { resolve(true); });
  505. });
  506. }
  507. /**
  508. * Handle a keydown event on an editor.
  509. */
  510. private _onEditorKeydown(editor: CodeEditor.IEditor, event: KeyboardEvent) {
  511. // Suppress "Enter" events.
  512. return event.keyCode === 13;
  513. }
  514. /**
  515. * Handle a change to the kernel.
  516. */
  517. private _onKernelChanged(sender: Session.ISession, kernel: Kernel.IKernel): void {
  518. this.clear();
  519. this._initialize();
  520. this._history.kernel = kernel;
  521. this._foreignHandler.kernel = kernel;
  522. this.newPrompt();
  523. }
  524. private _mimeTypeService: IEditorMimeTypeService;
  525. private _cells: IObservableVector<BaseCellWidget> = null;
  526. private _content: Panel = null;
  527. private _foreignHandler: ForeignHandler = null;
  528. private _history: IConsoleHistory = null;
  529. private _input: Panel = null;
  530. private _mimetype = 'text/x-ipython';
  531. }
  532. // Define the signals for the `CodeConsole` class.
  533. defineSignal(CodeConsole.prototype, 'executed');
  534. defineSignal(CodeConsole.prototype, 'promptCreated');
  535. /**
  536. * A namespace for CodeConsole statics.
  537. */
  538. export
  539. namespace CodeConsole {
  540. /**
  541. * The initialization options for a console widget.
  542. */
  543. export
  544. interface IOptions {
  545. /**
  546. * The content factory for the console widget.
  547. */
  548. contentFactory: IContentFactory;
  549. /**
  550. * The model factory for the console widget.
  551. */
  552. modelFactory?: IModelFactory;
  553. /**
  554. * The mime renderer for the console widget.
  555. */
  556. rendermime: IRenderMime;
  557. /**
  558. * The session for the console widget.
  559. */
  560. session: Session.ISession;
  561. /**
  562. * The service used to look up mime types.
  563. */
  564. mimeTypeService: IEditorMimeTypeService;
  565. }
  566. /**
  567. * A content factory for console children.
  568. */
  569. export
  570. interface IContentFactory {
  571. /**
  572. * The editor factory.
  573. */
  574. readonly editorFactory: CodeEditor.Factory;
  575. /**
  576. * The factory for code cell widget content.
  577. */
  578. readonly codeCellContentFactory: CodeCellWidget.IContentFactory;
  579. /**
  580. * The factory for raw cell widget content.
  581. */
  582. readonly rawCellContentFactory: BaseCellWidget.IContentFactory;
  583. /**
  584. * The history manager for a console widget.
  585. */
  586. createConsoleHistory(options: ConsoleHistory.IOptions): IConsoleHistory;
  587. /**
  588. * The foreign handler for a console widget.
  589. */
  590. createForeignHandler(options: ForeignHandler.IOptions):
  591. ForeignHandler;
  592. /**
  593. * Create a new banner widget.
  594. */
  595. createBanner(options: RawCellWidget.IOptions, parent: CodeConsole): RawCellWidget;
  596. /**
  597. * Create a new prompt widget.
  598. */
  599. createPrompt(options: CodeCellWidget.IOptions, parent: CodeConsole): CodeCellWidget;
  600. /**
  601. * Create a code cell whose input originated from a foreign session.
  602. */
  603. createForeignCell(options: CodeCellWidget.IOptions, parent: CodeConsole): CodeCellWidget;
  604. }
  605. /**
  606. * Default implementation of `IContentFactory`.
  607. */
  608. export
  609. class ContentFactory implements IContentFactory {
  610. /**
  611. * Create a new content factory.
  612. */
  613. constructor(options: IContentFactoryOptions) {
  614. let editorFactory = options.editorFactory;
  615. let outputAreaContentFactory = (options.outputAreaContentFactory ||
  616. OutputAreaWidget.defaultContentFactory
  617. );
  618. this.codeCellContentFactory = (options.codeCellContentFactory ||
  619. new CodeCellWidget.ContentFactory({
  620. editorFactory,
  621. outputAreaContentFactory
  622. })
  623. );
  624. this.rawCellContentFactory = (options.rawCellContentFactory ||
  625. new RawCellWidget.ContentFactory({ editorFactory })
  626. );
  627. }
  628. /**
  629. * The editor factory.
  630. */
  631. readonly editorFactory: CodeEditor.Factory;
  632. /**
  633. * The factory for code cell widget content.
  634. */
  635. readonly codeCellContentFactory: CodeCellWidget.IContentFactory;
  636. /**
  637. * The factory for raw cell widget content.
  638. */
  639. readonly rawCellContentFactory: BaseCellWidget.IContentFactory;
  640. /**
  641. * The history manager for a console widget.
  642. */
  643. createConsoleHistory(options: ConsoleHistory.IOptions): IConsoleHistory {
  644. return new ConsoleHistory(options);
  645. }
  646. /**
  647. * The foreign handler for a console widget.
  648. */
  649. createForeignHandler(options: ForeignHandler.IOptions):
  650. ForeignHandler {
  651. return new ForeignHandler(options);
  652. }
  653. /**
  654. * Create a new banner widget.
  655. */
  656. createBanner(options: RawCellWidget.IOptions, parent: CodeConsole): RawCellWidget {
  657. return new RawCellWidget(options);
  658. }
  659. /**
  660. * Create a new prompt widget.
  661. */
  662. createPrompt(options: CodeCellWidget.IOptions, parent: CodeConsole): CodeCellWidget {
  663. return new CodeCellWidget(options);
  664. }
  665. /**
  666. * Create a new code cell widget for an input from a foreign session.
  667. */
  668. createForeignCell(options: CodeCellWidget.IOptions, parent: CodeConsole): CodeCellWidget {
  669. return new CodeCellWidget(options);
  670. }
  671. }
  672. /**
  673. * An initialize options for `ContentFactory`.
  674. */
  675. export
  676. interface IContentFactoryOptions {
  677. /**
  678. * The editor factory.
  679. */
  680. editorFactory: CodeEditor.Factory;
  681. /**
  682. * The factory for output area content.
  683. */
  684. outputAreaContentFactory?: OutputAreaWidget.IContentFactory;
  685. /**
  686. * The factory for code cell widget content. If given, this will
  687. * take precedence over the `outputAreaContentFactory`.
  688. */
  689. codeCellContentFactory?: CodeCellWidget.IContentFactory;
  690. /**
  691. * The factory for raw cell widget content.
  692. */
  693. rawCellContentFactory?: BaseCellWidget.IContentFactory;
  694. }
  695. /**
  696. * A model factory for a console widget.
  697. */
  698. export
  699. interface IModelFactory {
  700. /**
  701. * The factory for code cell content.
  702. */
  703. readonly codeCellContentFactory: CodeCellModel.IContentFactory;
  704. /**
  705. * Create a new code cell.
  706. *
  707. * @param options - The options used to create the cell.
  708. *
  709. * @returns A new code cell. If a source cell is provided, the
  710. * new cell will be intialized with the data from the source.
  711. */
  712. createCodeCell(options: CodeCellModel.IOptions): ICodeCellModel;
  713. /**
  714. * Create a new raw cell.
  715. *
  716. * @param options - The options used to create the cell.
  717. *
  718. * @returns A new raw cell. If a source cell is provided, the
  719. * new cell will be intialized with the data from the source.
  720. */
  721. createRawCell(options: CellModel.IOptions): IRawCellModel;
  722. }
  723. /**
  724. * The default implementation of an `IModelFactory`.
  725. */
  726. export
  727. class ModelFactory {
  728. /**
  729. * Create a new cell model factory.
  730. */
  731. constructor(options: IModelFactoryOptions) {
  732. this.codeCellContentFactory = (options.codeCellContentFactory ||
  733. CodeCellModel.defaultContentFactory
  734. );
  735. }
  736. /**
  737. * The factory for output area models.
  738. */
  739. readonly codeCellContentFactory: CodeCellModel.IContentFactory;
  740. /**
  741. * Create a new code cell.
  742. *
  743. * @param source - The data to use for the original source data.
  744. *
  745. * @returns A new code cell. If a source cell is provided, the
  746. * new cell will be intialized with the data from the source.
  747. * If the contentFactory is not provided, the instance
  748. * `codeCellContentFactory` will be used.
  749. */
  750. createCodeCell(options: CodeCellModel.IOptions): ICodeCellModel {
  751. if (!options.contentFactory) {
  752. options.contentFactory = this.codeCellContentFactory;
  753. }
  754. return new CodeCellModel(options);
  755. }
  756. /**
  757. * Create a new raw cell.
  758. *
  759. * @param source - The data to use for the original source data.
  760. *
  761. * @returns A new raw cell. If a source cell is provided, the
  762. * new cell will be intialized with the data from the source.
  763. */
  764. createRawCell(options: CellModel.IOptions): IRawCellModel {
  765. return new RawCellModel(options);
  766. }
  767. }
  768. /**
  769. * The options used to initialize a `ModelFactory`.
  770. */
  771. export
  772. interface IModelFactoryOptions {
  773. /**
  774. * The factory for output area models.
  775. */
  776. codeCellContentFactory?: CodeCellModel.IContentFactory;
  777. }
  778. /**
  779. * The default `ModelFactory` instance.
  780. */
  781. export
  782. const defaultModelFactory = new ModelFactory({});
  783. }
  784. /**
  785. * A namespace for console widget private data.
  786. */
  787. namespace Private {
  788. /**
  789. * Jump to the bottom of a node.
  790. *
  791. * @param node - The scrollable element.
  792. */
  793. export
  794. function scrollToBottom(node: HTMLElement): void {
  795. node.scrollTop = node.scrollHeight - node.clientHeight;
  796. }
  797. }