mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-25 09:35:37 -05:00
* #3920: Notebooks file save * Missed in merge * #4290: Untitled save and native dirty implementation * Misc changes * Content Manager, notebooks extension and commented failed unit tests * Removing modelLoaded event
This commit is contained in:
@@ -14,4 +14,5 @@ export const RestoreFeatureName = 'restore';
|
||||
export const BackupFeatureName = 'backup';
|
||||
|
||||
export const MssqlProviderId = 'MSSQL';
|
||||
export const notebookModeId = 'notebook';
|
||||
|
||||
|
||||
@@ -13,10 +13,11 @@ import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput';
|
||||
import { QueryInput } from 'sql/parts/query/common/queryInput';
|
||||
import { IQueryEditorOptions } from 'sql/workbench/services/queryEditor/common/queryEditorService';
|
||||
import { QueryPlanInput } from 'sql/parts/queryPlan/queryPlanInput';
|
||||
import { NotebookInput, NotebookInputModel } from 'sql/parts/notebook/notebookInput';
|
||||
import { NotebookInput, NotebookEditorModel } from 'sql/parts/notebook/notebookInput';
|
||||
import { DEFAULT_NOTEBOOK_PROVIDER, INotebookService } from 'sql/workbench/services/notebook/common/notebookService';
|
||||
import { getProvidersForFileName, getStandardKernelsForProvider } from 'sql/parts/notebook/notebookUtils';
|
||||
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
|
||||
import { notebookModeId } from 'sql/common/constants';
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
@@ -27,7 +28,6 @@ export const untitledFilePrefix = 'SQLQuery';
|
||||
|
||||
// mode identifier for SQL mode
|
||||
export const sqlModeId = 'sql';
|
||||
export const notebookModeId = 'notebook';
|
||||
|
||||
/**
|
||||
* Checks if the specified input is supported by one our custom input types, and if so convert it
|
||||
@@ -63,16 +63,8 @@ export function convertEditorInput(input: EditorInput, options: IQueryEditorOpti
|
||||
let providerIds: string[] = [DEFAULT_NOTEBOOK_PROVIDER];
|
||||
if (input) {
|
||||
fileName = input.getName();
|
||||
providerIds = getProvidersForFileName(fileName, notebookService);
|
||||
}
|
||||
let notebookInputModel = new NotebookInputModel(uri, undefined, false, undefined);
|
||||
notebookInputModel.providerId = providerIds.filter(provider => provider !== DEFAULT_NOTEBOOK_PROVIDER)[0];
|
||||
notebookInputModel.providers = providerIds;
|
||||
notebookInputModel.providers.forEach(provider => {
|
||||
let standardKernels = getStandardKernelsForProvider(provider, notebookService);
|
||||
notebookInputModel.standardKernels = standardKernels;
|
||||
});
|
||||
let notebookInput: NotebookInput = instantiationService.createInstance(NotebookInput, fileName, notebookInputModel);
|
||||
let notebookInput: NotebookInput = instantiationService.createInstance(NotebookInput, fileName, uri);
|
||||
return notebookInput;
|
||||
});
|
||||
}
|
||||
@@ -257,4 +249,4 @@ export function getFileMode(instantiationService: IInstantiationService, resourc
|
||||
}
|
||||
return sqlModeId;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,15 +183,15 @@ export class QueryTextEditor extends BaseTextEditor {
|
||||
|
||||
public toggleEditorSelected(selected: boolean): void {
|
||||
this._selected = selected;
|
||||
this.refreshEditorConfguration();
|
||||
this.refreshEditorConfiguration();
|
||||
}
|
||||
|
||||
public set hideLineNumbers(value: boolean) {
|
||||
this._hideLineNumbers = value;
|
||||
this.refreshEditorConfguration();
|
||||
this.refreshEditorConfiguration();
|
||||
}
|
||||
|
||||
private refreshEditorConfguration(configuration = this.configurationService.getValue<IEditorConfiguration>(this.getResource())): void {
|
||||
private refreshEditorConfiguration(configuration = this.configurationService.getValue<IEditorConfiguration>(this.getResource())): void {
|
||||
if (!this.getControl()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -369,6 +369,11 @@ export interface INotebookModel {
|
||||
*/
|
||||
saveModel(): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Serialize notebook cell content to JSON
|
||||
*/
|
||||
toJSON(): nb.INotebookContents;
|
||||
|
||||
/**
|
||||
* Notifies the notebook of a change in the cell
|
||||
*/
|
||||
@@ -455,6 +460,12 @@ export interface IModelFactory {
|
||||
createClientSession(options: IClientSessionOptions): IClientSession;
|
||||
}
|
||||
|
||||
export interface IContentManager {
|
||||
/**
|
||||
* This is a specialized method intended to load for a default context - just the current Notebook's URI
|
||||
*/
|
||||
loadContent(): Promise<nb.INotebookContents>;
|
||||
}
|
||||
|
||||
export interface INotebookModelOptions {
|
||||
/**
|
||||
@@ -467,6 +478,7 @@ export interface INotebookModelOptions {
|
||||
*/
|
||||
factory: IModelFactory;
|
||||
|
||||
contentManager: IContentManager;
|
||||
notebookManagers: INotebookManager[];
|
||||
providerId: string;
|
||||
standardKernels: IStandardKernelWithProvider[];
|
||||
@@ -498,4 +510,4 @@ export interface ICellMagicMapper {
|
||||
|
||||
export namespace notebookConstants {
|
||||
export const SQL = 'SQL';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,19 +8,18 @@
|
||||
import { nb, connection } from 'azdata';
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { Event, Emitter, forEach } from 'vs/base/common/event';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
import { CellModel } from './cell';
|
||||
import { IClientSession, INotebookModel, IDefaultConnection, INotebookModelOptions, ICellModel, notebookConstants, NotebookContentChange } from './modelInterfaces';
|
||||
import { NotebookChangeType, CellType, CellTypes } from 'sql/parts/notebook/models/contracts';
|
||||
import { IClientSession, INotebookModel, IDefaultConnection, INotebookModelOptions, ICellModel, NotebookContentChange } from './modelInterfaces';
|
||||
import { NotebookChangeType, CellType } from 'sql/parts/notebook/models/contracts';
|
||||
import { nbversion } from '../notebookConstants';
|
||||
import * as notebookUtils from '../notebookUtils';
|
||||
import { INotebookManager, SQL_NOTEBOOK_PROVIDER, DEFAULT_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/common/notebookService';
|
||||
import { NotebookContexts } from 'sql/parts/notebook/models/notebookContexts';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { INotification, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
@@ -130,6 +129,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
return this._contentChangedEmitter.event;
|
||||
}
|
||||
|
||||
|
||||
public get isSessionReady(): boolean {
|
||||
return !!this._activeClientSession;
|
||||
}
|
||||
@@ -253,10 +253,11 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
try {
|
||||
this._trustedMode = isTrusted;
|
||||
let contents = null;
|
||||
if (this._notebookOptions.notebookUri.scheme !== Schemas.untitled) {
|
||||
// TODO: separate ContentManager from NotebookManager
|
||||
contents = await this.notebookManagers[0].contentManager.getNotebookContents(this._notebookOptions.notebookUri);
|
||||
|
||||
if (this._notebookOptions && this._notebookOptions.contentManager) {
|
||||
contents = await this._notebookOptions.contentManager.loadContent();
|
||||
}
|
||||
|
||||
let factory = this._notebookOptions.factory;
|
||||
// if cells already exist, create them with language info (if it is saved)
|
||||
this._cells = [];
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<text-cell-component *ngIf="cell.cellType === 'markdown'" [cellModel]="cell" [model]="model" [activeCellId]="activeCellId">
|
||||
</text-cell-component>
|
||||
</div>
|
||||
<div class="notebook-cell" *ngIf="!cells.length && !isLoading">
|
||||
<div class="notebook-cell" *ngIf="(!cells || !cells.length) && !isLoading">
|
||||
<placeholder-cell-component [cellModel]="cell" [model]="model">
|
||||
</placeholder-cell-component>
|
||||
</div>
|
||||
|
||||
@@ -194,7 +194,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
this._model.cells.forEach(cell => {
|
||||
cell.trustedMode = isTrusted;
|
||||
});
|
||||
this.setDirty(true);
|
||||
//TODO: Handle dirty for trust?
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
|
||||
@@ -253,6 +253,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
connectionService: this.connectionManagementService,
|
||||
notificationService: this.notificationService,
|
||||
notebookManagers: this.notebookManagers,
|
||||
contentManager: this._notebookParams.input.contentManager,
|
||||
standardKernels: this._notebookParams.input.standardKernels,
|
||||
cellMagicMapper: new CellMagicMapper(this.notebookService.languageMagics),
|
||||
providerId: 'sql', // this is tricky; really should also depend on the connection profile
|
||||
@@ -321,7 +322,6 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
|
||||
private handleContentChanged(change: NotebookContentChange) {
|
||||
// Note: for now we just need to set dirty state and refresh the UI.
|
||||
this.setDirty(true);
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,38 +8,148 @@
|
||||
import * as nls from 'vs/nls';
|
||||
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 { EditorInput, EditorModel } from 'vs/workbench/common/editor';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
import { IStandardKernelWithProvider } from 'sql/parts/notebook/notebookUtils';
|
||||
import { INotebookService, INotebookEditor } from 'sql/workbench/services/notebook/common/notebookService';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { IStandardKernelWithProvider, getProvidersForFileName, getStandardKernelsForProvider } from 'sql/parts/notebook/notebookUtils';
|
||||
import { INotebookService, DEFAULT_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/common/notebookService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { INotebookModel, IContentManager } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { notebookModeId } from 'sql/common/constants';
|
||||
import { LocalContentManager } from 'sql/workbench/services/notebook/node/localContentManager';
|
||||
|
||||
export type ModeViewSaveHandler = (handle: number) => Thenable<boolean>;
|
||||
|
||||
|
||||
export class NotebookInputModel extends EditorModel {
|
||||
export class NotebookEditorModel extends EditorModel {
|
||||
private dirty: boolean;
|
||||
private readonly _onDidChangeDirty: Emitter<void> = this._register(new Emitter<void>());
|
||||
private _providerId: string;
|
||||
private _standardKernels: IStandardKernelWithProvider[];
|
||||
private _defaultKernel: azdata.nb.IKernelSpec;
|
||||
constructor(public readonly notebookUri: URI,
|
||||
private readonly handle: number,
|
||||
private _isTrusted: boolean = false,
|
||||
private saveHandler?: ModeViewSaveHandler,
|
||||
provider?: string,
|
||||
private _providers?: string[],
|
||||
private _connectionProfileId?: string) {
|
||||
|
||||
private textEditorModel: TextFileEditorModel | UntitledEditorModel,
|
||||
@INotebookService private notebookService: INotebookService
|
||||
) {
|
||||
super();
|
||||
this.dirty = false;
|
||||
this._providerId = provider;
|
||||
this._register(this.notebookService.onNotebookEditorAdd(notebook => {
|
||||
if (notebook.id === this.notebookUri.toString()) {
|
||||
// Hook to content change events
|
||||
notebook.modelReady.then(() => {
|
||||
this._register(notebook.model.contentChanged(e => this.updateModel()));
|
||||
this._register(notebook.model.kernelChanged(e => this.updateModel()));
|
||||
}, err => undefined);
|
||||
}
|
||||
}));
|
||||
|
||||
if (this.textEditorModel instanceof UntitledEditorModel) {
|
||||
this._register(this.textEditorModel.onDidChangeDirty(e => this.setDirty(this.textEditorModel.isDirty())));
|
||||
} else {
|
||||
this._register(this.textEditorModel.onDidStateChange(e => this.setDirty(this.textEditorModel.isDirty())));
|
||||
}
|
||||
this.dirty = this.textEditorModel.isDirty();
|
||||
}
|
||||
|
||||
public get contentString(): string {
|
||||
let model = this.textEditorModel.textEditorModel;
|
||||
return model.getValue();
|
||||
}
|
||||
|
||||
get isDirty(): boolean {
|
||||
return this.textEditorModel.isDirty();
|
||||
}
|
||||
|
||||
public setDirty(dirty: boolean): void {
|
||||
if (this.dirty === dirty) {
|
||||
return;
|
||||
}
|
||||
this.dirty = dirty;
|
||||
this._onDidChangeDirty.fire();
|
||||
}
|
||||
|
||||
public updateModel(): void {
|
||||
let notebookModel = this.getNotebookModel();
|
||||
if (notebookModel && this.textEditorModel && this.textEditorModel.textEditorModel) {
|
||||
let content = JSON.stringify(notebookModel.toJSON(), undefined, ' ');
|
||||
let model = this.textEditorModel.textEditorModel;
|
||||
let endLine = model.getLineCount();
|
||||
let endCol = model.getLineLength(endLine);
|
||||
this.textEditorModel.textEditorModel.applyEdits([{
|
||||
range: new Range(1, 1, endLine, endCol),
|
||||
text: content
|
||||
}]);
|
||||
}
|
||||
}
|
||||
|
||||
isModelCreated(): boolean {
|
||||
return this.getNotebookModel() !== undefined;
|
||||
}
|
||||
|
||||
private getNotebookModel(): INotebookModel {
|
||||
let editor = this.notebookService.listNotebookEditors().find(n => n.id === this.notebookUri.toString());
|
||||
if (editor) {
|
||||
return editor.model;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
get onDidChangeDirty(): Event<void> {
|
||||
return this._onDidChangeDirty.event;
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookInput extends EditorInput {
|
||||
public static ID: string = 'workbench.editorinputs.notebookInput';
|
||||
private _providerId: string;
|
||||
private _providers: string[];
|
||||
private _standardKernels: IStandardKernelWithProvider[];
|
||||
private _connectionProfileId: string;
|
||||
private _defaultKernel: azdata.nb.IKernelSpec;
|
||||
private _isTrusted: boolean = false;
|
||||
public hasBootstrapped = false;
|
||||
// Holds the HTML content for the editor when the editor discards this input and loads another
|
||||
private _parentContainer: HTMLElement;
|
||||
private readonly _layoutChanged: Emitter<void> = this._register(new Emitter<void>());
|
||||
private _model: NotebookEditorModel;
|
||||
private _untitledEditorService: IUntitledEditorService;
|
||||
private _contentManager: IContentManager;
|
||||
|
||||
constructor(private _title: string,
|
||||
private resource: URI,
|
||||
@ITextModelService private textModelService: ITextModelService,
|
||||
@IUntitledEditorService untitledEditorService: IUntitledEditorService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@INotebookService private notebookService: INotebookService
|
||||
) {
|
||||
super();
|
||||
this._untitledEditorService = untitledEditorService;
|
||||
this.resource = resource;
|
||||
this._standardKernels = [];
|
||||
this.assignProviders();
|
||||
}
|
||||
|
||||
public get notebookUri(): URI {
|
||||
return this.resource;
|
||||
}
|
||||
|
||||
public get contentManager(): IContentManager {
|
||||
if (!this._contentManager) {
|
||||
this._contentManager = new NotebookEditorContentManager(this);
|
||||
}
|
||||
return this._contentManager;
|
||||
}
|
||||
|
||||
public getName(): string {
|
||||
if (!this._title) {
|
||||
this._title = resources.basenameOrAuthority(this.resource);
|
||||
}
|
||||
return this._title;
|
||||
}
|
||||
|
||||
public get providerId(): string {
|
||||
@@ -50,12 +160,16 @@ export class NotebookInputModel extends EditorModel {
|
||||
this._providerId = value;
|
||||
}
|
||||
|
||||
public get providers(): string[] {
|
||||
return this._providers;
|
||||
public get isTrusted(): boolean {
|
||||
return this._isTrusted;
|
||||
}
|
||||
|
||||
public set providers(value: string[]) {
|
||||
this._providers = value;
|
||||
public set isTrusted(value: boolean) {
|
||||
this._isTrusted = value;
|
||||
}
|
||||
|
||||
public set connectionProfileId(value: string) {
|
||||
this._connectionProfileId = value;
|
||||
}
|
||||
|
||||
public get connectionProfileId(): string {
|
||||
@@ -66,6 +180,14 @@ export class NotebookInputModel extends EditorModel {
|
||||
return this._standardKernels;
|
||||
}
|
||||
|
||||
public get providers(): string[] {
|
||||
return this._providers;
|
||||
}
|
||||
|
||||
public set providers(value: string[]) {
|
||||
this._providers = value;
|
||||
}
|
||||
|
||||
public set standardKernels(value: IStandardKernelWithProvider[]) {
|
||||
value.forEach(kernel => {
|
||||
this._standardKernels.push({
|
||||
@@ -84,76 +206,6 @@ export class NotebookInputModel extends EditorModel {
|
||||
this._defaultKernel = kernel;
|
||||
}
|
||||
|
||||
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 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;
|
||||
private readonly _layoutChanged: Emitter<void> = this._register(new Emitter<void>());
|
||||
constructor(private _title: string,
|
||||
private _model: NotebookInputModel,
|
||||
@INotebookService private notebookService: INotebookService,
|
||||
@IDialogService private dialogService: IDialogService
|
||||
) {
|
||||
super();
|
||||
this._model.onDidChangeDirty(() => this._onDidChangeDirty.fire());
|
||||
}
|
||||
|
||||
public get notebookUri(): URI {
|
||||
return this._model.notebookUri;
|
||||
}
|
||||
|
||||
public get providerId(): string {
|
||||
return this._model.providerId;
|
||||
}
|
||||
|
||||
public get providers(): string[] {
|
||||
return this._model.providers;
|
||||
}
|
||||
|
||||
public get connectionProfileId(): string {
|
||||
return this._model.connectionProfileId;
|
||||
}
|
||||
|
||||
public get standardKernels(): IStandardKernelWithProvider[] {
|
||||
return this._model.standardKernels;
|
||||
}
|
||||
|
||||
public get defaultKernel(): azdata.nb.IKernelSpec {
|
||||
return this._model.defaultKernel;
|
||||
}
|
||||
|
||||
get layoutChanged(): Event<void> {
|
||||
return this._layoutChanged.event;
|
||||
}
|
||||
@@ -166,20 +218,38 @@ export class NotebookInput extends EditorInput {
|
||||
return NotebookInput.ID;
|
||||
}
|
||||
|
||||
public resolve(refresh?: boolean): TPromise<IEditorModel> {
|
||||
return undefined;
|
||||
getResource(): URI {
|
||||
return this.resource;
|
||||
}
|
||||
|
||||
public getName(): string {
|
||||
if (!this._title) {
|
||||
this._title = resources.basenameOrAuthority(this._model.notebookUri);
|
||||
async resolve(): TPromise<NotebookEditorModel> {
|
||||
if (this._model && this._model.isModelCreated()) {
|
||||
return TPromise.as(this._model);
|
||||
} else {
|
||||
let textOrUntitledEditorModel: UntitledEditorModel | IEditorModel;
|
||||
if (this.resource.scheme === Schemas.untitled) {
|
||||
textOrUntitledEditorModel = await this._untitledEditorService.loadOrCreate({ resource: this.resource, modeId: notebookModeId });
|
||||
}
|
||||
else {
|
||||
const textEditorModelReference = await this.textModelService.createModelReference(this.resource);
|
||||
textOrUntitledEditorModel = await textEditorModelReference.object.load();
|
||||
}
|
||||
this._model = this.instantiationService.createInstance(NotebookEditorModel, this.resource, textOrUntitledEditorModel);
|
||||
this._model.onDidChangeDirty(() => this._onDidChangeDirty.fire());
|
||||
return this._model;
|
||||
}
|
||||
|
||||
return this._title;
|
||||
}
|
||||
|
||||
public get isTrusted(): boolean {
|
||||
return this._model.isTrusted;
|
||||
private assignProviders(): void {
|
||||
let providerIds: string[] = getProvidersForFileName(this._title, this.notebookService);
|
||||
if (providerIds && providerIds.length > 0) {
|
||||
this._providerId = providerIds.filter(provider => provider !== DEFAULT_NOTEBOOK_PROVIDER)[0];
|
||||
this._providers = providerIds;
|
||||
this._providers.forEach(provider => {
|
||||
let standardKernels = getStandardKernelsForProvider(provider, this.notebookService);
|
||||
this._standardKernels = standardKernels;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
@@ -212,50 +282,10 @@ export class NotebookInput extends EditorInput {
|
||||
* 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.
|
||||
const message = nls.localize('saveChangesMessage', "Do you want to save the changes you made to {0}?", this.getTitle());
|
||||
const buttons: string[] = [
|
||||
nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save"),
|
||||
nls.localize({ key: 'dontSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save"),
|
||||
nls.localize('cancel', "Cancel")
|
||||
];
|
||||
|
||||
return this.dialogService.show(Severity.Warning, message, buttons, {
|
||||
cancelId: 2,
|
||||
detail: nls.localize('saveChangesDetail', "Your changes will be lost if you don't save them.")
|
||||
}).then(index => {
|
||||
switch (index) {
|
||||
case 0: return ConfirmResult.SAVE;
|
||||
case 1: return ConfirmResult.DONT_SAVE;
|
||||
default: return ConfirmResult.CANCEL;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the editor if it is dirty. Subclasses return a promise with a boolean indicating the success of the operation.
|
||||
*/
|
||||
save(): TPromise<boolean> {
|
||||
let activeEditor: INotebookEditor;
|
||||
for (const editor of this.notebookService.listNotebookEditors()) {
|
||||
if (editor.isActive()) {
|
||||
activeEditor = editor;
|
||||
}
|
||||
if (this._model) {
|
||||
return this._model.isDirty;
|
||||
}
|
||||
if (activeEditor) {
|
||||
return TPromise.wrap(activeEditor.save().then((val) => { return val; }));
|
||||
}
|
||||
return TPromise.wrap(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,9 +293,14 @@ export class NotebookInput extends EditorInput {
|
||||
* @param isDirty boolean value to set editor dirty
|
||||
*/
|
||||
setDirty(isDirty: boolean): void {
|
||||
this._model.setDirty(isDirty);
|
||||
if (this._model) {
|
||||
this._model.setDirty(isDirty);
|
||||
}
|
||||
}
|
||||
|
||||
updateModel(): void {
|
||||
this._model.updateModel();
|
||||
}
|
||||
|
||||
public matches(otherInput: any): boolean {
|
||||
if (super.matches(otherInput) === true) {
|
||||
@@ -278,7 +313,18 @@ export class NotebookInput extends EditorInput {
|
||||
// Compare by resource
|
||||
return otherNotebookEditorInput.notebookUri.toString() === this.notebookUri.toString();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NotebookEditorContentManager implements IContentManager {
|
||||
constructor(private notebookInput: NotebookInput) {
|
||||
}
|
||||
|
||||
async loadContent(): Promise<azdata.nb.INotebookContents> {
|
||||
let notebookEditorModel = await this.notebookInput.resolve();
|
||||
let contentManager = new LocalContentManager();
|
||||
let contents = await contentManager.loadFromContentString(notebookEditorModel.contentString);
|
||||
return contents;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,9 @@ import {
|
||||
SqlMainContext, MainThreadNotebookDocumentsAndEditorsShape, SqlExtHostContext, ExtHostNotebookDocumentsAndEditorsShape,
|
||||
INotebookDocumentsAndEditorsDelta, INotebookEditorAddData, INotebookShowOptions, INotebookModelAddedData, INotebookModelChangedData
|
||||
} from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||
import { NotebookInputModel, NotebookInput } from 'sql/parts/notebook/notebookInput';
|
||||
import { NotebookInput, NotebookEditorModel } from 'sql/parts/notebook/notebookInput';
|
||||
import { INotebookService, INotebookEditor, DEFAULT_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/common/notebookService';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { getProvidersForFileName, getStandardKernelsForProvider } from 'sql/parts/notebook/notebookUtils';
|
||||
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { disposed } from 'vs/base/common/errors';
|
||||
import { ICellModel, NotebookContentChange, INotebookModel } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
@@ -361,26 +360,10 @@ export class MainThreadNotebookDocumentsAndEditors extends Disposable implements
|
||||
pinned: !options.preview
|
||||
};
|
||||
let trusted = uri.scheme === Schemas.untitled;
|
||||
let model = new NotebookInputModel(uri, undefined, trusted, undefined, undefined, undefined, options.connectionId);
|
||||
let providerId = options.providerId;
|
||||
let providers: string[] = undefined;
|
||||
// Ensure there is always a sensible provider ID for this file type
|
||||
providers = getProvidersForFileName(uri.fsPath, this._notebookService);
|
||||
// Try to use a non-builtin provider first
|
||||
if (providers) {
|
||||
providerId = providers.find(p => p !== DEFAULT_NOTEBOOK_PROVIDER);
|
||||
if (!providerId) {
|
||||
providerId = model.providerId;
|
||||
}
|
||||
}
|
||||
model.providers = providers;
|
||||
model.providerId = providerId;
|
||||
model.defaultKernel = options && options.defaultKernel;
|
||||
model.providers.forEach(provider => {
|
||||
let standardKernels = getStandardKernelsForProvider(provider, this._notebookService);
|
||||
model.standardKernels = standardKernels;
|
||||
});
|
||||
let input = this._instantiationService.createInstance(NotebookInput, undefined, model);
|
||||
let input = this._instantiationService.createInstance(NotebookInput, uri.fsPath, uri);
|
||||
input.isTrusted = trusted;
|
||||
input.defaultKernel = options.defaultKernel;
|
||||
input.connectionProfileId = options.connectionId;
|
||||
|
||||
let editor = await this._editorService.openEditor(input, editorOptions, viewColumnToEditorGroup(this._editorGroupService, options.position));
|
||||
if (!editor) {
|
||||
|
||||
@@ -22,6 +22,29 @@ import { nbformat } from 'sql/parts/notebook/models/nbformat';
|
||||
type MimeBundle = { [key: string]: string | string[] | undefined };
|
||||
|
||||
export class LocalContentManager implements nb.ContentManager {
|
||||
|
||||
public async loadFromContentString(contentString: string): Promise<nb.INotebookContents> {
|
||||
let contents: JSONObject = json.parse(contentString);
|
||||
|
||||
if (contents) {
|
||||
if (contents.nbformat === 4) {
|
||||
return v4.readNotebook(<any>contents);
|
||||
} else if (contents.nbformat === 3) {
|
||||
return v3.readNotebook(<any>contents);
|
||||
}
|
||||
if (contents.nbformat) {
|
||||
throw new TypeError(localize('nbformatNotRecognized', 'nbformat v{0}.{1} not recognized', contents.nbformat as any, contents.nbformat_minor as any));
|
||||
}
|
||||
} else if (contentString === '' || contentString === undefined) {
|
||||
// Empty?
|
||||
return v4.createEmptyNotebook();
|
||||
}
|
||||
|
||||
// else, fallthrough condition
|
||||
throw new TypeError(localize('nbNotSupported', 'This file does not have a valid notebook format'));
|
||||
|
||||
}
|
||||
|
||||
public async getNotebookContents(notebookUri: URI): Promise<nb.INotebookContents> {
|
||||
if (!notebookUri) {
|
||||
return undefined;
|
||||
|
||||
Reference in New Issue
Block a user