Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/environment/browser/environmentService.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 { Schemas } from '../../../../base/common/network.js';
7
import { joinPath } from '../../../../base/common/resources.js';
8
import { URI } from '../../../../base/common/uri.js';
9
import { ExtensionKind, IEnvironmentService, IExtensionHostDebugParams } from '../../../../platform/environment/common/environment.js';
10
import { IPath } from '../../../../platform/window/common/window.js';
11
import { IWorkbenchEnvironmentService } from '../common/environmentService.js';
12
import { IWorkbenchConstructionOptions } from '../../../browser/web.api.js';
13
import { IProductService } from '../../../../platform/product/common/productService.js';
14
import { memoize } from '../../../../base/common/decorators.js';
15
import { onUnexpectedError } from '../../../../base/common/errors.js';
16
import { parseLineAndColumnAware } from '../../../../base/common/extpath.js';
17
import { LogLevelToString } from '../../../../platform/log/common/log.js';
18
import { isUndefined } from '../../../../base/common/types.js';
19
import { refineServiceDecorator } from '../../../../platform/instantiation/common/instantiation.js';
20
import { ITextEditorOptions } from '../../../../platform/editor/common/editor.js';
21
import { EXTENSION_IDENTIFIER_WITH_LOG_REGEX } from '../../../../platform/environment/common/environmentService.js';
22
23
export const IBrowserWorkbenchEnvironmentService = refineServiceDecorator<IEnvironmentService, IBrowserWorkbenchEnvironmentService>(IEnvironmentService);
24
25
/**
26
* A subclass of the `IWorkbenchEnvironmentService` to be used only environments
27
* where the web API is available (browsers, Electron).
28
*/
29
export interface IBrowserWorkbenchEnvironmentService extends IWorkbenchEnvironmentService {
30
31
/**
32
* Options used to configure the workbench.
33
*/
34
readonly options?: IWorkbenchConstructionOptions;
35
36
/**
37
* Gets whether a resolver extension is expected for the environment.
38
*/
39
readonly expectsResolverExtension: boolean;
40
}
41
42
export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvironmentService {
43
44
declare readonly _serviceBrand: undefined;
45
46
@memoize
47
get remoteAuthority(): string | undefined { return this.options.remoteAuthority; }
48
49
@memoize
50
get expectsResolverExtension(): boolean {
51
return !!this.options.remoteAuthority?.includes('+') && !this.options.webSocketFactory;
52
}
53
54
@memoize
55
get isBuilt(): boolean { return !!this.productService.commit; }
56
57
@memoize
58
get logLevel(): string | undefined {
59
const logLevelFromPayload = this.payload?.get('logLevel');
60
if (logLevelFromPayload) {
61
return logLevelFromPayload.split(',').find(entry => !EXTENSION_IDENTIFIER_WITH_LOG_REGEX.test(entry));
62
}
63
64
return this.options.developmentOptions?.logLevel !== undefined ? LogLevelToString(this.options.developmentOptions?.logLevel) : undefined;
65
}
66
67
get extensionLogLevel(): [string, string][] | undefined {
68
const logLevelFromPayload = this.payload?.get('logLevel');
69
if (logLevelFromPayload) {
70
const result: [string, string][] = [];
71
for (const entry of logLevelFromPayload.split(',')) {
72
const matches = EXTENSION_IDENTIFIER_WITH_LOG_REGEX.exec(entry);
73
if (matches && matches[1] && matches[2]) {
74
result.push([matches[1], matches[2]]);
75
}
76
}
77
78
return result.length ? result : undefined;
79
}
80
81
return this.options.developmentOptions?.extensionLogLevel !== undefined ? this.options.developmentOptions?.extensionLogLevel.map(([extension, logLevel]) => ([extension, LogLevelToString(logLevel)])) : undefined;
82
}
83
84
get profDurationMarkers(): string[] | undefined {
85
const profDurationMarkersFromPayload = this.payload?.get('profDurationMarkers');
86
if (profDurationMarkersFromPayload) {
87
const result: string[] = [];
88
for (const entry of profDurationMarkersFromPayload.split(',')) {
89
result.push(entry);
90
}
91
92
return result.length === 2 ? result : undefined;
93
}
94
95
return undefined;
96
}
97
98
@memoize
99
get windowLogsPath(): URI { return this.logsHome; }
100
101
@memoize
102
get logFile(): URI { return joinPath(this.windowLogsPath, 'window.log'); }
103
104
@memoize
105
get userRoamingDataHome(): URI { return URI.file('/User').with({ scheme: Schemas.vscodeUserData }); }
106
107
@memoize
108
get argvResource(): URI { return joinPath(this.userRoamingDataHome, 'argv.json'); }
109
110
@memoize
111
get cacheHome(): URI { return joinPath(this.userRoamingDataHome, 'caches'); }
112
113
@memoize
114
get workspaceStorageHome(): URI { return joinPath(this.userRoamingDataHome, 'workspaceStorage'); }
115
116
@memoize
117
get localHistoryHome(): URI { return joinPath(this.userRoamingDataHome, 'History'); }
118
119
@memoize
120
get stateResource(): URI { return joinPath(this.userRoamingDataHome, 'State', 'storage.json'); }
121
122
/**
123
* In Web every workspace can potentially have scoped user-data
124
* and/or extensions and if Sync state is shared then it can make
125
* Sync error prone - say removing extensions from another workspace.
126
* Hence scope Sync state per workspace. Sync scoped to a workspace
127
* is capable of handling opening same workspace in multiple windows.
128
*/
129
@memoize
130
get userDataSyncHome(): URI { return joinPath(this.userRoamingDataHome, 'sync', this.workspaceId); }
131
132
@memoize
133
get sync(): 'on' | 'off' | undefined { return undefined; }
134
135
@memoize
136
get keyboardLayoutResource(): URI { return joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); }
137
138
@memoize
139
get untitledWorkspacesHome(): URI { return joinPath(this.userRoamingDataHome, 'Workspaces'); }
140
141
@memoize
142
get serviceMachineIdResource(): URI { return joinPath(this.userRoamingDataHome, 'machineid'); }
143
144
@memoize
145
get extHostLogsPath(): URI { return joinPath(this.logsHome, 'exthost'); }
146
147
private extensionHostDebugEnvironment: IExtensionHostDebugEnvironment | undefined = undefined;
148
149
@memoize
150
get debugExtensionHost(): IExtensionHostDebugParams {
151
if (!this.extensionHostDebugEnvironment) {
152
this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();
153
}
154
155
return this.extensionHostDebugEnvironment.params;
156
}
157
158
@memoize
159
get isExtensionDevelopment(): boolean {
160
if (!this.extensionHostDebugEnvironment) {
161
this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();
162
}
163
164
return this.extensionHostDebugEnvironment.isExtensionDevelopment;
165
}
166
167
@memoize
168
get extensionDevelopmentLocationURI(): URI[] | undefined {
169
if (!this.extensionHostDebugEnvironment) {
170
this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();
171
}
172
173
return this.extensionHostDebugEnvironment.extensionDevelopmentLocationURI;
174
}
175
176
@memoize
177
get extensionDevelopmentLocationKind(): ExtensionKind[] | undefined {
178
if (!this.extensionHostDebugEnvironment) {
179
this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();
180
}
181
182
return this.extensionHostDebugEnvironment.extensionDevelopmentKind;
183
}
184
185
@memoize
186
get extensionTestsLocationURI(): URI | undefined {
187
if (!this.extensionHostDebugEnvironment) {
188
this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();
189
}
190
191
return this.extensionHostDebugEnvironment.extensionTestsLocationURI;
192
}
193
194
@memoize
195
get extensionEnabledProposedApi(): string[] | undefined {
196
if (!this.extensionHostDebugEnvironment) {
197
this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();
198
}
199
200
return this.extensionHostDebugEnvironment.extensionEnabledProposedApi;
201
}
202
203
@memoize
204
get debugRenderer(): boolean {
205
if (!this.extensionHostDebugEnvironment) {
206
this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();
207
}
208
209
return this.extensionHostDebugEnvironment.debugRenderer;
210
}
211
212
@memoize
213
get enableSmokeTestDriver() { return this.options.developmentOptions?.enableSmokeTestDriver; }
214
215
@memoize
216
get disableExtensions() { return this.payload?.get('disableExtensions') === 'true'; }
217
218
@memoize
219
get enableExtensions() { return this.options.enabledExtensions; }
220
221
@memoize
222
get webviewExternalEndpoint(): string {
223
const endpoint = this.options.webviewEndpoint
224
|| this.productService.webviewContentExternalBaseUrlTemplate
225
|| 'https://{{uuid}}.vscode-cdn.net/{{quality}}/{{commit}}/out/vs/workbench/contrib/webview/browser/pre/';
226
227
const webviewExternalEndpointCommit = this.payload?.get('webviewExternalEndpointCommit');
228
return endpoint
229
.replace('{{commit}}', webviewExternalEndpointCommit ?? this.productService.commit ?? 'ef65ac1ba57f57f2a3961bfe94aa20481caca4c6')
230
.replace('{{quality}}', (webviewExternalEndpointCommit ? 'insider' : this.productService.quality) ?? 'insider');
231
}
232
233
@memoize
234
get extensionTelemetryLogResource(): URI { return joinPath(this.logsHome, 'extensionTelemetry.log'); }
235
236
@memoize
237
get disableTelemetry(): boolean { return false; }
238
239
@memoize
240
get disableExperiments(): boolean { return false; }
241
242
@memoize
243
get verbose(): boolean { return this.payload?.get('verbose') === 'true'; }
244
245
@memoize
246
get logExtensionHostCommunication(): boolean { return this.payload?.get('logExtensionHostCommunication') === 'true'; }
247
248
@memoize
249
get skipReleaseNotes(): boolean { return this.payload?.get('skipReleaseNotes') === 'true'; }
250
251
@memoize
252
get skipWelcome(): boolean { return this.payload?.get('skipWelcome') === 'true'; }
253
254
@memoize
255
get disableWorkspaceTrust(): boolean { return !this.options.enableWorkspaceTrust; }
256
257
@memoize
258
get profile(): string | undefined { return this.payload?.get('profile'); }
259
260
@memoize
261
get editSessionId(): string | undefined { return this.options.editSessionId; }
262
263
private payload: Map<string, string> | undefined;
264
265
constructor(
266
private readonly workspaceId: string,
267
readonly logsHome: URI,
268
readonly options: IWorkbenchConstructionOptions,
269
private readonly productService: IProductService
270
) {
271
if (options.workspaceProvider && Array.isArray(options.workspaceProvider.payload)) {
272
try {
273
this.payload = new Map(options.workspaceProvider.payload);
274
} catch (error) {
275
onUnexpectedError(error); // possible invalid payload for map
276
}
277
}
278
}
279
280
private resolveExtensionHostDebugEnvironment(): IExtensionHostDebugEnvironment {
281
const extensionHostDebugEnvironment: IExtensionHostDebugEnvironment = {
282
params: {
283
port: null,
284
break: false
285
},
286
debugRenderer: false,
287
isExtensionDevelopment: false,
288
extensionDevelopmentLocationURI: undefined,
289
extensionDevelopmentKind: undefined
290
};
291
292
// Fill in selected extra environmental properties
293
if (this.payload) {
294
for (const [key, value] of this.payload) {
295
switch (key) {
296
case 'extensionDevelopmentPath':
297
if (!extensionHostDebugEnvironment.extensionDevelopmentLocationURI) {
298
extensionHostDebugEnvironment.extensionDevelopmentLocationURI = [];
299
}
300
extensionHostDebugEnvironment.extensionDevelopmentLocationURI.push(URI.parse(value));
301
extensionHostDebugEnvironment.isExtensionDevelopment = true;
302
break;
303
case 'extensionDevelopmentKind':
304
extensionHostDebugEnvironment.extensionDevelopmentKind = [<ExtensionKind>value];
305
break;
306
case 'extensionTestsPath':
307
extensionHostDebugEnvironment.extensionTestsLocationURI = URI.parse(value);
308
break;
309
case 'debugRenderer':
310
extensionHostDebugEnvironment.debugRenderer = value === 'true';
311
break;
312
case 'debugId':
313
extensionHostDebugEnvironment.params.debugId = value;
314
break;
315
case 'inspect-brk-extensions':
316
extensionHostDebugEnvironment.params.port = parseInt(value);
317
extensionHostDebugEnvironment.params.break = true;
318
break;
319
case 'inspect-extensions':
320
extensionHostDebugEnvironment.params.port = parseInt(value);
321
break;
322
case 'enableProposedApi':
323
extensionHostDebugEnvironment.extensionEnabledProposedApi = [];
324
break;
325
}
326
}
327
}
328
329
const developmentOptions = this.options.developmentOptions;
330
if (developmentOptions && !extensionHostDebugEnvironment.isExtensionDevelopment) {
331
if (developmentOptions.extensions?.length) {
332
extensionHostDebugEnvironment.extensionDevelopmentLocationURI = developmentOptions.extensions.map(e => URI.revive(e));
333
extensionHostDebugEnvironment.isExtensionDevelopment = true;
334
}
335
336
if (developmentOptions.extensionTestsPath) {
337
extensionHostDebugEnvironment.extensionTestsLocationURI = URI.revive(developmentOptions.extensionTestsPath);
338
}
339
}
340
341
return extensionHostDebugEnvironment;
342
}
343
344
@memoize
345
get filesToOpenOrCreate(): IPath<ITextEditorOptions>[] | undefined {
346
if (this.payload) {
347
const fileToOpen = this.payload.get('openFile');
348
if (fileToOpen) {
349
const fileUri = URI.parse(fileToOpen);
350
351
// Support: --goto parameter to open on line/col
352
if (this.payload.has('gotoLineMode')) {
353
const pathColumnAware = parseLineAndColumnAware(fileUri.path);
354
355
return [{
356
fileUri: fileUri.with({ path: pathColumnAware.path }),
357
options: {
358
selection: !isUndefined(pathColumnAware.line) ? { startLineNumber: pathColumnAware.line, startColumn: pathColumnAware.column || 1 } : undefined
359
}
360
}];
361
}
362
363
return [{ fileUri }];
364
}
365
}
366
367
return undefined;
368
}
369
370
@memoize
371
get filesToDiff(): IPath[] | undefined {
372
if (this.payload) {
373
const fileToDiffPrimary = this.payload.get('diffFilePrimary');
374
const fileToDiffSecondary = this.payload.get('diffFileSecondary');
375
if (fileToDiffPrimary && fileToDiffSecondary) {
376
return [
377
{ fileUri: URI.parse(fileToDiffSecondary) },
378
{ fileUri: URI.parse(fileToDiffPrimary) }
379
];
380
}
381
}
382
383
return undefined;
384
}
385
386
@memoize
387
get filesToMerge(): IPath[] | undefined {
388
if (this.payload) {
389
const fileToMerge1 = this.payload.get('mergeFile1');
390
const fileToMerge2 = this.payload.get('mergeFile2');
391
const fileToMergeBase = this.payload.get('mergeFileBase');
392
const fileToMergeResult = this.payload.get('mergeFileResult');
393
if (fileToMerge1 && fileToMerge2 && fileToMergeBase && fileToMergeResult) {
394
return [
395
{ fileUri: URI.parse(fileToMerge1) },
396
{ fileUri: URI.parse(fileToMerge2) },
397
{ fileUri: URI.parse(fileToMergeBase) },
398
{ fileUri: URI.parse(fileToMergeResult) }
399
];
400
}
401
}
402
403
return undefined;
404
}
405
}
406
407
interface IExtensionHostDebugEnvironment {
408
params: IExtensionHostDebugParams;
409
debugRenderer: boolean;
410
isExtensionDevelopment: boolean;
411
extensionDevelopmentLocationURI?: URI[];
412
extensionDevelopmentKind?: ExtensionKind[];
413
extensionTestsLocationURI?: URI;
414
extensionEnabledProposedApi?: string[];
415
}
416
417