Skip to content

Commit

Permalink
Create capability implementation objects
Browse files Browse the repository at this point in the history
Part of #139396
  • Loading branch information
Tyriar committed Jan 13, 2022
1 parent e1566a3 commit 681ea6e
Show file tree
Hide file tree
Showing 11 changed files with 147 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1174,7 +1174,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {

protected _createProcessManager(): TerminalProcessManager {
const processManager = this._instantiationService.createInstance(TerminalProcessManager, this._instanceId, this._configHelper);
this.capabilities.addCapabilityStore(this._processManager.capabilities);
this.capabilities.add(this._processManager.capabilities);
processManager.onProcessReady(async (e) => {
this._onProcessIdReady.fire(this);
this._initialCwd = await this.getInitialCwd();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { IProcessEnvironment, isMacintosh, isWindows, OperatingSystem, OS } from
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { TerminalCapabilityStore } from 'vs/workbench/contrib/terminal/common/capabilities/terminalCapabilityStore';
import { NaiveCwdDetectionCapability } from 'vs/workbench/contrib/terminal/common/capabilities/naiveCwdDetectionCapability';

/** The amount of time to consider terminal errors to be related to the launch */
const LAUNCHING_DURATION = 500;
Expand Down Expand Up @@ -296,7 +297,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce

// Add any capabilities inherit to the backend
if (this.os === OperatingSystem.Linux || this.os === OperatingSystem.Macintosh) {
this.capabilities.addCapability(TerminalCapability.NaiveCwdDetection);
this.capabilities.add(TerminalCapability.NaiveCwdDetection, new NaiveCwdDetectionCapability());
}

this._dataFilter.newProcess(this._process, reset);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { TerminalCapability } from 'vs/platform/terminal/common/terminal';
import { TerminalCapabilityStore } from 'vs/workbench/contrib/terminal/common/capabilities/terminalCapabilityStore';
import { CommandDetectionCapability } from 'vs/workbench/contrib/terminal/common/capabilities/commandDetectionCapability';

/**
* Shell integration is a feature that enhances the terminal's understanding of what's happening
Expand Down Expand Up @@ -104,7 +105,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati
type = ShellIntegrationInteraction.CommandFinished;
break;
case ShellIntegrationOscPt.EnableShellIntegration:
this.capabilities.addCapability(TerminalCapability.CommandDetection);
this.capabilities.add(TerminalCapability.CommandDetection, new CommandDetectionCapability());
return true;
default:
return false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { TerminalCapability } from 'vs/platform/terminal/common/terminal';
import { ICommandDetectionCapability } from 'vs/workbench/contrib/terminal/common/terminal';

export class CommandDetectionCapability implements ICommandDetectionCapability {
readonly type = TerminalCapability.CommandDetection;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { TerminalCapability } from 'vs/platform/terminal/common/terminal';

export class CwdDetectionCapability {
readonly type = TerminalCapability.CwdDetection;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { TerminalCapability } from 'vs/platform/terminal/common/terminal';

export class NaiveCwdDetectionCapability {
readonly type = TerminalCapability.NaiveCwdDetection;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { TerminalCapability } from 'vs/platform/terminal/common/terminal';

export class PartialCommandDetectionCapability {
readonly type = TerminalCapability.CwdDetection;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,40 @@
import { Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { TerminalCapability } from 'vs/platform/terminal/common/terminal';
import { ITerminalCapabilityStore } from 'vs/workbench/contrib/terminal/common/terminal';
import { ITerminalCapabilityImplMap, ITerminalCapabilityStore } from 'vs/workbench/contrib/terminal/common/terminal';

export class TerminalCapabilityStore extends Disposable implements ITerminalCapabilityStore {
readonly items: TerminalCapability[] = [];
private _map: Map<TerminalCapability, { type: TerminalCapability }> = new Map();

private readonly _onDidRemoveCapability = this._register(new Emitter<TerminalCapability>());
readonly onDidRemoveCapability = this._onDidRemoveCapability.event;
private readonly _onDidAddCapability = this._register(new Emitter<TerminalCapability>());
readonly onDidAddCapability = this._onDidAddCapability.event;

addCapability(capability: TerminalCapability) {
this.items.push(capability);
get items(): IterableIterator<TerminalCapability> {
return this._map.keys();
}

add<T extends TerminalCapability>(capability: T, impl: ITerminalCapabilityImplMap[T]) {
this._map.set(capability, impl);
this._onDidAddCapability.fire(capability);
}

removeCapability(capability: TerminalCapability) {
const index = this.items.indexOf(capability);
if (index === -1) {
get<T extends TerminalCapability>(capability: T): ITerminalCapabilityImplMap[T] | undefined {
// HACK: This isn't totally safe since the Map key and value are not connected
return this._map.get(capability) as ITerminalCapabilityImplMap[T] | undefined;
}

remove(capability: TerminalCapability) {
if (!this._map.has(capability)) {
return;
}
this.items.splice(index, 1);
this._map.delete(capability);
this._onDidRemoveCapability.fire(capability);
}

has(capability: TerminalCapability) {
return this.items.includes(capability);
return this._map.has(capability);
}
}

Expand All @@ -43,18 +51,40 @@ export class TerminalCapabilityStoreMultiplexer extends Disposable implements IT
private readonly _onDidAddCapability = this._register(new Emitter<TerminalCapability>());
readonly onDidAddCapability = this._onDidAddCapability.event;

get items(): readonly TerminalCapability[] {
return this._stores.reduce<TerminalCapability[]>((p, c) => {
p.push(...c.items);
return p;
}, []);
get items(): IterableIterator<TerminalCapability> {
return this._items();
}

has(capability: TerminalCapability) {
return this.items.includes(capability);
private *_items(): IterableIterator<TerminalCapability> {
for (const store of this._stores) {
for (const c of store.items) {
yield c;
}
}
}

has(capability: TerminalCapability): boolean {
for (const store of this._stores) {
for (const c of store.items) {
if (c === capability) {
return true;
}
}
}
return false;
}

get<T extends TerminalCapability>(capability: T): ITerminalCapabilityImplMap[T] | undefined {
for (const store of this._stores) {
const c = store.get(capability);
if (c) {
return c;
}
}
return undefined;
}

addCapabilityStore(store: ITerminalCapabilityStore) {
add(store: ITerminalCapabilityStore) {
this._stores.push(store);
for (const capability of store.items) {
this._onDidAddCapability.fire(capability);
Expand Down
26 changes: 21 additions & 5 deletions src/vs/workbench/contrib/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,17 +402,33 @@ export interface ITerminalProcessManager extends IDisposable {
updateProperty<T extends ProcessPropertyType>(property: T, value: IProcessPropertyMap[T]): void;
}

// TODO: Move to capabilities/
export interface ITerminalCapabilityStore {
readonly items: readonly TerminalCapability[];
readonly items: IterableIterator<TerminalCapability>;
readonly onDidRemoveCapability: Event<TerminalCapability>;
readonly onDidAddCapability: Event<TerminalCapability>;
has(capability: TerminalCapability): boolean;
get<T extends TerminalCapability>(capability: T): ITerminalCapabilityImplMap[T] | undefined;
}

export interface ITerminalCapabilityStoreController {
addCapability(capability: TerminalCapability): void;
removeCapability(capability: TerminalCapability): void;
export interface ITerminalCapabilityImplMap {
[TerminalCapability.CwdDetection]: ICwdDetectionCapability;
[TerminalCapability.CommandDetection]: ICommandDetectionCapability;
[TerminalCapability.NaiveCwdDetection]: INaiveCwdDetectionCapability;
[TerminalCapability.PartialCommandDetection]: IPartialCommandDetectionCapability;
}
export interface ICwdDetectionCapability {
readonly type: TerminalCapability.CwdDetection;
}
export interface ICommandDetectionCapability {
readonly type: TerminalCapability.CommandDetection;
}
export interface INaiveCwdDetectionCapability {
readonly type: TerminalCapability.NaiveCwdDetection;
}
export interface IPartialCommandDetectionCapability {
readonly type: TerminalCapability.PartialCommandDetection;
}


export const enum ProcessState {
// The process has not been initialized yet.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { TerminalCapabilityStore } from 'vs/workbench/contrib/terminal/common/ca
function createInstance(partial?: Partial<ITerminalInstance>): Pick<ITerminalInstance, 'shellLaunchConfig' | 'userHome' | 'cwd' | 'initialCwd' | 'processName' | 'sequence' | 'workspaceFolder' | 'staticTitle' | 'capabilities' | 'title' | 'description'> {
const capabilities = new TerminalCapabilityStore();
if (!isWindows) {
capabilities.addCapability(TerminalCapability.NaiveCwdDetection);
capabilities.add(TerminalCapability.NaiveCwdDetection, null!);
}
return {
shellLaunchConfig: {},
Expand Down Expand Up @@ -169,7 +169,7 @@ suite('Workbench - TerminalInstance', () => {
instantiationService.stub(IWorkspaceContextService, new TestContextService());
capabilities = new TerminalCapabilityStore();
if (!isWindows) {
capabilities.addCapability(TerminalCapability.NaiveCwdDetection);
capabilities.add(TerminalCapability.NaiveCwdDetection, null!);
}

const ROOT_1_URI = getUri(ROOT_1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,30 +24,30 @@ suite('TerminalCapabilityStore', () => {

test('should fire events when capabilities are added', () => {
assertEvents(addEvents, []);
store.addCapability(TerminalCapability.CwdDetection);
store.add(TerminalCapability.CwdDetection, null!);
assertEvents(addEvents, [TerminalCapability.CwdDetection]);
});
test('should fire events when capabilities are removed', async () => {
assertEvents(removeEvents, []);
store.addCapability(TerminalCapability.CwdDetection);
store.add(TerminalCapability.CwdDetection, null!);
assertEvents(removeEvents, []);
store.removeCapability(TerminalCapability.CwdDetection);
store.remove(TerminalCapability.CwdDetection);
assertEvents(removeEvents, [TerminalCapability.CwdDetection]);
});
test('has should return whether a capability is present', () => {
deepStrictEqual(store.has(TerminalCapability.CwdDetection), false);
store.addCapability(TerminalCapability.CwdDetection);
store.add(TerminalCapability.CwdDetection, null!);
deepStrictEqual(store.has(TerminalCapability.CwdDetection), true);
store.removeCapability(TerminalCapability.CwdDetection);
store.remove(TerminalCapability.CwdDetection);
deepStrictEqual(store.has(TerminalCapability.CwdDetection), false);
});
test('items should reflect current state', () => {
deepStrictEqual(store.items, []);
store.addCapability(TerminalCapability.CwdDetection);
store.add(TerminalCapability.CwdDetection, null!);
deepStrictEqual(store.items, [TerminalCapability.CwdDetection]);
store.addCapability(TerminalCapability.NaiveCwdDetection);
store.add(TerminalCapability.NaiveCwdDetection, null!);
deepStrictEqual(store.items, [TerminalCapability.CwdDetection, TerminalCapability.NaiveCwdDetection]);
store.removeCapability(TerminalCapability.CwdDetection);
store.remove(TerminalCapability.CwdDetection);
deepStrictEqual(store.items, [TerminalCapability.NaiveCwdDetection]);
});
});
Expand All @@ -73,52 +73,52 @@ suite('TerminalCapabilityStoreMultiplexer', () => {

test('should fire events when capabilities are enabled', async () => {
assertEvents(addEvents, []);
multiplexer.addCapabilityStore(store1);
multiplexer.addCapabilityStore(store2);
store1.addCapability(TerminalCapability.CwdDetection);
multiplexer.add(store1);
multiplexer.add(store2);
store1.add(TerminalCapability.CwdDetection, null!);
assertEvents(addEvents, [TerminalCapability.CwdDetection]);
store2.addCapability(TerminalCapability.NaiveCwdDetection);
store2.add(TerminalCapability.NaiveCwdDetection, null!);
assertEvents(addEvents, [TerminalCapability.NaiveCwdDetection]);
});
test('should fire events when capabilities are disabled', async () => {
assertEvents(removeEvents, []);
multiplexer.addCapabilityStore(store1);
multiplexer.addCapabilityStore(store2);
store1.addCapability(TerminalCapability.CwdDetection);
store2.addCapability(TerminalCapability.NaiveCwdDetection);
multiplexer.add(store1);
multiplexer.add(store2);
store1.add(TerminalCapability.CwdDetection, null!);
store2.add(TerminalCapability.NaiveCwdDetection, null!);
assertEvents(removeEvents, []);
store1.removeCapability(TerminalCapability.CwdDetection);
store1.remove(TerminalCapability.CwdDetection);
assertEvents(removeEvents, [TerminalCapability.CwdDetection]);
store2.removeCapability(TerminalCapability.NaiveCwdDetection);
store2.remove(TerminalCapability.NaiveCwdDetection);
assertEvents(removeEvents, [TerminalCapability.NaiveCwdDetection]);
});
test('should fire events when stores are added', async () => {
assertEvents(addEvents, []);
store1.addCapability(TerminalCapability.CwdDetection);
store1.add(TerminalCapability.CwdDetection, null!);
assertEvents(addEvents, []);
store2.addCapability(TerminalCapability.NaiveCwdDetection);
multiplexer.addCapabilityStore(store1);
multiplexer.addCapabilityStore(store2);
store2.add(TerminalCapability.NaiveCwdDetection, null!);
multiplexer.add(store1);
multiplexer.add(store2);
assertEvents(addEvents, [TerminalCapability.CwdDetection, TerminalCapability.NaiveCwdDetection]);
});
test('items should return items from all stores', () => {
deepStrictEqual(multiplexer.items, []);
multiplexer.addCapabilityStore(store1);
multiplexer.addCapabilityStore(store2);
store1.addCapability(TerminalCapability.CwdDetection);
multiplexer.add(store1);
multiplexer.add(store2);
store1.add(TerminalCapability.CwdDetection, null!);
deepStrictEqual(multiplexer.items, [TerminalCapability.CwdDetection]);
store1.addCapability(TerminalCapability.CommandDetection);
store2.addCapability(TerminalCapability.NaiveCwdDetection);
store1.add(TerminalCapability.CommandDetection, null!);
store2.add(TerminalCapability.NaiveCwdDetection, null!);
deepStrictEqual(multiplexer.items, [TerminalCapability.CwdDetection, TerminalCapability.CommandDetection, TerminalCapability.NaiveCwdDetection]);
store2.removeCapability(TerminalCapability.NaiveCwdDetection);
store2.remove(TerminalCapability.NaiveCwdDetection);
deepStrictEqual(multiplexer.items, [TerminalCapability.CwdDetection, TerminalCapability.CommandDetection]);
});
test('has should return whether a capability is present', () => {
deepStrictEqual(multiplexer.has(TerminalCapability.CwdDetection), false);
multiplexer.addCapabilityStore(store1);
store1.addCapability(TerminalCapability.CwdDetection);
multiplexer.add(store1);
store1.add(TerminalCapability.CwdDetection, null!);
deepStrictEqual(multiplexer.has(TerminalCapability.CwdDetection), true);
store1.removeCapability(TerminalCapability.CwdDetection);
store1.remove(TerminalCapability.CwdDetection);
deepStrictEqual(multiplexer.has(TerminalCapability.CwdDetection), false);
});
});
Expand Down

0 comments on commit 681ea6e

Please sign in to comment.