aboutsummaryrefslogtreecommitdiff
path: root/editors/code
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code')
-rw-r--r--editors/code/package.json64
-rw-r--r--editors/code/src/client.ts106
-rw-r--r--editors/code/src/commands.ts93
-rw-r--r--editors/code/src/config.ts13
-rw-r--r--editors/code/src/debug.ts121
-rw-r--r--editors/code/src/lsp_ext.ts34
-rw-r--r--editors/code/src/main.ts1
-rw-r--r--editors/code/src/run.ts96
-rw-r--r--editors/code/src/tasks.ts7
-rw-r--r--editors/code/src/toolchain.ts (renamed from editors/code/src/cargo.ts)117
-rw-r--r--editors/code/src/util.ts18
-rw-r--r--editors/code/tests/unit/launch_config.test.ts14
12 files changed, 432 insertions, 252 deletions
diff --git a/editors/code/package.json b/editors/code/package.json
index 75dbafc05..e2027970d 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -238,7 +238,7 @@
238 }, 238 },
239 "rust-analyzer.cargo.allFeatures": { 239 "rust-analyzer.cargo.allFeatures": {
240 "type": "boolean", 240 "type": "boolean",
241 "default": true, 241 "default": false,
242 "description": "Activate all available features" 242 "description": "Activate all available features"
243 }, 243 },
244 "rust-analyzer.cargo.features": { 244 "rust-analyzer.cargo.features": {
@@ -318,9 +318,23 @@
318 "markdownDescription": "Check all targets and tests (will be passed as `--all-targets`)" 318 "markdownDescription": "Check all targets and tests (will be passed as `--all-targets`)"
319 }, 319 },
320 "rust-analyzer.checkOnSave.allFeatures": { 320 "rust-analyzer.checkOnSave.allFeatures": {
321 "type": "boolean", 321 "type": [
322 "default": true, 322 "null",
323 "markdownDescription": "Check with all features (will be passed as `--all-features`)" 323 "boolean"
324 ],
325 "default": null,
326 "markdownDescription": "Check with all features (will be passed as `--all-features`). Defaults to `rust-analyzer.cargo.allFeatures`."
327 },
328 "rust-analyzer.checkOnSave.features": {
329 "type": [
330 "null",
331 "array"
332 ],
333 "items": {
334 "type": "string"
335 },
336 "default": null,
337 "description": "List of features to activate. Defaults to `rust-analyzer.cargo.features`."
324 }, 338 },
325 "rust-analyzer.inlayHints.enable": { 339 "rust-analyzer.inlayHints.enable": {
326 "type": "boolean", 340 "type": "boolean",
@@ -462,17 +476,53 @@
462 "default": true 476 "default": true
463 }, 477 },
464 "rust-analyzer.lens.run": { 478 "rust-analyzer.lens.run": {
465 "markdownDescription": "Whether to show Run lens. Only applies when `#rust-analyzer.lens.enable#` is set.", 479 "markdownDescription": "Whether to show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
466 "type": "boolean", 480 "type": "boolean",
467 "default": true 481 "default": true
468 }, 482 },
469 "rust-analyzer.lens.debug": { 483 "rust-analyzer.lens.debug": {
470 "markdownDescription": "Whether to show Debug lens. Only applies when `#rust-analyzer.lens.enable#` is set.", 484 "markdownDescription": "Whether to show `Debug` lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
471 "type": "boolean", 485 "type": "boolean",
472 "default": true 486 "default": true
473 }, 487 },
474 "rust-analyzer.lens.implementations": { 488 "rust-analyzer.lens.implementations": {
475 "markdownDescription": "Whether to show Implementations lens. Only applies when `#rust-analyzer.lens.enable#` is set.", 489 "markdownDescription": "Whether to show `Implementations` lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
490 "type": "boolean",
491 "default": true
492 },
493 "rust-analyzer.hoverActions.enable": {
494 "description": "Whether to show HoverActions in Rust files.",
495 "type": "boolean",
496 "default": true
497 },
498 "rust-analyzer.hoverActions.implementations": {
499 "markdownDescription": "Whether to show `Implementations` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.",
500 "type": "boolean",
501 "default": true
502 },
503 "rust-analyzer.hoverActions.run": {
504 "markdownDescription": "Whether to show `Run` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.",
505 "type": "boolean",
506 "default": true
507 },
508 "rust-analyzer.hoverActions.debug": {
509 "markdownDescription": "Whether to show `Debug` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.",
510 "type": "boolean",
511 "default": true
512 },
513 "rust-analyzer.linkedProjects": {
514 "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set of projects. \nElements must be paths pointing to Cargo.toml, rust-project.json, or JSON objects in rust-project.json format",
515 "type": "array",
516 "items": {
517 "type": [
518 "string",
519 "object"
520 ]
521 },
522 "default": null
523 },
524 "rust-analyzer.withSysroot": {
525 "markdownDescription": "Internal config for debugging, disables loading of sysroot crates",
476 "type": "boolean", 526 "type": "boolean",
477 "default": true 527 "default": true
478 } 528 }
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index d64f9a3f9..65ad573d8 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -1,8 +1,25 @@
1import * as lc from 'vscode-languageclient'; 1import * as lc from 'vscode-languageclient';
2import * as vscode from 'vscode'; 2import * as vscode from 'vscode';
3import * as ra from '../src/lsp_ext';
4import * as Is from 'vscode-languageclient/lib/utils/is';
3 5
4import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed'; 6import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed';
5import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed'; 7import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed';
8import { assert } from './util';
9
10function renderCommand(cmd: ra.CommandLink) {
11 return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`;
12}
13
14function renderHoverActions(actions: ra.CommandLinkGroup[]): vscode.MarkdownString {
15 const text = actions.map(group =>
16 (group.title ? (group.title + " ") : "") + group.commands.map(renderCommand).join(' | ')
17 ).join('___');
18
19 const result = new vscode.MarkdownString(text);
20 result.isTrusted = true;
21 return result;
22}
6 23
7export function createClient(serverPath: string, cwd: string): lc.LanguageClient { 24export function createClient(serverPath: string, cwd: string): lc.LanguageClient {
8 // '.' Is the fallback if no folder is open 25 // '.' Is the fallback if no folder is open
@@ -32,6 +49,25 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
32 if (res === undefined) throw new Error('busy'); 49 if (res === undefined) throw new Error('busy');
33 return res; 50 return res;
34 }, 51 },
52 async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, _next: lc.ProvideHoverSignature) {
53 return client.sendRequest(lc.HoverRequest.type, client.code2ProtocolConverter.asTextDocumentPositionParams(document, position), token).then(
54 (result) => {
55 const hover = client.protocol2CodeConverter.asHover(result);
56 if (hover) {
57 const actions = (<any>result).actions;
58 if (actions) {
59 hover.contents.push(renderHoverActions(actions));
60 }
61 }
62 return hover;
63 },
64 (error) => {
65 client.logFailedRequest(lc.HoverRequest.type, error);
66 return Promise.resolve(null);
67 });
68 },
69 // Using custom handling of CodeActions where each code action is resloved lazily
70 // That's why we are not waiting for any command or edits
35 async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { 71 async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) {
36 const params: lc.CodeActionParams = { 72 const params: lc.CodeActionParams = {
37 textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), 73 textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
@@ -43,32 +79,36 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
43 const result: (vscode.CodeAction | vscode.Command)[] = []; 79 const result: (vscode.CodeAction | vscode.Command)[] = [];
44 const groups = new Map<string, { index: number; items: vscode.CodeAction[] }>(); 80 const groups = new Map<string, { index: number; items: vscode.CodeAction[] }>();
45 for (const item of values) { 81 for (const item of values) {
82 // In our case we expect to get code edits only from diagnostics
46 if (lc.CodeAction.is(item)) { 83 if (lc.CodeAction.is(item)) {
84 assert(!item.command, "We don't expect to receive commands in CodeActions");
47 const action = client.protocol2CodeConverter.asCodeAction(item); 85 const action = client.protocol2CodeConverter.asCodeAction(item);
48 const group = actionGroup(item); 86 result.push(action);
49 if (isSnippetEdit(item) || group) { 87 continue;
50 action.command = { 88 }
51 command: "rust-analyzer.applySnippetWorkspaceEdit", 89 assert(isCodeActionWithoutEditsAndCommands(item), "We don't expect edits or commands here");
52 title: "", 90 const action = new vscode.CodeAction(item.title);
53 arguments: [action.edit], 91 const group = (item as any).group;
54 }; 92 const id = (item as any).id;
55 action.edit = undefined; 93 const resolveParams: ra.ResolveCodeActionParams = {
56 } 94 id: id,
57 95 codeActionParams: params
58 if (group) { 96 };
59 let entry = groups.get(group); 97 action.command = {
60 if (!entry) { 98 command: "rust-analyzer.resolveCodeAction",
61 entry = { index: result.length, items: [] }; 99 title: item.title,
62 groups.set(group, entry); 100 arguments: [resolveParams],
63 result.push(action); 101 };
64 } 102 if (group) {
65 entry.items.push(action); 103 let entry = groups.get(group);
66 } else { 104 if (!entry) {
105 entry = { index: result.length, items: [] };
106 groups.set(group, entry);
67 result.push(action); 107 result.push(action);
68 } 108 }
109 entry.items.push(action);
69 } else { 110 } else {
70 const command = client.protocol2CodeConverter.asCommand(item); 111 result.push(action);
71 result.push(command);
72 } 112 }
73 } 113 }
74 for (const [group, { index, items }] of groups) { 114 for (const [group, { index, items }] of groups) {
@@ -80,7 +120,7 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
80 command: "rust-analyzer.applyActionGroup", 120 command: "rust-analyzer.applyActionGroup",
81 title: "", 121 title: "",
82 arguments: [items.map((item) => { 122 arguments: [items.map((item) => {
83 return { label: item.title, edit: item.command!!.arguments!![0] }; 123 return { label: item.title, arguments: item.command!!.arguments!![0] };
84 })], 124 })],
85 }; 125 };
86 result[index] = action; 126 result[index] = action;
@@ -119,24 +159,18 @@ class ExperimentalFeatures implements lc.StaticFeature {
119 const caps: any = capabilities.experimental ?? {}; 159 const caps: any = capabilities.experimental ?? {};
120 caps.snippetTextEdit = true; 160 caps.snippetTextEdit = true;
121 caps.codeActionGroup = true; 161 caps.codeActionGroup = true;
162 caps.resolveCodeAction = true;
163 caps.hoverActions = true;
122 capabilities.experimental = caps; 164 capabilities.experimental = caps;
123 } 165 }
124 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { 166 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void {
125 } 167 }
126} 168}
127 169
128function isSnippetEdit(action: lc.CodeAction): boolean { 170function isCodeActionWithoutEditsAndCommands(value: any): boolean {
129 const documentChanges = action.edit?.documentChanges ?? []; 171 const candidate: lc.CodeAction = value;
130 for (const edit of documentChanges) { 172 return candidate && Is.string(candidate.title) &&
131 if (lc.TextDocumentEdit.is(edit)) { 173 (candidate.diagnostics === void 0 || Is.typedArray(candidate.diagnostics, lc.Diagnostic.is)) &&
132 if (edit.edits.some((indel) => (indel as any).insertTextFormat === lc.InsertTextFormat.Snippet)) { 174 (candidate.kind === void 0 || Is.string(candidate.kind)) &&
133 return true; 175 (candidate.edit === void 0 && candidate.command === void 0);
134 }
135 }
136 }
137 return false;
138}
139
140function actionGroup(action: lc.CodeAction): string | undefined {
141 return (action as any).group;
142} 176}
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 86302db37..3e9c3aa0e 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -8,6 +8,7 @@ import { spawnSync } from 'child_process';
8import { RunnableQuickPick, selectRunnable, createTask } from './run'; 8import { RunnableQuickPick, selectRunnable, createTask } from './run';
9import { AstInspector } from './ast_inspector'; 9import { AstInspector } from './ast_inspector';
10import { isRustDocument, sleep, isRustEditor } from './util'; 10import { isRustDocument, sleep, isRustEditor } from './util';
11import { startDebugSession, makeDebugConfig } from './debug';
11 12
12export * from './ast_inspector'; 13export * from './ast_inspector';
13export * from './run'; 14export * from './run';
@@ -197,20 +198,6 @@ export function toggleInlayHints(ctx: Ctx): Cmd {
197 }; 198 };
198} 199}
199 200
200export function run(ctx: Ctx): Cmd {
201 let prevRunnable: RunnableQuickPick | undefined;
202
203 return async () => {
204 const item = await selectRunnable(ctx, prevRunnable);
205 if (!item) return;
206
207 item.detail = 'rerun';
208 prevRunnable = item;
209 const task = createTask(item.runnable);
210 return await vscode.tasks.executeTask(task);
211 };
212}
213
214// Opens the virtual file that will show the syntax tree 201// Opens the virtual file that will show the syntax tree
215// 202//
216// The contents of the file come from the `TextDocumentContentProvider` 203// The contents of the file come from the `TextDocumentContentProvider`
@@ -356,10 +343,25 @@ export function showReferences(ctx: Ctx): Cmd {
356} 343}
357 344
358export function applyActionGroup(_ctx: Ctx): Cmd { 345export function applyActionGroup(_ctx: Ctx): Cmd {
359 return async (actions: { label: string; edit: vscode.WorkspaceEdit }[]) => { 346 return async (actions: { label: string; arguments: ra.ResolveCodeActionParams }[]) => {
360 const selectedAction = await vscode.window.showQuickPick(actions); 347 const selectedAction = await vscode.window.showQuickPick(actions);
361 if (!selectedAction) return; 348 if (!selectedAction) return;
362 await applySnippetWorkspaceEdit(selectedAction.edit); 349 vscode.commands.executeCommand(
350 'rust-analyzer.resolveCodeAction',
351 selectedAction.arguments,
352 );
353 };
354}
355
356export function resolveCodeAction(ctx: Ctx): Cmd {
357 const client = ctx.client;
358 return async (params: ra.ResolveCodeActionParams) => {
359 const item: lc.WorkspaceEdit = await client.sendRequest(ra.resolveCodeAction, params);
360 if (!item) {
361 return;
362 }
363 const edit = client.protocol2CodeConverter.asWorkspaceEdit(item);
364 await applySnippetWorkspaceEdit(edit);
363 }; 365 };
364} 366}
365 367
@@ -368,3 +370,62 @@ export function applySnippetWorkspaceEditCommand(_ctx: Ctx): Cmd {
368 await applySnippetWorkspaceEdit(edit); 370 await applySnippetWorkspaceEdit(edit);
369 }; 371 };
370} 372}
373
374export function run(ctx: Ctx): Cmd {
375 let prevRunnable: RunnableQuickPick | undefined;
376
377 return async () => {
378 const item = await selectRunnable(ctx, prevRunnable);
379 if (!item) return;
380
381 item.detail = 'rerun';
382 prevRunnable = item;
383 const task = createTask(item.runnable);
384 return await vscode.tasks.executeTask(task);
385 };
386}
387
388export function runSingle(ctx: Ctx): Cmd {
389 return async (runnable: ra.Runnable) => {
390 const editor = ctx.activeRustEditor;
391 if (!editor) return;
392
393 const task = createTask(runnable);
394 task.group = vscode.TaskGroup.Build;
395 task.presentationOptions = {
396 reveal: vscode.TaskRevealKind.Always,
397 panel: vscode.TaskPanelKind.Dedicated,
398 clear: true,
399 };
400
401 return vscode.tasks.executeTask(task);
402 };
403}
404
405export function debug(ctx: Ctx): Cmd {
406 let prevDebuggee: RunnableQuickPick | undefined;
407
408 return async () => {
409 const item = await selectRunnable(ctx, prevDebuggee, true);
410 if (!item) return;
411
412 item.detail = 'restart';
413 prevDebuggee = item;
414 return await startDebugSession(ctx, item.runnable);
415 };
416}
417
418export function debugSingle(ctx: Ctx): Cmd {
419 return async (config: ra.Runnable) => {
420 await startDebugSession(ctx, config);
421 };
422}
423
424export function newDebugConfig(ctx: Ctx): Cmd {
425 return async () => {
426 const item = await selectRunnable(ctx, undefined, true, false);
427 if (!item) return;
428
429 await makeDebugConfig(ctx, item.runnable);
430 };
431}
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index e8abf8284..d8f0037d4 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -16,10 +16,8 @@ export class Config {
16 "files", 16 "files",
17 "highlighting", 17 "highlighting",
18 "updates.channel", 18 "updates.channel",
19 "lens.enable", 19 "lens", // works as lens.*
20 "lens.run", 20 "hoverActions", // works as hoverActions.*
21 "lens.debug",
22 "lens.implementations",
23 ] 21 ]
24 .map(opt => `${this.rootSection}.${opt}`); 22 .map(opt => `${this.rootSection}.${opt}`);
25 23
@@ -132,4 +130,11 @@ export class Config {
132 implementations: this.get<boolean>("lens.implementations"), 130 implementations: this.get<boolean>("lens.implementations"),
133 }; 131 };
134 } 132 }
133
134 get hoverActions() {
135 return {
136 enable: this.get<boolean>("hoverActions.enable"),
137 implementations: this.get<boolean>("hoverActions.implementations"),
138 };
139 }
135} 140}
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts
index 027504ecd..a0c9b3ab2 100644
--- a/editors/code/src/debug.ts
+++ b/editors/code/src/debug.ts
@@ -3,46 +3,59 @@ import * as vscode from 'vscode';
3import * as path from 'path'; 3import * as path from 'path';
4import * as ra from './lsp_ext'; 4import * as ra from './lsp_ext';
5 5
6import { Cargo } from './cargo'; 6import { Cargo } from './toolchain';
7import { Ctx } from "./ctx"; 7import { Ctx } from "./ctx";
8 8
9const debugOutput = vscode.window.createOutputChannel("Debug"); 9const debugOutput = vscode.window.createOutputChannel("Debug");
10type DebugConfigProvider = (config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>) => vscode.DebugConfiguration; 10type DebugConfigProvider = (config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>) => vscode.DebugConfiguration;
11 11
12function getLldbDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration { 12export async function makeDebugConfig(ctx: Ctx, runnable: ra.Runnable): Promise<void> {
13 return { 13 const scope = ctx.activeRustEditor?.document.uri;
14 type: "lldb", 14 if (!scope) return;
15 request: "launch",
16 name: config.label,
17 program: executable,
18 args: config.extraArgs,
19 cwd: config.cwd,
20 sourceMap: sourceFileMap,
21 sourceLanguages: ["rust"]
22 };
23}
24 15
25function getCppvsDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration { 16 const debugConfig = await getDebugConfiguration(ctx, runnable);
26 return { 17 if (!debugConfig) return;
27 type: (os.platform() === "win32") ? "cppvsdbg" : "cppdbg", 18
28 request: "launch", 19 const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope);
29 name: config.label, 20 const configurations = wsLaunchSection.get<any[]>("configurations") || [];
30 program: executable, 21
31 args: config.extraArgs, 22 const index = configurations.findIndex(c => c.name === debugConfig.name);
32 cwd: config.cwd, 23 if (index !== -1) {
33 sourceFileMap: sourceFileMap, 24 const answer = await vscode.window.showErrorMessage(`Launch configuration '${debugConfig.name}' already exists!`, 'Cancel', 'Update');
34 }; 25 if (answer === "Cancel") return;
26
27 configurations[index] = debugConfig;
28 } else {
29 configurations.push(debugConfig);
30 }
31
32 await wsLaunchSection.update("configurations", configurations);
35} 33}
36 34
37async function getDebugExecutable(config: ra.Runnable): Promise<string> { 35export async function startDebugSession(ctx: Ctx, runnable: ra.Runnable): Promise<boolean> {
38 const cargo = new Cargo(config.cwd || '.', debugOutput); 36 let debugConfig: vscode.DebugConfiguration | undefined = undefined;
39 const executable = await cargo.executableFromArgs(config.args); 37 let message = "";
40 38
41 // if we are here, there were no compilation errors. 39 const wsLaunchSection = vscode.workspace.getConfiguration("launch");
42 return executable; 40 const configurations = wsLaunchSection.get<any[]>("configurations") || [];
41
42 const index = configurations.findIndex(c => c.name === runnable.label);
43 if (-1 !== index) {
44 debugConfig = configurations[index];
45 message = " (from launch.json)";
46 debugOutput.clear();
47 } else {
48 debugConfig = await getDebugConfiguration(ctx, runnable);
49 }
50
51 if (!debugConfig) return false;
52
53 debugOutput.appendLine(`Launching debug configuration${message}:`);
54 debugOutput.appendLine(JSON.stringify(debugConfig, null, 2));
55 return vscode.debug.startDebugging(undefined, debugConfig);
43} 56}
44 57
45export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Promise<vscode.DebugConfiguration | undefined> { 58async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<vscode.DebugConfiguration | undefined> {
46 const editor = ctx.activeRustEditor; 59 const editor = ctx.activeRustEditor;
47 if (!editor) return; 60 if (!editor) return;
48 61
@@ -78,8 +91,8 @@ export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Prom
78 return path.normalize(p).replace(wsFolder, '${workspaceRoot}'); 91 return path.normalize(p).replace(wsFolder, '${workspaceRoot}');
79 } 92 }
80 93
81 const executable = await getDebugExecutable(config); 94 const executable = await getDebugExecutable(runnable);
82 const debugConfig = knownEngines[debugEngine.id](config, simplifyPath(executable), debugOptions.sourceFileMap); 95 const debugConfig = knownEngines[debugEngine.id](runnable, simplifyPath(executable), debugOptions.sourceFileMap);
83 if (debugConfig.type in debugOptions.engineSettings) { 96 if (debugConfig.type in debugOptions.engineSettings) {
84 const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; 97 const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type];
85 for (var key in settingsMap) { 98 for (var key in settingsMap) {
@@ -100,25 +113,35 @@ export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Prom
100 return debugConfig; 113 return debugConfig;
101} 114}
102 115
103export async function startDebugSession(ctx: Ctx, config: ra.Runnable): Promise<boolean> { 116async function getDebugExecutable(runnable: ra.Runnable): Promise<string> {
104 let debugConfig: vscode.DebugConfiguration | undefined = undefined; 117 const cargo = new Cargo(runnable.args.workspaceRoot || '.', debugOutput);
105 let message = ""; 118 const executable = await cargo.executableFromArgs(runnable.args.cargoArgs);
106
107 const wsLaunchSection = vscode.workspace.getConfiguration("launch");
108 const configurations = wsLaunchSection.get<any[]>("configurations") || [];
109 119
110 const index = configurations.findIndex(c => c.name === config.label); 120 // if we are here, there were no compilation errors.
111 if (-1 !== index) { 121 return executable;
112 debugConfig = configurations[index]; 122}
113 message = " (from launch.json)";
114 debugOutput.clear();
115 } else {
116 debugConfig = await getDebugConfiguration(ctx, config);
117 }
118 123
119 if (!debugConfig) return false; 124function getLldbDebugConfig(runnable: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration {
125 return {
126 type: "lldb",
127 request: "launch",
128 name: runnable.label,
129 program: executable,
130 args: runnable.args.executableArgs,
131 cwd: runnable.args.workspaceRoot,
132 sourceMap: sourceFileMap,
133 sourceLanguages: ["rust"]
134 };
135}
120 136
121 debugOutput.appendLine(`Launching debug configuration${message}:`); 137function getCppvsDebugConfig(runnable: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration {
122 debugOutput.appendLine(JSON.stringify(debugConfig, null, 2)); 138 return {
123 return vscode.debug.startDebugging(undefined, debugConfig); 139 type: (os.platform() === "win32") ? "cppvsdbg" : "cppdbg",
140 request: "launch",
141 name: runnable.label,
142 program: executable,
143 args: runnable.args.executableArgs,
144 cwd: runnable.args.workspaceRoot,
145 sourceFileMap: sourceFileMap,
146 };
124} 147}
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index 4da12eb30..e16ea799c 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -33,6 +33,12 @@ export const matchingBrace = new lc.RequestType<MatchingBraceParams, lc.Position
33 33
34export const parentModule = new lc.RequestType<lc.TextDocumentPositionParams, lc.LocationLink[], void>("experimental/parentModule"); 34export const parentModule = new lc.RequestType<lc.TextDocumentPositionParams, lc.LocationLink[], void>("experimental/parentModule");
35 35
36export interface ResolveCodeActionParams {
37 id: string;
38 codeActionParams: lc.CodeActionParams;
39}
40export const resolveCodeAction = new lc.RequestType<ResolveCodeActionParams, lc.WorkspaceEdit, unknown>('experimental/resolveCodeAction');
41
36export interface JoinLinesParams { 42export interface JoinLinesParams {
37 textDocument: lc.TextDocumentIdentifier; 43 textDocument: lc.TextDocumentIdentifier;
38 ranges: lc.Range[]; 44 ranges: lc.Range[];
@@ -45,16 +51,18 @@ export interface RunnablesParams {
45 textDocument: lc.TextDocumentIdentifier; 51 textDocument: lc.TextDocumentIdentifier;
46 position: lc.Position | null; 52 position: lc.Position | null;
47} 53}
54
48export interface Runnable { 55export interface Runnable {
49 range: lc.Range;
50 label: string; 56 label: string;
51 bin: string; 57 location?: lc.LocationLink;
52 args: string[]; 58 kind: "cargo";
53 extraArgs: string[]; 59 args: {
54 env: { [key: string]: string }; 60 workspaceRoot?: string;
55 cwd: string | null; 61 cargoArgs: string[];
62 executableArgs: string[];
63 };
56} 64}
57export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>("rust-analyzer/runnables"); 65export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>("experimental/runnables");
58 66
59export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint | InlayHint.ChainingHint; 67export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint | InlayHint.ChainingHint;
60 68
@@ -82,3 +90,15 @@ export interface SsrParams {
82 parseOnly: boolean; 90 parseOnly: boolean;
83} 91}
84export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>('experimental/ssr'); 92export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>('experimental/ssr');
93
94export interface CommandLink extends lc.Command {
95 /**
96 * A tooltip for the command, when represented in the UI.
97 */
98 tooltip?: string;
99}
100
101export interface CommandLinkGroup {
102 title?: string;
103 commands: CommandLink[];
104}
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index b7337621c..a92c676fa 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -98,6 +98,7 @@ export async function activate(context: vscode.ExtensionContext) {
98 ctx.registerCommand('debugSingle', commands.debugSingle); 98 ctx.registerCommand('debugSingle', commands.debugSingle);
99 ctx.registerCommand('showReferences', commands.showReferences); 99 ctx.registerCommand('showReferences', commands.showReferences);
100 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand); 100 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand);
101 ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction);
101 ctx.registerCommand('applyActionGroup', commands.applyActionGroup); 102 ctx.registerCommand('applyActionGroup', commands.applyActionGroup);
102 103
103 ctx.pushCleanup(activateTaskProvider(workspaceFolder)); 104 ctx.pushCleanup(activateTaskProvider(workspaceFolder));
diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts
index 8e1ba83ed..bb060cfe1 100644
--- a/editors/code/src/run.ts
+++ b/editors/code/src/run.ts
@@ -1,9 +1,10 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3import * as ra from './lsp_ext'; 3import * as ra from './lsp_ext';
4import * as toolchain from "./toolchain";
4 5
5import { Ctx, Cmd } from './ctx'; 6import { Ctx } from './ctx';
6import { startDebugSession, getDebugConfiguration } from './debug'; 7import { makeDebugConfig } from './debug';
7 8
8const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; 9const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }];
9 10
@@ -64,7 +65,7 @@ export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick,
64 quickPick.onDidHide(() => close()), 65 quickPick.onDidHide(() => close()),
65 quickPick.onDidAccept(() => close(quickPick.selectedItems[0])), 66 quickPick.onDidAccept(() => close(quickPick.selectedItems[0])),
66 quickPick.onDidTriggerButton((_button) => { 67 quickPick.onDidTriggerButton((_button) => {
67 (async () => await makeDebugConfig(ctx, quickPick.activeItems[0]))(); 68 (async () => await makeDebugConfig(ctx, quickPick.activeItems[0].runnable))();
68 close(); 69 close();
69 }), 70 }),
70 quickPick.onDidChangeActive((active) => { 71 quickPick.onDidChangeActive((active) => {
@@ -83,74 +84,6 @@ export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick,
83 }); 84 });
84} 85}
85 86
86export function runSingle(ctx: Ctx): Cmd {
87 return async (runnable: ra.Runnable) => {
88 const editor = ctx.activeRustEditor;
89 if (!editor) return;
90
91 const task = createTask(runnable);
92 task.group = vscode.TaskGroup.Build;
93 task.presentationOptions = {
94 reveal: vscode.TaskRevealKind.Always,
95 panel: vscode.TaskPanelKind.Dedicated,
96 clear: true,
97 };
98
99 return vscode.tasks.executeTask(task);
100 };
101}
102
103export function debug(ctx: Ctx): Cmd {
104 let prevDebuggee: RunnableQuickPick | undefined;
105
106 return async () => {
107 const item = await selectRunnable(ctx, prevDebuggee, true);
108 if (!item) return;
109
110 item.detail = 'restart';
111 prevDebuggee = item;
112 return await startDebugSession(ctx, item.runnable);
113 };
114}
115
116export function debugSingle(ctx: Ctx): Cmd {
117 return async (config: ra.Runnable) => {
118 await startDebugSession(ctx, config);
119 };
120}
121
122async function makeDebugConfig(ctx: Ctx, item: RunnableQuickPick): Promise<void> {
123 const scope = ctx.activeRustEditor?.document.uri;
124 if (!scope) return;
125
126 const debugConfig = await getDebugConfiguration(ctx, item.runnable);
127 if (!debugConfig) return;
128
129 const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope);
130 const configurations = wsLaunchSection.get<any[]>("configurations") || [];
131
132 const index = configurations.findIndex(c => c.name === debugConfig.name);
133 if (index !== -1) {
134 const answer = await vscode.window.showErrorMessage(`Launch configuration '${debugConfig.name}' already exists!`, 'Cancel', 'Update');
135 if (answer === "Cancel") return;
136
137 configurations[index] = debugConfig;
138 } else {
139 configurations.push(debugConfig);
140 }
141
142 await wsLaunchSection.update("configurations", configurations);
143}
144
145export function newDebugConfig(ctx: Ctx): Cmd {
146 return async () => {
147 const item = await selectRunnable(ctx, undefined, true, false);
148 if (!item) return;
149
150 await makeDebugConfig(ctx, item);
151 };
152}
153
154export class RunnableQuickPick implements vscode.QuickPickItem { 87export class RunnableQuickPick implements vscode.QuickPickItem {
155 public label: string; 88 public label: string;
156 public description?: string | undefined; 89 public description?: string | undefined;
@@ -170,18 +103,27 @@ interface CargoTaskDefinition extends vscode.TaskDefinition {
170 env?: { [key: string]: string }; 103 env?: { [key: string]: string };
171} 104}
172 105
173export function createTask(spec: ra.Runnable): vscode.Task { 106export function createTask(runnable: ra.Runnable): vscode.Task {
174 const TASK_SOURCE = 'Rust'; 107 const TASK_SOURCE = 'Rust';
108
109 let command;
110 switch (runnable.kind) {
111 case "cargo": command = toolchain.getPathForExecutable("cargo");
112 }
113 const args = [...runnable.args.cargoArgs]; // should be a copy!
114 if (runnable.args.executableArgs.length > 0) {
115 args.push('--', ...runnable.args.executableArgs);
116 }
175 const definition: CargoTaskDefinition = { 117 const definition: CargoTaskDefinition = {
176 type: 'cargo', 118 type: 'cargo',
177 label: spec.label, 119 label: runnable.label,
178 command: spec.bin, 120 command,
179 args: spec.extraArgs ? [...spec.args, '--', ...spec.extraArgs] : spec.args, 121 args,
180 env: Object.assign({}, process.env, spec.env), 122 env: Object.assign({}, process.env as { [key: string]: string }, { "RUST_BACKTRACE": "short" }),
181 }; 123 };
182 124
183 const execOption: vscode.ShellExecutionOptions = { 125 const execOption: vscode.ShellExecutionOptions = {
184 cwd: spec.cwd || '.', 126 cwd: runnable.args.workspaceRoot || '.',
185 env: definition.env, 127 env: definition.env,
186 }; 128 };
187 const exec = new vscode.ShellExecution( 129 const exec = new vscode.ShellExecution(
diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts
index 1366c76d6..9748824df 100644
--- a/editors/code/src/tasks.ts
+++ b/editors/code/src/tasks.ts
@@ -1,4 +1,5 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as toolchain from "./toolchain";
2 3
3// This ends up as the `type` key in tasks.json. RLS also uses `cargo` and 4// This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
4// our configuration should be compatible with it so use the same key. 5// our configuration should be compatible with it so use the same key.
@@ -24,6 +25,8 @@ class CargoTaskProvider implements vscode.TaskProvider {
24 // set of tasks that always exist. These tasks cannot be removed in 25 // set of tasks that always exist. These tasks cannot be removed in
25 // tasks.json - only tweaked. 26 // tasks.json - only tweaked.
26 27
28 const cargoPath = toolchain.cargoPath();
29
27 return [ 30 return [
28 { command: 'build', group: vscode.TaskGroup.Build }, 31 { command: 'build', group: vscode.TaskGroup.Build },
29 { command: 'check', group: vscode.TaskGroup.Build }, 32 { command: 'check', group: vscode.TaskGroup.Build },
@@ -46,7 +49,7 @@ class CargoTaskProvider implements vscode.TaskProvider {
46 `cargo ${command}`, 49 `cargo ${command}`,
47 'rust', 50 'rust',
48 // What to do when this command is executed. 51 // What to do when this command is executed.
49 new vscode.ShellExecution('cargo', [command]), 52 new vscode.ShellExecution(cargoPath, [command]),
50 // Problem matchers. 53 // Problem matchers.
51 ['$rustc'], 54 ['$rustc'],
52 ); 55 );
@@ -80,4 +83,4 @@ class CargoTaskProvider implements vscode.TaskProvider {
80export function activateTaskProvider(target: vscode.WorkspaceFolder): vscode.Disposable { 83export function activateTaskProvider(target: vscode.WorkspaceFolder): vscode.Disposable {
81 const provider = new CargoTaskProvider(target); 84 const provider = new CargoTaskProvider(target);
82 return vscode.tasks.registerTaskProvider(TASK_TYPE, provider); 85 return vscode.tasks.registerTaskProvider(TASK_TYPE, provider);
83} \ No newline at end of file 86}
diff --git a/editors/code/src/cargo.ts b/editors/code/src/toolchain.ts
index a55b2f860..80a7915e9 100644
--- a/editors/code/src/cargo.ts
+++ b/editors/code/src/toolchain.ts
@@ -1,9 +1,10 @@
1import * as cp from 'child_process'; 1import * as cp from 'child_process';
2import * as os from 'os'; 2import * as os from 'os';
3import * as path from 'path'; 3import * as path from 'path';
4import * as fs from 'fs';
4import * as readline from 'readline'; 5import * as readline from 'readline';
5import { OutputChannel } from 'vscode'; 6import { OutputChannel } from 'vscode';
6import { isValidExecutable } from './util'; 7import { log, memoize } from './util';
7 8
8interface CompilationArtifact { 9interface CompilationArtifact {
9 fileName: string; 10 fileName: string;
@@ -17,33 +18,34 @@ export interface ArtifactSpec {
17 filter?: (artifacts: CompilationArtifact[]) => CompilationArtifact[]; 18 filter?: (artifacts: CompilationArtifact[]) => CompilationArtifact[];
18} 19}
19 20
20export function artifactSpec(args: readonly string[]): ArtifactSpec { 21export class Cargo {
21 const cargoArgs = [...args, "--message-format=json"]; 22 constructor(readonly rootFolder: string, readonly output: OutputChannel) { }
22 23
23 // arguments for a runnable from the quick pick should be updated. 24 // Made public for testing purposes
24 // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens 25 static artifactSpec(args: readonly string[]): ArtifactSpec {
25 switch (cargoArgs[0]) { 26 const cargoArgs = [...args, "--message-format=json"];
26 case "run": cargoArgs[0] = "build"; break; 27
27 case "test": { 28 // arguments for a runnable from the quick pick should be updated.
28 if (!cargoArgs.includes("--no-run")) { 29 // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens
29 cargoArgs.push("--no-run"); 30 switch (cargoArgs[0]) {
31 case "run": cargoArgs[0] = "build"; break;
32 case "test": {
33 if (!cargoArgs.includes("--no-run")) {
34 cargoArgs.push("--no-run");
35 }
36 break;
30 } 37 }
31 break;
32 } 38 }
33 }
34 39
35 const result: ArtifactSpec = { cargoArgs: cargoArgs }; 40 const result: ArtifactSpec = { cargoArgs: cargoArgs };
36 if (cargoArgs[0] === "test") { 41 if (cargoArgs[0] === "test") {
37 // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests 42 // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests
38 // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"} 43 // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"}
39 result.filter = (artifacts) => artifacts.filter(it => it.isTest); 44 result.filter = (artifacts) => artifacts.filter(it => it.isTest);
40 } 45 }
41
42 return result;
43}
44 46
45export class Cargo { 47 return result;
46 constructor(readonly rootFolder: string, readonly output: OutputChannel) { } 48 }
47 49
48 private async getArtifacts(spec: ArtifactSpec): Promise<CompilationArtifact[]> { 50 private async getArtifacts(spec: ArtifactSpec): Promise<CompilationArtifact[]> {
49 const artifacts: CompilationArtifact[] = []; 51 const artifacts: CompilationArtifact[] = [];
@@ -77,7 +79,7 @@ export class Cargo {
77 } 79 }
78 80
79 async executableFromArgs(args: readonly string[]): Promise<string> { 81 async executableFromArgs(args: readonly string[]): Promise<string> {
80 const artifacts = await this.getArtifacts(artifactSpec(args)); 82 const artifacts = await this.getArtifacts(Cargo.artifactSpec(args));
81 83
82 if (artifacts.length === 0) { 84 if (artifacts.length === 0) {
83 throw new Error('No compilation artifacts'); 85 throw new Error('No compilation artifacts');
@@ -94,14 +96,7 @@ export class Cargo {
94 onStderrString: (data: string) => void 96 onStderrString: (data: string) => void
95 ): Promise<number> { 97 ): Promise<number> {
96 return new Promise((resolve, reject) => { 98 return new Promise((resolve, reject) => {
97 let cargoPath; 99 const cargo = cp.spawn(cargoPath(), cargoArgs, {
98 try {
99 cargoPath = getCargoPathOrFail();
100 } catch (err) {
101 return reject(err);
102 }
103
104 const cargo = cp.spawn(cargoPath, cargoArgs, {
105 stdio: ['ignore', 'pipe', 'pipe'], 100 stdio: ['ignore', 'pipe', 'pipe'],
106 cwd: this.rootFolder 101 cwd: this.rootFolder
107 }); 102 });
@@ -126,26 +121,54 @@ export class Cargo {
126 } 121 }
127} 122}
128 123
129// Mirrors `ra_env::get_path_for_executable` implementation 124/** Mirrors `ra_toolchain::cargo()` implementation */
130function getCargoPathOrFail(): string { 125export function cargoPath(): string {
131 const envVar = process.env.CARGO; 126 return getPathForExecutable("cargo");
132 const executableName = "cargo"; 127}
128
129/** Mirrors `ra_toolchain::get_path_for_executable()` implementation */
130export const getPathForExecutable = memoize(
131 // We apply caching to decrease file-system interactions
132 (executableName: "cargo" | "rustc" | "rustup"): string => {
133 {
134 const envVar = process.env[executableName.toUpperCase()];
135 if (envVar) return envVar;
136 }
137
138 if (lookupInPath(executableName)) return executableName;
133 139
134 if (envVar) { 140 try {
135 if (isValidExecutable(envVar)) return envVar; 141 // hmm, `os.homedir()` seems to be infallible
142 // it is not mentioned in docs and cannot be infered by the type signature...
143 const standardPath = path.join(os.homedir(), ".cargo", "bin", executableName);
136 144
137 throw new Error(`\`${envVar}\` environment variable points to something that's not a valid executable`); 145 if (isFile(standardPath)) return standardPath;
146 } catch (err) {
147 log.error("Failed to read the fs info", err);
148 }
149 return executableName;
138 } 150 }
151);
139 152
140 if (isValidExecutable(executableName)) return executableName; 153function lookupInPath(exec: string): boolean {
154 const paths = process.env.PATH ?? "";;
141 155
142 const standardLocation = path.join(os.homedir(), '.cargo', 'bin', executableName); 156 const candidates = paths.split(path.delimiter).flatMap(dirInPath => {
157 const candidate = path.join(dirInPath, exec);
158 return os.type() === "Windows_NT"
159 ? [candidate, `${candidate}.exe`]
160 : [candidate];
161 });
143 162
144 if (isValidExecutable(standardLocation)) return standardLocation; 163 return candidates.some(isFile);
164}
145 165
146 throw new Error( 166function isFile(suspectPath: string): boolean {
147 `Failed to find \`${executableName}\` executable. ` + 167 // It is not mentionned in docs, but `statSync()` throws an error when
148 `Make sure \`${executableName}\` is in \`$PATH\`, ` + 168 // the path doesn't exist
149 `or set \`${envVar}\` to point to a valid executable.` 169 try {
150 ); 170 return fs.statSync(suspectPath).isFile();
171 } catch {
172 return false;
173 }
151} 174}
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts
index 352ef9162..fe3fb71cd 100644
--- a/editors/code/src/util.ts
+++ b/editors/code/src/util.ts
@@ -99,3 +99,21 @@ export function isValidExecutable(path: string): boolean {
99export function setContextValue(key: string, value: any): Thenable<void> { 99export function setContextValue(key: string, value: any): Thenable<void> {
100 return vscode.commands.executeCommand('setContext', key, value); 100 return vscode.commands.executeCommand('setContext', key, value);
101} 101}
102
103/**
104 * Returns a higher-order function that caches the results of invoking the
105 * underlying function.
106 */
107export function memoize<Ret, TThis, Param extends string>(func: (this: TThis, arg: Param) => Ret) {
108 const cache = new Map<string, Ret>();
109
110 return function(this: TThis, arg: Param) {
111 const cached = cache.get(arg);
112 if (cached) return cached;
113
114 const result = func.call(this, arg);
115 cache.set(arg, result);
116
117 return result;
118 };
119}
diff --git a/editors/code/tests/unit/launch_config.test.ts b/editors/code/tests/unit/launch_config.test.ts
index d5cf1b74e..68794d53e 100644
--- a/editors/code/tests/unit/launch_config.test.ts
+++ b/editors/code/tests/unit/launch_config.test.ts
@@ -1,25 +1,25 @@
1import * as assert from 'assert'; 1import * as assert from 'assert';
2import * as cargo from '../../src/cargo'; 2import { Cargo } from '../../src/toolchain';
3 3
4suite('Launch configuration', () => { 4suite('Launch configuration', () => {
5 5
6 suite('Lens', () => { 6 suite('Lens', () => {
7 test('A binary', async () => { 7 test('A binary', async () => {
8 const args = cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "pkg_name"]); 8 const args = Cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "pkg_name"]);
9 9
10 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]); 10 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]);
11 assert.deepEqual(args.filter, undefined); 11 assert.deepEqual(args.filter, undefined);
12 }); 12 });
13 13
14 test('One of Multiple Binaries', async () => { 14 test('One of Multiple Binaries', async () => {
15 const args = cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "bin1"]); 15 const args = Cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "bin1"]);
16 16
17 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin1", "--message-format=json"]); 17 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin1", "--message-format=json"]);
18 assert.deepEqual(args.filter, undefined); 18 assert.deepEqual(args.filter, undefined);
19 }); 19 });
20 20
21 test('A test', async () => { 21 test('A test', async () => {
22 const args = cargo.artifactSpec(["test", "--package", "pkg_name", "--lib", "--no-run"]); 22 const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib", "--no-run"]);
23 23
24 assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--no-run", "--message-format=json"]); 24 assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--no-run", "--message-format=json"]);
25 assert.notDeepEqual(args.filter, undefined); 25 assert.notDeepEqual(args.filter, undefined);
@@ -28,7 +28,7 @@ suite('Launch configuration', () => {
28 28
29 suite('QuickPick', () => { 29 suite('QuickPick', () => {
30 test('A binary', async () => { 30 test('A binary', async () => {
31 const args = cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "pkg_name"]); 31 const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "pkg_name"]);
32 32
33 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]); 33 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]);
34 assert.deepEqual(args.filter, undefined); 34 assert.deepEqual(args.filter, undefined);
@@ -36,14 +36,14 @@ suite('Launch configuration', () => {
36 36
37 37
38 test('One of Multiple Binaries', async () => { 38 test('One of Multiple Binaries', async () => {
39 const args = cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "bin2"]); 39 const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "bin2"]);
40 40
41 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin2", "--message-format=json"]); 41 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin2", "--message-format=json"]);
42 assert.deepEqual(args.filter, undefined); 42 assert.deepEqual(args.filter, undefined);
43 }); 43 });
44 44
45 test('A test', async () => { 45 test('A test', async () => {
46 const args = cargo.artifactSpec(["test", "--package", "pkg_name", "--lib"]); 46 const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib"]);
47 47
48 assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--message-format=json", "--no-run"]); 48 assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--message-format=json", "--no-run"]);
49 assert.notDeepEqual(args.filter, undefined); 49 assert.notDeepEqual(args.filter, undefined);