Skip to content

Commit 972afff

Browse files
bors[bot]vsrs
andauthored
Merge #4222
4222: Introduce C/C++ for Visual Studio Code extension as an alternative debug engine for Debug Code lens. r=matklad a=vsrs At the moment Debug Code Lens can use only one debug engine: lldb via [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb) extension. This PR adds support of the debug engine from the [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) extension, as well as the configuration option. If both extensions are installed, `CodeLLDB` will be used by default. Another new option `rust-analyzer.debug.sourceFileMap` allows, for example, to step into Rust std library during debugging. Works only with `MS C++ tools`. On Windows: ```json "rust-analyzer.debug.sourceFileMap": { "/rustc/4fb7144ed159f94491249e86d5bbd033b5d60550": "${env:USERPROFILE}/.rustup/toolchains/stable-x86_64-pc-windows-msvc/lib/rustlib/src/rust" } ``` On Linux: ```json "rust-analyzer.debug.sourceFileMap": { "/rustc/4fb7144ed159f94491249e86d5bbd033b5d60550": "~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust" } ``` Co-authored-by: vsrs <vit@conrlab.com>
2 parents fec1e7c + 06b7175 commit 972afff

File tree

4 files changed

+194
-13
lines changed

4 files changed

+194
-13
lines changed

editors/code/package.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,28 @@
389389
"description": "Enable Proc macro support, cargo.loadOutDirsFromCheck must be enabled.",
390390
"type": "boolean",
391391
"default": false
392+
},
393+
"rust-analyzer.debug.engine": {
394+
"type": "string",
395+
"enum": [
396+
"auto",
397+
"vadimcn.vscode-lldb",
398+
"ms-vscode.cpptools"
399+
],
400+
"default": "auto",
401+
"description": "Preffered debug engine.",
402+
"markdownEnumDescriptions": [
403+
"First try to use [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb), if it's not installed try to use [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools).",
404+
"Use [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)",
405+
"Use [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools)"
406+
]
407+
},
408+
"rust-analyzer.debug.sourceFileMap": {
409+
"type": "object",
410+
"description": "Optional source file mappings passed to the debug engine.",
411+
"default": {
412+
"/rustc/<id>": "${env:USERPROFILE}/.rustup/toolchains/<toolchain-id>/lib/rustlib/src/rust"
413+
}
392414
}
393415
}
394416
},

editors/code/src/cargo.ts

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import * as cp from 'child_process';
2+
import * as readline from 'readline';
3+
import { OutputChannel } from 'vscode';
4+
5+
interface CompilationArtifact {
6+
fileName: string;
7+
name: string;
8+
kind: string;
9+
isTest: boolean;
10+
}
11+
12+
export class Cargo {
13+
rootFolder: string;
14+
env?: Record<string, string>;
15+
output: OutputChannel;
16+
17+
public constructor(cargoTomlFolder: string, output: OutputChannel, env: Record<string, string> | undefined = undefined) {
18+
this.rootFolder = cargoTomlFolder;
19+
this.output = output;
20+
this.env = env;
21+
}
22+
23+
public async artifactsFromArgs(cargoArgs: string[]): Promise<CompilationArtifact[]> {
24+
const artifacts: CompilationArtifact[] = [];
25+
26+
try {
27+
await this.runCargo(cargoArgs,
28+
message => {
29+
if (message.reason === 'compiler-artifact' && message.executable) {
30+
const isBinary = message.target.crate_types.includes('bin');
31+
const isBuildScript = message.target.kind.includes('custom-build');
32+
if ((isBinary && !isBuildScript) || message.profile.test) {
33+
artifacts.push({
34+
fileName: message.executable,
35+
name: message.target.name,
36+
kind: message.target.kind[0],
37+
isTest: message.profile.test
38+
});
39+
}
40+
}
41+
else if (message.reason === 'compiler-message') {
42+
this.output.append(message.message.rendered);
43+
}
44+
},
45+
stderr => {
46+
this.output.append(stderr);
47+
}
48+
);
49+
}
50+
catch (err) {
51+
this.output.show(true);
52+
throw new Error(`Cargo invocation has failed: ${err}`);
53+
}
54+
55+
return artifacts;
56+
}
57+
58+
public async executableFromArgs(args: string[]): Promise<string> {
59+
const cargoArgs = [...args]; // to remain args unchanged
60+
cargoArgs.push("--message-format=json");
61+
62+
const artifacts = await this.artifactsFromArgs(cargoArgs);
63+
64+
if (artifacts.length === 0) {
65+
throw new Error('No compilation artifacts');
66+
} else if (artifacts.length > 1) {
67+
throw new Error('Multiple compilation artifacts are not supported.');
68+
}
69+
70+
return artifacts[0].fileName;
71+
}
72+
73+
runCargo(
74+
cargoArgs: string[],
75+
onStdoutJson: (obj: any) => void,
76+
onStderrString: (data: string) => void
77+
): Promise<number> {
78+
return new Promise<number>((resolve, reject) => {
79+
const cargo = cp.spawn('cargo', cargoArgs, {
80+
stdio: ['ignore', 'pipe', 'pipe'],
81+
cwd: this.rootFolder,
82+
env: this.env,
83+
});
84+
85+
cargo.on('error', err => {
86+
reject(new Error(`could not launch cargo: ${err}`));
87+
});
88+
cargo.stderr.on('data', chunk => {
89+
onStderrString(chunk.toString());
90+
});
91+
92+
const rl = readline.createInterface({ input: cargo.stdout });
93+
rl.on('line', line => {
94+
const message = JSON.parse(line);
95+
onStdoutJson(message);
96+
});
97+
98+
cargo.on('exit', (exitCode, _) => {
99+
if (exitCode === 0)
100+
resolve(exitCode);
101+
else
102+
reject(new Error(`exit code: ${exitCode}.`));
103+
});
104+
});
105+
}
106+
}

