Skip to content

Commit ecad1e3

Browse files
CopilotTyriar
andcommitted
Implement workbench.action.terminal.sendSignal command
Co-authored-by: Tyriar <[email protected]>
1 parent caeaf6f commit ecad1e3

File tree

15 files changed

+81
-0
lines changed

15 files changed

+81
-0
lines changed

src/vs/platform/terminal/common/terminal.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ export interface IPtyService {
331331
start(id: number): Promise<ITerminalLaunchError | { injectedArgs: string[] } | undefined>;
332332
shutdown(id: number, immediate: boolean): Promise<void>;
333333
input(id: number, data: string): Promise<void>;
334+
sendSignal(id: number, signal: string): Promise<void>;
334335
resize(id: number, cols: number, rows: number): Promise<void>;
335336
clearBuffer(id: number): Promise<void>;
336337
getInitialCwd(id: number): Promise<string>;
@@ -791,6 +792,7 @@ export interface ITerminalChildProcess {
791792
*/
792793
shutdown(immediate: boolean): void;
793794
input(data: string): void;
795+
sendSignal(signal: string): void;
794796
processBinary(data: string): Promise<void>;
795797
resize(cols: number, rows: number): void;
796798
clearBuffer(): void | Promise<void>;

src/vs/platform/terminal/node/ptyService.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,10 @@ export class PtyService extends Disposable implements IPtyService {
422422
}
423423
}
424424
@traceRpc
425+
async sendSignal(id: number, signal: string): Promise<void> {
426+
return this._throwIfNoPty(id).sendSignal(signal);
427+
}
428+
@traceRpc
425429
async processBinary(id: number, data: string): Promise<void> {
426430
return this._throwIfNoPty(id).writeBinary(data);
427431
}

src/vs/platform/terminal/node/terminalProcess.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,13 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
481481
this._startWrite();
482482
}
483483

484+
sendSignal(signal: string): void {
485+
if (this._store.isDisposed || !this._ptyProcess) {
486+
return;
487+
}
488+
this._ptyProcess.kill(signal);
489+
}
490+
484491
async processBinary(data: string): Promise<void> {
485492
this.input(data, true);
486493
}

src/vs/server/node/remoteTerminalChannel.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel<
128128

129129
case RemoteTerminalChannelRequest.Start: return this._ptyHostService.start.apply(this._ptyHostService, args);
130130
case RemoteTerminalChannelRequest.Input: return this._ptyHostService.input.apply(this._ptyHostService, args);
131+
case RemoteTerminalChannelRequest.SendSignal: return this._ptyHostService.sendSignal.apply(this._ptyHostService, args);
131132
case RemoteTerminalChannelRequest.AcknowledgeDataEvent: return this._ptyHostService.acknowledgeDataEvent.apply(this._ptyHostService, args);
132133
case RemoteTerminalChannelRequest.Shutdown: return this._ptyHostService.shutdown.apply(this._ptyHostService, args);
133134
case RemoteTerminalChannelRequest.Resize: return this._ptyHostService.resize.apply(this._ptyHostService, args);

src/vs/workbench/api/common/extHostTerminalService.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,11 @@ class ExtHostPseudoterminal implements ITerminalChildProcess {
334334
this._pty.handleInput?.(data);
335335
}
336336

337+
sendSignal(signal: string): void {
338+
// Extension owned terminals don't support sending signals directly to processes
339+
// This could be extended in the future if the pseudoterminal API is enhanced
340+
}
341+
337342
resize(cols: number, rows: number): void {
338343
this._pty.setDimensions?.({ columns: cols, rows });
339344
}

