Skip to content

Commit 1b20e5a

Browse files
committed
Add "interactiveTerminal" debugger option
This adds a new option to the debugger configuration to run the program using brickrun -r in an interactive terminal. Issue #90
1 parent e8a6eee commit 1b20e5a

File tree

5 files changed

+101
-31
lines changed

5 files changed

+101
-31
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ All notable changes to the "ev3dev-browser" extension will be documented in this
44
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
55

66
## Unreleased
7+
### Added
8+
- New "interactiveTerminal" debugger option to run remote programs in
9+
interactive terminal instead of output pane
710
### Changed
811
- SSH shell no longer requires native executable on Windows
912

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,11 @@
195195
"type": "string",
196196
"description": "Absolute path to an executable file on the remote device.",
197197
"default": "/home/robot/myproject/myprogram"
198+
},
199+
"interactiveTerminal": {
200+
"type": "boolean",
201+
"description": "When true, program will be run in a new interactive terminal, when false the output pane will be used instead.",
202+
"default": false
198203
}
199204
}
200205
}

src/debugServer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import { DebugSession, Event, TerminatedEvent } from 'vscode-debugadapter';
22
import { DebugProtocol } from 'vscode-debugprotocol';
33

44
/**
5-
* This interface should always match the schema found in the mock-debug extension manifest.
5+
* This interface should always match the schema found in the extension manifest.
66
*/
77
export interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArguments {
88
/** An absolute path to the program to debug. */
99
program: string;
1010
/** Download files before running. Default is true. */
1111
download?: boolean;
12+
/** Run in terminal instead of output pane. */
13+
interactiveTerminal: boolean;
1214
}
1315

1416

