Skip to content

Commit 20c1619

Browse files
committed
Update use of LanguageClient
Lots of clean-up, a little refactoring, and most importantly, a less buggy `LanguageClient` implementation (which is less prone to race conditions). Also handle disposal of handler registrations.
1 parent 6cc7720 commit 20c1619

File tree

6 files changed

+302
-301
lines changed

6 files changed

+302
-301
lines changed

src/features/Console.ts

+53-55
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
"use strict";
5+
46
import vscode = require("vscode");
57
import { NotificationType, RequestType } from "vscode-languageclient";
68
import { LanguageClient } from "vscode-languageclient/node";
@@ -16,11 +18,11 @@ export const ExecutionStatusChangedNotificationType =
1618

1719
export const ShowChoicePromptRequestType =
1820
new RequestType<IShowChoicePromptRequestArgs,
19-
IShowChoicePromptResponseBody, string>("powerShell/showChoicePrompt");
21+
IShowChoicePromptResponseBody, string>("powerShell/showChoicePrompt");
2022

2123
export const ShowInputPromptRequestType =
2224
new RequestType<IShowInputPromptRequestArgs,
23-
IShowInputPromptResponseBody, string>("powerShell/showInputPrompt");
25+
IShowInputPromptResponseBody, string>("powerShell/showInputPrompt");
2426

2527
export interface IEvaluateRequestArguments {
2628
expression: string;
@@ -133,24 +135,16 @@ function showChoicePrompt(
133135

134136
resultThenable =
135137
showCheckboxQuickPick(
136-
checkboxQuickPickItems,
137-
{ confirmPlaceHolder: promptDetails.message })
138+
checkboxQuickPickItems,
139+
{ confirmPlaceHolder: promptDetails.message })
138140
.then(onItemsSelected);
139141
}
140142

141143
return resultThenable;
142144
}
143145

