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