diff --git a/extensions/notebook/package.json b/extensions/notebook/package.json
index 557f234e6d..97235d9a63 100644
--- a/extensions/notebook/package.json
+++ b/extensions/notebook/package.json
@@ -339,6 +339,12 @@
"when": "false"
}
],
+ "notebooks/title": [
+ {
+ "command": "notebook.command.createBook",
+ "group": "secondary"
+ }
+ ],
"touchBar": [
{
"command": "notebook.command.runactivecell",
@@ -376,12 +382,12 @@
},
{
"command": "notebook.command.searchUntitledBook",
- "when": "view == unsavedBookTreeView && viewItem == unsavedBook && unsavedBooks",
+ "when": "view == providedBooksView && viewItem == providedBook && providedBooks",
"group": "inline"
},
{
"command": "notebook.command.saveBook",
- "when": "view == unsavedBookTreeView && viewItem == unsavedBook && unsavedBooks",
+ "when": "view == providedBooksView && viewItem == providedBook && providedBooks",
"group": "inline"
},
{
@@ -400,8 +406,8 @@
"group": "navigation"
},
{
- "command": "notebook.command.createBook",
- "when": "view == bookTreeView"
+ "command": "books.sqlserver2019",
+ "when": "view == providedBooksView"
},
{
"command": "notebook.command.openNotebookFolder",
@@ -416,6 +422,18 @@
}
]
},
+ "views": {
+ "notebooks": [
+ {
+ "id": "bookTreeView",
+ "name": "%title.SavedBooks%"
+ },
+ {
+ "id": "providedBooksView",
+ "name": "%title.ProvidedBooks%"
+ }
+ ]
+ },
"keybindings": [
{
"command": "notebook.command.runactivecell",
@@ -507,27 +525,6 @@
"connectionProviderIds": []
}
]
- },
- "viewsContainers": {
- "activitybar": [
- {
- "id": "books-explorer",
- "title": "Notebooks",
- "icon": "resources/dark/JupyterBook_2.svg"
- }
- ]
- },
- "views": {
- "books-explorer": [
- {
- "id": "bookTreeView",
- "name": "%title.SavedBooks%"
- },
- {
- "id": "unsavedBookTreeView",
- "name": "%title.UnsavedBooks%"
- }
- ]
}
},
"dependencies": {
diff --git a/extensions/notebook/package.nls.json b/extensions/notebook/package.nls.json
index 9c605670fc..ecc0747485 100644
--- a/extensions/notebook/package.nls.json
+++ b/extensions/notebook/package.nls.json
@@ -34,7 +34,7 @@
"title.trustBook": "Trust Book",
"title.searchJupyterBook": "Search Book",
"title.SavedBooks": "Notebooks",
- "title.UnsavedBooks": "Provided Books",
+ "title.ProvidedBooks": "Provided Books",
"title.PreviewLocalizedBook": "Get localized SQL Server 2019 guide",
"title.openJupyterBook": "Open Jupyter Book",
"title.closeJupyterBook": "Close Jupyter Book",
diff --git a/extensions/notebook/resources/dark/JupyterBook_5.svg b/extensions/notebook/resources/dark/JupyterBook_5.svg
deleted file mode 100644
index 7f381926c6..0000000000
--- a/extensions/notebook/resources/dark/JupyterBook_5.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
diff --git a/extensions/notebook/src/book/bookTreeItem.ts b/extensions/notebook/src/book/bookTreeItem.ts
index b52c0bac9d..7971ad4dbe 100644
--- a/extensions/notebook/src/book/bookTreeItem.ts
+++ b/extensions/notebook/src/book/bookTreeItem.ts
@@ -41,7 +41,7 @@ export class BookTreeItem extends vscode.TreeItem {
this.collapsibleState = book.treeItemCollapsibleState;
this._sections = book.page;
if (book.isUntitled) {
- this.contextValue = 'unsavedBook';
+ this.contextValue = 'providedBook';
} else {
this.contextValue = 'savedBook';
}
diff --git a/extensions/notebook/src/book/bookTreeView.ts b/extensions/notebook/src/book/bookTreeView.ts
index 948d516150..c3de56cffd 100644
--- a/extensions/notebook/src/book/bookTreeView.ts
+++ b/extensions/notebook/src/book/bookTreeView.ts
@@ -54,7 +54,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider {
- await vscode.commands.executeCommand('setContext', 'unsavedBooks', this._openAsUntitled);
+ await vscode.commands.executeCommand('setContext', 'providedBooks', this._openAsUntitled);
await Promise.all(workspaceFolders.map(async (workspaceFolder) => {
try {
await this.loadNotebooksInFolder(workspaceFolder.uri.fsPath);
diff --git a/extensions/notebook/src/extension.ts b/extensions/notebook/src/extension.ts
index 45877940d5..fd7511a3ac 100644
--- a/extensions/notebook/src/extension.ts
+++ b/extensions/notebook/src/extension.ts
@@ -24,21 +24,21 @@ const JUPYTER_NOTEBOOK_PROVIDER = 'jupyter';
const msgSampleCodeDataFrame = localize('msgSampleCodeDataFrame', "This sample code loads the file into a data frame and shows the first 10 results.");
const noNotebookVisible = localize('noNotebookVisible', "No notebook editor is active");
const BOOKS_VIEWID = 'bookTreeView';
-const READONLY_BOOKS_VIEWID = 'unsavedBookTreeView';
+const PROVIDED_BOOKS_VIEWID = 'providedBooksView';
let controller: JupyterController;
type ChooseCellType = { label: string, id: CellType };
export async function activate(extensionContext: vscode.ExtensionContext): Promise {
const createBookPath: string = path.posix.join(extensionContext.extensionPath, 'resources', 'notebooks', 'JupyterBooksCreate.ipynb');
- extensionContext.subscriptions.push(vscode.commands.registerCommand('bookTreeView.openBook', (bookPath: string, openAsUntitled: boolean, urlToOpen?: string) => openAsUntitled ? untitledBookTreeViewProvider.openBook(bookPath, urlToOpen, true) : bookTreeViewProvider.openBook(bookPath, urlToOpen, true)));
+ extensionContext.subscriptions.push(vscode.commands.registerCommand('bookTreeView.openBook', (bookPath: string, openAsUntitled: boolean, urlToOpen?: string) => openAsUntitled ? providedBookTreeViewProvider.openBook(bookPath, urlToOpen, true) : bookTreeViewProvider.openBook(bookPath, urlToOpen, true)));
extensionContext.subscriptions.push(vscode.commands.registerCommand('bookTreeView.openNotebook', (resource) => bookTreeViewProvider.openNotebook(resource)));
- extensionContext.subscriptions.push(vscode.commands.registerCommand('bookTreeView.openUntitledNotebook', (resource) => untitledBookTreeViewProvider.openNotebookAsUntitled(resource)));
+ extensionContext.subscriptions.push(vscode.commands.registerCommand('bookTreeView.openUntitledNotebook', (resource) => providedBookTreeViewProvider.openNotebookAsUntitled(resource)));
extensionContext.subscriptions.push(vscode.commands.registerCommand('bookTreeView.openMarkdown', (resource) => bookTreeViewProvider.openMarkdown(resource)));
extensionContext.subscriptions.push(vscode.commands.registerCommand('bookTreeView.openExternalLink', (resource) => bookTreeViewProvider.openExternalLink(resource)));
- extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.saveBook', () => untitledBookTreeViewProvider.saveJupyterBooks()));
+ extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.saveBook', () => providedBookTreeViewProvider.saveJupyterBooks()));
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.trustBook', (resource) => bookTreeViewProvider.trustBook(resource)));
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.searchBook', (item) => bookTreeViewProvider.searchJupyterBooks(item)));
- extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.searchUntitledBook', () => untitledBookTreeViewProvider.searchJupyterBooks()));
+ extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.searchUntitledBook', () => providedBookTreeViewProvider.searchJupyterBooks()));
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.openBook', () => bookTreeViewProvider.openNewBook()));
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.closeBook', (book: any) => bookTreeViewProvider.closeBook(book)));
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.closeNotebook', (book: any) => bookTreeViewProvider.closeBook(book)));
@@ -114,7 +114,7 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi
}));
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.revealInBooksViewlet', (uri: vscode.Uri, shouldReveal: boolean) => bookTreeViewProvider.revealActiveDocumentInViewlet(uri, shouldReveal)));
- extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.revealInUntitledBooksViewlet', (uri: vscode.Uri, shouldReveal: boolean) => untitledBookTreeViewProvider.revealActiveDocumentInViewlet(uri, shouldReveal)));
+ extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.revealInUntitledBooksViewlet', (uri: vscode.Uri, shouldReveal: boolean) => providedBookTreeViewProvider.revealActiveDocumentInViewlet(uri, shouldReveal)));
let appContext = new AppContext(extensionContext, new ApiWrapper());
controller = new JupyterController(appContext);
@@ -126,9 +126,12 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi
let workspaceFolders = vscode.workspace.workspaceFolders?.slice() ?? [];
const bookTreeViewProvider = new BookTreeViewProvider(appContext.apiWrapper, workspaceFolders, extensionContext, false, BOOKS_VIEWID);
await bookTreeViewProvider.initialized;
- const untitledBookTreeViewProvider = new BookTreeViewProvider(appContext.apiWrapper, [], extensionContext, true, READONLY_BOOKS_VIEWID);
- await untitledBookTreeViewProvider.initialized;
+ const providedBookTreeViewProvider = new BookTreeViewProvider(appContext.apiWrapper, [], extensionContext, true, PROVIDED_BOOKS_VIEWID);
+ await providedBookTreeViewProvider.initialized;
+
+ extensionContext.subscriptions.push(vscode.window.registerTreeDataProvider(BOOKS_VIEWID, bookTreeViewProvider));
+ extensionContext.subscriptions.push(vscode.window.registerTreeDataProvider(PROVIDED_BOOKS_VIEWID, providedBookTreeViewProvider));
return {
getJupyterController() {
return controller;
diff --git a/src/sql/media/icons/book.svg b/src/sql/media/icons/book.svg
new file mode 100644
index 0000000000..bae52cfcc8
--- /dev/null
+++ b/src/sql/media/icons/book.svg
@@ -0,0 +1,7 @@
+
diff --git a/extensions/notebook/resources/dark/JupyterBook_2.svg b/src/sql/media/icons/book_image.svg
similarity index 100%
rename from extensions/notebook/resources/dark/JupyterBook_2.svg
rename to src/sql/media/icons/book_image.svg
diff --git a/src/sql/media/icons/book_inverse.svg b/src/sql/media/icons/book_inverse.svg
new file mode 100644
index 0000000000..464b3d228b
--- /dev/null
+++ b/src/sql/media/icons/book_inverse.svg
@@ -0,0 +1,11 @@
+
diff --git a/src/sql/media/icons/common-icons.css b/src/sql/media/icons/common-icons.css
index 11023dda22..803edf5b19 100644
--- a/src/sql/media/icons/common-icons.css
+++ b/src/sql/media/icons/common-icons.css
@@ -545,6 +545,13 @@ Includes non-masked style declarations. */
background-image: url("database_colored.svg");
}
+.book.codicon {
+
+ -webkit-mask-image: url("book_image.svg");
+ -webkit-mask-repeat: no-repeat;
+ -webkit-mask-position: 50% 50%;
+}
+
.small {
width: 16px;
height: 16px;
diff --git a/src/sql/workbench/contrib/notebook/browser/media/notebook.contribution.css b/src/sql/workbench/contrib/notebook/browser/media/notebook.contribution.css
new file mode 100644
index 0000000000..0808ab3a26
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/media/notebook.contribution.css
@@ -0,0 +1,14 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+/* Activity Bar */
+.monaco-workbench .activitybar .monaco-action-bar .action-label.book {
+ background-color: rgba(255, 255, 255, 0.4);
+}
+
+/* Activity Bar */
+.monaco-workbench .activitybar .monaco-action-bar .checked .action-label.book {
+ background-color: rgb(255, 255, 255);
+}
diff --git a/src/sql/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/sql/workbench/contrib/notebook/browser/notebook.contribution.ts
index 41300ed99f..af34a86d4d 100644
--- a/src/sql/workbench/contrib/notebook/browser/notebook.contribution.ts
+++ b/src/sql/workbench/contrib/notebook/browser/notebook.contribution.ts
@@ -46,6 +46,9 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } fr
import { NotebookThemingContribution } from 'sql/workbench/contrib/notebook/browser/notebookThemingContribution';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode';
+import { NotebookExplorerViewletViewsContribution, OpenNotebookExplorerViewletAction } from 'sql/workbench/contrib/notebook/browser/notebookExplorer/notebookExplorerViewlet';
+import 'vs/css!./media/notebook.contribution';
+
Registry.as(EditorInputFactoryExtensions.EditorInputFactories)
.registerEditorInputFactory(FileNotebookInput.ID, FileNoteBookEditorInputFactory);
@@ -348,3 +351,16 @@ registerComponentType({
selector: MimeRendererComponent.SELECTOR
});
registerCellComponent(TextCellComponent);
+
+const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench);
+workbenchRegistry.registerWorkbenchContribution(NotebookExplorerViewletViewsContribution, LifecyclePhase.Starting);
+const registry = Registry.as(WorkbenchActionsExtensions.WorkbenchActions);
+registry.registerWorkbenchAction(
+ SyncActionDescriptor.create(
+ OpenNotebookExplorerViewletAction,
+ OpenNotebookExplorerViewletAction.ID,
+ OpenNotebookExplorerViewletAction.LABEL,
+ { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_B }),
+ 'View: Show Notebook Explorer',
+ localize('notebookExplorer.view', "View")
+);
diff --git a/src/sql/workbench/contrib/notebook/browser/notebookExplorer/notebookExplorerViewlet.ts b/src/sql/workbench/contrib/notebook/browser/notebookExplorer/notebookExplorerViewlet.ts
new file mode 100644
index 0000000000..6073109d23
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/notebookExplorer/notebookExplorerViewlet.ts
@@ -0,0 +1,149 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { localize } from 'vs/nls';
+import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
+import { IAction } from 'vs/base/common/actions';
+import { append, $, addClass, toggleClass, Dimension } from 'vs/base/browser/dom';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
+import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
+import { IThemeService } from 'vs/platform/theme/common/themeService';
+import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
+import { IStorageService } from 'vs/platform/storage/common/storage';
+import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
+import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
+import { Extensions as ViewContainerExtensions, IViewDescriptor, IViewsRegistry, IViewContainersRegistry, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views';
+import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
+import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
+import { Registry } from 'vs/platform/registry/common/platform';
+import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
+import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
+import { ShowViewletAction, Viewlet } from 'vs/workbench/browser/viewlet';
+import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
+import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
+import { ViewPaneContainer, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
+import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
+
+export const VIEWLET_ID = 'workbench.view.notebooks';
+
+// Viewlet Action
+export class OpenNotebookExplorerViewletAction extends ShowViewletAction {
+ public static ID = VIEWLET_ID;
+ public static LABEL = localize('showNotebookExplorer', "Show Notebooks");
+
+ constructor(
+ id: string,
+ label: string,
+ @IViewletService viewletService: IViewletService,
+ @IEditorGroupsService editorGroupService: IEditorGroupsService,
+ @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
+ ) {
+ super(id, label, VIEWLET_ID, viewletService, editorGroupService, layoutService);
+ }
+}
+
+export class NotebookExplorerViewletViewsContribution implements IWorkbenchContribution {
+
+ constructor() {
+ this.registerViews();
+ }
+
+ private registerViews(): void {
+ let viewDescriptors = [];
+ Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews(viewDescriptors, NOTEBOOK_VIEW_CONTAINER);
+ }
+}
+
+export class NotebookExplorerViewlet extends Viewlet {
+ constructor(
+ @ITelemetryService telemetryService: ITelemetryService,
+ @IStorageService protected storageService: IStorageService,
+ @IInstantiationService protected instantiationService: IInstantiationService,
+ @IThemeService themeService: IThemeService,
+ @IContextMenuService protected contextMenuService: IContextMenuService,
+ @IExtensionService protected extensionService: IExtensionService,
+ @IWorkspaceContextService protected contextService: IWorkspaceContextService,
+ @IWorkbenchLayoutService protected layoutService: IWorkbenchLayoutService,
+ @IConfigurationService protected configurationService: IConfigurationService
+ ) {
+ super(VIEWLET_ID, instantiationService.createInstance(NotebookExplorerViewPaneContainer), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, layoutService, configurationService);
+ }
+}
+
+export class NotebookExplorerViewPaneContainer extends ViewPaneContainer {
+ private root: HTMLElement;
+
+ private notebookSourcesBox: HTMLElement;
+
+ constructor(
+ @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
+ @ITelemetryService telemetryService: ITelemetryService,
+ @IInstantiationService instantiationService: IInstantiationService,
+ @IThemeService themeService: IThemeService,
+ @IStorageService storageService: IStorageService,
+ @IWorkspaceContextService contextService: IWorkspaceContextService,
+ @IContextMenuService contextMenuService: IContextMenuService,
+ @IExtensionService extensionService: IExtensionService,
+ @IConfigurationService configurationService: IConfigurationService,
+ @IMenuService private menuService: IMenuService,
+ @IContextKeyService private contextKeyService: IContextKeyService,
+ @IViewDescriptorService viewDescriptorService: IViewDescriptorService
+ ) {
+ super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService);
+ }
+
+ create(parent: HTMLElement): void {
+ addClass(parent, 'notebookExplorer-viewlet');
+ this.root = parent;
+
+ this.notebookSourcesBox = append(this.root, $('.notebookSources'));
+
+ return super.create(this.notebookSourcesBox);
+ }
+
+ public updateStyles(): void {
+ super.updateStyles();
+ }
+
+ focus(): void {
+ }
+
+ layout(dimension: Dimension): void {
+ toggleClass(this.root, 'narrow', dimension.width <= 300);
+ super.layout(new Dimension(dimension.width, dimension.height));
+ }
+
+ getOptimalWidth(): number {
+ return 400;
+ }
+
+ getSecondaryActions(): IAction[] {
+ let menu = this.menuService.createMenu(MenuId.NotebookTitle, this.contextKeyService);
+ let actions = [];
+ menu.getActions({}).forEach(group => {
+ if (group[0] === 'secondary') {
+ actions.push(...group[1]);
+ }
+ });
+ menu.dispose();
+ return actions;
+ }
+
+ protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewPane {
+ let viewletPanel = this.instantiationService.createInstance(viewDescriptor.ctorDescriptor.ctor, options) as ViewPane;
+ this._register(viewletPanel);
+ return viewletPanel;
+ }
+}
+
+export const NOTEBOOK_VIEW_CONTAINER = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({
+ id: VIEWLET_ID,
+ name: localize('notebookExplorer.name', "Notebooks"),
+ ctorDescriptor: new SyncDescriptor(NotebookExplorerViewPaneContainer),
+ icon: 'book',
+ order: 6,
+ storageId: `${VIEWLET_ID}.state`
+}, ViewContainerLocation.Sidebar);
diff --git a/src/sql/workbench/contrib/notebook/test/browser/notebookExplorerViewlet.test.ts b/src/sql/workbench/contrib/notebook/test/browser/notebookExplorerViewlet.test.ts
new file mode 100644
index 0000000000..0c5f30cd85
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/test/browser/notebookExplorerViewlet.test.ts
@@ -0,0 +1,94 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as assert from 'assert';
+import * as Platform from 'vs/platform/registry/common/platform';
+import { ViewletDescriptor, Extensions, ViewletRegistry, Viewlet } from 'vs/workbench/browser/viewlet';
+import * as Types from 'vs/base/common/types';
+import { workbenchInstantiationService } from 'sql/workbench/test/workbenchTestServices';
+import { Extensions as ViewContainerExtensions, IViewDescriptor, IViewsRegistry } from 'vs/workbench/common/views';
+import { NotebookExplorerViewPaneContainer, NOTEBOOK_VIEW_CONTAINER } from 'sql/workbench/contrib/notebook/browser/notebookExplorer/notebookExplorerViewlet';
+import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
+
+suite('Notebook Explorer Views', () => {
+
+ class NotebookExplorerTestViewlet extends Viewlet {
+
+ constructor() {
+ const instantiationService = workbenchInstantiationService();
+ super('notebookExplorer', instantiationService.createInstance(NotebookExplorerViewPaneContainer), undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
+ }
+
+ public layout(dimension: any): void {
+ throw new Error('Method not implemented.');
+ }
+
+ }
+
+ test('ViewDescriptor API', function () {
+ let d = ViewletDescriptor.create(NotebookExplorerTestViewlet, 'id', 'name', 'class', 1);
+ assert.strictEqual(d.id, 'id');
+ assert.strictEqual(d.name, 'name');
+ assert.strictEqual(d.cssClass, 'class');
+ assert.strictEqual(d.order, 1);
+ });
+
+ test('Editor Aware ViewletDescriptor API', function () {
+ let d = ViewletDescriptor.create(NotebookExplorerTestViewlet, 'id', 'name', 'class', 5);
+ assert.strictEqual(d.id, 'id');
+ assert.strictEqual(d.name, 'name');
+
+ d = ViewletDescriptor.create(NotebookExplorerTestViewlet, 'id', 'name', 'class', 5);
+ assert.strictEqual(d.id, 'id');
+ assert.strictEqual(d.name, 'name');
+ });
+
+ test('NotebookExplorer Views registration', function () {
+ assert(Types.isFunction(Platform.Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews));
+ assert(Types.isFunction(Platform.Registry.as(ViewContainerExtensions.ViewsRegistry).getViews));
+ assert(Types.isFunction(Platform.Registry.as(ViewContainerExtensions.ViewsRegistry).getView));
+
+ Platform.Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews([], NOTEBOOK_VIEW_CONTAINER);
+
+ let oldcount = Platform.Registry.as(ViewContainerExtensions.ViewsRegistry).getViews(NOTEBOOK_VIEW_CONTAINER).length;
+ let d: IViewDescriptor = { id: 'notebookView-test-1', name: 'Notebooks', ctorDescriptor: new SyncDescriptor(NotebookExplorerViewPaneContainer) };
+ Platform.Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews([d], NOTEBOOK_VIEW_CONTAINER);
+ let retrieved = Platform.Registry.as(ViewContainerExtensions.ViewsRegistry).getView('notebookView-test-1');
+ assert(d === retrieved, 'Could not register view :' + d.id + 'Retrieved: ' + retrieved);
+ let newCount = Platform.Registry.as(ViewContainerExtensions.ViewsRegistry).getViews(NOTEBOOK_VIEW_CONTAINER).length;
+ assert.equal(oldcount + 1, newCount, 'View registration failed');
+
+
+ });
+
+ test('NotebookExplorer Views should not register duplicate views', function () {
+ let d: IViewDescriptor = { id: 'notebookView-test-1', name: 'Notebooks', ctorDescriptor: new SyncDescriptor(NotebookExplorerViewPaneContainer) };
+ assert.throws(() => Platform.Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews([d], NOTEBOOK_VIEW_CONTAINER));
+ });
+
+ test('NotebookExplorer Views deregistration', function () {
+ assert(Types.isFunction(Platform.Registry.as(ViewContainerExtensions.ViewsRegistry).deregisterViews));
+ assert(Types.isFunction(Platform.Registry.as(ViewContainerExtensions.ViewsRegistry).getViews));
+
+ let d: IViewDescriptor = { id: 'notebookView-test-1', name: 'Notebooks', ctorDescriptor: new SyncDescriptor(NotebookExplorerViewPaneContainer) };
+ assert.doesNotThrow(() => Platform.Registry.as(ViewContainerExtensions.ViewsRegistry).deregisterViews([d], NOTEBOOK_VIEW_CONTAINER));
+
+ });
+
+ test('NotebookExplorer Viewlet extension point should not register duplicate viewlets', function () {
+ let v1 = ViewletDescriptor.create(NotebookExplorerTestViewlet, 'notebookExplorer-test-id', 'name');
+ Platform.Registry.as(Extensions.Viewlets).registerViewlet(v1);
+ let oldCount = Platform.Registry.as(Extensions.Viewlets).getViewlets().length;
+
+ let v1Duplicate = ViewletDescriptor.create(NotebookExplorerTestViewlet, 'notebookExplorer-test-id', 'name');
+ // Shouldn't register the duplicate.
+ Platform.Registry.as(Extensions.Viewlets).registerViewlet(v1Duplicate);
+
+ let newCount = Platform.Registry.as(Extensions.Viewlets).getViewlets().length;
+ assert.equal(oldCount, newCount, 'Duplicate registration of views.');
+
+ });
+
+});
diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts
index 0847dbbbb8..2fe9813afb 100644
--- a/src/vs/platform/actions/common/actions.ts
+++ b/src/vs/platform/actions/common/actions.ts
@@ -126,6 +126,7 @@ export class MenuId {
static readonly DataExplorerAction = new MenuId('DataExplorerAction'); // {{SQL CARBON EDIT}}
static readonly ExplorerWidgetContext = new MenuId('ExplorerWidgetContext'); // {{SQL CARBON EDIT}}
static readonly DashboardToolbar = new MenuId('DashboardToolbar'); // {{SQL CARBON EDIT}}
+ static readonly NotebookTitle = new MenuId('NotebookTitle'); // {{SQL CARBON EDIT}}
static readonly TimelineItemContext = new MenuId('TimelineItemContext');
static readonly TimelineTitle = new MenuId('TimelineTitle');
static readonly TimelineTitleContext = new MenuId('TimelineTitleContext');
diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts
index f5cf39b33c..4a80aa8900 100644
--- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts
+++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts
@@ -20,6 +20,7 @@ import { VIEWLET_ID as EXPLORER } from 'vs/workbench/contrib/files/common/files'
import { VIEWLET_ID as SCM } from 'vs/workbench/contrib/scm/common/scm';
import { VIEWLET_ID as DEBUG } from 'vs/workbench/contrib/debug/common/debug';
import { VIEWLET_ID as REMOTE } from 'vs/workbench/contrib/remote/common/remote.contribution';
+import { VIEWLET_ID as NOTEBOOK } from 'sql/workbench/contrib/notebook/browser/notebookExplorer/notebookExplorerViewlet'; // {{SQL CARBON EDIT}}
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { URI } from 'vs/base/common/uri';
import { ViewletRegistry, Extensions as ViewletExtensions, ShowViewletAction } from 'vs/workbench/browser/viewlet';
@@ -164,6 +165,13 @@ const viewsContribution: IJSONSchema = {
type: 'array',
items: remoteViewDescriptor,
default: []
+ },
+ // {{SQL CARBON EDIT}}
+ 'notebooks': {
+ description: localize('views.notebooks', "Contributes views to Notebooks container in the Activity bar."),
+ type: 'array',
+ items: viewDescriptor,
+ default: []
}
},
additionalProperties: {
@@ -479,6 +487,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
case 'debug': return this.viewContainersRegistry.get(DEBUG);
case 'scm': return this.viewContainersRegistry.get(SCM);
case 'remote': return this.viewContainersRegistry.get(REMOTE);
+ case 'notebooks': return this.viewContainersRegistry.get(NOTEBOOK); // {{SQL CARBON EDIT}}
default: return this.viewContainersRegistry.get(`workbench.view.extension.${value}`);
}
}
diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts
index 9e8f5aacdb..5ff107eaf5 100644
--- a/src/vs/workbench/api/common/menusExtensionPoint.ts
+++ b/src/vs/workbench/api/common/menusExtensionPoint.ts
@@ -52,6 +52,7 @@ namespace schema {
case 'notebook/toolbar': return MenuId.NotebookToolbar;
case 'dataExplorer/context': return MenuId.DataExplorerContext;
case 'dataExplorer/action': return MenuId.DataExplorerAction;
+ case 'notebooks/title': return MenuId.NotebookTitle;
case 'comments/commentThread/title': return MenuId.CommentThreadTitle;
case 'comments/commentThread/context': return MenuId.CommentThreadActions;
case 'comments/comment/title': return MenuId.CommentTitle;
diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts
index 318e96ed07..1103e42cfb 100644
--- a/src/vs/workbench/browser/layout.ts
+++ b/src/vs/workbench/browser/layout.ts
@@ -596,6 +596,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
case 'remote':
viewletId = 'workbench.view.remote';
break;
+ // {{SQL CARBON EDIT}} add notebook view container to views
+ case 'notebooks':
+ viewletId = 'workbench.view.notebooks';
+ break;
default:
viewletId = `workbench.view.extension.${container.id}`;
}