144-
function showInputPrompt(
145-
promptDetails: IShowInputPromptRequestArgs,
146-
client: LanguageClient): Thenable<IShowInputPromptResponseBody> {
147-
148-
const resultThenable =
149-
vscode.window.showInputBox({
150-
placeHolder: promptDetails.name + ": ",
151-
}).then(onInputEntered);
152-
153-
return resultThenable;
146+
function showInputPrompt(promptDetails: IShowInputPromptRequestArgs): Thenable<IShowInputPromptResponseBody> {
147+
return vscode.window.showInputBox({ placeHolder: promptDetails.name + ": " }).then(onInputEntered);
154148
}
155149

156150
function onItemsSelected(chosenItems: ICheckboxQuickPickItem[]): IShowChoicePromptResponseBody {
@@ -199,13 +193,13 @@ function onInputEntered(responseText: string): IShowInputPromptResponseBody {
199193

200194
export class ConsoleFeature extends LanguageClientConsumer {
201195
private commands: vscode.Disposable[];
196+
private handlers: vscode.Disposable[];
202197
private resolveStatusBarPromise: (value?: {} | PromiseLike<{}>) => void;
203198

204199
constructor(private log: Logger) {
205200
super();
206201
this.commands = [
207202
vscode.commands.registerCommand("PowerShell.RunSelection", async () => {
208-
209203
if (vscode.window.activeTerminal &&
210204
vscode.window.activeTerminal.name !== "PowerShell Extension") {
211205
this.log.write("PowerShell Extension Terminal is not active! Running in current terminal using 'runSelectedText'");
@@ -224,15 +218,12 @@ export class ConsoleFeature extends LanguageClientConsumer {
224218
let selectionRange: vscode.Range;
225219

226220
if (!editor.selection.isEmpty) {
227-
selectionRange =
228-
new vscode.Range(
229-
editor.selection.start,
230-
editor.selection.end);
221+
selectionRange = new vscode.Range(editor.selection.start, editor.selection.end);
231222
} else {
232223
selectionRange = editor.document.lineAt(editor.selection.start.line).range;
233224
}
234225

235-
this.languageClient.sendRequest(EvaluateRequestType, {
226+
await this.languageClient.sendRequest(EvaluateRequestType, {
236227
expression: editor.document.getText(selectionRange),
237228
});
238229

@@ -247,51 +238,58 @@ export class ConsoleFeature extends LanguageClientConsumer {
247238
public dispose() {
248239
// Make sure we cancel any status bar
249240
this.clearStatusBar();
250-
this.commands.forEach((command) => command.dispose());
241+
for (const command of this.commands) {
242+
command.dispose();
243+
}
244+
for (const handler of this.handlers) {
245+
handler.dispose();
246+
}
251247
}
252248

253249
public setLanguageClient(languageClient: LanguageClient) {
254250
this.languageClient = languageClient;
255-
this.languageClient.onRequest(
256-
ShowChoicePromptRequestType,
257-
(promptDetails) => showChoicePrompt(promptDetails, this.languageClient));
258-
259-
this.languageClient.onRequest(
260-
ShowInputPromptRequestType,
261-
(promptDetails) => showInputPrompt(promptDetails, this.languageClient));
262-
263-
// Set up status bar alerts for when PowerShell is executing a script
264-
this.languageClient.onNotification(
265-
ExecutionStatusChangedNotificationType,
266-
(executionStatusDetails) => {
267-
switch (executionStatusDetails.executionStatus) {
268-
// If execution has changed to running, make a notification
269-
case ExecutionStatus.Running:
270-
this.showExecutionStatus("PowerShell");
271-
break;
272-
273-
// If the execution has stopped, destroy the previous notification
274-
case ExecutionStatus.Completed:
275-
case ExecutionStatus.Aborted:
276-
case ExecutionStatus.Failed:
277-
this.clearStatusBar();
278-
break;
279-
}
280-
});
281-
251+
this.handlers = [
252+
this.languageClient.onRequest(
253+
ShowChoicePromptRequestType,
254+
(promptDetails) => showChoicePrompt(promptDetails, this.languageClient)),
255+
256+
this.languageClient.onRequest(
257+
ShowInputPromptRequestType,
258+
(promptDetails) => showInputPrompt(promptDetails)),
259+
260+
// TODO: We're not receiving these events from the server any more.
261+
// Set up status bar alerts for when PowerShell is executing a script.
262+
this.languageClient.onNotification(
263+
ExecutionStatusChangedNotificationType,
264+
(executionStatusDetails) => {
265+
switch (executionStatusDetails.executionStatus) {
266+
// If execution has changed to running, make a notification
267+
case ExecutionStatus.Running:
268+
this.showExecutionStatus("PowerShell");
269+
break;
270+
271+
// If the execution has stopped, destroy the previous notification
272+
case ExecutionStatus.Completed:
273+
case ExecutionStatus.Aborted:
274+
case ExecutionStatus.Failed:
275+
this.clearStatusBar();
276+
break;
277+
}
278+
})
279+
]
282280
}
283281

284282
private showExecutionStatus(message: string) {
285283
vscode.window.withProgress({
286-
location: vscode.ProgressLocation.Window,
287-
}, (progress) => {
288-
return new Promise((resolve, reject) => {
289-
this.clearStatusBar();
284+
location: vscode.ProgressLocation.Window,
285+
}, (progress) => {
286+
return new Promise((resolve, _reject) => {
287+
this.clearStatusBar();
290288

291-
this.resolveStatusBarPromise = resolve;
292-
progress.report({ message });
293-
});
289+
this.resolveStatusBarPromise = resolve;
290+
progress.report({ message });
294291
});
292+
});
295293
}
296294

297295
private clearStatusBar() {

src/features/DebugSession.ts

+22-15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
"use strict";
5+
46
import vscode = require("vscode");
57
import { CancellationToken, DebugConfiguration, DebugConfigurationProvider,
68
ExtensionContext, WorkspaceFolder } from "vscode";
@@ -25,6 +27,7 @@ export class DebugSessionFeature extends LanguageClientConsumer
2527
private sessionCount: number = 1;
2628
private tempDebugProcess: PowerShellProcess;
2729
private tempSessionDetails: IEditorServicesSessionDetails;
30+
private handlers: vscode.Disposable[];
2831

2932
constructor(context: ExtensionContext, private sessionManager: SessionManager, private logger: Logger) {
3033
super();
@@ -47,28 +50,32 @@ export class DebugSessionFeature extends LanguageClientConsumer
4750
return new vscode.DebugAdapterNamedPipeServer(sessionDetails.debugServicePipeName);
4851
}
4952

50-
// tslint:disable-next-line:no-empty
5153
public dispose() {
54+
for (const handler of this.handlers) {
55+
handler.dispose();
56+
}
5257
}
5358

5459
public setLanguageClient(languageClient: LanguageClient) {
55-
languageClient.onNotification(
56-
StartDebuggerNotificationType,
57-
// TODO: Use a named debug configuration.
58-
() => vscode.debug.startDebugging(undefined, {
59-
request: "launch",
60-
type: "PowerShell",
61-
name: "PowerShell: Interactive Session"
62-
}));
63-
64-
languageClient.onNotification(
65-
StopDebuggerNotificationType,
66-
() => vscode.debug.stopDebugging(undefined));
60+
this.handlers = [
61+
languageClient.onNotification(
62+
StartDebuggerNotificationType,
63+
// TODO: Use a named debug configuration.
64+
async () => await vscode.debug.startDebugging(undefined, {
65+
request: "launch",
66+
type: "PowerShell",
67+
name: "PowerShell: Interactive Session"
68+
})),
69+
70+
languageClient.onNotification(
71+
StopDebuggerNotificationType,
72+
async () => await vscode.debug.stopDebugging(undefined))
73+
];
6774
}
6875

6976
public async provideDebugConfigurations(
70-
folder: WorkspaceFolder | undefined,
71-
token?: CancellationToken): Promise<DebugConfiguration[]> {
77+
_folder: WorkspaceFolder | undefined,
78+
_token?: CancellationToken): Promise<DebugConfiguration[]> {
7279

7380
enum DebugConfig {
7481
LaunchCurrentFile,

0 commit comments

Comments
 (0)