UNPKG

jupyterlab-emrys

Version:

A computational environment for Jupyter. Powered by Emrys

625 lines (624 loc) 19.8 kB
// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var codemirror_1 = require('../../codemirror'); var phosphor_panel_1 = require('phosphor-panel'); var phosphor_signaling_1 = require('phosphor-signaling'); var phosphor_widget_1 = require('phosphor-widget'); var output_area_1 = require('../output-area'); var editor_1 = require('./editor'); /** * The class name added to cell widgets. */ var CELL_CLASS = 'jp-Cell'; /** * The class name added to input area widgets. */ var INPUT_CLASS = 'jp-InputArea'; /** * The class name added to the prompt area of cell. */ var PROMPT_CLASS = 'jp-InputArea-prompt'; /** * The class name added to the editor area of the cell. */ var EDITOR_CLASS = 'jp-InputArea-editor'; /** * The class name added to the cell when collapsed. */ var COLLAPSED_CLASS = 'jp-mod-collapsed'; /** * The class name added to the cell when readonly. */ var READONLY_CLASS = 'jp-mod-readOnly'; /** * The class name added to code cells. */ var CODE_CELL_CLASS = 'jp-CodeCell'; /** * The class name added to markdown cells. */ var MARKDOWN_CELL_CLASS = 'jp-MarkdownCell'; /** * The class name added to the rendered markdown widget. */ var MARKDOWN_CONTENT_CLASS = 'jp-MarkdownCell-content'; /** * The class name added to raw cells. */ var RAW_CELL_CLASS = 'jp-RawCell'; /** * The class name added to a rendered markdown cell. */ var RENDERED_CLASS = 'jp-mod-rendered'; /** * The text applied to an empty markdown cell. */ var DEFAULT_MARKDOWN_TEXT = 'Type Markdown and LaTeX: $ α^2 $'; /** * A base cell widget. */ var BaseCellWidget = (function (_super) { __extends(BaseCellWidget, _super); /** * Construct a new base cell widget. */ function BaseCellWidget(options) { if (options === void 0) { options = {}; } _super.call(this); this._input = null; this._editor = null; this._model = null; this._mimetype = 'text/plain'; this._readOnly = false; this._trustedCursor = null; this._trusted = false; this.addClass(CELL_CLASS); this.layout = new phosphor_panel_1.PanelLayout(); var renderer = options.renderer || BaseCellWidget.defaultRenderer; this._editor = renderer.createCellEditor({ indentUnit: 4, readOnly: false, theme: 'default', extraKeys: { 'Cmd-Right': 'goLineRight', 'End': 'goLineRight', 'Cmd-Left': 'goLineLeft', 'Tab': 'indentMore', 'Shift-Tab': 'indentLess', 'Cmd-Alt-[': 'indentAuto', 'Ctrl-Alt-[': 'indentAuto', 'Cmd-/': 'toggleComment', 'Ctrl-/': 'toggleComment', } }); this._input = renderer.createInputArea(this._editor); this.layout.addChild(this._input); } Object.defineProperty(BaseCellWidget.prototype, "model", { /** * The model used by the widget. */ get: function () { return this._model; }, set: function (newValue) { if (!newValue && !this._model || newValue === this._model) { return; } var oldValue = this._model; this._model = newValue; // Trigger private, protected, and public updates. this._onModelChanged(oldValue, newValue); this.onModelChanged(oldValue, newValue); this.modelChanged.emit(void 0); }, enumerable: true, configurable: true }); Object.defineProperty(BaseCellWidget.prototype, "modelChanged", { /** * A signal emitted when the model of the cell changes. */ get: function () { return Private.modelChangedSignal.bind(this); }, enumerable: true, configurable: true }); Object.defineProperty(BaseCellWidget.prototype, "editor", { /** * Get the editor widget used by the cell. * * #### Notes * This is a ready-only property. */ get: function () { return this._editor; }, enumerable: true, configurable: true }); Object.defineProperty(BaseCellWidget.prototype, "mimetype", { /** * The mimetype used by the cell. */ get: function () { return this._mimetype; }, set: function (value) { if (!value) { return; } if (this._mimetype === value) { return; } this._mimetype = value; codemirror_1.loadModeByMIME(this.editor.editor, value); }, enumerable: true, configurable: true }); Object.defineProperty(BaseCellWidget.prototype, "readOnly", { /** * The read only state of the cell. */ get: function () { return this._readOnly; }, set: function (value) { if (value === this._readOnly) { return; } this._readOnly = value; this.update(); }, enumerable: true, configurable: true }); Object.defineProperty(BaseCellWidget.prototype, "trusted", { /** * The trusted state of the cell. */ get: function () { return this._trusted; }, set: function (value) { if (!this._model) { return; } this._trustedCursor.setValue(value); this._trusted = value; }, enumerable: true, configurable: true }); /** * Focus the widget. */ BaseCellWidget.prototype.focus = function () { this.editor.editor.focus(); }; /** * Set the prompt for the widget. */ BaseCellWidget.prototype.setPrompt = function (value) { this._input.setPrompt(value); }; /** * Toggle whether the input area is shown. */ BaseCellWidget.prototype.toggleInput = function (value) { if (value) { this._input.show(); this.focus(); } else { this._input.hide(); } }; /** * Dispose of the resources held by the widget. */ BaseCellWidget.prototype.dispose = function () { // Do nothing if already disposed. if (this.isDisposed) { return; } this._model = null; this._input = null; this._editor = null; this._trustedCursor = null; _super.prototype.dispose.call(this); }; /** * Handle `after-attach` messages. */ BaseCellWidget.prototype.onAfterAttach = function (msg) { this.update(); }; /** * Handle `update_request` messages. */ BaseCellWidget.prototype.onUpdateRequest = function (msg) { if (!this._model) { return; } // Handle read only state. var option = this._readOnly ? 'nocursor' : false; this.editor.editor.setOption('readOnly', option); this.toggleClass(READONLY_CLASS, this._readOnly); }; /** * Handle changes in the model. * * #### Notes * Subclasses may reimplement this method as needed. */ BaseCellWidget.prototype.onModelStateChanged = function (model, args) { }; /** * Handle changes in the model. */ BaseCellWidget.prototype.onMetadataChanged = function (model, args) { switch (args.name) { case 'trusted': this._trusted = !!this._trustedCursor.getValue(); this.update(); break; default: break; } }; /** * Handle a new model. * * #### Notes * This method is called after the model change has been handled * internally and before the `modelChanged` signal is emitted. * The default implementation is a no-op. */ BaseCellWidget.prototype.onModelChanged = function (oldValue, newValue) { }; BaseCellWidget.prototype._onModelChanged = function (oldValue, newValue) { // If the model is being replaced, disconnect the old signal handler. if (oldValue) { oldValue.stateChanged.disconnect(this.onModelStateChanged, this); oldValue.metadataChanged.disconnect(this.onMetadataChanged, this); } // Reset the editor model and set its mode to be the default MIME type. this._editor.model = this._model; codemirror_1.loadModeByMIME(this._editor.editor, this._mimetype); // Handle trusted cursor. this._trustedCursor = this._model.getMetadata('trusted'); this._trusted = !!this._trustedCursor.getValue(); // Connect signal handlers. this._model.metadataChanged.connect(this.onMetadataChanged, this); this._model.stateChanged.connect(this.onModelStateChanged, this); }; return BaseCellWidget; }(phosphor_widget_1.Widget)); exports.BaseCellWidget = BaseCellWidget; /** * The namespace for the `BaseCellWidget` class statics. */ var BaseCellWidget; (function (BaseCellWidget) { /** * The default implementation of an `IRenderer`. */ var Renderer = (function () { function Renderer() { } /** * Create a new cell editor for the widget. */ Renderer.prototype.createCellEditor = function (options) { return new editor_1.CellEditorWidget(options); }; /** * Create a new input area for the widget. */ Renderer.prototype.createInputArea = function (editor) { return new InputAreaWidget(editor); }; return Renderer; }()); BaseCellWidget.Renderer = Renderer; /** * The default `IRenderer` instance. */ BaseCellWidget.defaultRenderer = new Renderer(); })(BaseCellWidget = exports.BaseCellWidget || (exports.BaseCellWidget = {})); /** * A widget for a code cell. */ var CodeCellWidget = (function (_super) { __extends(CodeCellWidget, _super); /** * Construct a code cell widget. */ function CodeCellWidget(options) { _super.call(this, options); this._rendermime = null; this._output = null; this._collapsedCursor = null; this._scrolledCursor = null; this.editor.editor.setOption('matchBrackets', true); this.editor.editor.setOption('autoCloseBrackets', true); this.addClass(CODE_CELL_CLASS); this._rendermime = options.rendermime; this._renderer = options.renderer || CodeCellWidget.defaultRenderer; } /** * Dispose of the resources used by the widget. */ CodeCellWidget.prototype.dispose = function () { if (this.isDisposed) { return; } this._collapsedCursor = null; this._scrolledCursor = null; this._output = null; _super.prototype.dispose.call(this); }; /** * Execute the cell given a kernel. */ CodeCellWidget.prototype.execute = function (kernel) { var model = this.model; var code = model.source; if (!code.trim()) { model.executionCount = null; return Promise.resolve(null); } model.executionCount = null; this.setPrompt('*'); this.trusted = true; var outputs = model.outputs; return outputs.execute(code, kernel).then(function (reply) { model.executionCount = reply.content.execution_count; return reply; }); }; /** * Handle `update_request` messages. */ CodeCellWidget.prototype.onUpdateRequest = function (msg) { if (this._collapsedCursor) { this.toggleClass(COLLAPSED_CLASS, this._collapsedCursor.getValue()); } if (this._output) { // TODO: handle scrolled state. this._output.trusted = this.trusted; } _super.prototype.onUpdateRequest.call(this, msg); }; /** * Handle the widget receiving a new model. */ CodeCellWidget.prototype.onModelChanged = function (oldValue, newValue) { var model = newValue; var renderer = this._renderer; if (!this._output) { this._output = renderer.createOutputArea(this._rendermime); this.layout.addChild(this._output); } this._output.model = model.outputs; this._output.trusted = this.trusted; this._collapsedCursor = model.getMetadata('collapsed'); this._scrolledCursor = model.getMetadata('scrolled'); this.setPrompt("" + model.executionCount); }; /** * Handle changes in the model. */ CodeCellWidget.prototype.onModelStateChanged = function (model, args) { switch (args.name) { case 'executionCount': this.setPrompt("" + model.executionCount); break; default: break; } _super.prototype.onModelStateChanged.call(this, model, args); }; /** * Handle changes in the metadata. */ CodeCellWidget.prototype.onMetadataChanged = function (model, args) { switch (args.name) { case 'collapsed': case 'scrolled': this.update(); break; default: break; } _super.prototype.onMetadataChanged.call(this, model, args); }; return CodeCellWidget; }(BaseCellWidget)); exports.CodeCellWidget = CodeCellWidget; /** * The namespace for the `CodeCellWidget` class statics. */ var CodeCellWidget; (function (CodeCellWidget) { /** * The default implementation of an `IRenderer`. */ var Renderer = (function (_super) { __extends(Renderer, _super); function Renderer() { _super.apply(this, arguments); } /** * Create an output area widget. */ Renderer.prototype.createOutputArea = function (rendermime) { return new output_area_1.OutputAreaWidget({ rendermime: rendermime }); }; return Renderer; }(BaseCellWidget.Renderer)); CodeCellWidget.Renderer = Renderer; /** * The default `IRenderer` instance. */ CodeCellWidget.defaultRenderer = new Renderer(); })(CodeCellWidget = exports.CodeCellWidget || (exports.CodeCellWidget = {})); /** * A widget for a Markdown cell. * * #### Notes * Things get complicated if we want the rendered text to update * any time the text changes, the text editor model changes, * or the input area model changes. We don't support automatically * updating the rendered text in all of these cases. */ var MarkdownCellWidget = (function (_super) { __extends(MarkdownCellWidget, _super); /** * Construct a Markdown cell widget. */ function MarkdownCellWidget(options) { _super.call(this, options); this._rendermime = null; this._markdownWidget = null; this._rendered = true; this._prev = ''; this.addClass(MARKDOWN_CELL_CLASS); // Insist on the Github-flavored markdown mode. this.mimetype = 'text/x-ipythongfm'; this._rendermime = options.rendermime; this._markdownWidget = new phosphor_widget_1.Widget(); this._markdownWidget.addClass(MARKDOWN_CONTENT_CLASS); this.layout.addChild(this._markdownWidget); // Turn on line wrapping for markdown cells. this.editor.editor.setOption('lineWrapping', true); } Object.defineProperty(MarkdownCellWidget.prototype, "rendered", { /** * Whether the cell is rendered. */ get: function () { return this._rendered; }, set: function (value) { if (value === this._rendered) { return; } this._rendered = value; this.update(); }, enumerable: true, configurable: true }); /** * Dispose of the resource held by the widget. */ MarkdownCellWidget.prototype.dispose = function () { if (this.isDisposed) { return; } this._markdownWidget = null; _super.prototype.dispose.call(this); }; /** * Handle `update_request` messages. */ MarkdownCellWidget.prototype.onUpdateRequest = function (msg) { var model = this.model; if (this.rendered) { var text = model && model.source || DEFAULT_MARKDOWN_TEXT; // Do not re-render if the text has not changed. if (text !== this._prev) { var bundle = { 'text/markdown': text }; this._markdownWidget.dispose(); var widget = this._rendermime.render(bundle, this.trusted); this._markdownWidget = widget || new phosphor_widget_1.Widget(); this._markdownWidget.addClass(MARKDOWN_CONTENT_CLASS); this.layout.addChild(this._markdownWidget); } else { this._markdownWidget.show(); } this._prev = text; this.toggleInput(false); this.addClass(RENDERED_CLASS); } else { this._markdownWidget.hide(); this.toggleInput(true); this.removeClass(RENDERED_CLASS); } _super.prototype.onUpdateRequest.call(this, msg); }; return MarkdownCellWidget; }(BaseCellWidget)); exports.MarkdownCellWidget = MarkdownCellWidget; /** * A widget for a raw cell. */ var RawCellWidget = (function (_super) { __extends(RawCellWidget, _super); /** * Construct a raw cell widget. */ function RawCellWidget(options) { if (options === void 0) { options = {}; } _super.call(this, options); this.addClass(RAW_CELL_CLASS); // Turn on line wrapping for raw cells. this.editor.editor.setOption('lineWrapping', true); } return RawCellWidget; }(BaseCellWidget)); exports.RawCellWidget = RawCellWidget; /** * An input area widget, which hosts a prompt and an editor widget. */ var InputAreaWidget = (function (_super) { __extends(InputAreaWidget, _super); /** * Construct an input area widget. */ function InputAreaWidget(editor) { _super.call(this); this.addClass(INPUT_CLASS); editor.addClass(EDITOR_CLASS); this.layout = new phosphor_panel_1.PanelLayout(); var prompt = new phosphor_widget_1.Widget(); prompt.addClass(PROMPT_CLASS); var layout = this.layout; layout.addChild(prompt); layout.addChild(editor); } /** * Set the prompt of the input area. */ InputAreaWidget.prototype.setPrompt = function (value) { var prompt = this.layout.childAt(0); if (value === 'null') { value = ' '; } var text = "In [" + (value || ' ') + "]:"; prompt.node.textContent = text; }; return InputAreaWidget; }(phosphor_widget_1.Widget)); exports.InputAreaWidget = InputAreaWidget; /** * A namespace for private data. */ var Private; (function (Private) { /** * A signal emitted when the model changes on a cell widget. */ Private.modelChangedSignal = new phosphor_signaling_1.Signal(); })(Private || (Private = {}));