UNPKG

@jupyterlab/cells

Version:
472 lines 14.2 kB
/* ----------------------------------------------------------------------------- | Copyright (c) Jupyter Development Team. | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ import { Signal } from '@lumino/signaling'; import { AttachmentsModel } from '@jupyterlab/attachments'; import { CodeEditor } from '@jupyterlab/codeeditor'; import { OutputAreaModel } from '@jupyterlab/outputarea'; import { createMutex, createStandaloneCell } from '@jupyter/ydoc'; const globalModelDBMutex = createMutex(); export function isCodeCellModel(model) { return model.type === 'code'; } export function isMarkdownCellModel(model) { return model.type === 'markdown'; } export function isRawCellModel(model) { return model.type === 'raw'; } /** * An implementation of the cell model. */ export class CellModel extends CodeEditor.Model { constructor(options = {}) { const { cell_type, sharedModel, ...others } = options; super({ sharedModel: sharedModel !== null && sharedModel !== void 0 ? sharedModel : createStandaloneCell({ cell_type: cell_type !== null && cell_type !== void 0 ? cell_type : 'raw', id: options.id }), ...others }); /** * A signal emitted when the state of the model changes. */ this.contentChanged = new Signal(this); /** * A signal emitted when a model state changes. */ this.stateChanged = new Signal(this); this._metadataChanged = new Signal(this); this._trusted = false; this.standaloneModel = typeof options.sharedModel === 'undefined'; this.trusted = !!this.getMetadata('trusted') || !!options.trusted; this.sharedModel.changed.connect(this.onGenericChange, this); this.sharedModel.metadataChanged.connect(this._onMetadataChanged, this); } /** * Signal emitted when cell metadata changes. */ get metadataChanged() { return this._metadataChanged; } /** * The id for the cell. */ get id() { return this.sharedModel.getId(); } /** * The metadata associated with the cell. */ get metadata() { return this.sharedModel.metadata; } /** * The trusted state of the model. */ get trusted() { return this._trusted; } set trusted(newValue) { const oldValue = this.trusted; if (oldValue !== newValue) { this._trusted = newValue; this.onTrustedChanged(this, { newValue, oldValue }); } } /** * Dispose of the resources held by the model. */ dispose() { if (this.isDisposed) { return; } this.sharedModel.changed.disconnect(this.onGenericChange, this); this.sharedModel.metadataChanged.disconnect(this._onMetadataChanged, this); super.dispose(); } /** * Handle a change to the trusted state. * * The default implementation is a no-op. */ onTrustedChanged(trusted, args) { /* no-op */ } /** * Delete a metadata * * @param key Metadata key */ deleteMetadata(key) { return this.sharedModel.deleteMetadata(key); } /** * Get a metadata * * ### Notes * This returns a copy of the key value. * * @param key Metadata key */ getMetadata(key) { return this.sharedModel.getMetadata(key); } /** * Set a metadata * * @param key Metadata key * @param value Metadata value */ setMetadata(key, value) { if (typeof value === 'undefined') { this.sharedModel.deleteMetadata(key); } else { this.sharedModel.setMetadata(key, value); } } /** * Serialize the model to JSON. */ toJSON() { return this.sharedModel.toJSON(); } /** * Handle a change to the observable value. */ onGenericChange() { this.contentChanged.emit(void 0); } _onMetadataChanged(sender, change) { this._metadataChanged.emit(change); } } /** * A base implementation for cell models with attachments. */ export class AttachmentsCellModel extends CellModel { /** * Construct a new cell with optional attachments. */ constructor(options) { var _a; super(options); const factory = (_a = options.contentFactory) !== null && _a !== void 0 ? _a : AttachmentsCellModel.defaultContentFactory; const values = this.sharedModel.getAttachments(); this._attachments = factory.createAttachmentsModel({ values }); this._attachments.stateChanged.connect(this.onGenericChange, this); this._attachments.changed.connect(this._onAttachmentsChange, this); this.sharedModel.changed.connect(this._onSharedModelChanged, this); } /** * Get the attachments of the model. */ get attachments() { return this._attachments; } /** * Dispose of the resources held by the model. */ dispose() { if (this.isDisposed) { return; } this._attachments.stateChanged.disconnect(this.onGenericChange, this); this._attachments.changed.disconnect(this._onAttachmentsChange, this); this._attachments.dispose(); this.sharedModel.changed.disconnect(this._onSharedModelChanged, this); super.dispose(); } /** * Serialize the model to JSON. */ toJSON() { return super.toJSON(); } /** * Handle a change to the cell outputs modelDB and reflect it in the shared model. */ _onAttachmentsChange(sender, event) { const cell = this.sharedModel; globalModelDBMutex(() => cell.setAttachments(sender.toJSON())); } /** * Handle a change to the code cell value. */ _onSharedModelChanged(slot, change) { if (change.attachmentsChange) { const cell = this.sharedModel; globalModelDBMutex(() => { var _a; return this._attachments.fromJSON((_a = cell.getAttachments()) !== null && _a !== void 0 ? _a : {}); }); } } } /** * The namespace for `AttachmentsCellModel` statics. */ (function (AttachmentsCellModel) { /** * The default implementation of an `IContentFactory`. */ class ContentFactory { /** * Create an attachments model. */ createAttachmentsModel(options) { return new AttachmentsModel(options); } } AttachmentsCellModel.ContentFactory = ContentFactory; /** * The shared `ContentFactory` instance. */ AttachmentsCellModel.defaultContentFactory = new ContentFactory(); })(AttachmentsCellModel || (AttachmentsCellModel = {})); /** * An implementation of a raw cell model. */ export class RawCellModel extends AttachmentsCellModel { /** * Construct a raw cell model from optional shared model. */ constructor(options = {}) { super({ cell_type: 'raw', ...options }); } /** * The type of the cell. */ get type() { return 'raw'; } /** * Serialize the model to JSON. */ toJSON() { return super.toJSON(); } } /** * An implementation of a markdown cell model. */ export class MarkdownCellModel extends AttachmentsCellModel { /** * Construct a markdown cell model from optional shared model. */ constructor(options = {}) { super({ cell_type: 'markdown', ...options }); // Use the Github-flavored markdown mode. this.mimeType = 'text/x-ipythongfm'; } /** * The type of the cell. */ get type() { return 'markdown'; } /** * Serialize the model to JSON. */ toJSON() { return super.toJSON(); } } /** * An implementation of a code cell Model. */ export class CodeCellModel extends CellModel { /** * Construct a new code cell with optional original cell content. */ constructor(options = {}) { var _a; super({ cell_type: 'code', ...options }); this._executedCode = ''; this._isDirty = false; const factory = (_a = options === null || options === void 0 ? void 0 : options.contentFactory) !== null && _a !== void 0 ? _a : CodeCellModel.defaultContentFactory; const trusted = this.trusted; const outputs = this.sharedModel.getOutputs(); this._outputs = factory.createOutputArea({ trusted, values: outputs }); this.sharedModel.changed.connect(this._onSharedModelChanged, this); this._outputs.changed.connect(this.onGenericChange, this); this._outputs.changed.connect(this.onOutputsChange, this); } /** * The type of the cell. */ get type() { return 'code'; } /** * The execution count of the cell. */ get executionCount() { return this.sharedModel.execution_count || null; } set executionCount(newValue) { this.sharedModel.execution_count = newValue || null; } /** * Whether the cell is dirty or not. * * A cell is dirty if it is output is not empty and does not * result of the input code execution. */ get isDirty() { // Test could be done dynamically with this._executedCode // but for performance reason, the diff status is stored in a boolean. return this._isDirty; } /** * The cell outputs. */ get outputs() { return this._outputs; } clearExecution() { this.outputs.clear(); this.executionCount = null; this._setDirty(false); this.sharedModel.deleteMetadata('execution'); // We trust this cell as it no longer has any outputs. this.trusted = true; } /** * Dispose of the resources held by the model. */ dispose() { if (this.isDisposed) { return; } this.sharedModel.changed.disconnect(this._onSharedModelChanged, this); this._outputs.changed.disconnect(this.onGenericChange, this); this._outputs.changed.disconnect(this.onOutputsChange, this); this._outputs.dispose(); this._outputs = null; super.dispose(); } /** * Handle a change to the trusted state. */ onTrustedChanged(trusted, args) { const newTrusted = args.newValue; if (this._outputs) { this._outputs.trusted = newTrusted; } if (newTrusted) { const codeCell = this.sharedModel; const metadata = codeCell.getMetadata(); metadata.trusted = true; codeCell.setMetadata(metadata); } this.stateChanged.emit({ name: 'trusted', oldValue: args.oldValue, newValue: newTrusted }); } /** * Serialize the model to JSON. */ toJSON() { return super.toJSON(); } /** * Handle a change to the cell outputs modelDB and reflect it in the shared model. */ onOutputsChange(sender, event) { const codeCell = this.sharedModel; globalModelDBMutex(() => { switch (event.type) { case 'add': { const outputs = event.newValues.map(output => output.toJSON()); codeCell.updateOutputs(event.newIndex, event.newIndex, outputs); break; } case 'set': { const newValues = event.newValues.map(output => output.toJSON()); codeCell.updateOutputs(event.oldIndex, event.oldIndex + newValues.length, newValues); break; } case 'remove': codeCell.updateOutputs(event.oldIndex, event.oldValues.length); break; default: throw new Error(`Invalid event type: ${event.type}`); } }); } /** * Handle a change to the code cell value. */ _onSharedModelChanged(slot, change) { if (change.outputsChange) { globalModelDBMutex(() => { this.outputs.clear(); slot.getOutputs().forEach(output => this._outputs.add(output)); }); } if (change.executionCountChange) { if (change.executionCountChange.newValue && (this.isDirty || !change.executionCountChange.oldValue)) { this._setDirty(false); } this.stateChanged.emit({ name: 'executionCount', oldValue: change.executionCountChange.oldValue, newValue: change.executionCountChange.newValue }); } if (change.sourceChange && this.executionCount !== null) { this._setDirty(this._executedCode !== this.sharedModel.getSource().trim()); } } /** * Set whether the cell is dirty or not. */ _setDirty(v) { if (!v) { this._executedCode = this.sharedModel.getSource().trim(); } if (v !== this._isDirty) { this._isDirty = v; this.stateChanged.emit({ name: 'isDirty', oldValue: !v, newValue: v }); } } } /** * The namespace for `CodeCellModel` statics. */ (function (CodeCellModel) { /** * The default implementation of an `IContentFactory`. */ class ContentFactory { /** * Create an output area. */ createOutputArea(options) { return new OutputAreaModel(options); } } CodeCellModel.ContentFactory = ContentFactory; /** * The shared `ContentFactory` instance. */ CodeCellModel.defaultContentFactory = new ContentFactory(); })(CodeCellModel || (CodeCellModel = {})); //# sourceMappingURL=model.js.map