diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index e7fbd010b37..28a4c9b649f 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 710a3dd1833..87218189147 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 d6abf0bc370..d3ca7436df2 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 fe33a5dbdb2..3901d59aecc 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts @@ -116,7 +116,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; @@ -131,10 +131,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; } @@ -163,7 +163,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; } @@ -267,6 +267,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 49f5c69b10b..c9e3f95d139 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 1ed9571f758..8e0065d61e9 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 f69a3bf3aa3..8f1d6f43a25 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': { requireTrust: 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': { requireTrust: 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': { requireTrust: 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': { requireTrust: 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': { requireTrust: 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': { requireTrust: 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': { requireTrust: 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': { requireTrust: 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': { requireTrust: true, - // TODO: Remove when workspace trust is enabled by default - scope: ConfigurationScope.APPLICATION, markdownDescription: localize( { key: 'terminal.integrated.profile.linux', @@ -492,6 +474,7 @@ export const terminalConfiguration: IConfigurationNode = { description: localize('terminal.integrated.rightClickBehavior', "Controls how terminal reacts to right click.") }, 'terminal.integrated.cwd': { + requireTrust: 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 @@ -531,8 +514,6 @@ export const terminalConfiguration: IConfigurationNode = { }, 'terminal.integrated.env.osx': { requireTrust: 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: { @@ -542,8 +523,6 @@ export const terminalConfiguration: IConfigurationNode = { }, 'terminal.integrated.env.linux': { requireTrust: 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: { @@ -553,8 +532,6 @@ export const terminalConfiguration: IConfigurationNode = { }, 'terminal.integrated.env.windows': { requireTrust: 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: { @@ -664,6 +641,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 } } }; @@ -677,8 +660,6 @@ function getTerminalShellConfigurationStub(linux: string, osx: string, windows: properties: { 'terminal.integrated.shell.linux': { requireTrust: true, - // TODO: Remove when workspace trust is enabled by default - scope: ConfigurationScope.APPLICATION, markdownDescription: linux, type: ['string', 'null'], default: null, @@ -686,8 +667,6 @@ function getTerminalShellConfigurationStub(linux: string, osx: string, windows: }, 'terminal.integrated.shell.osx': { requireTrust: true, - // TODO: Remove when workspace trust is enabled by default - scope: ConfigurationScope.APPLICATION, markdownDescription: osx, type: ['string', 'null'], default: null, @@ -695,8 +674,6 @@ function getTerminalShellConfigurationStub(linux: string, osx: string, windows: }, 'terminal.integrated.shell.windows': { requireTrust: 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 5bec5055648..305d5aaf67c 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 d6d34bf808d..c96b8295bc7 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' } ];