Skip to content

Commit

Permalink
Allow to provide description to environment collection
Browse files Browse the repository at this point in the history
Allow to provide description to environment collection
  • Loading branch information
Kartik Raj authored Apr 21, 2023
2 parents 3ec13f0 + cb6d294 commit 5df31a1
Show file tree
Hide file tree
Showing 13 changed files with 188 additions and 32 deletions.
20 changes: 18 additions & 2 deletions src/vs/platform/terminal/common/environmentVariable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,30 @@ export interface IEnvironmentVariableMutator {
// readonly timing?: EnvironmentVariableMutatorTiming;
}

export interface IEnvironmentDescriptionMutator {
readonly description: string | undefined;
readonly scope?: EnvironmentVariableScope;
}

export type EnvironmentVariableScope = {
workspaceFolder?: IWorkspaceFolderData;
};

export interface IEnvironmentVariableCollection {
readonly map: ReadonlyMap<string, IEnvironmentVariableMutator>;
readonly descriptionMap?: ReadonlyMap<string, IEnvironmentDescriptionMutator>;
}

/** [variable, mutator] */
export type ISerializableEnvironmentVariableCollection = [string, IEnvironmentVariableMutator][];

/** [extension, collection] */
export type ISerializableEnvironmentVariableCollections = [string, ISerializableEnvironmentVariableCollection][];
export type ISerializableEnvironmentDescriptionMap = [string, IEnvironmentDescriptionMutator][];
export interface IExtensionOwnedEnvironmentDescriptionMutator extends IEnvironmentDescriptionMutator {
readonly extensionIdentifier: string;
}

/** [extension, collection, description] */
export type ISerializableEnvironmentVariableCollections = [string, ISerializableEnvironmentVariableCollection, ISerializableEnvironmentDescriptionMap][];

