1+ // Copyright (c) Microsoft Corporation. All rights reserved.
2+ // Licensed under the MIT License.
3+
4+ import * as path from 'path' ;
5+ import * as fsapi from 'fs-extra' ;
6+ import { Disposable , Uri , workspace , GlobalEnvironmentVariableCollection } from 'vscode' ;
7+ import { traceVerbose , traceError } from '../../common/logging' ;
8+ import { getConfiguration , onDidChangeConfiguration } from '../../common/workspace.apis' ;
9+ import { EnvVarManager } from '../execution/envVariableManager' ;
10+
11+ /**
12+ * Manages injection of workspace-specific environment variables into VS Code terminals
13+ * using the GlobalEnvironmentVariableCollection API.
14+ */
15+ export class TerminalEnvVarInjector implements Disposable {
16+ private disposables : Disposable [ ] = [ ] ;
17+
18+ constructor (
19+ private readonly envVarCollection : GlobalEnvironmentVariableCollection ,
20+ private readonly envVarManager : EnvVarManager ,
21+ ) {
22+ this . initialize ( ) ;
23+ }
24+
25+ /**
26+ * Initialize the injector by setting up watchers and injecting initial environment variables.
27+ */
28+ private async initialize ( ) : Promise < void > {
29+ traceVerbose ( 'TerminalEnvVarInjector: Initializing environment variable injection' ) ;
30+
31+ // Listen for configuration changes to python.envFile setting
32+ this . disposables . push (
33+ onDidChangeConfiguration ( ( e ) => {
34+ if ( e . affectsConfiguration ( 'python.envFile' ) ) {
35+ traceVerbose ( 'TerminalEnvVarInjector: python.envFile setting changed, reloading env vars' ) ;
36+ this . updateEnvironmentVariables ( ) . catch ( ( error ) => {
37+ traceError ( 'TerminalEnvVarInjector: Error updating env vars after setting change:' , error ) ;
38+ } ) ;
39+ }
40+ } ) ,
41+ ) ;
42+
43+ // Listen for environment variable changes from the manager
44+ this . disposables . push (
45+ this . envVarManager . onDidChangeEnvironmentVariables ( ( ) => {
46+ traceVerbose ( 'TerminalEnvVarInjector: Environment variables changed, reloading' ) ;
47+ this . updateEnvironmentVariables ( ) . catch ( ( error ) => {
48+ traceError ( 'TerminalEnvVarInjector: Error updating env vars after change event:' , error ) ;
49+ } ) ;
50+ } ) ,
51+ ) ;
52+
53+ // Initial load of environment variables
54+ await this . updateEnvironmentVariables ( ) ;
55+ }
56+
57+ /**
58+ * Update environment variables in the terminal collection.
59+ */
60+ private async updateEnvironmentVariables ( ) : Promise < void > {
61+ try {
62+ // Clear existing environment variables
63+ traceVerbose ( 'TerminalEnvVarInjector: Clearing existing environment variables' ) ;
64+ this . envVarCollection . clear ( ) ;
65+
66+ // Get environment variables for all workspace folders
67+ const workspaceFolders = workspace . workspaceFolders ;
68+ if ( ! workspaceFolders || workspaceFolders . length === 0 ) {
69+ traceVerbose ( 'TerminalEnvVarInjector: No workspace folders found, skipping env var injection' ) ;
70+ return ;
71+ }
72+
73+ // Process environment variables for each workspace folder
74+ for ( const folder of workspaceFolders ) {
75+ await this . injectEnvironmentVariablesForWorkspace ( folder . uri ) ;
76+ }
77+
78+ traceVerbose ( 'TerminalEnvVarInjector: Environment variable injection completed' ) ;
79+ } catch ( error ) {
80+ traceError ( 'TerminalEnvVarInjector: Error updating environment variables:' , error ) ;
81+ }
82+ }
83+
84+ /**
85+ * Inject environment variables for a specific workspace.
86+ */
87+ private async injectEnvironmentVariablesForWorkspace ( workspaceUri : Uri ) : Promise < void > {
88+ try {
89+ traceVerbose ( `TerminalEnvVarInjector: Processing workspace: ${ workspaceUri . fsPath } ` ) ;
90+
91+ // Get environment variables for this workspace
92+ const envVars = await this . envVarManager . getEnvironmentVariables ( workspaceUri ) ;
93+
94+ // Track which .env file is being used for logging
95+ const config = getConfiguration ( 'python' , workspaceUri ) ;
96+ const envFilePath = config . get < string > ( 'envFile' ) ;
97+ const resolvedEnvFilePath = envFilePath ? path . resolve ( envFilePath ) : undefined ;
98+ const defaultEnvFilePath = path . join ( workspaceUri . fsPath , '.env' ) ;
99+
100+ let activeEnvFilePath : string | undefined ;
101+ if ( resolvedEnvFilePath && ( await fsapi . pathExists ( resolvedEnvFilePath ) ) ) {
102+ activeEnvFilePath = resolvedEnvFilePath ;
103+ traceVerbose ( `TerminalEnvVarInjector: Using python.envFile setting: ${ activeEnvFilePath } ` ) ;
104+ } else if ( await fsapi . pathExists ( defaultEnvFilePath ) ) {
105+ activeEnvFilePath = defaultEnvFilePath ;
106+ traceVerbose ( `TerminalEnvVarInjector: Using default .env file: ${ activeEnvFilePath } ` ) ;
107+ } else {
108+ traceVerbose ( `TerminalEnvVarInjector: No .env file found for workspace: ${ workspaceUri . fsPath } ` ) ;
109+ }
110+
111+ // Inject environment variables into the collection
112+ let injectedCount = 0 ;
113+ for ( const [ key , value ] of Object . entries ( envVars ) ) {
114+ if ( value !== undefined && value !== process . env [ key ] ) {
115+ // Only inject if the value is different from the current process environment
116+ this . envVarCollection . replace ( key , value ) ;
117+ injectedCount ++ ;
118+ traceVerbose ( `TerminalEnvVarInjector: Injected ${ key } =${ value } ` ) ;
119+ }
120+ }
121+
122+ if ( injectedCount > 0 ) {
123+ traceVerbose (
124+ `TerminalEnvVarInjector: Injected ${ injectedCount } environment variables for workspace: ${ workspaceUri . fsPath } ` ,
125+ ) ;
126+ } else {
127+ traceVerbose (
128+ `TerminalEnvVarInjector: No environment variables to inject for workspace: ${ workspaceUri . fsPath } ` ,
129+ ) ;
130+ }
131+ } catch ( error ) {
132+ traceError (
133+ `TerminalEnvVarInjector: Error injecting environment variables for workspace ${ workspaceUri . fsPath } :` ,
134+ error ,
135+ ) ;
136+ }
137+ }
138+
139+ /**
140+ * Dispose of the injector and clean up resources.
141+ */
142+ dispose ( ) : void {
143+ traceVerbose ( 'TerminalEnvVarInjector: Disposing' ) ;
144+ this . disposables . forEach ( ( disposable ) => disposable . dispose ( ) ) ;
145+ this . disposables = [ ] ;
146+
147+ // Clear all environment variables from the collection
148+ this . envVarCollection . clear ( ) ;
149+ }
150+ }
0 commit comments