Apply ext env changes to new terminals

This commit is contained in:
Daniel Imms
2020-03-25 12:45:37 -07:00
parent 556550a29a
commit e775d54e2b
4 changed files with 75 additions and 18 deletions
@@ -48,13 +48,15 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
this._updateVariableResolver();
this._registerListeners();
const c = this.getEnvironmentVariableCollection({ identifier: { value: 'test' } } as any, true);
setTimeout(() => {
this._logService.info('get and set');
const c = this.getEnvironmentVariableCollection({ identifier: { value: 'test' } } as any, true);
c.prepend('a', 'b');
c.replace('c', 'd');
c.append('e', 'f');
}, 2000);
setTimeout(() => {
c.prepend('a', 'late addition');
}, 4000);
}
public createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal {
@@ -23,6 +23,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { Disposable } from 'vs/base/common/lifecycle';
import { withNullAsUndefined } from 'vs/base/common/types';
import { IEnvironmentVariableService, IEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
/** The amount of time to consider terminal errors to be related to the launch */
const LAUNCHING_DURATION = 500;
@@ -59,6 +60,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
private _latency: number = -1;
private _latencyLastMeasured: number = 0;
private _initialCwd: string | undefined;
private _extEnvironmentVariableCollection: IEnvironmentVariableCollection | undefined;
private readonly _onProcessReady = this._register(new Emitter<void>());
public get onProcessReady(): Event<void> { return this._onProcessReady.event; }
@@ -87,7 +89,8 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
@IProductService private readonly _productService: IProductService,
@ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService,
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
@IEnvironmentVariableService private readonly _environmentVariableService: IEnvironmentVariableService
) {
super();
this.ptyProcessReady = new Promise<void>(c => {
@@ -230,6 +233,15 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
const baseEnv = this._configHelper.config.inheritEnv ? processEnv : await this._terminalInstanceService.getMainProcessParentEnv();
const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, lastActiveWorkspace, envFromConfigValue, this._configurationResolverService, isWorkspaceShellAllowed, this._productService.version, this._configHelper.config.detectLocale, baseEnv);
// Fetch any extension environment additions and apply them
this._extEnvironmentVariableCollection = this._environmentVariableService.mergedCollection;
this._environmentVariableService.onDidChangeCollections((newCollection) => {
const newAdditions = this._extEnvironmentVariableCollection!.getNewAdditions(newCollection);
// TODO: React to event
console.log('new env additions!', newAdditions);
});
this._extEnvironmentVariableCollection.applyToProcessEnvironment(env);
const useConpty = this._configHelper.config.windowsEnableConpty && !isScreenReaderModeEnabled;
return this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, useConpty);
}
@@ -4,6 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import { IProcessEnvironment } from 'vs/base/common/platform';
export const IEnvironmentVariableService = createDecorator<IEnvironmentVariableService>('environmentVariableService');
@@ -20,7 +22,17 @@ export interface IEnvironmentVariableMutator {
export interface IEnvironmentVariableCollection {
readonly entries: ReadonlyMap<string, IEnvironmentVariableMutator>;
equals(other: IEnvironmentVariableCollection): boolean;
// TODO: Remove equals?
// equals(other: IEnvironmentVariableCollection): boolean;
/**
* Get's additions when compared to another collection. This only gets additions rather than
* doing a full diff because we can only reliably add entries to an environment, not remove
* them.
*/
getNewAdditions(other: IEnvironmentVariableCollection): ReadonlyMap<string, IEnvironmentVariableMutator> | undefined;
applyToProcessEnvironment(env: IProcessEnvironment): void;
}
/**
@@ -34,6 +46,8 @@ export interface IEnvironmentVariableService {
*/
readonly mergedCollection: IEnvironmentVariableCollection;
onDidChangeCollections: Event<IEnvironmentVariableCollection>;
set(extensionIdentifier: string, collection: IEnvironmentVariableCollection): void;
delete(extensionIdentifier: string): void;
}
@@ -6,6 +6,7 @@
import { IEnvironmentVariableService, IEnvironmentVariableCollection, IEnvironmentVariableMutator, EnvironmentVariableMutatorType } from 'vs/workbench/contrib/terminal/common/environmentVariable';
import { Event, Emitter } from 'vs/base/common/event';
import { debounce } from 'vs/base/common/decorators';
import { IProcessEnvironment } from 'vs/base/common/platform';
export class EnvironmentVariableCollection implements IEnvironmentVariableCollection {
readonly entries: Map<string, IEnvironmentVariableMutator>;
@@ -26,19 +27,47 @@ export class EnvironmentVariableCollection implements IEnvironmentVariableCollec
}
}
// TODO: Implement diff method?
equals(other: IEnvironmentVariableCollection): boolean {
if (this.entries.size !== other.entries.size) {
return false;
}
let result = true;
this.entries.forEach((mutator, variable) => {
const otherMutator = other.entries.get(variable);
if (otherMutator !== mutator) {
result = false;
// TODO: Remove? Does diff do the job?
// equals(other: IEnvironmentVariableCollection): boolean {
// if (this.entries.size !== other.entries.size) {
// return false;
// }
// let result = true;
// this.entries.forEach((mutator, variable) => {
// const otherMutator = other.entries.get(variable);
// if (otherMutator !== mutator) {
// result = false;
// }
// });
// return result;
// }
// TODO: Consider doing a full diff, just marking the environment as stale with no action available?
getNewAdditions(other: IEnvironmentVariableCollection): ReadonlyMap<string, IEnvironmentVariableMutator> | undefined {
const result = new Map<string, IEnvironmentVariableMutator>();
other.entries.forEach((newMutator, variable) => {
const currentMutator = this.entries.get(variable);
if (currentMutator?.type !== newMutator.type || currentMutator.value !== newMutator.value) {
result.set(variable, newMutator);
}
});
return result.size === 0 ? undefined : result;
}
applyToProcessEnvironment(env: IProcessEnvironment): void {
this.entries.forEach((mutator, variable) => {
switch (mutator.type) {
case EnvironmentVariableMutatorType.Append:
env[variable] = (env[variable] || '') + mutator.value;
break;
case EnvironmentVariableMutatorType.Prepend:
env[variable] = mutator.value + (env[variable] || '');
break;
case EnvironmentVariableMutatorType.Replace:
env[variable] = mutator.value;
break;
}
});
return result;
}
}
@@ -52,8 +81,8 @@ export class EnvironmentVariableService implements IEnvironmentVariableService {
private _mergedCollection: IEnvironmentVariableCollection = new EnvironmentVariableCollection();
// TODO: Generate a summary of changes inside the terminal component as it needs to be done per-terminal compared to what it started with
private readonly _onDidChangeCollections = new Emitter<void>();
get onDidChangeCollections(): Event<void> { return this._onDidChangeCollections.event; }
private readonly _onDidChangeCollections = new Emitter<IEnvironmentVariableCollection>();
get onDidChangeCollections(): Event<IEnvironmentVariableCollection> { return this._onDidChangeCollections.event; }
constructor() {
// TODO: Load in persisted collections
@@ -78,7 +107,7 @@ export class EnvironmentVariableService implements IEnvironmentVariableService {
@debounce(1000)
private _notifyCollectionUpdates(): void {
this._onDidChangeCollections.fire();
this._onDidChangeCollections.fire(this._mergedCollection);
}
private _resolveMergedCollection(): IEnvironmentVariableCollection {