Files
azuredatastudio/src/sql/parts/notebook/notebookInput.ts
Kevin Cunnane cac8cc99e1 Notebook extensibility: Move New Notebook and configuration to an extension (#3382)
initial support for Notebook extensibility. Fixes #3148 , Fixes #3382.

## Design notes
The extensibility patterns are modeled after the VSCode Document and Editor APIs but need to be different since core editor concepts are different - for example Notebooks have cells, and cells have contents rather than editors which have text lines.

Most importantly, a lot of the code is based on the MainThreadDocumentsAndEditors class, with some related classes (the MainThreadDocuments, and MainThreadEditors) brought in too. Given our current limitations I felt moving to add 3 full sets of extension host API classes was overkill so am currently using one. Will see if we need to change this in the future based on what we add in the additional APIs

## Limitations
The current implementation is limited to visible editors, rather than all documents in the workspace. We are not following the `openDocument` -> `showDocument` pattern, but instead just supporting `showDocument` directly.

## Changes in this PR
- Renamed existing APIs to make clear that they were about notebook contents, not about notebook behavior
- Added new APIs for querying notebook documents and editors 
- Added new API for opening a notebook
- Moved `New Notebook` command to an extension, and added an `Open Notebook` command too
- Moved notebook feature flag to the extension

## Not covered in this PR
- Need to actually implement support for defining the provider and connection IDs for a notebook. this will be important to support New Notebook from a big data connection in Object Explorer
- Need to add APIs for adding cells, to support 
- Need to implement the metadata for getting full notebook contents. I've only implemented to key APIs needed to make this all work.
2018-12-03 18:50:44 -08:00

172 lines
4.6 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { IEditorModel } from 'vs/platform/editor/common/editor';
import { EditorInput, EditorModel, ConfirmResult } from 'vs/workbench/common/editor';
import { Emitter, Event } from 'vs/base/common/event';
import URI from 'vs/base/common/uri';
import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import * as resources from 'vs/base/common/resources';
import { INotebookService } from 'sql/services/notebook/notebookService';
export type ModeViewSaveHandler = (handle: number) => Thenable<boolean>;
export let notebooksEnabledCondition = ContextKeyExpr.equals('config.notebook.enabled', true);
export class NotebookInputModel extends EditorModel {
private dirty: boolean;
private readonly _onDidChangeDirty: Emitter<void> = this._register(new Emitter<void>());
private _providerId: string;
constructor(public readonly notebookUri: URI, private readonly handle: number, private _isTrusted: boolean = false, private saveHandler?: ModeViewSaveHandler) {
super();
this.dirty = false;
}
public get providerId(): string {
return this._providerId;
}
public set providerId(value: string) {
this._providerId = value;
}
get isTrusted(): boolean {
return this._isTrusted;
}
get onDidChangeDirty(): Event<void> {
return this._onDidChangeDirty.event;
}
get isDirty(): boolean {
return this.dirty;
}
public setDirty(dirty: boolean): void {
if (this.dirty === dirty) {
return;
}
this.dirty = dirty;
this._onDidChangeDirty.fire();
}
save(): TPromise<boolean> {
if (this.saveHandler) {
return TPromise.wrap(this.saveHandler(this.handle));
}
return TPromise.wrap(true);
}
}
export class NotebookInputValidator {
constructor(@IContextKeyService private readonly _contextKeyService: IContextKeyService) {}
public isNotebookEnabled(): boolean {
return this._contextKeyService.contextMatchesRules(notebooksEnabledCondition);
}
}
export class NotebookInput extends EditorInput {
public static ID: string = 'workbench.editorinputs.notebookInput';
public hasBootstrapped = false;
// Holds the HTML content for the editor when the editor discards this input and loads another
private _parentContainer: HTMLElement;
constructor(private _title: string,
private _model: NotebookInputModel,
@INotebookService private notebookService: INotebookService
) {
super();
this._model.onDidChangeDirty(() => this._onDidChangeDirty.fire());
}
public get notebookUri(): URI {
return this._model.notebookUri;
}
public get providerId(): string {
return this._model.providerId;
}
public getTypeId(): string {
return NotebookInput.ID;
}
public resolve(refresh?: boolean): TPromise<IEditorModel> {
return undefined;
}
public getName(): string {
if (!this._title) {
this._title = resources.basenameOrAuthority(this._model.notebookUri);
}
return this._title;
}
public get isTrusted(): boolean {
return this._model.isTrusted;
}
public dispose(): void {
this._disposeContainer();
super.dispose();
}
private _disposeContainer() {
if (!this._parentContainer) {
return;
}
let parentNode = this._parentContainer.parentNode;
if (parentNode) {
parentNode.removeChild(this._parentContainer);
this._parentContainer = null;
}
}
set container(container: HTMLElement) {
this._disposeContainer();
this._parentContainer = container;
}
get container(): HTMLElement {
return this._parentContainer;
}
/**
* An editor that is dirty will be asked to be saved once it closes.
*/
isDirty(): boolean {
return this._model.isDirty;
}
/**
* Subclasses should bring up a proper dialog for the user if the editor is dirty and return the result.
*/
confirmSave(): TPromise<ConfirmResult> {
// TODO #2530 support save on close / confirm save. This is significantly more work
// as we need to either integrate with textFileService (seems like this isn't viable)
// or register our own complimentary service that handles the lifecycle operations such
// as close all, auto save etc.
return TPromise.wrap(ConfirmResult.DONT_SAVE);
}
/**
* Saves the editor if it is dirty. Subclasses return a promise with a boolean indicating the success of the operation.
*/
save(): TPromise<boolean> {
return this._model.save();
}
}