Feat/notebooks viewlet (#10170)

* clean up unsavedBooks to providedBooks

* added notebooks viewley contribution

* added notebookExporerAction context

* temp shortcut key B

* remove commenred code

* changes with master merge

* fix comments

* initial tests

* fix casing and description

* merged master and resolved errors

* remove extension point & add custom view container

* merge latest from master

* remove unused files

* move book images to common

* remove notebookExplorer contrib & move to notebook

* build fix

* remove explorer specific sryles from common

* vscode convention to define container actions

* rename notebooks/title
This commit is contained in:
Maddy
2020-06-01 15:47:56 -07:00
committed by GitHub
parent 7305bdaec4
commit 3088c73e2b
18 changed files with 349 additions and 43 deletions

View File

@@ -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": {

View File

@@ -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",

View File

@@ -1,7 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<title>ADS_jupyterBook</title>
<path d="M4.25,24A2.251,2.251,0,0,1,2,21.75V3.86A2.257,2.257,0,0,1,3.962,1.628L16.462.019A2.251,2.251,0,0,1,19,2.25H17.5a.741.741,0,0,0-.255-.563.755.755,0,0,0-.591-.181l-12.5,1.61A.752.752,0,0,0,3.5,3.86V21.75a.738.738,0,0,0,.255.563.746.746,0,0,0,.59.181l12.5-1.61a.753.753,0,0,0,.655-.744V2.263H19V20.14a2.257,2.257,0,0,1-1.963,2.232l-12.5,1.609A2.139,2.139,0,0,1,4.25,24Z" fill="#fff"/>
<path d="M19.75,24H4.5a.75.75,0,0,1,0-1.5H19.75a.75.75,0,0,0,.75-.75V3.763a.75.75,0,0,0-.75-.75h-1.5a.75.75,0,0,1,0-1.5h1.5A2.252,2.252,0,0,1,22,3.763V21.75A2.252,2.252,0,0,1,19.75,24Z" fill="#fff"/>
<rect width="24" height="24" fill="none"/>
<path d="M7.645,10.966A1.615,1.615,0,0,1,6.022,9.351v-1.8a1.619,1.619,0,0,1,1.411-1.6l5.809-.927a1.615,1.615,0,0,1,1.845,1.6v1.8a1.62,1.62,0,0,1-1.41,1.6l-5.809.927A1.693,1.693,0,0,1,7.645,10.966ZM13.36,5.757l.119.741-5.833.93c-.08.011-.124.06-.124.119v1.8l.228.859-.118-.74,5.831-.93a.126.126,0,0,0,.124-.119v-1.8Z" fill="#fff"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -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';
}

View File

@@ -54,7 +54,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
}
private async initialize(workspaceFolders: vscode.WorkspaceFolder[]): Promise<void> {
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);

View File

