-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Run without debugging #14351
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Run without debugging #14351
Changes from 3 commits
7250861
448763e
c315d20
a86afd6
bb77661
2608547
9f9dd61
d59f6c0
de0e20c
6008c13
da02611
68dddd8
da18921
ad01d4a
a69ba2a
b94df3e
44dda31
161627d
826e1d2
bd6eb8b
cf48779
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,222 @@ | ||
| /* -------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All Rights Reserved. | ||
| * See 'LICENSE' in the project root for license information. | ||
| * ------------------------------------------------------------------------------------------ */ | ||
|
|
||
| import * as cp from 'child_process'; | ||
| import * as os from 'os'; | ||
| import * as path from 'path'; | ||
| import * as vscode from 'vscode'; | ||
| import * as nls from 'vscode-nls'; | ||
| import { sessionIsWsl } from '../common'; | ||
|
|
||
| nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })(); | ||
| const localize = nls.loadMessageBundle(); | ||
|
|
||
| /** | ||
| * A minimal inline Debug Adapter that runs the target program directly without a debug adapter | ||
| * when the user invokes "Run Without Debugging". | ||
| */ | ||
| export class RunWithoutDebuggingAdapter implements vscode.DebugAdapter { | ||
| private readonly sendMessageEmitter = new vscode.EventEmitter<vscode.DebugProtocolMessage>(); | ||
| public readonly onDidSendMessage: vscode.Event<vscode.DebugProtocolMessage> = this.sendMessageEmitter.event; | ||
|
|
||
| private seq: number = 1; | ||
| private childProcess?: cp.ChildProcess; | ||
| private terminal?: vscode.Terminal; | ||
|
|
||
| public handleMessage(message: vscode.DebugProtocolMessage): void { | ||
| const msg = message as { type: string; command: string; seq: number; arguments?: any; }; | ||
| if (msg.type === 'request') { | ||
| void this.handleRequest(msg); | ||
| } | ||
| } | ||
|
|
||
| private async handleRequest(request: { command: string; seq: number; arguments?: any; }): Promise<void> { | ||
| switch (request.command) { | ||
| case 'initialize': | ||
| this.sendResponse(request, {}); | ||
| this.sendEvent('initialized'); | ||
| break; | ||
| case 'launch': | ||
| await this.launch(request); | ||
| break; | ||
| case 'configurationDone': | ||
| this.sendResponse(request, {}); | ||
| break; | ||
| case 'disconnect': | ||
| case 'terminate': | ||
| this.sendResponse(request, {}); | ||
| break; | ||
| default: | ||
| this.sendResponse(request, {}); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| private async launch(request: { command: string; seq: number; arguments?: any; }): Promise<void> { | ||
| const config = request.arguments as { | ||
| program?: string; | ||
| args?: string[]; | ||
| cwd?: string; | ||
| environment?: { name: string; value: string; }[]; | ||
| console?: string; | ||
| externalConsole?: boolean; | ||
| }; | ||
|
|
||
| const program: string = config.program ?? ''; | ||
| const args: string[] = config.args ?? []; | ||
| const cwd: string | undefined = config.cwd; | ||
| const environment: { name: string; value: string; }[] = config.environment ?? []; | ||
| const consoleMode: string = config.console ?? (config.externalConsole ? 'externalTerminal' : 'integratedTerminal'); | ||
|
|
||
| // Merge the launch config's environment variables on top of the inherited process environment. | ||
| const env: NodeJS.ProcessEnv = { ...process.env }; | ||
| for (const e of environment) { | ||
| env[e.name] = e.value; | ||
| } | ||
|
|
||
| this.sendResponse(request, {}); | ||
|
|
||
| if (consoleMode === 'integratedTerminal') { | ||
| this.launchIntegratedTerminal(program, args, cwd, env); | ||
| } else if (consoleMode === 'externalTerminal') { | ||
| this.launchExternalTerminal(program, args, cwd, env); | ||
| } else { | ||
| this.launchInternalConsole(program, args, cwd, env); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Launch the program in a VS Code integrated terminal. | ||
| * The terminal will remain open after the program exits and be reused for the next session, if applicable. | ||
| */ | ||
| private launchIntegratedTerminal(program: string, args: string[], cwd: string | undefined, env: NodeJS.ProcessEnv) { | ||
| const shellArgs: string[] = [program, ...args].map(a => this.quoteArg(a)); | ||
| const terminalName = path.normalize(program); | ||
| const existingTerminal = vscode.window.terminals.find(t => t.name === terminalName); | ||
| this.terminal = existingTerminal ?? vscode.window.createTerminal({ | ||
| name: terminalName, | ||
| cwd, | ||
| env: env as Record<string, string> | ||
| }); | ||
| this.terminal.show(true); | ||
| this.terminal.sendText(shellArgs.join(' ')); | ||
|
|
||
| // The terminal manages its own lifecycle; notify VS Code the "debug" session is done. | ||
| this.sendEvent('terminated'); | ||
| } | ||
|
|
||
| /** | ||
| * Launch the program in an external terminal. We do not keep track of this terminal or the spawned process. | ||
| */ | ||
| private launchExternalTerminal(program: string, args: string[], cwd: string | undefined, env: NodeJS.ProcessEnv): void { | ||
| const quotedArgs: string[] = [program, ...args].map(a => this.quoteArg(a)); | ||
| const cmdLine: string = quotedArgs.join(' '); | ||
| const platform: string = os.platform(); | ||
| if (platform === 'win32') { | ||
| cp.spawn('cmd.exe', ['/c', 'start', 'cmd.exe', '/K', cmdLine], { cwd, env, detached: true, stdio: 'ignore' }).unref(); | ||
| } else if (platform === 'darwin') { | ||
| cp.spawn('osascript', ['-e', `tell application "Terminal" to do script "${cmdLine.replace(/"/g, '\\"')}"`], { cwd, env, detached: true, stdio: 'ignore' }).unref(); | ||
Check failureCode scanning / CodeQL Incomplete string escaping or encoding High
This does not escape backslash characters in the input.
|
||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
|
||
| } else if (platform === 'linux' && sessionIsWsl()) { | ||
| cp.spawn('/mnt/c/Windows/System32/cmd.exe', ['/c', 'start', 'bash', '-c', `${cmdLine};read -p 'Press enter to continue...'`], { env, detached: true, stdio: 'ignore' }).unref(); | ||
| } else { // platform === 'linux' | ||
| this.launchLinuxExternalTerminal(cmdLine, cwd, env); | ||
| } | ||
| this.sendEvent('terminated'); | ||
| } | ||
|
|
||
| /** | ||
| * On Linux, find and launch an available terminal emulator to run the command. | ||
| */ | ||
| private launchLinuxExternalTerminal(cmdLine: string, cwd: string | undefined, env: NodeJS.ProcessEnv): void { | ||
| const bashCmd = `${cmdLine}; echo; read -p 'Press enter to continue...'`; | ||
| const bashArgs = ['bash', '-c', bashCmd]; | ||
|
|
||
| // Terminal emulators in order of preference, with the correct flag style for each. | ||
| const candidates: { cmd: string; buildArgs: () => string[] }[] = [ | ||
|
Check failure on line 137 in Extension/src/Debugger/runWithoutDebuggingAdapter.ts
|
||
| { cmd: 'x-terminal-emulator', buildArgs: () => ['-e', ...bashArgs] }, | ||
| { cmd: 'gnome-terminal', buildArgs: () => ['-e', ...bashArgs] }, | ||
| { cmd: 'konsole', buildArgs: () => ['-e', ...bashArgs] }, | ||
| { cmd: 'xterm', buildArgs: () => ['-e', ...bashArgs] } | ||
| ]; | ||
|
|
||
| // Honor the $TERMINAL environment variable if set. | ||
| const terminalEnv = process.env['TERMINAL']; | ||
| if (terminalEnv) { | ||
| candidates.unshift({ cmd: terminalEnv, buildArgs: () => ['-e', ...bashArgs] }); | ||
| } | ||
|
|
||
| for (const candidate of candidates) { | ||
| try { | ||
| const result = cp.spawnSync('which', [candidate.cmd], { stdio: 'pipe' }); | ||
| if (result.status === 0) { | ||
| cp.spawn(candidate.cmd, candidate.buildArgs(), { cwd, env, detached: true, stdio: 'ignore' }).unref(); | ||
| return; | ||
| } | ||
| } catch { | ||
| continue; | ||
| } | ||
| } | ||
|
|
||
| const message = localize('no.terminal.emulator', 'No terminal emulator found. Please set the $TERMINAL environment variable to your terminal emulator of choice, or install one of the following: x-terminal-emulator, gnome-terminal, konsole, xterm.'); | ||
| vscode.window.showErrorMessage(message); | ||
| } | ||
|
|
||
| /** | ||
| * Spawn the process and forward stdout/stderr as DAP output events. | ||
| */ | ||
| private launchInternalConsole(program: string, args: string[], cwd: string | undefined, env: NodeJS.ProcessEnv) { | ||
| this.childProcess = cp.spawn(program, args, { cwd, env }); | ||
|
|
||
| this.childProcess.stdout?.on('data', (data: Buffer) => { | ||
| this.sendEvent('output', { category: 'stdout', output: data.toString() }); | ||
| }); | ||
| this.childProcess.stderr?.on('data', (data: Buffer) => { | ||
| this.sendEvent('output', { category: 'stderr', output: data.toString() }); | ||
| }); | ||
| this.childProcess.on('error', (err: Error) => { | ||
| this.sendEvent('output', { category: 'stderr', output: `${err.message}\n` }); | ||
| this.sendEvent('exited', { exitCode: 1 }); | ||
| this.sendEvent('terminated'); | ||
| }); | ||
| this.childProcess.on('exit', (code: number | null) => { | ||
| this.sendEvent('exited', { exitCode: code ?? 0 }); | ||
| this.sendEvent('terminated'); | ||
| }); | ||
| } | ||
|
|
||
| private quoteArg(arg: string): string { | ||
| return /\s/.test(arg) ? `"${arg.replace(/"/g, '\\"')}"` : arg; | ||
Check failureCode scanning / CodeQL Incomplete string escaping or encoding High
This does not escape backslash characters in the input.
|
||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
|
||
| } | ||
|
|
||
| private sendResponse(request: { command: string; seq: number; }, body: object): void { | ||
| this.sendMessageEmitter.fire({ | ||
| type: 'response', | ||
| seq: this.seq++, | ||
| request_seq: request.seq, | ||
| success: true, | ||
| command: request.command, | ||
| body | ||
| } as vscode.DebugProtocolMessage); | ||
| } | ||
|
|
||
| private sendEvent(event: string, body?: object): void { | ||
| this.sendMessageEmitter.fire({ | ||
| type: 'event', | ||
| seq: this.seq++, | ||
| event, | ||
| body | ||
| } as vscode.DebugProtocolMessage); | ||
| } | ||
|
|
||
| public dispose(): void { | ||
| this.terminateProcess(); | ||
| this.sendMessageEmitter.dispose(); | ||
| } | ||
|
|
||
| private terminateProcess(): void { | ||
| this.childProcess?.kill(); | ||
| this.childProcess = undefined; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.