diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index e7fbd010b37062d56e339b97a1021a82d5c1648e..28a4c9b649f278ec3935c59fc8a119cfa498b531 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -439,3 +439,5 @@ export interface ITerminalDimensionsOverride extends Readonly(key: string) => T | undefined; diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 710a3dd1833fe7286e2e72c2c4431150988a7dbd..87218189147194c88ffdb4e1d3fcbc796fa0b224 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -8,6 +8,7 @@ import { withNullAsUndefined } from 'vs/base/common/types'; import { generateUuid } from 'vs/base/common/uuid'; import { getSystemShell, getSystemShellSync } from 'vs/base/node/shell'; import { ILogService } from 'vs/platform/log/common/log'; +import { SafeConfigProvider } from 'vs/platform/terminal/common/terminal'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IShellAndArgsDto } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostConfigProvider, ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; @@ -16,7 +17,7 @@ import { ExtHostDocumentsAndEditors, IExtHostDocumentsAndEditors } from 'vs/work import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { BaseExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/common/extHostTerminalService'; import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; -import { ITerminalConfiguration, ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal'; import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { detectAvailableProfiles } from 'vs/workbench/contrib/terminal/node/terminalProfiles'; import type * as vscode from 'vscode'; @@ -75,14 +76,8 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { } public getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string { - const fetchSetting = (key: string): string | undefined => { - return configProvider - .getConfiguration(key.substr(0, key.lastIndexOf('.'))) - .get(key.substr(key.lastIndexOf('.') + 1)); - }; - return terminalEnvironment.getDefaultShell( - fetchSetting, + this._buildSafeConfigProvider(configProvider), this._defaultShell ?? getSystemShellSync(platform.OS, process.env as platform.IProcessEnvironment), process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'), process.env.windir, @@ -93,13 +88,12 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { } public getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string { - const fetchSetting = (key: string): string | string[] | undefined => { - return configProvider - .getConfiguration(key.substr(0, key.lastIndexOf('.'))) - .get(key.substr(key.lastIndexOf('.') + 1)); - }; - - return terminalEnvironment.getDefaultShellArgs(fetchSetting, useAutomationShell, terminalEnvironment.createVariableResolver(this._lastActiveWorkspace, process.env, this._variableResolver), this._logService); + return terminalEnvironment.getDefaultShellArgs( + this._buildSafeConfigProvider(configProvider), + useAutomationShell, + terminalEnvironment.createVariableResolver(this._lastActiveWorkspace, process.env, this._variableResolver), + this._logService + ); } private _registerListeners(): void { @@ -124,8 +118,8 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { } public async $getAvailableProfiles(configuredProfilesOnly: boolean): Promise { - const config = await (await this._extHostConfiguration.getConfigProvider()).getConfiguration().get('terminal.integrated'); - return detectAvailableProfiles(configuredProfilesOnly, undefined, this._logService, config as ITerminalConfiguration, await this._variableResolverPromise, this._lastActiveWorkspace); + const safeConfigProvider = this._buildSafeConfigProvider(await this._extHostConfiguration.getConfigProvider()); + return detectAvailableProfiles(configuredProfilesOnly, safeConfigProvider, undefined, this._logService, await this._variableResolverPromise, this._lastActiveWorkspace); } public async $getDefaultShellAndArgs(useAutomationShell: boolean): Promise { @@ -135,4 +129,17 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { args: this.getDefaultShellArgs(useAutomationShell, configProvider) }; } + + // TODO: Remove when workspace trust is enabled + private _buildSafeConfigProvider(configProvider: ExtHostConfigProvider): SafeConfigProvider { + const config = configProvider.getConfiguration(); + return (key: string) => { + const isWorkspaceConfigAllowed = config.get('terminal.integrated.allowWorkspaceConfiguration'); + if (isWorkspaceConfigAllowed) { + return config.get(key) as any; + } + const inspected = config.inspect(key); + return inspected?.globalValue || inspected?.defaultValue; + }; + } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index d6abf0bc37083d9da2de6938ba945fd75f37d95d..d3ca7436df211a384536d92eff71458f4e293385 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -16,7 +16,6 @@ import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IProductService } from 'vs/platform/product/common/productService'; import { IRemoteTerminalService, ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { Disposable, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { withNullAsUndefined } from 'vs/base/common/types'; @@ -27,7 +26,7 @@ import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminal import { TerminalRecorder } from 'vs/platform/terminal/common/terminalRecorder'; import { localize } from 'vs/nls'; import { formatMessageForTerminal } from 'vs/workbench/contrib/terminal/common/terminalStrings'; -import { IProcessEnvironment, isMacintosh, isWindows, OperatingSystem, OS } from 'vs/base/common/platform'; +import { IProcessEnvironment, OperatingSystem, OS } from 'vs/base/common/platform'; /** The amount of time to consider terminal errors to be related to the launch */ const LAUNCHING_DURATION = 500; @@ -117,7 +116,6 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce @ILogService private readonly _logService: ILogService, @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, @IConfigurationResolverService private readonly _configurationResolverService: IConfigurationResolverService, - @IConfigurationService private readonly _configurationService: IConfigurationService, @IWorkbenchEnvironmentService private readonly _workbenchEnvironmentService: IWorkbenchEnvironmentService, @IProductService private readonly _productService: IProductService, @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, @@ -335,8 +333,9 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce // Fetch any extension environment additions and apply them private async _setupEnvVariableInfo(variableResolver: terminalEnvironment.VariableResolver | undefined, shellLaunchConfig: IShellLaunchConfig): Promise { - const platformKey = isWindows ? 'windows' : (isMacintosh ? 'osx' : 'linux'); - const envFromConfigValue = this._configurationService.getValue(`terminal.integrated.env.${platformKey}`); + // const platformKey = isWindows ? 'windows' : (isMacintosh ? 'osx' : 'linux'); + // this._configurationService.getValue(`terminal.integrated.env.${platformKey}`); + const envFromConfigValue = this._terminalProfileResolverService.getSafeConfigValue('env', OS) as ITerminalEnvironment | undefined; this._configHelper.showRecommendations(shellLaunchConfig); const baseEnv = await (this._configHelper.config.inheritEnv ? this._terminalProfileResolverService.getShellEnvironment(this.remoteAuthority) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts b/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts index 9cb5a6e617594ab8a0e0e3de5750d44fc6713310..fcedae4ecc8b09f3ddf837f56f56643e02f68e28 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts @@ -123,7 +123,7 @@ export abstract class BaseTerminalProfileResolverService implements ITerminalPro private _getRealDefaultProfile(sync: true, os: OperatingSystem): ITerminalProfile | undefined; private _getRealDefaultProfile(sync: false, os: OperatingSystem): Promise; private _getRealDefaultProfile(sync: boolean, os: OperatingSystem): ITerminalProfile | undefined | Promise { - const defaultProfileName = this._configurationService.getValue(`terminal.integrated.defaultProfile.${this._getOsKey(os)}`); + const defaultProfileName = this.getSafeConfigValue('defaultProfile', os); if (defaultProfileName && typeof defaultProfileName === 'string') { if (sync) { const profiles = this._terminalService.availableProfiles; @@ -138,10 +138,10 @@ export abstract class BaseTerminalProfileResolverService implements ITerminalPro private async _getFallbackDefaultProfile(options: IShellLaunchConfigResolveOptions): Promise { let executable: string; let args: string | string[] | undefined; - const shellSetting = this._configurationService.getValue(`terminal.integrated.shell.${this._getOsKey(options.os)}`); + const shellSetting = this.getSafeConfigValue('shell', options.os); if (this._isValidShell(shellSetting)) { executable = shellSetting; - const shellArgsSetting = this._configurationService.getValue(`terminal.integrated.shellArgs.${this._getOsKey(options.os)}`); + const shellArgsSetting = this.getSafeConfigValue('shellArgs', options.os); if (this._isValidShellArgs(shellArgsSetting, options.os)) { args = shellArgsSetting; } @@ -170,7 +170,7 @@ export abstract class BaseTerminalProfileResolverService implements ITerminalPro } private _getAutomationShellProfile(options: IShellLaunchConfigResolveOptions): ITerminalProfile | undefined { - const automationShell = this._configurationService.getValue(`terminal.integrated.automationShell.${this._getOsKey(options.os)}`); + const automationShell = this.getSafeConfigValue('automationShell', options.os); if (!automationShell || typeof automationShell !== 'string') { return undefined; } @@ -274,6 +274,28 @@ export abstract class BaseTerminalProfileResolverService implements ITerminalPro } return false; } + + // TODO: Remove when workspace trust is enabled + getSafeConfigValue(key: string, os: OperatingSystem): unknown | undefined { + return this.getSafeConfigValueFullKey(`terminal.integrated.${key}.${this._getOsKey(os)}`); + } + getSafeConfigValueFullKey(key: string): unknown | undefined { + const isWorkspaceConfigAllowed = this._configurationService.getValue('terminal.integrated.allowWorkspaceConfiguration'); + if (isWorkspaceConfigAllowed) { + return this._configurationService.getValue(key); + } else { + const config = this._configurationService.inspect(key); + const value = config.user?.value || config.default?.value; + // Clone if needed to allow extensibility + if (Array.isArray(value)) { + return value.slice(); + } + if (typeof value === 'object') { + return { ...value }; + } + return value; + } + } } export class BrowserTerminalProfileResolverService extends BaseTerminalProfileResolverService { diff --git a/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts b/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts index 49f5c69b10b5034bba4d2d3164e6f477bcd5c1b4..c9e3f95d139dbe6d515d6578481eb310a4cb35d6 100644 --- a/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts +++ b/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts @@ -19,7 +19,7 @@ import { Schemas } from 'vs/base/common/network'; import { ILabelService } from 'vs/platform/label/common/label'; import { IEnvironmentVariableService, ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { IProcessDataEvent, IShellLaunchConfig, IShellLaunchConfigDto, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalShellType } from 'vs/platform/terminal/common/terminal'; -import { ITerminalConfiguration, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalConfiguration, ITerminalProfileResolverService, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; import { IGetTerminalLayoutInfoArgs, IProcessDetails, IPtyHostProcessReplayEvent, ISetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess'; import { IProcessEnvironment, OperatingSystem } from 'vs/base/common/platform'; @@ -125,6 +125,7 @@ export class RemoteTerminalChannelClient { @IConfigurationResolverService private readonly _resolverService: IConfigurationResolverService, @IEnvironmentVariableService private readonly _environmentVariableService: IEnvironmentVariableService, @IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @ITerminalProfileResolverService private readonly _terminalProfileResolverService: ITerminalProfileResolverService, @ILogService private readonly _logService: ILogService, @IEditorService private readonly _editorService: IEditorService, @ILabelService private readonly _labelService: ILabelService, @@ -140,20 +141,20 @@ export class RemoteTerminalChannelClient { const terminalConfig = this._configurationService.getValue(TERMINAL_CONFIG_SECTION); const configuration: ICompleteTerminalConfiguration = { - 'terminal.integrated.automationShell.windows': this._configurationService.getValue('terminal.integrated.automationShell.windows'), - 'terminal.integrated.automationShell.osx': this._configurationService.getValue('terminal.integrated.automationShell.osx'), - 'terminal.integrated.automationShell.linux': this._configurationService.getValue('terminal.integrated.automationShell.linux'), - 'terminal.integrated.shell.windows': this._configurationService.getValue('terminal.integrated.shell.windows'), - 'terminal.integrated.shell.osx': this._configurationService.getValue('terminal.integrated.shell.osx'), - 'terminal.integrated.shell.linux': this._configurationService.getValue('terminal.integrated.shell.linux'), - 'terminal.integrated.shellArgs.windows': this._configurationService.getValue('terminal.integrated.shellArgs.windows'), - 'terminal.integrated.shellArgs.osx': this._configurationService.getValue('terminal.integrated.shellArgs.osx'), - 'terminal.integrated.shellArgs.linux': this._configurationService.getValue('terminal.integrated.shellArgs.linux'), - 'terminal.integrated.env.windows': this._configurationService.getValue('terminal.integrated.env.windows'), - 'terminal.integrated.env.osx': this._configurationService.getValue('terminal.integrated.env.osx'), - 'terminal.integrated.env.linux': this._configurationService.getValue('terminal.integrated.env.linux'), + 'terminal.integrated.automationShell.windows': this._terminalProfileResolverService.getSafeConfigValueFullKey('terminal.integrated.automationShell.windows') as string, + 'terminal.integrated.automationShell.osx': this._terminalProfileResolverService.getSafeConfigValueFullKey('terminal.integrated.automationShell.osx') as string, + 'terminal.integrated.automationShell.linux': this._terminalProfileResolverService.getSafeConfigValueFullKey('terminal.integrated.automationShell.linux') as string, + 'terminal.integrated.shell.windows': this._terminalProfileResolverService.getSafeConfigValueFullKey('terminal.integrated.shell.windows') as string, + 'terminal.integrated.shell.osx': this._terminalProfileResolverService.getSafeConfigValueFullKey('terminal.integrated.shell.osx') as string, + 'terminal.integrated.shell.linux': this._terminalProfileResolverService.getSafeConfigValueFullKey('terminal.integrated.shell.linux') as string, + 'terminal.integrated.shellArgs.windows': this._terminalProfileResolverService.getSafeConfigValueFullKey('terminal.integrated.shellArgs.windows') as string | string[], + 'terminal.integrated.shellArgs.osx': this._terminalProfileResolverService.getSafeConfigValueFullKey('terminal.integrated.shellArgs.osx') as string | string[], + 'terminal.integrated.shellArgs.linux': this._terminalProfileResolverService.getSafeConfigValueFullKey('terminal.integrated.shellArgs.linux') as string | string[], + 'terminal.integrated.env.windows': this._terminalProfileResolverService.getSafeConfigValueFullKey('terminal.integrated.env.windows') as ITerminalEnvironment, + 'terminal.integrated.env.osx': this._terminalProfileResolverService.getSafeConfigValueFullKey('terminal.integrated.env.osx') as ITerminalEnvironment, + 'terminal.integrated.env.linux': this._terminalProfileResolverService.getSafeConfigValueFullKey('terminal.integrated.env.linux') as ITerminalEnvironment, 'terminal.integrated.inheritEnv': terminalConfig.inheritEnv, - 'terminal.integrated.cwd': terminalConfig.cwd, + 'terminal.integrated.cwd': this._terminalProfileResolverService.getSafeConfigValueFullKey('terminal.integrated.cwd') as string, 'terminal.integrated.detectLocale': terminalConfig.detectLocale }; diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index e8f07ecc0635691711b1c51e73ab1320cb3a0ecf..7825250c4891775860d50cc48b02f58e3ec91f47 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -100,6 +100,10 @@ export interface ITerminalProfileResolverService { getDefaultShell(options: IShellLaunchConfigResolveOptions): Promise; getDefaultShellArgs(options: IShellLaunchConfigResolveOptions): Promise; getShellEnvironment(remoteAuthority: string | undefined): Promise; + + // TODO: Remove when workspace trust is enabled + getSafeConfigValue(key: string, os: OperatingSystem): unknown | undefined; + getSafeConfigValueFullKey(key: string): unknown | undefined; } export interface IShellLaunchConfigResolveOptions { @@ -195,6 +199,7 @@ export interface ITerminalConfiguration { focusMode: 'singleClick' | 'doubleClick'; }, bellDuration: number; + allowWorkspaceConfiguration: boolean; } export const DEFAULT_LOCAL_ECHO_EXCLUDE: ReadonlyArray = ['vim', 'vi', 'nano', 'tmux']; diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index 2accbbcf701bc1d63b557db9db31356f5dbf77f7..c4e381b0563002110d972b342d9b63383e09bd9b 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -60,8 +60,6 @@ export const terminalConfiguration: IConfigurationNode = { }, 'terminal.integrated.automationShell.linux': { restricted: true, - // TODO: Remove when workspace trust is enabled by default - scope: ConfigurationScope.APPLICATION, markdownDescription: localize({ key: 'terminal.integrated.automationShell.linux', comment: ['{0} and {1} are the `shell` and `shellArgs` settings keys'] @@ -71,8 +69,6 @@ export const terminalConfiguration: IConfigurationNode = { }, 'terminal.integrated.automationShell.osx': { restricted: true, - // TODO: Remove when workspace trust is enabled by default - scope: ConfigurationScope.APPLICATION, markdownDescription: localize({ key: 'terminal.integrated.automationShell.osx', comment: ['{0} and {1} are the `shell` and `shellArgs` settings keys'] @@ -82,8 +78,6 @@ export const terminalConfiguration: IConfigurationNode = { }, 'terminal.integrated.automationShell.windows': { restricted: true, - // TODO: Remove when workspace trust is enabled by default - scope: ConfigurationScope.APPLICATION, markdownDescription: localize({ key: 'terminal.integrated.automationShell.windows', comment: ['{0} and {1} are the `shell` and `shellArgs` settings keys'] @@ -93,8 +87,6 @@ export const terminalConfiguration: IConfigurationNode = { }, 'terminal.integrated.shellArgs.linux': { restricted: true, - // TODO: Remove when workspace trust is enabled by default - scope: ConfigurationScope.APPLICATION, markdownDescription: localize('terminal.integrated.shellArgs.linux', "The command line arguments to use when on the Linux terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), type: 'array', items: { @@ -105,8 +97,6 @@ export const terminalConfiguration: IConfigurationNode = { }, 'terminal.integrated.shellArgs.osx': { restricted: true, - // TODO: Remove when workspace trust is enabled by default - scope: ConfigurationScope.APPLICATION, markdownDescription: localize('terminal.integrated.shellArgs.osx', "The command line arguments to use when on the macOS terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), type: 'array', items: { @@ -120,8 +110,6 @@ export const terminalConfiguration: IConfigurationNode = { }, 'terminal.integrated.shellArgs.windows': { restricted: true, - // TODO: Remove when workspace trust is enabled by default - scope: ConfigurationScope.APPLICATION, markdownDescription: localize('terminal.integrated.shellArgs.windows', "The command line arguments to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), 'anyOf': [ { @@ -141,8 +129,6 @@ export const terminalConfiguration: IConfigurationNode = { }, 'terminal.integrated.profiles.windows': { restricted: true, - // TODO: Remove when workspace trust is enabled by default - scope: ConfigurationScope.APPLICATION, markdownDescription: localize( { key: 'terminal.integrated.profiles.windows', @@ -203,8 +189,6 @@ export const terminalConfiguration: IConfigurationNode = { }, 'terminal.integrated.profiles.osx': { restricted: true, - // TODO: Remove when workspace trust is enabled by default - scope: ConfigurationScope.APPLICATION, markdownDescription: localize( { key: 'terminal.integrated.profile.osx', @@ -242,8 +226,6 @@ export const terminalConfiguration: IConfigurationNode = { }, 'terminal.integrated.profiles.linux': { restricted: true, - // TODO: Remove when workspace trust is enabled by default - scope: ConfigurationScope.APPLICATION, markdownDescription: localize( { key: 'terminal.integrated.profile.linux', @@ -511,6 +493,7 @@ export const terminalConfiguration: IConfigurationNode = { description: localize('terminal.integrated.rightClickBehavior', "Controls how terminal reacts to right click.") }, 'terminal.integrated.cwd': { + restricted: true, description: localize('terminal.integrated.cwd', "An explicit start path where the terminal will be launched, this is used as the current working directory (cwd) for the shell process. This may be particularly useful in workspace settings if the root directory is not a convenient cwd."), type: 'string', default: undefined @@ -550,8 +533,6 @@ export const terminalConfiguration: IConfigurationNode = { }, 'terminal.integrated.env.osx': { restricted: true, - // TODO: Remove when workspace trust is enabled by default - scope: ConfigurationScope.APPLICATION, markdownDescription: localize('terminal.integrated.env.osx', "Object with environment variables that will be added to the VS Code process to be used by the terminal on macOS. Set to `null` to delete the environment variable."), type: 'object', additionalProperties: { @@ -561,8 +542,6 @@ export const terminalConfiguration: IConfigurationNode = { }, 'terminal.integrated.env.linux': { restricted: true, - // TODO: Remove when workspace trust is enabled by default - scope: ConfigurationScope.APPLICATION, markdownDescription: localize('terminal.integrated.env.linux', "Object with environment variables that will be added to the VS Code process to be used by the terminal on Linux. Set to `null` to delete the environment variable."), type: 'object', additionalProperties: { @@ -572,8 +551,6 @@ export const terminalConfiguration: IConfigurationNode = { }, 'terminal.integrated.env.windows': { restricted: true, - // TODO: Remove when workspace trust is enabled by default - scope: ConfigurationScope.APPLICATION, markdownDescription: localize('terminal.integrated.env.windows', "Object with environment variables that will be added to the VS Code process to be used by the terminal on Windows. Set to `null` to delete the environment variable."), type: 'object', additionalProperties: { @@ -683,6 +660,12 @@ export const terminalConfiguration: IConfigurationNode = { description: localize('terminal.integrated.enablePersistentSessions', "Persist terminal sessions for the workspace across window reloads."), type: 'boolean', default: true + }, + 'terminal.integrated.allowWorkspaceConfiguration': { + scope: ConfigurationScope.APPLICATION, + description: localize('terminal.integrated.allowWorkspaceConfiguration', "Allows shell and profile settings to be pick up from a workspace."), + type: 'boolean', + default: false } } }; @@ -696,8 +679,6 @@ function getTerminalShellConfigurationStub(linux: string, osx: string, windows: properties: { 'terminal.integrated.shell.linux': { restricted: true, - // TODO: Remove when workspace trust is enabled by default - scope: ConfigurationScope.APPLICATION, markdownDescription: linux, type: ['string', 'null'], default: null, @@ -705,8 +686,6 @@ function getTerminalShellConfigurationStub(linux: string, osx: string, windows: }, 'terminal.integrated.shell.osx': { restricted: true, - // TODO: Remove when workspace trust is enabled by default - scope: ConfigurationScope.APPLICATION, markdownDescription: osx, type: ['string', 'null'], default: null, @@ -714,8 +693,6 @@ function getTerminalShellConfigurationStub(linux: string, osx: string, windows: }, 'terminal.integrated.shell.windows': { restricted: true, - // TODO: Remove when workspace trust is enabled by default - scope: ConfigurationScope.APPLICATION, markdownDescription: windows, type: ['string', 'null'], default: null, diff --git a/src/vs/workbench/contrib/terminal/node/terminalProfiles.ts b/src/vs/workbench/contrib/terminal/node/terminalProfiles.ts index 5bec5055648f7fac96799557c1bbc99f545a8857..305d5aaf67c526e829b5ad3823fb9efff72ff05e 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalProfiles.ts +++ b/src/vs/workbench/contrib/terminal/node/terminalProfiles.ts @@ -7,27 +7,43 @@ import * as fs from 'fs'; import { normalize, basename, delimiter } from 'vs/base/common/path'; import { enumeratePowerShellInstallations } from 'vs/base/node/powershell'; import { findExecutable, getWindowsBuildNumber } from 'vs/platform/terminal/node/terminalEnvironment'; -import { ITerminalConfiguration, ITerminalProfile, ITerminalProfileObject, ProfileSource } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProfile, ITerminalProfileObject, ProfileSource } from 'vs/workbench/contrib/terminal/common/terminal'; import * as cp from 'child_process'; import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ILogService } from 'vs/platform/log/common/log'; import * as pfs from 'vs/base/node/pfs'; -import { ITerminalEnvironment } from 'vs/platform/terminal/common/terminal'; +import { ITerminalEnvironment, SafeConfigProvider } from 'vs/platform/terminal/common/terminal'; import { Codicon } from 'vs/base/common/codicons'; import { isMacintosh, isWindows } from 'vs/base/common/platform'; let profileSources: Map | undefined; -export function detectAvailableProfiles(configuredProfilesOnly: boolean, fsProvider?: IFsProvider, logService?: ILogService, config?: ITerminalConfiguration, variableResolver?: ExtHostVariableResolverService, workspaceFolder?: IWorkspaceFolder, testPaths?: string[]): Promise { +export function detectAvailableProfiles(configuredProfilesOnly: boolean, safeConfigProvider: SafeConfigProvider, fsProvider?: IFsProvider, logService?: ILogService, variableResolver?: ExtHostVariableResolverService, workspaceFolder?: IWorkspaceFolder, testPaths?: string[]): Promise { fsProvider = fsProvider || { existsFile: pfs.SymlinkSupport.existsFile, readFile: fs.promises.readFile }; if (isWindows) { - return detectAvailableWindowsProfiles(configuredProfilesOnly, fsProvider, logService, config?.useWslProfiles, config?.profiles.windows, variableResolver, workspaceFolder); + return detectAvailableWindowsProfiles( + configuredProfilesOnly, + fsProvider, + logService, + safeConfigProvider('terminal.integrated.useWslProfiles') || true, + safeConfigProvider('terminal.integrated.profiles.windows'), + variableResolver, + workspaceFolder + ); } - return detectAvailableUnixProfiles(fsProvider, logService, configuredProfilesOnly, isMacintosh ? config?.profiles.osx : config?.profiles.linux, testPaths, variableResolver, workspaceFolder); + return detectAvailableUnixProfiles( + fsProvider, + logService, + configuredProfilesOnly, + safeConfigProvider(`terminal.integrated.profiles.${isMacintosh ? 'osx' : 'linux'}`), + testPaths, + variableResolver, + workspaceFolder + ); } async function detectAvailableWindowsProfiles(configuredProfilesOnly: boolean, fsProvider: IFsProvider, logService?: ILogService, useWslProfiles?: boolean, configProfiles?: { [key: string]: ITerminalProfileObject }, variableResolver?: ExtHostVariableResolverService, workspaceFolder?: IWorkspaceFolder): Promise { diff --git a/src/vs/workbench/contrib/terminal/test/node/terminalProfiles.test.ts b/src/vs/workbench/contrib/terminal/test/node/terminalProfiles.test.ts index d6d34bf808d45c34a49d88fd44b20c23549d18f9..c96b8295bc7ac589db42a9db577924c474ecff09 100644 --- a/src/vs/workbench/contrib/terminal/test/node/terminalProfiles.test.ts +++ b/src/vs/workbench/contrib/terminal/test/node/terminalProfiles.test.ts @@ -5,6 +5,7 @@ import { deepStrictEqual, fail, ok, strictEqual } from 'assert'; import { isWindows } from 'vs/base/common/platform'; +import { SafeConfigProvider } from 'vs/platform/terminal/common/terminal'; import { ITerminalConfiguration, ITerminalProfile, ITerminalProfiles, ProfileSource } from 'vs/workbench/contrib/terminal/common/terminal'; import { detectAvailableProfiles, IFsProvider } from 'vs/workbench/contrib/terminal/node/terminalProfiles'; @@ -25,6 +26,18 @@ function profilesEqual(actualProfiles: ITerminalProfile[], expectedProfiles: ITe } } +function buildTestSafeConfigProvider(config: ITestTerminalConfig): SafeConfigProvider { + return (key: string) => { + switch (key) { + case 'terminal.integrated.profiles.linux': return config.profiles.linux as any; + case 'terminal.integrated.profiles.osx': return config.profiles.osx as any; + case 'terminal.integrated.profiles.windows': return config.profiles.windows as any; + case 'terminal.integrated.useWslProfiles': return config.useWslProfiles; + default: throw new Error('Unexpected config key'); + } + }; +} + suite('Workbench - TerminalProfiles', () => { suite('detectAvailableProfiles', () => { if (isWindows) { @@ -42,7 +55,7 @@ suite('Workbench - TerminalProfiles', () => { }, useWslProfiles: false }; - const profiles = await detectAvailableProfiles(true, fsProvider, undefined, config as ITerminalConfiguration, undefined, undefined); + const profiles = await detectAvailableProfiles(true, buildTestSafeConfigProvider(config), fsProvider, undefined, undefined, undefined); const expected = [ { profileName: 'Git Bash', path: 'C:\\Program Files\\Git\\bin\\bash.exe', args: ['--login'] } ]; @@ -62,7 +75,7 @@ suite('Workbench - TerminalProfiles', () => { }, useWslProfiles: false }; - const profiles = await detectAvailableProfiles(true, fsProvider, undefined, config as ITerminalConfiguration, undefined, undefined); + const profiles = await detectAvailableProfiles(true, buildTestSafeConfigProvider(config), fsProvider, undefined, undefined, undefined); const expected = [ { profileName: 'PowerShell NoProfile', path: 'C:\\Program Files\\PowerShell\\7\\pwsh.exe', overrideName: true, args: ['-NoProfile'] } ]; @@ -82,7 +95,7 @@ suite('Workbench - TerminalProfiles', () => { }, useWslProfiles: false }; - const profiles = await detectAvailableProfiles(true, fsProvider, undefined, config as ITerminalConfiguration, undefined, undefined); + const profiles = await detectAvailableProfiles(true, buildTestSafeConfigProvider(config), fsProvider, undefined, undefined, undefined); const expected = [{ profileName: 'Git Bash', path: 'C:\\Program Files\\Git\\bin\\bash.exe', args: [], isAutoDetected: undefined, overrideName: undefined }]; profilesEqual(profiles, expected); }); @@ -104,7 +117,7 @@ suite('Workbench - TerminalProfiles', () => { 'C:\\Sysnative\\WindowsPowerShell\\v1.0\\powershell.exe', 'C:\\System32\\WindowsPowerShell\\v1.0\\powershell.exe' ]); - const profiles = await detectAvailableProfiles(true, fsProvider, undefined, pwshSourceConfig, undefined, undefined); + const profiles = await detectAvailableProfiles(true, buildTestSafeConfigProvider(pwshSourceConfig), fsProvider, undefined, undefined, undefined); const expected = [ { profileName: 'PowerShell', path: 'C:\\Program Files\\PowerShell\\7\\pwsh.exe' } ]; @@ -117,7 +130,7 @@ suite('Workbench - TerminalProfiles', () => { 'C:\\Sysnative\\WindowsPowerShell\\v1.0\\powershell.exe', 'C:\\System32\\WindowsPowerShell\\v1.0\\powershell.exe' ]); - const profiles = await detectAvailableProfiles(true, fsProvider, undefined, pwshSourceConfig, undefined, undefined); + const profiles = await detectAvailableProfiles(true, buildTestSafeConfigProvider(pwshSourceConfig), fsProvider, undefined, undefined, undefined); const expected = [ { profileName: 'PowerShell', path: 'C:\\Program Files\\PowerShell\\7\\pwsh.exe' } ]; @@ -128,7 +141,7 @@ suite('Workbench - TerminalProfiles', () => { 'C:\\Windows\\Sysnative\\WindowsPowerShell\\v1.0\\powershell.exe', 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe' ]); - const profiles = await detectAvailableProfiles(true, fsProvider, undefined, pwshSourceConfig, undefined, undefined); + const profiles = await detectAvailableProfiles(true, buildTestSafeConfigProvider(pwshSourceConfig), fsProvider, undefined, undefined, undefined); strictEqual(profiles.length, 1); strictEqual(profiles[0].profileName, 'PowerShell'); }); @@ -172,7 +185,7 @@ suite('Workbench - TerminalProfiles', () => { '/bin/fakeshell1', '/bin/fakeshell3' ]); - const profiles = await detectAvailableProfiles(true, fsProvider, undefined, absoluteConfig, undefined, undefined); + const profiles = await detectAvailableProfiles(true, buildTestSafeConfigProvider(absoluteConfig), fsProvider, undefined, undefined, undefined); const expected: ITerminalProfile[] = [ { profileName: 'fakeshell1', path: '/bin/fakeshell1' }, { profileName: 'fakeshell3', path: '/bin/fakeshell3' } @@ -184,7 +197,7 @@ suite('Workbench - TerminalProfiles', () => { '/bin/fakeshell1', '/bin/fakeshell3' ], '/bin/fakeshell1\n/bin/fakeshell3'); - const profiles = await detectAvailableProfiles(false, fsProvider, undefined, onPathConfig, undefined, undefined); + const profiles = await detectAvailableProfiles(false, buildTestSafeConfigProvider(onPathConfig), fsProvider, undefined, undefined, undefined); const expected: ITerminalProfile[] = [ { profileName: 'fakeshell1', path: 'fakeshell1' }, { profileName: 'fakeshell3', path: 'fakeshell3' } @@ -196,7 +209,7 @@ suite('Workbench - TerminalProfiles', () => { const fsProvider = createFsProvider([ '/bin/fakeshell1' ], '/bin/fakeshell1\n/bin/fakeshell3'); - const profiles = await detectAvailableProfiles(false, fsProvider, undefined, onPathConfig, undefined, undefined); + const profiles = await detectAvailableProfiles(false, buildTestSafeConfigProvider(onPathConfig), fsProvider, undefined, undefined, undefined); const expected: ITerminalProfile[] = [ { profileName: 'fakeshell1', path: 'fakeshell1' } ]; diff --git a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts index ae741b3f5554fee6c71cd3c909831377ffbf663e..fc7dbe32bfb34973bc154cc2a1ce50452c1d9b5b 100644 --- a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts +++ b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts @@ -330,6 +330,8 @@ class SimpleTerminalProfileResolverService implements ITerminalProfileResolverSe getDefaultShell(options: IShellLaunchConfigResolveOptions): Promise { throw new Error('Method not implemented.'); } getDefaultShellArgs(options: IShellLaunchConfigResolveOptions): Promise { throw new Error('Method not implemented.'); } getShellEnvironment(remoteAuthority: string | undefined): Promise { throw new Error('Method not implemented.'); } + getSafeConfigValue(key: string, os: OperatingSystem): unknown | undefined { throw new Error('Method not implemented.'); } + getSafeConfigValueFullKey(key: string): unknown | undefined { throw new Error('Method not implemented.'); } } registerSingleton(ITerminalProfileResolverService, SimpleTerminalProfileResolverService); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index f26fa23e99ec0e1d0015d24916d63418402a6b67..91a7205a7cab1a0ef7fe2eb380537396a6da1509 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -1570,6 +1570,8 @@ export class TestTerminalProfileResolverService implements ITerminalProfileResol async getDefaultShell(options: IShellLaunchConfigResolveOptions): Promise { return '/default'; } async getDefaultShellArgs(options: IShellLaunchConfigResolveOptions): Promise { return []; } async getShellEnvironment(): Promise { return process.env; } + getSafeConfigValue(key: string, os: OperatingSystem): unknown | undefined { return undefined; } + getSafeConfigValueFullKey(key: string): unknown | undefined { return undefined; } } export class TestLocalTerminalService implements ILocalTerminalService {