jupyterlab-emrys
Version:
A computational environment for Jupyter. Powered by Emrys
625 lines (624 loc) • 19.8 kB
JavaScript
// 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 = {}));