@@ -7,14 +7,17 @@ import * as os from 'os';
77import { FileAccess } from 'vs/base/common/network' ;
88import { getCaseInsensitive } from 'vs/base/common/objects' ;
99import * as path from 'vs/base/common/path' ;
10- import { IProcessEnvironment , isWindows } from 'vs/base/common/platform' ;
10+ import { IProcessEnvironment , isMacintosh , isWindows } from 'vs/base/common/platform' ;
1111import * as process from 'vs/base/common/process' ;
1212import { format } from 'vs/base/common/strings' ;
1313import { isString } from 'vs/base/common/types' ;
1414import * as pfs from 'vs/base/node/pfs' ;
1515import { ILogService } from 'vs/platform/log/common/log' ;
1616import { IProductService } from 'vs/platform/product/common/productService' ;
1717import { IShellLaunchConfig , ITerminalEnvironment , ITerminalProcessOptions } from 'vs/platform/terminal/common/terminal' ;
18+ import { EnvironmentVariableMutatorType } from 'vs/platform/terminal/common/environmentVariable' ;
19+ import { deserializeEnvironmentVariableCollections } from 'vs/platform/terminal/common/environmentVariableShared' ;
20+ import { MergedEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariableCollection' ;
1821
1922export function getWindowsBuildNumber ( ) : number {
2023 const osVersion = ( / ( \d + ) \. ( \d + ) \. ( \d + ) / g) . exec ( os . release ( ) ) ;
@@ -104,7 +107,7 @@ export interface IShellIntegrationConfigInjection {
104107 */
105108export function getShellIntegrationInjection (
106109 shellLaunchConfig : IShellLaunchConfig ,
107- options : Pick < ITerminalProcessOptions , 'shellIntegration' | 'windowsEnableConpty' > ,
110+ options : ITerminalProcessOptions ,
108111 env : ITerminalEnvironment | undefined ,
109112 logService : ILogService ,
110113 productService : IProductService
@@ -151,6 +154,7 @@ export function getShellIntegrationInjection(
151154 newArgs = shellIntegrationArgs . get ( ShellIntegrationExecutable . Bash ) ;
152155 } else if ( areZshBashLoginArgs ( originalArgs ) ) {
153156 envMixin [ 'VSCODE_SHELL_LOGIN' ] = '1' ;
157+ addEnvMixinPathPrefix ( options , envMixin ) ;
154158 newArgs = shellIntegrationArgs . get ( ShellIntegrationExecutable . Bash ) ;
155159 }
156160 if ( ! newArgs ) {
@@ -166,6 +170,7 @@ export function getShellIntegrationInjection(
166170 const oldDataDirs = env ?. XDG_DATA_DIRS ?? '/usr/local/share:/usr/share' ;
167171 const newDataDir = path . join ( appRoot , 'out/vs/workbench/contrib/xdg_data' ) ;
168172 envMixin [ 'XDG_DATA_DIRS' ] = `${ oldDataDirs } :${ newDataDir } ` ;
173+ addEnvMixinPathPrefix ( options , envMixin ) ;
169174 return { newArgs : undefined , envMixin } ;
170175 }
171176 case 'pwsh' : {
@@ -186,6 +191,7 @@ export function getShellIntegrationInjection(
186191 newArgs = shellIntegrationArgs . get ( ShellIntegrationExecutable . Zsh ) ;
187192 } else if ( areZshBashLoginArgs ( originalArgs ) ) {
188193 newArgs = shellIntegrationArgs . get ( ShellIntegrationExecutable . ZshLogin ) ;
194+ addEnvMixinPathPrefix ( options , envMixin ) ;
189195 } else if ( originalArgs === shellIntegrationArgs . get ( ShellIntegrationExecutable . Zsh ) || originalArgs === shellIntegrationArgs . get ( ShellIntegrationExecutable . ZshLogin ) ) {
190196 newArgs = originalArgs ;
191197 }
@@ -230,6 +236,39 @@ export function getShellIntegrationInjection(
230236 return undefined ;
231237}
232238
239+ /**
240+ * On macOS the profile calls path_helper which adds a bunch of standard bin directories to the
241+ * beginning of the PATH. This causes significant problems for the environment variable
242+ * collection API as the custom paths added to the end will now be somewhere in the middle of
243+ * the PATH. To combat this, VSCODE_PATH_PREFIX is used to re-apply any prefix after the profile
244+ * has run. This will cause duplication in the PATH but should fix the issue.
245+ *
246+ * See #99878 for more information.
247+ */
248+ function addEnvMixinPathPrefix ( options : ITerminalProcessOptions , envMixin : IProcessEnvironment ) : void {
249+ if ( isMacintosh && options . environmentVariableCollections ) {
250+ // Deserialize and merge
251+ const deserialized = deserializeEnvironmentVariableCollections ( options . environmentVariableCollections ) ;
252+ const merged = new MergedEnvironmentVariableCollection ( deserialized ) ;
253+
254+ // Get all prepend PATH entries
255+ const pathEntry = merged . map . get ( 'PATH' ) ;
256+ const prependToPath : string [ ] = [ ] ;
257+ if ( pathEntry ) {
258+ for ( const mutator of pathEntry ) {
259+ if ( mutator . type === EnvironmentVariableMutatorType . Prepend ) {
260+ prependToPath . push ( mutator . value ) ;
261+ }
262+ }
263+ }
264+
265+ // Add to the environment mixin to be applied in the shell integration script
266+ if ( prependToPath . length > 0 ) {
267+ envMixin [ 'VSCODE_PATH_PREFIX' ] = prependToPath . join ( '' ) ;
268+ }
269+ }
270+ }
271+
233272enum ShellIntegrationExecutable {
234273 WindowsPwsh = 'windows-pwsh' ,
235274 WindowsPwshLogin = 'windows-pwsh-login' ,
0 commit comments