UNPKG

jupyterlab-emrys

Version:

A computational environment for Jupyter. Powered by Emrys

636 lines (635 loc) 19.8 kB
// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. "use strict"; var jupyter_js_services_1 = require('jupyter-js-services'); var phosphor_signaling_1 = require('phosphor-signaling'); var dialog_1 = require('../dialog'); /** * An implementation of a document context. */ var Context = (function () { /** * Construct a new document context. */ function Context(manager) { this._id = ''; this._manager = null; this._manager = manager; this._id = jupyter_js_services_1.utils.uuid(); } Object.defineProperty(Context.prototype, "kernelChanged", { /** * A signal emitted when the kernel changes. */ get: function () { return Private.kernelChangedSignal.bind(this); }, enumerable: true, configurable: true }); Object.defineProperty(Context.prototype, "pathChanged", { /** * A signal emitted when the path changes. */ get: function () { return Private.pathChangedSignal.bind(this); }, enumerable: true, configurable: true }); Object.defineProperty(Context.prototype, "contentsModelChanged", { /** * A signal emitted when the model is saved or reverted. */ get: function () { return Private.contentsModelChangedSignal.bind(this); }, enumerable: true, configurable: true }); Object.defineProperty(Context.prototype, "populated", { /** * A signal emitted when the context is fully populated for the first time. */ get: function () { return Private.populatedSignal.bind(this); }, enumerable: true, configurable: true }); Object.defineProperty(Context.prototype, "id", { /** * The unique id of the context. * * #### Notes * This is a read-only property. */ get: function () { return this._id; }, enumerable: true, configurable: true }); Object.defineProperty(Context.prototype, "model", { /** * Get the model associated with the document. * * #### Notes * This is a read-only property */ get: function () { return this._manager.getModel(this._id); }, enumerable: true, configurable: true }); Object.defineProperty(Context.prototype, "kernel", { /** * The current kernel associated with the document. * * #### Notes * This is a read-only propery. */ get: function () { return this._manager.getKernel(this._id); }, enumerable: true, configurable: true }); Object.defineProperty(Context.prototype, "path", { /** * The current path associated with the document. * * #### Notes * This is a read-only property. */ get: function () { return this._manager.getPath(this._id); }, enumerable: true, configurable: true }); Object.defineProperty(Context.prototype, "contentsModel", { /** * The current contents model associated with the document * * #### Notes * This is a read-only property. The model will have an * empty `contents` field. */ get: function () { return this._manager.getContentsModel(this._id); }, enumerable: true, configurable: true }); Object.defineProperty(Context.prototype, "kernelspecs", { /** * Get the kernel spec information. * * #### Notes * This is a read-only property. */ get: function () { return this._manager.getKernelspecs(); }, enumerable: true, configurable: true }); Object.defineProperty(Context.prototype, "isPopulated", { /** * Test whether the context is fully populated. * * #### Notes * This is a read-only property. */ get: function () { return this._manager.isPopulated(this._id); }, enumerable: true, configurable: true }); Object.defineProperty(Context.prototype, "isDisposed", { /** * Test whether the context has been disposed. */ get: function () { return this._manager === null; }, enumerable: true, configurable: true }); /** * Dispose of the resources held by the context. */ Context.prototype.dispose = function () { if (this.isDisposed) { return; } this._manager = null; this._id = ''; }; /** * Change the current kernel associated with the document. */ Context.prototype.changeKernel = function (options) { return this._manager.changeKernel(this._id, options); }; /** * Save the document contents to disk. */ Context.prototype.save = function () { return this._manager.save(this._id); }; /** * Save the document to a different path chosen by the user. */ Context.prototype.saveAs = function () { return this._manager.saveAs(this._id); }; /** * Revert the document contents to disk contents. */ Context.prototype.revert = function () { return this._manager.revert(this._id); }; /** * Get the list of running sessions. */ Context.prototype.listSessions = function () { return this._manager.listSessions(); }; /** * Resolve a url to a correct server path. */ Context.prototype.resolveUrl = function (url) { return this._manager.resolveUrl(this._id, url); }; /** * Add a sibling widget to the document manager. * * @param widget - The widget to add to the document manager. * * @returns A disposable used to remove the sibling if desired. * * #### Notes * It is assumed that the widget has the same model and context * as the original widget. */ Context.prototype.addSibling = function (widget) { return this._manager.addSibling(this._id, widget); }; return Context; }()); /** * An object which manages the active contexts. */ var ContextManager = (function () { /** * Construct a new context manager. */ function ContextManager(options) { this._manager = null; this._contexts = Object.create(null); this._opener = null; this._manager = options.manager; this._opener = options.opener; } Object.defineProperty(ContextManager.prototype, "isDisposed", { /** * Get whether the context manager has been disposed. */ get: function () { return this._manager === null; }, enumerable: true, configurable: true }); /** * Dispose of the resources held by the document manager. */ ContextManager.prototype.dispose = function () { if (this.isDisposed) { return; } this._manager = null; for (var id in this._contexts) { var contextEx = this._contexts[id]; contextEx.context.dispose(); contextEx.model.dispose(); var session = contextEx.session; if (session) { session.dispose(); } } this._contexts = null; this._opener = null; }; /** * Create a new context. */ ContextManager.prototype.createNew = function (path, model, factory) { var context = new Context(this); var id = context.id; this._contexts[id] = { context: context, path: path, model: model, modelName: factory.name, fileType: factory.fileType, fileFormat: factory.fileFormat, contentsModel: null, session: null, isPopulated: false }; return id; }; /** * Get a context for a given path and model name. */ ContextManager.prototype.findContext = function (path, modelName) { for (var id in this._contexts) { var contextEx = this._contexts[id]; if (contextEx.path === path && contextEx.modelName === modelName) { return id; } } }; /** * Find a context by path. */ ContextManager.prototype.getIdsForPath = function (path) { var ids = []; for (var id in this._contexts) { if (this._contexts[id].path === path) { ids.push(id); } } return ids; }; /** * Get a context by id. */ ContextManager.prototype.getContext = function (id) { return this._contexts[id].context; }; /** * Get the model associated with a context. */ ContextManager.prototype.getModel = function (id) { return this._contexts[id].model; }; /** * Remove a context. */ ContextManager.prototype.removeContext = function (id) { var contextEx = this._contexts[id]; contextEx.model.dispose(); contextEx.context.dispose(); delete this._contexts[id]; }; /** * Get the current kernel associated with a document. */ ContextManager.prototype.getKernel = function (id) { var session = this._contexts[id].session; return session ? session.kernel : null; }; /** * Get the current path associated with a document. */ ContextManager.prototype.getPath = function (id) { return this._contexts[id].path; }; /** * Get the current contents model associated with a document. */ ContextManager.prototype.getContentsModel = function (id) { return this._contexts[id].contentsModel; }; /** * Change the current kernel associated with the document. * * @param options - If given, change the kernel (starting a session * if necessary). If falsey, shut down any existing session and return * a void promise. */ ContextManager.prototype.changeKernel = function (id, options) { var contextEx = this._contexts[id]; var session = contextEx.session; if (options) { if (session) { return session.changeKernel(options); } else { var path = contextEx.path; var sOptions = { path: path, kernelName: options.name, kernelId: options.id }; return this._startSession(id, sOptions); } } else { if (session) { return session.shutdown().then(function () { session.dispose(); contextEx.session = null; contextEx.context.kernelChanged.emit(null); return void 0; }); } else { return Promise.resolve(void 0); } } }; /** * Update the path of an open document. * * @param id - The id of the context. * * @param newPath - The new path. */ ContextManager.prototype.handleRename = function (oldPath, newPath) { // Update all of the paths, but only update one session // so there is only one REST API call. var ids = this.getIdsForPath(oldPath); var sessionUpdated = false; for (var _i = 0, ids_1 = ids; _i < ids_1.length; _i++) { var id = ids_1[_i]; var contextEx = this._contexts[id]; contextEx.path = newPath; contextEx.context.pathChanged.emit(newPath); if (!sessionUpdated) { var session = contextEx.session; if (session) { session.rename(newPath); sessionUpdated = true; } } } }; /** * Get the current kernelspec information. */ ContextManager.prototype.getKernelspecs = function () { return this._manager.kernelspecs; }; /** * Save the document contents to disk. */ ContextManager.prototype.save = function (id) { var _this = this; var contextEx = this._contexts[id]; var model = contextEx.model; var contents = contextEx.contentsModel || {}; var path = contextEx.path; contents.type = contextEx.fileType; contents.format = contextEx.fileFormat; if (model.readOnly) { return Promise.reject(new Error('Read only')); } if (contents.format === 'json') { contents.content = model.toJSON(); } else { contents.content = model.toString(); } return this._manager.contents.save(path, contents).then(function (newContents) { contextEx.contentsModel = _this._copyContentsModel(newContents); model.dirty = false; }).catch(function (err) { dialog_1.showDialog({ title: 'File Save Error', body: err.xhr.responseText, buttons: [dialog_1.okButton] }); }); }; /** * Save a document to a new file path chosen by the user. * * This results in a new session. */ ContextManager.prototype.saveAs = function (id) { var _this = this; var contextEx = this._contexts[id]; return Private.saveAs(contextEx.path).then(function (newPath) { if (!newPath) { return; } contextEx.path = newPath; contextEx.context.pathChanged.emit(newPath); if (contextEx.session) { var options = { path: newPath, kernelId: contextEx.session.kernel.id, kernelName: contextEx.session.kernel.name }; return _this._startSession(id, options).then(function () { return _this.save(id); }); } return _this.save(id); }); }; /** * Revert the contents of a path. */ ContextManager.prototype.revert = function (id) { var _this = this; var contextEx = this._contexts[id]; var opts = { format: contextEx.fileFormat, type: contextEx.fileType, content: true }; var path = contextEx.path; var model = contextEx.model; return this._manager.contents.get(path, opts).then(function (contents) { if (contents.format === 'json') { model.fromJSON(contents.content); } else { model.fromString(contents.content); } var contentsModel = _this._copyContentsModel(contents); // TODO: use deepEqual to check for equality contextEx.contentsModel = contentsModel; contextEx.context.contentsModelChanged.emit(contentsModel); model.dirty = false; }).catch(function (err) { dialog_1.showDialog({ title: 'File Load Error', body: err.xhr.responseText, buttons: [dialog_1.okButton] }); }); }; /** * Test whether the context is fully populated. */ ContextManager.prototype.isPopulated = function (id) { var contextEx = this._contexts[id]; return contextEx.isPopulated; }; /** * Finalize a context. */ ContextManager.prototype.finalize = function (id) { var contextEx = this._contexts[id]; if (contextEx.isPopulated) { return; } contextEx.isPopulated = true; this._contexts[id].context.populated.emit(void 0); }; /** * Get the list of running sessions. */ ContextManager.prototype.listSessions = function () { return this._manager.sessions.listRunning(); }; /** * Resolve a relative url to a correct server path. */ ContextManager.prototype.resolveUrl = function (id, url) { // Ignore urls that have a protocol. if (jupyter_js_services_1.utils.urlParse(url).protocol || url.indexOf('//') === 0) { return url; } var contextEx = this._contexts[id]; var cwd = jupyter_js_services_1.ContentsManager.dirname(contextEx.path); var path = jupyter_js_services_1.ContentsManager.getAbsolutePath(url, cwd); return this._manager.contents.getDownloadUrl(path); }; /** * Add a sibling widget to the document manager. */ ContextManager.prototype.addSibling = function (id, widget) { var opener = this._opener; return opener(id, widget); }; /** * Start a session and set up its signals. */ ContextManager.prototype._startSession = function (id, options) { var contextEx = this._contexts[id]; var context = contextEx.context; return this._manager.sessions.startNew(options).then(function (session) { if (contextEx.session) { contextEx.session.dispose(); } contextEx.session = session; context.kernelChanged.emit(session.kernel); session.pathChanged.connect(function (s, path) { if (path !== contextEx.path) { contextEx.path = path; context.pathChanged.emit(path); } }); session.kernelChanged.connect(function (s, kernel) { context.kernelChanged.emit(kernel); }); return session.kernel; }); }; /** * Copy the contents of a contents model, without the content. */ ContextManager.prototype._copyContentsModel = function (model) { return { path: model.path, name: model.name, type: model.type, writable: model.writable, created: model.created, last_modified: model.last_modified, mimetype: model.mimetype, format: model.format }; }; return ContextManager; }()); exports.ContextManager = ContextManager; /** * A namespace for private data. */ var Private; (function (Private) { /** * A signal emitted when the kernel changes. */ Private.kernelChangedSignal = new phosphor_signaling_1.Signal(); /** * A signal emitted when the path changes. */ Private.pathChangedSignal = new phosphor_signaling_1.Signal(); /** * A signal emitted when the contentsModel changes. */ Private.contentsModelChangedSignal = new phosphor_signaling_1.Signal(); /** * A signal emitted when the context is fully populated for the first time. */ Private.populatedSignal = new phosphor_signaling_1.Signal(); /** * Get a new file path from the user. */ function saveAs(path) { var input = document.createElement('input'); input.value = path; return dialog_1.showDialog({ title: 'Save File As..', body: input, okText: 'SAVE' }).then(function (result) { if (result.text === 'SAVE') { return input.value; } }); } Private.saveAs = saveAs; })(Private || (Private = {}));