Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/debug/browser/debugViewlet.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 { IActionViewItem } from '../../../../base/browser/ui/actionbar/actionbar.js';
7
import { IAction } from '../../../../base/common/actions.js';
8
import { DisposableStore, dispose, IDisposable } from '../../../../base/common/lifecycle.js';
9
import './media/debugViewlet.css';
10
import * as nls from '../../../../nls.js';
11
import { createActionViewItem } from '../../../../platform/actions/browser/menuEntryActionViewItem.js';
12
import { Action2, MenuId, MenuItemAction, MenuRegistry, registerAction2 } from '../../../../platform/actions/common/actions.js';
13
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
14
import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
15
import { IContextMenuService, IContextViewService } from '../../../../platform/contextview/browser/contextView.js';
16
import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
17
import { IProgressService } from '../../../../platform/progress/common/progress.js';
18
import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js';
19
import { IStorageService } from '../../../../platform/storage/common/storage.js';
20
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
21
import { IThemeService } from '../../../../platform/theme/common/themeService.js';
22
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
23
import { ViewPane } from '../../../browser/parts/views/viewPane.js';
24
import { ViewPaneContainer, ViewsSubMenu } from '../../../browser/parts/views/viewPaneContainer.js';
25
import { WorkbenchStateContext } from '../../../common/contextkeys.js';
26
import { IViewDescriptorService } from '../../../common/views.js';
27
import { IViewsService } from '../../../services/views/common/viewsService.js';
28
import { FocusSessionActionViewItem, StartDebugActionViewItem } from './debugActionViewItems.js';
29
import { DEBUG_CONFIGURE_COMMAND_ID, DEBUG_CONFIGURE_LABEL, DEBUG_START_COMMAND_ID, DEBUG_START_LABEL, DISCONNECT_ID, FOCUS_SESSION_ID, SELECT_AND_START_ID, STOP_ID } from './debugCommands.js';
30
import { debugConfigure } from './debugIcons.js';
31
import { createDisconnectMenuItemAction } from './debugToolBar.js';
32
import { WelcomeView } from './welcomeView.js';
33
import { BREAKPOINTS_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE, CONTEXT_DEBUG_UX, CONTEXT_DEBUG_UX_KEY, getStateLabel, IDebugService, ILaunch, REPL_VIEW_ID, State, VIEWLET_ID, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution } from '../common/debug.js';
34
import { IExtensionService } from '../../../services/extensions/common/extensions.js';
35
import { IWorkbenchLayoutService } from '../../../services/layout/browser/layoutService.js';
36
import { IBaseActionViewItemOptions } from '../../../../base/browser/ui/actionbar/actionViewItems.js';
37
import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js';
38
import { ILogService } from '../../../../platform/log/common/log.js';
39
40
export class DebugViewPaneContainer extends ViewPaneContainer {
41
42
private startDebugActionViewItem: StartDebugActionViewItem | undefined;
43
private progressResolve: (() => void) | undefined;
44
private breakpointView: ViewPane | undefined;
45
private paneListeners = new Map<string, IDisposable>();
46
47
private readonly stopActionViewItemDisposables = this._register(new DisposableStore());
48
49
constructor(
50
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
51
@ITelemetryService telemetryService: ITelemetryService,
52
@IProgressService private readonly progressService: IProgressService,
53
@IDebugService private readonly debugService: IDebugService,
54
@IInstantiationService instantiationService: IInstantiationService,
55
@IWorkspaceContextService contextService: IWorkspaceContextService,
56
@IStorageService storageService: IStorageService,
57
@IThemeService themeService: IThemeService,
58
@IContextMenuService contextMenuService: IContextMenuService,
59
@IExtensionService extensionService: IExtensionService,
60
@IConfigurationService configurationService: IConfigurationService,
61
@IContextViewService private readonly contextViewService: IContextViewService,
62
@IContextKeyService private readonly contextKeyService: IContextKeyService,
63
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
64
@ILogService logService: ILogService,
65
) {
66
super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService, logService);
67
68
// When there are potential updates to the docked debug toolbar we need to update it
69
this._register(this.debugService.onDidChangeState(state => this.onDebugServiceStateChange(state)));
70
71
this._register(this.contextKeyService.onDidChangeContext(e => {
72
if (e.affectsSome(new Set([CONTEXT_DEBUG_UX_KEY, 'inDebugMode']))) {
73
this.updateTitleArea();
74
}
75
}));
76
77
this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateTitleArea()));
78
this._register(this.configurationService.onDidChangeConfiguration(e => {
79
if (e.affectsConfiguration('debug.toolBarLocation') || e.affectsConfiguration('debug.hideLauncherWhileDebugging')) {
80
this.updateTitleArea();
81
}
82
}));
83
}
84
85
override create(parent: HTMLElement): void {
86
super.create(parent);
87
parent.classList.add('debug-viewlet');
88
}
89
90
override focus(): void {
91
super.focus();
92
93
if (this.startDebugActionViewItem) {
94
this.startDebugActionViewItem.focus();
95
} else {
96
this.focusView(WelcomeView.ID);
97
}
98
}
99
100
override getActionViewItem(action: IAction, options: IBaseActionViewItemOptions): IActionViewItem | undefined {
101
if (action.id === DEBUG_START_COMMAND_ID) {
102
this.startDebugActionViewItem = this.instantiationService.createInstance(StartDebugActionViewItem, null, action, options);
103
return this.startDebugActionViewItem;
104
}
105
if (action.id === FOCUS_SESSION_ID) {
106
return new FocusSessionActionViewItem(action, undefined, this.debugService, this.contextViewService, this.configurationService);
107
}
108
109
if (action.id === STOP_ID || action.id === DISCONNECT_ID) {
110
this.stopActionViewItemDisposables.clear();
111
const item = this.instantiationService.invokeFunction(accessor => createDisconnectMenuItemAction(action as MenuItemAction, this.stopActionViewItemDisposables, accessor, { hoverDelegate: options.hoverDelegate }));
112
if (item) {
113
return item;
114
}
115
}
116
117
return createActionViewItem(this.instantiationService, action, options);
118
}
119
120
focusView(id: string): void {
121
const view = this.getView(id);
122
if (view) {
123
view.focus();
124
}
125
}
126
127
private onDebugServiceStateChange(state: State): void {
128
if (this.progressResolve) {
129
this.progressResolve();
130
this.progressResolve = undefined;
131
}
132
133
if (state === State.Initializing) {
134
this.progressService.withProgress({ location: VIEWLET_ID, }, _progress => {
135
return new Promise<void>(resolve => this.progressResolve = resolve);
136
});
137
}
138
}
139
140
override addPanes(panes: { pane: ViewPane; size: number; index?: number; disposable: IDisposable }[]): void {
141
super.addPanes(panes);
142
143
for (const { pane: pane } of panes) {
144
// attach event listener to
145
if (pane.id === BREAKPOINTS_VIEW_ID) {
146
this.breakpointView = pane;
147
this.updateBreakpointsMaxSize();
148
} else {
149
this.paneListeners.set(pane.id, pane.onDidChange(() => this.updateBreakpointsMaxSize()));
150
}
151
}
152
}
153
154
override removePanes(panes: ViewPane[]): void {
155
super.removePanes(panes);
156
for (const pane of panes) {
157
dispose(this.paneListeners.get(pane.id));
158
this.paneListeners.delete(pane.id);
159
}
160
}
161
162
private updateBreakpointsMaxSize(): void {
163
if (this.breakpointView) {
164
// We need to update the breakpoints view since all other views are collapsed #25384
165
const allOtherCollapsed = this.panes.every(view => !view.isExpanded() || view === this.breakpointView);
166
this.breakpointView.maximumBodySize = allOtherCollapsed ? Number.POSITIVE_INFINITY : this.breakpointView.minimumBodySize;
167
}
168
}
169
}
170
171
MenuRegistry.appendMenuItem(MenuId.ViewContainerTitle, {
172
when: ContextKeyExpr.and(
173
ContextKeyExpr.equals('viewContainer', VIEWLET_ID),
174
CONTEXT_DEBUG_UX.notEqualsTo('simple'),
175
WorkbenchStateContext.notEqualsTo('empty'),
176
ContextKeyExpr.or(
177
CONTEXT_DEBUG_STATE.isEqualTo('inactive'),
178
ContextKeyExpr.notEquals('config.debug.toolBarLocation', 'docked')
179
),
180
ContextKeyExpr.or(
181
ContextKeyExpr.not('config.debug.hideLauncherWhileDebugging'),
182
ContextKeyExpr.not('inDebugMode')
183
)
184
),
185
order: 10,
186
group: 'navigation',
187
command: {
188
precondition: CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing)),
189
id: DEBUG_START_COMMAND_ID,
190
title: DEBUG_START_LABEL
191
}
192
});
193
194
registerAction2(class extends Action2 {
195
constructor() {
196
super({
197
id: DEBUG_CONFIGURE_COMMAND_ID,
198
title: {
199
value: DEBUG_CONFIGURE_LABEL,
200
original: 'Open \'launch.json\'',
201
mnemonicTitle: nls.localize({ key: 'miOpenConfigurations', comment: ['&& denotes a mnemonic'] }, "Open &&Configurations")
202
},
203
metadata: {
204
description: nls.localize2('openLaunchConfigDescription', 'Opens the file used to configure how your program is debugged')
205
},
206
f1: true,
207
icon: debugConfigure,
208
precondition: CONTEXT_DEBUG_UX.notEqualsTo('simple'),
209
menu: [{
210
id: MenuId.ViewContainerTitle,
211
group: 'navigation',
212
order: 20,
213
when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), CONTEXT_DEBUG_UX.notEqualsTo('simple'), WorkbenchStateContext.notEqualsTo('empty'),
214
ContextKeyExpr.or(CONTEXT_DEBUG_STATE.isEqualTo('inactive'), ContextKeyExpr.notEquals('config.debug.toolBarLocation', 'docked')))
215
}, {
216
id: MenuId.ViewContainerTitle,
217
order: 20,
218
// Show in debug viewlet secondary actions when debugging and debug toolbar is docked
219
when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), CONTEXT_DEBUG_STATE.notEqualsTo('inactive'), ContextKeyExpr.equals('config.debug.toolBarLocation', 'docked'))
220
}, {
221
id: MenuId.MenubarDebugMenu,
222
group: '2_configuration',
223
order: 1,
224
when: CONTEXT_DEBUGGERS_AVAILABLE
225
}]
226
});
227
}
228
229
async run(accessor: ServicesAccessor, opts?: { addNew?: boolean }): Promise<void> {
230
const debugService = accessor.get(IDebugService);
231
const quickInputService = accessor.get(IQuickInputService);
232
const configurationManager = debugService.getConfigurationManager();
233
let launch: ILaunch | undefined;
234
if (configurationManager.selectedConfiguration.name) {
235
launch = configurationManager.selectedConfiguration.launch;
236
} else {
237
const launches = configurationManager.getLaunches().filter(l => !l.hidden);
238
if (launches.length === 1) {
239
launch = launches[0];
240
} else {
241
const picks = launches.map(l => ({ label: l.name, launch: l }));
242
const picked = await quickInputService.pick<{ label: string; launch: ILaunch }>(picks, {
243
activeItem: picks[0],
244
placeHolder: nls.localize({ key: 'selectWorkspaceFolder', comment: ['User picks a workspace folder or a workspace configuration file here. Workspace configuration files can contain settings and thus a launch.json configuration can be written into one.'] }, "Select a workspace folder to create a launch.json file in or add it to the workspace config file")
245
});
246
if (picked) {
247
launch = picked.launch;
248
}
249
}
250
}
251
252
if (launch) {
253
const { editor } = await launch.openConfigFile({ preserveFocus: false });
254
if (editor && opts?.addNew) {
255
const codeEditor = <ICodeEditor>editor.getControl();
256
if (codeEditor) {
257
await codeEditor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID)?.addLaunchConfiguration();
258
}
259
}
260
}
261
}
262
});
263
264
265
registerAction2(class extends Action2 {
266
constructor() {
267
super({
268
id: 'debug.toggleReplIgnoreFocus',
269
title: nls.localize('debugPanel', "Debug Console"),
270
toggled: ContextKeyExpr.has(`view.${REPL_VIEW_ID}.visible`),
271
menu: [{
272
id: ViewsSubMenu,
273
group: '3_toggleRepl',
274
order: 30,
275
when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID))
276
}]
277
});
278
}
279
280
async run(accessor: ServicesAccessor): Promise<void> {
281
const viewsService = accessor.get(IViewsService);
282
if (viewsService.isViewVisible(REPL_VIEW_ID)) {
283
viewsService.closeView(REPL_VIEW_ID);
284
} else {
285
await viewsService.openView(REPL_VIEW_ID);
286
}
287
}
288
});
289
290
MenuRegistry.appendMenuItem(MenuId.ViewContainerTitle, {
291
when: ContextKeyExpr.and(
292
ContextKeyExpr.equals('viewContainer', VIEWLET_ID),
293
CONTEXT_DEBUG_STATE.notEqualsTo('inactive'),
294
ContextKeyExpr.or(
295
ContextKeyExpr.equals('config.debug.toolBarLocation', 'docked'),
296
ContextKeyExpr.has('config.debug.hideLauncherWhileDebugging')
297
)
298
),
299
order: 10,
300
command: {
301
id: SELECT_AND_START_ID,
302
title: nls.localize('startAdditionalSession', "Start Additional Session"),
303
}
304
});
305
306