improving code to work with multi-workspaces

This commit is contained in:
Bruno Ortiz 2023-04-05 14:57:01 -03:00
parent 1b8288ff96
commit 8e687f7afb
6 changed files with 89 additions and 23 deletions

View File

@ -1,9 +1,9 @@
use ide_db::{ use ide_db::{
base_db::{CrateOrigin, SourceDatabase, SourceDatabaseExt}, base_db::{CrateOrigin, SourceDatabase, SourceDatabaseExt},
RootDatabase, FxIndexSet, RootDatabase,
}; };
#[derive(Debug)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CrateInfo { pub struct CrateInfo {
pub name: String, pub name: String,
pub version: String, pub version: String,
@ -16,7 +16,7 @@ pub struct CrateInfo {
// //
// |=== // |===
// image::https://user-images.githubusercontent.com/5748995/229394139-2625beab-f4c9-484b-84ed-ad5dee0b1e1a.png[] // image::https://user-images.githubusercontent.com/5748995/229394139-2625beab-f4c9-484b-84ed-ad5dee0b1e1a.png[]
pub(crate) fn fetch_crates(db: &RootDatabase) -> Vec<CrateInfo> { pub(crate) fn fetch_crates(db: &RootDatabase) -> FxIndexSet<CrateInfo> {
let crate_graph = db.crate_graph(); let crate_graph = db.crate_graph();
crate_graph crate_graph
.iter() .iter()

View File

@ -70,7 +70,7 @@ use ide_db::{
salsa::{self, ParallelDatabase}, salsa::{self, ParallelDatabase},
CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, VfsPath, CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, VfsPath,
}, },
symbol_index, FxHashMap, LineIndexDatabase, symbol_index, FxHashMap, FxIndexSet, LineIndexDatabase,
}; };
use syntax::SourceFile; use syntax::SourceFile;
@ -333,7 +333,7 @@ impl Analysis {
self.with_db(|db| view_crate_graph::view_crate_graph(db, full)) self.with_db(|db| view_crate_graph::view_crate_graph(db, full))
} }
pub fn fetch_crates(&self) -> Cancellable<Vec<CrateInfo>> { pub fn fetch_crates(&self) -> Cancellable<FxIndexSet<CrateInfo>> {
self.with_db(|db| fetch_crates::fetch_crates(db)) self.with_db(|db| fetch_crates::fetch_crates(db))
} }

View File