export interface IExtensionOwnedEnvironmentVariableMutator extends IEnvironmentVariableMutator {
readonly extensionIdentifier: string;
Expand All @@ -61,6 +72,11 @@ export interface IMergedEnvironmentVariableCollection {
* @param scope The scope to get the variable map for. If undefined, the global scope is used.
*/
getVariableMap(scope: EnvironmentVariableScope | undefined): Map<string, IExtensionOwnedEnvironmentVariableMutator[]>;
/**
* Gets the description map for a given scope.
* @param scope The scope to get the description map for. If undefined, the global scope is used.
*/
getDescriptionMap(scope: EnvironmentVariableScope | undefined): Map<string, string | undefined>;
/**
* Applies this collection to a process environment.
* @param variableResolver An optional function to use to resolve variables within the
Expand Down
47 changes: 45 additions & 2 deletions src/vs/platform/terminal/common/environmentVariableCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { IProcessEnvironment, isWindows } from 'vs/base/common/platform';
import { EnvironmentVariableMutatorType, EnvironmentVariableScope, IEnvironmentVariableCollection, IExtensionOwnedEnvironmentVariableMutator, IMergedEnvironmentVariableCollection, IMergedEnvironmentVariableCollectionDiff } from 'vs/platform/terminal/common/environmentVariable';
import { EnvironmentVariableMutatorType, EnvironmentVariableScope, IEnvironmentVariableCollection, IExtensionOwnedEnvironmentDescriptionMutator, IExtensionOwnedEnvironmentVariableMutator, IMergedEnvironmentVariableCollection, IMergedEnvironmentVariableCollectionDiff } from 'vs/platform/terminal/common/environmentVariable';

type VariableResolver = (str: string) => Promise<string>;

Expand All @@ -16,11 +16,13 @@ type VariableResolver = (str: string) => Promise<string>;

export class MergedEnvironmentVariableCollection implements IMergedEnvironmentVariableCollection {
private readonly map: Map<string, IExtensionOwnedEnvironmentVariableMutator[]> = new Map();
private readonly descriptionMap: Map<string, IExtensionOwnedEnvironmentDescriptionMutator[]> = new Map();

constructor(
readonly collections: ReadonlyMap<string, IEnvironmentVariableCollection>,
) {
collections.forEach((collection, extensionIdentifier) => {
this.populateDescriptionMap(collection, extensionIdentifier);
const it = collection.map.entries();
let next = it.next();
while (!next.done) {
Expand Down Expand Up @@ -137,10 +139,51 @@ export class MergedEnvironmentVariableCollection implements IMergedEnvironmentVa
});
return result;
}

getDescriptionMap(scope: EnvironmentVariableScope | undefined): Map<string, string | undefined> {
const result = new Map<string, string | undefined>();
this.descriptionMap.forEach((mutators, _key) => {
const filteredMutators = mutators.filter(m => filterScope(m, scope));
if (filteredMutators.length > 0) {
// There should be exactly one description per extension per scope.
result.set(filteredMutators[0].extensionIdentifier, filteredMutators[0].description);
}
});
return result;
}

private populateDescriptionMap(collection: IEnvironmentVariableCollection, extensionIdentifier: string): void {
if (!collection.descriptionMap) {
return;
}
const it = collection.descriptionMap.entries();
let next = it.next();
while (!next.done) {
const mutator = next.value[1];
const key = next.value[0];
let entry = this.descriptionMap.get(key);
if (!entry) {
entry = [];
this.descriptionMap.set(key, entry);
}
const extensionMutator = {
extensionIdentifier,
scope: mutator.scope,
description: mutator.description
};
if (!extensionMutator.scope) {
delete extensionMutator.scope; // Convenient for tests
}
entry.push(extensionMutator);

next = it.next();
}

}
}

function filterScope(
mutator: IExtensionOwnedEnvironmentVariableMutator,
mutator: IExtensionOwnedEnvironmentVariableMutator | IExtensionOwnedEnvironmentDescriptionMutator,
scope: EnvironmentVariableScope | undefined
): boolean {
if (!mutator.scope) {
Expand Down
16 changes: 13 additions & 3 deletions src/vs/platform/terminal/common/environmentVariableShared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,40 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { IEnvironmentVariableCollection, IEnvironmentVariableMutator, ISerializableEnvironmentVariableCollection, ISerializableEnvironmentVariableCollections } from 'vs/platform/terminal/common/environmentVariable';
import { IEnvironmentDescriptionMutator, IEnvironmentVariableCollection, IEnvironmentVariableMutator, ISerializableEnvironmentDescriptionMap as ISerializableEnvironmentDescriptionMap, ISerializableEnvironmentVariableCollection, ISerializableEnvironmentVariableCollections } from 'vs/platform/terminal/common/environmentVariable';

// This file is shared between the renderer and extension host

export function serializeEnvironmentVariableCollection(collection: ReadonlyMap<string, IEnvironmentVariableMutator>): ISerializableEnvironmentVariableCollection {
return [...collection.entries()];
}

export function serializeEnvironmentDescriptionMap(descriptionMap: ReadonlyMap<string, IEnvironmentDescriptionMutator> | undefined): ISerializableEnvironmentDescriptionMap {
return descriptionMap ? [...descriptionMap.entries()] : [];
}

export function deserializeEnvironmentVariableCollection(
serializedCollection: ISerializableEnvironmentVariableCollection
): Map<string, IEnvironmentVariableMutator> {
return new Map<string, IEnvironmentVariableMutator>(serializedCollection);
}

export function deserializeEnvironmentDescriptionMap(
serializableEnvironmentDescription: ISerializableEnvironmentDescriptionMap | undefined
): Map<string, IEnvironmentDescriptionMutator> {
return new Map<string, IEnvironmentDescriptionMutator>(serializableEnvironmentDescription ?? []);
}

export function serializeEnvironmentVariableCollections(collections: ReadonlyMap<string, IEnvironmentVariableCollection>): ISerializableEnvironmentVariableCollections {
return Array.from(collections.entries()).map(e => {
return [e[0], serializeEnvironmentVariableCollection(e[1].map)];
return [e[0], serializeEnvironmentVariableCollection(e[1].map), serializeEnvironmentDescriptionMap(e[1].descriptionMap)];
});
}

export function deserializeEnvironmentVariableCollections(
serializedCollection: ISerializableEnvironmentVariableCollections
): Map<string, IEnvironmentVariableCollection> {
return new Map<string, IEnvironmentVariableCollection>(serializedCollection.map(e => {
return [e[0], { map: deserializeEnvironmentVariableCollection(e[1]) }];
return [e[0], { map: deserializeEnvironmentVariableCollection(e[1]), descriptionMap: deserializeEnvironmentDescriptionMap(e[2]) }];
}));
}
6 changes: 3 additions & 3 deletions src/vs/server/node/remoteTerminalChannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { createURITransformer } from 'vs/workbench/api/node/uriTransformer';
import { CLIServerBase, ICommandsExecuter } from 'vs/workbench/api/node/extHostCLIServer';
import { IEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable';
import { MergedEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariableCollection';
import { deserializeEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariableShared';
import { deserializeEnvironmentDescriptionMap, deserializeEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariableShared';
import { ICreateTerminalProcessArguments, ICreateTerminalProcessResult, IWorkspaceFolderData } from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel';
import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver';
Expand Down Expand Up @@ -233,8 +233,8 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel<
// Apply extension environment variable collections to the environment
if (!shellLaunchConfig.strictEnv) {
const entries: [string, IEnvironmentVariableCollection][] = [];
for (const [k, v] of args.envVariableCollections) {
entries.push([k, { map: deserializeEnvironmentVariableCollection(v) }]);
for (const [k, v, d] of args.envVariableCollections) {
entries.push([k, { map: deserializeEnvironmentVariableCollection(v), descriptionMap: deserializeEnvironmentDescriptionMap(d) }]);
}
const envVariableCollections = new Map<string, IEnvironmentVariableCollection>(entries);
const mergedCollection = new MergedEnvironmentVariableCollection(envVariableCollections);
Expand Down
9 changes: 5 additions & 4 deletions src/vs/workbench/api/browser/mainThreadTerminalService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBu
import { ITerminalEditorService, ITerminalExternalLinkProvider, ITerminalGroupService, ITerminalInstance, ITerminalLink, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { TerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy';
import { IEnvironmentVariableService } from 'vs/workbench/contrib/terminal/common/environmentVariable';
import { deserializeEnvironmentVariableCollection, serializeEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariableShared';
import { deserializeEnvironmentDescriptionMap, deserializeEnvironmentVariableCollection, serializeEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariableShared';
import { IStartExtensionTerminalRequest, ITerminalProcessExtHostProxy, ITerminalProfileResolverService, ITerminalProfileService } from 'vs/workbench/contrib/terminal/common/terminal';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { withNullAsUndefined } from 'vs/base/common/types';
Expand All @@ -25,7 +25,7 @@ import { Promises } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ITerminalCommand } from 'vs/platform/terminal/common/capabilities/capabilities';
import { TerminalQuickFixType } from 'vs/workbench/api/common/extHostTypes';
import { ISerializableEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable';
import { ISerializableEnvironmentDescriptionMap, ISerializableEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable';
import { ITerminalLinkProviderService } from 'vs/workbench/contrib/terminalContrib/links/browser/links';
import { ITerminalQuickFixService, ITerminalQuickFixOptions, ITerminalQuickFix } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix';

Expand Down Expand Up @@ -400,11 +400,12 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
return terminal;
}

$setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined): void {
$setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined, descriptionMap: ISerializableEnvironmentDescriptionMap): void {
if (collection) {
const translatedCollection = {
persistent,
map: deserializeEnvironmentVariableCollection(collection)
map: deserializeEnvironmentVariableCollection(collection),
descriptionMap: deserializeEnvironmentDescriptionMap(descriptionMap)
};
this._environmentVariableService.set(extensionIdentifier, translatedCollection);
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import * as quickInput from 'vs/platform/quickinput/common/quickInput';
import { IRemoteConnectionData, TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { ClassifiedEvent, IGDPRProperty, OmitMetadata, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings';
import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry';
import { ISerializableEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable';
import { ISerializableEnvironmentDescriptionMap, ISerializableEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable';
import { ICreateContributedTerminalProfileOptions, IProcessProperty, IShellLaunchConfigDto, ITerminalEnvironment, ITerminalLaunchError, ITerminalProfile, TerminalExitReason, TerminalLocation } from 'vs/platform/terminal/common/terminal';
import { ProvidedPortAttributes, TunnelCreationOptions, TunnelOptions, TunnelPrivacyId, TunnelProviderFeatures } from 'vs/platform/tunnel/common/tunnel';
import { EditSessionIdentityMatch } from 'vs/platform/workspace/common/editSessions';
Expand Down Expand Up @@ -492,7 +492,7 @@ export interface MainThreadTerminalServiceShape extends IDisposable {
$unregisterProfileProvider(id: string): void;
$registerQuickFixProvider(id: string, extensionIdentifier: string): void;
$unregisterQuickFixProvider(id: string): void;
$setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined): void;
$setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined, descriptionMap: ISerializableEnvironmentDescriptionMap): void;

// Process
$sendProcessData(terminalId: number, data: string): void;
Expand Down
32 changes: 29 additions & 3 deletions src/vs/workbench/api/common/extHostTerminalService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import { Disposable as VSCodeDisposable, EnvironmentVariableMutatorType, Termina
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { localize } from 'vs/nls';
import { NotSupportedError } from 'vs/base/common/errors';
import { serializeEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariableShared';
import { serializeEnvironmentDescriptionMap, serializeEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariableShared';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { generateUuid } from 'vs/base/common/uuid';
import { IEnvironmentVariableMutator, ISerializableEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable';
import { IEnvironmentDescriptionMutator, 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';
Expand Down Expand Up @@ -834,7 +834,8 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I

private _syncEnvironmentVariableCollection(extensionIdentifier: string, collection: EnvironmentVariableCollection): void {
const serialized = serializeEnvironmentVariableCollection(collection.map);
this._proxy.$setEnvironmentVariableCollection(extensionIdentifier, collection.persistent, serialized.length === 0 ? undefined : serialized);
const serializedDescription = serializeEnvironmentDescriptionMap(collection.descriptionMap);
this._proxy.$setEnvironmentVariableCollection(extensionIdentifier, collection.persistent, serialized.length === 0 ? undefined : serialized, serializedDescription);
}

public $initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void {
Expand Down Expand Up @@ -868,6 +869,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I

class EnvironmentVariableCollection implements vscode.EnvironmentVariableCollection {
readonly map: Map<string, IEnvironmentVariableMutator> = new Map();
readonly descriptionMap: Map<string, IEnvironmentDescriptionMutator> = new Map();
private _persistent: boolean = true;

public get persistent(): boolean { return this._persistent; }
Expand Down Expand Up @@ -959,11 +961,35 @@ class EnvironmentVariableCollection implements vscode.EnvironmentVariableCollect
this.map.delete(key);
}
}
this.clearDescription(scope);
} else {
this.map.clear();
this.descriptionMap.clear();
}
this._onDidChangeCollection.fire();
}

setDescription(description: string | vscode.MarkdownString | undefined, scope?: vscode.EnvironmentVariableScope): void {
const key = this.getKey('', scope);
const current = this.descriptionMap.get(key);
if (!current || current.description !== description) {
let descriptionStr: string | undefined;
if (typeof description === 'string') {
descriptionStr = description;
} else {
// Only take the description before the first `\n\n`, so that the description doesn't mess up the UI
descriptionStr = description?.value.split('\n\n')[0];
}
const value: IEnvironmentDescriptionMutator = { description: descriptionStr, scope };
this.descriptionMap.set(key, value);
this._onDidChangeCollection.fire();
}
}

private clearDescription(scope?: vscode.EnvironmentVariableScope): void {
const key = this.getKey('', scope);
this.descriptionMap.delete(key);
}
}

export class WorkerExtHostTerminalService extends BaseExtHostTerminalService {
Expand Down
Loading

0 comments on commit 5df31a1

Please sign in to comment.