@@ -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<IExtensionApi> {
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;

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<title>Artboard 10</title>
<g>
<path d="M4,6h7V3H4ZM5,4h5V5H5Z"/>
<path d="M3,0a1.732,1.732,0,0,0-.742.168,2.256,2.256,0,0,0-1.09,1.09A1.735,1.735,0,0,0,1,2V14a1.947,1.947,0,0,0,.156.777,2.018,2.018,0,0,0,1.067,1.067A1.947,1.947,0,0,0,3,16H14V0ZM3,15a.972.972,0,0,1-.391-.078,1.023,1.023,0,0,1-.531-.531,1.019,1.019,0,0,1,0-.782,1.024,1.024,0,0,1,.215-.316,1.012,1.012,0,0,1,.316-.215A.972.972,0,0,1,3,13H13v2Zm10-3H3a1.836,1.836,0,0,0-.523.074A2.194,2.194,0,0,0,2,12.281V2a.8.8,0,0,1,.09-.359,1.223,1.223,0,0,1,.23-.321,1.246,1.246,0,0,1,.321-.23A.792.792,0,0,1,3,1H13Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 684 B

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,11 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path d="M4 6H11V3H4V6ZM5 4H10V5H5V4Z" fill="white"/>
<path d="M2.99998 0C2.74326 0.000315985 2.48981 0.0577002 2.25798 0.168C1.77794 0.392083 1.39207 0.777956 1.16798 1.258C1.05778 1.48986 1.0004 1.74328 0.999984 2V14C0.998165 14.267 1.05127 14.5314 1.15598 14.777C1.36029 15.2573 1.74272 15.6397 2.22298 15.844C2.46855 15.9487 2.73303 16.0018 2.99998 16H14V0H2.99998ZM2.99998 15C2.86566 15.0013 2.73253 14.9747 2.60898 14.922C2.37107 14.8187 2.18127 14.6289 2.07798 14.391C2.02649 14.2671 1.99998 14.1342 1.99998 14C1.99998 13.8658 2.02649 13.7329 2.07798 13.609C2.12918 13.4911 2.20212 13.3839 2.29298 13.293C2.38371 13.2019 2.49095 13.1289 2.60898 13.078C2.73253 13.0253 2.86566 12.9987 2.99998 13H13V15H2.99998ZM13 12H2.99998C2.823 11.9993 2.64684 12.0243 2.47698 12.074C2.31034 12.1238 2.15022 12.1933 1.99998 12.281V2C2.00151 1.87494 2.03233 1.75199 2.08998 1.641C2.14876 1.52225 2.22643 1.41383 2.31998 1.32C2.41408 1.22676 2.52244 1.14912 2.64098 1.09C2.75186 1.03207 2.87489 1.00123 2.99998 1H13V12Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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<IEditorInputFactoryRegistry>(EditorInputFactoryExtensions.EditorInputFactories)
.registerEditorInputFactory(FileNotebookInput.ID, FileNoteBookEditorInputFactory);
@@ -348,3 +351,16 @@ registerComponentType({
selector: MimeRendererComponent.SELECTOR
});
registerCellComponent(TextCellComponent);
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchRegistry.registerWorkbenchContribution(NotebookExplorerViewletViewsContribution, LifecyclePhase.Starting);
const registry = Registry.as<IWorkbenchActionRegistry>(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")
);

View File

@@ -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<IViewsRegistry>(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<IViewContainersRegistry>(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);

View File

@@ -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<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry).registerViews));
assert(Types.isFunction(Platform.Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry).getViews));
assert(Types.isFunction(Platform.Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry).getView));
Platform.Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry).registerViews([], NOTEBOOK_VIEW_CONTAINER);
let oldcount = Platform.Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry).getViews(NOTEBOOK_VIEW_CONTAINER).length;
let d: IViewDescriptor = { id: 'notebookView-test-1', name: 'Notebooks', ctorDescriptor: new SyncDescriptor(NotebookExplorerViewPaneContainer) };
Platform.Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry).registerViews([d], NOTEBOOK_VIEW_CONTAINER);
let retrieved = Platform.Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry).getView('notebookView-test-1');
assert(d === retrieved, 'Could not register view :' + d.id + 'Retrieved: ' + retrieved);
let newCount = Platform.Registry.as<IViewsRegistry>(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<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry).registerViews([d], NOTEBOOK_VIEW_CONTAINER));
});
test('NotebookExplorer Views deregistration', function () {
assert(Types.isFunction(Platform.Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry).deregisterViews));
assert(Types.isFunction(Platform.Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry).getViews));
let d: IViewDescriptor = { id: 'notebookView-test-1', name: 'Notebooks', ctorDescriptor: new SyncDescriptor(NotebookExplorerViewPaneContainer) };
assert.doesNotThrow(() => Platform.Registry.as<IViewsRegistry>(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<ViewletRegistry>(Extensions.Viewlets).registerViewlet(v1);
let oldCount = Platform.Registry.as<ViewletRegistry>(Extensions.Viewlets).getViewlets().length;
let v1Duplicate = ViewletDescriptor.create(NotebookExplorerTestViewlet, 'notebookExplorer-test-id', 'name');
// Shouldn't register the duplicate.
Platform.Registry.as<ViewletRegistry>(Extensions.Viewlets).registerViewlet(v1Duplicate);
let newCount = Platform.Registry.as<ViewletRegistry>(Extensions.Viewlets).getViewlets().length;
assert.equal(oldCount, newCount, 'Duplicate registration of views.');
});
});

View File

@@ -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');

View File

@@ -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}`);
}
}

View File

@@ -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;

View File

@@ -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}`;
}