From 8957d055f5c693967ad31d66549d0fa0aeb76296 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Fri, 9 Sep 2022 11:06:07 -0700 Subject: [PATCH 1/9] first proposed API steps, added types and API based on cell status provider --- .../workbench/api/common/extHost.api.impl.ts | 6 ++++- .../workbench/api/common/extHostNotebook.ts | 22 +++++++++++++++++++ src/vs/workbench/api/common/extHostTypes.ts | 4 ++++ .../common/extensionsApiProposals.ts | 1 + ...de.proposed.notebookStatusBarProvider.d.ts | 22 +++++++++++++++++++ 5 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/vscode-dts/vscode.proposed.notebookStatusBarProvider.d.ts diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 9a7f2faa5b588..d667f537a5a37 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1160,7 +1160,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I onDidChangeNotebookCellExecutionState(listener, thisArgs?, disposables?) { checkProposedApiEnabled(extension, 'notebookCellExecutionState'); return extHostNotebookKernels.onDidChangeNotebookCellExecutionState(listener, thisArgs, disposables); - } + }, + registerNotebookStatusBarItemProvider: (notebookType: string, provider: vscode.NotebookStatusBarItemProvider) => { + return extHostNotebook.registerNotebookStatusBarItemProvider(extension, notebookType, provider); + }, }; return { @@ -1326,6 +1329,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I NotebookCellOutput: extHostTypes.NotebookCellOutput, NotebookCellOutputItem: extHostTypes.NotebookCellOutputItem, NotebookCellStatusBarItem: extHostTypes.NotebookCellStatusBarItem, + NotebookStatusBarItem: extHostTypes.NotebookStatusBarItem, NotebookControllerAffinity: extHostTypes.NotebookControllerAffinity, NotebookEdit: extHostTypes.NotebookEdit, PortAttributes: extHostTypes.PortAttributes, diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 6a1df44dc0e24..d8aecd9b7e43c 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -219,6 +219,28 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { }); } + registerNotebookStatusBarItemProvider(extension: IExtensionDescription, notebookType: string, provider: vscode.NotebookStatusBarItemProvider) { + + // IANHU + // const handle = ExtHostNotebookController._notebookStatusBarItemProviderHandlePool++; + // const eventHandle = typeof provider.onDidChangeCellStatusBarItems === 'function' ? ExtHostNotebookController._notebookStatusBarItemProviderHandlePool++ : undefined; + + // this._notebookStatusBarItemProviders.set(handle, provider); + // this._notebookProxy.$registerNotebookCellStatusBarItemProvider(handle, eventHandle, notebookType); + + // let subscription: vscode.Disposable | undefined; + // if (eventHandle !== undefined) { + // subscription = provider.onDidChangeCellStatusBarItems!(_ => this._notebookProxy.$emitCellStatusBarEvent(eventHandle)); + // } + + // return new extHostTypes.Disposable(() => { + // this._notebookStatusBarItemProviders.delete(handle); + // this._notebookProxy.$unregisterNotebookCellStatusBarItemProvider(handle, eventHandle); + // subscription?.dispose(); + // }); + return new extHostTypes.Disposable(() => { }); + } + async createNotebookDocument(options: { viewType: string; content?: vscode.NotebookData }): Promise { const canonicalUri = await this._notebookDocumentsProxy.$tryCreateNotebook({ viewType: options.viewType, diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index d7f804b0affb7..ea7fdab3902f7 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -3579,6 +3579,10 @@ export class NotebookCellStatusBarItem { public text: string, public alignment: NotebookCellStatusBarAlignment) { } } +export class NotebookStatusBarItem { + constructor( + public text: string) { } +} export enum NotebookControllerAffinity { diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 62d6f6b280ef5..80cd155550761 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -47,6 +47,7 @@ export const allApiProposals = Object.freeze({ notebookLiveShare: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookLiveShare.d.ts', notebookMessaging: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts', notebookMime: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookMime.d.ts', + notebookStatusBarProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookStatusBarProvider.d.ts', portsAttributes: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.portsAttributes.d.ts', quickPickSortByLabel: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.quickPickSortByLabel.d.ts', resolvers: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.resolvers.d.ts', diff --git a/src/vscode-dts/vscode.proposed.notebookStatusBarProvider.d.ts b/src/vscode-dts/vscode.proposed.notebookStatusBarProvider.d.ts new file mode 100644 index 0000000000000..feeb03ee1d4b7 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookStatusBarProvider.d.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + export namespace notebooks { + export function registerNotebookStatusBarItemProvider(notebookType: string, provider: NotebookStatusBarItemProvider): Disposable; + } + export interface NotebookStatusBarItemProvider { + onDidChangeStatusBarItems?: Event; + provideStatusBarItems(document: NotebookDocument, token: CancellationToken): ProviderResult; + } + export class NotebookStatusBarItem { + text: string; + command?: string | Command; + tooltip?: string; + priority?: number; + accessibilityInformation?: AccessibilityInformation; + constructor(text: string); + } +} From d94757c37fd9427bff2da2639b7ea9c3818d6155 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Fri, 9 Sep 2022 12:07:49 -0700 Subject: [PATCH 2/9] proposed API now routed most of the way to extension host --- .../api/browser/mainThreadNotebook.ts | 53 ++++++++++++- .../workbench/api/common/extHost.protocol.ts | 14 ++++ .../workbench/api/common/extHostNotebook.ts | 76 +++++++++++++------ .../api/common/extHostTypeConverters.ts | 15 +++- .../contrib/notebook/common/notebookCommon.ts | 23 ++++++ .../common/notebookStatusBarService.ts | 24 ++++++ 6 files changed, 179 insertions(+), 26 deletions(-) create mode 100644 src/vs/workbench/contrib/notebook/common/notebookStatusBarService.ts diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index d6b554ebb17ab..90c91d889ad45 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -11,7 +11,8 @@ import { URI } from 'vs/base/common/uri'; import { NotebookDto } from 'vs/workbench/api/browser/mainThreadNotebookDto'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; -import { INotebookCellStatusBarItemProvider, INotebookContributionData, NotebookData as NotebookData, NotebookExtensionDescription, TransientCellMetadata, TransientDocumentMetadata, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookStatusBarService'; +import { INotebookCellStatusBarItemProvider, INotebookContributionData, INotebookStatusBarItemProvider, NotebookData as NotebookData, NotebookExtensionDescription, TransientCellMetadata, TransientDocumentMetadata, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookContentProvider, INotebookService, SimpleNotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookService'; import { SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import { ExtHostContext, ExtHostNotebookShape, MainContext, MainThreadNotebookShape } from '../common/extHost.protocol'; @@ -29,11 +30,13 @@ export class MainThreadNotebooks implements MainThreadNotebookShape { private readonly _notebookProviders = new Map(); private readonly _notebookSerializer = new Map(); private readonly _notebookCellStatusBarRegistrations = new Map(); + private readonly _notebookStatusBarRegistrations = new Map(); constructor( extHostContext: IExtHostContext, @INotebookService private readonly _notebookService: INotebookService, @INotebookCellStatusBarService private readonly _cellStatusBarService: INotebookCellStatusBarService, + @INotebookStatusBarService private readonly _statusBarService: INotebookStatusBarService, @ILogService private readonly _logService: ILogService, ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook); @@ -185,6 +188,54 @@ export class MainThreadNotebooks implements MainThreadNotebookShape { unregisterThing(eventHandle); } } + + $emitStatusBarEvent(eventHandle: number): void { + const emitter = this._notebookStatusBarRegistrations.get(eventHandle); + if (emitter instanceof Emitter) { + emitter.fire(undefined); + } + } + + async $registerNotebookStatusBarItemProvider(handle: number, eventHandle: number | undefined, viewType: string): Promise { + const that = this; + const provider: INotebookStatusBarItemProvider = { + async provideStatusBarItems(uri: URI, token: CancellationToken) { + const result = await that._proxy.$provideNotebookStatusBarItems(handle, uri, token); + return { + items: result?.items ?? [], + dispose() { + if (result) { + that._proxy.$releaseNotebookStatusBarItems(result.cacheId); + } + } + }; + }, + viewType + }; + + if (typeof eventHandle === 'number') { + const emitter = new Emitter(); + this._notebookStatusBarRegistrations.set(eventHandle, emitter); + provider.onDidChangeStatusBarItems = emitter.event; + } + + const disposable = this._statusBarService.registerStatusBarItemProvider(provider); + this._notebookStatusBarRegistrations.set(handle, disposable); + } + + async $unregisterNotebookStatusBarItemProvider(handle: number, eventHandle: number | undefined): Promise { + const unregisterThing = (handle: number) => { + const entry = this._notebookStatusBarRegistrations.get(handle); + if (entry) { + this._notebookStatusBarRegistrations.get(handle)?.dispose(); + this._notebookStatusBarRegistrations.delete(handle); + } + }; + unregisterThing(handle); + if (typeof eventHandle === 'number') { + unregisterThing(eventHandle); + } + } } CommandsRegistry.registerCommand('_executeDataToNotebook', async (accessor, ...args) => { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 98e4262551897..fe588895012d7 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -960,6 +960,13 @@ export interface INotebookCellStatusBarListDto { cacheId: number; } +export type INotebookStatusBarEntryDto = Dto; + +export interface INotebookStatusBarListDto { + items: INotebookStatusBarEntryDto[]; + cacheId: number; +} + export interface MainThreadNotebookShape extends IDisposable { $registerNotebookProvider(extension: notebookCommon.NotebookExtensionDescription, viewType: string, options: notebookCommon.TransientOptions, registration: notebookCommon.INotebookContributionData | undefined): Promise; $updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientCellMetadata: notebookCommon.TransientCellMetadata; transientDocumentMetadata: notebookCommon.TransientDocumentMetadata }): Promise; @@ -971,6 +978,10 @@ export interface MainThreadNotebookShape extends IDisposable { $registerNotebookCellStatusBarItemProvider(handle: number, eventHandle: number | undefined, viewType: string): Promise; $unregisterNotebookCellStatusBarItemProvider(handle: number, eventHandle: number | undefined): Promise; $emitCellStatusBarEvent(eventHandle: number): void; + + $registerNotebookStatusBarItemProvider(handle: number, eventHandle: number | undefined, viewType: string): Promise; + $unregisterNotebookStatusBarItemProvider(handle: number, eventHandle: number | undefined): Promise; + $emitStatusBarEvent(eventHandle: number): void; } export interface MainThreadNotebookEditorsShape extends IDisposable { @@ -2055,6 +2066,9 @@ export interface ExtHostNotebookShape extends ExtHostNotebookDocumentsAndEditors $provideNotebookCellStatusBarItems(handle: number, uri: UriComponents, index: number, token: CancellationToken): Promise; $releaseNotebookCellStatusBarItems(id: number): void; + $provideNotebookStatusBarItems(handle: number, uri: UriComponents, token: CancellationToken): Promise; + $releaseNotebookStatusBarItems(id: number): void; + $openNotebook(viewType: string, uri: UriComponents, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, token: CancellationToken): Promise>; $saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise; $saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index d8aecd9b7e43c..290f096a81218 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -16,7 +16,7 @@ import { assertIsDefined } from 'vs/base/common/types'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { Cache } from 'vs/workbench/api/common/cache'; -import { ExtHostNotebookShape, IMainContext, IModelAddedData, INotebookCellStatusBarListDto, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorAddData, MainContext, MainThreadNotebookDocumentsShape, MainThreadNotebookEditorsShape, MainThreadNotebookShape, NotebookDataDto } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostNotebookShape, IMainContext, IModelAddedData, INotebookCellStatusBarListDto, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorAddData, INotebookStatusBarListDto, MainContext, MainThreadNotebookDocumentsShape, MainThreadNotebookEditorsShape, MainThreadNotebookShape, NotebookDataDto } from 'vs/workbench/api/common/extHost.protocol'; import { ApiCommand, ApiCommandArgument, ApiCommandResult, CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; @@ -36,6 +36,7 @@ type NotebookContentProviderData = { }; export class ExtHostNotebookController implements ExtHostNotebookShape { + private static _notebookCellStatusBarItemProviderHandlePool: number = 0; private static _notebookStatusBarItemProviderHandlePool: number = 0; private readonly _notebookProxy: MainThreadNotebookShape; @@ -43,7 +44,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { private readonly _notebookEditorsProxy: MainThreadNotebookEditorsShape; private readonly _notebookContentProviders = new Map(); - private readonly _notebookStatusBarItemProviders = new Map(); + private readonly _notebookCellStatusBarItemProviders = new Map(); + private readonly _notebookStatusBarItemProviders = new Map(); private readonly _documents = new ResourceMap(); private readonly _editors = new Map(); private readonly _commandsConverter: CommandsConverter; @@ -68,7 +70,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { private _onDidChangeVisibleNotebookEditors = new Emitter(); onDidChangeVisibleNotebookEditors = this._onDidChangeVisibleNotebookEditors.event; - private _statusBarCache = new Cache('NotebookCellStatusBarCache'); + private _cellStatusBarCache = new Cache('NotebookCellStatusBarCache'); + private _statusBarCache = new Cache('NotebookStatusBarCache'); constructor( mainContext: IMainContext, @@ -201,10 +204,10 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { registerNotebookCellStatusBarItemProvider(extension: IExtensionDescription, notebookType: string, provider: vscode.NotebookCellStatusBarItemProvider) { - const handle = ExtHostNotebookController._notebookStatusBarItemProviderHandlePool++; - const eventHandle = typeof provider.onDidChangeCellStatusBarItems === 'function' ? ExtHostNotebookController._notebookStatusBarItemProviderHandlePool++ : undefined; + const handle = ExtHostNotebookController._notebookCellStatusBarItemProviderHandlePool++; + const eventHandle = typeof provider.onDidChangeCellStatusBarItems === 'function' ? ExtHostNotebookController._notebookCellStatusBarItemProviderHandlePool++ : undefined; - this._notebookStatusBarItemProviders.set(handle, provider); + this._notebookCellStatusBarItemProviders.set(handle, provider); this._notebookProxy.$registerNotebookCellStatusBarItemProvider(handle, eventHandle, notebookType); let subscription: vscode.Disposable | undefined; @@ -213,7 +216,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { } return new extHostTypes.Disposable(() => { - this._notebookStatusBarItemProviders.delete(handle); + this._notebookCellStatusBarItemProviders.delete(handle); this._notebookProxy.$unregisterNotebookCellStatusBarItemProvider(handle, eventHandle); subscription?.dispose(); }); @@ -221,24 +224,22 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { registerNotebookStatusBarItemProvider(extension: IExtensionDescription, notebookType: string, provider: vscode.NotebookStatusBarItemProvider) { - // IANHU - // const handle = ExtHostNotebookController._notebookStatusBarItemProviderHandlePool++; - // const eventHandle = typeof provider.onDidChangeCellStatusBarItems === 'function' ? ExtHostNotebookController._notebookStatusBarItemProviderHandlePool++ : undefined; + const handle = ExtHostNotebookController._notebookStatusBarItemProviderHandlePool++; + const eventHandle = typeof provider.onDidChangeStatusBarItems === 'function' ? ExtHostNotebookController._notebookStatusBarItemProviderHandlePool++ : undefined; - // this._notebookStatusBarItemProviders.set(handle, provider); - // this._notebookProxy.$registerNotebookCellStatusBarItemProvider(handle, eventHandle, notebookType); + this._notebookStatusBarItemProviders.set(handle, provider); + this._notebookProxy.$registerNotebookStatusBarItemProvider(handle, eventHandle, notebookType); - // let subscription: vscode.Disposable | undefined; - // if (eventHandle !== undefined) { - // subscription = provider.onDidChangeCellStatusBarItems!(_ => this._notebookProxy.$emitCellStatusBarEvent(eventHandle)); - // } + let subscription: vscode.Disposable | undefined; + if (eventHandle !== undefined) { + subscription = provider.onDidChangeStatusBarItems!(_ => this._notebookProxy.$emitStatusBarEvent(eventHandle)); + } - // return new extHostTypes.Disposable(() => { - // this._notebookStatusBarItemProviders.delete(handle); - // this._notebookProxy.$unregisterNotebookCellStatusBarItemProvider(handle, eventHandle); - // subscription?.dispose(); - // }); - return new extHostTypes.Disposable(() => { }); + return new extHostTypes.Disposable(() => { + this._notebookStatusBarItemProviders.delete(handle); + this._notebookProxy.$unregisterNotebookStatusBarItemProvider(handle, eventHandle); + subscription?.dispose(); + }); } async createNotebookDocument(options: { viewType: string; content?: vscode.NotebookData }): Promise { @@ -295,7 +296,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { } async $provideNotebookCellStatusBarItems(handle: number, uri: UriComponents, index: number, token: CancellationToken): Promise { - const provider = this._notebookStatusBarItemProviders.get(handle); + const provider = this._notebookCellStatusBarItemProviders.get(handle); const revivedUri = URI.revive(uri); const document = this._documents.get(revivedUri); if (!document || !provider) { @@ -312,6 +313,33 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { return undefined; } + const disposables = new DisposableStore(); + const cacheId = this._cellStatusBarCache.add([disposables]); + const resultArr = Array.isArray(result) ? result : [result]; + const items = resultArr.map(item => typeConverters.NotebookCellStatusBarItem.from(item, this._commandsConverter, disposables)); + return { + cacheId, + items + }; + } + + $releaseNotebookCellStatusBarItems(cacheId: number): void { + this._cellStatusBarCache.delete(cacheId); + } + + async $provideNotebookStatusBarItems(handle: number, uri: UriComponents, token: CancellationToken): Promise { + const provider = this._notebookStatusBarItemProviders.get(handle); + const revivedUri = URI.revive(uri); + const document = this._documents.get(revivedUri); + if (!document || !provider) { + return; + } + + const result = await provider.provideStatusBarItems(document.apiNotebook, token); + if (!result) { + return undefined; + } + const disposables = new DisposableStore(); const cacheId = this._statusBarCache.add([disposables]); const resultArr = Array.isArray(result) ? result : [result]; @@ -322,7 +350,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { }; } - $releaseNotebookCellStatusBarItems(cacheId: number): void { + $releaseNotebookStatusBarItems(cacheId: number): void { this._statusBarCache.delete(cacheId); } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index aba6d2d85a2b5..455706091d1d0 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1691,7 +1691,7 @@ export namespace NotebookExclusiveDocumentPattern { } } -export namespace NotebookStatusBarItem { +export namespace NotebookCellStatusBarItem { export function from(item: vscode.NotebookCellStatusBarItem, commandsConverter: Command.ICommandsConverter, disposables: DisposableStore): notebooks.INotebookCellStatusBarItem { const command = typeof item.command === 'string' ? { title: '', command: item.command } : item.command; return { @@ -1705,6 +1705,19 @@ export namespace NotebookStatusBarItem { } } +export namespace NotebookStatusBarItem { + export function from(item: vscode.NotebookStatusBarItem, commandsConverter: Command.ICommandsConverter, disposables: DisposableStore): notebooks.INotebookStatusBarItem { + const command = typeof item.command === 'string' ? { title: '', command: item.command } : item.command; + return { + command: commandsConverter.toInternal(command, disposables), // TODO@roblou + text: item.text, + tooltip: item.tooltip, + accessibilityInformation: item.accessibilityInformation, + priority: item.priority + }; + } +} + export namespace NotebookDocumentContentOptions { export function from(options: vscode.NotebookDocumentContentOptions | undefined): notebooks.TransientOptions { return { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 2e0b1580f127a..4ef551f1bcef3 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -885,6 +885,12 @@ export interface INotebookCellStatusBarItemProvider { provideCellStatusBarItems(uri: URI, index: number, token: CancellationToken): Promise; } +export interface INotebookStatusBarItemProvider { + viewType: string; + onDidChangeStatusBarItems?: Event; + provideStatusBarItems(uri: URI, token: CancellationToken): Promise; +} + export interface INotebookDiffResult { cellsDiff: IDiffResult; @@ -909,6 +915,23 @@ export interface INotebookCellStatusBarItemList { dispose?(): void; } +export interface INotebookStatusBarItem { + readonly text: string; + readonly priority?: number; + readonly color?: string | ThemeColor; + readonly backgroundColor?: string | ThemeColor; + readonly tooltip?: string; + readonly command?: string | Command; + readonly accessibilityInformation?: IAccessibilityInformation; + readonly opacity?: string; + readonly onlyShowWhenActive?: boolean; +} + +export interface INotebookStatusBarItemList { + items: INotebookStatusBarItem[]; + dispose?(): void; +} + export type ShowCellStatusBarType = 'hidden' | 'visible' | 'visibleAfterExecute'; export const NotebookSetting = { diff --git a/src/vs/workbench/contrib/notebook/common/notebookStatusBarService.ts b/src/vs/workbench/contrib/notebook/common/notebookStatusBarService.ts new file mode 100644 index 0000000000000..267e08dfdf2fe --- /dev/null +++ b/src/vs/workbench/contrib/notebook/common/notebookStatusBarService.ts @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Event } from 'vs/base/common/event'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { INotebookStatusBarItemList, INotebookStatusBarItemProvider } from 'vs/workbench/contrib/notebook/common/notebookCommon'; + +export const INotebookStatusBarService = createDecorator('notebookStatusBarService'); + +export interface INotebookStatusBarService { + readonly _serviceBrand: undefined; + + readonly onDidChangeProviders: Event; + readonly onDidChangeItems: Event; + + registerStatusBarItemProvider(provider: INotebookStatusBarItemProvider): IDisposable; + + getStatusBarItemsForDocument(docUri: URI, viewType: string, token: CancellationToken): Promise; +} From 9e3545f2fc5936d96e580a93a9c583cd2fd486e2 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Mon, 12 Sep 2022 13:50:54 -0700 Subject: [PATCH 3/9] add server reg --- .../notebook/browser/notebook.contribution.ts | 3 ++ .../services/notebookStatusBarServiceImpl.ts | 53 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 src/vs/workbench/contrib/notebook/browser/services/notebookStatusBarServiceImpl.ts diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 489c3a3670d10..8f4bfbbadf10a 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -39,6 +39,8 @@ import { INotebookEditorWorkerService } from 'vs/workbench/contrib/notebook/comm import { NotebookEditorWorkerServiceImpl } from 'vs/workbench/contrib/notebook/browser/services/notebookWorkerServiceImpl'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; import { NotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/browser/services/notebookCellStatusBarServiceImpl'; +import { INotebookStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookStatusBarService'; +import { NotebookStatusBarService } from 'vs/workbench/contrib/notebook/browser/services/notebookStatusBarServiceImpl'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { NotebookEditorWidgetService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; @@ -701,6 +703,7 @@ registerSingleton(INotebookService, NotebookService, false); registerSingleton(INotebookEditorWorkerService, NotebookEditorWorkerServiceImpl, false); registerSingleton(INotebookEditorModelResolverService, NotebookModelResolverServiceImpl, true); registerSingleton(INotebookCellStatusBarService, NotebookCellStatusBarService, true); +registerSingleton(INotebookStatusBarService, NotebookStatusBarService, true); registerSingleton(INotebookEditorService, NotebookEditorWidgetService, true); registerSingleton(INotebookKernelService, NotebookKernelService, true); registerSingleton(INotebookExecutionService, NotebookExecutionService, true); diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookStatusBarServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookStatusBarServiceImpl.ts new file mode 100644 index 0000000000000..a0711bf683031 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookStatusBarServiceImpl.ts @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from 'vs/base/common/cancellation'; +import { onUnexpectedExternalError } from 'vs/base/common/errors'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { INotebookStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookStatusBarService'; +import { INotebookStatusBarItemList, INotebookStatusBarItemProvider } from 'vs/workbench/contrib/notebook/common/notebookCommon'; + +export class NotebookStatusBarService extends Disposable implements INotebookStatusBarService { + + readonly _serviceBrand: undefined; + + private readonly _onDidChangeProviders = this._register(new Emitter()); + readonly onDidChangeProviders: Event = this._onDidChangeProviders.event; + + private readonly _onDidChangeItems = this._register(new Emitter()); + readonly onDidChangeItems: Event = this._onDidChangeItems.event; + + private readonly _providers: INotebookStatusBarItemProvider[] = []; + + registerStatusBarItemProvider(provider: INotebookStatusBarItemProvider): IDisposable { + this._providers.push(provider); + let changeListener: IDisposable | undefined; + if (provider.onDidChangeStatusBarItems) { + changeListener = provider.onDidChangeStatusBarItems(() => this._onDidChangeItems.fire()); + } + + this._onDidChangeProviders.fire(); + + return toDisposable(() => { + changeListener?.dispose(); + const idx = this._providers.findIndex(p => p === provider); + this._providers.splice(idx, 1); + }); + } + + async getStatusBarItemsForDocument(docUri: URI, viewType: string, token: CancellationToken): Promise { + const providers = this._providers.filter(p => p.viewType === viewType || p.viewType === '*'); + return await Promise.all(providers.map(async p => { + try { + return await p.provideStatusBarItems(docUri, token) ?? { items: [] }; + } catch (e) { + onUnexpectedExternalError(e); + return { items: [] }; + } + })); + } +} From 65ad29fa42b7dbbf39a4ba6f9eefd43de31d91a1 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Mon, 12 Sep 2022 15:07:43 -0700 Subject: [PATCH 4/9] rename old cell controller --- ...ontroller.ts => contributedCellStatusBarItemController.ts} | 4 ++-- .../contrib/notebook/browser/notebook.contribution.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/{contributedStatusBarItemController.ts => contributedCellStatusBarItemController.ts} (96%) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/contributedStatusBarItemController.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/contributedCellStatusBarItemController.ts similarity index 96% rename from src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/contributedStatusBarItemController.ts rename to src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/contributedCellStatusBarItemController.ts index 7e3796f534220..6657a41c4ec84 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/contributedStatusBarItemController.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/contributedCellStatusBarItemController.ts @@ -12,7 +12,7 @@ import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/brow import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; import { INotebookCellStatusBarItemList } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -export class ContributedStatusBarItemController extends Disposable implements INotebookEditorContribution { +export class ContributedCellStatusBarItemController extends Disposable implements INotebookEditorContribution { static id: string = 'workbench.notebook.statusBar.contributed'; private readonly _visibleCells = new Map(); @@ -135,4 +135,4 @@ class CellStatusBarHelper extends Disposable { } } -registerNotebookContribution(ContributedStatusBarItemController.id, ContributedStatusBarItemController); +registerNotebookContribution(ContributedCellStatusBarItemController.id, ContributedCellStatusBarItemController); diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 8f4bfbbadf10a..0cc0340573a1d 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -81,7 +81,7 @@ import 'vs/workbench/contrib/notebook/browser/contrib/navigation/arrow'; import 'vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline'; import 'vs/workbench/contrib/notebook/browser/contrib/profile/notebookProfile'; import 'vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders'; -import 'vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/contributedStatusBarItemController'; +import 'vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/contributedCellStatusBarItemController'; import 'vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController'; import 'vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar'; import 'vs/workbench/contrib/notebook/browser/contrib/undoRedo/notebookUndoRedo'; From d3b588cdbe6de70f0c03e8052d53839000f9351d Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Mon, 12 Sep 2022 15:56:33 -0700 Subject: [PATCH 5/9] renames --- .../cellStatusBar/executionStatusBarItemController.ts | 6 +++--- .../contrib/notebook/browser/notebookEditor.ts | 4 ++-- .../browser/viewParts/notebookEditorToolbar.ts | 10 +++++----- .../browser/viewParts/notebookKernelActionViewItem.ts | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts index 8acf11d042ae2..cb3bd4ba7074e 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts @@ -28,7 +28,7 @@ export function formatCellDuration(duration: number): string { } } -export class NotebookStatusBarController extends Disposable { +export class NotebookExecutionStatusBarController extends Disposable { private readonly _visibleCells = new Map(); private readonly _observer: NotebookVisibleCellObserver; @@ -80,7 +80,7 @@ export class ExecutionStateCellStatusBarContrib extends Disposable implements IN @IInstantiationService instantiationService: IInstantiationService ) { super(); - this._register(new NotebookStatusBarController(notebookEditor, (vm, cell) => instantiationService.createInstance(ExecutionStateCellStatusBarItem, vm, cell))); + this._register(new NotebookExecutionStatusBarController(notebookEditor, (vm, cell) => instantiationService.createInstance(ExecutionStateCellStatusBarItem, vm, cell))); } } registerNotebookContribution(ExecutionStateCellStatusBarContrib.id, ExecutionStateCellStatusBarContrib); @@ -203,7 +203,7 @@ export class TimerCellStatusBarContrib extends Disposable implements INotebookEd notebookEditor: INotebookEditor, @IInstantiationService instantiationService: IInstantiationService) { super(); - this._register(new NotebookStatusBarController(notebookEditor, (vm, cell) => instantiationService.createInstance(TimerCellStatusBarItem, vm, cell))); + this._register(new NotebookExecutionStatusBarController(notebookEditor, (vm, cell) => instantiationService.createInstance(TimerCellStatusBarItem, vm, cell))); } } registerNotebookContribution(TimerCellStatusBarContrib.id, TimerCellStatusBarContrib); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index cd5dca1d052be..ecc7f2596bad3 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -29,7 +29,7 @@ import { SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/controll import { INotebookEditorOptions, INotebookEditorViewState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { IBorrowValue, INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; -import { NotebooKernelActionViewItem } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem'; +import { NotebookKernelActionViewItem } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { NOTEBOOK_EDITOR_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput'; @@ -124,7 +124,7 @@ export class NotebookEditor extends EditorPane implements IEditorPaneWithSelecti override getActionViewItem(action: IAction): IActionViewItem | undefined { if (action.id === SELECT_KERNEL_ID) { // this is being disposed by the consumer - return this._instantiationService.createInstance(NotebooKernelActionViewItem, action, this); + return this._instantiationService.createInstance(NotebookKernelActionViewItem, action, this); } return undefined; } diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts index c80acf0d2ece3..93ad254fa8bf4 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts @@ -22,7 +22,7 @@ import { registerThemingParticipant } from 'vs/platform/theme/common/themeServic import { SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { NOTEBOOK_EDITOR_ID, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { NotebooKernelActionViewItem } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem'; +import { NotebookKernelActionViewItem } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem'; import { ActionViewWithLabel } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; @@ -64,7 +64,7 @@ class FixedLabelStrategy implements IActionLayoutStrategy { actionProvider(action: IAction) { if (action.id === SELECT_KERNEL_ID) { // // this is being disposed by the consumer - return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor); + return this.instantiationService.createInstance(NotebookKernelActionViewItem, action, this.notebookEditor); } const a = this.editorToolbar.primaryActions.find(a => a.action.id === action.id); @@ -121,7 +121,7 @@ class FixedLabellessStrategy extends FixedLabelStrategy { override actionProvider(action: IAction) { if (action.id === SELECT_KERNEL_ID) { // // this is being disposed by the consumer - return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor); + return this.instantiationService.createInstance(NotebookKernelActionViewItem, action, this.notebookEditor); } return action instanceof MenuItemAction ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined) : undefined; @@ -139,7 +139,7 @@ class DynamicLabelStrategy implements IActionLayoutStrategy { actionProvider(action: IAction) { if (action.id === SELECT_KERNEL_ID) { // // this is being disposed by the consumer - return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor); + return this.instantiationService.createInstance(NotebookKernelActionViewItem, action, this.notebookEditor); } const a = this.editorToolbar.primaryActions.find(a => a.action.id === action.id); @@ -354,7 +354,7 @@ export class NotebookEditorToolbar extends Disposable { const actionProvider = (action: IAction) => { if (action.id === SELECT_KERNEL_ID) { // // this is being disposed by the consumer - return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor); + return this.instantiationService.createInstance(NotebookKernelActionViewItem, action, this.notebookEditor); } if (this._renderLabel !== RenderLabel.Never) { diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts index 958a59df428ac..8af671b1f15ca 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts @@ -14,7 +14,7 @@ import { Event } from 'vs/base/common/event'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -export class NotebooKernelActionViewItem extends ActionViewItem { +export class NotebookKernelActionViewItem extends ActionViewItem { private _kernelLabel?: HTMLAnchorElement; @@ -99,7 +99,7 @@ export class NotebooKernelActionViewItem extends ActionViewItem { const selectedOrSuggested = info.selected ?? (info.suggestions.length === 1 ? info.suggestions[0] : undefined); if (selectedOrSuggested) { // selected or suggested kernel - this._action.label = this._generateKenrelLabel(selectedOrSuggested); + this._action.label = this._generateKernelLabel(selectedOrSuggested); this._action.tooltip = selectedOrSuggested.description ?? selectedOrSuggested.detail ?? ''; if (!info.selected) { // special UI for selected kernel? @@ -111,7 +111,7 @@ export class NotebooKernelActionViewItem extends ActionViewItem { } } - private _generateKenrelLabel(kernel: INotebookKernel) { + private _generateKernelLabel(kernel: INotebookKernel) { return kernel.label; } From 6cab30416dde55072a609a31806c59f5ce13cf54 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Tue, 13 Sep 2022 10:28:41 -0700 Subject: [PATCH 6/9] contributing to notebook editor view model, based on cell status setup --- .../statusBar/statusBarItemController.ts | 66 +++++++++++++++++++ .../browser/media/notebookToolbar.css | 5 ++ .../notebook/browser/notebook.contribution.ts | 1 + .../notebook/browser/notebookBrowser.ts | 4 +- .../viewModel/notebookViewModelImpl.ts | 12 +++- .../viewParts/notebookEditorToolbar.ts | 7 ++ 6 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 src/vs/workbench/contrib/notebook/browser/contrib/statusBar/statusBarItemController.ts diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/statusBarItemController.ts b/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/statusBarItemController.ts new file mode 100644 index 0000000000000..01f75a6167293 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/statusBarItemController.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { disposableTimeout, Throttler } from 'vs/base/common/async'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { INotebookEditor, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; +import { INotebookStatusBarItemList } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookStatusBarService'; + +export class ContributedStatusBarItemController extends Disposable implements INotebookEditorContribution { + // IANHU: Naming? + static id: string = 'workbench.notebook.statusBar.mainContributed'; + + private readonly _updateThrottler = new Throttler(); + private _activeToken: CancellationTokenSource | undefined; + private _currentItemLists: INotebookStatusBarItemList[] = []; + + constructor( + private readonly _notebookEditor: INotebookEditor, + @INotebookStatusBarService private readonly _notebookStatusBarService: INotebookStatusBarService + ) { + super(); + + this._updateEverything(); + this._register(this._notebookStatusBarService.onDidChangeProviders(this._updateEverything, this)); + this._register(this._notebookStatusBarService.onDidChangeItems(this._updateEverything, this)); + this._register(toDisposable(() => this._activeToken?.dispose(true))); + } + + // IANHU: Too simplistic? Try for now + private _updateEverything(): void { + // Wait a tick to make sure that the event is fired to the EH before triggering status bar providers + this._register(disposableTimeout(() => { + this._updateThrottler.queue(() => this._update()); + }, 0)); + } + + private async _update(): Promise { + const vm = this._notebookEditor._getViewModel(); + + if (vm) { + const tokenSource = this._activeToken = new CancellationTokenSource(); + const statusItemLists = await this._notebookStatusBarService.getStatusBarItemsForDocument(vm.notebookDocument.uri, vm.notebookDocument.viewType, this._activeToken.token); + if (tokenSource.token.isCancellationRequested) { + statusItemLists.forEach(itemList => itemList.dispose && itemList.dispose()); + return; + } + const items = statusItemLists.map(itemList => itemList.items).flat(); + vm.setStatusBarItems(items); + this._currentItemLists.forEach(itemList => itemList.dispose && itemList.dispose()); + this._currentItemLists = statusItemLists; + } + } + + override dispose(): void { + super.dispose(); + this._activeToken?.dispose(true); + this._currentItemLists.forEach(itemList => itemList.dispose && itemList.dispose()); + } +} + +registerNotebookContribution(ContributedStatusBarItemController.id, ContributedStatusBarItemController); diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebookToolbar.css b/src/vs/workbench/contrib/notebook/browser/media/notebookToolbar.css index f7ddb6665a70a..f4b2d96c8d91f 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebookToolbar.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebookToolbar.css @@ -84,3 +84,8 @@ .monaco-workbench .notebookOverlay .notebook-toolbar-container .monaco-action-bar .action-item .codicon-notebook-state-error { color: var(--notebook-cell-status-icon-error); } + +.monaco-workbench .notebookOverlay .notebook-toolbar-container .notebook-statusbar-container { + width: 100px; + background-color: blue; +} diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 0cc0340573a1d..8338653dc79f2 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -82,6 +82,7 @@ import 'vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline'; import 'vs/workbench/contrib/notebook/browser/contrib/profile/notebookProfile'; import 'vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders'; import 'vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/contributedCellStatusBarItemController'; +import 'vs/workbench/contrib/notebook/browser/contrib/statusBar/statusBarItemController'; import 'vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController'; import 'vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar'; import 'vs/workbench/contrib/notebook/browser/contrib/undoRedo/notebookUndoRedo'; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 5a3028c7400d0..cfc8a2fea925b 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -20,7 +20,7 @@ import { IEditorPane } from 'vs/workbench/common/editor'; import { CellViewModelStateChangeEvent, NotebookCellStateChangedEvent, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, ICellOutput, INotebookCellStatusBarItem, INotebookRendererInfo, INotebookSearchOptions, IOrderedMimeType, NotebookCellInternalMetadata, NotebookCellMetadata, NOTEBOOK_EDITOR_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, ICellOutput, INotebookCellStatusBarItem, INotebookRendererInfo, INotebookSearchOptions, INotebookStatusBarItem, IOrderedMimeType, NotebookCellInternalMetadata, NotebookCellMetadata, NOTEBOOK_EDITOR_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { isCompositeNotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput'; import { INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions'; @@ -397,6 +397,8 @@ export interface INotebookViewModel { getFoldedLength(index: number): number; replaceOne(cell: ICellViewModel, range: Range, text: string): Promise; replaceAll(matches: CellFindMatch[], texts: string[]): Promise; + setStatusBarItems(items: INotebookStatusBarItem[]): void; + getStatusBarItems(): INotebookStatusBarItem[]; } //#endregion diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts index 080427cbac2bc..8ce48302d5692 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts @@ -29,7 +29,7 @@ import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewM import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, ICell, INotebookSearchOptions, ISelectionState, NotebookCellsChangeType, NotebookCellTextModelSplice, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, ICell, INotebookSearchOptions, INotebookStatusBarItem, ISelectionState, NotebookCellsChangeType, NotebookCellTextModelSplice, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { cellIndexesToRanges, cellRangesToIndexes, ICellRange, reduceCellRanges } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { NotebookLayoutInfo, NotebookMetadataChangedEvent } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; @@ -177,6 +177,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD private _foldingRanges: FoldingRegions | null = null; private _hiddenRanges: ICellRange[] = []; private _focused: boolean = true; + private _statusBarItems: INotebookStatusBarItem[] = []; get focused() { return this._focused; @@ -747,6 +748,15 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return result; } + // IANHU: Too simplistic? Not using the delta here, just replacing + setStatusBarItems(items: INotebookStatusBarItem[]) { + this._statusBarItems = items; + } + + getStatusBarItems(): INotebookStatusBarItem[] { + return this._statusBarItems; + } + nearestCodeCellIndex(index: number /* exclusive */) { const nearest = this.viewCells.slice(0, index).reverse().findIndex(cell => cell.cellKind === CellKind.Code); if (nearest > -1) { diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts index 93ad254fa8bf4..952cb7fa5b9cc 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts @@ -266,6 +266,7 @@ export class NotebookEditorToolbar extends Disposable { private _notebookGlobalActionsMenu!: IMenu; private _notebookLeftToolbar!: ToolBar; private _primaryActions: IActionModel[]; + private _notebookTopRightStatusbarContainer!: HTMLElement; get primaryActions(): IActionModel[] { return this._primaryActions; } @@ -333,6 +334,12 @@ export class NotebookEditorToolbar extends Disposable { this._register(this._leftToolbarScrollable); DOM.append(this.domNode, this._leftToolbarScrollable.getDomNode()); + + // IANHU + this._notebookTopRightStatusbarContainer = document.createElement('div'); + this._notebookTopRightStatusbarContainer.classList.add('notebook-statusbar-container'); + DOM.append(this.domNode, this._notebookTopRightStatusbarContainer); + this._notebookTopRightToolbarContainer = document.createElement('div'); this._notebookTopRightToolbarContainer.classList.add('notebook-toolbar-right'); DOM.append(this.domNode, this._notebookTopRightToolbarContainer); From 3da3a247bce42272c6891656bee692a6519384ad Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Tue, 13 Sep 2022 13:18:48 -0700 Subject: [PATCH 7/9] unstyled UI element showing up in the notebook --- .../browser/media/notebookToolbar.css | 6 +++ .../notebook/browser/notebookBrowser.ts | 1 + .../viewModel/notebookViewModelImpl.ts | 4 ++ .../viewParts/notebookEditorToolbar.ts | 50 +++++++++++++++---- 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebookToolbar.css b/src/vs/workbench/contrib/notebook/browser/media/notebookToolbar.css index f4b2d96c8d91f..2d54b4ca65a4f 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebookToolbar.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebookToolbar.css @@ -88,4 +88,10 @@ .monaco-workbench .notebookOverlay .notebook-toolbar-container .notebook-statusbar-container { width: 100px; background-color: blue; + display: flex; +} + +.monaco-workbench .notebookOverlay .notebook-toolbar-container .notebook-statusbar-container .notebook-statusbar-item { + width: 50px; + background-color: green; } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index cfc8a2fea925b..31ef1e900464c 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -399,6 +399,7 @@ export interface INotebookViewModel { replaceAll(matches: CellFindMatch[], texts: string[]): Promise; setStatusBarItems(items: INotebookStatusBarItem[]): void; getStatusBarItems(): INotebookStatusBarItem[]; + onDidChangeStatusBarItems: Event; } //#endregion diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts index 8ce48302d5692..d0b0e05681557 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts @@ -186,6 +186,9 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD private _decorationIdToCellMap = new Map(); private _statusBarItemIdToCellMap = new Map(); + private readonly _onDidChangeStatusBarItems = this._register(new Emitter()); + readonly onDidChangeStatusBarItems: Event = this._onDidChangeStatusBarItems.event; + constructor( public viewType: string, private _notebook: NotebookTextModel, @@ -751,6 +754,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD // IANHU: Too simplistic? Not using the delta here, just replacing setStatusBarItems(items: INotebookStatusBarItem[]) { this._statusBarItems = items; + this._onDidChangeStatusBarItems.fire(); } getStatusBarItems(): INotebookStatusBarItem[] { diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts index 952cb7fa5b9cc..e20d3b7d6165f 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts @@ -8,7 +8,7 @@ import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableEle import { ToggleMenuAction, ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IAction, Separator } from 'vs/base/common/actions'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenu, IMenuService, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; @@ -287,6 +287,7 @@ export class NotebookEditorToolbar extends Disposable { } private _dimension: DOM.Dimension | null = null; + private readonly _modelDisposables = this._register(new DisposableStore()); constructor( readonly notebookEditor: INotebookEditorDelegate, @@ -312,12 +313,25 @@ export class NotebookEditorToolbar extends Disposable { const notebookEditor = this.editorService.activeEditorPane.getControl() as INotebookEditorDelegate; if (notebookEditor === this.notebookEditor) { // this is the active editor - this._showNotebookActionsinEditorToolbar(); + this._showNotebookActionsInEditorToolbar(); return; } } })); + // When we get a new view model register to show status items + this._register(this.notebookEditor.onDidChangeModel(() => { + this._modelDisposables.clear(); + + if (this.notebookEditor.hasModel()) { + this._modelDisposables.add(this.notebookEditor._getViewModel().onDidChangeStatusBarItems(() => { + this._statusBarItemsChanged(); + })); + + this._statusBarItemsChanged(); + } + })); + this._registerNotebookActionsToolbar(); } @@ -394,17 +408,17 @@ export class NotebookEditorToolbar extends Disposable { this._register(this._notebookRightToolbar); this._notebookRightToolbar.context = context; - this._showNotebookActionsinEditorToolbar(); + this._showNotebookActionsInEditorToolbar(); let dropdownIsVisible = false; let deferredUpdate: (() => void) | undefined; this._register(this._notebookGlobalActionsMenu.onDidChange(() => { if (dropdownIsVisible) { - deferredUpdate = () => this._showNotebookActionsinEditorToolbar(); + deferredUpdate = () => this._showNotebookActionsInEditorToolbar(); return; } - this._showNotebookActionsinEditorToolbar(); + this._showNotebookActionsInEditorToolbar(); })); this._register(this._notebookLeftToolbar.onDidChangeDropdownVisibility(visible => { @@ -421,7 +435,7 @@ export class NotebookEditorToolbar extends Disposable { this._register(this.notebookOptions.onDidChangeOptions(e => { if (e.globalToolbar !== undefined) { this._useGlobalToolbar = this.notebookOptions.getLayoutConfiguration().globalToolbar; - this._showNotebookActionsinEditorToolbar(); + this._showNotebookActionsInEditorToolbar(); } })); @@ -439,7 +453,7 @@ export class NotebookEditorToolbar extends Disposable { }); this._register(this._notebookLeftToolbar); this._notebookLeftToolbar.context = context; - this._showNotebookActionsinEditorToolbar(); + this._showNotebookActionsInEditorToolbar(); return; } })); @@ -451,7 +465,7 @@ export class NotebookEditorToolbar extends Disposable { } if (this._useGlobalToolbar !== treatment) { this._useGlobalToolbar = treatment; - this._showNotebookActionsinEditorToolbar(); + this._showNotebookActionsInEditorToolbar(); } }); } @@ -486,7 +500,7 @@ export class NotebookEditorToolbar extends Disposable { } } - private _showNotebookActionsinEditorToolbar() { + private _showNotebookActionsInEditorToolbar() { // when there is no view model, just ignore. if (!this.notebookEditor.hasModel()) { return; @@ -501,6 +515,24 @@ export class NotebookEditorToolbar extends Disposable { this._onDidChangeState.fire(); } + private _statusBarItemsChanged() { + // when there is no view model, just ignore. + if (!this.notebookEditor.hasModel()) { + return; + } + + const statusItems = this.notebookEditor._getViewModel().getStatusBarItems(); + + // IANHU: Not right, should actually swap in the new elements + this._notebookTopRightStatusbarContainer.replaceChildren(); + statusItems.forEach(statusItem => { + const newDiv = document.createElement('div'); + newDiv.classList.add('notebook-statusbar-item'); + newDiv.innerText = statusItem.text; + DOM.append(this._notebookTopRightStatusbarContainer, newDiv); + }); + } + private _setNotebookActions() { const groups = this._notebookGlobalActionsMenu.getActions({ shouldForwardArgs: true, renderShortTitle: true }); this.domNode.style.display = 'flex'; From b72464d0496342c1c3cc9dc0507a5612f1276486 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Tue, 13 Sep 2022 16:58:54 -0700 Subject: [PATCH 8/9] styling and command execution --- .../browser/media/notebookToolbar.css | 21 ++- .../viewParts/notebookEditorToolbar.ts | 128 +++++++++++++++++- 2 files changed, 138 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebookToolbar.css b/src/vs/workbench/contrib/notebook/browser/media/notebookToolbar.css index 2d54b4ca65a4f..8196280ef8549 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebookToolbar.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebookToolbar.css @@ -86,12 +86,25 @@ } .monaco-workbench .notebookOverlay .notebook-toolbar-container .notebook-statusbar-container { - width: 100px; - background-color: blue; display: flex; } .monaco-workbench .notebookOverlay .notebook-toolbar-container .notebook-statusbar-container .notebook-statusbar-item { - width: 50px; - background-color: green; + display: flex; + align-items: center; + white-space: pre; + + height: 21px; /* Editor outline is -1px in, don't overlap */ + margin: 0px 3px; + padding: 0px 3px; + overflow: hidden; + text-overflow: clip; +} + +.monaco-workbench .notebookOverlay .notebook-toolbar-container .notebook-statusbar-container .notebook-statusbar-item.notebook-statusbar-item-has-command { + cursor: pointer; +} + +.monaco-workbench .notebookOverlay .notebook-toolbar-container .notebook-statusbar-container .notebook-statusbar-item-show-when-active { + display: none; } diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts index e20d3b7d6165f..f5bd038163970 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts @@ -6,7 +6,7 @@ import * as DOM from 'vs/base/browser/dom'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ToggleMenuAction, ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; -import { IAction, Separator } from 'vs/base/common/actions'; +import { IAction, Separator, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; @@ -18,9 +18,9 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { toolbarActiveBackground } from 'vs/platform/theme/common/colorRegistry'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant, ThemeColor } from 'vs/platform/theme/common/themeService'; import { SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; -import { NOTEBOOK_EDITOR_ID, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NOTEBOOK_EDITOR_ID, NotebookSetting, INotebookStatusBarItem } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookKernelActionViewItem } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem'; import { ActionViewWithLabel } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView'; @@ -28,6 +28,17 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions'; import { IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { SimpleIconLabel } from 'vs/base/browser/ui/iconLabel/simpleIconLabel'; +import { isThemeColor } from 'vs/editor/common/editorCommon'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { stripIcons } from 'vs/base/common/iconLabels'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; + +const $ = DOM.$; interface IActionModel { action: IAction; @@ -523,13 +534,16 @@ export class NotebookEditorToolbar extends Disposable { const statusItems = this.notebookEditor._getViewModel().getStatusBarItems(); + // IANHU: Not right, should actually swap in the new elements this._notebookTopRightStatusbarContainer.replaceChildren(); statusItems.forEach(statusItem => { - const newDiv = document.createElement('div'); - newDiv.classList.add('notebook-statusbar-item'); - newDiv.innerText = statusItem.text; - DOM.append(this._notebookTopRightStatusbarContainer, newDiv); + // IANHU: Second param is maxItemWidth, just hardcoding for now + const statusItemElement = this.instantiationService.createInstance(StatusBarItem, statusItem, 200); + // const newDiv = document.createElement('div'); + // newDiv.classList.add('notebook-statusbar-item'); + // newDiv.innerText = statusItem.text; + DOM.append(this._notebookTopRightStatusbarContainer, statusItemElement.container); }); } @@ -662,3 +676,103 @@ registerThemingParticipant((theme, collector) => { `); } }); + +// IANHU: This should move out to a new file, as well as a container here +class StatusBarItem extends Disposable { + readonly container = $('.notebook-statusbar-item'); + + set maxWidth(v: number) { + this.container.style.maxWidth = v + 'px'; + } + + private _currentItem!: INotebookStatusBarItem; + private _itemDisposables = this._register(new DisposableStore()); + + constructor( + itemModel: INotebookStatusBarItem, + maxWidth: number | undefined, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @ICommandService private readonly _commandService: ICommandService, + @INotificationService private readonly _notificationService: INotificationService, + @IThemeService private readonly _themeService: IThemeService, + ) { + super(); + + this.updateItem(itemModel, maxWidth); + } + + updateItem(item: INotebookStatusBarItem, maxWidth: number | undefined) { + this._itemDisposables.clear(); + + if (!this._currentItem || this._currentItem.text !== item.text) { + new SimpleIconLabel(this.container).text = item.text.replace(/\n/g, ' '); + } + + const resolveColor = (color: ThemeColor | string) => { + return isThemeColor(color) ? + (this._themeService.getColorTheme().getColor(color.id)?.toString() || '') : + color; + }; + + this.container.style.color = item.color ? resolveColor(item.color) : ''; + this.container.style.backgroundColor = item.backgroundColor ? resolveColor(item.backgroundColor) : ''; + this.container.style.opacity = item.opacity ? item.opacity : ''; + + this.container.classList.toggle('notebook-statusbar-item-show-when-active', !!item.onlyShowWhenActive); + + if (typeof maxWidth === 'number') { + this.maxWidth = maxWidth; + } + + let ariaLabel: string; + let role: string | undefined; + if (item.accessibilityInformation) { + ariaLabel = item.accessibilityInformation.label; + role = item.accessibilityInformation.role; + } else { + ariaLabel = item.text ? stripIcons(item.text).trim() : ''; + } + + this.container.setAttribute('aria-label', ariaLabel); + this.container.setAttribute('role', role || ''); + this.container.title = item.tooltip ?? ''; + + this.container.classList.toggle('notebook-statusbar-item-has-command', !!item.command); + if (item.command) { + this.container.tabIndex = 0; + + this._itemDisposables.add(DOM.addDisposableListener(this.container, DOM.EventType.CLICK, _e => { + this.executeCommand(); + })); + this._itemDisposables.add(DOM.addDisposableListener(this.container, DOM.EventType.KEY_DOWN, e => { + const event = new StandardKeyboardEvent(e); + if (event.equals(KeyCode.Space) || event.equals(KeyCode.Enter)) { + this.executeCommand(); + } + })); + } else { + this.container.removeAttribute('tabIndex'); + } + + this._currentItem = item; + } + + private async executeCommand(): Promise { + const command = this._currentItem.command; + if (!command) { + return; + } + + const id = typeof command === 'string' ? command : command.id; + const args = typeof command === 'string' ? [] : command.arguments ?? []; + + // IANHU: Removed a context unshift here + + this._telemetryService.publicLog2('workbenchActionExecuted', { id, from: 'notebook status bar' }); + try { + await this._commandService.executeCommand(id, ...args); + } catch (error) { + this._notificationService.error(toErrorMessage(error)); + } + } +} From 24b8962eed62f8f633fa09ff1eed02cf9ca76437 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Wed, 14 Sep 2022 11:57:55 -0700 Subject: [PATCH 9/9] parking WIP --- .../contrib/editorStatusBar/editorStatusBar.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts index ca7dea6f626d0..fa7a500bc02b0 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts @@ -414,6 +414,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { private readonly _editorDisposables = this._register(new DisposableStore()); private readonly _kernelInfoElement = this._register(new DisposableStore()); + private readonly _modelDisposables = this._register(new DisposableStore()); constructor( @IEditorService private readonly _editorService: IEditorService, @@ -445,19 +446,32 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { const notebook = activeEditor.textModel; if (notebook) { this._showKernelStatus(notebook); + this._showStatusBarItems(activeEditor); } else { this._kernelInfoElement.clear(); } }; + const modelChange = () => { + this._modelDisposables.dispose(); + if (activeEditor.hasModel()) { + this._modelDisposables.add(activeEditor._getViewModel().onDidChangeStatusBarItems(updateStatus)); + } + updateStatus(); + }; + this._editorDisposables.add(this._notebookKernelService.onDidAddKernel(updateStatus)); this._editorDisposables.add(this._notebookKernelService.onDidChangeSelectedNotebooks(updateStatus)); this._editorDisposables.add(this._notebookKernelService.onDidChangeNotebookAffinity(updateStatus)); - this._editorDisposables.add(activeEditor.onDidChangeModel(updateStatus)); + //this._editorDisposables.add(activeEditor.onDidChangeModel(updateStatus)); + this._editorDisposables.add(activeEditor.onDidChangeModel(modelChange)); this._editorDisposables.add(activeEditor.notebookOptions.onDidChangeOptions(updateStatus)); updateStatus(); } + private _showStatusBarItems(notebookEditor: INotebookEditor) { + } + private _showKernelStatus(notebook: NotebookTextModel) { this._kernelInfoElement.clear();