Change:
With we are trying to remove from the configuration resolver service. The new way to get the environment is async and requires that the all the configuration resolver methods be async. I have started the adoption of the new async methods in , but there's one use in terminal that looks like it will require more work: Option 1: Convert that use of to use . Option 2: I provide a new method on the configuration resolver service that is sync, but requires you to pass in your own . Which option makes the most sense here?
I think the usage of in must remain sync since it's used as part of the extension API. Is this a problem there since is fine to use? The main purpose of this type was to share code between variable processes.
Sounds like Option 2 is the way to go then. I will add a new method that is sync and lets you pass in your own .
Added: will that let you keep your code reuse?

Discussion:
this._defaultShell ?? getSystemShellSync(platform.OS, process.env as platform.IProcessEnvironment), process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'), process.env.windir, <del> terminalEnvironment.createVariableResolver(this._lastActiveWorkspace, this._variableResolver), </del> <ins> terminalEnvironment.createVariableResolver(this._lastActiveWorkspace, process.env, this._variableResolver), </ins> this._logService, useAutomationShell );

.get<string | string[]>(key.substr(key.lastIndexOf('.') + 1)); }; <del> return terminalEnvironment.getDefaultShellArgs(fetchSetting, useAutomationShell, terminalEnvironment.createVariableResolver(this._lastActiveWorkspace, this._variableResolver), this._logService); </del> <ins> return terminalEnvironment.getDefaultShellArgs(fetchSetting, useAutomationShell, terminalEnvironment.createVariableResolver(this._lastActiveWorkspace, process.env, this._variableResolver), this._logService); </ins> } private _registerListeners(): void {

import { withNullAsUndefined } from 'vs/base/common/types'; import { EnvironmentVariableInfoChangesActive, EnvironmentVariableInfoStale } from 'vs/workbench/contrib/terminal/browser/environmentVariableInfo'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; <del> import { URI } from 'vs/base/common/uri'; </del> import { IEnvironmentVariableInfo, IEnvironmentVariableService, IMergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, FlowControlConstants, TerminalShellType, ILocalTerminalService, IOffProcessTerminalService, ITerminalDimensions } from 'vs/platform/terminal/common/terminal'; import { TerminalRecorder } from 'vs/platform/terminal/common/terminalRecorder';

} const hasRemoteAuthority = !!this.remoteAuthority; <ins> // Create variable resolver const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(); const lastActiveWorkspace = activeWorkspaceRootUri ? withNullAsUndefined(this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined; const variableResolver = terminalEnvironment.createVariableResolver(lastActiveWorkspace, await this._terminalProfileResolverService.getShellEnvironment(this.remoteAuthority), this._configurationResolverService); </ins> // resolvedUserHome is needed here as remote resolvers can launch local terminals before // they're connected to the remote. this.userHome = this._pathService.resolvedUserHome?.fsPath;

this.userHome = remoteEnv.userHome.path; this.os = remoteEnv.os; <del> const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(); </del> // this is a copy of what the merged environment collection is on the remote side <del> await this._setupEnvVariableInfo(activeWorkspaceRootUri, shellLaunchConfig); </del> <ins> await this._setupEnvVariableInfo(variableResolver, shellLaunchConfig); </ins> const shouldPersist = !shellLaunchConfig.isFeatureTerminal && this._configHelper.config.enablePersistentSessions; if (shellLaunchConfig.attachPersistentProcess) {

return undefined; } } else { <del> newProcess = await this._launchLocalProcess(this._localTerminalService, shellLaunchConfig, cols, rows, this.userHome, isScreenReaderModeEnabled); </del> <ins> newProcess = await this._launchLocalProcess(this._localTerminalService, shellLaunchConfig, cols, rows, this.userHome, isScreenReaderModeEnabled, variableResolver); </ins> } if (!this._isDisposed) { this._setupPtyHostListeners(this._localTerminalService);

} // Fetch any extension environment additions and apply them <del> private async _setupEnvVariableInfo(activeWorkspaceRootUri: URI | undefined, shellLaunchConfig: IShellLaunchConfig): Promise<IProcessEnvironment> { </del> <ins> private async _setupEnvVariableInfo(variableResolver: terminalEnvironment.VariableResolver | undefined, shellLaunchConfig: IShellLaunchConfig): Promise<IProcessEnvironment> { </ins> const platformKey = isWindows ? 'windows' : (isMacintosh ? 'osx' : 'linux'); <del> const lastActiveWorkspace = activeWorkspaceRootUri ? withNullAsUndefined(this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined; </del> const envFromConfigValue = this._configurationService.getValue<ITerminalEnvironment | undefined>(`terminal.integrated.env.${platformKey}`); this._configHelper.showRecommendations(shellLaunchConfig); const baseEnv = await (this._configHelper.config.inheritEnv ? this._terminalProfileResolverService.getShellEnvironment(this.remoteAuthority) : this._terminalInstanceService.getMainProcessParentEnv()); <del> const variableResolver = terminalEnvironment.createVariableResolver(lastActiveWorkspace, this._configurationResolverService); </del> const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, envFromConfigValue, variableResolver, this._productService.version, this._configHelper.config.detectLocale, baseEnv); if (!shellLaunchConfig.strictEnv && !shellLaunchConfig.hideFromUser) {

cols: number, rows: number, userHome: string | undefined, <del> isScreenReaderModeEnabled: boolean </del> <ins> isScreenReaderModeEnabled: boolean, variableResolver: terminalEnvironment.VariableResolver | undefined </ins> ): Promise<ITerminalChildProcess> { await this._terminalProfileResolverService.resolveShellLaunchConfig(shellLaunchConfig, { remoteAuthority: undefined,

}); const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file); <del> const lastActiveWorkspace = activeWorkspaceRootUri ? withNullAsUndefined(this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined; </del> const initialCwd = terminalEnvironment.getCwd( shellLaunchConfig, userHome, <del> terminalEnvironment.createVariableResolver(lastActiveWorkspace, this._configurationResolverService), </del> <ins> variableResolver, </ins> activeWorkspaceRootUri, this._configHelper.config.cwd, this._logService ); <del> const env = await this._setupEnvVariableInfo(activeWorkspaceRootUri, shellLaunchConfig); </del> <ins> const env = await this._setupEnvVariableInfo(variableResolver, shellLaunchConfig); </ins> const useConpty = this._configHelper.config.windowsEnableConpty && !isScreenReaderModeEnabled; const shouldPersist = this._configHelper.config.enablePersistentSessions && !shellLaunchConfig.isFeatureTerminal;

export type VariableResolver = (str: string) => string; <del> export function createVariableResolver(lastActiveWorkspace: IWorkspaceFolder | undefined, configurationResolverService: IConfigurationResolverService | undefined): VariableResolver | undefined { </del> <ins> export function createVariableResolver(lastActiveWorkspace: IWorkspaceFolder | undefined, env: IProcessEnvironment, configurationResolverService: IConfigurationResolverService | undefined): VariableResolver | undefined { </ins> if (!configurationResolverService) { return undefined; } <del> return (str) => configurationResolverService.resolve(lastActiveWorkspace, str); </del> <ins> return (str) => configurationResolverService.resolveWithEnvironment(env, lastActiveWorkspace, str); </ins> } /**