Skip to content

Commit 93ed7ef

Browse files
authored
Revert "Run tests in SwiftPtyProcess (#1933)" (#1952)
This reverts commit 90e62a0.
1 parent 3e4d419 commit 93ed7ef

File tree

5 files changed

+113
-5
lines changed

5 files changed

+113
-5
lines changed

CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
### Fixed
66

77
- Suggest "Open Documentation" when toolchain not found ([#1939](https://github.com/swiftlang/vscode-swift/pull/1939))
8-
- Fix colorization of standard swift-testing test runs ([#1933](https://github.com/swiftlang/vscode-swift/pull/1933))
98
- Make sure all folder operation listeners get past folder add events ([#1945](https://github.com/swiftlang/vscode-swift/pull/1945))
109

1110
## 2.14.0 - 2025-11-11

src/TestExplorer/TestRunner.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -952,7 +952,8 @@ export class TestRunner {
952952
presentationOptions: { reveal: vscode.TaskRevealKind.Never },
953953
},
954954
this.folderContext.toolchain,
955-
{ ...process.env, ...testBuildConfig.env }
955+
{ ...process.env, ...testBuildConfig.env },
956+
{ readOnlyTerminal: process.platform !== "win32" }
956957
);
957958

958959
task.execution.onDidWrite(str => {

src/tasks/SwiftExecution.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313
//===----------------------------------------------------------------------===//
1414
import * as vscode from "vscode";
1515

16-
import { SwiftProcess, SwiftPtyProcess } from "./SwiftProcess";
16+
import { ReadOnlySwiftProcess, SwiftProcess, SwiftPtyProcess } from "./SwiftProcess";
1717
import { SwiftPseudoterminal } from "./SwiftPseudoterminal";
1818

1919
export interface SwiftExecutionOptions extends vscode.ProcessExecutionOptions {
2020
presentation?: vscode.TaskPresentationOptions;
21+
readOnlyTerminal?: boolean;
2122
}
2223

2324
/**
@@ -41,7 +42,9 @@ export class SwiftExecution extends vscode.CustomExecution implements vscode.Dis
4142
super(async () => {
4243
const createSwiftProcess = () => {
4344
if (!swiftProcess) {
44-
this.swiftProcess = new SwiftPtyProcess(command, args, options);
45+
this.swiftProcess = options.readOnlyTerminal
46+
? new ReadOnlySwiftProcess(command, args, options)
47+
: new SwiftPtyProcess(command, args, options);
4548
this.listen(this.swiftProcess);
4649
}
4750
return this.swiftProcess!;

src/tasks/SwiftProcess.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// SPDX-License-Identifier: Apache-2.0
1212
//
1313
//===----------------------------------------------------------------------===//
14+
import * as child_process from "child_process";
1415
import type * as nodePty from "node-pty";
1516
import * as vscode from "vscode";
1617

@@ -201,3 +202,105 @@ export class SwiftPtyProcess implements SwiftProcess {
201202

202203
onDidClose: vscode.Event<number | void> = this.closeHandler.event;
203204
}
205+
206+
/**
207+
* A {@link SwiftProcess} that spawns a child process and does not bind to stdio.
208+
*
209+
* Use this for Swift tasks that do not need to accept input, as its lighter weight and
210+
* less error prone than using a spawned node-pty process.
211+
*
212+
* Specifically node-pty on Linux suffers from a long standing issue where the last chunk
213+
* of output before a program exits is sometimes dropped, especially if that program produces
214+
* a lot of output immediately before exiting. See https://github.com/microsoft/node-pty/issues/72
215+
*/
216+
export class ReadOnlySwiftProcess implements SwiftProcess {
217+
private readonly spawnEmitter: vscode.EventEmitter<void> = new vscode.EventEmitter<void>();
218+
private readonly writeEmitter: vscode.EventEmitter<string> = new vscode.EventEmitter<string>();
219+
private readonly errorEmitter: vscode.EventEmitter<Error> = new vscode.EventEmitter<Error>();
220+
private readonly closeHandler: CloseHandler = new CloseHandler();
221+
private disposables: vscode.Disposable[] = [];
222+
223+
private spawnedProcess: child_process.ChildProcessWithoutNullStreams | undefined;
224+
225+
constructor(
226+
public readonly command: string,
227+
public readonly args: string[],
228+
private readonly options: vscode.ProcessExecutionOptions = {}
229+
) {
230+
this.disposables.push(
231+
this.spawnEmitter,
232+
this.writeEmitter,
233+
this.errorEmitter,
234+
this.closeHandler
235+
);
236+
}
237+
238+
spawn(): void {
239+
try {
240+
this.spawnedProcess = child_process.spawn(this.command, this.args, {
241+
cwd: this.options.cwd,
242+
env: { ...process.env, ...this.options.env },
243+
});
244+
this.spawnEmitter.fire();
245+
246+
this.spawnedProcess.stdout.on("data", data => {
247+
this.writeEmitter.fire(data.toString());
248+
this.closeHandler.reset();
249+
});
250+
251+
this.spawnedProcess.stderr.on("data", data => {
252+
this.writeEmitter.fire(data.toString());
253+
this.closeHandler.reset();
254+
});
255+
256+
this.spawnedProcess.on("error", error => {
257+
this.errorEmitter.fire(new Error(`${error}`));
258+
this.closeHandler.handle();
259+
});
260+
261+
this.spawnedProcess.once("exit", code => {
262+
this.closeHandler.handle(code ?? undefined);
263+
});
264+
265+
this.disposables.push(
266+
this.onDidClose(() => {
267+
this.dispose();
268+
})
269+
);
270+
} catch (error) {
271+
this.errorEmitter.fire(new Error(`${error}`));
272+
this.closeHandler.handle();
273+
}
274+
}
275+
276+
handleInput(_s: string): void {
277+
// Do nothing
278+
}
279+
280+
terminate(signal?: NodeJS.Signals): void {
281+
if (!this.spawnedProcess) {
282+
return;
283+
}
284+
this.spawnedProcess.kill(signal);
285+
this.dispose();
286+
}
287+
288+
setDimensions(_dimensions: vscode.TerminalDimensions): void {
289+
// Do nothing
290+
}
291+
292+
dispose(): void {
293+
this.spawnedProcess?.stdout.removeAllListeners();
294+
this.spawnedProcess?.stderr.removeAllListeners();
295+
this.spawnedProcess?.removeAllListeners();
296+
this.disposables.forEach(d => d.dispose());
297+
}
298+
299+
onDidSpawn: vscode.Event<void> = this.spawnEmitter.event;
300+
301+
onDidWrite: vscode.Event<string> = this.writeEmitter.event;
302+
303+
onDidThrowError: vscode.Event<Error> = this.errorEmitter.event;
304+
305+
onDidClose: vscode.Event<number | void> = this.closeHandler.event;
306+
}

src/tasks/SwiftTaskProvider.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,8 @@ export function createSwiftTask(
291291
name: string,
292292
config: TaskConfig,
293293
toolchain: SwiftToolchain,
294-
cmdEnv: { [key: string]: string } = {}
294+
cmdEnv: { [key: string]: string } = {},
295+
options: { readOnlyTerminal: boolean } = { readOnlyTerminal: false }
295296
): SwiftTask {
296297
const swift = toolchain.getToolchainExecutable("swift");
297298
args = toolchain.buildFlags.withAdditionalFlags(args);
@@ -339,6 +340,7 @@ export function createSwiftTask(
339340
cwd: fullCwd,
340341
env: env,
341342
presentation,
343+
readOnlyTerminal: options.readOnlyTerminal,
342344
})
343345
);
344346
// This doesn't include any quotes added by VS Code.

0 commit comments

Comments
 (0)