From a706ff4d5b172a1ddd64cac01cc71a38d6bb1b9d Mon Sep 17 00:00:00 2001 From: Lucy Zhang Date: Fri, 12 Jul 2019 10:18:46 -0700 Subject: [PATCH] Books/navigation (#6280) * added previous and next buttons * previous and next notebook API * links for prev/next notebooks * fixed first and last pages * made code more readable * addressed Kevin's comments * moved logic over to BookTreeItem * show buttons in dev mode only * added BookTreeItemFormat interface * added interface and enum * removed localize call --- extensions/notebook/src/book/bookTreeItem.ts | 118 ++++++++++++++++-- extensions/notebook/src/book/bookTreeView.ts | 87 ++++++++++--- extensions/notebook/src/extension.ts | 9 +- src/sql/azdata.proposed.d.ts | 13 ++ .../extHostNotebookDocumentsAndEditors.ts | 43 +++++++ .../mainThreadNotebookDocumentsAndEditors.ts | 21 +++- .../workbench/api/node/sqlExtHost.api.impl.ts | 3 + .../workbench/api/node/sqlExtHost.protocol.ts | 2 + .../parts/notebook/notebook.component.html | 2 + .../parts/notebook/notebook.component.ts | 52 +++++++- src/sql/workbench/parts/notebook/notebook.css | 12 ++ .../notebook/common/notebookService.ts | 10 ++ .../notebook/common/notebookServiceImpl.ts | 12 +- 13 files changed, 347 insertions(+), 37 deletions(-) diff --git a/extensions/notebook/src/book/bookTreeItem.ts b/extensions/notebook/src/book/bookTreeItem.ts index 09a34f8d60..be22d53f73 100644 --- a/extensions/notebook/src/book/bookTreeItem.ts +++ b/extensions/notebook/src/book/bookTreeItem.ts @@ -4,20 +4,118 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import * as path from 'path'; +import * as fs from 'fs'; +import * as nls from 'vscode-nls'; +const localize = nls.loadMessageBundle(); + +export enum BookTreeItemType { + Book = 'Book', + Notebook = 'Notebook', + Markdown = 'Markdown', + ExternalLink = 'ExternalLink' +} + +export interface BookTreeItemFormat { + title: string; + root: string; + tableOfContents: any[]; + page: any; + type: BookTreeItemType; +} export class BookTreeItem extends vscode.TreeItem { + private _sections: any[]; + private _uri: string; + private _previousUri: string; + private _nextUri: string; + public command: vscode.Command; - constructor( - public readonly title: string, - public readonly root: string, - public readonly tableOfContents: any[], - public readonly collapsibleState: vscode.TreeItemCollapsibleState, - public uri?: string, - public command?: vscode.Command - ) { - super(title, collapsibleState); + constructor(public book: BookTreeItemFormat) { + super(book.title, vscode.TreeItemCollapsibleState.Collapsed); + + if (book.type === BookTreeItemType.Book) { + this.collapsibleState = vscode.TreeItemCollapsibleState.Collapsed; + this._sections = book.page; + } else { + this.setPageVariables(); + this.setCommand(); + } } - contextValue = 'book'; + private setPageVariables() { + this.collapsibleState = (this.book.page.sections || this.book.page.subsections) && this.book.page.expand_sections ? + vscode.TreeItemCollapsibleState.Expanded : + this.book.page.sections || this.book.page.subsections ? + vscode.TreeItemCollapsibleState.Collapsed : + vscode.TreeItemCollapsibleState.None; + this._sections = this.book.page.sections || this.book.page.subsections; + this._uri = this.book.page.url; + let index = (this.book.tableOfContents.indexOf(this.book.page)); + this.setPreviousUri(index); + this.setNextUri(index); + } + + private setCommand() { + if (this.book.type === BookTreeItemType.Notebook) { + let pathToNotebook = path.join(this.book.root, 'content', this._uri.concat('.ipynb')); + this.command = { command: 'bookTreeView.openNotebook', title: localize('openNotebookCommand', 'Open Notebook'), arguments: [pathToNotebook], }; + } else if (this.book.type === BookTreeItemType.Markdown) { + let pathToMarkdown = path.join(this.book.root, 'content', this._uri.concat('.md')); + this.command = { command: 'bookTreeView.openMarkdown', title: localize('openMarkdownCommand', 'Open Markdown'), arguments: [pathToMarkdown], }; + } else if (this.book.type === BookTreeItemType.ExternalLink) { + this.command = { command: 'bookTreeView.openExternalLink', title: localize('openExternalLinkCommand', 'Open External Link'), arguments: [this._uri], }; + } + } + + private setPreviousUri(index: number): void { + let i = --index; + while (i > -1) { + if (this.book.tableOfContents[i].url) { + // TODO: Currently only navigating to notebooks. Need to add logic for markdown. + let pathToNotebook = path.join(this.book.root, 'content', this.book.tableOfContents[i].url.concat('.ipynb')); + if (fs.existsSync(pathToNotebook)) { + this._previousUri = pathToNotebook; + return; + } + } + i--; + } + } + + private setNextUri(index: number): void { + let i = ++index; + while (i < this.book.tableOfContents.length) { + if (this.book.tableOfContents[i].url) { + // TODO: Currently only navigating to notebooks. Need to add logic for markdown. + let pathToNotebook = path.join(this.book.root, 'content', this.book.tableOfContents[i].url.concat('.ipynb')); + if (fs.existsSync(pathToNotebook)) { + this._nextUri = pathToNotebook; + return; + } + } + i++; + } + } + + public get root(): string { + return this.book.root; + } + + public get tableOfContents(): any[] { + return this.book.tableOfContents; + } + + public get sections(): any[] { + return this._sections; + } + + public get previousUri(): string { + return this._previousUri; + } + + public get nextUri(): string { + return this._nextUri; + } } \ No newline at end of file diff --git a/extensions/notebook/src/book/bookTreeView.ts b/extensions/notebook/src/book/bookTreeView.ts index 2e24a6b20f..88bfcfc4b3 100644 --- a/extensions/notebook/src/book/bookTreeView.ts +++ b/extensions/notebook/src/book/bookTreeView.ts @@ -3,20 +3,23 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as azdata from 'azdata'; import * as vscode from 'vscode'; import * as path from 'path'; import * as fs from 'fs'; import * as yaml from 'js-yaml'; -import { BookTreeItem } from './bookTreeItem'; +import { BookTreeItem, BookTreeItemType } from './bookTreeItem'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -export class BookTreeViewProvider implements vscode.TreeDataProvider { +export class BookTreeViewProvider implements vscode.TreeDataProvider, azdata.nb.NavigationProvider { + readonly providerId: string = 'BookNavigator'; private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; private _tableOfContentsPath: string[]; + private _allNotebooks = new Map(); constructor(private workspaceRoot: string) { if (workspaceRoot !== '') { @@ -71,15 +74,14 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider { if (element) { - if (element.tableOfContents) { - return Promise.resolve(this.getSections(element.tableOfContents, element.root)); + if (element.sections) { + return Promise.resolve(this.getSections(element.tableOfContents, element.sections, element.root)); } else { return Promise.resolve([]); } @@ -88,6 +90,10 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider Array.isArray(val.sections) ? acc.concat(val).concat(this.flattenArray(val.sections)) : acc.concat(val), []); + } + private getBooks(): BookTreeItem[] { let books: BookTreeItem[] = []; for (let i in this._tableOfContentsPath) { @@ -95,7 +101,13 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider { + let notebook = this._allNotebooks.get(uri.fsPath); + let result: azdata.nb.NavigationResult; + if (notebook) { + result = { + hasNavigation: true, + previous: notebook.previousUri ? vscode.Uri.file(notebook.previousUri) : undefined, + next: notebook.nextUri ? vscode.Uri.file(notebook.nextUri) : undefined + }; + } else { + result = { + hasNavigation: false, + previous: undefined, + next: undefined + }; + } + return Promise.resolve(result); + } + } \ No newline at end of file diff --git a/extensions/notebook/src/extension.ts b/extensions/notebook/src/extension.ts index 3d6987c595..fc18462842 100644 --- a/extensions/notebook/src/extension.ts +++ b/extensions/notebook/src/extension.ts @@ -29,10 +29,11 @@ type ChooseCellType = { label: string, id: CellType }; export async function activate(extensionContext: vscode.ExtensionContext): Promise { const bookTreeViewProvider = new BookTreeViewProvider(vscode.workspace.rootPath || ''); - vscode.window.registerTreeDataProvider('bookTreeView', bookTreeViewProvider); - vscode.commands.registerCommand('bookTreeView.openNotebook', (resource) => bookTreeViewProvider.openNotebook(resource)); - vscode.commands.registerCommand('bookTreeView.openMarkdown', (resource) => bookTreeViewProvider.openMarkdown(resource)); - vscode.commands.registerCommand('bookTreeView.openExternalLink', (resource) => bookTreeViewProvider.openExternalLink(resource)); + extensionContext.subscriptions.push(vscode.window.registerTreeDataProvider('bookTreeView', bookTreeViewProvider)); + extensionContext.subscriptions.push(azdata.nb.registerNavigationProvider(bookTreeViewProvider)); + extensionContext.subscriptions.push(vscode.commands.registerCommand('bookTreeView.openNotebook', (resource) => bookTreeViewProvider.openNotebook(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.new', (context?: azdata.ConnectedContext) => { let connectionProfile: azdata.IConnectionProfile = undefined; diff --git a/src/sql/azdata.proposed.d.ts b/src/sql/azdata.proposed.d.ts index 347d780014..7c56609d7b 100644 --- a/src/sql/azdata.proposed.d.ts +++ b/src/sql/azdata.proposed.d.ts @@ -5255,6 +5255,19 @@ declare module 'azdata' { value: string; } + export function registerNavigationProvider(provider: NavigationProvider): vscode.Disposable; + + export interface NavigationProvider { + readonly providerId: string; + getNavigation(notebookUri: vscode.Uri): Thenable; + } + + export interface NavigationResult { + hasNavigation: boolean; + previous?: vscode.Uri; + next?: vscode.Uri; + } + //#endregion } diff --git a/src/sql/workbench/api/node/extHostNotebookDocumentsAndEditors.ts b/src/sql/workbench/api/node/extHostNotebookDocumentsAndEditors.ts index 5410ba10e2..233722ab87 100644 --- a/src/sql/workbench/api/node/extHostNotebookDocumentsAndEditors.ts +++ b/src/sql/workbench/api/node/extHostNotebookDocumentsAndEditors.ts @@ -13,6 +13,7 @@ import { Disposable } from 'vs/workbench/api/common/extHostTypes'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { IMainContext } from 'vs/workbench/api/common/extHost.protocol'; import { ok } from 'vs/base/common/assert'; +import { localize } from 'vs/nls'; import { SqlMainContext, INotebookDocumentsAndEditorsDelta, ExtHostNotebookDocumentsAndEditorsShape, @@ -21,10 +22,13 @@ import { import { ExtHostNotebookDocumentData } from 'sql/workbench/api/node/extHostNotebookDocumentData'; import { ExtHostNotebookEditor } from 'sql/workbench/api/node/extHostNotebookEditor'; +type Adapter = azdata.nb.NavigationProvider; export class ExtHostNotebookDocumentsAndEditors implements ExtHostNotebookDocumentsAndEditorsShape { + private static _handlePool: number = 0; private _disposables: Disposable[] = []; + private _adapters = new Map(); private _activeEditorId: string; private _proxy: MainThreadNotebookDocumentsAndEditorsShape; @@ -152,6 +156,33 @@ export class ExtHostNotebookDocumentsAndEditors implements ExtHostNotebookDocume } } + private _nextHandle(): number { + return ExtHostNotebookDocumentsAndEditors._handlePool++; + } + + private _addNewAdapter(adapter: Adapter): number { + const handle = this._nextHandle(); + this._adapters.set(handle, adapter); + return handle; + } + + private _getAdapter(id: number): T { + let adapter = this._adapters.get(id); + if (adapter === undefined) { + throw new Error('No adapter found'); + } + return adapter; + } + + $getNavigation(handle: number, notebookUri: UriComponents): Thenable { + let navProvider = this._getAdapter(handle); + if (navProvider) { + let uri = URI.revive(notebookUri); + return navProvider.getNavigation(uri); + } + throw new Error('No navigation provider found for handle ${handle}'); + } + //#endregion //#region Extension accessible methods @@ -212,5 +243,17 @@ export class ExtHostNotebookDocumentsAndEditors implements ExtHostNotebookDocume this._editors.forEach(data => result.push(data)); return result; } + + registerNavigationProvider(provider: azdata.nb.NavigationProvider): vscode.Disposable { + if (!provider || !provider.providerId) { + throw new Error(localize('providerRequired', 'A NotebookProvider with valid providerId must be passed to this method')); + } + const handle = this._addNewAdapter(provider); + this._proxy.$registerNavigationProvider(provider.providerId, handle); + return new Disposable(() => { + this._adapters.delete(handle); + }); + } + //#endregion } diff --git a/src/sql/workbench/api/node/mainThreadNotebookDocumentsAndEditors.ts b/src/sql/workbench/api/node/mainThreadNotebookDocumentsAndEditors.ts index a6522fdec7..87c6ded919 100644 --- a/src/sql/workbench/api/node/mainThreadNotebookDocumentsAndEditors.ts +++ b/src/sql/workbench/api/node/mainThreadNotebookDocumentsAndEditors.ts @@ -327,7 +327,8 @@ export class MainThreadNotebookDocumentsAndEditors extends Disposable implements @IInstantiationService private _instantiationService: IInstantiationService, @IEditorService private _editorService: IEditorService, @IEditorGroupsService private _editorGroupService: IEditorGroupsService, - @ICapabilitiesService private _capabilitiesService: ICapabilitiesService + @ICapabilitiesService private _capabilitiesService: ICapabilitiesService, + @INotebookService private readonly _notebookService: INotebookService ) { super(); if (extHostContext) { @@ -685,4 +686,22 @@ export class MainThreadNotebookDocumentsAndEditors extends Disposable implements this._modelToDisposeMap.set(editor.id, listeners); }); } + + $registerNavigationProvider(providerId: string, handle: number): void { + this._notebookService.registerNavigationProvider({ + providerId: providerId, + onNext: async (uri) => { + let result = await this._proxy.$getNavigation(handle, uri); + if (result) { + this.doOpenEditor(result.next, {}); + } + }, + onPrevious: async (uri) => { + let result = await this._proxy.$getNavigation(handle, uri); + if (result) { + this.doOpenEditor(result.previous, {}); + } + } + }); + } } diff --git a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts index 6c161c516b..21a1273833 100644 --- a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts +++ b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts @@ -501,6 +501,9 @@ export function createApiFactory( registerNotebookProvider(provider: azdata.nb.NotebookProvider): vscode.Disposable { return extHostNotebook.registerNotebookProvider(provider); }, + registerNavigationProvider(provider: azdata.nb.NavigationProvider): vscode.Disposable { + return extHostNotebookDocumentsAndEditors.registerNavigationProvider(provider); + }, CellRange: sqlExtHostTypes.CellRange, NotebookChangeKind: sqlExtHostTypes.NotebookChangeKind }; diff --git a/src/sql/workbench/api/node/sqlExtHost.protocol.ts b/src/sql/workbench/api/node/sqlExtHost.protocol.ts index 9889e52bb0..598918449d 100644 --- a/src/sql/workbench/api/node/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/node/sqlExtHost.protocol.ts @@ -913,6 +913,7 @@ export interface INotebookShowOptions { export interface ExtHostNotebookDocumentsAndEditorsShape { $acceptDocumentsAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta): void; $acceptModelChanged(strURL: UriComponents, e: INotebookModelChangedData); + $getNavigation(handle: number, uri: vscode.Uri): Thenable; } export interface MainThreadNotebookDocumentsAndEditorsShape extends IDisposable { @@ -924,6 +925,7 @@ export interface MainThreadNotebookDocumentsAndEditorsShape extends IDisposable $clearOutput(id: string, cellUri: UriComponents): Promise; $clearAllOutputs(id: string): Promise; $changeKernel(id: string, kernel: azdata.nb.IKernelInfo): Promise; + $registerNavigationProvider(providerId: string, handle: number); } export interface ExtHostExtensionManagementShape { diff --git a/src/sql/workbench/parts/notebook/notebook.component.html b/src/sql/workbench/parts/notebook/notebook.component.html index d8d7c1dd20..0bddbe2e9d 100644 --- a/src/sql/workbench/parts/notebook/notebook.component.html +++ b/src/sql/workbench/parts/notebook/notebook.component.html @@ -19,5 +19,7 @@ +
+
diff --git a/src/sql/workbench/parts/notebook/notebook.component.ts b/src/sql/workbench/parts/notebook/notebook.component.ts index bb1897a1af..f372e9552c 100644 --- a/src/sql/workbench/parts/notebook/notebook.component.ts +++ b/src/sql/workbench/parts/notebook/notebook.component.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { nb } from 'azdata'; +import * as vscode from 'vscode'; import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, OnDestroy } from '@angular/core'; import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; @@ -19,6 +20,7 @@ import { IAction, Action, IActionViewItem } from 'vs/base/common/actions'; import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import * as DOM from 'vs/base/browser/dom'; import { AngularDisposable } from 'sql/base/node/lifecycle'; import { CellTypes, CellType } from 'sql/workbench/parts/notebook/models/contracts'; @@ -50,6 +52,8 @@ import { ILogService } from 'vs/platform/log/common/log'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { LabeledMenuItemActionItem, fillInActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { Button } from 'sql/base/browser/ui/button/button'; +import { attachButtonStyler } from 'sql/platform/theme/common/styler'; import { isUndefinedOrNull } from 'vs/base/common/types'; @@ -63,6 +67,8 @@ export const NOTEBOOK_SELECTOR: string = 'notebook-component'; export class NotebookComponent extends AngularDisposable implements OnInit, OnDestroy, INotebookEditor { @ViewChild('toolbar', { read: ElementRef }) private toolbar: ElementRef; @ViewChild('container', { read: ElementRef }) private container: ElementRef; + @ViewChild('bookNav', { read: ElementRef }) private bookNav: ElementRef; + private _model: NotebookModel; private _isInErrorState: boolean = false; private _errorMessage: string; @@ -127,6 +133,10 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this)); this.updateTheme(this.themeService.getColorTheme()); this.initActionBar(); + if (this.contextKeyService.getContextKeyValue('isDevelopment') && + this.contextKeyService.getContextKeyValue('bookOpened')) { + this.initNavSection(); + } this.setScrollPosition(); this.doLoad(); } @@ -392,7 +402,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe this.notificationService.error(error); } - protected initActionBar() { + protected initActionBar(): void { let kernelContainer = document.createElement('div'); let kernelDropdown = new KernelsDropdown(kernelContainer, this.contextViewService, this.modelReady); kernelDropdown.render(kernelContainer); @@ -428,6 +438,24 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe { action: this._runAllCellsAction }, { action: clearResultsButton } ]); + } + + protected initNavSection(): void { + this.addButton(localize('previousButtonLabel', "Previous"), + () => this.previousPage()); + this.addButton(localize('nextButtonLabel', "Next"), + () => this.nextPage()); + } + + private addButton(label: string, onDidClick?: () => void): void { + const container = DOM.append(this.bookNav.nativeElement, DOM.$('.dialog-message-button')); + let button = new Button(container); + button.icon = ''; + button.label = label; + if (onDidClick) { + this._register(button.onDidClick(onDidClick)); + } + this._register(attachButtonStyler(button, this.themeService)); } @@ -582,6 +610,28 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe } } + public async nextPage(): Promise { + try { + let navProvider = this.notebookService.getNavigationProvider(this.model.notebookUri); + if (navProvider) { + navProvider.onNext(this.model.notebookUri); + } + } catch (error) { + this.notificationService.error(notebookUtils.getErrorMessage(error)); + } + } + + public previousPage() { + try { + let navProvider = this.notebookService.getNavigationProvider(this.model.notebookUri); + if (navProvider) { + navProvider.onPrevious(this.model.notebookUri); + } + } catch (error) { + this.notificationService.error(notebookUtils.getErrorMessage(error)); + } + } + getSections(): INotebookSection[] { return this.getSectionElements(); } diff --git a/src/sql/workbench/parts/notebook/notebook.css b/src/sql/workbench/parts/notebook/notebook.css index 7649c8bea1..73d001ebc2 100644 --- a/src/sql/workbench/parts/notebook/notebook.css +++ b/src/sql/workbench/parts/notebook/notebook.css @@ -125,3 +125,15 @@ .hc-black .monaco-workbench .notebook-action.new-notebook { background: url('./media/dark/new_notebook_inverse.svg') center center no-repeat; } + +.notebookEditor .book-nav { + display: flex; + align-items: center; + justify-content: center; + margin: 5px; +} + +.notebookEditor .book-nav .dialog-message-button { + min-width: 60px; + margin-right: 10px; +} \ No newline at end of file diff --git a/src/sql/workbench/services/notebook/common/notebookService.ts b/src/sql/workbench/services/notebook/common/notebookService.ts index 7dcef36598..d01745b697 100644 --- a/src/sql/workbench/services/notebook/common/notebookService.ts +++ b/src/sql/workbench/services/notebook/common/notebookService.ts @@ -45,6 +45,10 @@ export interface INotebookService { */ unregisterProvider(providerId: string): void; + registerNavigationProvider(provider: INavigationProvider): void; + + getNavigationProvider(notebookUri: URI): INavigationProvider; + getSupportedFileExtensions(): string[]; getProvidersForFileType(fileType: string): string[]; @@ -149,3 +153,9 @@ export interface INotebookEditor { getSections(): INotebookSection[]; navigateToSection(sectionId: string): void; } + +export interface INavigationProvider { + providerId: string; + onNext(uri: URI): void; + onPrevious(uri: URI): void; +} diff --git a/src/sql/workbench/services/notebook/common/notebookServiceImpl.ts b/src/sql/workbench/services/notebook/common/notebookServiceImpl.ts index 4f413558da..b1908eb4f7 100644 --- a/src/sql/workbench/services/notebook/common/notebookServiceImpl.ts +++ b/src/sql/workbench/services/notebook/common/notebookServiceImpl.ts @@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { INotebookService, INotebookManager, INotebookProvider, - DEFAULT_NOTEBOOK_FILETYPE, INotebookEditor, SQL_NOTEBOOK_PROVIDER, OVERRIDE_EDITOR_THEMING_SETTING + DEFAULT_NOTEBOOK_FILETYPE, INotebookEditor, SQL_NOTEBOOK_PROVIDER, OVERRIDE_EDITOR_THEMING_SETTING, INavigationProvider } from 'sql/workbench/services/notebook/common/notebookService'; import { RenderMimeRegistry } from 'sql/workbench/parts/notebook/outputs/registry'; import { standardRendererFactories } from 'sql/workbench/parts/notebook/outputs/factories'; @@ -97,6 +97,7 @@ export class NotebookService extends Disposable implements INotebookService { private _trustedNotebooksMemento: Memento; private _mimeRegistry: RenderMimeRegistry; private _providers: Map = new Map(); + private _navigationProviders: Map = new Map(); private _managersMap: Map = new Map(); private _onNotebookEditorAdd = new Emitter(); private _onNotebookEditorRemove = new Emitter(); @@ -269,6 +270,15 @@ export class NotebookService extends Disposable implements INotebookService { this._providers.delete(providerId); } + registerNavigationProvider(provider: INavigationProvider): void { + this._navigationProviders.set(provider.providerId, provider); + } + + getNavigationProvider(): INavigationProvider { + let provider = this._navigationProviders.size > 0 ? this._navigationProviders.values().next().value : undefined; + return provider; + } + get isRegistrationComplete(): boolean { return this._isRegistrationComplete; }