Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--editors/code/package.json18
-rw-r--r--editors/code/src/commands.ts41
-rw-r--r--editors/code/src/ctx.ts55
-rw-r--r--editors/code/src/dependencies_provider.ts151
-rw-r--r--editors/code/src/main.ts102
-rw-r--r--editors/code/src/toolchain.ts113
6 files changed, 410 insertions, 70 deletions
diff --git a/editors/code/package.json b/editors/code/package.json
index f36e34b6a1..5e2a1c69e9 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -284,6 +284,14 @@
"command": "rust-analyzer.clearFlycheck",
"title": "Clear flycheck diagnostics",
"category": "rust-analyzer"
+ },
+ {
+ "command": "rust-analyzer.openFile",
+ "title": "Open File"
+ },
+ {
+ "command": "rust-analyzer.revealDependency",
+ "title": "Reveal File"
}
],
"keybindings": [
@@ -1956,6 +1964,14 @@
}
]
},
+ "views": {
+ "explorer": [
+ {
+ "id": "rustDependencies",
+ "name": "Rust Dependencies"
+ }
+ ]
+ },
"jsonValidation": [
{
"fileMatch": "rust-project.json",
@@ -1963,4 +1979,4 @@
}
]
}
-}
+} \ No newline at end of file
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 2d5272d199..e5aa06025b 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -8,10 +8,11 @@ import { applySnippetWorkspaceEdit, applySnippetTextEdits } from "./snippets";
import { spawnSync } from "child_process";
import { RunnableQuickPick, selectRunnable, createTask, createArgs } from "./run";
import { AstInspector } from "./ast_inspector";
-import { isRustDocument, isCargoTomlDocument, sleep, isRustEditor } from "./util";
+import { isRustDocument, isCargoTomlDocument, sleep, isRustEditor, RustEditor } from './util';
import { startDebugSession, makeDebugConfig } from "./debug";
import { LanguageClient } from "vscode-languageclient/node";
import { LINKED_COMMANDS } from "./client";
+import { DependencyId } from './dependencies_provider';
export * from "./ast_inspector";
export * from "./run";
@@ -266,6 +267,44 @@ export function openCargoToml(ctx: CtxInit): Cmd {
};
}
+export function openFile(_ctx: CtxInit): Cmd {
+ return async (uri: vscode.Uri) => {
+ try {
+ await vscode.window.showTextDocument(uri);
+ } catch (err) {
+ await vscode.window.showErrorMessage(err.message);
+ }
+ };
+}
+
+export function revealDependency(ctx: CtxInit): Cmd {
+ return async (editor: RustEditor) => {
+ const rootPath = vscode.workspace.workspaceFolders![0].uri.fsPath;
+ const documentPath = editor.document.uri.fsPath;
+ if (documentPath.startsWith(rootPath)) return;
+ const dep = ctx.dependencies.getDependency(documentPath);
+ if (dep) {
+ await ctx.treeView.reveal(dep, { select: true, expand: true });
+ } else {
+ let documentPath = editor.document.uri.fsPath;
+ const parentChain: DependencyId[] = [{ id: documentPath.toLowerCase() }];
+ do {
+ documentPath = path.dirname(documentPath);
+ parentChain.push({ id: documentPath.toLowerCase() });
+ }
+ while (!ctx.dependencies.contains(documentPath));
+ parentChain.reverse();
+ for (const idx in parentChain) {
+ await ctx.treeView.reveal(parentChain[idx], { select: true, expand: true });
+ }
+ }
+ };
+}
+
+export async function execRevealDependency(e: RustEditor): Promise<void> {
+ await vscode.commands.executeCommand('rust-analyzer.revealDependency', e);
+}
+
export function ssr(ctx: CtxInit): Cmd {
return async () => {
const editor = vscode.window.activeTextEditor;
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index 567b9216bc..e6829ac4b9 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -3,8 +3,8 @@ import * as lc from "vscode-languageclient/node";
import * as ra from "./lsp_ext";
import * as path from "path";
-import { Config, prepareVSCodeConfig } from "./config";
-import { createClient } from "./client";
+import {Config, prepareVSCodeConfig} from './config';
+import {createClient} from './client';
import {
executeDiscoverProject,
isRustDocument,
@@ -12,11 +12,13 @@ import {
LazyOutputChannel,
log,
RustEditor,
-} from "./util";
-import { ServerStatusParams } from "./lsp_ext";
-import { PersistentState } from "./persistent_state";
-import { bootstrap } from "./bootstrap";
-import { ExecOptions } from "child_process";
+} from './util';
+import {ServerStatusParams} from './lsp_ext';
+import {Dependency, DependencyFile, RustDependenciesProvider, DependencyId} from './dependencies_provider';
+import {execRevealDependency} from './commands';
+import {PersistentState} from "./persistent_state";
+import {bootstrap} from "./bootstrap";
+import {ExecOptions} from "child_process";
// We only support local folders, not eg. Live Share (`vlsl:` scheme), so don't activate if
// only those are in use. We use "Empty" to represent these scenarios
@@ -25,12 +27,12 @@ import { ExecOptions } from "child_process";
export type Workspace =
| { kind: "Empty" }
| {
- kind: "Workspace Folder";
- }
+ kind: "Workspace Folder";
+}
| {
- kind: "Detached Files";
- files: vscode.TextDocument[];
- };
+ kind: "Detached Files";
+ files: vscode.TextDocument[];
+};
export function fetchWorkspace(): Workspace {
const folders = (vscode.workspace.workspaceFolders || []).filter(
@@ -42,12 +44,12 @@ export function fetchWorkspace(): Workspace {
return folders.length === 0
? rustDocuments.length === 0
- ? { kind: "Empty" }
+ ? {kind: "Empty"}
: {
- kind: "Detached Files",
- files: rustDocuments,
- }
- : { kind: "Workspace Folder" };
+ kind: "Detached Files",
+ files: rustDocuments,
+ }
+ : {kind: "Workspace Folder"};
}
export async function discoverWorkspace(
@@ -84,6 +86,8 @@ export class Ctx {
private commandFactories: Record<string, CommandFactory>;
private commandDisposables: Disposable[];
private unlinkedFiles: vscode.Uri[];
+ readonly dependencies: RustDependenciesProvider;
+ readonly treeView: vscode.TreeView<Dependency | DependencyFile | DependencyId>;
get client() {
return this._client;
@@ -92,7 +96,9 @@ export class Ctx {
constructor(
readonly extCtx: vscode.ExtensionContext,
commandFactories: Record<string, CommandFactory>,
- workspace: Workspace
+ workspace: Workspace,
+ dependencies: RustDependenciesProvider,
+ treeView: vscode.TreeView<Dependency | DependencyFile | DependencyId>
) {
extCtx.subscriptions.push(this);
this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
@@ -101,6 +107,8 @@ export class Ctx {
this.commandDisposables = [];
this.commandFactories = commandFactories;
this.unlinkedFiles = [];
+ this.dependencies = dependencies;
+ this.treeView = treeView;
this.state = new PersistentState(extCtx.globalState);
this.config = new Config(extCtx);
@@ -109,6 +117,13 @@ export class Ctx {
this.setServerStatus({
health: "stopped",
});
+ vscode.window.onDidChangeActiveTextEditor(e => {
+ if (e && isRustEditor(e)) {
+ execRevealDependency(e).catch(reason => {
+ void vscode.window.showErrorMessage(`Dependency error: ${reason}`);
+ });
+ }
+ });
}
dispose() {
@@ -174,7 +189,7 @@ export class Ctx {
const newEnv = Object.assign({}, process.env, this.config.serverExtraEnv);
const run: lc.Executable = {
command: this._serverPath,
- options: { env: newEnv },
+ options: {env: newEnv},
};
const serverOptions = {
run,
@@ -348,6 +363,7 @@ export class Ctx {
statusBar.color = undefined;
statusBar.backgroundColor = undefined;
statusBar.command = "rust-analyzer.stopServer";
+ this.dependencies.refresh();
break;
case "warning":
if (status.message) {
@@ -410,4 +426,5 @@ export class Ctx {
export interface Disposable {
dispose(): void;
}
+
export type Cmd = (...args: any[]) => unknown;
diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts
new file mode 100644
index 0000000000..0f2e5e5ea0
--- /dev/null
+++ b/editors/code/src/dependencies_provider.ts
@@ -0,0 +1,151 @@
+import * as vscode from 'vscode';
+import * as fspath from 'path';
+import * as fs from 'fs';
+import * as os from 'os';
+import { activeToolchain, Cargo, Crate, getRustcVersion } from './toolchain';
+
+const debugOutput = vscode.window.createOutputChannel("Debug");
+
+export class RustDependenciesProvider implements vscode.TreeDataProvider<Dependency | DependencyFile>{
+ cargo: Cargo;
+ dependenciesMap: { [id: string]: Dependency | DependencyFile };
+
+ constructor(
+ private readonly workspaceRoot: string,
+ ) {
+ this.cargo = new Cargo(this.workspaceRoot || '.', debugOutput);
+ this.dependenciesMap = {};
+ }
+
+ private _onDidChangeTreeData: vscode.EventEmitter<Dependency | DependencyFile | undefined | null | void> = new vscode.EventEmitter<Dependency | undefined | null | void>();
+
+ readonly onDidChangeTreeData: vscode.Event<Dependency | DependencyFile | undefined | null | void> = this._onDidChangeTreeData.event;
+
+
+ getDependency(filePath: string): Dependency | DependencyFile | undefined {
+ return this.dependenciesMap[filePath.toLowerCase()];
+ }
+
+ contains(filePath: string): boolean {
+ return filePath.toLowerCase() in this.dependenciesMap;
+ }
+
+ refresh(): void {
+ this._onDidChangeTreeData.fire();
+ }
+
+ getParent?(element: Dependency | DependencyFile): vscode.ProviderResult<Dependency | DependencyFile> {
+ if (element instanceof Dependency) return undefined;
+ return element.parent;
+ }
+
+ getTreeItem(element: Dependency | DependencyFile): vscode.TreeItem | Thenable<vscode.TreeItem> {
+ if (element.id! in this.dependenciesMap) return this.dependenciesMap[element.id!];
+ return element;
+ }
+
+ getChildren(element?: Dependency | DependencyFile): vscode.ProviderResult<Dependency[] | DependencyFile[]> {
+ return new Promise((resolve, _reject) => {
+ if (!this.workspaceRoot) {
+ void vscode.window.showInformationMessage('No dependency in empty workspace');
+ return Promise.resolve([]);
+ }
+
+ if (element) {
+ const files = fs.readdirSync(element.dependencyPath).map(fileName => {
+ const filePath = fspath.join(element.dependencyPath, fileName);
+ const collapsibleState = fs.lstatSync(filePath).isDirectory() ?
+ vscode.TreeItemCollapsibleState.Collapsed :
+ vscode.TreeItemCollapsibleState.None;
+ const dep = new DependencyFile(
+ fileName,
+ filePath,
+ element,
+ collapsibleState
+ );
+ this.dependenciesMap[dep.dependencyPath.toLowerCase()] = dep;
+ return dep;
+ });
+ return resolve(
+ files
+ );
+ } else {
+ return resolve(this.getRootDependencies());
+ }
+ });
+ }
+
+ private async getRootDependencies(): Promise<Dependency[]> {
+ const registryDir = fspath.join(os.homedir(), '.cargo', 'registry', 'src');
+ const basePath = fspath.join(registryDir, fs.readdirSync(registryDir)[0]);
+ const deps = await this.getDepsInCartoTree(basePath);
+ const stdlib = await this.getStdLib();
+ return [stdlib].concat(deps);
+ }
+
+ private async getStdLib(): Promise<Dependency> {
+ const toolchain = await activeToolchain();
+ const rustVersion = await getRustcVersion(os.homedir());
+ const stdlibPath = fspath.join(os.homedir(), '.rustup', 'toolchains', toolchain, 'lib', 'rustlib', 'src', 'rust', 'library');
+ return new Dependency(
+ "stdlib",
+ rustVersion,
+ stdlibPath,
+ vscode.TreeItemCollapsibleState.Collapsed
+ );
+ }
+
+ private async getDepsInCartoTree(basePath: string): Promise<Dependency[]> {
+ const crates: Crate[] = await this.cargo.crates();
+ const toDep = (moduleName: string, version: string): Dependency => {
+ const cratePath = fspath.join(basePath, `${moduleName}-${version}`);
+ return new Dependency(
+ moduleName,
+ version,
+ cratePath,
+ vscode.TreeItemCollapsibleState.Collapsed
+ );
+ };
+
+ const deps = crates.map(crate => {
+ const dep = toDep(crate.name, crate.version);
+ this.dependenciesMap[dep.dependencyPath.toLowerCase()] = dep;
+ return dep;
+ });
+ return deps;
+ }
+}
+
+
+export class Dependency extends vscode.TreeItem {
+ constructor(
+ public readonly label: string,
+ private version: string,
+ readonly dependencyPath: string,
+ public readonly collapsibleState: vscode.TreeItemCollapsibleState
+ ) {
+ super(label, collapsibleState);
+ this.tooltip = `${this.label}-${this.version}`;
+ this.description = this.version;
+ this.resourceUri = vscode.Uri.file(dependencyPath);
+ }
+}
+
+export class DependencyFile extends vscode.TreeItem {
+
+ constructor(
+ readonly label: string,
+ readonly dependencyPath: string,
+ readonly parent: Dependency | DependencyFile,
+ public readonly collapsibleState: vscode.TreeItemCollapsibleState
+ ) {
+ super(vscode.Uri.file(dependencyPath), collapsibleState);
+ const isDir = fs.lstatSync(this.dependencyPath).isDirectory();
+ this.id = this.dependencyPath.toLowerCase();
+ if (!isDir) {
+ this.command = { command: 'rust-analyzer.openFile', title: "Open File", arguments: [vscode.Uri.file(this.dependencyPath)], };
+ }
+ }
+}
+
+export type DependencyId = { id: string }; \ No newline at end of file
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 7ae8fa8ca2..62b2e7a277 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -2,10 +2,10 @@ import * as vscode from "vscode";
import * as lc from "vscode-languageclient/node";
import * as commands from "./commands";
-import { CommandFactory, Ctx, fetchWorkspace } from "./ctx";
+import {CommandFactory, Ctx, fetchWorkspace} from "./ctx";
import * as diagnostics from "./diagnostics";
-import { activateTaskProvider } from "./tasks";
-import { setContextValue } from "./util";
+import {activateTaskProvider} from "./tasks";
+import {setContextValue} from "./util";
const RUST_PROJECT_CONTEXT_NAME = "inRustProject";
@@ -24,11 +24,12 @@ export async function activate(
vscode.window
.showWarningMessage(
`You have both the rust-analyzer (rust-lang.rust-analyzer) and Rust (rust-lang.rust) ` +
- "plugins enabled. These are known to conflict and cause various functions of " +
- "both plugins to not work correctly. You should disable one of them.",
+ "plugins enabled. These are known to conflict and cause various functions of " +
+ "both plugins to not work correctly. You should disable one of them.",
"Got it"
)
- .then(() => {}, console.error);
+ .then(() => {
+ }, console.error);
}
const ctx = new Ctx(context, createCommands(), fetchWorkspace());
@@ -118,7 +119,7 @@ function createCommands(): Record<string, CommandFactory> {
return {
onEnter: {
enabled: commands.onEnter,
- disabled: (_) => () => vscode.commands.executeCommand("default:type", { text: "\n" }),
+ disabled: (_) => () => vscode.commands.executeCommand("default:type", {text: "\n"}),
},
restartServer: {
enabled: (ctx) => async () => {
@@ -144,51 +145,54 @@ function createCommands(): Record<string, CommandFactory> {
health: "stopped",
});
},
- disabled: (_) => async () => {},
+ disabled: (_) => async () => {
+ },
},
- analyzerStatus: { enabled: commands.analyzerStatus },
- memoryUsage: { enabled: commands.memoryUsage },
- shuffleCrateGraph: { enabled: commands.shuffleCrateGraph },
- reloadWorkspace: { enabled: commands.reloadWorkspace },
- rebuildProcMacros: { enabled: commands.rebuildProcMacros },
- addProject: { enabled: commands.addProject },
- matchingBrace: { enabled: commands.matchingBrace },
- joinLines: { enabled: commands.joinLines },
- parentModule: { enabled: commands.parentModule },
- syntaxTree: { enabled: commands.syntaxTree },
- viewHir: { enabled: commands.viewHir },
- viewMir: { enabled: commands.viewMir },
+ analyzerStatus: {enabled: commands.analyzerStatus},
+ memoryUsage: {enabled: commands.memoryUsage},
+ shuffleCrateGraph: {enabled: commands.shuffleCrateGraph},
+ reloadWorkspace: {enabled: commands.reloadWorkspace},
+ rebuildProcMacros: {enabled: commands.rebuildProcMacros},
+ addProject: {enabled: commands.addProject},
+ matchingBrace: {enabled: commands.matchingBrace},
+ joinLines: {enabled: commands.joinLines},
+ parentModule: {enabled: commands.parentModule},
+ syntaxTree: {enabled: commands.syntaxTree},
+ viewHir: {enabled: commands.viewHir},
+ viewMir: {enabled: commands.viewMir},
interpretFunction: { enabled: commands.interpretFunction },
- viewFileText: { enabled: commands.viewFileText },
- viewItemTree: { enabled: commands.viewItemTree },
- viewCrateGraph: { enabled: commands.viewCrateGraph },
- viewFullCrateGraph: { enabled: commands.viewFullCrateGraph },
- expandMacro: { enabled: commands.expandMacro },
- run: { enabled: commands.run },
- copyRunCommandLine: { enabled: commands.copyRunCommandLine },
- debug: { enabled: commands.debug },
- newDebugConfig: { enabled: commands.newDebugConfig },
- openDocs: { enabled: commands.openDocs },
- openCargoToml: { enabled: commands.openCargoToml },
- peekTests: { enabled: commands.peekTests },
- moveItemUp: { enabled: commands.moveItemUp },
- moveItemDown: { enabled: commands.moveItemDown },
- cancelFlycheck: { enabled: commands.cancelFlycheck },
- clearFlycheck: { enabled: commands.clearFlycheck },
- runFlycheck: { enabled: commands.runFlycheck },
- ssr: { enabled: commands.ssr },
- serverVersion: { enabled: commands.serverVersion },
+ viewFileText: {enabled: commands.viewFileText},
+ viewItemTree: {enabled: commands.viewItemTree},
+ viewCrateGraph: {enabled: commands.viewCrateGraph},
+ viewFullCrateGraph: {enabled: commands.viewFullCrateGraph},
+ expandMacro: {enabled: commands.expandMacro},
+ run: {enabled: commands.run},
+ copyRunCommandLine: {enabled: commands.copyRunCommandLine},
+ debug: {enabled: commands.debug},
+ newDebugConfig: {enabled: commands.newDebugConfig},
+ openDocs: {enabled: commands.openDocs},
+ openCargoToml: {enabled: commands.openCargoToml},
+ peekTests: {enabled: commands.peekTests},
+ moveItemUp: {enabled: commands.moveItemUp},
+ moveItemDown: {enabled: commands.moveItemDown},
+ cancelFlycheck: {enabled: commands.cancelFlycheck},
+ clearFlycheck: {enabled: commands.clearFlycheck},
+ runFlycheck: {enabled: commands.runFlycheck},
+ ssr: {enabled: commands.ssr},
+ serverVersion: {enabled: commands.serverVersion},
// Internal commands which are invoked by the server.
- applyActionGroup: { enabled: commands.applyActionGroup },
- applySnippetWorkspaceEdit: { enabled: commands.applySnippetWorkspaceEditCommand },
- debugSingle: { enabled: commands.debugSingle },
- gotoLocation: { enabled: commands.gotoLocation },
- linkToCommand: { enabled: commands.linkToCommand },
- resolveCodeAction: { enabled: commands.resolveCodeAction },
- runSingle: { enabled: commands.runSingle },
- showReferences: { enabled: commands.showReferences },
- triggerParameterHints: { enabled: commands.triggerParameterHints },
- openLogs: { enabled: commands.openLogs },
+ applyActionGroup: {enabled: commands.applyActionGroup},
+ applySnippetWorkspaceEdit: {enabled: commands.applySnippetWorkspaceEditCommand},
+ debugSingle: {enabled: commands.debugSingle},
+ gotoLocation: {enabled: commands.gotoLocation},
+ linkToCommand: {enabled: commands.linkToCommand},
+ resolveCodeAction: {enabled: commands.resolveCodeAction},
+ runSingle: {enabled: commands.runSingle},
+ showReferences: {enabled: commands.showReferences},
+ triggerParameterHints: {enabled: commands.triggerParameterHints},
+ openLogs: {enabled: commands.openLogs},
+ openFile: {enabled: commands.openFile},
+ revealDependency: {enabled: commands.revealDependency}
};
}
diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts
index 917a1d6b09..6f37451edd 100644
--- a/editors/code/src/toolchain.ts
+++ b/editors/code/src/toolchain.ts
@@ -5,6 +5,15 @@ import * as readline from "readline";
import * as vscode from "vscode";
import { execute, log, memoizeAsync } from "./util";
+
+const TREE_LINE_PATTERN = new RegExp(/(.+)\sv(\d+\.\d+\.\d+)(?:\s\((.+)\))?/);
+const TOOLCHAIN_PATTERN = new RegExp(/(.*)\s\(.*\)/);
+
+export interface Crate {
+ name: string;
+ version: string;
+}
+
interface CompilationArtifact {
fileName: string;
name: string;
@@ -96,6 +105,43 @@ export class Cargo {
return artifacts[0].fileName;
}
+ async crates(): Promise<Crate[]> {
+ const pathToCargo = await cargoPath();
+ return await new Promise((resolve, reject) => {
+ const crates: Crate[] = [];
+
+ const cargo = cp.spawn(pathToCargo, ['tree', '--prefix', 'none'], {
+ stdio: ['ignore', 'pipe', 'pipe'],
+ cwd: this.rootFolder
+ });
+ const rl = readline.createInterface({ input: cargo.stdout });
+ rl.on('line', line => {
+ const match = line.match(TREE_LINE_PATTERN);
+ if (match) {
+ const name = match[1];
+ const version = match[2];
+ const extraInfo = match[3];
+ // ignore duplicates '(*)' and path dependencies
+ if (this.shouldIgnore(extraInfo)) {
+ return;
+ }
+ crates.push({ name, version });
+ }
+ });
+ cargo.on('exit', (exitCode, _) => {
+ if (exitCode === 0)
+ resolve(crates);
+ else
+ reject(new Error(`exit code: ${exitCode}.`));
+ });
+
+ });
+ }
+
+ private shouldIgnore(extraInfo: string): boolean {
+ return extraInfo !== undefined && (extraInfo === '*' || path.isAbsolute(extraInfo));
+ }
+
private async runCargo(
cargoArgs: string[],
onStdoutJson: (obj: any) => void,
@@ -127,6 +173,58 @@ export class Cargo {
}
}
+export async function activeToolchain(): Promise<string> {
+ const pathToRustup = await rustupPath();
+ return await new Promise((resolve, reject) => {
+ const execution = cp.spawn(pathToRustup, ['show', 'active-toolchain'], {
+ stdio: ['ignore', 'pipe', 'pipe'],
+ cwd: os.homedir()
+ });
+ const rl = readline.createInterface({ input: execution.stdout });
+
+ let currToolchain: string | undefined = undefined;
+ rl.on('line', line => {
+ const match = line.match(TOOLCHAIN_PATTERN);
+ if (match) {
+ currToolchain = match[1];
+ }
+ });
+ execution.on('exit', (exitCode, _) => {
+ if (exitCode === 0 && currToolchain)
+ resolve(currToolchain);
+ else
+ reject(new Error(`exit code: ${exitCode}.`));
+ });
+
+ });
+}
+
+export async function rustVersion(): Promise<string> {
+ const pathToRustup = await rustupPath();
+ return await new Promise((resolve, reject) => {
+ const execution = cp.spawn(pathToRustup, ['show', 'active-toolchain'], {
+ stdio: ['ignore', 'pipe', 'pipe'],
+ cwd: os.homedir()
+ });
+ const rl = readline.createInterface({ input: execution.stdout });
+
+ let currToolchain: string | undefined = undefined;
+ rl.on('line', line => {
+ const match = line.match(TOOLCHAIN_PATTERN);
+ if (match) {
+ currToolchain = match[1];
+ }
+ });
+ execution.on('exit', (exitCode, _) => {
+ if (exitCode === 1 && currToolchain)
+ resolve(currToolchain);
+ else
+ reject(new Error(`exit code: ${exitCode}.`));
+ });
+
+ });
+}
+
/** Mirrors `project_model::sysroot::discover_sysroot_dir()` implementation*/
export async function getSysroot(dir: string): Promise<string> {
const rustcPath = await getPathForExecutable("rustc");
@@ -145,11 +243,26 @@ export async function getRustcId(dir: string): Promise<string> {
return rx.exec(data)![1];
}
+export async function getRustcVersion(dir: string): Promise<string> {
+ const rustcPath = await getPathForExecutable("rustc");
+
+ // do not memoize the result because the toolchain may change between runs
+ const data = await execute(`${rustcPath} -V`, { cwd: dir });
+ const rx = /(\d\.\d+\.\d+)/;
+
+ return rx.exec(data)![1];
+}
+
/** Mirrors `toolchain::cargo()` implementation */
export function cargoPath(): Promise<string> {
return getPathForExecutable("cargo");
}
+/** Mirrors `toolchain::cargo()` implementation */
+export function rustupPath(): Promise<string> {
+ return getPathForExecutable("rustup");
+}
+
/** Mirrors `toolchain::get_path_for_executable()` implementation */
export const getPathForExecutable = memoizeAsync(
// We apply caching to decrease file-system interactions