Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/code/electron-browser/workbench/workbench.ts
3294 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
/* eslint-disable no-restricted-globals */
7
8
(async function () {
9
10
// Add a perf entry right from the top
11
performance.mark('code/didStartRenderer');
12
13
type ISandboxConfiguration = import('../../../base/parts/sandbox/common/sandboxTypes.js').ISandboxConfiguration;
14
type ILoadResult<M, T extends ISandboxConfiguration> = import('../../../platform/window/electron-browser/window.js').ILoadResult<M, T>;
15
type ILoadOptions<T extends ISandboxConfiguration> = import('../../../platform/window/electron-browser/window.js').ILoadOptions<T>;
16
type INativeWindowConfiguration = import('../../../platform/window/common/window.ts').INativeWindowConfiguration;
17
type IMainWindowSandboxGlobals = import('../../../base/parts/sandbox/electron-browser/globals.js').IMainWindowSandboxGlobals;
18
type IDesktopMain = import('../../../workbench/electron-browser/desktop.main.js').IDesktopMain;
19
20
const preloadGlobals: IMainWindowSandboxGlobals = (window as any).vscode; // defined by preload.ts
21
const safeProcess = preloadGlobals.process;
22
23
//#region Splash Screen Helpers
24
25
function showSplash(configuration: INativeWindowConfiguration) {
26
performance.mark('code/willShowPartsSplash');
27
28
let data = configuration.partsSplash;
29
if (data) {
30
if (configuration.autoDetectHighContrast && configuration.colorScheme.highContrast) {
31
if ((configuration.colorScheme.dark && data.baseTheme !== 'hc-black') || (!configuration.colorScheme.dark && data.baseTheme !== 'hc-light')) {
32
data = undefined; // high contrast mode has been turned by the OS -> ignore stored colors and layouts
33
}
34
} else if (configuration.autoDetectColorScheme) {
35
if ((configuration.colorScheme.dark && data.baseTheme !== 'vs-dark') || (!configuration.colorScheme.dark && data.baseTheme !== 'vs')) {
36
data = undefined; // OS color scheme is tracked and has changed
37
}
38
}
39
}
40
41
// developing an extension -> ignore stored layouts
42
if (data && configuration.extensionDevelopmentPath) {
43
data.layoutInfo = undefined;
44
}
45
46
// minimal color configuration (works with or without persisted data)
47
let baseTheme;
48
let shellBackground;
49
let shellForeground;
50
if (data) {
51
baseTheme = data.baseTheme;
52
shellBackground = data.colorInfo.editorBackground;
53
shellForeground = data.colorInfo.foreground;
54
} else if (configuration.autoDetectHighContrast && configuration.colorScheme.highContrast) {
55
if (configuration.colorScheme.dark) {
56
baseTheme = 'hc-black';
57
shellBackground = '#000000';
58
shellForeground = '#FFFFFF';
59
} else {
60
baseTheme = 'hc-light';
61
shellBackground = '#FFFFFF';
62
shellForeground = '#000000';
63
}
64
} else if (configuration.autoDetectColorScheme) {
65
if (configuration.colorScheme.dark) {
66
baseTheme = 'vs-dark';
67
shellBackground = '#1E1E1E';
68
shellForeground = '#CCCCCC';
69
} else {
70
baseTheme = 'vs';
71
shellBackground = '#FFFFFF';
72
shellForeground = '#000000';
73
}
74
}
75
76
const style = document.createElement('style');
77
style.className = 'initialShellColors';
78
window.document.head.appendChild(style);
79
style.textContent = `body { background-color: ${shellBackground}; color: ${shellForeground}; margin: 0; padding: 0; }`;
80
81
// set zoom level as soon as possible
82
if (typeof data?.zoomLevel === 'number' && typeof preloadGlobals?.webFrame?.setZoomLevel === 'function') {
83
preloadGlobals.webFrame.setZoomLevel(data.zoomLevel);
84
}
85
86
// restore parts if possible (we might not always store layout info)
87
if (data?.layoutInfo) {
88
const { layoutInfo, colorInfo } = data;
89
90
const splash = document.createElement('div');
91
splash.id = 'monaco-parts-splash';
92
splash.className = baseTheme ?? 'vs-dark';
93
94
if (layoutInfo.windowBorder && colorInfo.windowBorder) {
95
const borderElement = document.createElement('div');
96
borderElement.style.position = 'absolute';
97
borderElement.style.width = 'calc(100vw - 2px)';
98
borderElement.style.height = 'calc(100vh - 2px)';
99
borderElement.style.zIndex = '1'; // allow border above other elements
100
borderElement.style.border = `1px solid var(--window-border-color)`;
101
borderElement.style.setProperty('--window-border-color', colorInfo.windowBorder);
102
103
if (layoutInfo.windowBorderRadius) {
104
borderElement.style.borderRadius = layoutInfo.windowBorderRadius;
105
}
106
107
splash.appendChild(borderElement);
108
}
109
110
if (layoutInfo.auxiliaryBarWidth === Number.MAX_SAFE_INTEGER) {
111
// if auxiliary bar is maximized, it goes as wide as the
112
// window width but leaving room for activity bar
113
layoutInfo.auxiliaryBarWidth = window.innerWidth - layoutInfo.activityBarWidth;
114
} else {
115
// otherwise adjust for other parts sizes if not maximized
116
layoutInfo.auxiliaryBarWidth = Math.min(layoutInfo.auxiliaryBarWidth, window.innerWidth - (layoutInfo.activityBarWidth + layoutInfo.editorPartMinWidth + layoutInfo.sideBarWidth));
117
}
118
layoutInfo.sideBarWidth = Math.min(layoutInfo.sideBarWidth, window.innerWidth - (layoutInfo.activityBarWidth + layoutInfo.editorPartMinWidth + layoutInfo.auxiliaryBarWidth));
119
120
// part: title
121
if (layoutInfo.titleBarHeight > 0) {
122
const titleDiv = document.createElement('div');
123
titleDiv.style.position = 'absolute';
124
titleDiv.style.width = '100%';
125
titleDiv.style.height = `${layoutInfo.titleBarHeight}px`;
126
titleDiv.style.left = '0';
127
titleDiv.style.top = '0';
128
titleDiv.style.backgroundColor = `${colorInfo.titleBarBackground}`;
129
(titleDiv.style as any)['-webkit-app-region'] = 'drag';
130
splash.appendChild(titleDiv);
131
132
if (colorInfo.titleBarBorder) {
133
const titleBorder = document.createElement('div');
134
titleBorder.style.position = 'absolute';
135
titleBorder.style.width = '100%';
136
titleBorder.style.height = '1px';
137
titleBorder.style.left = '0';
138
titleBorder.style.bottom = '0';
139
titleBorder.style.borderBottom = `1px solid ${colorInfo.titleBarBorder}`;
140
titleDiv.appendChild(titleBorder);
141
}
142
}
143
144
// part: activity bar
145
if (layoutInfo.activityBarWidth > 0) {
146
const activityDiv = document.createElement('div');
147
activityDiv.style.position = 'absolute';
148
activityDiv.style.width = `${layoutInfo.activityBarWidth}px`;
149
activityDiv.style.height = `calc(100% - ${layoutInfo.titleBarHeight + layoutInfo.statusBarHeight}px)`;
150
activityDiv.style.top = `${layoutInfo.titleBarHeight}px`;
151
if (layoutInfo.sideBarSide === 'left') {
152
activityDiv.style.left = '0';
153
} else {
154
activityDiv.style.right = '0';
155
}
156
activityDiv.style.backgroundColor = `${colorInfo.activityBarBackground}`;
157
splash.appendChild(activityDiv);
158
159
if (colorInfo.activityBarBorder) {
160
const activityBorderDiv = document.createElement('div');
161
activityBorderDiv.style.position = 'absolute';
162
activityBorderDiv.style.width = '1px';
163
activityBorderDiv.style.height = '100%';
164
activityBorderDiv.style.top = '0';
165
if (layoutInfo.sideBarSide === 'left') {
166
activityBorderDiv.style.right = '0';
167
activityBorderDiv.style.borderRight = `1px solid ${colorInfo.activityBarBorder}`;
168
} else {
169
activityBorderDiv.style.left = '0';
170
activityBorderDiv.style.borderLeft = `1px solid ${colorInfo.activityBarBorder}`;
171
}
172
activityDiv.appendChild(activityBorderDiv);
173
}
174
}
175
176
// part: side bar
177
if (layoutInfo.sideBarWidth > 0) {
178
const sideDiv = document.createElement('div');
179
sideDiv.style.position = 'absolute';
180
sideDiv.style.width = `${layoutInfo.sideBarWidth}px`;
181
sideDiv.style.height = `calc(100% - ${layoutInfo.titleBarHeight + layoutInfo.statusBarHeight}px)`;
182
sideDiv.style.top = `${layoutInfo.titleBarHeight}px`;
183
if (layoutInfo.sideBarSide === 'left') {
184
sideDiv.style.left = `${layoutInfo.activityBarWidth}px`;
185
} else {
186
sideDiv.style.right = `${layoutInfo.activityBarWidth}px`;
187
}
188
sideDiv.style.backgroundColor = `${colorInfo.sideBarBackground}`;
189
splash.appendChild(sideDiv);
190
191
if (colorInfo.sideBarBorder) {
192
const sideBorderDiv = document.createElement('div');
193
sideBorderDiv.style.position = 'absolute';
194
sideBorderDiv.style.width = '1px';
195
sideBorderDiv.style.height = '100%';
196
sideBorderDiv.style.top = '0';
197
sideBorderDiv.style.right = '0';
198
if (layoutInfo.sideBarSide === 'left') {
199
sideBorderDiv.style.borderRight = `1px solid ${colorInfo.sideBarBorder}`;
200
} else {
201
sideBorderDiv.style.left = '0';
202
sideBorderDiv.style.borderLeft = `1px solid ${colorInfo.sideBarBorder}`;
203
}
204
sideDiv.appendChild(sideBorderDiv);
205
}
206
}
207
208
// part: auxiliary sidebar
209
if (layoutInfo.auxiliaryBarWidth > 0) {
210
const auxSideDiv = document.createElement('div');
211
auxSideDiv.style.position = 'absolute';
212
auxSideDiv.style.width = `${layoutInfo.auxiliaryBarWidth}px`;
213
auxSideDiv.style.height = `calc(100% - ${layoutInfo.titleBarHeight + layoutInfo.statusBarHeight}px)`;
214
auxSideDiv.style.top = `${layoutInfo.titleBarHeight}px`;
215
if (layoutInfo.sideBarSide === 'left') {
216
auxSideDiv.style.right = '0';
217
} else {
218
auxSideDiv.style.left = '0';
219
}
220
auxSideDiv.style.backgroundColor = `${colorInfo.sideBarBackground}`;
221
splash.appendChild(auxSideDiv);
222
223
if (colorInfo.sideBarBorder) {
224
const auxSideBorderDiv = document.createElement('div');
225
auxSideBorderDiv.style.position = 'absolute';
226
auxSideBorderDiv.style.width = '1px';
227
auxSideBorderDiv.style.height = '100%';
228
auxSideBorderDiv.style.top = '0';
229
if (layoutInfo.sideBarSide === 'left') {
230
auxSideBorderDiv.style.left = '0';
231
auxSideBorderDiv.style.borderLeft = `1px solid ${colorInfo.sideBarBorder}`;
232
} else {
233
auxSideBorderDiv.style.right = '0';
234
auxSideBorderDiv.style.borderRight = `1px solid ${colorInfo.sideBarBorder}`;
235
}
236
auxSideDiv.appendChild(auxSideBorderDiv);
237
}
238
}
239
240
// part: statusbar
241
if (layoutInfo.statusBarHeight > 0) {
242
const statusDiv = document.createElement('div');
243
statusDiv.style.position = 'absolute';
244
statusDiv.style.width = '100%';
245
statusDiv.style.height = `${layoutInfo.statusBarHeight}px`;
246
statusDiv.style.bottom = '0';
247
statusDiv.style.left = '0';
248
if (configuration.workspace && colorInfo.statusBarBackground) {
249
statusDiv.style.backgroundColor = colorInfo.statusBarBackground;
250
} else if (!configuration.workspace && colorInfo.statusBarNoFolderBackground) {
251
statusDiv.style.backgroundColor = colorInfo.statusBarNoFolderBackground;
252
}
253
splash.appendChild(statusDiv);
254
255
if (colorInfo.statusBarBorder) {
256
const statusBorderDiv = document.createElement('div');
257
statusBorderDiv.style.position = 'absolute';
258
statusBorderDiv.style.width = '100%';
259
statusBorderDiv.style.height = '1px';
260
statusBorderDiv.style.top = '0';
261
statusBorderDiv.style.borderTop = `1px solid ${colorInfo.statusBarBorder}`;
262
statusDiv.appendChild(statusBorderDiv);
263
}
264
}
265
266
window.document.body.appendChild(splash);
267
}
268
269
performance.mark('code/didShowPartsSplash');
270
}
271
272
//#endregion
273
274
//#region Window Helpers
275
276
async function load<M, T extends ISandboxConfiguration>(esModule: string, options: ILoadOptions<T>): Promise<ILoadResult<M, T>> {
277
278
// Window Configuration from Preload Script
279
const configuration = await resolveWindowConfiguration<T>();
280
281
// Signal before import()
282
options?.beforeImport?.(configuration);
283
284
// Developer settings
285
const { enableDeveloperKeybindings, removeDeveloperKeybindingsAfterLoad, developerDeveloperKeybindingsDisposable, forceDisableShowDevtoolsOnError } = setupDeveloperKeybindings(configuration, options);
286
287
// NLS
288
setupNLS<T>(configuration);
289
290
// Compute base URL and set as global
291
const baseUrl = new URL(`${fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32', scheme: 'vscode-file', fallbackAuthority: 'vscode-app' })}/out/`);
292
globalThis._VSCODE_FILE_ROOT = baseUrl.toString();
293
294
// Dev only: CSS import map tricks
295
setupCSSImportMaps<T>(configuration, baseUrl);
296
297
// ESM Import
298
try {
299
const result = await import(new URL(`${esModule}.js`, baseUrl).href);
300
301
if (developerDeveloperKeybindingsDisposable && removeDeveloperKeybindingsAfterLoad) {
302
developerDeveloperKeybindingsDisposable();
303
}
304
305
return { result, configuration };
306
} catch (error) {
307
onUnexpectedError(error, enableDeveloperKeybindings && !forceDisableShowDevtoolsOnError);
308
309
throw error;
310
}
311
}
312
313
async function resolveWindowConfiguration<T extends ISandboxConfiguration>() {
314
const timeout = setTimeout(() => { console.error(`[resolve window config] Could not resolve window configuration within 10 seconds, but will continue to wait...`); }, 10000);
315
performance.mark('code/willWaitForWindowConfig');
316
317
const configuration = await preloadGlobals.context.resolveConfiguration() as T;
318
performance.mark('code/didWaitForWindowConfig');
319
320
clearTimeout(timeout);
321
322
return configuration;
323
}
324
325
function setupDeveloperKeybindings<T extends ISandboxConfiguration>(configuration: T, options: ILoadOptions<T>) {
326
const {
327
forceEnableDeveloperKeybindings,
328
disallowReloadKeybinding,
329
removeDeveloperKeybindingsAfterLoad,
330
forceDisableShowDevtoolsOnError
331
} = typeof options?.configureDeveloperSettings === 'function' ? options.configureDeveloperSettings(configuration) : {
332
forceEnableDeveloperKeybindings: false,
333
disallowReloadKeybinding: false,
334
removeDeveloperKeybindingsAfterLoad: false,
335
forceDisableShowDevtoolsOnError: false
336
};
337
338
const isDev = !!safeProcess.env['VSCODE_DEV'];
339
const enableDeveloperKeybindings = Boolean(isDev || forceEnableDeveloperKeybindings);
340
let developerDeveloperKeybindingsDisposable: Function | undefined = undefined;
341
if (enableDeveloperKeybindings) {
342
developerDeveloperKeybindingsDisposable = registerDeveloperKeybindings(disallowReloadKeybinding);
343
}
344
345
return {
346
enableDeveloperKeybindings,
347
removeDeveloperKeybindingsAfterLoad,
348
developerDeveloperKeybindingsDisposable,
349
forceDisableShowDevtoolsOnError
350
};
351
}
352
353
function registerDeveloperKeybindings(disallowReloadKeybinding: boolean | undefined): Function {
354
const ipcRenderer = preloadGlobals.ipcRenderer;
355
356
const extractKey =
357
function (e: KeyboardEvent) {
358
return [
359
e.ctrlKey ? 'ctrl-' : '',
360
e.metaKey ? 'meta-' : '',
361
e.altKey ? 'alt-' : '',
362
e.shiftKey ? 'shift-' : '',
363
e.keyCode
364
].join('');
365
};
366
367
// Devtools & reload support
368
const TOGGLE_DEV_TOOLS_KB = (safeProcess.platform === 'darwin' ? 'meta-alt-73' : 'ctrl-shift-73'); // mac: Cmd-Alt-I, rest: Ctrl-Shift-I
369
const TOGGLE_DEV_TOOLS_KB_ALT = '123'; // F12
370
const RELOAD_KB = (safeProcess.platform === 'darwin' ? 'meta-82' : 'ctrl-82'); // mac: Cmd-R, rest: Ctrl-R
371
372
let listener: ((e: KeyboardEvent) => void) | undefined = function (e) {
373
const key = extractKey(e);
374
if (key === TOGGLE_DEV_TOOLS_KB || key === TOGGLE_DEV_TOOLS_KB_ALT) {
375
ipcRenderer.send('vscode:toggleDevTools');
376
} else if (key === RELOAD_KB && !disallowReloadKeybinding) {
377
ipcRenderer.send('vscode:reloadWindow');
378
}
379
};
380
381
window.addEventListener('keydown', listener);
382
383
return function () {
384
if (listener) {
385
window.removeEventListener('keydown', listener);
386
listener = undefined;
387
}
388
};
389
}
390
391
function setupNLS<T extends ISandboxConfiguration>(configuration: T): void {
392
globalThis._VSCODE_NLS_MESSAGES = configuration.nls.messages;
393
globalThis._VSCODE_NLS_LANGUAGE = configuration.nls.language;
394
395
let language = configuration.nls.language || 'en';
396
if (language === 'zh-tw') {
397
language = 'zh-Hant';
398
} else if (language === 'zh-cn') {
399
language = 'zh-Hans';
400
}
401
402
window.document.documentElement.setAttribute('lang', language);
403
}
404
405
function onUnexpectedError(error: string | Error, showDevtoolsOnError: boolean): void {
406
if (showDevtoolsOnError) {
407
const ipcRenderer = preloadGlobals.ipcRenderer;
408
ipcRenderer.send('vscode:openDevTools');
409
}
410
411
console.error(`[uncaught exception]: ${error}`);
412
413
if (error && typeof error !== 'string' && error.stack) {
414
console.error(error.stack);
415
}
416
}
417
418
function fileUriFromPath(path: string, config: { isWindows?: boolean; scheme?: string; fallbackAuthority?: string }): string {
419
420
// Since we are building a URI, we normalize any backslash
421
// to slashes and we ensure that the path begins with a '/'.
422
let pathName = path.replace(/\\/g, '/');
423
if (pathName.length > 0 && pathName.charAt(0) !== '/') {
424
pathName = `/${pathName}`;
425
}
426
427
let uri: string;
428
429
// Windows: in order to support UNC paths (which start with '//')
430
// that have their own authority, we do not use the provided authority
431
// but rather preserve it.
432
if (config.isWindows && pathName.startsWith('//')) {
433
uri = encodeURI(`${config.scheme || 'file'}:${pathName}`);
434
}
435
436
// Otherwise we optionally add the provided authority if specified
437
else {
438
uri = encodeURI(`${config.scheme || 'file'}://${config.fallbackAuthority || ''}${pathName}`);
439
}
440
441
return uri.replace(/#/g, '%23');
442
}
443
444
function setupCSSImportMaps<T extends ISandboxConfiguration>(configuration: T, baseUrl: URL) {
445
446
// DEV ---------------------------------------------------------------------------------------
447
// DEV: This is for development and enables loading CSS via import-statements via import-maps.
448
// DEV: For each CSS modules that we have we defined an entry in the import map that maps to
449
// DEV: a blob URL that loads the CSS via a dynamic @import-rule.
450
// DEV ---------------------------------------------------------------------------------------
451
452
if (Array.isArray(configuration.cssModules) && configuration.cssModules.length > 0) {
453
performance.mark('code/willAddCssLoader');
454
455
globalThis._VSCODE_CSS_LOAD = function (url) {
456
const link = document.createElement('link');
457
link.setAttribute('rel', 'stylesheet');
458
link.setAttribute('type', 'text/css');
459
link.setAttribute('href', url);
460
461
window.document.head.appendChild(link);
462
};
463
464
const importMap: { imports: Record<string, string> } = { imports: {} };
465
for (const cssModule of configuration.cssModules) {
466
const cssUrl = new URL(cssModule, baseUrl).href;
467
const jsSrc = `globalThis._VSCODE_CSS_LOAD('${cssUrl}');\n`;
468
const blob = new Blob([jsSrc], { type: 'application/javascript' });
469
importMap.imports[cssUrl] = URL.createObjectURL(blob);
470
}
471
472
const ttp = window.trustedTypes?.createPolicy('vscode-bootstrapImportMap', { createScript(value) { return value; }, });
473
const importMapSrc = JSON.stringify(importMap, undefined, 2);
474
const importMapScript = document.createElement('script');
475
importMapScript.type = 'importmap';
476
importMapScript.setAttribute('nonce', '0c6a828f1297');
477
// @ts-ignore
478
importMapScript.textContent = ttp?.createScript(importMapSrc) ?? importMapSrc;
479
window.document.head.appendChild(importMapScript);
480
481
performance.mark('code/didAddCssLoader');
482
}
483
}
484
485
//#endregion
486
487
const { result, configuration } = await load<IDesktopMain, INativeWindowConfiguration>('vs/workbench/workbench.desktop.main',
488
{
489
configureDeveloperSettings: function (windowConfig) {
490
return {
491
// disable automated devtools opening on error when running extension tests
492
// as this can lead to nondeterministic test execution (devtools steals focus)
493
forceDisableShowDevtoolsOnError: typeof windowConfig.extensionTestsPath === 'string' || windowConfig['enable-smoke-test-driver'] === true,
494
// enable devtools keybindings in extension development window
495
forceEnableDeveloperKeybindings: Array.isArray(windowConfig.extensionDevelopmentPath) && windowConfig.extensionDevelopmentPath.length > 0,
496
removeDeveloperKeybindingsAfterLoad: true
497
};
498
},
499
beforeImport: function (windowConfig) {
500
501
// Show our splash as early as possible
502
showSplash(windowConfig);
503
504
// Code windows have a `vscodeWindowId` property to identify them
505
Object.defineProperty(window, 'vscodeWindowId', {
506
get: () => windowConfig.windowId
507
});
508
509
// It looks like browsers only lazily enable
510
// the <canvas> element when needed. Since we
511
// leverage canvas elements in our code in many
512
// locations, we try to help the browser to
513
// initialize canvas when it is idle, right
514
// before we wait for the scripts to be loaded.
515
window.requestIdleCallback(() => {
516
const canvas = document.createElement('canvas');
517
const context = canvas.getContext('2d');
518
context?.clearRect(0, 0, canvas.width, canvas.height);
519
canvas.remove();
520
}, { timeout: 50 });
521
522
// Track import() perf
523
performance.mark('code/willLoadWorkbenchMain');
524
}
525
}
526
);
527
528
// Mark start of workbench
529
performance.mark('code/didLoadWorkbenchMain');
530
531
// Load workbench
532
result.main(configuration);
533
}());
534
535