diff --git a/src/sql/parts/notebook/models/notebookModel.ts b/src/sql/parts/notebook/models/notebookModel.ts index 9a722e9b36..44cd767091 100644 --- a/src/sql/parts/notebook/models/notebookModel.ts +++ b/src/sql/parts/notebook/models/notebookModel.ts @@ -22,6 +22,7 @@ import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; import { NotebookConnection } from 'sql/parts/notebook/models/notebookConnection'; 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'; /* @@ -97,6 +98,13 @@ export class NotebookModel extends Disposable implements INotebookModel { return this.notebookOptions.notebookManager; } + public get notebookUri() : URI { + return this.notebookOptions.notebookUri; + } + public set notebookUri(value : URI) { + this.notebookOptions.notebookUri = value; + } + public get hasServerManager(): boolean { // If the service has a server manager, then we can show the start button return !!this.notebookManager.serverManager; diff --git a/src/sql/parts/notebook/notebook.component.ts b/src/sql/parts/notebook/notebook.component.ts index 08bfaf0279..bdb448a905 100644 --- a/src/sql/parts/notebook/notebook.component.ts +++ b/src/sql/parts/notebook/notebook.component.ts @@ -5,8 +5,6 @@ import './notebookStyles'; -import { nb } from 'sqlops'; - import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, OnDestroy } from '@angular/core'; import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; @@ -20,7 +18,7 @@ import { AngularDisposable } from 'sql/base/common/lifecycle'; import { CellTypes, CellType } from 'sql/parts/notebook/models/contracts'; import { ICellModel, IModelFactory, notebookConstants } from 'sql/parts/notebook/models/modelInterfaces'; import { IConnectionManagementService, IConnectionDialogService } from 'sql/parts/connection/common/connectionManagement'; -import { INotebookService, INotebookParams, INotebookManager, INotebookEditor } from 'sql/services/notebook/notebookService'; +import { INotebookService, INotebookParams, INotebookManager, INotebookEditor, DEFAULT_NOTEBOOK_FILETYPE } from 'sql/services/notebook/notebookService'; import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService'; import { NotebookModel, NotebookContentChange } from 'sql/parts/notebook/models/notebookModel'; import { ModelFactory } from 'sql/parts/notebook/models/modelFactory'; @@ -40,6 +38,12 @@ import { fillInActions, LabeledMenuItemActionItem } from 'vs/platform/actions/br import { IObjectExplorerService } from 'sql/parts/objectExplorer/common/objectExplorerService'; import * as TaskUtilities from 'sql/workbench/common/taskUtilities'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Schemas } from 'vs/base/common/network'; +import URI from 'vs/base/common/uri'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import * as paths from 'vs/base/common/paths'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { TPromise } from 'vs/base/common/winjs.base'; import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes'; export const NOTEBOOK_SELECTOR: string = 'notebook-component'; @@ -81,7 +85,9 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe @Inject(IConnectionDialogService) private connectionDialogService: IConnectionDialogService, @Inject(IContextKeyService) private contextKeyService: IContextKeyService, @Inject(IMenuService) private menuService: IMenuService, - @Inject(IKeybindingService) private keybindingService: IKeybindingService + @Inject(IKeybindingService) private keybindingService: IKeybindingService, + @Inject(IHistoryService) private historyService: IHistoryService, + @Inject(IWindowService) private windowService: IWindowService, ) { super(); this.updateProfile(); @@ -332,7 +338,54 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe } } + // Gets file path from recent workspace in local + private getLastActiveFilePath(untitledResource: URI): string { + let fileName = untitledResource.path + '.' + DEFAULT_NOTEBOOK_FILETYPE.toLocaleLowerCase(); + + let lastActiveFile = this.historyService.getLastActiveFile(); + if (lastActiveFile) { + return URI.file(paths.join(paths.dirname(lastActiveFile.fsPath), fileName)).fsPath; + } + + let lastActiveFolder = this.historyService.getLastActiveWorkspaceRoot('file'); + if (lastActiveFolder) { + return URI.file(paths.join(lastActiveFolder.fsPath, fileName)).fsPath; + } + return fileName; + } + + promptForPath(defaultPath: string): TPromise { + return this.windowService.showSaveDialog({ defaultPath }); + } + + // Entry point to save notebook public async save(): Promise { + let self = this; + let notebookUri = this.notebookParams.notebookUri; + if (notebookUri.scheme === Schemas.untitled) { + let dialogPath = this.getLastActiveFilePath(notebookUri); + return this.promptForPath(dialogPath).then(path => { + if (path) { + let target = URI.parse(path); + let resource = self._model.notebookUri; + self._model.notebookUri = target; + this.saveNotebook().then(result => { + if(result) + { + this.notebookService.renameNotebookEditor(resource, target, this); + } + return result; + }); + } + return false; // User clicks cancel + }); + } + else { + return await this.saveNotebook(); + } + } + + private async saveNotebook(): Promise { try { let saved = await this._model.saveModel(); if (saved) { diff --git a/src/sql/parts/notebook/notebookActions.ts b/src/sql/parts/notebook/notebookActions.ts index 0242eb449f..aac3cfdae7 100644 --- a/src/sql/parts/notebook/notebookActions.ts +++ b/src/sql/parts/notebook/notebookActions.ts @@ -65,8 +65,6 @@ export class SaveNotebookAction extends Action { let saved = await context.save(); if (saved) { this._notificationService.notify({ severity: Severity.Info, message: SaveNotebookAction.notebookSavedMsg, actions }); - } else { - this._notificationService.error(SaveNotebookAction.notebookFailedSaveMsg); } return saved; } diff --git a/src/sql/services/notebook/notebookService.ts b/src/sql/services/notebook/notebookService.ts index c40aeed07f..830fa5b0f0 100644 --- a/src/sql/services/notebook/notebookService.ts +++ b/src/sql/services/notebook/notebookService.ts @@ -28,6 +28,7 @@ export interface INotebookService { onNotebookEditorAdd: Event; onNotebookEditorRemove: Event; + onNotebookEditorRename: Event; /** * Register a metadata provider @@ -57,6 +58,8 @@ export interface INotebookService { shutdown(): void; getMimeRegistry(): RenderMimeRegistry; + + renameNotebookEditor(oldUri: URI, newUri: URI, currentEditor: INotebookEditor): void; } export interface INotebookProvider { diff --git a/src/sql/services/notebook/notebookServiceImpl.ts b/src/sql/services/notebook/notebookServiceImpl.ts index 836ca7c579..35e21e363f 100644 --- a/src/sql/services/notebook/notebookServiceImpl.ts +++ b/src/sql/services/notebook/notebookServiceImpl.ts @@ -29,6 +29,7 @@ export class NotebookService implements INotebookService { private _managers: Map = new Map(); private _onNotebookEditorAdd = new Emitter(); private _onNotebookEditorRemove = new Emitter(); + private _onNotebookEditorRename = new Emitter(); private _editors = new Map(); constructor() { @@ -84,6 +85,10 @@ export class NotebookService implements INotebookService { return this._onNotebookEditorRemove.event; } + get onNotebookEditorRename(): Event { + return this._onNotebookEditorRename.event; + } + addNotebookEditor(editor: INotebookEditor): void { this._editors.set(editor.id, editor); this._onNotebookEditorAdd.fire(editor); @@ -103,6 +108,17 @@ export class NotebookService implements INotebookService { return editors; } + renameNotebookEditor(oldUri: URI, newUri: URI, currentEditor: INotebookEditor): void { + let oldUriKey = oldUri.toString(); + if(this._editors.has(oldUriKey)) + { + this._editors.delete(oldUriKey); + currentEditor.notebookParams.notebookUri = newUri; + this._editors.set(newUri.toString(), currentEditor); + this._onNotebookEditorRename.fire(currentEditor); + } + } + private sendNotebookCloseToProvider(editor: INotebookEditor) { let notebookUri = editor.notebookParams.notebookUri; let uriString = notebookUri.toString(); diff --git a/src/sql/workbench/api/node/mainThreadNotebookDocumentsAndEditors.ts b/src/sql/workbench/api/node/mainThreadNotebookDocumentsAndEditors.ts index d3f184c1fd..07fe282f3d 100644 --- a/src/sql/workbench/api/node/mainThreadNotebookDocumentsAndEditors.ts +++ b/src/sql/workbench/api/node/mainThreadNotebookDocumentsAndEditors.ts @@ -206,6 +206,7 @@ class MainThreadNotebookDocumentAndEditorStateComputer extends Disposable { this._register(this._editorService.onDidVisibleEditorsChange(this._updateState, this)); this._register(this._notebookService.onNotebookEditorAdd(this._onDidAddEditor, this)); this._register(this._notebookService.onNotebookEditorRemove(this._onDidRemoveEditor, this)); + this._register(this._notebookService.onNotebookEditorRename(this._onDidRenameEditor, this)); this._updateState(); } @@ -220,6 +221,11 @@ class MainThreadNotebookDocumentAndEditorStateComputer extends Disposable { this._updateState(); } + private _onDidRenameEditor(e: INotebookEditor): void { + this._updateState(); + //TODO: Close editor and open it + } + private _updateState(): void { // editor const editors = new Map();