diff --git a/src/vs/workbench/api/browser/mainThreadQuickDiff.ts b/src/vs/workbench/api/browser/mainThreadQuickDiff.ts index f9606f61a85..2d15ed9a843 100644 --- a/src/vs/workbench/api/browser/mainThreadQuickDiff.ts +++ b/src/vs/workbench/api/browser/mainThreadQuickDiff.ts @@ -23,12 +23,12 @@ export class MainThreadQuickDiff implements MainThreadQuickDiffShape { this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostQuickDiff); } - async $registerQuickDiffProvider(handle: number, selector: IDocumentFilterDto[], label: string, rootUri: UriComponents | undefined): Promise { + async $registerQuickDiffProvider(handle: number, selector: IDocumentFilterDto[], id: string, label: string, rootUri: UriComponents | undefined): Promise { const provider: QuickDiffProvider = { + id, label, rootUri: URI.revive(rootUri), selector, - visible: true, kind: 'contributed', getOriginalResource: async (uri: URI) => { return URI.revive(await this.proxy.$provideOriginalResource(handle, uri, CancellationToken.None)); diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 86387c27651..13be9f16149 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -334,9 +334,9 @@ class MainThreadSCMProvider implements ISCMProvider { if (features.hasQuickDiffProvider && !this._quickDiff) { this._quickDiff = this._quickDiffService.addQuickDiffProvider({ + id: `${this._providerId}.quickDiffProvider`, label: features.quickDiffLabel ?? this.label, rootUri: this.rootUri, - visible: true, kind: 'primary', getOriginalResource: async (uri: URI) => { if (!this.features.hasQuickDiffProvider) { @@ -354,9 +354,9 @@ class MainThreadSCMProvider implements ISCMProvider { if (features.hasSecondaryQuickDiffProvider && !this._stagedQuickDiff) { this._stagedQuickDiff = this._quickDiffService.addQuickDiffProvider({ + id: `${this._providerId}.secondaryQuickDiffProvider`, label: features.secondaryQuickDiffLabel ?? this.label, rootUri: this.rootUri, - visible: true, kind: 'secondary', getOriginalResource: async (uri: URI) => { if (!this.features.hasSecondaryQuickDiffProvider) { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index f0d7e72251a..b5699214188 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -917,9 +917,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension, 'profileContentHandlers'); return extHostProfileContentHandlers.registerProfileContentHandler(extension, id, handler); }, - registerQuickDiffProvider(selector: vscode.DocumentSelector, quickDiffProvider: vscode.QuickDiffProvider, label: string, rootUri?: vscode.Uri): vscode.Disposable { + registerQuickDiffProvider(selector: vscode.DocumentSelector, quickDiffProvider: vscode.QuickDiffProvider, id: string, label: string, rootUri?: vscode.Uri): vscode.Disposable { checkProposedApiEnabled(extension, 'quickDiffProvider'); - return extHostQuickDiff.registerQuickDiffProvider(checkSelector(selector), quickDiffProvider, label, rootUri); + return extHostQuickDiff.registerQuickDiffProvider(extension, checkSelector(selector), quickDiffProvider, id, label, rootUri); }, get tabGroups(): vscode.TabGroups { return extHostEditorTabs.tabGroups; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 6a4906c6321..acfa2a7f3c9 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1669,7 +1669,7 @@ export interface MainThreadSCMShape extends IDisposable { } export interface MainThreadQuickDiffShape extends IDisposable { - $registerQuickDiffProvider(handle: number, selector: IDocumentFilterDto[], label: string, rootUri: UriComponents | undefined): Promise; + $registerQuickDiffProvider(handle: number, selector: IDocumentFilterDto[], id: string, label: string, rootUri: UriComponents | undefined): Promise; $unregisterQuickDiffProvider(handle: number): Promise; } diff --git a/src/vs/workbench/api/common/extHostQuickDiff.ts b/src/vs/workbench/api/common/extHostQuickDiff.ts index f959ebf9d92..5418497ce54 100644 --- a/src/vs/workbench/api/common/extHostQuickDiff.ts +++ b/src/vs/workbench/api/common/extHostQuickDiff.ts @@ -10,6 +10,7 @@ import { ExtHostQuickDiffShape, IMainContext, MainContext, MainThreadQuickDiffSh import { asPromise } from '../../../base/common/async.js'; import { DocumentSelector } from './extHostTypeConverters.js'; import { IURITransformer } from '../../../base/common/uriIpc.js'; +import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; export class ExtHostQuickDiff implements ExtHostQuickDiffShape { private static handlePool: number = 0; @@ -36,10 +37,10 @@ export class ExtHostQuickDiff implements ExtHostQuickDiffShape { .then(r => r || null); } - registerQuickDiffProvider(selector: vscode.DocumentSelector, quickDiffProvider: vscode.QuickDiffProvider, label: string, rootUri?: vscode.Uri): vscode.Disposable { + registerQuickDiffProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, quickDiffProvider: vscode.QuickDiffProvider, id: string, label: string, rootUri?: vscode.Uri): vscode.Disposable { const handle = ExtHostQuickDiff.handlePool++; this.providers.set(handle, quickDiffProvider); - this.proxy.$registerQuickDiffProvider(handle, DocumentSelector.from(selector, this.uriTransformer), label, rootUri); + this.proxy.$registerQuickDiffProvider(handle, DocumentSelector.from(selector, this.uriTransformer), `${extension.identifier.value}.${id}`, label, rootUri); return { dispose: () => { this.proxy.$unregisterQuickDiffProvider(handle); diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts index e1abee3350d..20d58302c89 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts @@ -373,24 +373,25 @@ export class QuickDiffWorkbenchController extends Disposable implements IWorkben for (let index = 0; index < providers.length; index++) { const provider = providers[index]; + const visible = this.quickDiffService.isQuickDiffProviderVisible(provider.id); + const group = provider.kind !== 'contributed' ? '0_scm' : '1_contributed'; + const order = index + 1; + store.add(registerAction2(class extends Action2 { constructor() { super({ - id: `workbench.scm.action.toggleQuickDiffVisibility.${provider.label}`, + id: `workbench.scm.action.toggleQuickDiffVisibility.${provider.id}`, title: provider.label, - toggled: provider.visible - ? ContextKeyTrueExpr.INSTANCE - : ContextKeyFalseExpr.INSTANCE, + toggled: visible ? ContextKeyTrueExpr.INSTANCE : ContextKeyFalseExpr.INSTANCE, menu: { - id: MenuId.SCMQuickDiffDecorations, - order: index + 1, + id: MenuId.SCMQuickDiffDecorations, group, order }, f1: false }); } override run(accessor: ServicesAccessor): void { const quickDiffService = accessor.get(IQuickDiffService); - quickDiffService.toggleQuickDiffVisibility(provider.label); + quickDiffService.toggleQuickDiffProviderVisibility(provider.id); } })); } diff --git a/src/vs/workbench/contrib/scm/common/quickDiff.ts b/src/vs/workbench/contrib/scm/common/quickDiff.ts index 99920db6e37..f290f4dc0f2 100644 --- a/src/vs/workbench/contrib/scm/common/quickDiff.ts +++ b/src/vs/workbench/contrib/scm/common/quickDiff.ts @@ -67,18 +67,19 @@ export const editorGutterItemGlyphForeground = registerColor('editorGutter.itemG export const editorGutterItemBackground = registerColor('editorGutter.itemBackground', { dark: opaque(listInactiveSelectionBackground, editorBackground), light: darken(opaque(listInactiveSelectionBackground, editorBackground), .05), hcDark: Color.white, hcLight: Color.black }, nls.localize('editorGutterItemBackground', 'Editor gutter decoration color for gutter item background. This color should be opaque.')); export interface QuickDiffProvider { - label: string; - rootUri: URI | undefined; - selector?: LanguageSelector; - visible: boolean; + readonly id: string; + readonly label: string; + readonly rootUri: URI | undefined; + readonly selector?: LanguageSelector; readonly kind: 'primary' | 'secondary' | 'contributed'; getOriginalResource(uri: URI): Promise; } export interface QuickDiff { - label: string; - originalResource: URI; - visible: boolean; + readonly id: string; + readonly label: string; + readonly originalResource: URI; + readonly visible: boolean; readonly kind: 'primary' | 'secondary' | 'contributed'; } @@ -105,7 +106,8 @@ export interface IQuickDiffService { readonly providers: readonly QuickDiffProvider[]; addQuickDiffProvider(quickDiff: QuickDiffProvider): IDisposable; getQuickDiffs(uri: URI, language?: string, isSynchronized?: boolean): Promise; - toggleQuickDiffVisibility(label: string): void; + toggleQuickDiffProviderVisibility(id: string): void; + isQuickDiffProviderVisible(id: string): boolean; } export enum ChangeType { diff --git a/src/vs/workbench/contrib/scm/common/quickDiffService.ts b/src/vs/workbench/contrib/scm/common/quickDiffService.ts index b4da8c5feb1..3942c918364 100644 --- a/src/vs/workbench/contrib/scm/common/quickDiffService.ts +++ b/src/vs/workbench/contrib/scm/common/quickDiffService.ts @@ -10,6 +10,7 @@ import { isEqualOrParent } from '../../../../base/common/resources.js'; import { score } from '../../../../editor/common/languageSelector.js'; import { Emitter } from '../../../../base/common/event.js'; import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; function createProviderComparer(uri: URI): (a: QuickDiffProvider, b: QuickDiffProvider) => number { return (a, b) => { @@ -51,64 +52,99 @@ function providerComparer(a: QuickDiffProvider, b: QuickDiffProvider): number { export class QuickDiffService extends Disposable implements IQuickDiffService { declare readonly _serviceBrand: undefined; + private static readonly STORAGE_KEY = 'workbench.scm.quickDiffProviders.hidden'; - private quickDiffProviders: Set = new Set(); + private quickDiffProviders: Map = new Map(); get providers(): readonly QuickDiffProvider[] { - return Array.from(this.quickDiffProviders).sort(providerComparer); + return Array.from(this.quickDiffProviders.values()).sort(providerComparer); } private readonly _onDidChangeQuickDiffProviders = this._register(new Emitter()); readonly onDidChangeQuickDiffProviders = this._onDidChangeQuickDiffProviders.event; - constructor(@IUriIdentityService private readonly uriIdentityService: IUriIdentityService) { + private hiddenQuickDiffProviders = new Set(); + + constructor( + @IStorageService private readonly storageService: IStorageService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService + ) { super(); + + this.loadState(); } addQuickDiffProvider(quickDiff: QuickDiffProvider): IDisposable { - this.quickDiffProviders.add(quickDiff); + this.quickDiffProviders.set(quickDiff.id, quickDiff); this._onDidChangeQuickDiffProviders.fire(); return { dispose: () => { - this.quickDiffProviders.delete(quickDiff); + this.quickDiffProviders.delete(quickDiff.id); this._onDidChangeQuickDiffProviders.fire(); } }; } - private isQuickDiff(diff: { originalResource?: URI; label?: string }): diff is QuickDiff { - return !!diff.originalResource && (typeof diff.label === 'string'); - } - async getQuickDiffs(uri: URI, language: string = '', isSynchronized: boolean = false): Promise { - const providers = Array.from(this.quickDiffProviders) + const providers = Array.from(this.quickDiffProviders.values()) .filter(provider => !provider.rootUri || this.uriIdentityService.extUri.isEqualOrParent(uri, provider.rootUri)) .sort(createProviderComparer(uri)); const quickDiffs = await Promise.all(providers.map(async provider => { + const visible = this.isQuickDiffProviderVisible(provider.id); const scoreValue = provider.selector ? score(provider.selector, uri, language, isSynchronized, undefined, undefined) : 10; const originalResource = scoreValue > 0 ? await provider.getOriginalResource(uri) ?? undefined : undefined; return { - originalResource, + id: provider.id, label: provider.label, - visible: provider.visible, - kind: provider.kind + visible: visible, + kind: provider.kind, + originalResource } satisfies Partial; })); return quickDiffs.filter(this.isQuickDiff); } - toggleQuickDiffVisibility(label: string): void { - const quickDiff = Array.from(this.quickDiffProviders) - .find(provider => provider.label === label); - if (!quickDiff) { + toggleQuickDiffProviderVisibility(id: string): void { + if (!this.quickDiffProviders.has(id)) { return; } - quickDiff.visible = !quickDiff.visible; + if (this.isQuickDiffProviderVisible(id)) { + this.hiddenQuickDiffProviders.add(id); + } else { + this.hiddenQuickDiffProviders.delete(id); + } + + this.saveState(); this._onDidChangeQuickDiffProviders.fire(); } + + isQuickDiffProviderVisible(id: string): boolean { + return !this.hiddenQuickDiffProviders.has(id); + } + + private loadState(): void { + const raw = this.storageService.get(QuickDiffService.STORAGE_KEY, StorageScope.PROFILE); + if (raw) { + try { + this.hiddenQuickDiffProviders = new Set(JSON.parse(raw)); + } catch { } + } + } + + private saveState(): void { + if (this.hiddenQuickDiffProviders.size === 0) { + this.storageService.remove(QuickDiffService.STORAGE_KEY, StorageScope.PROFILE); + } else { + this.storageService.store(QuickDiffService.STORAGE_KEY, JSON.stringify(Array.from(this.hiddenQuickDiffProviders)), StorageScope.PROFILE, StorageTarget.USER); + } + } + + private isQuickDiff(diff: { id?: string; label?: string; originalResource?: URI }): diff is QuickDiff { + return typeof diff.id === 'string' && typeof diff.label === 'string' && !!diff.originalResource; + } } export async function getOriginalResource(quickDiffService: IQuickDiffService, uri: URI, language: string | undefined, isSynchronized: boolean | undefined): Promise { diff --git a/src/vscode-dts/vscode.proposed.quickDiffProvider.d.ts b/src/vscode-dts/vscode.proposed.quickDiffProvider.d.ts index a90a9f7e9cb..43f4c935993 100644 --- a/src/vscode-dts/vscode.proposed.quickDiffProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.quickDiffProvider.d.ts @@ -8,7 +8,7 @@ declare module 'vscode' { // https://github.com/microsoft/vscode/issues/169012 export namespace window { - export function registerQuickDiffProvider(selector: DocumentSelector, quickDiffProvider: QuickDiffProvider, label: string, rootUri?: Uri): Disposable; + export function registerQuickDiffProvider(selector: DocumentSelector, quickDiffProvider: QuickDiffProvider, id: string, label: string, rootUri?: Uri): Disposable; } export interface SourceControl { @@ -16,6 +16,7 @@ declare module 'vscode' { } export interface QuickDiffProvider { + readonly id?: string; readonly label?: string; } }