diff --git a/src/sql/media/icons/common-icons.css b/src/sql/media/icons/common-icons.css index e908539152..ab3fe8663a 100644 --- a/src/sql/media/icons/common-icons.css +++ b/src/sql/media/icons/common-icons.css @@ -33,6 +33,13 @@ background: url("database_inverse.svg") center center no-repeat; } +.vs-dark .codicon.icon-dashboard-view::before, +.hc-black .codicon.icon-dashboard-view::before, +.vs .codicon.icon-dashboard-view::before { + -webkit-mask-image: url("dashboard_view.svg"); + mask-image: url("dashboard_view.svg"); +} + .vs .codicon.error, .vs-dark .codicon.error, .hc-black .codicon.error { diff --git a/src/sql/media/icons/dashboard_view.svg b/src/sql/media/icons/dashboard_view.svg new file mode 100644 index 0000000000..537a84cfc9 --- /dev/null +++ b/src/sql/media/icons/dashboard_view.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/sql/workbench/contrib/notebook/browser/notebook.component.ts b/src/sql/workbench/contrib/notebook/browser/notebook.component.ts index 77be99777f..b5ff1550e3 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebook.component.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebook.component.ts @@ -28,7 +28,7 @@ import { INotebookService, INotebookParams, INotebookEditor, INotebookSection, I import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel'; import { Deferred } from 'sql/base/common/promise'; import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar'; -import { AddCellAction, KernelsDropdown, AttachToDropdown, TrustedAction, RunAllCellsAction, ClearAllOutputsAction, CollapseCellsAction, RunParametersAction } from 'sql/workbench/contrib/notebook/browser/notebookActions'; +import { AddCellAction, KernelsDropdown, AttachToDropdown, TrustedAction, RunAllCellsAction, ClearAllOutputsAction, CollapseCellsAction, RunParametersAction, NotebookViewsActionProvider } from 'sql/workbench/contrib/notebook/browser/notebookActions'; import { DropdownMenuActionViewItem } from 'sql/base/browser/ui/buttonMenu/buttonMenu'; import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes'; import { IConnectionDialogService } from 'sql/workbench/services/connection/common/connectionDialogService'; @@ -51,6 +51,7 @@ import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/not import { IColorTheme } from 'vs/platform/theme/common/themeService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CellToolbarComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.component'; +import { NotebookViewsExtension } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension'; import { MaskedLabeledMenuItemActionItem } from 'sql/platform/actions/browser/menuEntryActionViewItem'; import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -70,6 +71,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe @ViewChildren(CellToolbarComponent) private cellToolbar: QueryList; @Input() _model: NotebookModel; + @Input() _views: NotebookViewsExtension; protected _actionBar: Taskbar; protected isLoading: boolean; @@ -130,10 +132,14 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe this.notebookService.removeNotebookEditor(this); } } - public get model(): NotebookModel | null { + public get model(): NotebookModel | undefined { return this._model; } + public get views(): NotebookViewsExtension | undefined { + return this._views; + } + public get activeCellId(): string { return this._model && this._model.activeCell ? this._model.activeCell.id : ''; } @@ -442,6 +448,28 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe dropdownMenuActionViewItem.render(buttonDropdownContainer); dropdownMenuActionViewItem.setActionContext(this._notebookParams.notebookUri); + let viewsDropdownContainer; + if (this._configurationService.getValue('notebookViews.enabled')) { + let viewsContainer = document.createElement('li'); + let viewsActionsProvider = new NotebookViewsActionProvider(viewsContainer, this.views, this.modelReady, this.notebookService, this.notificationService, this.instantiationService); + let viewsButton = new Action('notebook.OpenViews', localize('views', "Views"), 'notebook-button masked-pseudo code'); + viewsDropdownContainer = DOM.$('li.action-item'); + viewsDropdownContainer.setAttribute('role', 'presentation'); + let viewsDropdownMenuActionViewItem = new DropdownMenuActionViewItem( + viewsButton, + viewsActionsProvider, + this.contextMenuService, + undefined, + this._actionBar.actionRunner, + undefined, + 'codicon notebook-button masked-pseudo masked-pseudo-after icon-dashboard-view dropdown-arrow', + localize('editor', "Editor"), + undefined + ); + viewsDropdownMenuActionViewItem.render(viewsDropdownContainer); + viewsDropdownMenuActionViewItem.setActionContext(this._notebookParams.notebookUri); + } + this._actionBar.setContent([ { element: buttonDropdownContainer }, { action: this._runAllCellsAction }, @@ -449,6 +477,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe { element: kernelContainer }, { element: attachToContainer }, { element: spacerElement }, + { element: viewsDropdownContainer }, { action: collapseCellsAction }, { action: clearResultsButton }, { action: this._trustedAction }, diff --git a/src/sql/workbench/contrib/notebook/browser/notebookActions.ts b/src/sql/workbench/contrib/notebook/browser/notebookActions.ts index 7aaf60f842..276d83e59f 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebookActions.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebookActions.ts @@ -6,7 +6,7 @@ import * as azdata from 'azdata'; import * as path from 'vs/base/common/path'; -import { Action } from 'vs/base/common/actions'; +import { Action, IAction, Separator } from 'vs/base/common/actions'; import { localize } from 'vs/nls'; import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; import { INotificationService, Severity, INotificationActions } from 'vs/platform/notification/common/notification'; @@ -18,21 +18,25 @@ import { ConnectionProfile } from 'sql/platform/connection/common/connectionProf import { IConnectionDialogService } from 'sql/workbench/services/connection/common/connectionDialogService'; import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { CellType } from 'sql/workbench/services/notebook/common/contracts'; +import { CellType, NotebookChangeType } from 'sql/workbench/services/notebook/common/contracts'; import { getErrorMessage } from 'vs/base/common/errors'; import { IEditorAction } from 'vs/editor/common/editorCommon'; import { IFindNotebookController } from 'sql/workbench/contrib/notebook/browser/find/notebookFindWidget'; -import { INotebookModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces'; +import { INotebookModel, ViewMode } from 'sql/workbench/services/notebook/browser/models/modelInterfaces'; import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService'; import { TreeUpdateUtils } from 'sql/workbench/services/objectExplorer/browser/treeUpdateUtils'; import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CellContext } from 'sql/workbench/contrib/notebook/browser/cellViews/codeActions'; import { URI } from 'vs/base/common/uri'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IActionProvider } from 'vs/base/browser/ui/dropdown/dropdown'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys'; import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { KernelsLanguage } from 'sql/workbench/services/notebook/common/notebookConstants'; +import { INotebookViews } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViews'; const msgLoading = localize('loading', "Loading kernels..."); export const msgChanging = localize('changing', "Changing kernel..."); @@ -178,6 +182,148 @@ export abstract class ToggleableAction extends Action { } } +export class NotebookViewsActionProvider implements IActionProvider { + private _options: Action[] = []; + private views: INotebookViews; + private viewMode: ViewMode; + private readonly _optionsUpdated = new Emitter(); + + constructor( + container: HTMLElement, + views: INotebookViews, + modelReady: Promise, + @INotebookService private _notebookService: INotebookService, + @INotificationService private _notificationService: INotificationService, + @IInstantiationService private instantiationService: IInstantiationService) { + + modelReady?.then((model) => { + this.views = views; + this.viewMode = model.viewMode; + this.updateView(); + }) + .catch((err) => { + this._notificationService.error(getErrorMessage(err)); + }); + } + + getActions(): IAction[] { + return this._options; + } + + public get options(): Action[] { + return this._options; + } + + /** + * Update SelectBox values + */ + public updateView() { + const backToNotebookButton = this.instantiationService.createInstance(NotebookViewAction, 'notebookView.backToNotebook', localize('notebookViewLabel', 'Editor'), 'notebook-button'); + const newViewButton = this.instantiationService.createInstance(CreateNotebookViewAction, 'notebookView.newView', localize('newViewLabel', 'Create New View'), 'notebook-button notebook-button-newview'); + + const views = this.views.getViews(); + this._options = []; + + this._options.push(backToNotebookButton); + this._options.push(newViewButton); + + if (views.length) { + this._options.push(this.instantiationService.createInstance(Separator)); + } + + views.forEach((view) => { + const option = new DashboardViewAction(view.guid, view.name, 'button', this._notebookService, this._notificationService); + this._options.push(option); + + if (this.viewMode === ViewMode.Views && this.views.getActiveView() === view) { + option.checked = true; + option.enabled = false; + } + }); + + if (this.viewMode === ViewMode.Notebook) { + backToNotebookButton.checked = true; + backToNotebookButton.enabled = false; + } + + this._optionsUpdated.fire(true); + } + + public get onUpdated(): Event { + return this._optionsUpdated.event; + } + + public optionSelected(displayName: string): void { + const view = this.views.getViews().find(view => view.name === displayName); + this.views.setActiveView(view); + } +} + +/** + * Action to open a Notebook View + */ +export class DashboardViewAction extends Action { + constructor( + id: string, label: string, cssClass: string, + @INotebookService private _notebookService: INotebookService, + @INotificationService private _notificationService: INotificationService, + ) { + super(id, label, cssClass); + } + + public override async run(context: URI): Promise { + if (context) { + const editor = this._notebookService.findNotebookEditor(context); + let views = editor.views; + const view = views.getViews().find(view => view.guid === this.id); + + if (view) { + views.setActiveView(view); + editor.model.viewMode = ViewMode.Views; + } else { + this._notificationService.error(localize('viewNotFound', "Unable to find view: {0}", this.id)); + } + } + } +} + +/** + * Action to open enter the default notebook editor + */ +export class NotebookViewAction extends Action { + constructor( + id: string, label: string, cssClass: string, + @INotebookService private _notebookService: INotebookService + ) { + super(id, label, cssClass); + } + public override async run(context: URI): Promise { + const editor = this._notebookService.findNotebookEditor(context); + editor.model.viewMode = ViewMode.Notebook; + } +} + +export class CreateNotebookViewAction extends Action { + constructor( + id: string, label: string, cssClass: string, + @INotebookService private _notebookService: INotebookService + ) { + super(id, label, cssClass); + } + public override async run(context: URI): Promise { + if (context) { + const editor = this._notebookService.findNotebookEditor(context); + const views = editor.views; + + const newView = views.createNewView(); + views.setActiveView(newView); + + editor.model.viewMode = ViewMode.Views; + editor.model.serializationStateChanged(NotebookChangeType.MetadataChanged); + } + } +} + export class TrustedAction extends ToggleableAction { // Constants private static readonly trustedLabel = localize('trustLabel', "Trusted"); diff --git a/src/sql/workbench/contrib/notebook/browser/notebookEditor.component.html b/src/sql/workbench/contrib/notebook/browser/notebookEditor.component.html index 80fc2ad0dc..fd0603203b 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebookEditor.component.html +++ b/src/sql/workbench/contrib/notebook/browser/notebookEditor.component.html @@ -5,5 +5,5 @@ *--------------------------------------------------------------------------------------------*/ --> - + diff --git a/src/sql/workbench/contrib/notebook/browser/notebookEditor.component.ts b/src/sql/workbench/contrib/notebook/browser/notebookEditor.component.ts index 3d2a54dd5d..fda0e319bb 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebookEditor.component.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebookEditor.component.ts @@ -24,6 +24,7 @@ import { IAction, SubmenuAction } from 'vs/base/common/actions'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { fillInActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { NotebookViewsExtension } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension'; export const NOTEBOOKEDITOR_SELECTOR: string = 'notebookeditor-component'; @@ -36,6 +37,8 @@ export class NotebookEditorComponent extends AngularDisposable { private notebookManagers: INotebookManager[] = []; private _model: NotebookModel; + public views: NotebookViewsExtension; + constructor( @Inject(ILogService) private readonly logService: ILogService, @Inject(IBootstrapParams) private _notebookParams: INotebookParams, @@ -104,6 +107,8 @@ export class NotebookEditorComponent extends AngularDisposable { this._model = this._register(model); await this.model.loadContents(trusted); + this.views = new NotebookViewsExtension(this.model); + this._register(model.viewModeChanged((mode) => this.onViewModeChanged())); this._register(model.contentChanged((change) => this.handleContentChanged(change))); this._register(model.onCellTypeChanged(() => this.detectChanges())); diff --git a/src/sql/workbench/contrib/notebook/test/browser/notebookActions.test.ts b/src/sql/workbench/contrib/notebook/test/browser/notebookActions.test.ts index 0a3224652b..f238b1c42d 100644 --- a/src/sql/workbench/contrib/notebook/test/browser/notebookActions.test.ts +++ b/src/sql/workbench/contrib/notebook/test/browser/notebookActions.test.ts @@ -7,15 +7,15 @@ import * as assert from 'assert'; import * as azdata from 'azdata'; import * as sinon from 'sinon'; import { TestConfigurationService } from 'sql/platform/connection/test/common/testConfigurationService'; -import { AddCellAction, ClearAllOutputsAction, CollapseCellsAction, kernelNotSupported, KernelsDropdown, msgChanging, NewNotebookAction, noKernelName, noParameterCell, noParametersInCell, RunAllCellsAction, RunParametersAction, TrustedAction } from 'sql/workbench/contrib/notebook/browser/notebookActions'; -import { ClientSessionStub, ContextViewProviderStub, NotebookComponentStub, NotebookModelStub, NotebookServiceStub } from 'sql/workbench/contrib/notebook/test/stubs'; +import { AddCellAction, ClearAllOutputsAction, CollapseCellsAction, CreateNotebookViewAction, DashboardViewAction, kernelNotSupported, KernelsDropdown, msgChanging, NewNotebookAction, noKernelName, noParameterCell, noParametersInCell, NotebookViewAction, NotebookViewsActionProvider, RunAllCellsAction, RunParametersAction, TrustedAction } from 'sql/workbench/contrib/notebook/browser/notebookActions'; +import { ClientSessionStub, ContextViewProviderStub, NotebookComponentStub, NotebookModelStub, NotebookServiceStub, NotebookViewsStub, NotebookViewStub } from 'sql/workbench/contrib/notebook/test/stubs'; import { NotebookEditorStub } from 'sql/workbench/contrib/notebook/test/testCommon'; -import { ICellModel, INotebookModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces'; +import { ICellModel, INotebookModel, ViewMode } from 'sql/workbench/services/notebook/browser/models/modelInterfaces'; import { IStandardKernelWithProvider } from 'sql/workbench/services/notebook/browser/models/notebookUtils'; import { INotebookEditor, INotebookService } from 'sql/workbench/services/notebook/browser/notebookService'; import { CellType, CellTypes } from 'sql/workbench/services/notebook/common/contracts'; import * as TypeMoq from 'typemoq'; -import { Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { TestCommandService } from 'vs/editor/test/browser/editorTestServices'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationChangeEvent, IConfigurationOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -26,6 +26,9 @@ import { workbenchInstantiationService } from 'vs/workbench/test/browser/workben import { URI } from 'vs/base/common/uri'; import { NullAdsTelemetryService } from 'sql/platform/telemetry/common/adsTelemetryService'; import { MockQuickInputService } from 'sql/workbench/contrib/notebook/test/common/quickInputServiceMock'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { Separator } from 'vs/base/common/actions'; +import { INotebookView, INotebookViews } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViews'; class TestClientSession extends ClientSessionStub { private _errorState: boolean = false; @@ -519,6 +522,50 @@ suite('Notebook Actions', function (): void { assert.strictEqual(actualMsg, expectedMsg); }); + test('notebookViewsActionProvider', async () => { + const testGuid = '1'; + const testName = 'Notebook-0'; + + const testNotebookModel: INotebookModel = { + viewMode: ViewMode.Notebook + }; + + const notebookEditor = new NotebookEditorStub({ model: testNotebookModel }); + + const mockNotification = TypeMoq.Mock.ofType(TestNotificationService); + const notebookViews = TypeMoq.Mock.ofType(NotebookViewsStub); + + const notebookView = TypeMoq.Mock.ofType(NotebookViewStub); + notebookView.setup(x => x.guid).returns(() => testGuid); + notebookView.setup(x => x.name).returns(() => testName); + const views: INotebookView[] = [notebookView.object]; + + notebookViews.setup(x => x.getViews()).returns(() => views); + notebookViews.setup(x => x.getActiveView()).returns(() => undefined); + + const notebookViewAction = new NotebookViewAction('notebookView.backToNotebook', 'Editor', 'notebook-button', mockNotebookService.object); + const createNotebookViewAction = new CreateNotebookViewAction('notebookView.newView', 'Create New View', 'notebook-button notebook-button-newview', mockNotebookService.object); + const separator = new Separator(); + + // Create a mocked out instantiation service + const mockInstantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Strict); + mockInstantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(NotebookViewAction), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => notebookViewAction); + mockInstantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(CreateNotebookViewAction), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => createNotebookViewAction); + mockInstantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(Separator))).returns(() => separator); + + const viewsContainer = document.createElement('li'); + const viewsActionsProvider = new NotebookViewsActionProvider(viewsContainer, notebookViews.object, notebookEditor.modelReady, mockNotebookService.object, mockNotification.object, mockInstantiationService.object); + + await Event.toPromise(viewsActionsProvider.onUpdated); + + const actions = viewsActionsProvider.getActions(); + + // It includes all the options + assert.strictEqual(actions.filter(a => a instanceof DashboardViewAction).length, 1); + assert.strictEqual(actions.filter(a => a instanceof NotebookViewAction).length, 1); + assert.strictEqual(actions.filter(a => a instanceof CreateNotebookViewAction).length, 1); + }); + suite('Kernels dropdown', async () => { let kernelsDropdown: KernelsDropdown; let contextViewProvider: ContextViewProviderStub; diff --git a/src/sql/workbench/contrib/notebook/test/electron-browser/notebookEditorModel.test.ts b/src/sql/workbench/contrib/notebook/test/electron-browser/notebookEditorModel.test.ts index c30958638c..c5cfa72836 100644 --- a/src/sql/workbench/contrib/notebook/test/electron-browser/notebookEditorModel.test.ts +++ b/src/sql/workbench/contrib/notebook/test/electron-browser/notebookEditorModel.test.ts @@ -132,6 +132,7 @@ suite('Notebook Editor Model', function (): void { notebookParams: undefined, modelReady: undefined, model: notebookModel, + views: undefined, isDirty: undefined, isActive: undefined, isVisible: undefined, diff --git a/src/sql/workbench/contrib/notebook/test/stubs.ts b/src/sql/workbench/contrib/notebook/test/stubs.ts index 98230df695..b597ec947f 100644 --- a/src/sql/workbench/contrib/notebook/test/stubs.ts +++ b/src/sql/workbench/contrib/notebook/test/stubs.ts @@ -19,6 +19,8 @@ import { QueryTextEditor } from 'sql/workbench/browser/modelComponents/queryText import { IContextViewProvider, IDelegate } from 'vs/base/browser/ui/contextview/contextview'; import { IEditorPane } from 'vs/workbench/common/editor'; import { INotebookShowOptions } from 'sql/workbench/api/common/sqlExtHost.protocol'; +import { NotebookViewsExtension } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension'; +import { INotebookView, INotebookViewCell, INotebookViews } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViews'; export class NotebookModelStub implements INotebookModel { constructor(private _languageInfo?: nb.ILanguageInfo, private _cells?: ICellModel[], private _testContents?: nb.INotebookContents) { @@ -490,6 +492,9 @@ export class NotebookComponentStub implements INotebookEditor { get model(): INotebookModel { throw new Error('Method not implemented.'); } + get views(): NotebookViewsExtension { + throw new Error('Method not implemented.'); + } isDirty(): boolean { throw new Error('Method not implemented.'); } @@ -684,6 +689,7 @@ export class NotebookEditorStub implements INotebookEditor { cellEditors: CellEditorProviderStub[]; modelReady: Promise; model: INotebookModel; + views: NotebookViewsExtension; viewMode: string; isDirty(): boolean { throw new Error('Method not implemented.'); @@ -754,3 +760,77 @@ export class ContextViewProviderStub implements IContextViewProvider { throw new Error('Method not implemented.'); } } + +export class NotebookViewStub implements INotebookView { + isNew: boolean; + name: string = ''; + guid: string = ''; + cells: readonly ICellModel[] = []; + hiddenCells: readonly ICellModel[]; + displayedCells: readonly ICellModel[]; + + onDeleted: vsEvent.Event; + initialize(): void { + throw new Error('Method not implemented.'); + } + nameAvailable(name: string): boolean { + throw new Error('Method not implemented.'); + } + getCellMetadata(cell: ICellModel): INotebookViewCell { + throw new Error('Method not implemented.'); + } + hideCell(cell: ICellModel): void { + throw new Error('Method not implemented.'); + } + moveCell(cell: ICellModel, x: number, y: number): void { + throw new Error('Method not implemented.'); + } + resizeCell(cell: ICellModel, width: number, height: number): void { + throw new Error('Method not implemented.'); + } + compactCells() { + throw new Error('Method not implemented.'); + } + markAsViewed(): void { + throw new Error('Method not implemented.'); + } + getCell(guid: string): Readonly { + throw new Error('Method not implemented.'); + } + insertCell(cell: ICellModel): void { + throw new Error('Method not implemented.'); + } + save(): void { + throw new Error('Method not implemented.'); + } + delete(): void { + throw new Error('Method not implemented.'); + } +} + +export class NotebookViewsStub implements INotebookViews { + notebook: INotebookModel; + onViewDeleted: vsEvent.Event; + createNewView(name?: string): INotebookView { + throw new Error('Method not implemented.'); + } + removeView(guid: string): void { + throw new Error('Method not implemented.'); + } + generateDefaultViewName(): string { + throw new Error('Method not implemented.'); + } + getViews(): INotebookView[] { + throw new Error('Method not implemented.'); + } + getActiveView(): INotebookView { + throw new Error('Method not implemented.'); + } + setActiveView(view: INotebookView): void { + throw new Error('Method not implemented.'); + } + viewNameIsTaken(name: string): boolean { + throw new Error('Method not implemented.'); + } + +} diff --git a/src/sql/workbench/contrib/notebook/test/testCommon.ts b/src/sql/workbench/contrib/notebook/test/testCommon.ts index d4552598e6..4c4256c814 100644 --- a/src/sql/workbench/contrib/notebook/test/testCommon.ts +++ b/src/sql/workbench/contrib/notebook/test/testCommon.ts @@ -13,15 +13,17 @@ import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtil import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { TestEditorGroupsService, TestEditorService, TestTextResourceConfigurationService } from 'vs/workbench/test/browser/workbenchTestServices'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; +import { NotebookViewsExtension } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension'; // Typically you will pass in either editor or the instantiationService parameter. // Leave both undefined when you want the underlying object(s) to have an undefined editor. export class NotebookEditorStub extends stubs.NotebookEditorStub { // Normally one needs to provide either the editor or the instantiationService as the constructor parameter - constructor({ cellGuid, instantiationService, editor, model, notebookParams }: { cellGuid?: string; instantiationService?: IInstantiationService; editor?: QueryTextEditor; model?: INotebookModel, notebookParams?: INotebookParams } = {}) { + constructor({ cellGuid, instantiationService, editor, model, views, notebookParams }: { cellGuid?: string; instantiationService?: IInstantiationService; editor?: QueryTextEditor; model?: INotebookModel, views?: NotebookViewsExtension, notebookParams?: INotebookParams } = {}) { super(); this.cells = []; this.model = model; + this.views = views; this.notebookParams = notebookParams; this.cellEditors = [new CellEditorProviderStub({ cellGuid: cellGuid, instantiationService: instantiationService, editor: editor })]; this.id = this.notebookParams?.notebookUri?.toString(); diff --git a/src/sql/workbench/services/notebook/browser/notebookService.ts b/src/sql/workbench/services/notebook/browser/notebookService.ts index d603044e45..d48b4ef38f 100644 --- a/src/sql/workbench/services/notebook/browser/notebookService.ts +++ b/src/sql/workbench/services/notebook/browser/notebookService.ts @@ -20,6 +20,7 @@ import { Range } from 'vs/editor/common/core/range'; import { IEditorPane } from 'vs/workbench/common/editor'; import { INotebookInput } from 'sql/workbench/services/notebook/browser/interface'; import { INotebookShowOptions } from 'sql/workbench/api/common/sqlExtHost.protocol'; +import { NotebookViewsExtension } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension'; export const SERVICE_ID = 'sqlNotebookService'; export const INotebookService = createDecorator(SERVICE_ID); @@ -210,6 +211,7 @@ export interface INotebookEditor { readonly cellEditors: ICellEditorProvider[]; readonly modelReady: Promise; readonly model: INotebookModel | null; + readonly views: NotebookViewsExtension | null; isDirty(): boolean; isActive(): boolean; isVisible(): boolean; diff --git a/src/sql/workbench/services/notebook/browser/notebookViews/notebookViews.d.ts b/src/sql/workbench/services/notebook/browser/notebookViews/notebookViews.d.ts index befac71e27..449884f92e 100644 --- a/src/sql/workbench/services/notebook/browser/notebookViews/notebookViews.d.ts +++ b/src/sql/workbench/services/notebook/browser/notebookViews/notebookViews.d.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces'; +import { ICellModel, INotebookModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces'; import { Event } from 'vs/base/common/event'; export type CellChangeEventType = 'hide' | 'insert' | 'active'; @@ -62,3 +62,16 @@ export interface INotebookViewMetadata { export interface INotebookViewCellMetadata { views: INotebookViewCell[]; } + +export interface INotebookViews { + onViewDeleted: Event; + notebook: INotebookModel; + + createNewView(name?: string): INotebookView; + removeView(guid: string): void; + generateDefaultViewName(): string; + getViews(): INotebookView[]; + getActiveView(): INotebookView; + setActiveView(view: INotebookView): void; + viewNameIsTaken(name: string): boolean; +} diff --git a/src/sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension.ts b/src/sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension.ts index 676c8be03c..853d345b0d 100644 --- a/src/sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension.ts +++ b/src/sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension.ts @@ -9,9 +9,9 @@ import { Emitter, Event } from 'vs/base/common/event'; import { localize } from 'vs/nls'; import { NotebookViewModel } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewModel'; import { NotebookExtension } from 'sql/workbench/services/notebook/browser/models/notebookExtension'; -import { INotebookView, INotebookViewCell, INotebookViewCellMetadata, INotebookViewMetadata } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViews'; +import { INotebookView, INotebookViewCell, INotebookViewCellMetadata, INotebookViewMetadata, INotebookViews } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViews'; -export class NotebookViewsExtension extends NotebookExtension { +export class NotebookViewsExtension extends NotebookExtension implements INotebookViews { static readonly defaultViewName = localize('notebookView.untitledView', "Untitled View"); readonly maxNameIterationAttempts = 100;