jupyterlab-emrys
Version:
A computational environment for Jupyter. Powered by Emrys
395 lines (394 loc) • 13.3 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 phosphor_panel_1 = require('phosphor-panel');
var phosphor_signaling_1 = require('phosphor-signaling');
var phosphor_widget_1 = require('phosphor-widget');
var inspector_1 = require('../inspector');
var cells_1 = require('../notebook/cells');
var mimetype_1 = require('../notebook/common/mimetype');
var completion_1 = require('../notebook/completion');
var history_1 = require('./history');
/**
* The class name added to console widgets.
*/
var CONSOLE_CLASS = 'jp-Console';
/**
* The class name added to the console banner.
*/
var BANNER_CLASS = 'jp-Console-banner';
/**
* The class name of the active prompt
*/
var PROMPT_CLASS = 'jp-Console-prompt';
/**
* A widget containing a Jupyter console.
*/
var ConsoleWidget = (function (_super) {
__extends(ConsoleWidget, _super);
/**
* Construct a console widget.
*/
function ConsoleWidget(options) {
var _this = this;
_super.call(this);
this._completion = null;
this._completionHandler = null;
this._inspectionHandler = null;
this._mimetype = 'text/x-ipython';
this._rendermime = null;
this._renderer = null;
this._history = null;
this._session = null;
this.addClass(CONSOLE_CLASS);
var layout = new phosphor_panel_1.PanelLayout();
this.layout = layout;
this._renderer = options.renderer || ConsoleWidget.defaultRenderer;
this._rendermime = options.rendermime;
this._session = options.session;
this._history = new history_1.ConsoleHistory(this._session.kernel);
// Instantiate tab completion widget.
var completion = options.completion || new completion_1.CompletionWidget({
model: new completion_1.CompletionModel()
});
this._completion = completion;
// Set the completion widget's anchor node to peg its position.
completion.anchor = this.node;
// Because a completion widget may be passed in, check if it is attached.
if (!completion.isAttached) {
completion.attach(document.body);
}
// Set up the completion handler.
this._completionHandler = new completion_1.CellCompletionHandler(this._completion);
this._completionHandler.kernel = this._session.kernel;
// Set up the inspection handler.
this._inspectionHandler = new inspector_1.InspectionHandler(this._rendermime);
this._inspectionHandler.kernel = this._session.kernel;
// Create the banner.
var banner = this._renderer.createBanner();
banner.addClass(BANNER_CLASS);
banner.readOnly = true;
banner.model.source = '...';
layout.addChild(banner);
// Set the banner text and the mimetype.
this.initialize();
// Create the prompt.
this.newPrompt();
// Handle changes to the kernel.
this._session.kernelChanged.connect(function (s, kernel) {
_this.clear();
_this.newPrompt();
_this.initialize();
_this._history.dispose();
_this._history = new history_1.ConsoleHistory(kernel);
_this._completionHandler.kernel = kernel;
_this._inspectionHandler.kernel = kernel;
});
}
Object.defineProperty(ConsoleWidget.prototype, "inspectionHandler", {
/**
* Get the inspection handler used by the console.
*
* #### Notes
* This is a read-only property.
*/
get: function () {
return this._inspectionHandler;
},
enumerable: true,
configurable: true
});
Object.defineProperty(ConsoleWidget.prototype, "prompt", {
/*
* The last cell in a console is always a `CodeCellWidget` prompt.
*/
get: function () {
var layout = this.layout;
var last = layout.childCount() - 1;
return last > 0 ? layout.childAt(last) : null;
},
enumerable: true,
configurable: true
});
Object.defineProperty(ConsoleWidget.prototype, "session", {
/**
* Get the session used by the console.
*
* #### Notes
* This is a read-only property.
*/
get: function () {
return this._session;
},
enumerable: true,
configurable: true
});
/**
* Dispose of the resources held by the widget.
*/
ConsoleWidget.prototype.dispose = function () {
// Do nothing if already disposed.
if (this.isDisposed) {
return;
}
this._history.dispose();
this._history = null;
this._completionHandler.dispose();
this._completionHandler = null;
this._completion.dispose();
this._completion = null;
this._inspectionHandler.dispose();
this._inspectionHandler = null;
this._session.dispose();
this._session = null;
_super.prototype.dispose.call(this);
};
/**
* Execute the current prompt.
*/
ConsoleWidget.prototype.execute = function () {
var _this = this;
this.dismissCompletion();
if (this._session.status === 'dead') {
this._inspectionHandler.handleExecuteReply(null);
return;
}
var prompt = this.prompt;
prompt.trusted = true;
this._history.push(prompt.model.source);
// Create a new prompt before kernel execution to allow typeahead.
this.newPrompt();
return prompt.execute(this._session.kernel).then(function (value) {
if (!value) {
_this._inspectionHandler.handleExecuteReply(null);
return;
}
if (value.content.status === 'ok') {
var content = value.content;
_this._inspectionHandler.handleExecuteReply(content);
}
Private.scrollToBottom(_this.node);
}, function () { Private.scrollToBottom(_this.node); });
};
/**
* Clear the code cells.
*/
ConsoleWidget.prototype.clear = function () {
while (this.prompt) {
this.prompt.dispose();
}
this.newPrompt();
};
/**
* Dismiss the completion widget for a console.
*/
ConsoleWidget.prototype.dismissCompletion = function () {
this._completion.reset();
};
/**
* Serialize the output.
*/
ConsoleWidget.prototype.serialize = function () {
var output = [];
var layout = this.layout;
for (var i = 1; i < layout.childCount(); i++) {
var widget = layout.childAt(i);
output.push(widget.model.toJSON());
}
return output;
};
/**
* Handle the DOM events for the widget.
*
* @param event - The DOM event sent to the widget.
*
* #### Notes
* This method implements the DOM `EventListener` interface and is
* called in response to events on the dock panel's node. It should
* not be called directly by user code.
*/
ConsoleWidget.prototype.handleEvent = function (event) {
switch (event.type) {
case 'click':
var prompt_1 = this.prompt;
if (prompt_1) {
prompt_1.focus();
}
break;
default:
break;
}
};
/**
* Handle `after_attach` messages for the widget.
*/
ConsoleWidget.prototype.onAfterAttach = function (msg) {
this.node.addEventListener('click', this);
var prompt = this.prompt;
if (prompt) {
prompt.focus();
}
};
/**
* Handle `before_detach` messages for the widget.
*/
ConsoleWidget.prototype.onBeforeDetach = function (msg) {
this.node.removeEventListener('click', this);
};
/**
* Handle an edge requested signal.
*/
ConsoleWidget.prototype.onEdgeRequest = function (editor, location) {
var prompt = this.prompt;
if (location === 'top') {
this._history.back().then(function (value) {
if (!value) {
return;
}
prompt.model.source = value;
prompt.editor.setCursorPosition(0);
});
}
else {
this._history.forward().then(function (value) {
// If at the bottom end of history, then clear the prompt.
var text = value || '';
prompt.model.source = text;
prompt.editor.setCursorPosition(text.length);
});
}
};
/**
* Handle `update_request` messages.
*/
ConsoleWidget.prototype.onUpdateRequest = function (msg) {
var prompt = this.prompt;
Private.scrollIfNeeded(this.parent.node, prompt.node);
};
/**
* Initialize the banner and mimetype.
*/
ConsoleWidget.prototype.initialize = function () {
var _this = this;
if (this._session.kernel.info) {
this._handleInfo(this._session.kernel.info);
}
else {
this._session.kernel.kernelInfo().then(function (msg) {
_this._handleInfo(msg.content);
});
}
};
/**
* Make a new prompt.
*/
ConsoleWidget.prototype.newPrompt = function () {
// Make the previous editor read-only and clear its signals.
var prompt = this.prompt;
if (prompt) {
prompt.readOnly = true;
prompt.removeClass(PROMPT_CLASS);
phosphor_signaling_1.clearSignalData(prompt.editor);
}
// Create the new prompt and add to layout.
var layout = this.layout;
prompt = this._renderer.createPrompt(this._rendermime);
prompt.mimetype = this._mimetype;
prompt.addClass(PROMPT_CLASS);
layout.addChild(prompt);
// Hook up completion and history handling.
var editor = prompt.editor;
editor.edgeRequested.connect(this.onEdgeRequest, this);
// Associate the new prompt with the completion handler.
this._completionHandler.activeCell = prompt;
this._inspectionHandler.activeCell = prompt;
// Jump to the bottom of the console.
Private.scrollToBottom(this.node);
prompt.focus();
};
/**
* Update the console based on the kernel info.
*/
ConsoleWidget.prototype._handleInfo = function (info) {
var layout = this.layout;
var banner = layout.childAt(0);
banner.model.source = info.banner;
this._mimetype = mimetype_1.mimetypeForLanguage(info.language_info);
this.prompt.mimetype = this._mimetype;
};
return ConsoleWidget;
}(phosphor_widget_1.Widget));
exports.ConsoleWidget = ConsoleWidget;
/**
* A namespace for ConsoleWidget statics.
*/
var ConsoleWidget;
(function (ConsoleWidget) {
/**
* The default implementation of an `IRenderer`.
*/
var Renderer = (function () {
function Renderer() {
}
/**
* Create a new banner widget.
*/
Renderer.prototype.createBanner = function () {
var widget = new cells_1.RawCellWidget();
widget.model = new cells_1.RawCellModel();
return widget;
};
/**
* Create a new prompt widget.
*/
Renderer.prototype.createPrompt = function (rendermime) {
var widget = new cells_1.CodeCellWidget({ rendermime: rendermime });
widget.model = new cells_1.CodeCellModel();
return widget;
};
return Renderer;
}());
ConsoleWidget.Renderer = Renderer;
/**
* The default `IRenderer` instance.
*/
ConsoleWidget.defaultRenderer = new Renderer();
})(ConsoleWidget = exports.ConsoleWidget || (exports.ConsoleWidget = {}));
/**
* A namespace for console widget private data.
*/
var Private;
(function (Private) {
/**
* Scroll an element into view if needed.
*
* @param area - The scroll area element.
*
* @param elem - The element of interest.
*/
function scrollIfNeeded(area, elem) {
var ar = area.getBoundingClientRect();
var er = elem.getBoundingClientRect();
if (er.top < ar.top - 10) {
area.scrollTop -= ar.top - er.top + 10;
}
else if (er.bottom > ar.bottom + 10) {
area.scrollTop += er.bottom - ar.bottom + 10;
}
}
Private.scrollIfNeeded = scrollIfNeeded;
/**
* Jump to the bottom of a node.
*
* @param node - The scrollable element.
*/
function scrollToBottom(node) {
node.scrollTop = node.scrollHeight;
}
Private.scrollToBottom = scrollToBottom;
})(Private || (Private = {}));