Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/ide/src/fetch_crates.rs59
-rw-r--r--crates/ide/src/lib.rs6
-rw-r--r--crates/rust-analyzer/src/handlers.rs23
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs13
-rw-r--r--crates/rust-analyzer/src/main_loop.rs2
-rw-r--r--editors/code/src/commands.ts8
-rw-r--r--editors/code/src/ctx.ts48
-rw-r--r--editors/code/src/dependencies_provider.ts77
-rw-r--r--editors/code/src/toolchain.ts75
9 files changed, 155 insertions, 156 deletions
diff --git a/crates/ide/src/fetch_crates.rs b/crates/ide/src/fetch_crates.rs
new file mode 100644
index 0000000000..c0bc4103c2
--- /dev/null
+++ b/crates/ide/src/fetch_crates.rs
@@ -0,0 +1,59 @@
+use ide_db::{
+ base_db::{CrateOrigin, SourceDatabase, SourceDatabaseExt},
+ RootDatabase,
+};
+
+#[derive(Debug)]
+pub struct CrateInfo {
+ pub name: String,
+ pub version: String,
+ pub path: String,
+}
+
+pub(crate) fn fetch_crates(db: &RootDatabase) -> Vec<CrateInfo> {
+ let crate_graph = db.crate_graph();
+ crate_graph
+ .iter()
+ .map(|crate_id| &crate_graph[crate_id])
+ .filter(|&data| !matches!(data.origin, CrateOrigin::Local { .. }))
+ .map(|data| {
+ let crate_name = crate_name(data);
+ let version = data.version.clone().unwrap_or_else(|| "".to_owned());
+ let crate_path = crate_path(db, data, &crate_name);
+
+ CrateInfo { name: crate_name, version, path: crate_path }
+ })
+ .collect()
+}
+
+fn crate_name(data: &ide_db::base_db::CrateData) -> String {
+ data.display_name
+ .clone()
+ .map(|it| it.canonical_name().to_owned())
+ .unwrap_or("unknown".to_string())
+}
+
+fn crate_path(db: &RootDatabase, data: &ide_db::base_db::CrateData, crate_name: &str) -> String {
+ let source_root_id = db.file_source_root(data.root_file_id);
+ let source_root = db.source_root(source_root_id);
+ let source_root_path = source_root.path_for_file(&data.root_file_id);
+ match source_root_path.cloned() {
+ Some(mut root_path) => {
+ let mut crate_path = "".to_string();
+ while let Some(vfs_path) = root_path.parent() {
+ match vfs_path.name_and_extension() {
+ Some((name, _)) => {
+ if name.starts_with(crate_name) {
+ crate_path = vfs_path.to_string();
+ break;
+ }
+ }
+ None => break,
+ }
+ root_path = vfs_path;
+ }
+ crate_path
+ }
+ None => "".to_owned(),
+ }
+}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index e3900fa0d6..96adb11dcd 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -59,10 +59,12 @@ mod view_mir;
mod interpret_function;
mod view_item_tree;
mod shuffle_crate_graph;
+mod fetch_crates;
use std::sync::Arc;
use cfg::CfgOptions;
+use fetch_crates::CrateInfo;
use ide_db::{
base_db::{
salsa::{self, ParallelDatabase},
@@ -331,6 +333,10 @@ impl Analysis {
self.with_db(|db| view_crate_graph::view_crate_graph(db, full))
}
+ pub fn fetch_crates(&self) -> Cancellable<Vec<CrateInfo>> {
+ self.with_db(|db| fetch_crates::fetch_crates(db))
+ }
+
pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> {
self.with_db(|db| expand_macro::expand_macro(db, position))
}
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index a00d0fba7c..2324490e53 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -6,7 +6,13 @@ use ide::AssistResolveStrategy;
use lsp_types::{Diagnostic, DiagnosticTag, NumberOrString};
use vfs::FileId;
-use crate::{global_state::GlobalStateSnapshot, to_proto, Result};
+use crate::{
+ global_state::GlobalStateSnapshot, to_proto, Result,
+ lsp_ext::{
+ CrateInfoResult, FetchDependencyGraphResult, FetchDependencyGraphParams,
+ },
+};
+
pub(crate) mod request;
pub(crate) mod notification;
@@ -31,7 +37,7 @@ pub(crate) fn publish_diagnostics(
"https://rust-analyzer.github.io/manual.html#{}",
d.code.as_str()
))
- .unwrap(),
+ .unwrap(),
}),
source: Some("rust-analyzer".to_string()),
message: d.message,
@@ -42,3 +48,16 @@ pub(crate) fn publish_diagnostics(
.collect();
Ok(diagnostics)
}
+
+pub(crate) fn fetch_dependency_graph(
+ state: GlobalStateSnapshot,
+ _params: FetchDependencyGraphParams,
+) -> Result<FetchDependencyGraphResult> {
+ let crates = state.analysis.fetch_crates()?;
+ Ok(FetchDependencyGraphResult {
+ crates: crates
+ .into_iter()
+ .map(|it| CrateInfoResult { name: it.name, version: it.version, path: it.path })
+ .collect(),
+ })
+}
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 420118ad68..18511da468 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -27,6 +27,13 @@ pub struct AnalyzerStatusParams {
pub text_document: Option<TextDocumentIdentifier>,
}
+#[derive(Deserialize, Serialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct CrateInfoResult {
+ pub name: String,
+ pub version: String,
+ pub path: String,
+}
pub enum FetchDependencyGraph {}
impl Request for FetchDependencyGraph {
@@ -38,9 +45,12 @@ impl Request for FetchDependencyGraph {
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct FetchDependencyGraphParams {}
+
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
-pub struct FetchDependencyGraphResult {}
+pub struct FetchDependencyGraphResult {
+ pub crates: Vec<CrateInfoResult>,
+}
pub enum MemoryUsage {}
@@ -374,6 +384,7 @@ impl Request for CodeActionRequest {
}
pub enum CodeActionResolveRequest {}
+
impl Request for CodeActionResolveRequest {
type Params = CodeAction;
type Result = CodeAction;
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 72fc1f1e25..7a81a18f4a 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -655,12 +655,12 @@ impl GlobalState {
.on_sync_mut::<lsp_ext::ReloadWorkspace>(handlers::handle_workspace_reload)
.on_sync_mut::<lsp_ext::RebuildProcMacros>(handlers::handle_proc_macros_rebuild)
.on_sync_mut::<lsp_ext::MemoryUsage>(handlers::handle_memory_usage)
- .on_sync_mut::<lsp_ext::FetchDependencyGraph>(handlers::fetch_dependency_graph)
.on_sync_mut::<lsp_ext::ShuffleCrateGraph>(handlers::handle_shuffle_crate_graph)
.on_sync::<lsp_ext::JoinLines>(handlers::handle_join_lines)
.on_sync::<lsp_ext::OnEnter>(handlers::handle_on_enter)
.on_sync::<lsp_types::request::SelectionRangeRequest>(handlers::handle_selection_range)
.on_sync::<lsp_ext::MatchingBrace>(handlers::handle_matching_brace)
+ .on::<lsp_ext::FetchDependencyGraph>(handlers::fetch_dependency_graph)
.on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)
.on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
.on::<lsp_ext::ViewHir>(handlers::handle_view_hir)
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 70eeab897c..7fe32754c9 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -272,19 +272,19 @@ export function revealDependency(ctx: CtxInit): Cmd {
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);
+ const dep = ctx.dependencies?.getDependency(documentPath);
if (dep) {
- await ctx.treeView.reveal(dep, { select: true, expand: true });
+ 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));
+ } while (!ctx.dependencies?.contains(documentPath));
parentChain.reverse();
for (const idx in parentChain) {
- await ctx.treeView.reveal(parentChain[idx], { select: true, expand: true });
+ await ctx.treeView?.reveal(parentChain[idx], { select: true, expand: true });
}
}
};
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index feb39198c2..d62716c26d 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -91,19 +91,25 @@ 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>;
+ private _dependencies: RustDependenciesProvider | undefined;
+ private _treeView: vscode.TreeView<Dependency | DependencyFile | DependencyId> | undefined;
get client() {
return this._client;
}
+ get treeView() {
+ return this._treeView;
+ }
+
+ get dependencies() {
+ return this._dependencies;
+ }
+
constructor(
readonly extCtx: vscode.ExtensionContext,
commandFactories: Record<string, CommandFactory>,
workspace: Workspace,
- dependencies: RustDependenciesProvider,
- treeView: vscode.TreeView<Dependency | DependencyFile | DependencyId>
) {
extCtx.subscriptions.push(this);
this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
@@ -112,9 +118,6 @@ 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);
@@ -123,13 +126,6 @@ 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() {
@@ -267,6 +263,28 @@ export class Ctx {
}
await client.start();
this.updateCommands();
+ this.prepareTreeDependenciesView(client);
+ }
+
+ private prepareTreeDependenciesView(client: lc.LanguageClient) {
+ const ctxInit: CtxInit = {
+ ...this,
+ client: client
+ };
+ const rootPath = vscode.workspace.workspaceFolders![0].uri.fsPath;
+ this._dependencies = new RustDependenciesProvider(rootPath, ctxInit);
+ this._treeView = vscode.window.createTreeView("rustDependencies", {
+ treeDataProvider: this._dependencies,
+ showCollapseAll: true,
+ });
+
+ vscode.window.onDidChangeActiveTextEditor((e) => {
+ if (e && isRustEditor(e)) {
+ execRevealDependency(e).catch((reason) => {
+ void vscode.window.showErrorMessage(`Dependency error: ${reason}`);
+ });
+ }
+ });
}
async restart() {
@@ -369,7 +387,7 @@ export class Ctx {
statusBar.color = undefined;
statusBar.backgroundColor = undefined;
statusBar.command = "rust-analyzer.stopServer";
- this.dependencies.refresh();
+ this.dependencies?.refresh();
break;
case "warning":
if (status.message) {
diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts
index 48d51523e8..195f41417d 100644
--- a/editors/code/src/dependencies_provider.ts
+++ b/editors/code/src/dependencies_provider.ts
@@ -1,23 +1,16 @@
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";
-import { Ctx } from "./ctx";
-import { setFlagsFromString } from "v8";
+import { CtxInit } from "./ctx";
import * as ra from "./lsp_ext";
-
-const debugOutput = vscode.window.createOutputChannel("Debug");
+import { FetchDependencyGraphResult } from "./lsp_ext";
export class RustDependenciesProvider
- implements vscode.TreeDataProvider<Dependency | DependencyFile>
-{
- cargo: Cargo;
+ implements vscode.TreeDataProvider<Dependency | DependencyFile> {
dependenciesMap: { [id: string]: Dependency | DependencyFile };
- ctx: Ctx;
+ ctx: CtxInit;
- constructor(private readonly workspaceRoot: string, ctx: Ctx) {
- this.cargo = new Cargo(this.workspaceRoot || ".", debugOutput);
+ constructor(private readonly workspaceRoot: string, ctx: CtxInit) {
this.dependenciesMap = {};
this.ctx = ctx;
}
@@ -62,7 +55,6 @@ export class RustDependenciesProvider
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);
@@ -81,59 +73,26 @@ export class RustDependenciesProvider
}
private async getRootDependencies(): Promise<Dependency[]> {
- const crates = await this.ctx.client.sendRequest(ra.fetchDependencyGraph, {});
-
- 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();
- this.dependenciesMap[stdlib.dependencyPath.toLowerCase()] = stdlib;
- 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"
- );
- const stdlib = new Dependency(
- "stdlib",
- rustVersion,
- stdlibPath,
- vscode.TreeItemCollapsibleState.Collapsed
- );
-
- return stdlib;
- }
-
- 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 dependenciesResult: FetchDependencyGraphResult = await this.ctx.client.sendRequest(ra.fetchDependencyGraph, {});
+ const crates = dependenciesResult.crates;
const deps = crates.map((crate) => {
- const dep = toDep(crate.name, crate.version);
+ const dep = this.toDep(crate.name, crate.version, crate.path);
this.dependenciesMap[dep.dependencyPath.toLowerCase()] = dep;
return dep;
});
return deps;
}
+
+ private toDep(moduleName: string, version: string, path: string): Dependency {
+ // const cratePath = fspath.join(basePath, `${moduleName}-${version}`);
+ return new Dependency(
+ moduleName,
+ version,
+ path,
+ vscode.TreeItemCollapsibleState.Collapsed
+ );
+ }
}
export class Dependency extends vscode.TreeItem {
diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts
index c068cfc311..771f6bcba4 100644
--- a/editors/code/src/toolchain.ts
+++ b/editors/code/src/toolchain.ts
@@ -5,14 +5,8 @@ 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;
@@ -30,7 +24,7 @@ export class Cargo {
readonly rootFolder: string,
readonly output: vscode.OutputChannel,
readonly env: Record<string, string>
- ) {}
+ ) { }
// Made public for testing purposes
static artifactSpec(args: readonly string[]): ArtifactSpec {
@@ -104,40 +98,6 @@ 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,
@@ -169,29 +129,6 @@ 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}.`));
- });
- });
-}
-
/** Mirrors `project_model::sysroot::discover_sysroot_dir()` implementation*/
export async function getSysroot(dir: string): Promise<string> {
const rustcPath = await getPathForExecutable("rustc");
@@ -210,16 +147,6 @@ 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");