123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- // Copyright (c) Jupyter Development Team.
- // Distributed under the terms of the Modified BSD License.
- 'use strict';
- import * as CodeMirror
- from 'codemirror';
- import {
- IKernelSpecId, IContentsOpts, IKernelId
- } from 'jupyter-js-services';
- import {
- ISignal, Signal
- } from 'phosphor-signaling';
- import {
- Widget
- } from 'phosphor-widget';
- import {
- loadModeByFileName
- } from '../codemirror';
- import {
- CodeMirrorWidget
- } from '../codemirror/widget';
- import {
- IDocumentModel, IWidgetFactory, IModelFactory, IDocumentContext,
- IKernelPreference
- } from './index';
- /**
- * The class name added to a dirty widget.
- */
- const DIRTY_CLASS = 'jp-mod-dirty';
- /**
- * The class name added to a jupyter code mirror widget.
- */
- const EDITOR_CLASS = 'jp-CodeMirrorWidget';
- /**
- * The default implementation of a document model.
- */
- export
- class DocumentModel implements IDocumentModel {
- /**
- * Construct a new document model.
- */
- constructor(languagePreference: string) {
- this._defaultLang = languagePreference;
- }
- /**
- * Get whether the model factory has been disposed.
- */
- get isDisposed(): boolean {
- return this._isDisposed;
- }
- /**
- * Dispose of the resources held by the document manager.
- */
- dispose(): void {
- this._isDisposed = true;
- }
- /**
- * A signal emitted when the document content changes.
- */
- get contentChanged(): ISignal<IDocumentModel, string> {
- return Private.contentChangedSignal.bind(this);
- }
- /**
- * A signal emitted when the document dirty state changes.
- */
- get dirtyChanged(): ISignal<IDocumentModel, boolean> {
- return Private.dirtyChangedSignal.bind(this);
- }
- /**
- * The dirty state of the document.
- */
- get dirty(): boolean {
- return this._dirty;
- }
- set dirty(value: boolean) {
- if (value === this._dirty) {
- return;
- }
- this._dirty = value;
- this.dirtyChanged.emit(value);
- }
- /**
- * The read only state of the document.
- */
- get readOnly(): boolean {
- return this._readOnly;
- }
- set readOnly(value: boolean) {
- if (value === this._readOnly) {
- return;
- }
- this._readOnly = value;
- }
- /**
- * The default kernel name of the document.
- *
- * #### Notes
- * This is a read-only property.
- */
- get defaultKernelName(): string {
- return '';
- }
- /**
- * The default kernel language of the document.
- *
- * #### Notes
- * This is a read-only property.
- */
- get defaultKernelLanguage(): string {
- return this._defaultLang;
- }
- /**
- * Serialize the model to a string.
- */
- toString(): string {
- return this._text;
- }
- /**
- * Deserialize the model from a string.
- *
- * #### Notes
- * Should emit a [contentChanged] signal.
- */
- fromString(value: string): void {
- if (this._text === value) {
- return;
- }
- this._text = value;
- this.contentChanged.emit(value);
- this.dirty = true;
- }
- /**
- * Serialize the model to JSON.
- */
- toJSON(): any {
- return JSON.stringify(this._text);
- }
- /**
- * Deserialize the model from JSON.
- *
- * #### Notes
- * Should emit a [contentChanged] signal.
- */
- fromJSON(value: any): void {
- this.fromString(JSON.parse(value));
- }
- private _text = '';
- private _defaultLang = '';
- private _dirty = false;
- private _readOnly = false;
- private _isDisposed = false;
- }
- /**
- * The default implementation of a model factory.
- */
- export
- class ModelFactory {
- /**
- * Get whether the model factory has been disposed.
- */
- get isDisposed(): boolean {
- return this._isDisposed;
- }
- /**
- * Dispose of the resources held by the document manager.
- */
- dispose(): void {
- this._isDisposed = true;
- }
- /**
- * Create a new model for a given path.
- *
- * @param languagePreference - An optional kernel language preference.
- *
- * @returns A new document model.
- */
- createNew(languagePreference?: string): IDocumentModel {
- return new DocumentModel(languagePreference);
- }
- /**
- * Get the preferred kernel language given a path.
- */
- preferredLanguage(path: string): string {
- // TODO: use a mapping of extension to language.
- return '';
- }
- private _isDisposed = false;
- }
- /**
- * A document widget for codemirrors.
- */
- export
- class EditorWidget extends CodeMirrorWidget {
- /**
- * Construct a new editor widget.
- */
- constructor(model: IDocumentModel, context: IDocumentContext) {
- super();
- this.addClass(EDITOR_CLASS);
- let editor = this.editor;
- let doc = editor.getDoc();
- doc.setValue(model.toString());
- this.title.text = context.path.split('/').pop();
- loadModeByFileName(editor, context.path);
- model.dirtyChanged.connect((m, value) => {
- if (value) {
- this.title.className += ` ${DIRTY_CLASS}`;
- } else {
- this.title.className = this.title.className.replace(DIRTY_CLASS, '');
- }
- });
- context.pathChanged.connect((c, path) => {
- loadModeByFileName(editor, path);
- this.title.text = path.split('/').pop();
- });
- model.contentChanged.connect((m, text) => {
- let old = doc.getValue();
- if (old !== text) {
- doc.setValue(text);
- }
- });
- CodeMirror.on(doc, 'change', (instance, change) => {
- if (change.origin !== 'setValue') {
- model.fromString(instance.getValue());
- }
- });
- }
- }
- /**
- * The default implemetation of a widget factory.
- */
- export
- class WidgetFactory implements IWidgetFactory<EditorWidget> {
- /**
- * Get whether the model factory has been disposed.
- */
- get isDisposed(): boolean {
- return this._isDisposed;
- }
- /**
- * Dispose of the resources held by the document manager.
- */
- dispose(): void {
- this._isDisposed = true;
- }
- /**
- * Create a new widget given a document model and a context.
- */
- createNew(model: IDocumentModel, context: IDocumentContext, kernel: IKernelId): EditorWidget {
- // TODO: if a kernel id or a name other than 'none' or 'default'
- // was given, start that kernel
- return new EditorWidget(model, context);
- }
- /**
- * Take an action on a widget before closing it.
- *
- * @returns A promise that resolves to true if the document should close
- * and false otherwise.
- */
- beforeClose(model: IDocumentModel, context: IDocumentContext, widget: Widget): Promise<boolean> {
- // TODO: handle live kernels here.
- return Promise.resolve(true);
- }
- private _isDisposed = false;
- }
- /**
- * A private namespace for data.
- */
- namespace Private {
- /**
- * A signal emitted when a document content changes.
- */
- export
- const contentChangedSignal = new Signal<IDocumentModel, string>();
- /**
- * A signal emitted when a document dirty state changes.
- */
- export
- const dirtyChangedSignal = new Signal<IDocumentModel, boolean>();
- }
|