Skip to content

Commit bb5dbb8

Browse files
Expose the API via exports instead of editor commands (#2855)
* Redo API correctly * up timeout
1 parent 1ba62de commit bb5dbb8

File tree

4 files changed

+127
-95
lines changed

4 files changed

+127
-95
lines changed

src/features/ExternalApi.ts

Lines changed: 66 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
*--------------------------------------------------------*/
44
import * as vscode from "vscode";
55
import { v4 as uuidv4 } from 'uuid';
6-
import { LanguageClient } from "vscode-languageclient";
76
import { LanguageClientConsumer } from "../languageClientConsumer";
87
import { Logger } from "../logging";
98
import { SessionManager } from "../session";
@@ -15,71 +14,44 @@ export interface IExternalPowerShellDetails {
1514
architecture: string;
1615
}
1716

18-
export class ExternalApiFeature extends LanguageClientConsumer {
19-
private commands: vscode.Disposable[];
17+
export interface IPowerShellExtensionClient {
18+
registerExternalExtension(id: string, apiVersion?: string): string;
19+
unregisterExternalExtension(uuid: string): boolean;
20+
getPowerShellVersionDetails(uuid: string): Promise<IExternalPowerShellDetails>;
21+
}
22+
23+
/*
24+
In order to use this in a Visual Studio Code extension, you can do the following:
25+
26+
const powershellExtension = vscode.extensions.getExtension<IPowerShellExtensionClient>("ms-vscode.PowerShell-Preview");
27+
const powerShellExtensionClient = powershellExtension!.exports as IPowerShellExtensionClient;
28+
29+
NOTE: At some point, we should release a helper npm package that wraps the API and does:
30+
* Discovery of what extension they have installed: PowerShell or PowerShell Preview
31+
* Manages session id for you
32+
33+
*/
34+
export class ExternalApiFeature extends LanguageClientConsumer implements IPowerShellExtensionClient {
2035
private static readonly registeredExternalExtension: Map<string, IExternalExtension> = new Map<string, IExternalExtension>();
2136

2237
constructor(private sessionManager: SessionManager, private log: Logger) {
2338
super();
24-
this.commands = [
25-
/*
26-
DESCRIPTION:
27-
Registers your extension to allow usage of the external API. The returns
28-
a session UUID that will need to be passed in to subsequent API calls.
29-
30-
USAGE:
31-
vscode.commands.executeCommand(
32-
"PowerShell.RegisterExternalExtension",
33-
"ms-vscode.PesterTestExplorer" // the name of the extension using us
34-
"v1"); // API Version.
35-
36-
RETURNS:
37-
string session uuid
38-
*/
39-
vscode.commands.registerCommand("PowerShell.RegisterExternalExtension", (id: string, apiVersion: string = 'v1'): string =>
40-
this.registerExternalExtension(id, apiVersion)),
41-
42-
/*
43-
DESCRIPTION:
44-
Unregisters a session that an extension has. This returns
45-
true if it succeeds or throws if it fails.
46-
47-
USAGE:
48-
vscode.commands.executeCommand(
49-
"PowerShell.UnregisterExternalExtension",
50-
"uuid"); // the uuid from above for tracking purposes
51-
52-
RETURNS:
53-
true if it worked, otherwise throws an error.
54-
*/
55-
vscode.commands.registerCommand("PowerShell.UnregisterExternalExtension", (uuid: string = ""): boolean =>
56-
this.unregisterExternalExtension(uuid)),
57-
58-
/*
59-
DESCRIPTION:
60-
This will fetch the version details of the PowerShell used to start
61-
PowerShell Editor Services in the PowerShell extension.
62-
63-
USAGE:
64-
vscode.commands.executeCommand(
65-
"PowerShell.GetPowerShellVersionDetails",
66-
"uuid"); // the uuid from above for tracking purposes
67-
68-
RETURNS:
69-
An IPowerShellVersionDetails which consists of:
70-
{
71-
version: string;
72-
displayVersion: string;
73-
edition: string;
74-
architecture: string;
75-
}
76-
*/
77-
vscode.commands.registerCommand("PowerShell.GetPowerShellVersionDetails", (uuid: string = ""): Promise<IExternalPowerShellDetails> =>
78-
this.getPowerShellVersionDetails(uuid)),
79-
]
8039
}
8140

82-
private registerExternalExtension(id: string, apiVersion: string = 'v1'): string {
41+
/*
42+
DESCRIPTION:
43+
Registers your extension to allow usage of the external API. The returns
44+
a session UUID that will need to be passed in to subsequent API calls.
45+
46+
USAGE:
47+
powerShellExtensionClient.registerExternalExtension(
48+
"ms-vscode.PesterTestExplorer" // the name of the extension using us
49+
"v1"); // API Version.
50+
51+
RETURNS:
52+
string session uuid
53+
*/
54+
public registerExternalExtension(id: string, apiVersion: string = 'v1'): string {
8355
this.log.writeDiagnostic(`Registering extension '${id}' for use with API version '${apiVersion}'.`);
8456

8557
for (const [_, externalExtension] of ExternalApiFeature.registeredExternalExtension) {
@@ -107,18 +79,48 @@ export class ExternalApiFeature extends LanguageClientConsumer {
10779
return uuid;
10880
}
10981

110-
private unregisterExternalExtension(uuid: string = ""): boolean {
82+
/*
83+
DESCRIPTION:
84+
Unregisters a session that an extension has. This returns
85+
true if it succeeds or throws if it fails.
86+
87+
USAGE:
88+
powerShellExtensionClient.unregisterExternalExtension(
89+
"uuid"); // the uuid from above for tracking purposes
90+
91+
RETURNS:
92+
true if it worked, otherwise throws an error.
93+
*/
94+
public unregisterExternalExtension(uuid: string = ""): boolean {
11195
this.log.writeDiagnostic(`Unregistering extension with session UUID: ${uuid}`);
11296
if (!ExternalApiFeature.registeredExternalExtension.delete(uuid)) {
11397
throw new Error(`No extension registered with session UUID: ${uuid}`);
11498
}
11599
return true;
116100
}
117101

118-
private async getPowerShellVersionDetails(uuid: string = ""): Promise<IExternalPowerShellDetails> {
102+
/*
103+
DESCRIPTION:
104+
This will fetch the version details of the PowerShell used to start
105+
PowerShell Editor Services in the PowerShell extension.
106+
107+
USAGE:
108+
powerShellExtensionClient.getPowerShellVersionDetails(
109+
"uuid"); // the uuid from above for tracking purposes
110+
111+
RETURNS:
112+
An IPowerShellVersionDetails which consists of:
113+
{
114+
version: string;
115+
displayVersion: string;
116+
edition: string;
117+
architecture: string;
118+
}
119+
*/
120+
public async getPowerShellVersionDetails(uuid: string = ""): Promise<IExternalPowerShellDetails> {
119121
if (!ExternalApiFeature.registeredExternalExtension.has(uuid)) {
120122
throw new Error(
121-
"UUID provided was invalid, make sure you execute the 'PowerShell.GetPowerShellVersionDetails' command and pass in the UUID that it returns to subsequent command executions.");
123+
"UUID provided was invalid, make sure you ran the 'powershellExtensionClient.registerExternalExtension(extensionId)' method and pass in the UUID that it returns to subsequent methods.");
122124
}
123125

124126
// TODO: When we have more than one API version, make sure to include a check here.
@@ -137,9 +139,7 @@ export class ExternalApiFeature extends LanguageClientConsumer {
137139
}
138140

139141
public dispose() {
140-
for (const command of this.commands) {
141-
command.dispose();
142-
}
142+
// Nothing to dispose.
143143
}
144144
}
145145

src/main.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { DebugSessionFeature } from "./features/DebugSession";
1515
import { ExamplesFeature } from "./features/Examples";
1616
import { ExpandAliasFeature } from "./features/ExpandAlias";
1717
import { ExtensionCommandsFeature } from "./features/ExtensionCommands";
18-
import { ExternalApiFeature } from "./features/ExternalApi";
18+
import { ExternalApiFeature, IPowerShellExtensionClient } from "./features/ExternalApi";
1919
import { FindModuleFeature } from "./features/FindModule";
2020
import { GenerateBugReportFeature } from "./features/GenerateBugReport";
2121
import { GetCommandsFeature } from "./features/GetCommands";
@@ -54,7 +54,7 @@ const documentSelector: DocumentSelector = [
5454
{ language: "powershell", scheme: "untitled" },
5555
];
5656

57-
export function activate(context: vscode.ExtensionContext): void {
57+
export function activate(context: vscode.ExtensionContext): IPowerShellExtensionClient {
5858
// create telemetry reporter on extension activation
5959
telemetryReporter = new TelemetryReporter(PackageJSON.name, PackageJSON.version, AI_KEY);
6060

@@ -147,6 +147,8 @@ export function activate(context: vscode.ExtensionContext): void {
147147
new SpecifyScriptArgsFeature(context),
148148
]
149149

150+
const externalApi = new ExternalApiFeature(sessionManager, logger);
151+
150152
// Features and command registrations that require language client
151153
languageClientConsumers = [
152154
new ConsoleFeature(logger),
@@ -162,7 +164,7 @@ export function activate(context: vscode.ExtensionContext): void {
162164
new HelpCompletionFeature(logger),
163165
new CustomViewsFeature(),
164166
new PickRunspaceFeature(),
165-
new ExternalApiFeature(sessionManager, logger)
167+
externalApi
166168
];
167169

168170
// Notebook UI is only supported in VS Code Insiders.
@@ -188,6 +190,12 @@ export function activate(context: vscode.ExtensionContext): void {
188190
if (extensionSettings.startAutomatically) {
189191
sessionManager.start();
190192
}
193+
194+
return {
195+
registerExternalExtension: (id: string, apiVersion: string = 'v1') => externalApi.registerExternalExtension(id, apiVersion),
196+
unregisterExternalExtension: uuid => externalApi.unregisterExternalExtension(uuid),
197+
getPowerShellVersionDetails: uuid => externalApi.getPowerShellVersionDetails(uuid),
198+
};
191199
}
192200

193201
function checkForUpdatedVersion(context: vscode.ExtensionContext, version: string) {

test/features/ExternalApi.test.ts

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,37 @@
33
*--------------------------------------------------------*/
44
import * as assert from "assert";
55
import * as vscode from "vscode";
6-
import { beforeEach, afterEach } from "mocha";
7-
import { IExternalPowerShellDetails } from "../../src/features/ExternalApi";
6+
import { before, beforeEach, afterEach } from "mocha";
7+
import { IExternalPowerShellDetails, IPowerShellExtensionClient } from "../../src/features/ExternalApi";
88

99
const testExtensionId = "ms-vscode.powershell-preview";
1010

1111
suite("ExternalApi feature - Registration API", () => {
12-
test("It can register and unregister an extension", async () => {
13-
const sessionId: string = await vscode.commands.executeCommand("PowerShell.RegisterExternalExtension", testExtensionId);
12+
let powerShellExtensionClient: IPowerShellExtensionClient;
13+
before(async () => {
14+
const powershellExtension = vscode.extensions.getExtension<IPowerShellExtensionClient>(testExtensionId);
15+
if (!powershellExtension.isActive) {
16+
powerShellExtensionClient = await powershellExtension.activate();
17+
return;
18+
}
19+
powerShellExtensionClient = powershellExtension!.exports as IPowerShellExtensionClient;
20+
});
21+
22+
test("It can register and unregister an extension", () => {
23+
const sessionId: string = powerShellExtensionClient.registerExternalExtension(testExtensionId);
1424
assert.notStrictEqual(sessionId , "");
1525
assert.notStrictEqual(sessionId , null);
1626
assert.strictEqual(
17-
await vscode.commands.executeCommand("PowerShell.UnregisterExternalExtension", sessionId),
27+
powerShellExtensionClient.unregisterExternalExtension(sessionId),
1828
true);
1929
});
2030

21-
test("It can register and unregister an extension with a version", async () => {
22-
const sessionId: string = await vscode.commands.executeCommand("PowerShell.RegisterExternalExtension", "ms-vscode.powershell-preview", "v2");
31+
test("It can register and unregister an extension with a version", () => {
32+
const sessionId: string = powerShellExtensionClient.registerExternalExtension(testExtensionId, "v2");
2333
assert.notStrictEqual(sessionId , "");
2434
assert.notStrictEqual(sessionId , null);
2535
assert.strictEqual(
26-
await vscode.commands.executeCommand("PowerShell.UnregisterExternalExtension", sessionId),
36+
powerShellExtensionClient.unregisterExternalExtension(sessionId),
2737
true);
2838
});
2939

@@ -32,41 +42,55 @@ suite("ExternalApi feature - Registration API", () => {
3242
*/
3343
test("API fails if not registered", async () => {
3444
assert.rejects(
35-
async () => await vscode.commands.executeCommand("PowerShell.GetPowerShellVersionDetails"),
36-
"UUID provided was invalid, make sure you execute the 'PowerShell.RegisterExternalExtension' command and pass in the UUID that it returns to subsequent command executions.");
45+
async () => await powerShellExtensionClient.getPowerShellVersionDetails(""),
46+
"UUID provided was invalid, make sure you ran the 'powershellExtensionClient.registerExternalExtension(extensionId)' method and pass in the UUID that it returns to subsequent methods.");
3747
});
3848

3949
test("It can't register the same extension twice", async () => {
40-
const sessionId: string = await vscode.commands.executeCommand("PowerShell.RegisterExternalExtension", testExtensionId);
50+
const sessionId: string = powerShellExtensionClient.registerExternalExtension(testExtensionId);
4151
try {
42-
assert.rejects(
43-
async () => await vscode.commands.executeCommand("PowerShell.RegisterExternalExtension", testExtensionId),
44-
`The extension '${testExtensionId}' is already registered.`);
52+
assert.throws(
53+
() => powerShellExtensionClient.registerExternalExtension(testExtensionId),
54+
{
55+
message: `The extension '${testExtensionId}' is already registered.`
56+
});
4557
} finally {
46-
await vscode.commands.executeCommand("PowerShell.UnregisterExternalExtension", sessionId);
58+
powerShellExtensionClient.unregisterExternalExtension(sessionId);
4759
}
4860
});
4961

5062
test("It can't unregister an extension that isn't registered", async () => {
51-
assert.rejects(
52-
async () => await vscode.commands.executeCommand("PowerShell.RegisterExternalExtension", "not-real"),
53-
`No extension registered with session UUID: not-real`);
54-
});
63+
assert.throws(
64+
() => powerShellExtensionClient.unregisterExternalExtension("not-real"),
65+
{
66+
message: `No extension registered with session UUID: not-real`
67+
});
68+
});
5569
});
5670

5771
suite("ExternalApi feature - Other APIs", () => {
5872
let sessionId: string;
73+
let powerShellExtensionClient: IPowerShellExtensionClient;
74+
75+
before(async () => {
76+
const powershellExtension = vscode.extensions.getExtension<IPowerShellExtensionClient>(testExtensionId);
77+
if (!powershellExtension.isActive) {
78+
powerShellExtensionClient = await powershellExtension.activate();
79+
return;
80+
}
81+
powerShellExtensionClient = powershellExtension!.exports as IPowerShellExtensionClient;
82+
});
5983

60-
beforeEach(async () => {
61-
sessionId = await vscode.commands.executeCommand("PowerShell.RegisterExternalExtension", "ms-vscode.powershell-preview");
84+
beforeEach(() => {
85+
sessionId = powerShellExtensionClient.registerExternalExtension("ms-vscode.powershell-preview");
6286
});
6387

64-
afterEach(async () => {
65-
await vscode.commands.executeCommand("PowerShell.UnregisterExternalExtension", sessionId);
88+
afterEach(() => {
89+
powerShellExtensionClient.unregisterExternalExtension(sessionId);
6690
});
6791

6892
test("It can get PowerShell version details", async () => {
69-
const versionDetails: IExternalPowerShellDetails = await vscode.commands.executeCommand("PowerShell.GetPowerShellVersionDetails", sessionId);
93+
const versionDetails: IExternalPowerShellDetails = await powerShellExtensionClient.getPowerShellVersionDetails(sessionId);
7094

7195
assert.notStrictEqual(versionDetails.architecture, "");
7296
assert.notStrictEqual(versionDetails.architecture, null);

test/features/ISECompatibility.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ suite("ISECompatibility feature", () => {
2323
const currently = vscode.workspace.getConfiguration(iseSetting.path).get(iseSetting.name);
2424
assert.notEqual(currently, iseSetting.value);
2525
}
26-
});
26+
}).timeout(10000);
2727
test("It leaves Theme after being changed after enabling ISE Mode", async () => {
2828
await vscode.commands.executeCommand("PowerShell.EnableISEMode");
2929
assert.equal(vscode.workspace.getConfiguration("workbench").get("colorTheme"), "PowerShell ISE");
@@ -35,5 +35,5 @@ suite("ISECompatibility feature", () => {
3535
assert.notEqual(currently, iseSetting.value);
3636
}
3737
assert.equal(vscode.workspace.getConfiguration("workbench").get("colorTheme"), "Dark+");
38-
});
38+
}).timeout(10000);
3939
});

0 commit comments

Comments
 (0)