src/vs/workbench/contrib/terminal/browser/terminal.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,13 @@ export interface ITerminalInstance extends IBaseTerminalInstance {
899899
*/
900900
sendText(text: string, shouldExecute: boolean, bracketedPasteMode?: boolean): Promise<void>;
901901

902+
/**
903+
* Sends a signal to the terminal instance's process.
904+
*
905+
* @param signal The signal to send (e.g., 'SIGTERM', 'SIGINT', 'SIGKILL').
906+
*/
907+
sendSignal(signal: string): Promise<void>;
908+
902909
/**
903910
* Sends a path to the terminal instance, preparing it as needed based on the detected shell
904911
* running within the terminal. The text is written to the stdin of the underlying pty process

src/vs/workbench/contrib/terminal/browser/terminalActions.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,17 @@ export const terminalSendSequenceCommand = async (accessor: ServicesAccessor, ar
137137
}
138138
};
139139

140+
export const terminalSendSignalCommand = async (accessor: ServicesAccessor, args: unknown) => {
141+
const instance = accessor.get(ITerminalService).activeInstance;
142+
if (instance) {
143+
const signal = isObject(args) && 'signal' in args ? toOptionalString(args.signal) : undefined;
144+
if (!signal) {
145+
return;
146+
}
147+
instance.sendSignal(signal);
148+
}
149+
};
150+
140151
export class TerminalLaunchHelpAction extends Action {
141152

142153
constructor(
@@ -977,6 +988,29 @@ export function registerTerminalActions() {
977988
run: (c, accessor, args) => terminalSendSequenceCommand(accessor, args)
978989
});
979990

991+
registerTerminalAction({
992+
id: TerminalCommandId.SendSignal,
993+
title: terminalStrings.sendSignal,
994+
f1: false,
995+
metadata: {
996+
description: terminalStrings.sendSignal.value,
997+
args: [{
998+
name: 'args',
999+
schema: {
1000+
type: 'object',
1001+
required: ['signal'],
1002+
properties: {
1003+
signal: {
1004+
description: localize('sendSignal', "The signal to send to the terminal process (e.g., 'SIGTERM', 'SIGINT', 'SIGKILL')"),
1005+
type: 'string'
1006+
}
1007+
},
1008+
}
1009+
}]
1010+
},
1011+
run: (c, accessor, args) => terminalSendSignalCommand(accessor, args)
1012+
});
1013+
9801014
registerTerminalAction({
9811015
id: TerminalCommandId.NewWithCwd,
9821016
title: terminalStrings.newWithCwd,

src/vs/workbench/contrib/terminal/browser/terminalInstance.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,6 +1321,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
13211321
}
13221322
}
13231323

1324+
async sendSignal(signal: string): Promise<void> {
1325+
this._logService.debug('sending signal (vscode)', signal);
1326+
await this._processManager.sendSignal(signal);
1327+
}
1328+
13241329
async sendPath(originalPath: string | URI, shouldExecute: boolean): Promise<void> {
13251330
return this.sendText(await this.preparePathForShell(originalPath), shouldExecute);
13261331
}

src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,13 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
609609
}
610610
}
611611

612+
async sendSignal(signal: string): Promise<void> {
613+
await this.ptyProcessReady;
614+
if (this._process) {
615+
this._process.sendSignal(signal);
616+
}
617+
}
618+
612619
async processBinary(data: string): Promise<void> {
613620
await this.ptyProcessReady;
614621
this._dataFilter.disableSeamlessRelaunch();

src/vs/workbench/contrib/terminal/common/remote/remoteTerminalChannel.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@ export class RemoteTerminalChannelClient implements IPtyHostController {
216216
input(id: number, data: string): Promise<void> {
217217
return this._channel.call(RemoteTerminalChannelRequest.Input, [id, data]);
218218
}
219+
sendSignal(id: number, signal: string): Promise<void> {
220+
return this._channel.call(RemoteTerminalChannelRequest.SendSignal, [id, signal]);
221+
}
219222
acknowledgeDataEvent(id: number, charCount: number): Promise<void> {
220223
return this._channel.call(RemoteTerminalChannelRequest.AcknowledgeDataEvent, [id, charCount]);
221224
}

0 commit comments

Comments
 (0)