editors/code/src/commands/runnables.ts

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import * as vscode from 'vscode';
22
import * as lc from 'vscode-languageclient';
33
import * as ra from '../rust-analyzer-api';
4+
import * as os from "os";
45

56
import { Ctx, Cmd } from '../ctx';
7+
import { Cargo } from '../cargo';
68

79
export function run(ctx: Ctx): Cmd {
810
let prevRunnable: RunnableQuickPick | undefined;
@@ -62,25 +64,69 @@ export function runSingle(ctx: Ctx): Cmd {
6264
};
6365
}
6466

67+
function getLldbDebugConfig(config: ra.Runnable, sourceFileMap: Record<string, string>): vscode.DebugConfiguration {
68+
return {
69+
type: "lldb",
70+
request: "launch",
71+
name: config.label,
72+
cargo: {
73+
args: config.args,
74+
},
75+
args: config.extraArgs,
76+
cwd: config.cwd,
77+
sourceMap: sourceFileMap
78+
};
79+
}
80+
81+
const debugOutput = vscode.window.createOutputChannel("Debug");
82+
83+
async function getCppvsDebugConfig(config: ra.Runnable, sourceFileMap: Record<string, string>): Promise<vscode.DebugConfiguration> {
84+
debugOutput.clear();
85+
86+
const cargo = new Cargo(config.cwd || '.', debugOutput);
87+
const executable = await cargo.executableFromArgs(config.args);
88+
89+
// if we are here, there were no compilation errors.
90+
return {
91+
type: (os.platform() === "win32") ? "cppvsdbg" : 'cppdbg',
92+
request: "launch",
93+
name: config.label,
94+
program: executable,
95+
args: config.extraArgs,
96+
cwd: config.cwd,
97+
sourceFileMap: sourceFileMap,
98+
};
99+
}
100+
65101
export function debugSingle(ctx: Ctx): Cmd {
66102
return async (config: ra.Runnable) => {
67103
const editor = ctx.activeRustEditor;
68104
if (!editor) return;
69-
if (!vscode.extensions.getExtension("vadimcn.vscode-lldb")) {
70-
vscode.window.showErrorMessage("Install `vadimcn.vscode-lldb` extension for debugging");
105+
106+
const lldbId = "vadimcn.vscode-lldb";
107+
const cpptoolsId = "ms-vscode.cpptools";
108+
109+
const debugEngineId = ctx.config.debug.engine;
110+
let debugEngine = null;
111+
if (debugEngineId === "auto") {
112+
debugEngine = vscode.extensions.getExtension(lldbId);
113+
if (!debugEngine) {
114+
debugEngine = vscode.extensions.getExtension(cpptoolsId);
115+
}
116+
}
117+
else {
118+
debugEngine = vscode.extensions.getExtension(debugEngineId);
119+
}
120+
121+
if (!debugEngine) {
122+
vscode.window.showErrorMessage(`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=${lldbId})`
123+
+ ` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=${cpptoolsId}) extension for debugging.`);
71124
return;
72125
}
73126

74-
const debugConfig = {
75-
type: "lldb",
76-
request: "launch",
77-
name: config.label,
78-
cargo: {
79-
args: config.args,
80-
},
81-
args: config.extraArgs,
82-
cwd: config.cwd
83-
};
127+
const debugConfig = lldbId === debugEngine.id
128+
? getLldbDebugConfig(config, ctx.config.debug.sourceFileMap)
129+
: await getCppvsDebugConfig(config, ctx.config.debug.sourceFileMap);
84130

85131
return vscode.debug.startDebugging(undefined, debugConfig);
86132
};

editors/code/src/config.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ export class Config {
9292
get askBeforeDownload() { return this.get<boolean>("updates.askBeforeDownload"); }
9393
get traceExtension() { return this.get<boolean>("trace.extension"); }
9494

95-
9695
get inlayHints() {
9796
return {
9897
typeHints: this.get<boolean>("inlayHints.typeHints"),
@@ -107,4 +106,12 @@ export class Config {
107106
command: this.get<string>("checkOnSave.command"),
108107
};
109108
}
109+
110+
get debug() {
111+
return {
112+
engine: this.get<string>("debug.engine"),
113+
sourceFileMap: this.get<Record<string, string>>("debug.sourceFileMap"),
114+
};
115+
}
116+
110117
}

0 commit comments

Comments
 (0)