Setup: allow a command link that opens setup view (fix microsoft/vscode-copilot#11301) (#237323)

pull/235318/head^2
Benjamin Pasero 2025-01-06 11:27:00 +01:00 committed by GitHub
parent e8b620fc96
commit dc1cfc044d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 46 additions and 2 deletions

View File

@ -60,6 +60,7 @@ import { IHostService } from '../../../services/host/browser/host.js';
import Severity from '../../../../base/common/severity.js';
import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';
import { isWeb } from '../../../../base/common/platform.js';
import { ExtensionUrlHandlerOverrideRegistry } from '../../../services/extensions/browser/extensionUrlHandler.js';
const defaultChat = {
extensionId: product.defaultChatAgent?.extensionId ?? '',
@ -109,7 +110,9 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr
constructor(
@IProductService private readonly productService: IProductService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@ICommandService private readonly commandService: ICommandService,
@ITelemetryService private readonly telemetryService: ITelemetryService
) {
super();
@ -122,6 +125,7 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr
this.registerChatWelcome();
this.registerActions();
this.registerUrlLinkHandler();
}
private registerChatWelcome(): void {
@ -292,6 +296,18 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr
registerAction2(ChatSetupHideAction);
registerAction2(UpgradePlanAction);
}
private registerUrlLinkHandler(): void {
this._register(ExtensionUrlHandlerOverrideRegistry.registerHandler(URI.parse(`${this.productService.urlProtocol}://${defaultChat.chatExtensionId}`), {
handleURL: async () => {
this.telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', { id: TRIGGER_SETUP_COMMAND_ID, from: 'url' });
await this.commandService.executeCommand(TRIGGER_SETUP_COMMAND_ID);
return true;
}
}));
}
}
//#endregion

View File

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { localize, localize2 } from '../../../../nls.js';
import { IDisposable, combinedDisposable } from '../../../../base/common/lifecycle.js';
import { IDisposable, combinedDisposable, toDisposable } from '../../../../base/common/lifecycle.js';
import { URI } from '../../../../base/common/uri.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';
@ -27,6 +27,7 @@ import { ICommandService } from '../../../../platform/commands/common/commands.j
import { isCancellationError } from '../../../../base/common/errors.js';
import { INotificationService } from '../../../../platform/notification/common/notification.js';
import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js';
import { ResourceMap } from '../../../../base/common/map.js';
const FIVE_MINUTES = 5 * 60 * 1000;
const THIRTY_SECONDS = 30 * 1000;
@ -99,6 +100,25 @@ type ExtensionUrlReloadHandlerClassification = {
comment: 'This is used to understand the drop funnel of extension URI handling by the OS & VS Code.';
};
export interface IExtensionUrlHandlerOverride {
handleURL(uri: URI): Promise<boolean>;
}
export class ExtensionUrlHandlerOverrideRegistry {
private static readonly handlers = new ResourceMap<IExtensionUrlHandlerOverride>();
static registerHandler(uri: URI, handler: IExtensionUrlHandlerOverride): IDisposable {
this.handlers.set(uri, handler);
return toDisposable(() => this.handlers.delete(uri));
}
static getHandler(uri: URI): IExtensionUrlHandlerOverride | undefined {
return this.handlers.get(uri);
}
}
/**
* This class handles URLs which are directed towards extensions.
* If a URL is directed towards an inactive extension, it buffers it,
@ -153,6 +173,14 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
return false;
}
const overrideHandler = ExtensionUrlHandlerOverrideRegistry.getHandler(uri);
if (overrideHandler) {
const handled = await overrideHandler.handleURL(uri);
if (handled) {
return handled;
}
}
const extensionId = uri.authority;
this.telemetryService.publicLog2<ExtensionUrlHandlerEvent, ExtensionUrlHandlerClassification>('uri_invoked/start', { extensionId });