@ -8,7 +8,15 @@ import { applySnippetWorkspaceEdit, applySnippetTextEdits } from "./snippets";
import { spawnSync } from "child_process"; import { spawnSync } from "child_process";
import { RunnableQuickPick, selectRunnable, createTask, createArgs } from "./run"; import { RunnableQuickPick, selectRunnable, createTask, createArgs } from "./run";
import { AstInspector } from "./ast_inspector"; import { AstInspector } from "./ast_inspector";
import { isRustDocument, isCargoTomlDocument, sleep, isRustEditor, RustEditor } from "./util"; import {
isRustDocument,
isCargoTomlDocument,
sleep,
isRustEditor,
RustEditor,
RustDocument,
closeDocument,
} from "./util";
import { startDebugSession, makeDebugConfig } from "./debug"; import { startDebugSession, makeDebugConfig } from "./debug";
import { LanguageClient } from "vscode-languageclient/node"; import { LanguageClient } from "vscode-languageclient/node";
import { LINKED_COMMANDS } from "./client"; import { LINKED_COMMANDS } from "./client";
@ -269,27 +277,63 @@ export function openCargoToml(ctx: CtxInit): Cmd {
export function revealDependency(ctx: CtxInit): Cmd { export function revealDependency(ctx: CtxInit): Cmd {
return async (editor: RustEditor) => { return async (editor: RustEditor) => {
const rootPath = vscode.workspace.workspaceFolders![0].uri.fsPath;
const documentPath = editor.document.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) { if (dep) {
await ctx.treeView?.reveal(dep, { select: true, expand: true }); await ctx.treeView?.reveal(dep, { select: true, expand: true });
} else { } else {
let documentPath = editor.document.uri.fsPath; await revealParentChain(editor.document, ctx);
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 });
}
} }
}; };
} }
/**
* This function calculates the parent chain of a given file until it reaches it crate root contained in ctx.dependencies.
* This is need because the TreeView is Lazy, so at first it only has the root dependencies: For example if we have the following crates:
* - core
* - alloc
* - std
*
* if I want to reveal alloc/src/str.rs, I have to:
* 1. reveal every children of alloc
* - core
* - alloc\
* &emsp;|-beches\
* &emsp;|-src\
* &emsp;|- ...
* - std
* 2. reveal every children of src:
* core
* alloc\
* &emsp;|-beches\
* &emsp;|-src\
* &emsp;&emsp;|- lib.rs\
* &emsp;&emsp;|- str.rs <------- FOUND IT!\
* &emsp;&emsp;|- ...\
* &emsp;|- ...\
* std
*/
async function revealParentChain(document: RustDocument, ctx: CtxInit) {
let documentPath = document.uri.fsPath;
const maxDepth = documentPath.split(path.sep).length - 1;
const parentChain: DependencyId[] = [{ id: documentPath.toLowerCase() }];
do {
documentPath = path.dirname(documentPath);
parentChain.push({ id: documentPath.toLowerCase() });
if (parentChain.length >= maxDepth) {
// this is an odd case that can happen when we change a crate version but we'd still have
// a open file referencing the old version
await closeDocument(document);
return;
}
} 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> { export async function execRevealDependency(e: RustEditor): Promise<void> {
await vscode.commands.executeCommand("rust-analyzer.revealDependency", e); await vscode.commands.executeCommand("rust-analyzer.revealDependency", e);
} }

View File

@ -7,6 +7,7 @@ import { Config, prepareVSCodeConfig } from "./config";
import { createClient } from "./client"; import { createClient } from "./client";
import { import {
executeDiscoverProject, executeDiscoverProject,
isDocumentInWorkspace,
isRustDocument, isRustDocument,
isRustEditor, isRustEditor,
LazyOutputChannel, LazyOutputChannel,
@ -277,15 +278,16 @@ export class Ctx {
...this, ...this,
client: client, client: client,
}; };
const rootPath = vscode.workspace.workspaceFolders![0].uri.fsPath; this._dependencies = new RustDependenciesProvider(ctxInit);
this._dependencies = new RustDependenciesProvider(rootPath, ctxInit);
this._treeView = vscode.window.createTreeView("rustDependencies", { this._treeView = vscode.window.createTreeView("rustDependencies", {
treeDataProvider: this._dependencies, treeDataProvider: this._dependencies,
showCollapseAll: true, showCollapseAll: true,
}); });
this.pushExtCleanup(this._treeView);
vscode.window.onDidChangeActiveTextEditor((e) => { vscode.window.onDidChangeActiveTextEditor((e) => {
if (e && isRustEditor(e)) { // we should skip documents that belong to the current workspace
if (e && isRustEditor(e) && !isDocumentInWorkspace(e.document)) {
execRevealDependency(e).catch((reason) => { execRevealDependency(e).catch((reason) => {
void vscode.window.showErrorMessage(`Dependency error: ${reason}`); void vscode.window.showErrorMessage(`Dependency error: ${reason}`);
}); });

View File

@ -15,7 +15,7 @@ export class RustDependenciesProvider
dependenciesMap: { [id: string]: Dependency | DependencyFile }; dependenciesMap: { [id: string]: Dependency | DependencyFile };
ctx: CtxInit; ctx: CtxInit;
constructor(private readonly workspaceRoot: string,ctx: CtxInit) { constructor(ctx: CtxInit) {
this.dependenciesMap = {}; this.dependenciesMap = {};
this.ctx = ctx; this.ctx = ctx;
} }
@ -37,6 +37,7 @@ export class RustDependenciesProvider
} }
refresh(): void { refresh(): void {
this.dependenciesMap = {};
this._onDidChangeTreeData.fire(); this._onDidChangeTreeData.fire();
} }
@ -56,7 +57,7 @@ export class RustDependenciesProvider
element?: Dependency | DependencyFile element?: Dependency | DependencyFile
): vscode.ProviderResult<Dependency[] | DependencyFile[]> { ): vscode.ProviderResult<Dependency[] | DependencyFile[]> {
return new Promise((resolve, _reject) => { return new Promise((resolve, _reject) => {
if (!this.workspaceRoot) { if (!vscode.workspace.workspaceFolders) {
void vscode.window.showInformationMessage("No dependency in empty workspace"); void vscode.window.showInformationMessage("No dependency in empty workspace");
return Promise.resolve([]); return Promise.resolve([]);
} }
@ -108,6 +109,7 @@ export class Dependency extends vscode.TreeItem {
public readonly collapsibleState: vscode.TreeItemCollapsibleState public readonly collapsibleState: vscode.TreeItemCollapsibleState
) { ) {
super(label, collapsibleState); super(label, collapsibleState);
this.id = this.dependencyPath.toLowerCase();
this.tooltip = `${this.label}-${this.version}`; this.tooltip = `${this.label}-${this.version}`;
this.description = this.version; this.description = this.version;
this.resourceUri = vscode.Uri.file(dependencyPath); this.resourceUri = vscode.Uri.file(dependencyPath);

View File

@ -112,6 +112,24 @@ export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor {
return isRustDocument(editor.document); return isRustDocument(editor.document);
} }
export function isDocumentInWorkspace(document: RustDocument): boolean {
const workspaceFolders = vscode.workspace.workspaceFolders;
if (!workspaceFolders) {
return false;
}
for (const folder of workspaceFolders) {
if (document.uri.fsPath.startsWith(folder.uri.fsPath)) {
return true;
}
}
return false;
}
export async function closeDocument(document: RustDocument) {
await vscode.window.showTextDocument(document, { preview: true, preserveFocus: false });
await vscode.commands.executeCommand("workbench.action.closeActiveEditor");
}
export function isValidExecutable(path: string): boolean { export function isValidExecutable(path: string): boolean {
log.debug("Checking availability of a binary at", path); log.debug("Checking availability of a binary at", path);