Path: blob/main/src/vs/workbench/services/environment/browser/environmentService.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 { Schemas } from '../../../../base/common/network.js';6import { joinPath } from '../../../../base/common/resources.js';7import { URI } from '../../../../base/common/uri.js';8import { ExtensionKind, IEnvironmentService, IExtensionHostDebugParams } from '../../../../platform/environment/common/environment.js';9import { IPath } from '../../../../platform/window/common/window.js';10import { IWorkbenchEnvironmentService } from '../common/environmentService.js';11import { IWorkbenchConstructionOptions } from '../../../browser/web.api.js';12import { IProductService } from '../../../../platform/product/common/productService.js';13import { memoize } from '../../../../base/common/decorators.js';14import { onUnexpectedError } from '../../../../base/common/errors.js';15import { parseLineAndColumnAware } from '../../../../base/common/extpath.js';16import { LogLevelToString } from '../../../../platform/log/common/log.js';17import { isUndefined } from '../../../../base/common/types.js';18import { refineServiceDecorator } from '../../../../platform/instantiation/common/instantiation.js';19import { ITextEditorOptions } from '../../../../platform/editor/common/editor.js';20import { EXTENSION_IDENTIFIER_WITH_LOG_REGEX } from '../../../../platform/environment/common/environmentService.js';2122export const IBrowserWorkbenchEnvironmentService = refineServiceDecorator<IEnvironmentService, IBrowserWorkbenchEnvironmentService>(IEnvironmentService);2324/**25* A subclass of the `IWorkbenchEnvironmentService` to be used only environments26* where the web API is available (browsers, Electron).27*/28export interface IBrowserWorkbenchEnvironmentService extends IWorkbenchEnvironmentService {2930/**31* Options used to configure the workbench.32*/33readonly options?: IWorkbenchConstructionOptions;3435/**36* Gets whether a resolver extension is expected for the environment.37*/38readonly expectsResolverExtension: boolean;39}4041export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvironmentService {4243declare readonly _serviceBrand: undefined;4445@memoize46get remoteAuthority(): string | undefined { return this.options.remoteAuthority; }4748@memoize49get expectsResolverExtension(): boolean {50return !!this.options.remoteAuthority?.includes('+') && !this.options.webSocketFactory;51}5253@memoize54get isBuilt(): boolean { return !!this.productService.commit; }5556@memoize57get logLevel(): string | undefined {58const logLevelFromPayload = this.payload?.get('logLevel');59if (logLevelFromPayload) {60return logLevelFromPayload.split(',').find(entry => !EXTENSION_IDENTIFIER_WITH_LOG_REGEX.test(entry));61}6263return this.options.developmentOptions?.logLevel !== undefined ? LogLevelToString(this.options.developmentOptions?.logLevel) : undefined;64}6566get extensionLogLevel(): [string, string][] | undefined {67const logLevelFromPayload = this.payload?.get('logLevel');68if (logLevelFromPayload) {69const result: [string, string][] = [];70for (const entry of logLevelFromPayload.split(',')) {71const matches = EXTENSION_IDENTIFIER_WITH_LOG_REGEX.exec(entry);72if (matches && matches[1] && matches[2]) {73result.push([matches[1], matches[2]]);74}75}7677return result.length ? result : undefined;78}7980return this.options.developmentOptions?.extensionLogLevel !== undefined ? this.options.developmentOptions?.extensionLogLevel.map(([extension, logLevel]) => ([extension, LogLevelToString(logLevel)])) : undefined;81}8283get profDurationMarkers(): string[] | undefined {84const profDurationMarkersFromPayload = this.payload?.get('profDurationMarkers');85if (profDurationMarkersFromPayload) {86const result: string[] = [];87for (const entry of profDurationMarkersFromPayload.split(',')) {88result.push(entry);89}9091return result.length === 2 ? result : undefined;92}9394return undefined;95}9697@memoize98get windowLogsPath(): URI { return this.logsHome; }99100@memoize101get logFile(): URI { return joinPath(this.windowLogsPath, 'window.log'); }102103@memoize104get userRoamingDataHome(): URI { return URI.file('/User').with({ scheme: Schemas.vscodeUserData }); }105106@memoize107get argvResource(): URI { return joinPath(this.userRoamingDataHome, 'argv.json'); }108109@memoize110get cacheHome(): URI { return joinPath(this.userRoamingDataHome, 'caches'); }111112@memoize113get workspaceStorageHome(): URI { return joinPath(this.userRoamingDataHome, 'workspaceStorage'); }114115@memoize116get localHistoryHome(): URI { return joinPath(this.userRoamingDataHome, 'History'); }117118@memoize119get stateResource(): URI { return joinPath(this.userRoamingDataHome, 'State', 'storage.json'); }120121/**122* In Web every workspace can potentially have scoped user-data123* and/or extensions and if Sync state is shared then it can make124* Sync error prone - say removing extensions from another workspace.125* Hence scope Sync state per workspace. Sync scoped to a workspace126* is capable of handling opening same workspace in multiple windows.127*/128@memoize129get userDataSyncHome(): URI { return joinPath(this.userRoamingDataHome, 'sync', this.workspaceId); }130131@memoize132get sync(): 'on' | 'off' | undefined { return undefined; }133134@memoize135get keyboardLayoutResource(): URI { return joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); }136137@memoize138get untitledWorkspacesHome(): URI { return joinPath(this.userRoamingDataHome, 'Workspaces'); }139140@memoize141get serviceMachineIdResource(): URI { return joinPath(this.userRoamingDataHome, 'machineid'); }142143@memoize144get extHostLogsPath(): URI { return joinPath(this.logsHome, 'exthost'); }145146private extensionHostDebugEnvironment: IExtensionHostDebugEnvironment | undefined = undefined;147148@memoize149get debugExtensionHost(): IExtensionHostDebugParams {150if (!this.extensionHostDebugEnvironment) {151this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();152}153154return this.extensionHostDebugEnvironment.params;155}156157@memoize158get isExtensionDevelopment(): boolean {159if (!this.extensionHostDebugEnvironment) {160this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();161}162163return this.extensionHostDebugEnvironment.isExtensionDevelopment;164}165166@memoize167get extensionDevelopmentLocationURI(): URI[] | undefined {168if (!this.extensionHostDebugEnvironment) {169this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();170}171172return this.extensionHostDebugEnvironment.extensionDevelopmentLocationURI;173}174175@memoize176get extensionDevelopmentLocationKind(): ExtensionKind[] | undefined {177if (!this.extensionHostDebugEnvironment) {178this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();179}180181return this.extensionHostDebugEnvironment.extensionDevelopmentKind;182}183184@memoize185get extensionTestsLocationURI(): URI | undefined {186if (!this.extensionHostDebugEnvironment) {187this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();188}189190return this.extensionHostDebugEnvironment.extensionTestsLocationURI;191}192193@memoize194get extensionEnabledProposedApi(): string[] | undefined {195if (!this.extensionHostDebugEnvironment) {196this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();197}198199return this.extensionHostDebugEnvironment.extensionEnabledProposedApi;200}201202@memoize203get debugRenderer(): boolean {204if (!this.extensionHostDebugEnvironment) {205this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();206}207208return this.extensionHostDebugEnvironment.debugRenderer;209}210211@memoize212get enableSmokeTestDriver() { return this.options.developmentOptions?.enableSmokeTestDriver; }213214@memoize215get disableExtensions() { return this.payload?.get('disableExtensions') === 'true'; }216217@memoize218get enableExtensions() { return this.options.enabledExtensions; }219220@memoize221get webviewExternalEndpoint(): string {222const endpoint = this.options.webviewEndpoint223|| this.productService.webviewContentExternalBaseUrlTemplate224|| 'https://{{uuid}}.vscode-cdn.net/{{quality}}/{{commit}}/out/vs/workbench/contrib/webview/browser/pre/';225226const webviewExternalEndpointCommit = this.payload?.get('webviewExternalEndpointCommit');227return endpoint228.replace('{{commit}}', webviewExternalEndpointCommit ?? this.productService.commit ?? 'ef65ac1ba57f57f2a3961bfe94aa20481caca4c6')229.replace('{{quality}}', (webviewExternalEndpointCommit ? 'insider' : this.productService.quality) ?? 'insider');230}231232@memoize233get extensionTelemetryLogResource(): URI { return joinPath(this.logsHome, 'extensionTelemetry.log'); }234235@memoize236get disableTelemetry(): boolean { return false; }237238@memoize239get disableExperiments(): boolean { return false; }240241@memoize242get verbose(): boolean { return this.payload?.get('verbose') === 'true'; }243244@memoize245get logExtensionHostCommunication(): boolean { return this.payload?.get('logExtensionHostCommunication') === 'true'; }246247@memoize248get skipReleaseNotes(): boolean { return this.payload?.get('skipReleaseNotes') === 'true'; }249250@memoize251get skipWelcome(): boolean { return this.payload?.get('skipWelcome') === 'true'; }252253@memoize254get disableWorkspaceTrust(): boolean { return !this.options.enableWorkspaceTrust; }255256@memoize257get profile(): string | undefined { return this.payload?.get('profile'); }258259@memoize260get editSessionId(): string | undefined { return this.options.editSessionId; }261262private payload: Map<string, string> | undefined;263264constructor(265private readonly workspaceId: string,266readonly logsHome: URI,267readonly options: IWorkbenchConstructionOptions,268private readonly productService: IProductService269) {270if (options.workspaceProvider && Array.isArray(options.workspaceProvider.payload)) {271try {272this.payload = new Map(options.workspaceProvider.payload);273} catch (error) {274onUnexpectedError(error); // possible invalid payload for map275}276}277}278279private resolveExtensionHostDebugEnvironment(): IExtensionHostDebugEnvironment {280const extensionHostDebugEnvironment: IExtensionHostDebugEnvironment = {281params: {282port: null,283break: false284},285debugRenderer: false,286isExtensionDevelopment: false,287extensionDevelopmentLocationURI: undefined,288extensionDevelopmentKind: undefined289};290291// Fill in selected extra environmental properties292if (this.payload) {293for (const [key, value] of this.payload) {294switch (key) {295case 'extensionDevelopmentPath':296if (!extensionHostDebugEnvironment.extensionDevelopmentLocationURI) {297extensionHostDebugEnvironment.extensionDevelopmentLocationURI = [];298}299extensionHostDebugEnvironment.extensionDevelopmentLocationURI.push(URI.parse(value));300extensionHostDebugEnvironment.isExtensionDevelopment = true;301break;302case 'extensionDevelopmentKind':303extensionHostDebugEnvironment.extensionDevelopmentKind = [<ExtensionKind>value];304break;305case 'extensionTestsPath':306extensionHostDebugEnvironment.extensionTestsLocationURI = URI.parse(value);307break;308case 'debugRenderer':309extensionHostDebugEnvironment.debugRenderer = value === 'true';310break;311case 'debugId':312extensionHostDebugEnvironment.params.debugId = value;313break;314case 'inspect-brk-extensions':315extensionHostDebugEnvironment.params.port = parseInt(value);316extensionHostDebugEnvironment.params.break = true;317break;318case 'inspect-extensions':319extensionHostDebugEnvironment.params.port = parseInt(value);320break;321case 'enableProposedApi':322extensionHostDebugEnvironment.extensionEnabledProposedApi = [];323break;324}325}326}327328const developmentOptions = this.options.developmentOptions;329if (developmentOptions && !extensionHostDebugEnvironment.isExtensionDevelopment) {330if (developmentOptions.extensions?.length) {331extensionHostDebugEnvironment.extensionDevelopmentLocationURI = developmentOptions.extensions.map(e => URI.revive(e));332extensionHostDebugEnvironment.isExtensionDevelopment = true;333}334335if (developmentOptions.extensionTestsPath) {336extensionHostDebugEnvironment.extensionTestsLocationURI = URI.revive(developmentOptions.extensionTestsPath);337}338}339340return extensionHostDebugEnvironment;341}342343@memoize344get filesToOpenOrCreate(): IPath<ITextEditorOptions>[] | undefined {345if (this.payload) {346const fileToOpen = this.payload.get('openFile');347if (fileToOpen) {348const fileUri = URI.parse(fileToOpen);349350// Support: --goto parameter to open on line/col351if (this.payload.has('gotoLineMode')) {352const pathColumnAware = parseLineAndColumnAware(fileUri.path);353354return [{355fileUri: fileUri.with({ path: pathColumnAware.path }),356options: {357selection: !isUndefined(pathColumnAware.line) ? { startLineNumber: pathColumnAware.line, startColumn: pathColumnAware.column || 1 } : undefined358}359}];360}361362return [{ fileUri }];363}364}365366return undefined;367}368369@memoize370get filesToDiff(): IPath[] | undefined {371if (this.payload) {372const fileToDiffPrimary = this.payload.get('diffFilePrimary');373const fileToDiffSecondary = this.payload.get('diffFileSecondary');374if (fileToDiffPrimary && fileToDiffSecondary) {375return [376{ fileUri: URI.parse(fileToDiffSecondary) },377{ fileUri: URI.parse(fileToDiffPrimary) }378];379}380}381382return undefined;383}384385@memoize386get filesToMerge(): IPath[] | undefined {387if (this.payload) {388const fileToMerge1 = this.payload.get('mergeFile1');389const fileToMerge2 = this.payload.get('mergeFile2');390const fileToMergeBase = this.payload.get('mergeFileBase');391const fileToMergeResult = this.payload.get('mergeFileResult');392if (fileToMerge1 && fileToMerge2 && fileToMergeBase && fileToMergeResult) {393return [394{ fileUri: URI.parse(fileToMerge1) },395{ fileUri: URI.parse(fileToMerge2) },396{ fileUri: URI.parse(fileToMergeBase) },397{ fileUri: URI.parse(fileToMergeResult) }398];399}400}401402return undefined;403}404}405406interface IExtensionHostDebugEnvironment {407params: IExtensionHostDebugParams;408debugRenderer: boolean;409isExtensionDevelopment: boolean;410extensionDevelopmentLocationURI?: URI[];411extensionDevelopmentKind?: ExtensionKind[];412extensionTestsLocationURI?: URI;413extensionEnabledProposedApi?: string[];414}415416417