mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-11 05:30:03 -05:00
Merge pull request #179323 from microsoft/kartik/terminal
Add proposed API to specific workspace-specific environment variables via EnvironmentVariableCollection
This commit is contained in:
@@ -850,17 +850,17 @@ import { assertNoRpc, poll } from '../utils';
|
||||
collection.prepend('C', '~c2~');
|
||||
|
||||
// Verify get
|
||||
deepStrictEqual(collection.get('A'), { value: '~a2~', type: EnvironmentVariableMutatorType.Replace });
|
||||
deepStrictEqual(collection.get('B'), { value: '~b2~', type: EnvironmentVariableMutatorType.Append });
|
||||
deepStrictEqual(collection.get('C'), { value: '~c2~', type: EnvironmentVariableMutatorType.Prepend });
|
||||
deepStrictEqual(collection.get('A'), { value: '~a2~', type: EnvironmentVariableMutatorType.Replace, scope: undefined });
|
||||
deepStrictEqual(collection.get('B'), { value: '~b2~', type: EnvironmentVariableMutatorType.Append, scope: undefined });
|
||||
deepStrictEqual(collection.get('C'), { value: '~c2~', type: EnvironmentVariableMutatorType.Prepend, scope: undefined });
|
||||
|
||||
// Verify forEach
|
||||
const entries: [string, EnvironmentVariableMutator][] = [];
|
||||
collection.forEach((v, m) => entries.push([v, m]));
|
||||
deepStrictEqual(entries, [
|
||||
['A', { value: '~a2~', type: EnvironmentVariableMutatorType.Replace }],
|
||||
['B', { value: '~b2~', type: EnvironmentVariableMutatorType.Append }],
|
||||
['C', { value: '~c2~', type: EnvironmentVariableMutatorType.Prepend }]
|
||||
['A', { value: '~a2~', type: EnvironmentVariableMutatorType.Replace, scope: undefined }],
|
||||
['B', { value: '~b2~', type: EnvironmentVariableMutatorType.Append, scope: undefined }],
|
||||
['C', { value: '~c2~', type: EnvironmentVariableMutatorType.Prepend, scope: undefined }]
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { IWorkspaceFolderData } from 'vs/platform/workspace/common/workspace';
|
||||
|
||||
export enum EnvironmentVariableMutatorType {
|
||||
Replace = 1,
|
||||
@@ -16,11 +17,17 @@ export enum EnvironmentVariableMutatorType {
|
||||
// // TODO: Do we need a both?
|
||||
// }
|
||||
export interface IEnvironmentVariableMutator {
|
||||
readonly variable: string;
|
||||
readonly value: string;
|
||||
readonly type: EnvironmentVariableMutatorType;
|
||||
readonly scope?: EnvironmentVariableScope;
|
||||
// readonly timing?: EnvironmentVariableMutatorTiming;
|
||||
}
|
||||
|
||||
export type EnvironmentVariableScope = {
|
||||
workspaceFolder?: IWorkspaceFolderData;
|
||||
};
|
||||
|
||||
export interface IEnvironmentVariableCollection {
|
||||
readonly map: ReadonlyMap<string, IEnvironmentVariableMutator>;
|
||||
}
|
||||
@@ -49,18 +56,21 @@ type VariableResolver = (str: string) => Promise<string>;
|
||||
*/
|
||||
export interface IMergedEnvironmentVariableCollection {
|
||||
readonly collections: ReadonlyMap<string, IEnvironmentVariableCollection>;
|
||||
readonly map: ReadonlyMap<string, IExtensionOwnedEnvironmentVariableMutator[]>;
|
||||
|
||||
/**
|
||||
* Gets the variable map for a given scope.
|
||||
* @param scope The scope to get the variable map for. If undefined, the global scope is used.
|
||||
*/
|
||||
getVariableMap(scope: EnvironmentVariableScope | undefined): Map<string, IExtensionOwnedEnvironmentVariableMutator[]>;
|
||||
/**
|
||||
* Applies this collection to a process environment.
|
||||
* @param variableResolver An optional function to use to resolve variables within the
|
||||
* environment values.
|
||||
*/
|
||||
applyToProcessEnvironment(env: IProcessEnvironment, variableResolver?: VariableResolver): Promise<void>;
|
||||
applyToProcessEnvironment(env: IProcessEnvironment, scope: EnvironmentVariableScope | undefined, variableResolver?: VariableResolver): Promise<void>;
|
||||
|
||||
/**
|
||||
* Generates a diff of this collection against another. Returns undefined if the collections are
|
||||
* the same.
|
||||
*/
|
||||
diff(other: IMergedEnvironmentVariableCollection): IMergedEnvironmentVariableCollectionDiff | undefined;
|
||||
diff(other: IMergedEnvironmentVariableCollection, scope: EnvironmentVariableScope | undefined): IMergedEnvironmentVariableCollectionDiff | undefined;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IProcessEnvironment, isWindows } from 'vs/base/common/platform';
|
||||
import { EnvironmentVariableMutatorType, IEnvironmentVariableCollection, IExtensionOwnedEnvironmentVariableMutator, IMergedEnvironmentVariableCollection, IMergedEnvironmentVariableCollectionDiff } from 'vs/platform/terminal/common/environmentVariable';
|
||||
import { EnvironmentVariableMutatorType, EnvironmentVariableScope, IEnvironmentVariableCollection, IExtensionOwnedEnvironmentVariableMutator, IMergedEnvironmentVariableCollection, IMergedEnvironmentVariableCollectionDiff } from 'vs/platform/terminal/common/environmentVariable';
|
||||
|
||||
type VariableResolver = (str: string) => Promise<string>;
|
||||
|
||||
@@ -15,20 +15,21 @@ type VariableResolver = (str: string) => Promise<string>;
|
||||
// ]);
|
||||
|
||||
export class MergedEnvironmentVariableCollection implements IMergedEnvironmentVariableCollection {
|
||||
readonly map: Map<string, IExtensionOwnedEnvironmentVariableMutator[]> = new Map();
|
||||
private readonly map: Map<string, IExtensionOwnedEnvironmentVariableMutator[]> = new Map();
|
||||
|
||||
constructor(
|
||||
readonly collections: ReadonlyMap<string, IEnvironmentVariableCollection>
|
||||
readonly collections: ReadonlyMap<string, IEnvironmentVariableCollection>,
|
||||
) {
|
||||
collections.forEach((collection, extensionIdentifier) => {
|
||||
const it = collection.map.entries();
|
||||
let next = it.next();
|
||||
while (!next.done) {
|
||||
const variable = next.value[0];
|
||||
let entry = this.map.get(variable);
|
||||
const mutator = next.value[1];
|
||||
const key = next.value[0];
|
||||
let entry = this.map.get(key);
|
||||
if (!entry) {
|
||||
entry = [];
|
||||
this.map.set(variable, entry);
|
||||
this.map.set(key, entry);
|
||||
}
|
||||
|
||||
// If the first item in the entry is replace ignore any other entries as they would
|
||||
@@ -38,26 +39,31 @@ export class MergedEnvironmentVariableCollection implements IMergedEnvironmentVa
|
||||
continue;
|
||||
}
|
||||
|
||||
// Mutators get applied in the reverse order than they are created
|
||||
const mutator = next.value[1];
|
||||
entry.unshift({
|
||||
const extensionMutator = {
|
||||
extensionIdentifier,
|
||||
value: mutator.value,
|
||||
type: mutator.type
|
||||
});
|
||||
type: mutator.type,
|
||||
scope: mutator.scope,
|
||||
variable: mutator.variable
|
||||
};
|
||||
if (!extensionMutator.scope) {
|
||||
delete extensionMutator.scope; // Convenient for tests
|
||||
}
|
||||
// Mutators get applied in the reverse order than they are created
|
||||
entry.unshift(extensionMutator);
|
||||
|
||||
next = it.next();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async applyToProcessEnvironment(env: IProcessEnvironment, variableResolver?: VariableResolver): Promise<void> {
|
||||
async applyToProcessEnvironment(env: IProcessEnvironment, scope: EnvironmentVariableScope | undefined, variableResolver?: VariableResolver): Promise<void> {
|
||||
let lowerToActualVariableNames: { [lowerKey: string]: string | undefined } | undefined;
|
||||
if (isWindows) {
|
||||
lowerToActualVariableNames = {};
|
||||
Object.keys(env).forEach(e => lowerToActualVariableNames![e.toLowerCase()] = e);
|
||||
}
|
||||
for (const [variable, mutators] of this.map) {
|
||||
for (const [variable, mutators] of this.getVariableMap(scope)) {
|
||||
const actualVariable = isWindows ? lowerToActualVariableNames![variable.toLowerCase()] || variable : variable;
|
||||
for (const mutator of mutators) {
|
||||
const value = variableResolver ? await variableResolver(mutator.value) : mutator.value;
|
||||
@@ -81,14 +87,14 @@ export class MergedEnvironmentVariableCollection implements IMergedEnvironmentVa
|
||||
}
|
||||
}
|
||||
|
||||
diff(other: IMergedEnvironmentVariableCollection): IMergedEnvironmentVariableCollectionDiff | undefined {
|
||||
diff(other: IMergedEnvironmentVariableCollection, scope: EnvironmentVariableScope | undefined): IMergedEnvironmentVariableCollectionDiff | undefined {
|
||||
const added: Map<string, IExtensionOwnedEnvironmentVariableMutator[]> = new Map();
|
||||
const changed: Map<string, IExtensionOwnedEnvironmentVariableMutator[]> = new Map();
|
||||
const removed: Map<string, IExtensionOwnedEnvironmentVariableMutator[]> = new Map();
|
||||
|
||||
// Find added
|
||||
other.map.forEach((otherMutators, variable) => {
|
||||
const currentMutators = this.map.get(variable);
|
||||
other.getVariableMap(scope).forEach((otherMutators, variable) => {
|
||||
const currentMutators = this.getVariableMap(scope).get(variable);
|
||||
const result = getMissingMutatorsFromArray(otherMutators, currentMutators);
|
||||
if (result) {
|
||||
added.set(variable, result);
|
||||
@@ -96,8 +102,8 @@ export class MergedEnvironmentVariableCollection implements IMergedEnvironmentVa
|
||||
});
|
||||
|
||||
// Find removed
|
||||
this.map.forEach((currentMutators, variable) => {
|
||||
const otherMutators = other.map.get(variable);
|
||||
this.getVariableMap(scope).forEach((currentMutators, variable) => {
|
||||
const otherMutators = other.getVariableMap(scope).get(variable);
|
||||
const result = getMissingMutatorsFromArray(currentMutators, otherMutators);
|
||||
if (result) {
|
||||
removed.set(variable, result);
|
||||
@@ -105,8 +111,8 @@ export class MergedEnvironmentVariableCollection implements IMergedEnvironmentVa
|
||||
});
|
||||
|
||||
// Find changed
|
||||
this.map.forEach((currentMutators, variable) => {
|
||||
const otherMutators = other.map.get(variable);
|
||||
this.getVariableMap(scope).forEach((currentMutators, variable) => {
|
||||
const otherMutators = other.getVariableMap(scope).get(variable);
|
||||
const result = getChangedMutatorsFromArray(currentMutators, otherMutators);
|
||||
if (result) {
|
||||
changed.set(variable, result);
|
||||
@@ -119,6 +125,33 @@ export class MergedEnvironmentVariableCollection implements IMergedEnvironmentVa
|
||||
|
||||
return { added, changed, removed };
|
||||
}
|
||||
|
||||
getVariableMap(scope: EnvironmentVariableScope | undefined): Map<string, IExtensionOwnedEnvironmentVariableMutator[]> {
|
||||
const result = new Map<string, IExtensionOwnedEnvironmentVariableMutator[]>();
|
||||
this.map.forEach((mutators, _key) => {
|
||||
const filteredMutators = mutators.filter(m => filterScope(m, scope));
|
||||
if (filteredMutators.length > 0) {
|
||||
// All of these mutators are for the same variable because they are in the same scope, hence choose anyone to form a key.
|
||||
result.set(filteredMutators[0].variable, filteredMutators);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function filterScope(
|
||||
mutator: IExtensionOwnedEnvironmentVariableMutator,
|
||||
scope: EnvironmentVariableScope | undefined
|
||||
): boolean {
|
||||
if (!mutator.scope) {
|
||||
return true;
|
||||
}
|
||||
// If a mutator is scoped to a workspace folder, only apply it if the workspace
|
||||
// folder matches.
|
||||
if (mutator.scope.workspaceFolder && scope?.workspaceFolder && mutator.scope.workspaceFolder.index === scope.workspaceFolder.index) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getMissingMutatorsFromArray(
|
||||
@@ -162,7 +195,7 @@ function getChangedMutatorsFromArray(
|
||||
const result: IExtensionOwnedEnvironmentVariableMutator[] = [];
|
||||
current.forEach(mutator => {
|
||||
const otherMutator = otherMutatorExtensions.get(mutator.extensionIdentifier);
|
||||
if (otherMutator && (mutator.type !== otherMutator.type || mutator.value !== otherMutator.value)) {
|
||||
if (otherMutator && (mutator.type !== otherMutator.type || mutator.value !== otherMutator.value || mutator.scope?.workspaceFolder?.index !== otherMutator.scope?.workspaceFolder?.index)) {
|
||||
// Return the new result, not the old one
|
||||
result.push(otherMutator);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { IGetTerminalLayoutInfoArgs, IProcessDetails, ISetTerminalLayoutInfoArgs
|
||||
import { ThemeIcon } from 'vs/base/common/themables';
|
||||
import { ISerializableEnvironmentVariableCollections } from 'vs/platform/terminal/common/environmentVariable';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
|
||||
export const terminalTabFocusContextKey = new RawContextKey<boolean>('terminalTabFocusMode', false, true);
|
||||
|
||||
@@ -600,6 +601,7 @@ export interface ITerminalProcessOptions {
|
||||
};
|
||||
windowsEnableConpty: boolean;
|
||||
environmentVariableCollections: ISerializableEnvironmentVariableCollections | undefined;
|
||||
workspaceFolder: IWorkspaceFolder | undefined;
|
||||
}
|
||||
|
||||
export interface ITerminalEnvironment {
|
||||
|
||||
@@ -259,7 +259,7 @@ function addEnvMixinPathPrefix(options: ITerminalProcessOptions, envMixin: IProc
|
||||
const merged = new MergedEnvironmentVariableCollection(deserialized);
|
||||
|
||||
// Get all prepend PATH entries
|
||||
const pathEntry = merged.map.get('PATH');
|
||||
const pathEntry = merged.getVariableMap({ workspaceFolder: options.workspaceFolder }).get('PATH');
|
||||
const prependToPath: string[] = [];
|
||||
if (pathEntry) {
|
||||
for (const mutator of pathEntry) {
|
||||
|
||||
@@ -11,9 +11,9 @@ import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { ITerminalProcessOptions } from 'vs/platform/terminal/common/terminal';
|
||||
import { getShellIntegrationInjection, getWindowsBuildNumber, IShellIntegrationConfigInjection } from 'vs/platform/terminal/node/terminalEnvironment';
|
||||
|
||||
const enabledProcessOptions: ITerminalProcessOptions = { shellIntegration: { enabled: true, suggestEnabled: false }, windowsEnableConpty: true, environmentVariableCollections: undefined };
|
||||
const disabledProcessOptions: ITerminalProcessOptions = { shellIntegration: { enabled: false, suggestEnabled: false }, windowsEnableConpty: true, environmentVariableCollections: undefined };
|
||||
const winptyProcessOptions: ITerminalProcessOptions = { shellIntegration: { enabled: true, suggestEnabled: false }, windowsEnableConpty: false, environmentVariableCollections: undefined };
|
||||
const enabledProcessOptions: ITerminalProcessOptions = { shellIntegration: { enabled: true, suggestEnabled: false }, windowsEnableConpty: true, environmentVariableCollections: undefined, workspaceFolder: undefined };
|
||||
const disabledProcessOptions: ITerminalProcessOptions = { shellIntegration: { enabled: false, suggestEnabled: false }, windowsEnableConpty: true, environmentVariableCollections: undefined, workspaceFolder: undefined };
|
||||
const winptyProcessOptions: ITerminalProcessOptions = { shellIntegration: { enabled: true, suggestEnabled: false }, windowsEnableConpty: false, environmentVariableCollections: undefined, workspaceFolder: undefined };
|
||||
const pwshExe = process.platform === 'win32' ? 'pwsh.exe' : 'pwsh';
|
||||
const repoRoot = process.platform === 'win32' ? process.cwd()[0].toLowerCase() + process.cwd().substring(1) : process.cwd();
|
||||
const logService = new NullLogService();
|
||||
|
||||
@@ -31,6 +31,7 @@ import { IServerEnvironmentService } from 'vs/server/node/serverEnvironmentServi
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
|
||||
class CustomVariableResolver extends AbstractVariableResolverService {
|
||||
constructor(
|
||||
@@ -237,7 +238,8 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel<
|
||||
}
|
||||
const envVariableCollections = new Map<string, IEnvironmentVariableCollection>(entries);
|
||||
const mergedCollection = new MergedEnvironmentVariableCollection(envVariableCollections);
|
||||
await mergedCollection.applyToProcessEnvironment(env, variableResolver);
|
||||
const workspaceFolder = activeWorkspaceFolder ? withNullAsUndefined(activeWorkspaceFolder) : undefined;
|
||||
await mergedCollection.applyToProcessEnvironment(env, { workspaceFolder }, variableResolver);
|
||||
}
|
||||
|
||||
// Fork the process and listen for messages
|
||||
|
||||
@@ -17,7 +17,7 @@ import { NotSupportedError } from 'vs/base/common/errors';
|
||||
import { serializeEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariableShared';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable';
|
||||
import { IEnvironmentVariableMutator, ISerializableEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable';
|
||||
import { ICreateContributedTerminalProfileOptions, IProcessReadyEvent, IShellLaunchConfigDto, ITerminalChildProcess, ITerminalLaunchError, ITerminalProfile, TerminalIcon, TerminalLocation, IProcessProperty, ProcessPropertyType, IProcessPropertyMap } from 'vs/platform/terminal/common/terminal';
|
||||
import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering';
|
||||
import { ThemeColor } from 'vs/base/common/themables';
|
||||
@@ -867,7 +867,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
}
|
||||
|
||||
class EnvironmentVariableCollection implements vscode.EnvironmentVariableCollection {
|
||||
readonly map: Map<string, vscode.EnvironmentVariableMutator> = new Map();
|
||||
readonly map: Map<string, IEnvironmentVariableMutator> = new Map();
|
||||
private _persistent: boolean = true;
|
||||
|
||||
public get persistent(): boolean { return this._persistent; }
|
||||
@@ -889,45 +889,79 @@ class EnvironmentVariableCollection implements vscode.EnvironmentVariableCollect
|
||||
return this.map.size;
|
||||
}
|
||||
|
||||
replace(variable: string, value: string): void {
|
||||
this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Replace });
|
||||
replace(variable: string, value: string, scope?: vscode.EnvironmentVariableScope): void {
|
||||
this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Replace, scope });
|
||||
}
|
||||
|
||||
append(variable: string, value: string): void {
|
||||
this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Append });
|
||||
append(variable: string, value: string, scope?: vscode.EnvironmentVariableScope): void {
|
||||
this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Append, scope });
|
||||
}
|
||||
|
||||
prepend(variable: string, value: string): void {
|
||||
this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Prepend });
|
||||
prepend(variable: string, value: string, scope?: vscode.EnvironmentVariableScope): void {
|
||||
this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Prepend, scope });
|
||||
}
|
||||
|
||||
private _setIfDiffers(variable: string, mutator: vscode.EnvironmentVariableMutator): void {
|
||||
const current = this.map.get(variable);
|
||||
if (!current || current.value !== mutator.value || current.type !== mutator.type) {
|
||||
this.map.set(variable, mutator);
|
||||
if (!mutator.scope) {
|
||||
delete (mutator as any).scope; // Convenient for tests
|
||||
}
|
||||
const key = this.getKey(variable, mutator.scope);
|
||||
const current = this.map.get(key);
|
||||
if (!current || current.value !== mutator.value || current.type !== mutator.type || current.scope?.workspaceFolder?.index !== mutator.scope?.workspaceFolder?.index) {
|
||||
const key = this.getKey(variable, mutator.scope);
|
||||
const value: IEnvironmentVariableMutator = { variable, ...mutator };
|
||||
this.map.set(key, value);
|
||||
this._onDidChangeCollection.fire();
|
||||
}
|
||||
}
|
||||
|
||||
get(variable: string): vscode.EnvironmentVariableMutator | undefined {
|
||||
return this.map.get(variable);
|
||||
get(variable: string, scope?: vscode.EnvironmentVariableScope): vscode.EnvironmentVariableMutator | undefined {
|
||||
const key = this.getKey(variable, scope);
|
||||
const value = this.map.get(key);
|
||||
return value ? convertMutator(value) : undefined;
|
||||
}
|
||||
|
||||
private getKey(variable: string, scope: vscode.EnvironmentVariableScope | undefined) {
|
||||
const workspaceKey = this.getWorkspaceKey(scope?.workspaceFolder);
|
||||
return workspaceKey ? `${variable}:::${workspaceKey}` : variable;
|
||||
}
|
||||
|
||||
private getWorkspaceKey(workspaceFolder: vscode.WorkspaceFolder | undefined): string | undefined {
|
||||
return workspaceFolder ? workspaceFolder.uri.toString() : undefined;
|
||||
}
|
||||
|
||||
forEach(callback: (variable: string, mutator: vscode.EnvironmentVariableMutator, collection: vscode.EnvironmentVariableCollection) => any, thisArg?: any): void {
|
||||
this.map.forEach((value, key) => callback.call(thisArg, key, value, this));
|
||||
this.map.forEach((value, _) => callback.call(thisArg, value.variable, convertMutator(value), this));
|
||||
}
|
||||
|
||||
[Symbol.iterator](): IterableIterator<[variable: string, mutator: vscode.EnvironmentVariableMutator]> {
|
||||
return this.map.entries();
|
||||
const map: Map<string, vscode.EnvironmentVariableMutator> = new Map();
|
||||
this.map.forEach((mutator, _key) => {
|
||||
if (mutator.scope) {
|
||||
// Scoped mutators are not supported via this iterator, as it returns variable as the key which is supposed to be unique.
|
||||
return;
|
||||
}
|
||||
map.set(mutator.variable, convertMutator(mutator));
|
||||
});
|
||||
return map.entries();
|
||||
}
|
||||
|
||||
delete(variable: string): void {
|
||||
this.map.delete(variable);
|
||||
delete(variable: string, scope?: vscode.EnvironmentVariableScope): void {
|
||||
const key = this.getKey(variable, scope);
|
||||
this.map.delete(key);
|
||||
this._onDidChangeCollection.fire();
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.map.clear();
|
||||
clear(scope?: vscode.EnvironmentVariableScope): void {
|
||||
if (scope?.workspaceFolder) {
|
||||
for (const [key, mutator] of this.map) {
|
||||
if (mutator.scope?.workspaceFolder?.index === scope.workspaceFolder.index) {
|
||||
this.map.delete(key);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.map.clear();
|
||||
}
|
||||
this._onDidChangeCollection.fire();
|
||||
}
|
||||
}
|
||||
@@ -966,3 +1000,10 @@ function asTerminalIcon(iconPath?: vscode.Uri | { light: vscode.Uri; dark: vscod
|
||||
function asTerminalColor(color?: vscode.ThemeColor): ThemeColor | undefined {
|
||||
return ThemeColor.isThemeColor(color) ? color as ThemeColor : undefined;
|
||||
}
|
||||
|
||||
function convertMutator(mutator: IEnvironmentVariableMutator): vscode.EnvironmentVariableMutator {
|
||||
const newMutator = { ...mutator };
|
||||
newMutator.scope = newMutator.scope ?? undefined;
|
||||
delete (newMutator as any).variable;
|
||||
return newMutator as vscode.EnvironmentVariableMutator;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { ITerminalStatus, ITerminalStatusHoverAction, TerminalCommandId } from '
|
||||
import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { IExtensionOwnedEnvironmentVariableMutator, IMergedEnvironmentVariableCollection, IMergedEnvironmentVariableCollectionDiff } from 'vs/platform/terminal/common/environmentVariable';
|
||||
import { EnvironmentVariableScope, IExtensionOwnedEnvironmentVariableMutator, IMergedEnvironmentVariableCollection, IMergedEnvironmentVariableCollectionDiff } from 'vs/platform/terminal/common/environmentVariable';
|
||||
import { TerminalStatus } from 'vs/workbench/contrib/terminal/browser/terminalStatusList';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
@@ -68,9 +68,9 @@ export class EnvironmentVariableInfoChangesActive implements IEnvironmentVariabl
|
||||
) {
|
||||
}
|
||||
|
||||
private _getInfo(): string {
|
||||
private _getInfo(scope: EnvironmentVariableScope | undefined): string {
|
||||
const extSet: Set<string> = new Set();
|
||||
addExtensionIdentifiers(extSet, this._collection.map.values());
|
||||
addExtensionIdentifiers(extSet, this._collection.getVariableMap(scope).values());
|
||||
|
||||
let message = localize('extensionEnvironmentContributionInfoActive', "The following extensions have contributed to this terminal's environment:");
|
||||
message += '\n';
|
||||
@@ -80,20 +80,20 @@ export class EnvironmentVariableInfoChangesActive implements IEnvironmentVariabl
|
||||
return message;
|
||||
}
|
||||
|
||||
private _getActions(): ITerminalStatusHoverAction[] {
|
||||
private _getActions(scope: EnvironmentVariableScope | undefined): ITerminalStatusHoverAction[] {
|
||||
return [{
|
||||
label: localize('showEnvironmentContributions', "Show environment contributions"),
|
||||
run: () => this._commandService.executeCommand(TerminalCommandId.ShowEnvironmentContributions),
|
||||
run: () => this._commandService.executeCommand(TerminalCommandId.ShowEnvironmentContributions, scope),
|
||||
commandId: TerminalCommandId.ShowEnvironmentContributions
|
||||
}];
|
||||
}
|
||||
|
||||
getStatus(): ITerminalStatus {
|
||||
getStatus(scope: EnvironmentVariableScope | undefined): ITerminalStatus {
|
||||
return {
|
||||
id: TerminalStatus.EnvironmentVariableInfoChangesActive,
|
||||
severity: Severity.Info,
|
||||
tooltip: this._getInfo(),
|
||||
hoverActions: this._getActions()
|
||||
tooltip: this._getInfo(scope),
|
||||
hoverActions: this._getActions(scope)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme
|
||||
import { TerminalExtensionsRegistry } from 'vs/workbench/contrib/terminal/browser/terminalExtensions';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keybindings';
|
||||
import { ResultKind } from 'vs/platform/keybinding/common/keybindingResolver';
|
||||
import { getWorkspaceForTerminal } from 'vs/workbench/services/configurationResolver/common/terminalResolver';
|
||||
|
||||
const enum Constants {
|
||||
/**
|
||||
@@ -2162,9 +2163,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
this.relaunch();
|
||||
return;
|
||||
}
|
||||
|
||||
// Re-create statuses
|
||||
this.statusList.add(info.getStatus());
|
||||
const workspaceFolder = getWorkspaceForTerminal(this.shellLaunchConfig.cwd, this._workspaceContextService, this._historyService);
|
||||
this.statusList.add(info.getStatus({ workspaceFolder }));
|
||||
}
|
||||
|
||||
async toggleEscapeSequenceLogging(): Promise<boolean> {
|
||||
|
||||
@@ -7,7 +7,6 @@ import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IProcessEnvironment, isMacintosh, isWindows, OperatingSystem, OS } from 'vs/base/common/platform';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
import { formatMessageForTerminal } from 'vs/platform/terminal/common/terminalStrings';
|
||||
@@ -22,7 +21,7 @@ import { NaiveCwdDetectionCapability } from 'vs/platform/terminal/common/capabil
|
||||
import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore';
|
||||
import { FlowControlConstants, IProcessDataEvent, IProcessProperty, IProcessPropertyMap, IProcessReadyEvent, IReconnectionProperties, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensions, ITerminalEnvironment, ITerminalLaunchError, ITerminalProcessOptions, ProcessPropertyType, TerminalSettingId } from 'vs/platform/terminal/common/terminal';
|
||||
import { TerminalRecorder } from 'vs/platform/terminal/common/terminalRecorder';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { EnvironmentVariableInfoChangesActive, EnvironmentVariableInfoStale } from 'vs/workbench/contrib/terminal/browser/environmentVariableInfo';
|
||||
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { IEnvironmentVariableInfo, IEnvironmentVariableService } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
@@ -39,6 +38,7 @@ import { TaskSettingId } from 'vs/workbench/contrib/tasks/common/tasks';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IEnvironmentVariableCollection, IMergedEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable';
|
||||
import { getWorkspaceForTerminal } from 'vs/workbench/services/configurationResolver/common/terminalResolver';
|
||||
|
||||
const enum ProcessConstants {
|
||||
/**
|
||||
@@ -116,6 +116,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
|
||||
readonly onProcessExit = this._onProcessExit.event;
|
||||
private readonly _onRestoreCommands = this._register(new Emitter<ISerializedCommandDetectionCapability>());
|
||||
readonly onRestoreCommands = this._onRestoreCommands.event;
|
||||
private _cwdWorkspaceFolder: IWorkspaceFolder | undefined;
|
||||
|
||||
get persistentProcessId(): number | undefined { return this._process?.id; }
|
||||
get shouldPersist(): boolean { return !!this.reconnectionProperties || (this._process ? this._process.shouldPersist : false); }
|
||||
@@ -146,7 +147,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
|
||||
@INotificationService private readonly _notificationService: INotificationService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._cwdWorkspaceFolder = getWorkspaceForTerminal(cwd, this._workspaceContextService, this._historyService);
|
||||
this.ptyProcessReady = this._createPtyProcessReadyPromise();
|
||||
this.getLatency();
|
||||
this._ackDataBufferer = new AckDataBufferer(e => this._process?.acknowledgeDataEvent(e));
|
||||
@@ -239,9 +240,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
|
||||
this.backend = backend;
|
||||
|
||||
// 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.getEnvironment(this.remoteAuthority), this._configurationResolverService);
|
||||
const variableResolver = terminalEnvironment.createVariableResolver(this._cwdWorkspaceFolder, await this._terminalProfileResolverService.getEnvironment(this.remoteAuthority), this._configurationResolverService);
|
||||
|
||||
// resolvedUserHome is needed here as remote resolvers can launch local terminals before
|
||||
// they're connected to the remote.
|
||||
@@ -282,7 +281,8 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
|
||||
suggestEnabled: this._configurationService.getValue(TerminalSettingId.ShellIntegrationSuggestEnabled),
|
||||
},
|
||||
windowsEnableConpty: this._configHelper.config.windowsEnableConpty,
|
||||
environmentVariableCollections: this._extEnvironmentVariableCollection?.collections ? serializeEnvironmentVariableCollections(this._extEnvironmentVariableCollection.collections) : undefined
|
||||
environmentVariableCollections: this._extEnvironmentVariableCollection?.collections ? serializeEnvironmentVariableCollections(this._extEnvironmentVariableCollection.collections) : undefined,
|
||||
workspaceFolder: this._cwdWorkspaceFolder,
|
||||
};
|
||||
try {
|
||||
newProcess = await backend.createProcess(
|
||||
@@ -409,6 +409,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
|
||||
|
||||
// Fetch any extension environment additions and apply them
|
||||
private async _resolveEnvironment(backend: ITerminalBackend, variableResolver: terminalEnvironment.VariableResolver | undefined, shellLaunchConfig: IShellLaunchConfig): Promise<IProcessEnvironment> {
|
||||
const workspaceFolder = getWorkspaceForTerminal(shellLaunchConfig.cwd, this._workspaceContextService, this._historyService);
|
||||
const platformKey = isWindows ? 'windows' : (isMacintosh ? 'osx' : 'linux');
|
||||
const envFromConfigValue = this._configurationService.getValue<ITerminalEnvironment | undefined>(`terminal.integrated.env.${platformKey}`);
|
||||
this._configHelper.showRecommendations(shellLaunchConfig);
|
||||
@@ -431,8 +432,8 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
|
||||
// info widget. While technically these could differ due to the slight change of a race
|
||||
// condition, the chance is minimal plus the impact on the user is also not that great
|
||||
// if it happens - it's not worth adding plumbing to sync back the resolved collection.
|
||||
await this._extEnvironmentVariableCollection.applyToProcessEnvironment(env, variableResolver);
|
||||
if (this._extEnvironmentVariableCollection.map.size > 0) {
|
||||
await this._extEnvironmentVariableCollection.applyToProcessEnvironment(env, { workspaceFolder }, variableResolver);
|
||||
if (this._extEnvironmentVariableCollection.getVariableMap({ workspaceFolder }).size) {
|
||||
this.environmentVariableInfo = this._instantiationService.createInstance(EnvironmentVariableInfoChangesActive, this._extEnvironmentVariableCollection);
|
||||
this._onEnvironmentVariableInfoChange.fire(this.environmentVariableInfo);
|
||||
}
|
||||
@@ -471,7 +472,8 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
|
||||
suggestEnabled: this._configurationService.getValue(TerminalSettingId.ShellIntegrationSuggestEnabled),
|
||||
},
|
||||
windowsEnableConpty: this._configHelper.config.windowsEnableConpty,
|
||||
environmentVariableCollections: this._extEnvironmentVariableCollection ? serializeEnvironmentVariableCollections(this._extEnvironmentVariableCollection.collections) : undefined
|
||||
environmentVariableCollections: this._extEnvironmentVariableCollection ? serializeEnvironmentVariableCollections(this._extEnvironmentVariableCollection.collections) : undefined,
|
||||
workspaceFolder: this._cwdWorkspaceFolder,
|
||||
};
|
||||
const shouldPersist = ((this._configurationService.getValue(TaskSettingId.Reconnection) && shellLaunchConfig.reconnectionProperties) || !shellLaunchConfig.isFeatureTerminal) && this._configHelper.config.enablePersistentSessions && !shellLaunchConfig.isTransient;
|
||||
return await backend.createProcess(shellLaunchConfig, initialCwd, cols, rows, this._configHelper.config.unicodeVersion, env, options, shouldPersist);
|
||||
@@ -647,7 +649,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
|
||||
}
|
||||
|
||||
private _onEnvironmentVariableCollectionChange(newCollection: IMergedEnvironmentVariableCollection): void {
|
||||
const diff = this._extEnvironmentVariableCollection!.diff(newCollection);
|
||||
const diff = this._extEnvironmentVariableCollection!.diff(newCollection, { workspaceFolder: this._cwdWorkspaceFolder });
|
||||
if (diff === undefined) {
|
||||
// If there are no longer differences, remove the stale info indicator
|
||||
if (this.environmentVariableInfo instanceof EnvironmentVariableInfoStale) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IEnvironmentVariableCollection, IMergedEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable';
|
||||
import { EnvironmentVariableScope, IEnvironmentVariableCollection, IMergedEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable';
|
||||
import { ITerminalStatus } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
|
||||
export const IEnvironmentVariableService = createDecorator<IEnvironmentVariableService>('environmentVariableService');
|
||||
@@ -51,5 +51,5 @@ export interface IEnvironmentVariableCollectionWithPersistence extends IEnvironm
|
||||
|
||||
export interface IEnvironmentVariableInfo {
|
||||
readonly requiresAction: boolean;
|
||||
getStatus(): ITerminalStatus;
|
||||
getStatus(scope: EnvironmentVariableScope | undefined): ITerminalStatus;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ export class EnvironmentVariableService implements IEnvironmentVariableService {
|
||||
@IExtensionService private readonly _extensionService: IExtensionService,
|
||||
@IStorageService private readonly _storageService: IStorageService
|
||||
) {
|
||||
this._storageService.remove(TerminalStorageKeys.DeprecatedEnvironmentVariableCollections, StorageScope.WORKSPACE);
|
||||
const serializedPersistedCollections = this._storageService.get(TerminalStorageKeys.EnvironmentVariableCollections, StorageScope.WORKSPACE);
|
||||
if (serializedPersistedCollections) {
|
||||
const collectionsJson: ISerializableExtensionEnvironmentVariableCollection[] = JSON.parse(serializedPersistedCollections);
|
||||
|
||||
@@ -8,7 +8,8 @@ export const enum TerminalStorageKeys {
|
||||
SuggestedRendererType = 'terminal.integrated.suggestedRendererType',
|
||||
TabsListWidthHorizontal = 'tabs-list-width-horizontal',
|
||||
TabsListWidthVertical = 'tabs-list-width-vertical',
|
||||
EnvironmentVariableCollections = 'terminal.integrated.environmentVariableCollections',
|
||||
DeprecatedEnvironmentVariableCollections = 'terminal.integrated.environmentVariableCollections',
|
||||
EnvironmentVariableCollections = 'terminal.integrated.environmentVariableCollectionsV2',
|
||||
TerminalBufferState = 'terminal.integrated.bufferState',
|
||||
TerminalLayoutInfo = 'terminal.integrated.layoutInfo',
|
||||
PinnedRecentCommandsPrefix = 'terminal.pinnedRecentCommands',
|
||||
|
||||
@@ -30,6 +30,7 @@ import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/termi
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IEnvironmentVariableService } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { BaseTerminalBackend } from 'vs/workbench/contrib/terminal/browser/baseTerminalBackend';
|
||||
import { getWorkspaceForTerminal } from 'vs/workbench/services/configurationResolver/common/terminalResolver';
|
||||
|
||||
export class LocalTerminalBackendContribution implements IWorkbenchContribution {
|
||||
constructor(
|
||||
@@ -262,7 +263,8 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke
|
||||
const baseEnv = await (shellLaunchConfig.useShellEnvironment ? this.getShellEnvironment() : this.getEnvironment());
|
||||
const env = await terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, envFromConfigValue, variableResolver, this._productService.version, this._configurationService.getValue(TerminalSettingId.DetectLocale), baseEnv);
|
||||
if (!shellLaunchConfig.strictEnv && !shellLaunchConfig.hideFromUser) {
|
||||
await this._environmentVariableService.mergedCollection.applyToProcessEnvironment(env, variableResolver);
|
||||
const workspaceFolder = getWorkspaceForTerminal(shellLaunchConfig.cwd, this._workspaceContextService, this._historyService);
|
||||
await this._environmentVariableService.mergedCollection.applyToProcessEnvironment(env, { workspaceFolder }, variableResolver);
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { EnvironmentVariableMutatorType } from 'vs/platform/terminal/common/envi
|
||||
import { IProcessEnvironment, isWindows } from 'vs/base/common/platform';
|
||||
import { MergedEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariableCollection';
|
||||
import { deserializeEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariableShared';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||
suite('ctor', () => {
|
||||
@@ -15,31 +16,31 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||
const merged = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a1', type: EnvironmentVariableMutatorType.Prepend }]
|
||||
['A-key', { value: 'a1', type: EnvironmentVariableMutatorType.Prepend, variable: 'A' }]
|
||||
])
|
||||
}],
|
||||
['ext2', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a2', type: EnvironmentVariableMutatorType.Append }]
|
||||
['A-key', { value: 'a2', type: EnvironmentVariableMutatorType.Append, variable: 'A' }]
|
||||
])
|
||||
}],
|
||||
['ext3', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a3', type: EnvironmentVariableMutatorType.Prepend }]
|
||||
['A-key', { value: 'a3', type: EnvironmentVariableMutatorType.Prepend, variable: 'A' }]
|
||||
])
|
||||
}],
|
||||
['ext4', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a4', type: EnvironmentVariableMutatorType.Append }]
|
||||
['A-key', { value: 'a4', type: EnvironmentVariableMutatorType.Append, variable: 'A' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
deepStrictEqual([...merged.map.entries()], [
|
||||
deepStrictEqual([...merged.getVariableMap(undefined).entries()], [
|
||||
['A', [
|
||||
{ extensionIdentifier: 'ext4', type: EnvironmentVariableMutatorType.Append, value: 'a4' },
|
||||
{ extensionIdentifier: 'ext3', type: EnvironmentVariableMutatorType.Prepend, value: 'a3' },
|
||||
{ extensionIdentifier: 'ext2', type: EnvironmentVariableMutatorType.Append, value: 'a2' },
|
||||
{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Prepend, value: 'a1' }
|
||||
{ extensionIdentifier: 'ext4', type: EnvironmentVariableMutatorType.Append, value: 'a4', variable: 'A' },
|
||||
{ extensionIdentifier: 'ext3', type: EnvironmentVariableMutatorType.Prepend, value: 'a3', variable: 'A' },
|
||||
{ extensionIdentifier: 'ext2', type: EnvironmentVariableMutatorType.Append, value: 'a2', variable: 'A' },
|
||||
{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Prepend, value: 'a1', variable: 'A' }
|
||||
]]
|
||||
]);
|
||||
});
|
||||
@@ -48,33 +49,101 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||
const merged = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a1', type: EnvironmentVariableMutatorType.Prepend }]
|
||||
['A-key', { value: 'a1', type: EnvironmentVariableMutatorType.Prepend, variable: 'A' }]
|
||||
])
|
||||
}],
|
||||
['ext2', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a2', type: EnvironmentVariableMutatorType.Append }]
|
||||
['A-key', { value: 'a2', type: EnvironmentVariableMutatorType.Append, variable: 'A' }]
|
||||
])
|
||||
}],
|
||||
['ext3', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a3', type: EnvironmentVariableMutatorType.Replace }]
|
||||
['A-key', { value: 'a3', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }]
|
||||
])
|
||||
}],
|
||||
['ext4', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a4', type: EnvironmentVariableMutatorType.Append }]
|
||||
['A-key', { value: 'a4', type: EnvironmentVariableMutatorType.Append, variable: 'A' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
deepStrictEqual([...merged.map.entries()], [
|
||||
deepStrictEqual([...merged.getVariableMap(undefined).entries()], [
|
||||
['A', [
|
||||
{ extensionIdentifier: 'ext3', type: EnvironmentVariableMutatorType.Replace, value: 'a3' },
|
||||
{ extensionIdentifier: 'ext2', type: EnvironmentVariableMutatorType.Append, value: 'a2' },
|
||||
{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Prepend, value: 'a1' }
|
||||
{ extensionIdentifier: 'ext3', type: EnvironmentVariableMutatorType.Replace, value: 'a3', variable: 'A' },
|
||||
{ extensionIdentifier: 'ext2', type: EnvironmentVariableMutatorType.Append, value: 'a2', variable: 'A' },
|
||||
{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Prepend, value: 'a1', variable: 'A' }
|
||||
]]
|
||||
], 'The ext4 entry should be removed as it comes after a Replace');
|
||||
});
|
||||
|
||||
test('Appropriate workspace scoped entries are returned when querying for a particular workspace folder', () => {
|
||||
const scope1 = { workspaceFolder: { uri: URI.file('workspace1'), name: 'workspace1', index: 0 } };
|
||||
const scope2 = { workspaceFolder: { uri: URI.file('workspace2'), name: 'workspace2', index: 3 } };
|
||||
const merged = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A-key', { value: 'a1', type: EnvironmentVariableMutatorType.Prepend, scope: scope1, variable: 'A' }]
|
||||
])
|
||||
}],
|
||||
['ext2', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A-key', { value: 'a2', type: EnvironmentVariableMutatorType.Append, variable: 'A' }]
|
||||
])
|
||||
}],
|
||||
['ext3', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A-key', { value: 'a3', type: EnvironmentVariableMutatorType.Prepend, scope: scope2, variable: 'A' }]
|
||||
])
|
||||
}],
|
||||
['ext4', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A-key', { value: 'a4', type: EnvironmentVariableMutatorType.Append, variable: 'A' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
deepStrictEqual([...merged.getVariableMap(scope2).entries()], [
|
||||
['A', [
|
||||
{ extensionIdentifier: 'ext4', type: EnvironmentVariableMutatorType.Append, value: 'a4', variable: 'A' },
|
||||
{ extensionIdentifier: 'ext3', type: EnvironmentVariableMutatorType.Prepend, value: 'a3', scope: scope2, variable: 'A' },
|
||||
{ extensionIdentifier: 'ext2', type: EnvironmentVariableMutatorType.Append, value: 'a2', variable: 'A' },
|
||||
]]
|
||||
]);
|
||||
});
|
||||
|
||||
test('Workspace scoped entries are not included when looking for global entries', () => {
|
||||
const scope1 = { workspaceFolder: { uri: URI.file('workspace1'), name: 'workspace1', index: 0 } };
|
||||
const scope2 = { workspaceFolder: { uri: URI.file('workspace2'), name: 'workspace2', index: 3 } };
|
||||
const merged = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A-key', { value: 'a1', type: EnvironmentVariableMutatorType.Prepend, scope: scope1, variable: 'A' }]
|
||||
])
|
||||
}],
|
||||
['ext2', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A-key', { value: 'a2', type: EnvironmentVariableMutatorType.Append, variable: 'A' }]
|
||||
])
|
||||
}],
|
||||
['ext3', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A-key', { value: 'a3', type: EnvironmentVariableMutatorType.Prepend, scope: scope2, variable: 'A' }]
|
||||
])
|
||||
}],
|
||||
['ext4', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A-key', { value: 'a4', type: EnvironmentVariableMutatorType.Append, variable: 'A' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
deepStrictEqual([...merged.getVariableMap(undefined).entries()], [
|
||||
['A', [
|
||||
{ extensionIdentifier: 'ext4', type: EnvironmentVariableMutatorType.Append, value: 'a4', variable: 'A' },
|
||||
{ extensionIdentifier: 'ext2', type: EnvironmentVariableMutatorType.Append, value: 'a2', variable: 'A' },
|
||||
]]
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('applyToProcessEnvironment', () => {
|
||||
@@ -82,9 +151,9 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||
const merged = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a', type: EnvironmentVariableMutatorType.Replace }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Append }],
|
||||
['C', { value: 'c', type: EnvironmentVariableMutatorType.Prepend }]
|
||||
['A-key', { value: 'a', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Append, variable: 'B' }],
|
||||
['C', { value: 'c', type: EnvironmentVariableMutatorType.Prepend, variable: 'C' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
@@ -93,7 +162,7 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||
B: 'bar',
|
||||
C: 'baz'
|
||||
};
|
||||
await merged.applyToProcessEnvironment(env);
|
||||
await merged.applyToProcessEnvironment(env, undefined);
|
||||
deepStrictEqual(env, {
|
||||
A: 'a',
|
||||
B: 'barb',
|
||||
@@ -101,18 +170,43 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('should apply the appropriate workspace scoped entries to an environment', async () => {
|
||||
const scope1 = { workspaceFolder: { uri: URI.file('workspace1'), name: 'workspace1', index: 0 } };
|
||||
const scope2 = { workspaceFolder: { uri: URI.file('workspace2'), name: 'workspace2', index: 3 } };
|
||||
const merged = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A-key', { value: 'a', type: EnvironmentVariableMutatorType.Replace, scope: scope1, variable: 'A' }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Append, scope: scope2, variable: 'B' }],
|
||||
['C', { value: 'c', type: EnvironmentVariableMutatorType.Prepend, variable: 'C' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const env: IProcessEnvironment = {
|
||||
A: 'foo',
|
||||
B: 'bar',
|
||||
C: 'baz'
|
||||
};
|
||||
await merged.applyToProcessEnvironment(env, scope1);
|
||||
deepStrictEqual(env, {
|
||||
A: 'a',
|
||||
B: 'bar', // This is not changed because the scope does not match
|
||||
C: 'cbaz'
|
||||
});
|
||||
});
|
||||
|
||||
test('should apply the collection to environment entries with no values', async () => {
|
||||
const merged = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a', type: EnvironmentVariableMutatorType.Replace }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Append }],
|
||||
['C', { value: 'c', type: EnvironmentVariableMutatorType.Prepend }]
|
||||
['A-key', { value: 'a', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Append, variable: 'B' }],
|
||||
['C', { value: 'c', type: EnvironmentVariableMutatorType.Prepend, variable: 'C' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const env: IProcessEnvironment = {};
|
||||
await merged.applyToProcessEnvironment(env);
|
||||
await merged.applyToProcessEnvironment(env, undefined);
|
||||
deepStrictEqual(env, {
|
||||
A: 'a',
|
||||
B: 'b',
|
||||
@@ -124,9 +218,9 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||
const merged = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['a', { value: 'a', type: EnvironmentVariableMutatorType.Replace }],
|
||||
['b', { value: 'b', type: EnvironmentVariableMutatorType.Append }],
|
||||
['c', { value: 'c', type: EnvironmentVariableMutatorType.Prepend }]
|
||||
['A-key', { value: 'a', type: EnvironmentVariableMutatorType.Replace, variable: 'a' }],
|
||||
['b', { value: 'b', type: EnvironmentVariableMutatorType.Append, variable: 'b' }],
|
||||
['c', { value: 'c', type: EnvironmentVariableMutatorType.Prepend, variable: 'c' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
@@ -135,7 +229,7 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||
B: 'B',
|
||||
C: 'C'
|
||||
};
|
||||
await merged.applyToProcessEnvironment(env);
|
||||
await merged.applyToProcessEnvironment(env, undefined);
|
||||
if (isWindows) {
|
||||
deepStrictEqual(env, {
|
||||
A: 'a',
|
||||
@@ -160,18 +254,18 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||
const merged1 = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a', type: EnvironmentVariableMutatorType.Replace }]
|
||||
['A-key', { value: 'a', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const merged2 = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a', type: EnvironmentVariableMutatorType.Replace }]
|
||||
['A-key', { value: 'a', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const diff = merged1.diff(merged2);
|
||||
const diff = merged1.diff(merged2, undefined);
|
||||
strictEqual(diff, undefined);
|
||||
});
|
||||
test('should generate added diffs from when the first entry is added', () => {
|
||||
@@ -179,16 +273,16 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||
const merged2 = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a', type: EnvironmentVariableMutatorType.Replace }]
|
||||
['A-key', { value: 'a', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const diff = merged1.diff(merged2)!;
|
||||
const diff = merged1.diff(merged2, undefined)!;
|
||||
strictEqual(diff.changed.size, 0);
|
||||
strictEqual(diff.removed.size, 0);
|
||||
const entries = [...diff.added.entries()];
|
||||
deepStrictEqual(entries, [
|
||||
['A', [{ extensionIdentifier: 'ext1', value: 'a', type: EnvironmentVariableMutatorType.Replace }]]
|
||||
['A', [{ extensionIdentifier: 'ext1', value: 'a', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }]]
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -196,24 +290,24 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||
const merged1 = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a', type: EnvironmentVariableMutatorType.Replace }]
|
||||
['A-key', { value: 'a', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const merged2 = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a', type: EnvironmentVariableMutatorType.Replace }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Append }]
|
||||
['A-key', { value: 'a', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Append, variable: 'B' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const diff = merged1.diff(merged2)!;
|
||||
const diff = merged1.diff(merged2, undefined)!;
|
||||
strictEqual(diff.changed.size, 0);
|
||||
strictEqual(diff.removed.size, 0);
|
||||
const entries = [...diff.added.entries()];
|
||||
deepStrictEqual(entries, [
|
||||
['B', [{ extensionIdentifier: 'ext1', value: 'b', type: EnvironmentVariableMutatorType.Append }]]
|
||||
['B', [{ extensionIdentifier: 'ext1', value: 'b', type: EnvironmentVariableMutatorType.Append, variable: 'B' }]]
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -221,7 +315,7 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||
const merged1 = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a1', type: EnvironmentVariableMutatorType.Prepend }]
|
||||
['A-key', { value: 'a1', type: EnvironmentVariableMutatorType.Prepend, variable: 'A' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
@@ -229,36 +323,36 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||
const merged2 = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext2', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a2', type: EnvironmentVariableMutatorType.Append }]
|
||||
['A-key', { value: 'a2', type: EnvironmentVariableMutatorType.Append, variable: 'A' }]
|
||||
])
|
||||
}],
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a1', type: EnvironmentVariableMutatorType.Prepend }]
|
||||
['A-key', { value: 'a1', type: EnvironmentVariableMutatorType.Prepend, variable: 'A' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const diff = merged1.diff(merged2)!;
|
||||
const diff = merged1.diff(merged2, undefined)!;
|
||||
strictEqual(diff.changed.size, 0);
|
||||
strictEqual(diff.removed.size, 0);
|
||||
deepStrictEqual([...diff.added.entries()], [
|
||||
['A', [{ extensionIdentifier: 'ext2', value: 'a2', type: EnvironmentVariableMutatorType.Append }]]
|
||||
['A', [{ extensionIdentifier: 'ext2', value: 'a2', type: EnvironmentVariableMutatorType.Append, variable: 'A' }]]
|
||||
]);
|
||||
|
||||
const merged3 = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a1', type: EnvironmentVariableMutatorType.Prepend }]
|
||||
['A-key', { value: 'a1', type: EnvironmentVariableMutatorType.Prepend, variable: 'A' }]
|
||||
])
|
||||
}],
|
||||
// This entry should get removed
|
||||
['ext2', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a2', type: EnvironmentVariableMutatorType.Append }]
|
||||
['A-key', { value: 'a2', type: EnvironmentVariableMutatorType.Append, variable: 'A' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const diff2 = merged1.diff(merged3)!;
|
||||
const diff2 = merged1.diff(merged3, undefined)!;
|
||||
strictEqual(diff2.changed.size, 0);
|
||||
strictEqual(diff2.removed.size, 0);
|
||||
deepStrictEqual([...diff.added.entries()], [...diff2.added.entries()], 'Swapping the order of the entries in the other collection should yield the same result');
|
||||
@@ -268,24 +362,24 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||
const merged1 = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a1', type: EnvironmentVariableMutatorType.Replace }]
|
||||
['A-key', { value: 'a1', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const merged4 = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a1', type: EnvironmentVariableMutatorType.Replace }]
|
||||
['A-key', { value: 'a1', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }]
|
||||
])
|
||||
}],
|
||||
// This entry should get removed as it comes after a replace
|
||||
['ext2', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a2', type: EnvironmentVariableMutatorType.Append }]
|
||||
['A-key', { value: 'a2', type: EnvironmentVariableMutatorType.Append, variable: 'A' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const diff = merged1.diff(merged4);
|
||||
const diff = merged1.diff(merged4, undefined);
|
||||
strictEqual(diff, undefined, 'Replace should ignore any entries after it');
|
||||
});
|
||||
|
||||
@@ -293,23 +387,23 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||
const merged1 = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a', type: EnvironmentVariableMutatorType.Replace }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Replace }]
|
||||
['A-key', { value: 'a', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Replace, variable: 'B' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const merged2 = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a', type: EnvironmentVariableMutatorType.Replace }]
|
||||
['A-key', { value: 'a', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const diff = merged1.diff(merged2)!;
|
||||
const diff = merged1.diff(merged2, undefined)!;
|
||||
strictEqual(diff.changed.size, 0);
|
||||
strictEqual(diff.added.size, 0);
|
||||
deepStrictEqual([...diff.removed.entries()], [
|
||||
['B', [{ extensionIdentifier: 'ext1', value: 'b', type: EnvironmentVariableMutatorType.Replace }]]
|
||||
['B', [{ extensionIdentifier: 'ext1', value: 'b', type: EnvironmentVariableMutatorType.Replace, variable: 'B' }]]
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -317,25 +411,25 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||
const merged1 = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a1', type: EnvironmentVariableMutatorType.Replace }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Replace }]
|
||||
['A-key', { value: 'a1', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Replace, variable: 'B' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const merged2 = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a2', type: EnvironmentVariableMutatorType.Replace }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Append }]
|
||||
['A-key', { value: 'a2', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Append, variable: 'B' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const diff = merged1.diff(merged2)!;
|
||||
const diff = merged1.diff(merged2, undefined)!;
|
||||
strictEqual(diff.added.size, 0);
|
||||
strictEqual(diff.removed.size, 0);
|
||||
deepStrictEqual([...diff.changed.entries()], [
|
||||
['A', [{ extensionIdentifier: 'ext1', value: 'a2', type: EnvironmentVariableMutatorType.Replace }]],
|
||||
['B', [{ extensionIdentifier: 'ext1', value: 'b', type: EnvironmentVariableMutatorType.Append }]]
|
||||
['A', [{ extensionIdentifier: 'ext1', value: 'a2', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }]],
|
||||
['B', [{ extensionIdentifier: 'ext1', value: 'b', type: EnvironmentVariableMutatorType.Append, variable: 'B' }]]
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -343,28 +437,57 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||
const merged1 = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a1', type: EnvironmentVariableMutatorType.Replace }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Prepend }]
|
||||
['A-key', { value: 'a1', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Prepend, variable: 'B' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const merged2 = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a2', type: EnvironmentVariableMutatorType.Replace }],
|
||||
['C', { value: 'c', type: EnvironmentVariableMutatorType.Append }]
|
||||
['A-key', { value: 'a2', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }],
|
||||
['C', { value: 'c', type: EnvironmentVariableMutatorType.Append, variable: 'C' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const diff = merged1.diff(merged2)!;
|
||||
const diff = merged1.diff(merged2, undefined)!;
|
||||
deepStrictEqual([...diff.added.entries()], [
|
||||
['C', [{ extensionIdentifier: 'ext1', value: 'c', type: EnvironmentVariableMutatorType.Append }]],
|
||||
['C', [{ extensionIdentifier: 'ext1', value: 'c', type: EnvironmentVariableMutatorType.Append, variable: 'C' }]],
|
||||
]);
|
||||
deepStrictEqual([...diff.removed.entries()], [
|
||||
['B', [{ extensionIdentifier: 'ext1', value: 'b', type: EnvironmentVariableMutatorType.Prepend }]]
|
||||
['B', [{ extensionIdentifier: 'ext1', value: 'b', type: EnvironmentVariableMutatorType.Prepend, variable: 'B' }]]
|
||||
]);
|
||||
deepStrictEqual([...diff.changed.entries()], [
|
||||
['A', [{ extensionIdentifier: 'ext1', value: 'a2', type: EnvironmentVariableMutatorType.Replace }]]
|
||||
['A', [{ extensionIdentifier: 'ext1', value: 'a2', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }]]
|
||||
]);
|
||||
});
|
||||
|
||||
test('should only generate workspace specific diffs', () => {
|
||||
const scope1 = { workspaceFolder: { uri: URI.file('workspace1'), name: 'workspace1', index: 0 } };
|
||||
const scope2 = { workspaceFolder: { uri: URI.file('workspace2'), name: 'workspace2', index: 3 } };
|
||||
const merged1 = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A-key', { value: 'a1', type: EnvironmentVariableMutatorType.Replace, scope: scope1, variable: 'A' }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Prepend, variable: 'B' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const merged2 = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext1', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
['A-key', { value: 'a2', type: EnvironmentVariableMutatorType.Replace, scope: scope1, variable: 'A' }],
|
||||
['C', { value: 'c', type: EnvironmentVariableMutatorType.Append, scope: scope2, variable: 'C' }]
|
||||
])
|
||||
}]
|
||||
]));
|
||||
const diff = merged1.diff(merged2, scope1)!;
|
||||
strictEqual(diff.added.size, 0);
|
||||
deepStrictEqual([...diff.removed.entries()], [
|
||||
['B', [{ extensionIdentifier: 'ext1', value: 'b', type: EnvironmentVariableMutatorType.Prepend, variable: 'B' }]]
|
||||
]);
|
||||
deepStrictEqual([...diff.changed.entries()], [
|
||||
['A', [{ extensionIdentifier: 'ext1', value: 'a2', type: EnvironmentVariableMutatorType.Replace, scope: scope1, variable: 'A' }]]
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { deepStrictEqual } from 'assert';
|
||||
import { TestExtensionService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
|
||||
import { TestExtensionService, TestHistoryService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
|
||||
import { EnvironmentVariableService } from 'vs/workbench/contrib/terminal/common/environmentVariableService';
|
||||
import { EnvironmentVariableMutatorType, IEnvironmentVariableMutator } from 'vs/platform/terminal/common/environmentVariable';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
@@ -12,6 +12,8 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
class TestEnvironmentVariableService extends EnvironmentVariableService {
|
||||
persistCollections(): void { this._persistCollections(); }
|
||||
@@ -22,6 +24,7 @@ suite('EnvironmentVariable - EnvironmentVariableService', () => {
|
||||
let instantiationService: TestInstantiationService;
|
||||
let environmentVariableService: TestEnvironmentVariableService;
|
||||
let storageService: TestStorageService;
|
||||
let historyService: TestHistoryService;
|
||||
let changeExtensionsEvent: Emitter<void>;
|
||||
|
||||
setup(() => {
|
||||
@@ -30,6 +33,7 @@ suite('EnvironmentVariable - EnvironmentVariableService', () => {
|
||||
instantiationService = new TestInstantiationService();
|
||||
instantiationService.stub(IExtensionService, TestExtensionService);
|
||||
storageService = new TestStorageService();
|
||||
historyService = new TestHistoryService();
|
||||
instantiationService.stub(IStorageService, storageService);
|
||||
instantiationService.stub(IExtensionService, TestExtensionService);
|
||||
instantiationService.stub(IExtensionService, 'onDidChangeExtensions', changeExtensionsEvent.event);
|
||||
@@ -38,29 +42,30 @@ suite('EnvironmentVariable - EnvironmentVariableService', () => {
|
||||
{ identifier: { value: 'ext2' } },
|
||||
{ identifier: { value: 'ext3' } }
|
||||
]);
|
||||
instantiationService.stub(IHistoryService, historyService);
|
||||
|
||||
environmentVariableService = instantiationService.createInstance(TestEnvironmentVariableService);
|
||||
});
|
||||
|
||||
test('should persist collections to the storage service and be able to restore from them', () => {
|
||||
const collection = new Map<string, IEnvironmentVariableMutator>();
|
||||
collection.set('A', { value: 'a', type: EnvironmentVariableMutatorType.Replace });
|
||||
collection.set('B', { value: 'b', type: EnvironmentVariableMutatorType.Append });
|
||||
collection.set('C', { value: 'c', type: EnvironmentVariableMutatorType.Prepend });
|
||||
collection.set('A-key', { value: 'a', type: EnvironmentVariableMutatorType.Replace, variable: 'A' });
|
||||
collection.set('B-key', { value: 'b', type: EnvironmentVariableMutatorType.Append, variable: 'B' });
|
||||
collection.set('C-key', { value: 'c', type: EnvironmentVariableMutatorType.Prepend, variable: 'C' });
|
||||
environmentVariableService.set('ext1', { map: collection, persistent: true });
|
||||
deepStrictEqual([...environmentVariableService.mergedCollection.map.entries()], [
|
||||
['A', [{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Replace, value: 'a' }]],
|
||||
['B', [{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Append, value: 'b' }]],
|
||||
['C', [{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Prepend, value: 'c' }]]
|
||||
deepStrictEqual([...environmentVariableService.mergedCollection.getVariableMap(undefined).entries()], [
|
||||
['A', [{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Replace, value: 'a', variable: 'A' }]],
|
||||
['B', [{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Append, value: 'b', variable: 'B' }]],
|
||||
['C', [{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Prepend, value: 'c', variable: 'C' }]]
|
||||
]);
|
||||
|
||||
// Persist with old service, create a new service with the same storage service to verify restore
|
||||
environmentVariableService.persistCollections();
|
||||
const service2: TestEnvironmentVariableService = instantiationService.createInstance(TestEnvironmentVariableService);
|
||||
deepStrictEqual([...service2.mergedCollection.map.entries()], [
|
||||
['A', [{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Replace, value: 'a' }]],
|
||||
['B', [{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Append, value: 'b' }]],
|
||||
['C', [{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Prepend, value: 'c' }]]
|
||||
deepStrictEqual([...service2.mergedCollection.getVariableMap(undefined).entries()], [
|
||||
['A', [{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Replace, value: 'a', variable: 'A' }]],
|
||||
['B', [{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Append, value: 'b', variable: 'B' }]],
|
||||
['C', [{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Prepend, value: 'c', variable: 'C' }]]
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -69,21 +74,21 @@ suite('EnvironmentVariable - EnvironmentVariableService', () => {
|
||||
const collection1 = new Map<string, IEnvironmentVariableMutator>();
|
||||
const collection2 = new Map<string, IEnvironmentVariableMutator>();
|
||||
const collection3 = new Map<string, IEnvironmentVariableMutator>();
|
||||
collection1.set('A', { value: 'a1', type: EnvironmentVariableMutatorType.Append });
|
||||
collection1.set('B', { value: 'b1', type: EnvironmentVariableMutatorType.Replace });
|
||||
collection2.set('A', { value: 'a2', type: EnvironmentVariableMutatorType.Replace });
|
||||
collection2.set('B', { value: 'b2', type: EnvironmentVariableMutatorType.Append });
|
||||
collection3.set('A', { value: 'a3', type: EnvironmentVariableMutatorType.Prepend });
|
||||
collection3.set('B', { value: 'b3', type: EnvironmentVariableMutatorType.Replace });
|
||||
collection1.set('A-key', { value: 'a1', type: EnvironmentVariableMutatorType.Append, variable: 'A' });
|
||||
collection1.set('B-key', { value: 'b1', type: EnvironmentVariableMutatorType.Replace, variable: 'B' });
|
||||
collection2.set('A-key', { value: 'a2', type: EnvironmentVariableMutatorType.Replace, variable: 'A' });
|
||||
collection2.set('B-key', { value: 'b2', type: EnvironmentVariableMutatorType.Append, variable: 'B' });
|
||||
collection3.set('A-key', { value: 'a3', type: EnvironmentVariableMutatorType.Prepend, variable: 'A' });
|
||||
collection3.set('B-key', { value: 'b3', type: EnvironmentVariableMutatorType.Replace, variable: 'B' });
|
||||
environmentVariableService.set('ext1', { map: collection1, persistent: true });
|
||||
environmentVariableService.set('ext2', { map: collection2, persistent: true });
|
||||
environmentVariableService.set('ext3', { map: collection3, persistent: true });
|
||||
deepStrictEqual([...environmentVariableService.mergedCollection.map.entries()], [
|
||||
deepStrictEqual([...environmentVariableService.mergedCollection.getVariableMap(undefined).entries()], [
|
||||
['A', [
|
||||
{ extensionIdentifier: 'ext2', type: EnvironmentVariableMutatorType.Replace, value: 'a2' },
|
||||
{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Append, value: 'a1' }
|
||||
{ extensionIdentifier: 'ext2', type: EnvironmentVariableMutatorType.Replace, value: 'a2', variable: 'A' },
|
||||
{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Append, value: 'a1', variable: 'A' }
|
||||
]],
|
||||
['B', [{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Replace, value: 'b1' }]]
|
||||
['B', [{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Replace, value: 'b1', variable: 'B' }]]
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -91,26 +96,53 @@ suite('EnvironmentVariable - EnvironmentVariableService', () => {
|
||||
const collection1 = new Map<string, IEnvironmentVariableMutator>();
|
||||
const collection2 = new Map<string, IEnvironmentVariableMutator>();
|
||||
const collection3 = new Map<string, IEnvironmentVariableMutator>();
|
||||
collection1.set('A', { value: ':a1', type: EnvironmentVariableMutatorType.Append });
|
||||
collection2.set('A', { value: 'a2:', type: EnvironmentVariableMutatorType.Prepend });
|
||||
collection3.set('A', { value: 'a3', type: EnvironmentVariableMutatorType.Replace });
|
||||
collection1.set('A-key', { value: ':a1', type: EnvironmentVariableMutatorType.Append, variable: 'A' });
|
||||
collection2.set('A-key', { value: 'a2:', type: EnvironmentVariableMutatorType.Prepend, variable: 'A' });
|
||||
collection3.set('A-key', { value: 'a3', type: EnvironmentVariableMutatorType.Replace, variable: 'A' });
|
||||
environmentVariableService.set('ext1', { map: collection1, persistent: true });
|
||||
environmentVariableService.set('ext2', { map: collection2, persistent: true });
|
||||
environmentVariableService.set('ext3', { map: collection3, persistent: true });
|
||||
|
||||
// The entries should be ordered in the order they are applied
|
||||
deepStrictEqual([...environmentVariableService.mergedCollection.map.entries()], [
|
||||
deepStrictEqual([...environmentVariableService.mergedCollection.getVariableMap(undefined).entries()], [
|
||||
['A', [
|
||||
{ extensionIdentifier: 'ext3', type: EnvironmentVariableMutatorType.Replace, value: 'a3' },
|
||||
{ extensionIdentifier: 'ext2', type: EnvironmentVariableMutatorType.Prepend, value: 'a2:' },
|
||||
{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Append, value: ':a1' }
|
||||
{ extensionIdentifier: 'ext3', type: EnvironmentVariableMutatorType.Replace, value: 'a3', variable: 'A' },
|
||||
{ extensionIdentifier: 'ext2', type: EnvironmentVariableMutatorType.Prepend, value: 'a2:', variable: 'A' },
|
||||
{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Append, value: ':a1', variable: 'A' }
|
||||
]]
|
||||
]);
|
||||
|
||||
// Verify the entries get applied to the environment as expected
|
||||
const env: IProcessEnvironment = { A: 'foo' };
|
||||
await environmentVariableService.mergedCollection.applyToProcessEnvironment(env);
|
||||
await environmentVariableService.mergedCollection.applyToProcessEnvironment(env, undefined);
|
||||
deepStrictEqual(env, { A: 'a2:a3:a1' });
|
||||
});
|
||||
|
||||
test('should correctly apply the workspace specific environment values from multiple extension contributions in the correct order', async () => {
|
||||
const scope1 = { workspaceFolder: { uri: URI.file('workspace1'), name: 'workspace1', index: 0 } };
|
||||
const scope2 = { workspaceFolder: { uri: URI.file('workspace2'), name: 'workspace2', index: 3 } };
|
||||
const collection1 = new Map<string, IEnvironmentVariableMutator>();
|
||||
const collection2 = new Map<string, IEnvironmentVariableMutator>();
|
||||
const collection3 = new Map<string, IEnvironmentVariableMutator>();
|
||||
collection1.set('A-key', { value: ':a1', type: EnvironmentVariableMutatorType.Append, scope: scope1, variable: 'A' });
|
||||
collection2.set('A-key', { value: 'a2:', type: EnvironmentVariableMutatorType.Prepend, variable: 'A' });
|
||||
collection3.set('A-key', { value: 'a3', type: EnvironmentVariableMutatorType.Replace, scope: scope2, variable: 'A' });
|
||||
environmentVariableService.set('ext1', { map: collection1, persistent: true });
|
||||
environmentVariableService.set('ext2', { map: collection2, persistent: true });
|
||||
environmentVariableService.set('ext3', { map: collection3, persistent: true });
|
||||
|
||||
// The entries should be ordered in the order they are applied
|
||||
deepStrictEqual([...environmentVariableService.mergedCollection.getVariableMap(scope1).entries()], [
|
||||
['A', [
|
||||
{ extensionIdentifier: 'ext2', type: EnvironmentVariableMutatorType.Prepend, value: 'a2:', variable: 'A' },
|
||||
{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Append, value: ':a1', scope: scope1, variable: 'A' }
|
||||
]]
|
||||
]);
|
||||
|
||||
// Verify the entries get applied to the environment as expected
|
||||
const env: IProcessEnvironment = { A: 'foo' };
|
||||
await environmentVariableService.mergedCollection.applyToProcessEnvironment(env, scope1);
|
||||
deepStrictEqual(env, { A: 'a2:foo:a1' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,15 +10,15 @@ import { EnvironmentVariableMutatorType, IEnvironmentVariableMutator } from 'vs/
|
||||
suite('EnvironmentVariable - deserializeEnvironmentVariableCollection', () => {
|
||||
test('should construct correctly with 3 arguments', () => {
|
||||
const c = deserializeEnvironmentVariableCollection([
|
||||
['A', { value: 'a', type: EnvironmentVariableMutatorType.Replace }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Append }],
|
||||
['C', { value: 'c', type: EnvironmentVariableMutatorType.Prepend }]
|
||||
['A', { value: 'a', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Append, variable: 'B' }],
|
||||
['C', { value: 'c', type: EnvironmentVariableMutatorType.Prepend, variable: 'C' }]
|
||||
]);
|
||||
const keys = [...c.keys()];
|
||||
deepStrictEqual(keys, ['A', 'B', 'C']);
|
||||
deepStrictEqual(c.get('A'), { value: 'a', type: EnvironmentVariableMutatorType.Replace });
|
||||
deepStrictEqual(c.get('B'), { value: 'b', type: EnvironmentVariableMutatorType.Append });
|
||||
deepStrictEqual(c.get('C'), { value: 'c', type: EnvironmentVariableMutatorType.Prepend });
|
||||
deepStrictEqual(c.get('A'), { value: 'a', type: EnvironmentVariableMutatorType.Replace, variable: 'A' });
|
||||
deepStrictEqual(c.get('B'), { value: 'b', type: EnvironmentVariableMutatorType.Append, variable: 'B' });
|
||||
deepStrictEqual(c.get('C'), { value: 'c', type: EnvironmentVariableMutatorType.Prepend, variable: 'C' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,13 +26,13 @@ suite('EnvironmentVariable - serializeEnvironmentVariableCollection', () => {
|
||||
test('should correctly serialize the object', () => {
|
||||
const collection = new Map<string, IEnvironmentVariableMutator>();
|
||||
deepStrictEqual(serializeEnvironmentVariableCollection(collection), []);
|
||||
collection.set('A', { value: 'a', type: EnvironmentVariableMutatorType.Replace });
|
||||
collection.set('B', { value: 'b', type: EnvironmentVariableMutatorType.Append });
|
||||
collection.set('C', { value: 'c', type: EnvironmentVariableMutatorType.Prepend });
|
||||
collection.set('A', { value: 'a', type: EnvironmentVariableMutatorType.Replace, variable: 'A' });
|
||||
collection.set('B', { value: 'b', type: EnvironmentVariableMutatorType.Append, variable: 'B' });
|
||||
collection.set('C', { value: 'c', type: EnvironmentVariableMutatorType.Prepend, variable: 'C' });
|
||||
deepStrictEqual(serializeEnvironmentVariableCollection(collection), [
|
||||
['A', { value: 'a', type: EnvironmentVariableMutatorType.Replace }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Append }],
|
||||
['C', { value: 'c', type: EnvironmentVariableMutatorType.Prepend }]
|
||||
['A', { value: 'a', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }],
|
||||
['B', { value: 'b', type: EnvironmentVariableMutatorType.Append, variable: 'B' }],
|
||||
['C', { value: 'c', type: EnvironmentVariableMutatorType.Prepend, variable: 'C' }]
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
import { EnvironmentVariableMutatorType, IMergedEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable';
|
||||
import { EnvironmentVariableMutatorType, EnvironmentVariableScope, IEnvironmentVariableMutator, IMergedEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable';
|
||||
import { registerActiveInstanceAction } from 'vs/workbench/contrib/terminal/browser/terminalActions';
|
||||
import { TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
@@ -16,7 +16,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
|
||||
registerActiveInstanceAction({
|
||||
id: TerminalCommandId.ShowEnvironmentContributions,
|
||||
title: { value: localize('workbench.action.terminal.showEnvironmentContributions', "Show Environment Contributions"), original: 'Show Environment Contributions' },
|
||||
run: async (activeInstance, c, accessor) => {
|
||||
run: async (activeInstance, c, accessor, scope) => {
|
||||
const collection = activeInstance.extEnvironmentVariableCollection;
|
||||
if (collection) {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
@@ -24,7 +24,7 @@ registerActiveInstanceAction({
|
||||
resource: URI.from({
|
||||
scheme: Schemas.untitled
|
||||
}),
|
||||
contents: describeEnvironmentChanges(collection),
|
||||
contents: describeEnvironmentChanges(collection, scope as EnvironmentVariableScope | undefined),
|
||||
languageId: 'markdown'
|
||||
});
|
||||
}
|
||||
@@ -32,18 +32,35 @@ registerActiveInstanceAction({
|
||||
});
|
||||
|
||||
|
||||
function describeEnvironmentChanges(collection: IMergedEnvironmentVariableCollection): string {
|
||||
function describeEnvironmentChanges(collection: IMergedEnvironmentVariableCollection, scope: EnvironmentVariableScope | undefined): string {
|
||||
let content = `# ${localize('envChanges', 'Terminal Environment Changes')}`;
|
||||
for (const [ext, coll] of collection.collections) {
|
||||
content += `\n\n## ${localize('extension', 'Extension: {0}', ext)}`;
|
||||
content += '\n';
|
||||
for (const [variable, mutator] of coll.map.entries()) {
|
||||
content += `\n- \`${mutatorTypeLabel(mutator.type, mutator.value, variable)}\``;
|
||||
for (const [_, mutator] of coll.map.entries()) {
|
||||
if (filterScope(mutator, scope) === false) {
|
||||
continue;
|
||||
}
|
||||
content += `\n- \`${mutatorTypeLabel(mutator.type, mutator.value, mutator.variable)}\``;
|
||||
}
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
function filterScope(
|
||||
mutator: IEnvironmentVariableMutator,
|
||||
scope: EnvironmentVariableScope | undefined
|
||||
): boolean {
|
||||
if (!mutator.scope) {
|
||||
return true;
|
||||
}
|
||||
// Only mutators which are applicable on the relevant workspace should be shown.
|
||||
if (mutator.scope.workspaceFolder && scope?.workspaceFolder && mutator.scope.workspaceFolder.index === scope.workspaceFolder.index) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function mutatorTypeLabel(type: EnvironmentVariableMutatorType, value: string, variable: string): string {
|
||||
switch (type) {
|
||||
case EnvironmentVariableMutatorType.Prepend: return `${variable}=${value}\${env:${variable}}`;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
|
||||
export function getWorkspaceForTerminal(cwd: URI | string | undefined, workspaceContextService: IWorkspaceContextService, historyService: IHistoryService): IWorkspaceFolder | undefined {
|
||||
const cwdUri = typeof cwd === 'string' ? URI.parse(cwd) : cwd;
|
||||
let workspaceFolder = cwdUri ? withNullAsUndefined(workspaceContextService.getWorkspaceFolder(cwdUri)) : undefined;
|
||||
if (!workspaceFolder) {
|
||||
// fallback to last active workspace if cwd is not available or it is not in workspace
|
||||
// TOOD: last active workspace is known to be unreliable, we should remove this fallback eventually
|
||||
const activeWorkspaceRootUri = historyService.getLastActiveWorkspaceRoot();
|
||||
workspaceFolder = activeWorkspaceRootUri ? withNullAsUndefined(workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined;
|
||||
}
|
||||
return workspaceFolder;
|
||||
}
|
||||
@@ -31,6 +31,7 @@ export const allApiProposals = Object.freeze({
|
||||
dropMetadata: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.dropMetadata.d.ts',
|
||||
editSessionIdentityProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.editSessionIdentityProvider.d.ts',
|
||||
editorInsets: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.editorInsets.d.ts',
|
||||
envCollectionWorkspace: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts',
|
||||
envShellEvent: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.envShellEvent.d.ts',
|
||||
extensionRuntime: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.extensionRuntime.d.ts',
|
||||
extensionsAny: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.extensionsAny.d.ts',
|
||||
|
||||
@@ -18,12 +18,15 @@ import { NullExtensionService } from 'vs/workbench/services/extensions/common/ex
|
||||
import { IWorkingCopyFileService, IWorkingCopyFileOperationParticipant, WorkingCopyFileEvent, IDeleteOperation, ICopyOperation, IMoveOperation, IFileOperationUndoRedoInfo, ICreateFileOperation, ICreateOperation, IStoredFileWorkingCopySaveParticipant } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IFileStatWithMetadata } from 'vs/platform/files/common/files';
|
||||
import { ISaveOptions, IRevertOptions, SaveReason } from 'vs/workbench/common/editor';
|
||||
import { ISaveOptions, IRevertOptions, SaveReason, GroupIdentifier } from 'vs/workbench/common/editor';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { IActivity, IActivityService } from 'vs/workbench/services/activity/common/activity';
|
||||
import { IStoredFileWorkingCopySaveEvent } from 'vs/workbench/services/workingCopy/common/storedFileWorkingCopy';
|
||||
import { AbstractLoggerService, ILogger, LogLevel, NullLogger } from 'vs/platform/log/common/log';
|
||||
import { IResourceEditorInput } from 'vs/platform/editor/common/editor';
|
||||
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
|
||||
export class TestLoggerService extends AbstractLoggerService {
|
||||
constructor(logsHome?: URI) {
|
||||
@@ -140,6 +143,27 @@ export class TestStorageService extends InMemoryStorageService {
|
||||
}
|
||||
}
|
||||
|
||||
export class TestHistoryService implements IHistoryService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
constructor(private root?: URI) { }
|
||||
|
||||
async reopenLastClosedEditor(): Promise<void> { }
|
||||
async goForward(): Promise<void> { }
|
||||
async goBack(): Promise<void> { }
|
||||
async goPrevious(): Promise<void> { }
|
||||
async goLast(): Promise<void> { }
|
||||
removeFromHistory(_input: EditorInput | IResourceEditorInput): void { }
|
||||
clear(): void { }
|
||||
clearRecentlyOpened(): void { }
|
||||
getHistory(): readonly (EditorInput | IResourceEditorInput)[] { return []; }
|
||||
async openNextRecentlyUsedEditor(group?: GroupIdentifier): Promise<void> { }
|
||||
async openPreviouslyUsedEditor(group?: GroupIdentifier): Promise<void> { }
|
||||
getLastActiveWorkspaceRoot(_schemeFilter: string): URI | undefined { return this.root; }
|
||||
getLastActiveFile(_schemeFilter: string): URI | undefined { return undefined; }
|
||||
}
|
||||
|
||||
export class TestWorkingCopy extends Disposable implements IWorkingCopy {
|
||||
|
||||
private readonly _onDidChangeDirty = this._register(new Emitter<void>());
|
||||
|
||||
31
src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts
vendored
Normal file
31
src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
declare module 'vscode' {
|
||||
|
||||
// https://github.com/microsoft/vscode/issues/171173
|
||||
|
||||
export interface EnvironmentVariableMutator {
|
||||
readonly type: EnvironmentVariableMutatorType;
|
||||
readonly value: string;
|
||||
readonly scope: EnvironmentVariableScope | undefined;
|
||||
}
|
||||
|
||||
export interface EnvironmentVariableCollection extends Iterable<[variable: string, mutator: EnvironmentVariableMutator]> {
|
||||
replace(variable: string, value: string, scope?: EnvironmentVariableScope): void;
|
||||
append(variable: string, value: string, scope?: EnvironmentVariableScope): void;
|
||||
prepend(variable: string, value: string, scope?: EnvironmentVariableScope): void;
|
||||
get(variable: string, scope?: EnvironmentVariableScope): EnvironmentVariableMutator | undefined;
|
||||
delete(variable: string, scope?: EnvironmentVariableScope): void;
|
||||
clear(scope?: EnvironmentVariableScope): void;
|
||||
}
|
||||
|
||||
export type EnvironmentVariableScope = {
|
||||
/**
|
||||
* The workspace folder to which this collection applies to. If unspecified, collection applies to all workspace folders.
|
||||
*/
|
||||
workspaceFolder?: WorkspaceFolder;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user