From 253865493b96fa9ebdd65fdfab5fbf57aef03dec Mon Sep 17 00:00:00 2001 From: Jiri Vrany Date: Wed, 6 Jun 2018 10:04:12 +0200 Subject: [PATCH 1/4] finished plugin with suggestet changes for more clean code --- packages/csvviewer-extension/src/index.ts | 83 ++++++++++++++++++----- packages/csvviewer/src/widget.ts | 16 +++++ packages/docregistry/src/registry.ts | 47 +++++++------ 3 files changed, 110 insertions(+), 36 deletions(-) diff --git a/packages/csvviewer-extension/src/index.ts b/packages/csvviewer-extension/src/index.ts index 184677f3e5d2..8c053dea552b 100644 --- a/packages/csvviewer-extension/src/index.ts +++ b/packages/csvviewer-extension/src/index.ts @@ -10,7 +10,7 @@ import { } from '@jupyterlab/apputils'; import { - CSVViewer, CSVViewerFactory + CSVViewer, CSVViewerFactory, TSVViewerFactory } from '@jupyterlab/csvviewer'; import { @@ -18,44 +18,51 @@ import { } from '@jupyterlab/docregistry'; /** - * The name of the factory that creates CSV widgets. + * The name of the factories that creates widgets. */ -const FACTORY = 'Table'; +const FACTORY_CSV = 'CSVTable'; +const FACTORY_TSV = 'TSVTable'; /** - * The table file handler extension. + * The CSV file handler extension. */ -const plugin: JupyterLabPlugin = { - activate, - id: '@jupyterlab/csvviewer-extension:plugin', + +const csv: JupyterLabPlugin = { + activate: activateCsv, + id: '@jupyterlab/csvviewer-extension:csv', requires: [ILayoutRestorer], autoStart: true }; /** - * Export the plugin as default. + * The TSV file handler extension. */ -export default plugin; +const tsv: JupyterLabPlugin = { + activate: activateTsv, + id: '@jupyterlab/csvviewer-extension:tsv', + requires: [ILayoutRestorer], + autoStart: true +}; /** - * Activate the table widget extension. + * Activate cssviewer extension for CSV files */ -function activate(app: JupyterLab, restorer: ILayoutRestorer): void { +function activateCsv(app: JupyterLab, restorer: ILayoutRestorer): void { const factory = new CSVViewerFactory({ - name: FACTORY, + name: FACTORY_CSV, fileTypes: ['csv'], defaultFor: ['csv'], readOnly: true }); - const tracker = new InstanceTracker>({ namespace: 'csvviewer' }); + const tracker = new InstanceTracker>({namespace: 'csvviewer'}); // Handle state restoration. restorer.restore(tracker, { command: 'docmanager:open', - args: widget => ({ path: widget.context.path, factory: FACTORY }), + args: widget => ({path: widget.context.path, factory: FACTORY_CSV}), name: widget => widget.context.path }); @@ -65,11 +72,55 @@ function activate(app: JupyterLab, restorer: ILayoutRestorer): void { // Track the widget. tracker.add(widget); // Notify the instance tracker if restore data needs to update. - widget.context.pathChanged.connect(() => { tracker.save(widget); }); + widget.context.pathChanged.connect(() => { + tracker.save(widget); + }); + + if (ft) { + widget.title.iconClass = ft.iconClass; + widget.title.iconLabel = ft.iconLabel; + } + }); +}; + +/** + * Activate cssviewer extension for TSV files + */ +function activateTsv(app: JupyterLab, restorer: ILayoutRestorer): void { + const factory = new TSVViewerFactory({ + name: FACTORY_TSV, + fileTypes: ['tsv'], + defaultFor: ['tsv'], + readOnly: true + }); + const tracker = new InstanceTracker>({namespace: 'tsvviewer'}); + + // Handle state restoration. + restorer.restore(tracker, { + command: 'docmanager:open', + args: widget => ({path: widget.context.path, factory: FACTORY_TSV}), + name: widget => widget.context.path + }); + + app.docRegistry.addWidgetFactory(factory); + let ft = app.docRegistry.getFileType('tsv'); + factory.widgetCreated.connect((sender, widget) => { + // Track the widget. + tracker.add(widget); + // Notify the instance tracker if restore data needs to update. + widget.context.pathChanged.connect(() => { + tracker.save(widget); + }); if (ft) { widget.title.iconClass = ft.iconClass; widget.title.iconLabel = ft.iconLabel; } }); -} +}; + +/** + * Export the plugins as default. + */ +const plugins: JupyterLabPlugin[] = [csv, tsv]; +export default plugins; diff --git a/packages/csvviewer/src/widget.ts b/packages/csvviewer/src/widget.ts index 18083f1b30e9..861d9629d421 100644 --- a/packages/csvviewer/src/widget.ts +++ b/packages/csvviewer/src/widget.ts @@ -166,6 +166,7 @@ namespace CSVViewer { } } + /** * A document widget for CSV content widgets. */ @@ -218,3 +219,18 @@ class CSVViewerFactory extends ABCWidgetFactory> { return new CSVDocumentWidget({ context }); } } + +/** + * A widget factory for TSV widgets. + */ +export +class TSVViewerFactory extends ABCWidgetFactory> { + /** + * Create a new widget given a context. + */ + protected createNewWidget(context: DocumentRegistry.Context): IDocumentWidget { + const delimiter = '\t'; + return new CSVDocumentWidget({context, delimiter}); + } +} + diff --git a/packages/docregistry/src/registry.ts b/packages/docregistry/src/registry.ts index d0052c38ea82..3525ff652a18 100644 --- a/packages/docregistry/src/registry.ts +++ b/packages/docregistry/src/registry.ts @@ -488,21 +488,21 @@ class DocumentRegistry implements IDisposable { */ getFileTypeForModel(model: Partial): DocumentRegistry.IFileType { switch (model.type) { - case 'directory': - return find(this._fileTypes, ft => ft.contentType === 'directory') || DocumentRegistry.defaultDirectoryFileType; - case 'notebook': - return find(this._fileTypes, ft => ft.contentType === 'notebook') || - DocumentRegistry.defaultNotebookFileType; - default: - // Find the best matching extension. - if (model.name || model.path) { - let name = model.name || PathExt.basename(model.path); - let fts = this.getFileTypesForPath(name); - if (fts.length > 0) { - return fts[0]; + case 'directory': + return find(this._fileTypes, ft => ft.contentType === 'directory') || DocumentRegistry.defaultDirectoryFileType; + case 'notebook': + return find(this._fileTypes, ft => ft.contentType === 'notebook') || + DocumentRegistry.defaultNotebookFileType; + default: + // Find the best matching extension. + if (model.name || model.path) { + let name = model.name || PathExt.basename(model.path); + let fts = this.getFileTypesForPath(name); + if (fts.length > 0) { + return fts[0]; + } } - } - return this.getFileType('text') || DocumentRegistry.defaultTextFileType; + return this.getFileType('text') || DocumentRegistry.defaultTextFileType; } } @@ -799,14 +799,14 @@ namespace DocumentRegistry { * A type alias for a context. */ export - type Context = IContext; + type Context = IContext; /** * A type alias for a code context. */ export - type CodeContext = IContext; + type CodeContext = IContext; /** * The options used to initialize a widget factory. @@ -898,7 +898,7 @@ namespace DocumentRegistry { * A type alias for a standard widget factory. */ export - type WidgetFactory = IWidgetFactory; + type WidgetFactory = IWidgetFactory; /** * An interface for a widget extension. @@ -915,7 +915,7 @@ namespace DocumentRegistry { * A type alias for a standard widget extension. */ export - type WidgetExtension = IWidgetExtension; + type WidgetExtension = IWidgetExtension; /** * The interface for a model factory. @@ -956,13 +956,13 @@ namespace DocumentRegistry { * A type alias for a standard model factory. */ export - type ModelFactory = IModelFactory; + type ModelFactory = IModelFactory; /** * A type alias for a code model factory. */ export - type CodeModelFactory = IModelFactory; + type CodeModelFactory = IModelFactory; /** * An interface for a file type. @@ -1126,6 +1126,13 @@ namespace DocumentRegistry { mimeTypes: ['text/csv'], iconClass: 'jp-MaterialIcon jp-SpreadsheetIcon' }, + { + name: 'tsv', + displayName: 'TSV File', + extensions: ['.tsv'], + mimeTypes: ['text/csv'], + iconClass: 'jp-MaterialIcon jp-SpreadsheetIcon' + }, { name: 'r', displayName: 'R File', From 6b7bc7fcdc83eaa506adb17b5d5211b5a35abd61 Mon Sep 17 00:00:00 2001 From: Jiri Vrany Date: Wed, 6 Jun 2018 11:15:45 +0200 Subject: [PATCH 2/4] reformat registry.ts - fix editor formating mistakes --- packages/docregistry/src/registry.ts | 99 +++++++++++----------------- 1 file changed, 38 insertions(+), 61 deletions(-) diff --git a/packages/docregistry/src/registry.ts b/packages/docregistry/src/registry.ts index 3525ff652a18..03da8af48cc5 100644 --- a/packages/docregistry/src/registry.ts +++ b/packages/docregistry/src/registry.ts @@ -26,7 +26,7 @@ import { } from '@phosphor/widgets'; import { - IClientSession, Toolbar + IClientSession } from '@jupyterlab/apputils'; import { @@ -488,21 +488,21 @@ class DocumentRegistry implements IDisposable { */ getFileTypeForModel(model: Partial): DocumentRegistry.IFileType { switch (model.type) { - case 'directory': - return find(this._fileTypes, ft => ft.contentType === 'directory') || DocumentRegistry.defaultDirectoryFileType; - case 'notebook': - return find(this._fileTypes, ft => ft.contentType === 'notebook') || - DocumentRegistry.defaultNotebookFileType; - default: - // Find the best matching extension. - if (model.name || model.path) { - let name = model.name || PathExt.basename(model.path); - let fts = this.getFileTypesForPath(name); - if (fts.length > 0) { - return fts[0]; - } + case 'directory': + return find(this._fileTypes, ft => ft.contentType === 'directory') || DocumentRegistry.defaultDirectoryFileType; + case 'notebook': + return find(this._fileTypes, ft => ft.contentType === 'notebook') || + DocumentRegistry.defaultNotebookFileType; + default: + // Find the best matching extension. + if (model.name || model.path) { + let name = model.name || PathExt.basename(model.path); + let fts = this.getFileTypesForPath(name); + if (fts.length > 0) { + return fts[0]; } - return this.getFileType('text') || DocumentRegistry.defaultTextFileType; + } + return this.getFileType('text') || DocumentRegistry.defaultTextFileType; } } @@ -646,15 +646,6 @@ namespace DocumentRegistry { * Should emit a [contentChanged] signal. */ fromJSON(value: any): void; - - /** - * Initialize model state after initial data load. - * - * #### Notes - * This function must be called after the initial data is loaded to set up - * initial model state, such as an initial undo stack, etc. - */ - initialize(): void; } /** @@ -684,7 +675,7 @@ namespace DocumentRegistry { disposed: ISignal; /** - * The data model for the document. + * Get the model associated with the document. */ readonly model: T; @@ -706,11 +697,11 @@ namespace DocumentRegistry { readonly localPath: string; /** - * The document metadata, stored as a services contents model. + * The current contents model associated with the document * * #### Notes - * This will be null until the context is 'ready'. Since we only store - * metadata here, the `.contents` attribute will always be empty. + * The contents model will be null until the context is ready. + * It will have an empty `contents` field. */ readonly contentsModel: Contents.IModel | null; @@ -799,14 +790,14 @@ namespace DocumentRegistry { * A type alias for a context. */ export - type Context = IContext; + type Context = IContext; /** * A type alias for a code context. */ export - type CodeContext = IContext; + type CodeContext = IContext; /** * The options used to initialize a widget factory. @@ -849,6 +840,17 @@ namespace DocumentRegistry { readonly canStartKernel?: boolean; } + /** + * A widget for a document. + */ + export + interface IReadyWidget extends Widget { + /** + * A promise that resolves when the widget is ready. + */ + readonly ready: Promise; + } + /** * The options used to open a widget. */ @@ -879,9 +881,9 @@ namespace DocumentRegistry { * The interface for a widget factory. */ export - interface IWidgetFactory extends IDisposable, IWidgetFactoryOptions { + interface IWidgetFactory extends IDisposable, IWidgetFactoryOptions { /** - * A signal emitted when a new widget is created. + * A signal emitted when a widget is created. */ widgetCreated: ISignal, T>; @@ -898,7 +900,7 @@ namespace DocumentRegistry { * A type alias for a standard widget factory. */ export - type WidgetFactory = IWidgetFactory; + type WidgetFactory = IWidgetFactory; /** * An interface for a widget extension. @@ -915,7 +917,7 @@ namespace DocumentRegistry { * A type alias for a standard widget extension. */ export - type WidgetExtension = IWidgetExtension; + type WidgetExtension = IWidgetExtension; /** * The interface for a model factory. @@ -956,13 +958,13 @@ namespace DocumentRegistry { * A type alias for a standard model factory. */ export - type ModelFactory = IModelFactory; + type ModelFactory = IModelFactory; /** * A type alias for a code model factory. */ export - type CodeModelFactory = IModelFactory; + type CodeModelFactory = IModelFactory; /** * An interface for a file type. @@ -1198,31 +1200,6 @@ namespace DocumentRegistry { ]; } -/** - * An interface for a document widget. - */ -export -interface IDocumentWidget extends Widget { - /** - * The content widget. - */ - readonly content: T; - - /** - * A promise resolving after the content widget is revealed. - */ - readonly revealed: Promise; - - /** - * The context associated with the document. - */ - readonly context: DocumentRegistry.IContext; - - /** - * The toolbar for the widget. - */ - readonly toolbar: Toolbar; -} /** * A private namespace for DocumentRegistry data. From 15f3e67d96db38180bc0b0ae36e81d84b2dbee76 Mon Sep 17 00:00:00 2001 From: Jiri Vrany Date: Wed, 6 Jun 2018 11:21:01 +0200 Subject: [PATCH 3/4] reformat registry.ts - fix editor formating mistakes --- packages/docregistry/src/registry.ts | 61 +++++++++++++++++++--------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/packages/docregistry/src/registry.ts b/packages/docregistry/src/registry.ts index 03da8af48cc5..43f8cd6adf8f 100644 --- a/packages/docregistry/src/registry.ts +++ b/packages/docregistry/src/registry.ts @@ -26,7 +26,7 @@ import { } from '@phosphor/widgets'; import { - IClientSession + IClientSession, Toolbar } from '@jupyterlab/apputils'; import { @@ -646,6 +646,15 @@ namespace DocumentRegistry { * Should emit a [contentChanged] signal. */ fromJSON(value: any): void; + + /** + * Initialize model state after initial data load. + * + * #### Notes + * This function must be called after the initial data is loaded to set up + * initial model state, such as an initial undo stack, etc. + */ + initialize(): void; } /** @@ -675,7 +684,7 @@ namespace DocumentRegistry { disposed: ISignal; /** - * Get the model associated with the document. + * The data model for the document. */ readonly model: T; @@ -697,11 +706,11 @@ namespace DocumentRegistry { readonly localPath: string; /** - * The current contents model associated with the document + * The document metadata, stored as a services contents model. * * #### Notes - * The contents model will be null until the context is ready. - * It will have an empty `contents` field. + * This will be null until the context is 'ready'. Since we only store + * metadata here, the `.contents` attribute will always be empty. */ readonly contentsModel: Contents.IModel | null; @@ -840,17 +849,6 @@ namespace DocumentRegistry { readonly canStartKernel?: boolean; } - /** - * A widget for a document. - */ - export - interface IReadyWidget extends Widget { - /** - * A promise that resolves when the widget is ready. - */ - readonly ready: Promise; - } - /** * The options used to open a widget. */ @@ -881,9 +879,9 @@ namespace DocumentRegistry { * The interface for a widget factory. */ export - interface IWidgetFactory extends IDisposable, IWidgetFactoryOptions { + interface IWidgetFactory extends IDisposable, IWidgetFactoryOptions { /** - * A signal emitted when a widget is created. + * A signal emitted when a new widget is created. */ widgetCreated: ISignal, T>; @@ -900,7 +898,7 @@ namespace DocumentRegistry { * A type alias for a standard widget factory. */ export - type WidgetFactory = IWidgetFactory; + type WidgetFactory = IWidgetFactory; /** * An interface for a widget extension. @@ -1200,6 +1198,31 @@ namespace DocumentRegistry { ]; } +/** + * An interface for a document widget. + */ +export +interface IDocumentWidget extends Widget { + /** + * The content widget. + */ + readonly content: T; + + /** + * A promise resolving after the content widget is revealed. + */ + readonly revealed: Promise; + + /** + * The context associated with the document. + */ + readonly context: DocumentRegistry.IContext; + + /** + * The toolbar for the widget. + */ + readonly toolbar: Toolbar; +} /** * A private namespace for DocumentRegistry data. From 1199aea7749905e2d6e289965c405cd30cb75cce Mon Sep 17 00:00:00 2001 From: Jiri Vrany Date: Thu, 7 Jun 2018 00:00:52 +0200 Subject: [PATCH 4/4] fixed unnecessary semicolons in cssviewer-extension --- packages/csvviewer-extension/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/csvviewer-extension/src/index.ts b/packages/csvviewer-extension/src/index.ts index 8c053dea552b..09884a903843 100644 --- a/packages/csvviewer-extension/src/index.ts +++ b/packages/csvviewer-extension/src/index.ts @@ -81,7 +81,7 @@ function activateCsv(app: JupyterLab, restorer: ILayoutRestorer): void { widget.title.iconLabel = ft.iconLabel; } }); -}; +} /** * Activate cssviewer extension for TSV files @@ -117,7 +117,7 @@ function activateTsv(app: JupyterLab, restorer: ILayoutRestorer): void { widget.title.iconLabel = ft.iconLabel; } }); -}; +} /** * Export the plugins as default.