src/device.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,11 @@ export class Device extends vscode.Disposable {
223223
* Executes a command on the remote device.
224224
* @param command The absolute path of the command.
225225
*/
226-
public exec(command: string): Promise<ssh2.Channel> {
226+
public exec(command: string, pty?: ssh2.PseudoTtyOptions): Promise<ssh2.ClientChannel> {
227227
return new Promise((resolve, reject) => {
228228
const options = {
229-
env: vscode.workspace.getConfiguration('ev3devBrowser').get('env')
229+
env: vscode.workspace.getConfiguration('ev3devBrowser').get('env'),
230+
pty: pty,
230231
};
231232
this.client.exec(command, options, (err, channel) => {
232233
if (err) {

src/extension.ts

Lines changed: 87 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ async function pickDevice(): Promise<void> {
8585
}
8686

8787
const activeDebugSessions = new Set<string>();
88+
let debugTerminal: vscode.Terminal;
8889

8990
async function handleCustomDebugEvent(event: vscode.DebugSessionCustomEvent): Promise<void> {
9091
let device: Device | undefined;
@@ -116,35 +117,93 @@ async function handleCustomDebugEvent(event: vscode.DebugSessionCustomEvent): Pr
116117
// run the program
117118
try {
118119
const dirname = path.posix.dirname(args.program);
119-
const command = `brickrun --directory="${dirname}" "${args.program}"`;
120-
output.show(true);
121-
output.clear();
122-
output.appendLine(`Starting: ${command}`);
123-
const channel = await device.exec(command);
124-
channel.on('close', () => {
125-
event.session.customRequest('ev3devBrowser.debugger.terminate');
126-
});
127-
channel.on('exit', (code, signal, coreDump, desc) => {
128-
output.appendLine('----------');
129-
if (code === 0) {
130-
output.appendLine('Completed successfully.');
131-
}
132-
else if (code) {
133-
output.appendLine(`Exited with error code ${code}.`);
134-
}
135-
else {
136-
output.appendLine(`Exited with signal ${signal}.`);
120+
const command = `brickrun -r --directory="${dirname}" "${args.program}"`;
121+
if (args.interactiveTerminal) {
122+
const config = vscode.workspace.getConfiguration(`terminal.integrated.env.${getPlatform()}`);
123+
const termEnv = config.get<string>('TERM');
124+
const ch = await device.exec(command, { term: termEnv || process.env['TERM'] || 'xterm-256color' });
125+
const writeEmitter = new vscode.EventEmitter<string>();
126+
ch.stdout.on('data', (data: string | Buffer) => writeEmitter.fire(String(data)));
127+
ch.stderr.on('data', (data: string | Buffer) => writeEmitter.fire(String(data)));
128+
if (debugTerminal) {
129+
debugTerminal.dispose();
137130
}
138-
activeDebugSessions.delete(event.session.id);
139-
});
140-
channel.on('data', (chunk) => {
141-
output.append(chunk.toString());
142-
});
143-
channel.stderr.on('data', (chunk) => {
144-
output.append(chunk.toString());
145-
});
146-
output.appendLine('Started.');
147-
output.appendLine('----------');
131+
debugTerminal = vscode.window.createTerminal({
132+
name: `SSH: ${device.name}`,
133+
pty: {
134+
onDidWrite: writeEmitter.event,
135+
open: (dim: vscode.TerminalDimensions | undefined) => {
136+
if (dim !== undefined) {
137+
ch.setWindow(dim.rows, dim.columns, 0, 0);
138+
}
139+
writeEmitter.fire(`Starting: ${command}\r\n`);
140+
writeEmitter.fire('----------\r\n');
141+
},
142+
close: () => {
143+
ch.close();
144+
activeDebugSessions.delete(event.session.id);
145+
},
146+
handleInput: (data: string) => {
147+
ch.stdin.write(data);
148+
},
149+
setDimensions: (dim: vscode.TerminalDimensions) => {
150+
ch.setWindow(dim.rows, dim.columns, 0, 0);
151+
},
152+
},
153+
});
154+
ch.on('close', () => {
155+
event.session.customRequest('ev3devBrowser.debugger.terminate');
156+
ch.destroy();
157+
});
158+
ch.on('exit', (code, signal, coreDump, desc) => {
159+
writeEmitter.fire('----------\r\n');
160+
if (code === 0) {
161+
writeEmitter.fire('Completed successfully.\r\n');
162+
}
163+
else if (code) {
164+
writeEmitter.fire(`Exited with error code ${code}.\r\n`);
165+
}
166+
else {
167+
writeEmitter.fire(`Exited with signal ${signal}.\r\n`);
168+
}
169+
activeDebugSessions.delete(event.session.id);
170+
});
171+
ch.on('error', (err: any) => {
172+
vscode.window.showErrorMessage(`Connection error: ${err || err.message}`);
173+
debugTerminal.dispose();
174+
ch.destroy();
175+
});
176+
debugTerminal.show();
177+
}
178+
else {
179+
output.show(true);
180+
output.clear();
181+
output.appendLine(`Starting: ${command}`);
182+
const channel = await device.exec(command);
183+
channel.on('close', () => {
184+
event.session.customRequest('ev3devBrowser.debugger.terminate');
185+
});
186+
channel.on('exit', (code, signal, coreDump, desc) => {
187+
output.appendLine('----------');
188+
if (code === 0) {
189+
output.appendLine('Completed successfully.');
190+
}
191+
else if (code) {
192+
output.appendLine(`Exited with error code ${code}.`);
193+
}
194+
else {
195+
output.appendLine(`Exited with signal ${signal}.`);
196+
}
197+
activeDebugSessions.delete(event.session.id);
198+
});
199+
channel.on('data', (chunk: string | Buffer) => {
200+
output.append(chunk.toString());
201+
});
202+
channel.stderr.on('data', (chunk) => {
203+
output.append(chunk.toString());
204+
});
205+
output.appendLine('----------');
206+
}
148207
activeDebugSessions.add(event.session.id);
149208
}
150209
catch (err) {

0 commit comments

Comments
 (0)