-
Notifications
You must be signed in to change notification settings - Fork 511
/
Copy pathExternalApi.ts
177 lines (147 loc) · 6.56 KB
/
ExternalApi.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import * as vscode from "vscode";
import { v4 as uuidv4 } from 'uuid';
import { LanguageClientConsumer } from "../languageClientConsumer";
import { Logger } from "../logging";
import { SessionManager } from "../session";
export interface IExternalPowerShellDetails {
exePath: string;
version: string;
displayName: string;
architecture: string;
}
export interface IPowerShellExtensionClient {
registerExternalExtension(id: string, apiVersion?: string): string;
unregisterExternalExtension(uuid: string): boolean;
getPowerShellVersionDetails(uuid: string): Promise<IExternalPowerShellDetails>;
waitUntilStarted(uuid: string): Promise<void>;
}
/*
In order to use this in a Visual Studio Code extension, you can do the following:
const powershellExtension = vscode.extensions.getExtension<IPowerShellExtensionClient>("ms-vscode.PowerShell-Preview");
const powerShellExtensionClient = powershellExtension!.exports as IPowerShellExtensionClient;
NOTE: At some point, we should release a helper npm package that wraps the API and does:
* Discovery of what extension they have installed: PowerShell or PowerShell Preview
* Manages session id for you
*/
export class ExternalApiFeature extends LanguageClientConsumer implements IPowerShellExtensionClient {
private static readonly registeredExternalExtension: Map<string, IExternalExtension> = new Map<string, IExternalExtension>();
constructor(
private extensionContext: vscode.ExtensionContext,
private sessionManager: SessionManager,
private log: Logger) {
super();
}
/*
DESCRIPTION:
Registers your extension to allow usage of the external API. The returns
a session UUID that will need to be passed in to subsequent API calls.
USAGE:
powerShellExtensionClient.registerExternalExtension(
"ms-vscode.PesterTestExplorer" // the name of the extension using us
"v1"); // API Version.
RETURNS:
string session uuid
*/
public registerExternalExtension(id: string, apiVersion: string = 'v1'): string {
this.log.writeDiagnostic(`Registering extension '${id}' for use with API version '${apiVersion}'.`);
for (const [_, externalExtension] of ExternalApiFeature.registeredExternalExtension) {
if (externalExtension.id === id) {
const message = `The extension '${id}' is already registered.`;
this.log.writeWarning(message);
throw new Error(message);
}
}
if (!vscode.extensions.all.some(ext => ext.id === id)) {
throw new Error(`No extension installed with id '${id}'. You must use a valid extension id.`);
}
// These are only allowed to be used in our unit tests.
if ((id === "ms-vscode.powershell" || id === "ms-vscode.powershell-preview")
&& !(this.extensionContext.extensionMode === vscode.ExtensionMode.Test)) {
throw new Error("You can't use the PowerShell extension's id in this registration.");
}
const uuid = uuidv4();
ExternalApiFeature.registeredExternalExtension.set(uuid, {
id,
apiVersion
});
return uuid;
}
/*
DESCRIPTION:
Unregisters a session that an extension has. This returns
true if it succeeds or throws if it fails.
USAGE:
powerShellExtensionClient.unregisterExternalExtension(
"uuid"); // the uuid from above for tracking purposes
RETURNS:
true if it worked, otherwise throws an error.
*/
public unregisterExternalExtension(uuid: string = ""): boolean {
this.log.writeDiagnostic(`Unregistering extension with session UUID: ${uuid}`);
if (!ExternalApiFeature.registeredExternalExtension.delete(uuid)) {
throw new Error(`No extension registered with session UUID: ${uuid}`);
}
return true;
}
private getRegisteredExtension(uuid: string = ""): IExternalExtension {
if (!ExternalApiFeature.registeredExternalExtension.has(uuid)) {
throw new Error(
"UUID provided was invalid, make sure you ran the 'powershellExtensionClient.registerExternalExtension(extensionId)' method and pass in the UUID that it returns to subsequent methods.");
}
// TODO: When we have more than one API version, make sure to include a check here.
return ExternalApiFeature.registeredExternalExtension.get(uuid);
}
/*
DESCRIPTION:
This will fetch the version details of the PowerShell used to start
PowerShell Editor Services in the PowerShell extension.
USAGE:
powerShellExtensionClient.getPowerShellVersionDetails(
"uuid"); // the uuid from above for tracking purposes
RETURNS:
An IPowerShellVersionDetails which consists of:
{
version: string;
displayVersion: string;
edition: string;
architecture: string;
}
*/
public async getPowerShellVersionDetails(uuid: string = ""): Promise<IExternalPowerShellDetails> {
const extension = this.getRegisteredExtension(uuid);
this.log.writeDiagnostic(`Extension '${extension.id}' called 'getPowerShellVersionDetails'`);
await this.sessionManager.waitUntilStarted();
const versionDetails = this.sessionManager.getPowerShellVersionDetails();
return {
exePath: this.sessionManager.PowerShellExeDetails.exePath,
version: versionDetails.version,
displayName: this.sessionManager.PowerShellExeDetails.displayName, // comes from the Session Menu
architecture: versionDetails.architecture
};
}
/*
DESCRIPTION:
This will wait until the extension's PowerShell session is started.
USAGE:
powerShellExtensionClient.waitUntilStarted(
"uuid"); // the uuid from above for tracking purposes
RETURNS:
A void promise that resolves only once the extension is started.
If the extension is not started by some mechanism
then this will wait indefinitely.
*/
public async waitUntilStarted(uuid: string = ""): Promise<void> {
const extension = this.getRegisteredExtension(uuid);
this.log.writeDiagnostic(`Extension '${extension.id}' called 'waitUntilStarted'`);
return this.sessionManager.waitUntilStarted();
}
public dispose() {
// Nothing to dispose.
}
}
interface IExternalExtension {
readonly id: string;
readonly apiVersion: string;
}