Path: blob/main/src/vs/workbench/contrib/extensions/electron-browser/debugExtensionHostAction.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Codicon } from '../../../../base/common/codicons.js';6import { Disposable } from '../../../../base/common/lifecycle.js';7import { randomPort } from '../../../../base/common/ports.js';8import * as nls from '../../../../nls.js';9import { Categories } from '../../../../platform/action/common/actionCommonCategories.js';10import { Action2, MenuId } from '../../../../platform/actions/common/actions.js';11import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';12import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';13import { INativeHostService } from '../../../../platform/native/common/native.js';14import { IProductService } from '../../../../platform/product/common/productService.js';15import { IProgressService, ProgressLocation } from '../../../../platform/progress/common/progress.js';16import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';17import { ActiveEditorContext } from '../../../common/contextkeys.js';18import { IWorkbenchContribution } from '../../../common/contributions.js';19import { ExtensionHostKind } from '../../../services/extensions/common/extensionHostKind.js';20import { IExtensionService, IExtensionInspectInfo } from '../../../services/extensions/common/extensions.js';21import { IHostService } from '../../../services/host/browser/host.js';22import { IConfig, IDebugService } from '../../debug/common/debug.js';23import { RuntimeExtensionsEditor } from './runtimeExtensionsEditor.js';24import { IQuickInputService, IQuickPickItem } from '../../../../platform/quickinput/common/quickInput.js';2526interface IExtensionHostQuickPickItem extends IQuickPickItem {27portInfo: IExtensionInspectInfo;28}2930export class DebugExtensionHostInDevToolsAction extends Action2 {31constructor() {32super({33id: 'workbench.extensions.action.devtoolsExtensionHost',34title: nls.localize2('openDevToolsForExtensionHost', 'Debug Extension Host In Dev Tools'),35category: Categories.Developer,36f1: true,37icon: Codicon.debugStart,38});39}4041async run(accessor: ServicesAccessor): Promise<void> {42const extensionService = accessor.get(IExtensionService);43const nativeHostService = accessor.get(INativeHostService);44const quickInputService = accessor.get(IQuickInputService);4546const inspectPorts = await extensionService.getInspectPorts(ExtensionHostKind.LocalProcess, true);4748if (inspectPorts.length === 0) {49console.log('[devtoolsExtensionHost] No extension host inspect ports found.');50return;51}5253const items: IExtensionHostQuickPickItem[] = inspectPorts.filter(portInfo => portInfo.devtoolsUrl).map(portInfo => ({54label: portInfo.devtoolsLabel ?? `${portInfo.host}:${portInfo.port}`,55detail: `${portInfo.host}:${portInfo.port}`,56portInfo: portInfo57}));5859if (items.length === 1) {60const portInfo = items[0].portInfo;61nativeHostService.openDevToolsWindow(portInfo.devtoolsUrl!);62return;63}6465const selected = await quickInputService.pick<IExtensionHostQuickPickItem>(items, {66placeHolder: nls.localize('selectExtensionHost', "Pick extension host"),67matchOnDetail: true,68});6970if (selected) {71const portInfo = selected.portInfo;72nativeHostService.openDevToolsWindow(portInfo.devtoolsUrl!);73}74}75}7677export class DebugExtensionHostInNewWindowAction extends Action2 {78constructor() {79super({80id: 'workbench.extensions.action.debugExtensionHost',81title: nls.localize2('debugExtensionHost', "Debug Extension Host In New Window"),82category: Categories.Developer,83f1: true,84icon: Codicon.debugStart,85menu: {86id: MenuId.EditorTitle,87when: ActiveEditorContext.isEqualTo(RuntimeExtensionsEditor.ID),88group: 'navigation',89}90});91}9293run(accessor: ServicesAccessor): void {94const nativeHostService = accessor.get(INativeHostService);95const dialogService = accessor.get(IDialogService);96const extensionService = accessor.get(IExtensionService);97const productService = accessor.get(IProductService);98const instantiationService = accessor.get(IInstantiationService);99const hostService = accessor.get(IHostService);100101extensionService.getInspectPorts(ExtensionHostKind.LocalProcess, false).then(async inspectPorts => {102if (inspectPorts.length === 0) {103const res = await dialogService.confirm({104message: nls.localize('restart1', "Debug Extensions"),105detail: nls.localize('restart2', "In order to debug extensions a restart is required. Do you want to restart '{0}' now?", productService.nameLong),106primaryButton: nls.localize({ key: 'restart3', comment: ['&& denotes a mnemonic'] }, "&&Restart")107});108if (res.confirmed) {109await nativeHostService.relaunch({ addArgs: [`--inspect-extensions=${randomPort()}`] });110}111return;112}113114if (inspectPorts.length > 1) {115// TODO116console.warn(`There are multiple extension hosts available for debugging. Picking the first one...`);117}118119const s = instantiationService.createInstance(Storage);120s.storeDebugOnNewWindow(inspectPorts[0].port);121122hostService.openWindow();123});124}125}126127class Storage {128constructor(@IStorageService private readonly _storageService: IStorageService,) {129}130131storeDebugOnNewWindow(targetPort: number) {132this._storageService.store('debugExtensionHost.debugPort', targetPort, StorageScope.APPLICATION, StorageTarget.MACHINE);133}134135getAndDeleteDebugPortIfSet(): number | undefined {136const port = this._storageService.getNumber('debugExtensionHost.debugPort', StorageScope.APPLICATION);137if (port !== undefined) {138this._storageService.remove('debugExtensionHost.debugPort', StorageScope.APPLICATION);139}140return port;141}142}143144export class DebugExtensionsContribution extends Disposable implements IWorkbenchContribution {145constructor(146@IDebugService private readonly _debugService: IDebugService,147@IInstantiationService private readonly _instantiationService: IInstantiationService,148@IProgressService _progressService: IProgressService,149) {150super();151152const storage = this._instantiationService.createInstance(Storage);153const port = storage.getAndDeleteDebugPortIfSet();154if (port !== undefined) {155_progressService.withProgress({156location: ProgressLocation.Notification,157title: nls.localize('debugExtensionHost.progress', "Attaching Debugger To Extension Host"),158}, async p => {159// eslint-disable-next-line local/code-no-dangerous-type-assertions160await this._debugService.startDebugging(undefined, {161type: 'node',162name: nls.localize('debugExtensionHost.launch.name', "Attach Extension Host"),163request: 'attach',164port,165trace: true,166// resolve source maps everywhere:167resolveSourceMapLocations: null,168// announces sources eagerly for the loaded scripts view:169eagerSources: true,170// source maps of published VS Code are on the CDN and can take a while to load171timeouts: {172sourceMapMinPause: 30_000,173sourceMapCumulativePause: 300_000,174},175} as IConfig);176});177}178}179}180181182