Path: blob/main/src/vs/workbench/services/environment/browser/environmentService.ts
5242 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?.[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 builtinWorkbenchModesHome(): URI { return joinPath(this.userRoamingDataHome, 'builtinWorkbenchModes'); }142143@memoize144get serviceMachineIdResource(): URI { return joinPath(this.userRoamingDataHome, 'machineid'); }145146@memoize147get extHostLogsPath(): URI { return joinPath(this.logsHome, 'exthost'); }148149private extensionHostDebugEnvironment: IExtensionHostDebugEnvironment | undefined = undefined;150151@memoize152get debugExtensionHost(): IExtensionHostDebugParams {153if (!this.extensionHostDebugEnvironment) {154this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();155}156157return this.extensionHostDebugEnvironment.params;158}159160@memoize161get isExtensionDevelopment(): boolean {162if (!this.extensionHostDebugEnvironment) {163this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();164}165166return this.extensionHostDebugEnvironment.isExtensionDevelopment;167}168169@memoize170get extensionDevelopmentLocationURI(): URI[] | undefined {171if (!this.extensionHostDebugEnvironment) {172this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();173}174175return this.extensionHostDebugEnvironment.extensionDevelopmentLocationURI;176}177178@memoize179get extensionDevelopmentLocationKind(): ExtensionKind[] | undefined {180if (!this.extensionHostDebugEnvironment) {181this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();182}183184return this.extensionHostDebugEnvironment.extensionDevelopmentKind;185}186187@memoize188get extensionTestsLocationURI(): URI | undefined {189if (!this.extensionHostDebugEnvironment) {190this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();191}192193return this.extensionHostDebugEnvironment.extensionTestsLocationURI;194}195196@memoize197get extensionEnabledProposedApi(): string[] | undefined {198if (!this.extensionHostDebugEnvironment) {199this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();200}201202return this.extensionHostDebugEnvironment.extensionEnabledProposedApi;203}204205@memoize206get debugRenderer(): boolean {207if (!this.extensionHostDebugEnvironment) {208this.extensionHostDebugEnvironment = this.resolveExtensionHostDebugEnvironment();209}210211return this.extensionHostDebugEnvironment.debugRenderer;212}213214@memoize215get enableSmokeTestDriver() { return this.options.developmentOptions?.enableSmokeTestDriver; }216217@memoize218get disableExtensions() { return this.payload?.get('disableExtensions') === 'true'; }219220@memoize221get enableExtensions() { return this.options.enabledExtensions; }222223@memoize224get webviewExternalEndpoint(): string {225const endpoint = this.options.webviewEndpoint226|| this.productService.webviewContentExternalBaseUrlTemplate227|| 'https://{{uuid}}.vscode-cdn.net/{{quality}}/{{commit}}/out/vs/workbench/contrib/webview/browser/pre/';228229const webviewExternalEndpointCommit = this.payload?.get('webviewExternalEndpointCommit');230return endpoint231.replace('{{commit}}', webviewExternalEndpointCommit ?? this.productService.commit ?? 'ef65ac1ba57f57f2a3961bfe94aa20481caca4c6')232.replace('{{quality}}', (webviewExternalEndpointCommit ? 'insider' : this.productService.quality) ?? 'insider');233}234235@memoize236get extensionTelemetryLogResource(): URI { return joinPath(this.logsHome, 'extensionTelemetry.log'); }237238@memoize239get disableTelemetry(): boolean { return false; }240241@memoize242get disableExperiments(): boolean { return false; }243244@memoize245get verbose(): boolean { return this.payload?.get('verbose') === 'true'; }246247@memoize248get logExtensionHostCommunication(): boolean { return this.payload?.get('logExtensionHostCommunication') === 'true'; }249250@memoize251get skipReleaseNotes(): boolean { return this.payload?.get('skipReleaseNotes') === 'true'; }252253@memoize254get skipWelcome(): boolean { return this.payload?.get('skipWelcome') === 'true'; }255256@memoize257get disableWorkspaceTrust(): boolean { return !this.options.enableWorkspaceTrust; }258259@memoize260get profile(): string | undefined { return this.payload?.get('profile'); }261262@memoize263get editSessionId(): string | undefined { return this.options.editSessionId; }264265private payload: Map<string, string> | undefined;266267constructor(268private readonly workspaceId: string,269readonly logsHome: URI,270readonly options: IWorkbenchConstructionOptions,271private readonly productService: IProductService272) {273if (options.workspaceProvider && Array.isArray(options.workspaceProvider.payload)) {274try {275this.payload = new Map(options.workspaceProvider.payload);276} catch (error) {277onUnexpectedError(error); // possible invalid payload for map278}279}280}281282private resolveExtensionHostDebugEnvironment(): IExtensionHostDebugEnvironment {283const extensionHostDebugEnvironment: IExtensionHostDebugEnvironment = {284params: {285port: null,286break: false287},288debugRenderer: false,289isExtensionDevelopment: false,290extensionDevelopmentLocationURI: undefined,291extensionDevelopmentKind: undefined292};293294// Fill in selected extra environmental properties295if (this.payload) {296for (const [key, value] of this.payload) {297switch (key) {298case 'extensionDevelopmentPath':299if (!extensionHostDebugEnvironment.extensionDevelopmentLocationURI) {300extensionHostDebugEnvironment.extensionDevelopmentLocationURI = [];301}302extensionHostDebugEnvironment.extensionDevelopmentLocationURI.push(URI.parse(value));303extensionHostDebugEnvironment.isExtensionDevelopment = true;304break;305case 'extensionDevelopmentKind':306extensionHostDebugEnvironment.extensionDevelopmentKind = [<ExtensionKind>value];307break;308case 'extensionTestsPath':309extensionHostDebugEnvironment.extensionTestsLocationURI = URI.parse(value);310break;311case 'debugRenderer':312extensionHostDebugEnvironment.debugRenderer = value === 'true';313break;314case 'debugId':315extensionHostDebugEnvironment.params.debugId = value;316break;317case 'inspect-brk-extensions':318extensionHostDebugEnvironment.params.port = parseInt(value);319extensionHostDebugEnvironment.params.break = true;320break;321case 'inspect-extensions':322extensionHostDebugEnvironment.params.port = parseInt(value);323break;324case 'enableProposedApi':325extensionHostDebugEnvironment.extensionEnabledProposedApi = [];326break;327}328}329}330331const developmentOptions = this.options.developmentOptions;332if (developmentOptions && !extensionHostDebugEnvironment.isExtensionDevelopment) {333if (developmentOptions.extensions?.length) {334extensionHostDebugEnvironment.extensionDevelopmentLocationURI = developmentOptions.extensions.map(e => URI.revive(e));335extensionHostDebugEnvironment.isExtensionDevelopment = true;336}337338if (developmentOptions.extensionTestsPath) {339extensionHostDebugEnvironment.extensionTestsLocationURI = URI.revive(developmentOptions.extensionTestsPath);340}341}342343return extensionHostDebugEnvironment;344}345346@memoize347get filesToOpenOrCreate(): IPath<ITextEditorOptions>[] | undefined {348if (this.payload) {349const fileToOpen = this.payload.get('openFile');350if (fileToOpen) {351const fileUri = URI.parse(fileToOpen);352353// Support: --goto parameter to open on line/col354if (this.payload.has('gotoLineMode')) {355const pathColumnAware = parseLineAndColumnAware(fileUri.path);356357return [{358fileUri: fileUri.with({ path: pathColumnAware.path }),359options: {360selection: !isUndefined(pathColumnAware.line) ? { startLineNumber: pathColumnAware.line, startColumn: pathColumnAware.column || 1 } : undefined361}362}];363}364365return [{ fileUri }];366}367}368369return undefined;370}371372@memoize373get filesToDiff(): IPath[] | undefined {374if (this.payload) {375const fileToDiffPrimary = this.payload.get('diffFilePrimary');376const fileToDiffSecondary = this.payload.get('diffFileSecondary');377if (fileToDiffPrimary && fileToDiffSecondary) {378return [379{ fileUri: URI.parse(fileToDiffSecondary) },380{ fileUri: URI.parse(fileToDiffPrimary) }381];382}383}384385return undefined;386}387388@memoize389get filesToMerge(): IPath[] | undefined {390if (this.payload) {391const fileToMerge1 = this.payload.get('mergeFile1');392const fileToMerge2 = this.payload.get('mergeFile2');393const fileToMergeBase = this.payload.get('mergeFileBase');394const fileToMergeResult = this.payload.get('mergeFileResult');395if (fileToMerge1 && fileToMerge2 && fileToMergeBase && fileToMergeResult) {396return [397{ fileUri: URI.parse(fileToMerge1) },398{ fileUri: URI.parse(fileToMerge2) },399{ fileUri: URI.parse(fileToMergeBase) },400{ fileUri: URI.parse(fileToMergeResult) }401];402}403}404405return undefined;406}407}408409interface IExtensionHostDebugEnvironment {410params: IExtensionHostDebugParams;411debugRenderer: boolean;412isExtensionDevelopment: boolean;413extensionDevelopmentLocationURI?: URI[];414extensionDevelopmentKind?: ExtensionKind[];415extensionTestsLocationURI?: URI;416extensionEnabledProposedApi?: string[];417}418419420