// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. import { nbformat, utils } from '@jupyterlab/services'; import { JSONValue } from '@phosphor/coreutils'; import { ISignal, Signal } from '@phosphor/signaling'; import { CodeEditor } from '../codeeditor'; import { IChangedArgs } from '../common/interfaces'; import { IObservableJSON, ObservableJSON } from '../common/observablejson'; import { IOutputAreaModel, OutputAreaModel } from '../outputarea'; /** * The definition of a model object for a cell. */ export interface ICellModel extends CodeEditor.IModel { /** * The type of the cell. */ readonly type: nbformat.CellType; /** * A signal emitted when the content of the model changes. */ readonly contentChanged: ISignal; /** * A signal emitted when a model state changes. */ readonly stateChanged: ISignal>; /** * Whether the cell is trusted. */ trusted: boolean; /** * The metadata associated with the cell. */ readonly metadata: IObservableJSON; /** * Serialize the model to JSON. */ toJSON(): nbformat.ICell; } /** * The definition of a code cell. */ export interface ICodeCellModel extends ICellModel { /** * The type of the cell. * * #### Notes * This is a read-only property. */ type: 'code'; /** * The code cell's prompt number. Will be null if the cell has not been run. */ executionCount: nbformat.ExecutionCount; /** * The cell outputs. */ outputs: IOutputAreaModel; } /** * The definition of a markdown cell. */ export interface IMarkdownCellModel extends ICellModel { /** * The type of the cell. */ type: 'markdown'; } /** * The definition of a raw cell. */ export interface IRawCellModel extends ICellModel { /** * The type of the cell. */ type: 'raw'; } /** * An implementation of the cell model. */ export class CellModel extends CodeEditor.Model implements ICellModel { /** * Construct a cell model from optional cell content. */ constructor(options: CellModel.IOptions) { super(); this.value.changed.connect(this.onGenericChange, this); let cell = options.cell; if (!cell) { return; } this._trusted = !!cell.metadata['trusted']; delete cell.metadata['trusted']; if (Array.isArray(cell.source)) { this.value.text = (cell.source as string[]).join('\n'); } else { this.value.text = cell.source as string; } let metadata = utils.copy(cell.metadata); if (this.type !== 'raw') { delete metadata['format']; } if (this.type !== 'code') { delete metadata['collapsed']; delete metadata['scrolled']; } for (let key in metadata) { this._metadata.set(key, metadata[key]); } this._metadata.changed.connect(this.onGenericChange, this); } /** * The type of cell. */ readonly type: nbformat.CellType; /** * A signal emitted when the state of the model changes. */ readonly contentChanged = new Signal(this); /** * A signal emitted when a model state changes. */ readonly stateChanged = new Signal>(this); /** * The metadata associated with the cell. */ get metadata(): IObservableJSON { return this._metadata; } /** * Get the trusted state of the model. */ get trusted(): boolean { return this._trusted; } /** * Set the trusted state of the model. */ set trusted(newValue: boolean) { if (this._trusted === newValue) { return; } let oldValue = this._trusted; this._trusted = newValue; this.onTrustedChanged(newValue); this.stateChanged.emit({ name: 'trusted', oldValue, newValue }); } /** * Dispose of the resources held by the model. */ dispose(): void { this._metadata.dispose(); super.dispose(); } /** * Serialize the model to JSON. */ toJSON(): nbformat.ICell { let metadata: nbformat.IBaseCellMetadata = Object.create(null); for (let key of this.metadata.keys()) { let value = JSON.parse(JSON.stringify(this.metadata.get(key))); metadata[key] = value as JSONValue; } if (this.trusted) { metadata['trusted'] = true; } return { cell_type: this.type, source: this.value.text, metadata, } as nbformat.ICell; } /** * Handle a change to the trusted state. * * The default implementation is a no-op. */ onTrustedChanged(value: boolean): void { /* no-op */} /** * Handle a change to the observable value. */ protected onGenericChange(): void { this.contentChanged.emit(void 0); } private _metadata = new ObservableJSON(); private _trusted = false; } /** * The namespace for `CellModel` statics. */ export namespace CellModel { /** * The options used to initialize a `CellModel`. */ export interface IOptions { /** * The source cell data. */ cell?: nbformat.IBaseCell; } } /** * An implementation of a raw cell model. */ export class RawCellModel extends CellModel { /** * The type of the cell. */ get type(): 'raw' { return 'raw'; } } /** * An implementation of a markdown cell model. */ export class MarkdownCellModel extends CellModel { /** * Construct a markdown cell model from optional cell content. */ constructor(options: CellModel.IOptions) { super(options); // Use the Github-flavored markdown mode. this.mimeType = 'text/x-ipythongfm'; } /** * The type of the cell. */ get type(): 'markdown' { return 'markdown'; } } /** * An implementation of a code cell Model. */ export class CodeCellModel extends CellModel implements ICodeCellModel { /** * Construct a new code cell with optional original cell content. */ constructor(options: CodeCellModel.IOptions) { super(options); let factory = (options.contentFactory || CodeCellModel.defaultContentFactory ); let trusted = this.trusted; let cell = options.cell as nbformat.ICodeCell; let outputs: nbformat.IOutput[] = []; if (cell && cell.cell_type === 'code') { this.executionCount = cell.execution_count; outputs = cell.outputs; } this._outputs = factory.createOutputArea({ trusted, values: outputs }); this._outputs.stateChanged.connect(this.onGenericChange, this); } /** * The type of the cell. */ get type(): 'code' { return 'code'; } /** * The execution count of the cell. */ get executionCount(): nbformat.ExecutionCount { return this._executionCount || null; } set executionCount(newValue: nbformat.ExecutionCount) { if (newValue === this._executionCount) { return; } let oldValue = this.executionCount; this._executionCount = newValue || null; this.contentChanged.emit(void 0); this.stateChanged.emit({ name: 'executionCount', oldValue, newValue }); } /** * The cell outputs. */ get outputs(): IOutputAreaModel { return this._outputs; } /** * Dispose of the resources held by the model. */ dispose(): void { if (this.isDisposed) { return; } this._outputs.dispose(); this._outputs = null; super.dispose(); } /** * Serialize the model to JSON. */ toJSON(): nbformat.ICodeCell { let cell = super.toJSON() as nbformat.ICodeCell; cell.execution_count = this.executionCount || null; cell.outputs = this.outputs.toJSON(); return cell; } /** * Handle a change to the trusted state. * * The default implementation is a no-op. */ onTrustedChanged(value: boolean): void { this._outputs.trusted = value; } private _outputs: IOutputAreaModel = null; private _executionCount: nbformat.ExecutionCount = null; } /** * The namespace for `CodeCellModel` statics. */ export namespace CodeCellModel { /** * The options used to initialize a `CodeCellModel`. */ export interface IOptions { /** * The source cell data. */ cell?: nbformat.IBaseCell; /** * The factory for output area model creation. */ contentFactory?: IContentFactory; } /** * A factory for creating code cell model content. */ export interface IContentFactory { /** * Create an output area. */ createOutputArea(options: IOutputAreaModel.IOptions): IOutputAreaModel; } /** * The default implementation of an `IContentFactory`. */ export class ContentFactory { /** * Create an output area. */ createOutputArea(options: IOutputAreaModel.IOptions): IOutputAreaModel { return new OutputAreaModel(options); } } /** * The shared `ConetntFactory` instance. */ export const defaultContentFactory = new ContentFactory(); }