From 795a1cbe89bc2428937aec828963fe64a2c8d4d9 Mon Sep 17 00:00:00 2001 From: bruno-ortiz Date: Fri, 25 Feb 2022 21:37:55 -0300 Subject: [PATCH 01/31] Creating rust dependencies tree view --- editors/code/package.json | 18 ++- editors/code/src/commands.ts | 41 +++++- editors/code/src/ctx.ts | 55 +++++--- editors/code/src/dependencies_provider.ts | 151 ++++++++++++++++++++++ editors/code/src/main.ts | 102 ++++++++------- editors/code/src/toolchain.ts | 113 ++++++++++++++++ 6 files changed, 410 insertions(+), 70 deletions(-) create mode 100644 editors/code/src/dependencies_provider.ts diff --git a/editors/code/package.json b/editors/code/package.json index f36e34b6a1be..5e2a1c69e944 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 2d5272d199d8..e5aa06025b21 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 { + 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 567b9216bc1f..e6829ac4b9ee 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; private commandDisposables: Disposable[]; private unlinkedFiles: vscode.Uri[]; + readonly dependencies: RustDependenciesProvider; + readonly treeView: vscode.TreeView; get client() { return this._client; @@ -92,7 +96,9 @@ export class Ctx { constructor( readonly extCtx: vscode.ExtensionContext, commandFactories: Record, - workspace: Workspace + workspace: Workspace, + dependencies: RustDependenciesProvider, + treeView: vscode.TreeView ) { 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 000000000000..0f2e5e5ea077 --- /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{ + 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 = new vscode.EventEmitter(); + + readonly onDidChangeTreeData: vscode.Event = 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 { + if (element instanceof Dependency) return undefined; + return element.parent; + } + + getTreeItem(element: Dependency | DependencyFile): vscode.TreeItem | Thenable { + if (element.id! in this.dependenciesMap) return this.dependenciesMap[element.id!]; + return element; + } + + getChildren(element?: Dependency | DependencyFile): vscode.ProviderResult { + 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 { + 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 { + 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 { + 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 7ae8fa8ca285..62b2e7a2771e 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 { 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 { 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 917a1d6b0997..6f37451edd20 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 { + 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 { + 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 { + 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 { const rustcPath = await getPathForExecutable("rustc"); @@ -145,11 +243,26 @@ export async function getRustcId(dir: string): Promise { return rx.exec(data)![1]; } +export async function getRustcVersion(dir: string): Promise { + 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 { return getPathForExecutable("cargo"); } +/** Mirrors `toolchain::cargo()` implementation */ +export function rustupPath(): Promise { + return getPathForExecutable("rustup"); +} + /** Mirrors `toolchain::get_path_for_executable()` implementation */ export const getPathForExecutable = memoizeAsync( // We apply caching to decrease file-system interactions From 68aa20bb8add41a2996c857235f554e11cde71aa Mon Sep 17 00:00:00 2001 From: bruno-ortiz Date: Fri, 25 Feb 2022 21:53:47 -0300 Subject: [PATCH 02/31] fixing stblib loading --- editors/code/src/dependencies_provider.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index 0f2e5e5ea077..1b9ab897350a 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -80,6 +80,7 @@ export class RustDependenciesProvider implements vscode.TreeDataProvider { From 77a4bfd34f2d251482aca8e8da10634f34552e3b Mon Sep 17 00:00:00 2001 From: bruno-ortiz Date: Fri, 25 Feb 2022 22:37:09 -0300 Subject: [PATCH 03/31] fixing linting problemas --- editors/code/src/dependencies_provider.ts | 262 +++++++++++----------- 1 file changed, 131 insertions(+), 131 deletions(-) diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index 1b9ab897350a..2df5ef61111c 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -7,148 +7,148 @@ import { activeToolchain, Cargo, Crate, getRustcVersion } from './toolchain'; const debugOutput = vscode.window.createOutputChannel("Debug"); export class RustDependenciesProvider implements vscode.TreeDataProvider{ - 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 = new vscode.EventEmitter(); - - readonly onDidChangeTreeData: vscode.Event = 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 { - if (element instanceof Dependency) return undefined; - return element.parent; - } - - getTreeItem(element: Dependency | DependencyFile): vscode.TreeItem | Thenable { - if (element.id! in this.dependenciesMap) return this.dependenciesMap[element.id!]; - return element; - } - - getChildren(element?: Dependency | DependencyFile): vscode.ProviderResult { - 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; + 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 = new vscode.EventEmitter(); + + readonly onDidChangeTreeData: vscode.Event = 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 { + if (element instanceof Dependency) return undefined; + return element.parent; + } + + getTreeItem(element: Dependency | DependencyFile): vscode.TreeItem | Thenable { + if (element.id! in this.dependenciesMap) return this.dependenciesMap[element.id!]; + return element; + } + + getChildren(element?: Dependency | DependencyFile): vscode.ProviderResult { + 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()); + } }); - return resolve( - files + } + + private async getRootDependencies(): Promise { + 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 { + 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 ); - } else { - return resolve(this.getRootDependencies()); - } - }); - } - - private async getRootDependencies(): Promise { - 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 { - 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 { - 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; - } + + return stdlib; + } + + private async getDepsInCartoTree(basePath: string): Promise { + 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); - } + 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)], }; + 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 From 364308d1dc653199dd786486f4eed0d456a95330 Mon Sep 17 00:00:00 2001 From: bruno-ortiz Date: Sat, 26 Feb 2022 11:17:10 -0300 Subject: [PATCH 04/31] removing unused function --- editors/code/src/toolchain.ts | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts index 6f37451edd20..75e0ed1d125b 100644 --- a/editors/code/src/toolchain.ts +++ b/editors/code/src/toolchain.ts @@ -199,32 +199,6 @@ export async function activeToolchain(): Promise { }); } -export async function rustVersion(): Promise { - 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 { const rustcPath = await getPathForExecutable("rustc"); From 76432d39cb01423ca3c3add391218269d95c46a4 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 17 Jul 2022 17:38:56 +0200 Subject: [PATCH 05/31] Reformat VSCode client code --- editors/code/src/commands.ts | 9 ++- editors/code/src/ctx.ts | 24 ++++--- editors/code/src/dependencies_provider.ts | 84 +++++++++++++---------- editors/code/src/toolchain.ts | 37 ++++------ 4 files changed, 82 insertions(+), 72 deletions(-) diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index e5aa06025b21..c8b54970a5fa 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -8,11 +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, RustEditor } 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'; +import { DependencyId } from "./dependencies_provider"; export * from "./ast_inspector"; export * from "./run"; @@ -291,8 +291,7 @@ export function revealDependency(ctx: CtxInit): Cmd { 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 }); @@ -302,7 +301,7 @@ export function revealDependency(ctx: CtxInit): Cmd { } export async function execRevealDependency(e: RustEditor): Promise { - await vscode.commands.executeCommand('rust-analyzer.revealDependency', e); + await vscode.commands.executeCommand("rust-analyzer.revealDependency", e); } export function ssr(ctx: CtxInit): Cmd { diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index e6829ac4b9ee..feb39198c228 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,10 +12,15 @@ import { LazyOutputChannel, log, RustEditor, -} from './util'; -import {ServerStatusParams} from './lsp_ext'; -import {Dependency, DependencyFile, RustDependenciesProvider, DependencyId} from './dependencies_provider'; -import {execRevealDependency} from './commands'; +} 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"; @@ -113,13 +118,14 @@ export class Ctx { this.state = new PersistentState(extCtx.globalState); this.config = new Config(extCtx); - this.updateCommands("disable"); + this.updateCommands("disable" + ); this.setServerStatus({ health: "stopped", }); - vscode.window.onDidChangeActiveTextEditor(e => { + vscode.window.onDidChangeActiveTextEditor((e) => { if (e && isRustEditor(e)) { - execRevealDependency(e).catch(reason => { + execRevealDependency(e).catch((reason) => { void vscode.window.showErrorMessage(`Dependency error: ${reason}`); }); } diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index 2df5ef61111c..b8b33a9be6ad 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -1,26 +1,29 @@ -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 * 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{ +export class RustDependenciesProvider + implements vscode.TreeDataProvider +{ cargo: Cargo; dependenciesMap: { [id: string]: Dependency | DependencyFile }; - constructor( - private readonly workspaceRoot: string, - ) { - this.cargo = new Cargo(this.workspaceRoot || '.', debugOutput); + constructor(private readonly workspaceRoot: string) { + this.cargo = new Cargo(this.workspaceRoot || ".", debugOutput); this.dependenciesMap = {}; } - private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); - - readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + private _onDidChangeTreeData: vscode.EventEmitter< + Dependency | DependencyFile | undefined | null | void + > = new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event< + Dependency | DependencyFile | undefined | null | void + > = this._onDidChangeTreeData.event; getDependency(filePath: string): Dependency | DependencyFile | undefined { return this.dependenciesMap[filePath.toLowerCase()]; @@ -34,7 +37,9 @@ export class RustDependenciesProvider implements vscode.TreeDataProvider { + getParent?( + element: Dependency | DependencyFile + ): vscode.ProviderResult { if (element instanceof Dependency) return undefined; return element.parent; } @@ -44,31 +49,26 @@ export class RustDependenciesProvider implements vscode.TreeDataProvider { + getChildren( + element?: Dependency | DependencyFile + ): vscode.ProviderResult { return new Promise((resolve, _reject) => { if (!this.workspaceRoot) { - void vscode.window.showInformationMessage('No dependency in empty workspace'); + void vscode.window.showInformationMessage("No dependency in empty workspace"); return Promise.resolve([]); } if (element) { - const files = fs.readdirSync(element.dependencyPath).map(fileName => { + 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 - ); + 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 - ); + return resolve(files); } else { return resolve(this.getRootDependencies()); } @@ -76,7 +76,7 @@ export class RustDependenciesProvider implements vscode.TreeDataProvider { - const registryDir = fspath.join(os.homedir(), '.cargo', 'registry', 'src'); + 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(); @@ -87,7 +87,17 @@ export class RustDependenciesProvider implements vscode.TreeDataProvider { 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 stdlibPath = fspath.join( + os.homedir(), + ".rustup", + "toolchains", + toolchain, + "lib", + "rustlib", + "src", + "rust", + "library" + ); const stdlib = new Dependency( "stdlib", rustVersion, @@ -110,7 +120,7 @@ export class RustDependenciesProvider implements vscode.TreeDataProvider { + const deps = crates.map((crate) => { const dep = toDep(crate.name, crate.version); this.dependenciesMap[dep.dependencyPath.toLowerCase()] = dep; return dep; @@ -119,7 +129,6 @@ export class RustDependenciesProvider implements vscode.TreeDataProvider { const crates: Crate[] = []; - const cargo = cp.spawn(pathToCargo, ['tree', '--prefix', 'none'], { - stdio: ['ignore', 'pipe', 'pipe'], - cwd: this.rootFolder + 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 => { + rl.on("line", (line) => { const match = line.match(TREE_LINE_PATTERN); if (match) { const name = match[1]; @@ -128,18 +127,15 @@ export class Cargo { crates.push({ name, version }); } }); - cargo.on('exit', (exitCode, _) => { - if (exitCode === 0) - resolve(crates); - else - reject(new Error(`exit code: ${exitCode}.`)); + 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)); + return extraInfo !== undefined && (extraInfo === "*" || path.isAbsolute(extraInfo)); } private async runCargo( @@ -176,26 +172,23 @@ export class Cargo { export async function activeToolchain(): Promise { 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 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 => { + 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}.`)); + execution.on("exit", (exitCode, _) => { + if (exitCode === 0 && currToolchain) resolve(currToolchain); + else reject(new Error(`exit code: ${exitCode}.`)); }); - }); } From 16cba19ff3324c4703bf861b96af97bc622e1d6a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 17 Jul 2022 17:45:43 +0200 Subject: [PATCH 06/31] Remove unnecessary openFile command --- editors/code/package.json | 6 +----- editors/code/src/commands.ts | 10 ---------- editors/code/src/dependencies_provider.ts | 2 +- editors/code/src/main.ts | 1 - 4 files changed, 2 insertions(+), 17 deletions(-) diff --git a/editors/code/package.json b/editors/code/package.json index 5e2a1c69e944..0efc191d7480 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -285,10 +285,6 @@ "title": "Clear flycheck diagnostics", "category": "rust-analyzer" }, - { - "command": "rust-analyzer.openFile", - "title": "Open File" - }, { "command": "rust-analyzer.revealDependency", "title": "Reveal File" @@ -1979,4 +1975,4 @@ } ] } -} \ No newline at end of file +} diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index c8b54970a5fa..70eeab897c93 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -267,16 +267,6 @@ 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; diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index b8b33a9be6ad..777cb0d33a7c 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -155,7 +155,7 @@ export class DependencyFile extends vscode.TreeItem { this.id = this.dependencyPath.toLowerCase(); if (!isDir) { this.command = { - command: "rust-analyzer.openFile", + command: "vscode.open", title: "Open File", arguments: [vscode.Uri.file(this.dependencyPath)], }; diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 62b2e7a2771e..774600f6c397 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -192,7 +192,6 @@ function createCommands(): Record { showReferences: {enabled: commands.showReferences}, triggerParameterHints: {enabled: commands.triggerParameterHints}, openLogs: {enabled: commands.openLogs}, - openFile: {enabled: commands.openFile}, revealDependency: {enabled: commands.revealDependency} }; } From 1201b156d839ac5b5cc7bca1ea1c1f0cf6fbf6a9 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 17 Jul 2022 18:05:55 +0200 Subject: [PATCH 07/31] WIP: Add lsp-ext scaffold --- crates/rust-analyzer/src/lsp_ext.rs | 15 +++++++++++++++ crates/rust-analyzer/src/main_loop.rs | 1 + editors/code/src/dependencies_provider.ts | 9 ++++++++- editors/code/src/lsp_ext.ts | 16 ++++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 625ffe0763c2..420118ad68b1 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -27,6 +27,21 @@ pub struct AnalyzerStatusParams { pub text_document: Option, } +pub enum FetchDependencyGraph {} + +impl Request for FetchDependencyGraph { + type Params = FetchDependencyGraphParams; + type Result = FetchDependencyGraphResult; + const METHOD: &'static str = "rust-analyzer/fetchDependencyGraph"; +} + +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct FetchDependencyGraphParams {} +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct FetchDependencyGraphResult {} + pub enum MemoryUsage {} impl Request for MemoryUsage { diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index dc0ea0b17e09..72fc1f1e250c 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -655,6 +655,7 @@ impl GlobalState { .on_sync_mut::(handlers::handle_workspace_reload) .on_sync_mut::(handlers::handle_proc_macros_rebuild) .on_sync_mut::(handlers::handle_memory_usage) + .on_sync_mut::(handlers::fetch_dependency_graph) .on_sync_mut::(handlers::handle_shuffle_crate_graph) .on_sync::(handlers::handle_join_lines) .on_sync::(handlers::handle_on_enter) diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index 777cb0d33a7c..48d51523e861 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -3,6 +3,9 @@ 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 * as ra from "./lsp_ext"; const debugOutput = vscode.window.createOutputChannel("Debug"); @@ -11,10 +14,12 @@ export class RustDependenciesProvider { cargo: Cargo; dependenciesMap: { [id: string]: Dependency | DependencyFile }; + ctx: Ctx; - constructor(private readonly workspaceRoot: string) { + constructor(private readonly workspaceRoot: string, ctx: Ctx) { this.cargo = new Cargo(this.workspaceRoot || ".", debugOutput); this.dependenciesMap = {}; + this.ctx = ctx; } private _onDidChangeTreeData: vscode.EventEmitter< @@ -76,6 +81,8 @@ export class RustDependenciesProvider } private async getRootDependencies(): Promise { + 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); diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 82955acf25ed..1a887b372017 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -70,6 +70,22 @@ export const viewItemTree = new lc.RequestType export type AnalyzerStatusParams = { textDocument?: lc.TextDocumentIdentifier }; +export interface FetchDependencyGraphParams {} + +export interface FetchDependencyGraphResult { + crates: { + name: string; + version: string; + path: string; + }[]; +} + +export const fetchDependencyGraph = new lc.RequestType< + FetchDependencyGraphParams, + FetchDependencyGraphResult, + void +>("rust-analyzer/fetchDependencyGraph"); + export type ExpandMacroParams = { textDocument: lc.TextDocumentIdentifier; position: lc.Position; From 09e0a00d3648eb4080d16f07c6dae73f7c73c431 Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Sun, 2 Apr 2023 21:58:20 -0300 Subject: [PATCH 08/31] fetching dependencies from the server --- crates/ide/src/fetch_crates.rs | 59 +++++++++++++++++ crates/ide/src/lib.rs | 6 ++ crates/rust-analyzer/src/handlers.rs | 23 ++++++- crates/rust-analyzer/src/lsp_ext.rs | 13 +++- crates/rust-analyzer/src/main_loop.rs | 2 +- editors/code/src/commands.ts | 8 +-- editors/code/src/ctx.ts | 48 +++++++++----- editors/code/src/dependencies_provider.ts | 77 ++++++----------------- editors/code/src/toolchain.ts | 75 +--------------------- 9 files changed, 155 insertions(+), 156 deletions(-) create mode 100644 crates/ide/src/fetch_crates.rs diff --git a/crates/ide/src/fetch_crates.rs b/crates/ide/src/fetch_crates.rs new file mode 100644 index 000000000000..c0bc4103c2f7 --- /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 { + 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 e3900fa0d63b..96adb11dcde3 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> { + self.with_db(|db| fetch_crates::fetch_crates(db)) + } + pub fn expand_macro(&self, position: FilePosition) -> Cancellable> { 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 a00d0fba7c4e..2324490e53b6 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 { + 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 420118ad68b1..18511da46839 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, } +#[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, +} 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 72fc1f1e250c..7a81a18f4af9 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::(handlers::handle_workspace_reload) .on_sync_mut::(handlers::handle_proc_macros_rebuild) .on_sync_mut::(handlers::handle_memory_usage) - .on_sync_mut::(handlers::fetch_dependency_graph) .on_sync_mut::(handlers::handle_shuffle_crate_graph) .on_sync::(handlers::handle_join_lines) .on_sync::(handlers::handle_on_enter) .on_sync::(handlers::handle_selection_range) .on_sync::(handlers::handle_matching_brace) + .on::(handlers::fetch_dependency_graph) .on::(handlers::handle_analyzer_status) .on::(handlers::handle_syntax_tree) .on::(handlers::handle_view_hir) diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 70eeab897c93..7fe32754c903 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 feb39198c228..d62716c26db3 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -91,19 +91,25 @@ export class Ctx { private commandFactories: Record; private commandDisposables: Disposable[]; private unlinkedFiles: vscode.Uri[]; - readonly dependencies: RustDependenciesProvider; - readonly treeView: vscode.TreeView; + private _dependencies: RustDependenciesProvider | undefined; + private _treeView: vscode.TreeView | undefined; get client() { return this._client; } + get treeView() { + return this._treeView; + } + + get dependencies() { + return this._dependencies; + } + constructor( readonly extCtx: vscode.ExtensionContext, commandFactories: Record, workspace: Workspace, - dependencies: RustDependenciesProvider, - treeView: vscode.TreeView ) { 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 48d51523e861..195f41417dd7 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 -{ - cargo: Cargo; + implements vscode.TreeDataProvider { 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 { - 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 { - 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 { - 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 c068cfc3118b..771f6bcba458 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 - ) {} + ) { } // 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 { - 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 { - 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 { const rustcPath = await getPathForExecutable("rustc"); @@ -210,16 +147,6 @@ export async function getRustcId(dir: string): Promise { return rx.exec(data)![1]; } -export async function getRustcVersion(dir: string): Promise { - 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 { return getPathForExecutable("cargo"); From d01fc6405b45ed0fafd68f0f874068ed404ab976 Mon Sep 17 00:00:00 2001 From: bruno-ortiz Date: Fri, 25 Feb 2022 21:37:55 -0300 Subject: [PATCH 09/31] Creating rust dependencies tree view --- editors/code/package.json | 10 +++- editors/code/src/commands.ts | 2 +- editors/code/src/ctx.ts | 10 ++-- editors/code/src/toolchain.ts | 99 +++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 6 deletions(-) diff --git a/editors/code/package.json b/editors/code/package.json index 0efc191d7480..b009e381ec9f 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -285,6 +285,14 @@ "title": "Clear flycheck diagnostics", "category": "rust-analyzer" }, + { + "command": "rust-analyzer.revealDependency", + "title": "Reveal File" + }, + { + "command": "rust-analyzer.openFile", + "title": "Open File" + }, { "command": "rust-analyzer.revealDependency", "title": "Reveal File" @@ -1975,4 +1983,4 @@ } ] } -} +} \ No newline at end of file diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 7fe32754c903..fb6778b687f4 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -8,7 +8,7 @@ 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, RustEditor } 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"; diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index d62716c26db3..ce70dfd2f669 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,8 +12,10 @@ import { LazyOutputChannel, log, RustEditor, -} from "./util"; -import {ServerStatusParams} from "./lsp_ext"; +} from './util'; +import {ServerStatusParams} from './lsp_ext'; +import { Dependency, DependencyFile, RustDependenciesProvider, DependencyId } from './dependencies_provider'; +import { execRevealDependency } from './commands'; import { Dependency, DependencyFile, diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts index 771f6bcba458..6b40cb678c39 100644 --- a/editors/code/src/toolchain.ts +++ b/editors/code/src/toolchain.ts @@ -98,6 +98,43 @@ export class Cargo { return artifacts[0].fileName; } + async crates(): Promise { + 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, @@ -129,6 +166,58 @@ export class Cargo { } } +export async function activeToolchain(): Promise { + 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 { + 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 { const rustcPath = await getPathForExecutable("rustc"); @@ -147,6 +236,16 @@ export async function getRustcId(dir: string): Promise { return rx.exec(data)![1]; } +export async function getRustcVersion(dir: string): Promise { + 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 { return getPathForExecutable("cargo"); From f8215dd42695e8f0b9e02f65986c6dc6933aab0a Mon Sep 17 00:00:00 2001 From: bruno-ortiz Date: Fri, 25 Feb 2022 21:53:47 -0300 Subject: [PATCH 10/31] fixing stblib loading --- editors/code/src/dependencies_provider.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index 195f41417dd7..cee9d17584a1 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -79,7 +79,8 @@ export class RustDependenciesProvider const deps = crates.map((crate) => { const dep = this.toDep(crate.name, crate.version, crate.path); this.dependenciesMap[dep.dependencyPath.toLowerCase()] = dep; - return dep; + this.dependenciesMap[stdlib.dependencyPath.toLowerCase()] = stdlib; + return dep; }); return deps; } From ee54c6558dff4307922eae4793850596bb35bcfc Mon Sep 17 00:00:00 2001 From: bruno-ortiz Date: Fri, 25 Feb 2022 22:37:09 -0300 Subject: [PATCH 11/31] fixing linting problemas --- editors/code/src/dependencies_provider.ts | 66 ++++++++++++----------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index cee9d17584a1..028a2573cc2e 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -5,13 +5,15 @@ import { CtxInit } from "./ctx"; import * as ra from "./lsp_ext"; import { FetchDependencyGraphResult } from "./lsp_ext"; -export class RustDependenciesProvider - implements vscode.TreeDataProvider { - dependenciesMap: { [id: string]: Dependency | DependencyFile }; - ctx: CtxInit; - constructor(private readonly workspaceRoot: string, ctx: CtxInit) { - this.dependenciesMap = {}; + +export class RustDependenciesProvider implements vscode.TreeDataProvider{ + + dependenciesMap: { [id: string]: Dependency | DependencyFile };ctx: CtxInit; + + constructor( + private readonly workspaceRoot: string,ctx: CtxInit) { + this.dependenciesMap = {}; this.ctx = ctx; } @@ -47,25 +49,31 @@ export class RustDependenciesProvider return element; } - getChildren( - element?: Dependency | DependencyFile - ): vscode.ProviderResult { + getChildren(element?: Dependency | DependencyFile): vscode.ProviderResult { 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); + :vscode.TreeItemCollapsibleState.None; + const dep = new DependencyFile( + fileName, + filePath, + element, + collapsibleState); + this.dependenciesMap[dep.dependencyPath.toLowerCase()] = dep; return dep; }); - return resolve(files); + return resolve( + files + ); } else { return resolve(this.getRootDependencies()); } @@ -75,25 +83,24 @@ export class RustDependenciesProvider private async getRootDependencies(): Promise { const dependenciesResult: FetchDependencyGraphResult = await this.ctx.client.sendRequest(ra.fetchDependencyGraph, {}); const crates = dependenciesResult.crates; - const deps = crates.map((crate) => { - const dep = this.toDep(crate.name, crate.version, crate.path); + const dep = this.toDep(crate.name, crate.version, crate.path); this.dependenciesMap[dep.dependencyPath.toLowerCase()] = dep; - this.dependenciesMap[stdlib.dependencyPath.toLowerCase()] = stdlib; - return dep; + this.dependenciesMap[stdlib.dependencyPath.toLowerCase()] = stdlib; + 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 - ); - } + 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 { @@ -111,6 +118,7 @@ export class Dependency extends vscode.TreeItem { } export class DependencyFile extends vscode.TreeItem { + constructor( readonly label: string, readonly dependencyPath: string, @@ -121,13 +129,11 @@ export class DependencyFile extends vscode.TreeItem { const isDir = fs.lstatSync(this.dependencyPath).isDirectory(); this.id = this.dependencyPath.toLowerCase(); if (!isDir) { - this.command = { - command: "vscode.open", + this.command = { command: "vscode.open", title: "Open File", arguments: [vscode.Uri.file(this.dependencyPath)], - }; - } - } + }; + }} } export type DependencyId = { id: string }; From d1721b11e923df58489578b48fbc15105e960aa4 Mon Sep 17 00:00:00 2001 From: bruno-ortiz Date: Sat, 26 Feb 2022 11:17:10 -0300 Subject: [PATCH 12/31] removing unused function --- editors/code/src/toolchain.ts | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts index 6b40cb678c39..5fc5d0ddbdf0 100644 --- a/editors/code/src/toolchain.ts +++ b/editors/code/src/toolchain.ts @@ -192,32 +192,6 @@ export async function activeToolchain(): Promise { }); } -export async function rustVersion(): Promise { - 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 { const rustcPath = await getPathForExecutable("rustc"); From af999f152bcec9a0fb9c792bbb14eba093ddeec8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 17 Jul 2022 17:38:56 +0200 Subject: [PATCH 13/31] Reformat VSCode client code --- editors/code/src/commands.ts | 2 +- editors/code/src/ctx.ts | 17 +++++++---- editors/code/src/dependencies_provider.ts | 22 ++++++-------- editors/code/src/toolchain.ts | 36 ++++++++++------------- 4 files changed, 36 insertions(+), 41 deletions(-) diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index fb6778b687f4..7fe32754c903 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -8,7 +8,7 @@ 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, RustEditor } 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"; diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index ce70dfd2f669..ea42b249ff94 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,10 +12,15 @@ import { LazyOutputChannel, log, RustEditor, -} from './util'; -import {ServerStatusParams} from './lsp_ext'; -import { Dependency, DependencyFile, RustDependenciesProvider, DependencyId } from './dependencies_provider'; -import { execRevealDependency } from './commands'; +} from "./util"; +import {ServerStatusParams} from "./lsp_ext"; +import { + Dependency, + DependencyFile, + RustDependenciesProvider, + DependencyId, +} from "./dependencies_provider"; +import { execRevealDependency } from "./commands"; import { Dependency, DependencyFile, diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index 028a2573cc2e..18a96be12486 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -7,7 +7,9 @@ import { FetchDependencyGraphResult } from "./lsp_ext"; -export class RustDependenciesProvider implements vscode.TreeDataProvider{ +export class RustDependenciesProvider + implements vscode.TreeDataProvider +{ dependenciesMap: { [id: string]: Dependency | DependencyFile };ctx: CtxInit; @@ -49,7 +51,9 @@ export class RustDependenciesProvider implements vscode.TreeDataProvider { + getChildren( + element?: Dependency | DependencyFile + ): vscode.ProviderResult { return new Promise((resolve, _reject) => { if (!this.workspaceRoot) { void vscode.window.showInformationMessage("No dependency in empty workspace"); @@ -61,19 +65,12 @@ export class RustDependenciesProvider implements vscode.TreeDataProvider { const crates: Crate[] = []; - const cargo = cp.spawn(pathToCargo, ['tree', '--prefix', 'none'], { - stdio: ['ignore', 'pipe', 'pipe'], - cwd: this.rootFolder + 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 => { + rl.on("line", (line) => { const match = line.match(TREE_LINE_PATTERN); if (match) { const name = match[1]; @@ -121,18 +121,15 @@ export class Cargo { crates.push({ name, version }); } }); - cargo.on('exit', (exitCode, _) => { - if (exitCode === 0) - resolve(crates); - else - reject(new Error(`exit code: ${exitCode}.`)); + 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)); + return extraInfo !== undefined && (extraInfo === "*" || path.isAbsolute(extraInfo)); } private async runCargo( @@ -169,26 +166,23 @@ export class Cargo { export async function activeToolchain(): Promise { 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 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 => { + 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}.`)); + execution.on("exit", (exitCode, _) => { + if (exitCode === 0 && currToolchain) resolve(currToolchain); + else reject(new Error(`exit code: ${exitCode}.`)); }); - }); } From 9533644ccfe9cbb2ded5bdd45c61dd30bcbab720 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 17 Jul 2022 17:45:43 +0200 Subject: [PATCH 14/31] Remove unnecessary openFile command --- editors/code/package.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/editors/code/package.json b/editors/code/package.json index b009e381ec9f..ca00da9f361e 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -289,10 +289,6 @@ "command": "rust-analyzer.revealDependency", "title": "Reveal File" }, - { - "command": "rust-analyzer.openFile", - "title": "Open File" - }, { "command": "rust-analyzer.revealDependency", "title": "Reveal File" @@ -1983,4 +1979,4 @@ } ] } -} \ No newline at end of file +} From 299382dacd53a4a19728d9e125cd490b1077c985 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 17 Jul 2022 18:05:55 +0200 Subject: [PATCH 15/31] WIP: Add lsp-ext scaffold --- crates/rust-analyzer/src/main_loop.rs | 1 + editors/code/src/dependencies_provider.ts | 10 +++++++--- editors/code/src/lsp_ext.ts | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 7a81a18f4af9..5d3aca29b539 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -655,6 +655,7 @@ impl GlobalState { .on_sync_mut::(handlers::handle_workspace_reload) .on_sync_mut::(handlers::handle_proc_macros_rebuild) .on_sync_mut::(handlers::handle_memory_usage) + .on_sync_mut::(handlers::fetch_dependency_graph) .on_sync_mut::(handlers::handle_shuffle_crate_graph) .on_sync::(handlers::handle_join_lines) .on_sync::(handlers::handle_on_enter) diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index 18a96be12486..f2838af6e060 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -4,6 +4,9 @@ import * as fs from "fs"; import { CtxInit } from "./ctx"; import * as ra from "./lsp_ext"; import { FetchDependencyGraphResult } from "./lsp_ext"; +import { Ctx } from "./ctx"; +import { setFlagsFromString } from "v8"; +import * as ra from "./lsp_ext"; @@ -13,9 +16,8 @@ export class RustDependenciesProvider dependenciesMap: { [id: string]: Dependency | DependencyFile };ctx: CtxInit; - constructor( - private readonly workspaceRoot: string,ctx: CtxInit) { - this.dependenciesMap = {}; + constructor(private readonly workspaceRoot: string,ctx: CtxInit) { + this.dependenciesMap = {}; this.ctx = ctx; } @@ -78,6 +80,8 @@ export class RustDependenciesProvider } private async getRootDependencies(): Promise { + const crates = await this.ctx.client.sendRequest(ra.fetchDependencyGraph, {}); + const dependenciesResult: FetchDependencyGraphResult = await this.ctx.client.sendRequest(ra.fetchDependencyGraph, {}); const crates = dependenciesResult.crates; const deps = crates.map((crate) => { diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 1a887b372017..f066a1c65481 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -86,6 +86,22 @@ export const fetchDependencyGraph = new lc.RequestType< void >("rust-analyzer/fetchDependencyGraph"); +export interface FetchDependencyGraphParams {} + +export interface FetchDependencyGraphResult { + crates: { + name: string; + version: string; + path: string; + }[]; +} + +export const fetchDependencyGraph = new lc.RequestType< + FetchDependencyGraphParams, + FetchDependencyGraphResult, + void +>("rust-analyzer/fetchDependencyGraph"); + export type ExpandMacroParams = { textDocument: lc.TextDocumentIdentifier; position: lc.Position; From fc57339fdf2566a7e149918a37d31ffd6c664e7b Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Sun, 2 Apr 2023 22:06:15 -0300 Subject: [PATCH 16/31] removing unused code --- editors/code/src/toolchain.ts | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts index 2d23e8c35ed5..8c2e55bcc26a 100644 --- a/editors/code/src/toolchain.ts +++ b/editors/code/src/toolchain.ts @@ -3,9 +3,7 @@ import * as os from "os"; import * as path from "path"; import * as readline from "readline"; import * as vscode from "vscode"; -import { execute, log, memoizeAsync } from "./util"; - -const TOOLCHAIN_PATTERN = new RegExp(/(.*)\s\(.*\)/); +import {execute, log, memoizeAsync} from "./util"; interface CompilationArtifact { fileName: string; @@ -24,7 +22,7 @@ export class Cargo { readonly rootFolder: string, readonly output: vscode.OutputChannel, readonly env: Record - ) { } + ) {} // Made public for testing purposes static artifactSpec(args: readonly string[]): ArtifactSpec { @@ -44,7 +42,7 @@ export class Cargo { } } - const result: ArtifactSpec = { cargoArgs: cargoArgs }; + const result: ArtifactSpec = {cargoArgs: cargoArgs}; if (cargoArgs[0] === "test" || cargoArgs[0] === "bench") { // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"} @@ -149,7 +147,7 @@ export class Cargo { cargo.stderr.on("data", (chunk) => onStderrString(chunk.toString())); - const rl = readline.createInterface({ input: cargo.stdout }); + const rl = readline.createInterface({input: cargo.stdout}); rl.on("line", (line) => { const message = JSON.parse(line); onStdoutJson(message); @@ -191,14 +189,14 @@ export async function getSysroot(dir: string): Promise { const rustcPath = await getPathForExecutable("rustc"); // do not memoize the result because the toolchain may change between runs - return await execute(`${rustcPath} --print sysroot`, { cwd: dir }); + return await execute(`${rustcPath} --print sysroot`, {cwd: dir}); } export async function getRustcId(dir: string): Promise { const rustcPath = await getPathForExecutable("rustc"); // do not memoize the result because the toolchain may change between runs - const data = await execute(`${rustcPath} -V -v`, { cwd: dir }); + const data = await execute(`${rustcPath} -V -v`, {cwd: dir}); const rx = /commit-hash:\s(.*)$/m; return rx.exec(data)![1]; @@ -219,11 +217,6 @@ export function cargoPath(): Promise { return getPathForExecutable("cargo"); } -/** Mirrors `toolchain::cargo()` implementation */ -export function rustupPath(): Promise { - return getPathForExecutable("rustup"); -} - /** Mirrors `toolchain::get_path_for_executable()` implementation */ export const getPathForExecutable = memoizeAsync( // We apply caching to decrease file-system interactions From 440889edec0bc4e2ca3b12f2d56f3eaff278c01b Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Sun, 2 Apr 2023 22:08:02 -0300 Subject: [PATCH 17/31] fixing main_loop.rs --- crates/rust-analyzer/src/main_loop.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 5d3aca29b539..7a81a18f4af9 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -655,7 +655,6 @@ impl GlobalState { .on_sync_mut::(handlers::handle_workspace_reload) .on_sync_mut::(handlers::handle_proc_macros_rebuild) .on_sync_mut::(handlers::handle_memory_usage) - .on_sync_mut::(handlers::fetch_dependency_graph) .on_sync_mut::(handlers::handle_shuffle_crate_graph) .on_sync::(handlers::handle_join_lines) .on_sync::(handlers::handle_on_enter) From 061940dad905e1a439b1eaead3342276294c4a4c Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Sun, 2 Apr 2023 22:37:07 -0300 Subject: [PATCH 18/31] running prettier --- editors/code/src/ctx.ts | 43 +++++---- editors/code/src/dependencies_provider.ts | 22 +++-- editors/code/src/main.ts | 102 +++++++++++----------- editors/code/src/toolchain.ts | 10 +-- 4 files changed, 86 insertions(+), 91 deletions(-) diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index ea42b249ff94..69347522b864 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, @@ -13,7 +13,7 @@ import { log, RustEditor, } from "./util"; -import {ServerStatusParams} from "./lsp_ext"; +import { ServerStatusParams } from "./lsp_ext"; import { Dependency, DependencyFile, @@ -27,10 +27,10 @@ import { RustDependenciesProvider, DependencyId, } from "./dependencies_provider"; -import {execRevealDependency} from "./commands"; -import {PersistentState} from "./persistent_state"; -import {bootstrap} from "./bootstrap"; -import {ExecOptions} from "child_process"; +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 @@ -39,12 +39,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( @@ -56,12 +56,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( @@ -116,7 +116,7 @@ export class Ctx { constructor( readonly extCtx: vscode.ExtensionContext, commandFactories: Record, - workspace: Workspace, + workspace: Workspace ) { extCtx.subscriptions.push(this); this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); @@ -128,8 +128,7 @@ export class Ctx { this.state = new PersistentState(extCtx.globalState); this.config = new Config(extCtx); - this.updateCommands("disable" - ); + this.updateCommands("disable"); this.setServerStatus({ health: "stopped", }); @@ -198,7 +197,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, @@ -276,7 +275,7 @@ export class Ctx { private prepareTreeDependenciesView(client: lc.LanguageClient) { const ctxInit: CtxInit = { ...this, - client: client + client: client, }; const rootPath = vscode.workspace.workspaceFolders![0].uri.fsPath; this._dependencies = new RustDependenciesProvider(rootPath, ctxInit); diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index f2838af6e060..3713250b8fca 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -13,8 +13,8 @@ import * as ra from "./lsp_ext"; export class RustDependenciesProvider implements vscode.TreeDataProvider { - - dependenciesMap: { [id: string]: Dependency | DependencyFile };ctx: CtxInit; + dependenciesMap: { [id: string]: Dependency | DependencyFile }; + ctx: CtxInit; constructor(private readonly workspaceRoot: string,ctx: CtxInit) { this.dependenciesMap = {}; @@ -82,7 +82,10 @@ export class RustDependenciesProvider private async getRootDependencies(): Promise { const crates = await this.ctx.client.sendRequest(ra.fetchDependencyGraph, {}); - const dependenciesResult: FetchDependencyGraphResult = await this.ctx.client.sendRequest(ra.fetchDependencyGraph, {}); + const dependenciesResult: FetchDependencyGraphResult = await this.ctx.client.sendRequest( + ra.fetchDependencyGraph, + {} + ); const crates = dependenciesResult.crates; const deps = crates.map((crate) => { const dep = this.toDep(crate.name, crate.version, crate.path); @@ -93,15 +96,10 @@ export class RustDependenciesProvider 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 - ); - } + 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/main.ts b/editors/code/src/main.ts index 774600f6c397..be9bc9d363ce 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,12 +24,11 @@ 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()); @@ -119,7 +118,7 @@ function createCommands(): Record { 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 () => { @@ -145,53 +144,52 @@ function createCommands(): Record { 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}, - revealDependency: {enabled: commands.revealDependency} + 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 }, + revealDependency: { enabled: commands.revealDependency }, }; } diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts index 8c2e55bcc26a..9c5c88c49e54 100644 --- a/editors/code/src/toolchain.ts +++ b/editors/code/src/toolchain.ts @@ -3,7 +3,7 @@ import * as os from "os"; import * as path from "path"; import * as readline from "readline"; import * as vscode from "vscode"; -import {execute, log, memoizeAsync} from "./util"; +import { execute, log, memoizeAsync } from "./util"; interface CompilationArtifact { fileName: string; @@ -42,7 +42,7 @@ export class Cargo { } } - const result: ArtifactSpec = {cargoArgs: cargoArgs}; + const result: ArtifactSpec = { cargoArgs: cargoArgs }; if (cargoArgs[0] === "test" || cargoArgs[0] === "bench") { // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"} @@ -147,7 +147,7 @@ export class Cargo { cargo.stderr.on("data", (chunk) => onStderrString(chunk.toString())); - const rl = readline.createInterface({input: cargo.stdout}); + const rl = readline.createInterface({ input: cargo.stdout }); rl.on("line", (line) => { const message = JSON.parse(line); onStdoutJson(message); @@ -189,14 +189,14 @@ export async function getSysroot(dir: string): Promise { const rustcPath = await getPathForExecutable("rustc"); // do not memoize the result because the toolchain may change between runs - return await execute(`${rustcPath} --print sysroot`, {cwd: dir}); + return await execute(`${rustcPath} --print sysroot`, { cwd: dir }); } export async function getRustcId(dir: string): Promise { const rustcPath = await getPathForExecutable("rustc"); // do not memoize the result because the toolchain may change between runs - const data = await execute(`${rustcPath} -V -v`, {cwd: dir}); + const data = await execute(`${rustcPath} -V -v`, { cwd: dir }); const rx = /commit-hash:\s(.*)$/m; return rx.exec(data)![1]; From e2535926e9587aa9c4ec08637aaa45a9fd92f5ea Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Sun, 2 Apr 2023 23:04:32 -0300 Subject: [PATCH 19/31] Fixing tests --- crates/ide/src/fetch_crates.rs | 6 +++++ crates/rust-analyzer/tests/slow-tests/tidy.rs | 2 +- docs/dev/lsp-extensions.md | 23 +++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/crates/ide/src/fetch_crates.rs b/crates/ide/src/fetch_crates.rs index c0bc4103c2f7..ec6d66d5c43a 100644 --- a/crates/ide/src/fetch_crates.rs +++ b/crates/ide/src/fetch_crates.rs @@ -10,6 +10,12 @@ pub struct CrateInfo { pub path: String, } +// Feature: Show Dependency Tree +// +// Shows a view tree with all the dependencies of this project +// +// |=== +// image::https://user-images.githubusercontent.com/5748995/229394139-2625beab-f4c9-484b-84ed-ad5dee0b1e1a.png[] pub(crate) fn fetch_crates(db: &RootDatabase) -> Vec { let crate_graph = db.crate_graph(); crate_graph diff --git a/crates/rust-analyzer/tests/slow-tests/tidy.rs b/crates/rust-analyzer/tests/slow-tests/tidy.rs index 8e3097fce423..91dde1c7528a 100644 --- a/crates/rust-analyzer/tests/slow-tests/tidy.rs +++ b/crates/rust-analyzer/tests/slow-tests/tidy.rs @@ -45,7 +45,7 @@ fn check_lsp_extensions_docs() { sh.read_file(sourcegen::project_root().join("docs/dev/lsp-extensions.md")).unwrap(); let text = lsp_extensions_md .lines() - .find_map(|line| line.strip_prefix("lsp_ext.rs hash:")) + .find_map(|line| dbg!(line.strip_prefix("lsp_ext.rs hash:"))) .unwrap() .trim(); u64::from_str_radix(text, 16).unwrap() diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 42f58fee30e1..41bda554be98 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -851,3 +851,26 @@ export interface Diagnostic { rendered?: string; }; } +``` + +## Dependency Tree + +**Method:** `rust-analyzer/fetchDependencyGraph` + +**Request:** + +```typescript +export interface FetchDependencyGraphParams {} +``` + +**Response:** +```typescript +export interface FetchDependencyGraphResult { + crates: { + name: string; + version: string; + path: string; + }[]; +} +``` +Returns all crates from this workspace, so it can be used create a viewTree to help navigate the dependency tree. \ No newline at end of file From 1b8288ff966bcabadf8f15d71511ff96184e81ea Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Tue, 4 Apr 2023 13:47:01 -0300 Subject: [PATCH 20/31] Fixing naming from graph to list --- crates/ide/src/fetch_crates.rs | 52 +++++++++++++---------- crates/rust-analyzer/src/handlers.rs | 10 ++--- crates/rust-analyzer/src/lsp_ext.rs | 14 +++--- crates/rust-analyzer/src/main_loop.rs | 2 +- docs/dev/lsp-extensions.md | 2 +- editors/code/src/dependencies_provider.ts | 8 ++-- editors/code/src/lsp_ext.ts | 12 +++--- 7 files changed, 52 insertions(+), 48 deletions(-) diff --git a/crates/ide/src/fetch_crates.rs b/crates/ide/src/fetch_crates.rs index ec6d66d5c43a..416082ae73b0 100644 --- a/crates/ide/src/fetch_crates.rs +++ b/crates/ide/src/fetch_crates.rs @@ -22,16 +22,21 @@ pub(crate) fn fetch_crates(db: &RootDatabase) -> Vec { .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 } - }) + .filter_map(|data| crate_info(data, db)) .collect() } +fn crate_info(data: &ide_db::base_db::CrateData, db: &RootDatabase) -> Option { + let crate_name = crate_name(data); + let crate_path = crate_path(db, data, &crate_name); + if let Some(crate_path) = crate_path { + let version = data.version.clone().unwrap_or_else(|| "".to_owned()); + Some(CrateInfo { name: crate_name, version, path: crate_path }) + } else { + None + } +} + fn crate_name(data: &ide_db::base_db::CrateData) -> String { data.display_name .clone() @@ -39,27 +44,28 @@ fn crate_name(data: &ide_db::base_db::CrateData) -> String { .unwrap_or("unknown".to_string()) } -fn crate_path(db: &RootDatabase, data: &ide_db::base_db::CrateData, crate_name: &str) -> String { +fn crate_path( + db: &RootDatabase, + data: &ide_db::base_db::CrateData, + crate_name: &str, +) -> Option { 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; - } + source_root_path.cloned().and_then(|mut root_path| { + let mut crate_path = None; + while let Some(vfs_path) = root_path.parent() { + match vfs_path.name_and_extension() { + Some((name, _)) => { + if name.starts_with(crate_name) { + crate_path = Some(vfs_path.to_string()); + break; } - None => break, } - root_path = vfs_path; + None => break, } - crate_path + root_path = vfs_path; } - None => "".to_owned(), - } + crate_path + }) } diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 2324490e53b6..5c966f0f68b0 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -9,7 +9,7 @@ use vfs::FileId; use crate::{ global_state::GlobalStateSnapshot, to_proto, Result, lsp_ext::{ - CrateInfoResult, FetchDependencyGraphResult, FetchDependencyGraphParams, + CrateInfoResult, FetchDependencyListResult, FetchDependencyListParams, }, }; @@ -49,12 +49,12 @@ pub(crate) fn publish_diagnostics( Ok(diagnostics) } -pub(crate) fn fetch_dependency_graph( +pub(crate) fn fetch_dependency_list( state: GlobalStateSnapshot, - _params: FetchDependencyGraphParams, -) -> Result { + _params: FetchDependencyListParams, +) -> Result { let crates = state.analysis.fetch_crates()?; - Ok(FetchDependencyGraphResult { + Ok(FetchDependencyListResult { crates: crates .into_iter() .map(|it| CrateInfoResult { name: it.name, version: it.version, path: it.path }) diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 18511da46839..6285578a6cea 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -34,21 +34,21 @@ pub struct CrateInfoResult { pub version: String, pub path: String, } -pub enum FetchDependencyGraph {} +pub enum FetchDependencyList {} -impl Request for FetchDependencyGraph { - type Params = FetchDependencyGraphParams; - type Result = FetchDependencyGraphResult; - const METHOD: &'static str = "rust-analyzer/fetchDependencyGraph"; +impl Request for FetchDependencyList { + type Params = FetchDependencyListParams; + type Result = FetchDependencyListResult; + const METHOD: &'static str = "rust-analyzer/fetchDependencyList"; } #[derive(Deserialize, Serialize, Debug)] #[serde(rename_all = "camelCase")] -pub struct FetchDependencyGraphParams {} +pub struct FetchDependencyListParams {} #[derive(Deserialize, Serialize, Debug)] #[serde(rename_all = "camelCase")] -pub struct FetchDependencyGraphResult { +pub struct FetchDependencyListResult { pub crates: Vec, } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 7a81a18f4af9..d3cfc5e40d63 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -660,7 +660,7 @@ impl GlobalState { .on_sync::(handlers::handle_on_enter) .on_sync::(handlers::handle_selection_range) .on_sync::(handlers::handle_matching_brace) - .on::(handlers::fetch_dependency_graph) + .on::(handlers::fetch_dependency_list) .on::(handlers::handle_analyzer_status) .on::(handlers::handle_syntax_tree) .on::(handlers::handle_view_hir) diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 41bda554be98..d73ae3d519e7 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -855,7 +855,7 @@ export interface Diagnostic { ## Dependency Tree -**Method:** `rust-analyzer/fetchDependencyGraph` +**Method:** `rust-analyzer/fetchDependencyList` **Request:** diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index 3713250b8fca..2e5ea04922fc 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -3,13 +3,12 @@ import * as fspath from "path"; import * as fs from "fs"; import { CtxInit } from "./ctx"; import * as ra from "./lsp_ext"; -import { FetchDependencyGraphResult } from "./lsp_ext"; +import { FetchDependencyListResult } from "./lsp_ext"; import { Ctx } from "./ctx"; import { setFlagsFromString } from "v8"; import * as ra from "./lsp_ext"; - export class RustDependenciesProvider implements vscode.TreeDataProvider { @@ -82,8 +81,8 @@ export class RustDependenciesProvider private async getRootDependencies(): Promise { const crates = await this.ctx.client.sendRequest(ra.fetchDependencyGraph, {}); - const dependenciesResult: FetchDependencyGraphResult = await this.ctx.client.sendRequest( - ra.fetchDependencyGraph, + const dependenciesResult: FetchDependencyListResult = await this.ctx.client.sendRequest( + ra.fetchDependencyList, {} ); const crates = dependenciesResult.crates; @@ -97,7 +96,6 @@ export class RustDependenciesProvider } 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); } } diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index f066a1c65481..30de5035d63b 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -70,9 +70,9 @@ export const viewItemTree = new lc.RequestType export type AnalyzerStatusParams = { textDocument?: lc.TextDocumentIdentifier }; -export interface FetchDependencyGraphParams {} +export interface FetchDependencyListParams {} -export interface FetchDependencyGraphResult { +export interface FetchDependencyListResult { crates: { name: string; version: string; @@ -80,11 +80,11 @@ export interface FetchDependencyGraphResult { }[]; } -export const fetchDependencyGraph = new lc.RequestType< - FetchDependencyGraphParams, - FetchDependencyGraphResult, +export const fetchDependencyList = new lc.RequestType< + FetchDependencyListParams, + FetchDependencyListResult, void ->("rust-analyzer/fetchDependencyGraph"); +>("rust-analyzer/fetchDependencyList"); export interface FetchDependencyGraphParams {} From 8e687f7afb156a7ad2458b7a5aa481b5d7e435d1 Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Wed, 5 Apr 2023 14:57:01 -0300 Subject: [PATCH 21/31] improving code to work with multi-workspaces --- crates/ide/src/fetch_crates.rs | 6 +- crates/ide/src/lib.rs | 4 +- editors/code/src/commands.ts | 70 ++++++++++++++++++----- editors/code/src/ctx.ts | 8 ++- editors/code/src/dependencies_provider.ts | 6 +- editors/code/src/util.ts | 18 ++++++ 6 files changed, 89 insertions(+), 23 deletions(-) diff --git a/crates/ide/src/fetch_crates.rs b/crates/ide/src/fetch_crates.rs index 416082ae73b0..5750d6b42612 100644 --- a/crates/ide/src/fetch_crates.rs +++ b/crates/ide/src/fetch_crates.rs @@ -1,9 +1,9 @@ use ide_db::{ base_db::{CrateOrigin, SourceDatabase, SourceDatabaseExt}, - RootDatabase, + FxIndexSet, RootDatabase, }; -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct CrateInfo { pub name: 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[] -pub(crate) fn fetch_crates(db: &RootDatabase) -> Vec { +pub(crate) fn fetch_crates(db: &RootDatabase) -> FxIndexSet { let crate_graph = db.crate_graph(); crate_graph .iter() diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 96adb11dcde3..24e2aed65a5b 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -70,7 +70,7 @@ use ide_db::{ salsa::{self, ParallelDatabase}, CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, VfsPath, }, - symbol_index, FxHashMap, LineIndexDatabase, + symbol_index, FxHashMap, FxIndexSet, LineIndexDatabase, }; use syntax::SourceFile; @@ -333,7 +333,7 @@ impl Analysis { self.with_db(|db| view_crate_graph::view_crate_graph(db, full)) } - pub fn fetch_crates(&self) -> Cancellable> { + pub fn fetch_crates(&self) -> Cancellable> { self.with_db(|db| fetch_crates::fetch_crates(db)) } diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 7fe32754c903..4393d5fccbcc 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -8,7 +8,15 @@ 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, RustEditor } from "./util"; +import { + isRustDocument, + isCargoTomlDocument, + sleep, + isRustEditor, + RustEditor, + RustDocument, + closeDocument, +} from "./util"; import { startDebugSession, makeDebugConfig } from "./debug"; import { LanguageClient } from "vscode-languageclient/node"; import { LINKED_COMMANDS } from "./client"; @@ -269,27 +277,63 @@ export function openCargoToml(ctx: CtxInit): Cmd { 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 }); - } + await revealParentChain(editor.document, ctx); } }; } +/** + * 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\ + *  |-beches\ + *  |-src\ + *  |- ... + * - std + * 2. reveal every children of src: + * core + * alloc\ + *  |-beches\ + *  |-src\ + *   |- lib.rs\ + *   |- str.rs <------- FOUND IT!\ + *   |- ...\ + *  |- ...\ + * 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 { await vscode.commands.executeCommand("rust-analyzer.revealDependency", e); } diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 69347522b864..dd2373d58473 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -7,6 +7,7 @@ import { Config, prepareVSCodeConfig } from "./config"; import { createClient } from "./client"; import { executeDiscoverProject, + isDocumentInWorkspace, isRustDocument, isRustEditor, LazyOutputChannel, @@ -277,15 +278,16 @@ export class Ctx { ...this, client: client, }; - const rootPath = vscode.workspace.workspaceFolders![0].uri.fsPath; - this._dependencies = new RustDependenciesProvider(rootPath, ctxInit); + this._dependencies = new RustDependenciesProvider(ctxInit); this._treeView = vscode.window.createTreeView("rustDependencies", { treeDataProvider: this._dependencies, showCollapseAll: true, }); + this.pushExtCleanup(this._treeView); 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) => { void vscode.window.showErrorMessage(`Dependency error: ${reason}`); }); diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index 2e5ea04922fc..3edbb3168143 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -15,7 +15,7 @@ export class RustDependenciesProvider dependenciesMap: { [id: string]: Dependency | DependencyFile }; ctx: CtxInit; - constructor(private readonly workspaceRoot: string,ctx: CtxInit) { + constructor(ctx: CtxInit) { this.dependenciesMap = {}; this.ctx = ctx; } @@ -37,6 +37,7 @@ export class RustDependenciesProvider } refresh(): void { + this.dependenciesMap = {}; this._onDidChangeTreeData.fire(); } @@ -56,7 +57,7 @@ export class RustDependenciesProvider element?: Dependency | DependencyFile ): vscode.ProviderResult { return new Promise((resolve, _reject) => { - if (!this.workspaceRoot) { + if (!vscode.workspace.workspaceFolders) { void vscode.window.showInformationMessage("No dependency in empty workspace"); return Promise.resolve([]); } @@ -108,6 +109,7 @@ export class Dependency extends vscode.TreeItem { public readonly collapsibleState: vscode.TreeItemCollapsibleState ) { super(label, collapsibleState); + this.id = this.dependencyPath.toLowerCase(); this.tooltip = `${this.label}-${this.version}`; this.description = this.version; this.resourceUri = vscode.Uri.file(dependencyPath); diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index 922fbcbcf35a..0196b37b8b65 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts @@ -112,6 +112,24 @@ export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor { 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 { log.debug("Checking availability of a binary at", path); From a3081a67742a67680f7ddef503094e62e2e68c55 Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Sat, 8 Apr 2023 13:07:25 -0300 Subject: [PATCH 22/31] Adding crate_root_path to crate_graph --- crates/base-db/src/fixture.rs | 4 +++ crates/base-db/src/input.rs | 13 +++++++++ crates/ide/src/fetch_crates.rs | 34 +++-------------------- crates/ide/src/lib.rs | 1 + crates/ide/src/shuffle_crate_graph.rs | 1 + crates/paths/src/lib.rs | 7 +++++ crates/project-model/src/tests.rs | 6 ++++ crates/project-model/src/workspace.rs | 32 ++++++++++++++++++++- crates/vfs/src/vfs_path.rs | 5 +--- editors/code/src/dependencies_provider.ts | 8 ++++-- 10 files changed, 74 insertions(+), 37 deletions(-) diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs index 1936eabdd9f3..cf3be9d07fdc 100644 --- a/crates/base-db/src/fixture.rs +++ b/crates/base-db/src/fixture.rs @@ -165,6 +165,7 @@ impl ChangeFixture { meta.edition, Some(crate_name.clone().into()), version, + None, meta.cfg, Default::default(), meta.env, @@ -205,6 +206,7 @@ impl ChangeFixture { Edition::CURRENT, Some(CrateName::new("test").unwrap().into()), None, + None, default_cfg, Default::default(), Env::default(), @@ -249,6 +251,7 @@ impl ChangeFixture { Edition::Edition2021, Some(CrateDisplayName::from_canonical_name("core".to_string())), None, + None, Default::default(), Default::default(), Env::default(), @@ -288,6 +291,7 @@ impl ChangeFixture { Edition::Edition2021, Some(CrateDisplayName::from_canonical_name("proc_macros".to_string())), None, + None, Default::default(), Default::default(), Env::default(), diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index e6d265df675d..466e2eab5601 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -304,6 +304,7 @@ pub struct CrateData { /// For purposes of analysis, crates are anonymous (only names in /// `Dependency` matters), this name should only be used for UI. pub display_name: Option, + pub crate_root_path: Option, pub cfg_options: CfgOptions, /// The cfg options that could be used by the crate pub potential_cfg_options: Option, @@ -361,6 +362,7 @@ impl CrateGraph { edition: Edition, display_name: Option, version: Option, + crate_root_path: Option, cfg_options: CfgOptions, potential_cfg_options: Option, env: Env, @@ -374,6 +376,7 @@ impl CrateGraph { edition, version, display_name, + crate_root_path, cfg_options, potential_cfg_options, env, @@ -740,6 +743,7 @@ mod tests { Edition2018, None, None, + None, Default::default(), Default::default(), Env::default(), @@ -753,6 +757,7 @@ mod tests { Edition2018, None, None, + None, Default::default(), Default::default(), Env::default(), @@ -766,6 +771,7 @@ mod tests { Edition2018, None, None, + None, Default::default(), Default::default(), Env::default(), @@ -793,6 +799,7 @@ mod tests { Edition2018, None, None, + None, Default::default(), Default::default(), Env::default(), @@ -806,6 +813,7 @@ mod tests { Edition2018, None, None, + None, Default::default(), Default::default(), Env::default(), @@ -830,6 +838,7 @@ mod tests { Edition2018, None, None, + None, Default::default(), Default::default(), Env::default(), @@ -843,6 +852,7 @@ mod tests { Edition2018, None, None, + None, Default::default(), Default::default(), Env::default(), @@ -856,6 +866,7 @@ mod tests { Edition2018, None, None, + None, Default::default(), Default::default(), Env::default(), @@ -880,6 +891,7 @@ mod tests { Edition2018, None, None, + None, Default::default(), Default::default(), Env::default(), @@ -893,6 +905,7 @@ mod tests { Edition2018, None, None, + None, Default::default(), Default::default(), Env::default(), diff --git a/crates/ide/src/fetch_crates.rs b/crates/ide/src/fetch_crates.rs index 5750d6b42612..916c26855a2e 100644 --- a/crates/ide/src/fetch_crates.rs +++ b/crates/ide/src/fetch_crates.rs @@ -1,5 +1,5 @@ use ide_db::{ - base_db::{CrateOrigin, SourceDatabase, SourceDatabaseExt}, + base_db::{CrateOrigin, SourceDatabase}, FxIndexSet, RootDatabase, }; @@ -22,13 +22,13 @@ pub(crate) fn fetch_crates(db: &RootDatabase) -> FxIndexSet { .iter() .map(|crate_id| &crate_graph[crate_id]) .filter(|&data| !matches!(data.origin, CrateOrigin::Local { .. })) - .filter_map(|data| crate_info(data, db)) + .filter_map(|data| crate_info(data)) .collect() } -fn crate_info(data: &ide_db::base_db::CrateData, db: &RootDatabase) -> Option { +fn crate_info(data: &ide_db::base_db::CrateData) -> Option { let crate_name = crate_name(data); - let crate_path = crate_path(db, data, &crate_name); + let crate_path = data.crate_root_path.as_ref().map(|p| p.display().to_string()); if let Some(crate_path) = crate_path { let version = data.version.clone().unwrap_or_else(|| "".to_owned()); Some(CrateInfo { name: crate_name, version, path: crate_path }) @@ -43,29 +43,3 @@ fn crate_name(data: &ide_db::base_db::CrateData) -> String { .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, -) -> Option { - 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); - source_root_path.cloned().and_then(|mut root_path| { - let mut crate_path = None; - while let Some(vfs_path) = root_path.parent() { - match vfs_path.name_and_extension() { - Some((name, _)) => { - if name.starts_with(crate_name) { - crate_path = Some(vfs_path.to_string()); - break; - } - } - None => break, - } - root_path = vfs_path; - } - crate_path - }) -} diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 24e2aed65a5b..131c781beee9 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -239,6 +239,7 @@ impl Analysis { Edition::CURRENT, None, None, + None, cfg_options.clone(), None, Env::default(), diff --git a/crates/ide/src/shuffle_crate_graph.rs b/crates/ide/src/shuffle_crate_graph.rs index d94b15f60c84..51ecc4001f6d 100644 --- a/crates/ide/src/shuffle_crate_graph.rs +++ b/crates/ide/src/shuffle_crate_graph.rs @@ -34,6 +34,7 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) { data.edition, data.display_name.clone(), data.version.clone(), + data.crate_root_path.clone(), data.cfg_options.clone(), data.potential_cfg_options.clone(), data.env.clone(), diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs index ac09121aedf3..6a3c685016dc 100644 --- a/crates/paths/src/lib.rs +++ b/crates/paths/src/lib.rs @@ -213,6 +213,13 @@ impl AbsPath { pub fn exists(&self) -> bool { self.0.exists() } + + pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { + Some(( + self.file_stem()?.to_str()?, + self.extension().and_then(|extension| extension.to_str()), + )) + } // endregion:delegate-methods } diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index 9acf60bf4a87..ca28d742d5c4 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -102,6 +102,11 @@ fn replace_root(s: &mut String, direction: bool) { } } +fn replace_fake_sys_root(s: &mut String) { + let root = get_test_path("fake-sysroot"); + *s = s.replace(root.to_str().expect("expected str"), "$FAKESYSROOT$") +} + fn get_test_path(file: &str) -> PathBuf { let base = PathBuf::from(env!("CARGO_MANIFEST_DIR")); base.join("test_data").join(file) @@ -140,6 +145,7 @@ fn to_crate_graph(project_workspace: ProjectWorkspace) -> (CrateGraph, ProcMacro fn check_crate_graph(crate_graph: CrateGraph, expect: ExpectFile) { let mut crate_graph = format!("{crate_graph:#?}"); replace_root(&mut crate_graph, false); + replace_fake_sys_root(&mut crate_graph); expect.assert_eq(&crate_graph); } diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 5924287a37f9..ba5a1c4e3543 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -766,6 +766,7 @@ fn project_json_to_crate_graph( proc_macro_dylib_path, is_proc_macro, repository, + root_module, .. }, file_id, @@ -784,6 +785,7 @@ fn project_json_to_crate_graph( *edition, display_name.clone(), version.clone(), + crate_path(display_name.as_ref(), root_module), target_cfgs.iter().chain(cfg.iter()).cloned().collect(), None, env, @@ -832,6 +834,30 @@ fn project_json_to_crate_graph( res } +//Thats a best effort to try and find the crate path for a project configured using JsonProject model +fn crate_path( + crate_name: Option<&CrateDisplayName>, + root_module_path: &AbsPathBuf, +) -> Option { + crate_name.and_then(|crate_name| { + let mut crate_path = None; + let mut root_path = root_module_path.as_path(); + while let Some(path) = root_path.parent() { + match path.name_and_extension() { + Some((name, _)) => { + if name.starts_with(crate_name.canonical_name()) { + crate_path = Some(path.to_path_buf()); + break; + } + } + None => break, + } + root_path = path; + } + crate_path + }) +} + fn cargo_to_crate_graph( load: &mut dyn FnMut(&AbsPath) -> Option, rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>, @@ -1053,6 +1079,7 @@ fn detached_files_to_crate_graph( Edition::CURRENT, display_name.clone(), None, + None, cfg_options.clone(), None, Env::default(), @@ -1249,6 +1276,7 @@ fn add_target_crate_root( edition, Some(display_name), Some(pkg.version.to_string()), + Some(pkg.manifest.parent().to_owned()), cfg_options, potential_cfg_options, env, @@ -1320,11 +1348,13 @@ fn sysroot_to_crate_graph( let env = Env::default(); let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone()); - let crate_id = crate_graph.add_crate_root( + let crate_root_path = sysroot.src_root().join(display_name.canonical_name()); + let crate_id = crate_graph.add_crate_root( file_id, Edition::CURRENT, Some(display_name), None, + Some(crate_root_path), cfg_options.clone(), None, env, diff --git a/crates/vfs/src/vfs_path.rs b/crates/vfs/src/vfs_path.rs index 38501a8ba5a3..d327f2edf144 100644 --- a/crates/vfs/src/vfs_path.rs +++ b/crates/vfs/src/vfs_path.rs @@ -107,10 +107,7 @@ impl VfsPath { /// Returns `self`'s base name and file extension. pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { match &self.0 { - VfsPathRepr::PathBuf(p) => Some(( - p.file_stem()?.to_str()?, - p.extension().and_then(|extension| extension.to_str()), - )), + VfsPathRepr::PathBuf(p) => p.name_and_extension(), VfsPathRepr::VirtualPath(p) => p.name_and_extension(), } } diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index 3edbb3168143..aff5102e01f0 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -110,9 +110,13 @@ export class Dependency extends vscode.TreeItem { ) { super(label, collapsibleState); this.id = this.dependencyPath.toLowerCase(); - this.tooltip = `${this.label}-${this.version}`; this.description = this.version; this.resourceUri = vscode.Uri.file(dependencyPath); + if (this.version) { + this.tooltip = `${this.label}-${this.version}`; + } else { + this.tooltip = this.label; + } } } @@ -124,8 +128,8 @@ export class DependencyFile extends vscode.TreeItem { public readonly collapsibleState: vscode.TreeItemCollapsibleState ) { super(vscode.Uri.file(dependencyPath), collapsibleState); - const isDir = fs.lstatSync(this.dependencyPath).isDirectory(); this.id = this.dependencyPath.toLowerCase(); + const isDir = fs.lstatSync(this.dependencyPath).isDirectory(); if (!isDir) { this.command = { command: "vscode.open", title: "Open File", From fe7874af90b9e1190b5aebcf3a6d5544177cf9b2 Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Sat, 8 Apr 2023 14:18:29 -0300 Subject: [PATCH 23/31] reveal only when tree is visible --- editors/code/src/commands.ts | 2 -- editors/code/src/ctx.ts | 34 +++++++++++++++++++++++++++++----- editors/code/src/util.ts | 5 ----- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 4393d5fccbcc..81d3b8da7f90 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -15,7 +15,6 @@ import { isRustEditor, RustEditor, RustDocument, - closeDocument, } from "./util"; import { startDebugSession, makeDebugConfig } from "./debug"; import { LanguageClient } from "vscode-languageclient/node"; @@ -324,7 +323,6 @@ async function revealParentChain(document: RustDocument, ctx: CtxInit) { 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)); diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index dd2373d58473..4e592823b04c 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -285,14 +285,38 @@ export class Ctx { }); this.pushExtCleanup(this._treeView); - vscode.window.onDidChangeActiveTextEditor((e) => { + vscode.window.onDidChangeActiveTextEditor(async (e) => { // we should skip documents that belong to the current workspace - if (e && isRustEditor(e) && !isDocumentInWorkspace(e.document)) { - execRevealDependency(e).catch((reason) => { - void vscode.window.showErrorMessage(`Dependency error: ${reason}`); - }); + if (this.shouldRevealDependency(e)) { + try { + await execRevealDependency(e); + } catch (reason) { + await vscode.window.showErrorMessage(`Dependency error: ${reason}`); + } } }); + + this.treeView?.onDidChangeVisibility(async (e) => { + if (e.visible) { + const activeEditor = vscode.window.activeTextEditor; + if (this.shouldRevealDependency(activeEditor)) { + try { + await execRevealDependency(activeEditor); + } catch (reason) { + await vscode.window.showErrorMessage(`Dependency error: ${reason}`); + } + } + } + }); + } + + private shouldRevealDependency(e: vscode.TextEditor | undefined): e is RustEditor { + return ( + e !== undefined && + isRustEditor(e) && + !isDocumentInWorkspace(e.document) && + (this.treeView?.visible || false) + ); } async restart() { diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index 0196b37b8b65..b6b779e26601 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts @@ -125,11 +125,6 @@ export function isDocumentInWorkspace(document: RustDocument): boolean { 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 { log.debug("Checking availability of a binary at", path); From c372fb3495af20f3e76097a782018cc122f3ab78 Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Sat, 8 Apr 2023 15:26:48 -0300 Subject: [PATCH 24/31] fixing tests for windows --- crates/project-model/src/tests.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index ca28d742d5c4..c3c654ddb6f5 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -103,8 +103,15 @@ fn replace_root(s: &mut String, direction: bool) { } fn replace_fake_sys_root(s: &mut String) { - let root = get_test_path("fake-sysroot"); - *s = s.replace(root.to_str().expect("expected str"), "$FAKESYSROOT$") + let fake_sysroot_path = get_test_path("fake-sysroot"); + let fake_sysroot_path = if cfg!(windows) { + let normalized_path = + fake_sysroot_path.to_str().expect("expected str").replace(r#"\"#, r#"\\"#); + format!(r#"{}\\"#, normalized_path) + } else { + format!("{}/", fake_sysroot_path.to_str().expect("expected str")) + }; + *s = s.replace(&fake_sysroot_path, "$FAKESYSROOT$") } fn get_test_path(file: &str) -> PathBuf { From 66fe84d9369c40f2b17ae8cd541d6d87c501b45f Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Thu, 13 Apr 2023 13:06:43 -0300 Subject: [PATCH 25/31] accepting review suggestions --- crates/base-db/src/fixture.rs | 4 -- crates/base-db/src/input.rs | 13 ----- crates/ide/src/fetch_crates.rs | 28 ++++----- crates/ide/src/lib.rs | 1 - crates/ide/src/shuffle_crate_graph.rs | 1 - crates/paths/src/lib.rs | 14 ++--- crates/project-model/src/workspace.rs | 57 +++++-------------- crates/rust-analyzer/src/handlers.rs | 30 +++++++++- crates/rust-analyzer/src/lsp_ext.rs | 4 +- crates/rust-analyzer/tests/slow-tests/tidy.rs | 2 +- docs/dev/lsp-extensions.md | 4 +- editors/code/src/dependencies_provider.ts | 2 +- editors/code/src/lsp_ext.ts | 4 +- 13 files changed, 67 insertions(+), 97 deletions(-) diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs index cf3be9d07fdc..1936eabdd9f3 100644 --- a/crates/base-db/src/fixture.rs +++ b/crates/base-db/src/fixture.rs @@ -165,7 +165,6 @@ impl ChangeFixture { meta.edition, Some(crate_name.clone().into()), version, - None, meta.cfg, Default::default(), meta.env, @@ -206,7 +205,6 @@ impl ChangeFixture { Edition::CURRENT, Some(CrateName::new("test").unwrap().into()), None, - None, default_cfg, Default::default(), Env::default(), @@ -251,7 +249,6 @@ impl ChangeFixture { Edition::Edition2021, Some(CrateDisplayName::from_canonical_name("core".to_string())), None, - None, Default::default(), Default::default(), Env::default(), @@ -291,7 +288,6 @@ impl ChangeFixture { Edition::Edition2021, Some(CrateDisplayName::from_canonical_name("proc_macros".to_string())), None, - None, Default::default(), Default::default(), Env::default(), diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index 466e2eab5601..e6d265df675d 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -304,7 +304,6 @@ pub struct CrateData { /// For purposes of analysis, crates are anonymous (only names in /// `Dependency` matters), this name should only be used for UI. pub display_name: Option, - pub crate_root_path: Option, pub cfg_options: CfgOptions, /// The cfg options that could be used by the crate pub potential_cfg_options: Option, @@ -362,7 +361,6 @@ impl CrateGraph { edition: Edition, display_name: Option, version: Option, - crate_root_path: Option, cfg_options: CfgOptions, potential_cfg_options: Option, env: Env, @@ -376,7 +374,6 @@ impl CrateGraph { edition, version, display_name, - crate_root_path, cfg_options, potential_cfg_options, env, @@ -743,7 +740,6 @@ mod tests { Edition2018, None, None, - None, Default::default(), Default::default(), Env::default(), @@ -757,7 +753,6 @@ mod tests { Edition2018, None, None, - None, Default::default(), Default::default(), Env::default(), @@ -771,7 +766,6 @@ mod tests { Edition2018, None, None, - None, Default::default(), Default::default(), Env::default(), @@ -799,7 +793,6 @@ mod tests { Edition2018, None, None, - None, Default::default(), Default::default(), Env::default(), @@ -813,7 +806,6 @@ mod tests { Edition2018, None, None, - None, Default::default(), Default::default(), Env::default(), @@ -838,7 +830,6 @@ mod tests { Edition2018, None, None, - None, Default::default(), Default::default(), Env::default(), @@ -852,7 +843,6 @@ mod tests { Edition2018, None, None, - None, Default::default(), Default::default(), Env::default(), @@ -866,7 +856,6 @@ mod tests { Edition2018, None, None, - None, Default::default(), Default::default(), Env::default(), @@ -891,7 +880,6 @@ mod tests { Edition2018, None, None, - None, Default::default(), Default::default(), Env::default(), @@ -905,7 +893,6 @@ mod tests { Edition2018, None, None, - None, Default::default(), Default::default(), Env::default(), diff --git a/crates/ide/src/fetch_crates.rs b/crates/ide/src/fetch_crates.rs index 916c26855a2e..d326b7c2ccc9 100644 --- a/crates/ide/src/fetch_crates.rs +++ b/crates/ide/src/fetch_crates.rs @@ -1,13 +1,13 @@ use ide_db::{ - base_db::{CrateOrigin, SourceDatabase}, + base_db::{CrateOrigin, FileId, SourceDatabase}, FxIndexSet, RootDatabase, }; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct CrateInfo { - pub name: String, - pub version: String, - pub path: String, + pub name: Option, + pub version: Option, + pub root_file_id: FileId, } // Feature: Show Dependency Tree @@ -22,24 +22,16 @@ pub(crate) fn fetch_crates(db: &RootDatabase) -> FxIndexSet { .iter() .map(|crate_id| &crate_graph[crate_id]) .filter(|&data| !matches!(data.origin, CrateOrigin::Local { .. })) - .filter_map(|data| crate_info(data)) + .map(|data| crate_info(data)) .collect() } -fn crate_info(data: &ide_db::base_db::CrateData) -> Option { +fn crate_info(data: &ide_db::base_db::CrateData) -> CrateInfo { let crate_name = crate_name(data); - let crate_path = data.crate_root_path.as_ref().map(|p| p.display().to_string()); - if let Some(crate_path) = crate_path { - let version = data.version.clone().unwrap_or_else(|| "".to_owned()); - Some(CrateInfo { name: crate_name, version, path: crate_path }) - } else { - None - } + let version = data.version.clone(); + CrateInfo { name: crate_name, version, root_file_id: data.root_file_id } } -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_name(data: &ide_db::base_db::CrateData) -> Option { + data.display_name.as_ref().map(|it| it.canonical_name().to_owned()) } diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 131c781beee9..24e2aed65a5b 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -239,7 +239,6 @@ impl Analysis { Edition::CURRENT, None, None, - None, cfg_options.clone(), None, Env::default(), diff --git a/crates/ide/src/shuffle_crate_graph.rs b/crates/ide/src/shuffle_crate_graph.rs index 51ecc4001f6d..d94b15f60c84 100644 --- a/crates/ide/src/shuffle_crate_graph.rs +++ b/crates/ide/src/shuffle_crate_graph.rs @@ -34,7 +34,6 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) { data.edition, data.display_name.clone(), data.version.clone(), - data.crate_root_path.clone(), data.cfg_options.clone(), data.potential_cfg_options.clone(), data.env.clone(), diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs index 6a3c685016dc..083dfcf43d96 100644 --- a/crates/paths/src/lib.rs +++ b/crates/paths/src/lib.rs @@ -184,6 +184,13 @@ impl AbsPath { self.0.ends_with(&suffix.0) } + pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { + Some(( + self.file_stem()?.to_str()?, + self.extension().and_then(|extension| extension.to_str()), + )) + } + // region:delegate-methods // Note that we deliberately don't implement `Deref` here. @@ -213,13 +220,6 @@ impl AbsPath { pub fn exists(&self) -> bool { self.0.exists() } - - pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { - Some(( - self.file_stem()?.to_str()?, - self.extension().and_then(|extension| extension.to_str()), - )) - } // endregion:delegate-methods } diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index ba5a1c4e3543..102f862522e6 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -766,7 +766,6 @@ fn project_json_to_crate_graph( proc_macro_dylib_path, is_proc_macro, repository, - root_module, .. }, file_id, @@ -785,7 +784,6 @@ fn project_json_to_crate_graph( *edition, display_name.clone(), version.clone(), - crate_path(display_name.as_ref(), root_module), target_cfgs.iter().chain(cfg.iter()).cloned().collect(), None, env, @@ -834,30 +832,6 @@ fn project_json_to_crate_graph( res } -//Thats a best effort to try and find the crate path for a project configured using JsonProject model -fn crate_path( - crate_name: Option<&CrateDisplayName>, - root_module_path: &AbsPathBuf, -) -> Option { - crate_name.and_then(|crate_name| { - let mut crate_path = None; - let mut root_path = root_module_path.as_path(); - while let Some(path) = root_path.parent() { - match path.name_and_extension() { - Some((name, _)) => { - if name.starts_with(crate_name.canonical_name()) { - crate_path = Some(path.to_path_buf()); - break; - } - } - None => break, - } - root_path = path; - } - crate_path - }) -} - fn cargo_to_crate_graph( load: &mut dyn FnMut(&AbsPath) -> Option, rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>, @@ -1079,7 +1053,6 @@ fn detached_files_to_crate_graph( Edition::CURRENT, display_name.clone(), None, - None, cfg_options.clone(), None, Env::default(), @@ -1276,7 +1249,6 @@ fn add_target_crate_root( edition, Some(display_name), Some(pkg.version.to_string()), - Some(pkg.manifest.parent().to_owned()), cfg_options, potential_cfg_options, env, @@ -1345,27 +1317,24 @@ fn sysroot_to_crate_graph( .filter_map(|krate| { let file_id = load(&sysroot[krate].root)?; - let env = Env::default(); - let display_name = - CrateDisplayName::from_canonical_name(sysroot[krate].name.clone()); - let crate_root_path = sysroot.src_root().join(display_name.canonical_name()); + let env = Env::default(); + let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone()); let crate_id = crate_graph.add_crate_root( file_id, Edition::CURRENT, Some(display_name), None, - Some(crate_root_path), - cfg_options.clone(), - None, - env, - false, - CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)), - target_layout.clone(), - channel, - ); - Some((krate, crate_id)) - }) - .collect(), + cfg_options.clone(), + None, + env, + false, + CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)), + target_layout.clone(), + channel, + ); + Some((krate, crate_id)) + }) + .collect(), }; for from in sysroot.crates() { for &to in sysroot[from].deps.iter() { diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 5c966f0f68b0..ae6dd4dd5613 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -57,7 +57,35 @@ pub(crate) fn fetch_dependency_list( Ok(FetchDependencyListResult { crates: crates .into_iter() - .map(|it| CrateInfoResult { name: it.name, version: it.version, path: it.path }) + .filter_map(|it| { + let root_file_path = state.file_id_to_file_path(it.root_file_id); + crate_path(it.name.as_ref(), root_file_path).map(|crate_path| CrateInfoResult { + name: it.name, + version: it.version, + path: crate_path.to_string(), + }) + }) .collect(), }) } + +//Thats a best effort to try and find the crate path +fn crate_path(crate_name: Option<&String>, root_file_path: VfsPath) -> Option { + crate_name.and_then(|crate_name| { + let mut crate_path = None; + let mut root_path = root_file_path; + while let Some(path) = root_path.parent() { + match path.name_and_extension() { + Some((name, _)) => { + if name.starts_with(crate_name.as_str()) { + crate_path = Some(path); + break; + } + } + None => break, + } + root_path = path; + } + crate_path + }) +} \ No newline at end of file diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 6285578a6cea..8689fae0798b 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -30,8 +30,8 @@ pub struct AnalyzerStatusParams { #[derive(Deserialize, Serialize, Debug)] #[serde(rename_all = "camelCase")] pub struct CrateInfoResult { - pub name: String, - pub version: String, + pub name: Option, + pub version: Option, pub path: String, } pub enum FetchDependencyList {} diff --git a/crates/rust-analyzer/tests/slow-tests/tidy.rs b/crates/rust-analyzer/tests/slow-tests/tidy.rs index 91dde1c7528a..8e3097fce423 100644 --- a/crates/rust-analyzer/tests/slow-tests/tidy.rs +++ b/crates/rust-analyzer/tests/slow-tests/tidy.rs @@ -45,7 +45,7 @@ fn check_lsp_extensions_docs() { sh.read_file(sourcegen::project_root().join("docs/dev/lsp-extensions.md")).unwrap(); let text = lsp_extensions_md .lines() - .find_map(|line| dbg!(line.strip_prefix("lsp_ext.rs hash:"))) + .find_map(|line| line.strip_prefix("lsp_ext.rs hash:")) .unwrap() .trim(); u64::from_str_radix(text, 16).unwrap() diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index d73ae3d519e7..0fb92638e0d2 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -860,12 +860,12 @@ export interface Diagnostic { **Request:** ```typescript -export interface FetchDependencyGraphParams {} +export interface FetchDependencyListParams {} ``` **Response:** ```typescript -export interface FetchDependencyGraphResult { +export interface FetchDependencyListResult { crates: { name: string; version: string; diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index aff5102e01f0..b1ea36588677 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -88,7 +88,7 @@ export class RustDependenciesProvider ); const crates = dependenciesResult.crates; const deps = crates.map((crate) => { - const dep = this.toDep(crate.name, crate.version, crate.path); + const dep = this.toDep(crate.name || "unknown", crate.version || "", crate.path); this.dependenciesMap[dep.dependencyPath.toLowerCase()] = dep; this.dependenciesMap[stdlib.dependencyPath.toLowerCase()] = stdlib; return dep; diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 30de5035d63b..b72804e510ce 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -74,8 +74,8 @@ export interface FetchDependencyListParams {} export interface FetchDependencyListResult { crates: { - name: string; - version: string; + name: string | undefined; + version: string | undefined; path: string; }[]; } From bd2160f014aa0bd6761c90697e00650bf008993a Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Wed, 26 Apr 2023 21:54:31 -0300 Subject: [PATCH 26/31] final rabasing fixes --- editors/code/src/ctx.ts | 25 +++----- editors/code/src/dependencies_provider.ts | 33 +++++------ editors/code/src/toolchain.ts | 69 +---------------------- 3 files changed, 23 insertions(+), 104 deletions(-) diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 4e592823b04c..60ea76957955 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -22,13 +22,6 @@ import { DependencyId, } from "./dependencies_provider"; import { execRevealDependency } from "./commands"; -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"; @@ -40,12 +33,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( @@ -59,9 +52,9 @@ export function fetchWorkspace(): Workspace { ? rustDocuments.length === 0 ? { kind: "Empty" } : { - kind: "Detached Files", - files: rustDocuments, - } + kind: "Detached Files", + files: rustDocuments, + } : { kind: "Workspace Folder" }; } @@ -483,4 +476,4 @@ export interface Disposable { dispose(): void; } -export type Cmd = (...args: any[]) => unknown; +export type Cmd = (...args: any[]) => unknown; \ No newline at end of file diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index b1ea36588677..0faf7665cfda 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -1,17 +1,12 @@ import * as vscode from "vscode"; import * as fspath from "path"; import * as fs from "fs"; -import { CtxInit } from "./ctx"; +import {CtxInit} from "./ctx"; import * as ra from "./lsp_ext"; -import { FetchDependencyListResult } from "./lsp_ext"; -import { Ctx } from "./ctx"; -import { setFlagsFromString } from "v8"; -import * as ra from "./lsp_ext"; - +import {FetchDependencyListResult} from "./lsp_ext"; export class RustDependenciesProvider - implements vscode.TreeDataProvider -{ + implements vscode.TreeDataProvider { dependenciesMap: { [id: string]: Dependency | DependencyFile }; ctx: CtxInit; @@ -61,7 +56,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); @@ -80,20 +74,17 @@ export class RustDependenciesProvider } private async getRootDependencies(): Promise { - const crates = await this.ctx.client.sendRequest(ra.fetchDependencyGraph, {}); - const dependenciesResult: FetchDependencyListResult = await this.ctx.client.sendRequest( ra.fetchDependencyList, {} ); const crates = dependenciesResult.crates; - const deps = crates.map((crate) => { - const dep = this.toDep(crate.name || "unknown", crate.version || "", crate.path); + + return crates.map((crate) => { + const dep = this.toDep(crate.name || "unknown", crate.version || "", crate.path); this.dependenciesMap[dep.dependencyPath.toLowerCase()] = dep; - this.dependenciesMap[stdlib.dependencyPath.toLowerCase()] = stdlib; - return dep; + return dep; }); - return deps; } private toDep(moduleName: string, version: string, path: string): Dependency { @@ -131,11 +122,13 @@ export class DependencyFile extends vscode.TreeItem { this.id = this.dependencyPath.toLowerCase(); const isDir = fs.lstatSync(this.dependencyPath).isDirectory(); if (!isDir) { - this.command = { command: "vscode.open", + this.command = { + command: "vscode.open", title: "Open File", arguments: [vscode.Uri.file(this.dependencyPath)], - }; - }} + }; + } + } } -export type DependencyId = { id: string }; +export type DependencyId = { id: string }; \ No newline at end of file diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts index 9c5c88c49e54..c876048ba651 100644 --- a/editors/code/src/toolchain.ts +++ b/editors/code/src/toolchain.ts @@ -96,40 +96,6 @@ export class Cargo { return artifacts[0].fileName; } - async crates(): Promise { - 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, @@ -161,29 +127,6 @@ export class Cargo { } } -export async function activeToolchain(): Promise { - 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 { const rustcPath = await getPathForExecutable("rustc"); @@ -202,16 +145,6 @@ export async function getRustcId(dir: string): Promise { return rx.exec(data)![1]; } -export async function getRustcVersion(dir: string): Promise { - 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 { return getPathForExecutable("cargo"); @@ -278,4 +211,4 @@ async function isFileAtUri(uri: vscode.Uri): Promise { } catch { return false; } -} +} \ No newline at end of file From 072f69e4c16bb49f9fd94989029d3e5086e8ac22 Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Wed, 26 Apr 2023 22:02:38 -0300 Subject: [PATCH 27/31] fixing ts linting and rust test --- crates/project-model/src/workspace.rs | 29 ++++++++++++----------- editors/code/src/ctx.ts | 18 +++++++------- editors/code/src/dependencies_provider.ts | 9 +++---- editors/code/src/toolchain.ts | 2 +- 4 files changed, 30 insertions(+), 28 deletions(-) diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 102f862522e6..5924287a37f9 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -1317,24 +1317,25 @@ fn sysroot_to_crate_graph( .filter_map(|krate| { let file_id = load(&sysroot[krate].root)?; - let env = Env::default(); - let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone()); - let crate_id = crate_graph.add_crate_root( + let env = Env::default(); + let display_name = + CrateDisplayName::from_canonical_name(sysroot[krate].name.clone()); + let crate_id = crate_graph.add_crate_root( file_id, Edition::CURRENT, Some(display_name), None, - cfg_options.clone(), - None, - env, - false, - CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)), - target_layout.clone(), - channel, - ); - Some((krate, crate_id)) - }) - .collect(), + cfg_options.clone(), + None, + env, + false, + CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)), + target_layout.clone(), + channel, + ); + Some((krate, crate_id)) + }) + .collect(), }; for from in sysroot.crates() { for &to in sysroot[from].deps.iter() { diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 60ea76957955..8bed74b88eaa 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -33,12 +33,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( @@ -52,9 +52,9 @@ export function fetchWorkspace(): Workspace { ? rustDocuments.length === 0 ? { kind: "Empty" } : { - kind: "Detached Files", - files: rustDocuments, - } + kind: "Detached Files", + files: rustDocuments, + } : { kind: "Workspace Folder" }; } @@ -476,4 +476,4 @@ export interface Disposable { dispose(): void; } -export type Cmd = (...args: any[]) => unknown; \ No newline at end of file +export type Cmd = (...args: any[]) => unknown; diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index 0faf7665cfda..59b3b27731c3 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -1,12 +1,13 @@ import * as vscode from "vscode"; import * as fspath from "path"; import * as fs from "fs"; -import {CtxInit} from "./ctx"; +import { CtxInit } from "./ctx"; import * as ra from "./lsp_ext"; -import {FetchDependencyListResult} from "./lsp_ext"; +import { FetchDependencyListResult } from "./lsp_ext"; export class RustDependenciesProvider - implements vscode.TreeDataProvider { + implements vscode.TreeDataProvider +{ dependenciesMap: { [id: string]: Dependency | DependencyFile }; ctx: CtxInit; @@ -131,4 +132,4 @@ export class DependencyFile extends vscode.TreeItem { } } -export type DependencyId = { id: string }; \ No newline at end of file +export type DependencyId = { id: string }; diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts index c876048ba651..917a1d6b0997 100644 --- a/editors/code/src/toolchain.ts +++ b/editors/code/src/toolchain.ts @@ -211,4 +211,4 @@ async function isFileAtUri(uri: vscode.Uri): Promise { } catch { return false; } -} \ No newline at end of file +} From 800b3b6323cb8f738db2c2419c917159efc9f9df Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Wed, 26 Apr 2023 22:56:14 -0300 Subject: [PATCH 28/31] adding doc and simplifying function --- crates/rust-analyzer/src/handlers.rs | 57 ++++++++++++++++------------ 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index ae6dd4dd5613..4518b2a76a97 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -51,41 +51,50 @@ pub(crate) fn publish_diagnostics( pub(crate) fn fetch_dependency_list( state: GlobalStateSnapshot, - _params: FetchDependencyListParams, -) -> Result { + _params: lsp_ext::FetchDependencyListParams, +) -> Result { let crates = state.analysis.fetch_crates()?; - Ok(FetchDependencyListResult { - crates: crates - .into_iter() - .filter_map(|it| { - let root_file_path = state.file_id_to_file_path(it.root_file_id); - crate_path(it.name.as_ref(), root_file_path).map(|crate_path| CrateInfoResult { - name: it.name, - version: it.version, - path: crate_path.to_string(), - }) + let crate_infos = crates + .into_iter() + .filter_map(|it| { + let root_file_path = state.file_id_to_file_path(it.root_file_id); + crate_path(it.name.as_ref(), root_file_path).map(|path| CrateInfoResult { + name: it.name, + version: it.version, + path: path.to_string(), }) - .collect(), - }) + }) + .collect(); + Ok(FetchDependencyListResult { crates: crate_infos }) } -//Thats a best effort to try and find the crate path +/// Searches for the directory of a Rust crate with a given name in the directory tree +/// of the root file of that crate. +/// +/// # Arguments +/// +/// * `crate_name`: The name of the crate to search for. This should be a `Some` value if +/// a crate name has been specified, or `None` if no crate name has been specified. +/// * `root_file_path`: The path to the root file of the crate. +/// +/// # Returns +/// +/// An `Option` value representing the path to the directory of the crate with the given +/// name, if such a crate is found. If no crate with the given name is found, this function +/// returns `None`. fn crate_path(crate_name: Option<&String>, root_file_path: VfsPath) -> Option { crate_name.and_then(|crate_name| { - let mut crate_path = None; let mut root_path = root_file_path; while let Some(path) = root_path.parent() { - match path.name_and_extension() { - Some((name, _)) => { - if name.starts_with(crate_name.as_str()) { - crate_path = Some(path); - break; - } + if let Some((name, _)) = path.name_and_extension() { + if name.starts_with(crate_name.as_str()) { + return Some(path); } - None => break, + } else { + break; } root_path = path; } - crate_path + None }) } \ No newline at end of file From bcb21311eae7f2c13ca012b3b3088b0d3b417c6d Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Thu, 27 Apr 2023 16:13:05 -0300 Subject: [PATCH 29/31] Accepting review suggestions --- crates/rust-analyzer/src/handlers.rs | 39 +++++++++++------------ crates/rust-analyzer/src/lsp_ext.rs | 4 +-- editors/code/src/commands.ts | 3 ++ editors/code/src/dependencies_provider.ts | 21 ++++++++---- editors/code/src/lsp_ext.ts | 1 + 5 files changed, 40 insertions(+), 28 deletions(-) diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 4518b2a76a97..76ad847b133c 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -58,23 +58,20 @@ pub(crate) fn fetch_dependency_list( .into_iter() .filter_map(|it| { let root_file_path = state.file_id_to_file_path(it.root_file_id); - crate_path(it.name.as_ref(), root_file_path).map(|path| CrateInfoResult { + crate_path(root_file_path).and_then(to_url).map(|path| CrateInfoResult { name: it.name, version: it.version, - path: path.to_string(), + path, }) }) .collect(); Ok(FetchDependencyListResult { crates: crate_infos }) } -/// Searches for the directory of a Rust crate with a given name in the directory tree -/// of the root file of that crate. +/// Searches for the directory of a Rust crate given this crate's root file path. /// /// # Arguments /// -/// * `crate_name`: The name of the crate to search for. This should be a `Some` value if -/// a crate name has been specified, or `None` if no crate name has been specified. /// * `root_file_path`: The path to the root file of the crate. /// /// # Returns @@ -82,19 +79,21 @@ pub(crate) fn fetch_dependency_list( /// An `Option` value representing the path to the directory of the crate with the given /// name, if such a crate is found. If no crate with the given name is found, this function /// returns `None`. -fn crate_path(crate_name: Option<&String>, root_file_path: VfsPath) -> Option { - crate_name.and_then(|crate_name| { - let mut root_path = root_file_path; - while let Some(path) = root_path.parent() { - if let Some((name, _)) = path.name_and_extension() { - if name.starts_with(crate_name.as_str()) { - return Some(path); - } - } else { - break; - } - root_path = path; +fn crate_path(root_file_path: VfsPath) -> Option { + let mut current_dir = root_file_path.parent(); + while let Some(path) = current_dir { + let cargo_toml_path = path.join("../Cargo.toml")?; + if fs::metadata(cargo_toml_path.as_path()?).is_ok() { + let crate_path = cargo_toml_path.parent()?; + return Some(crate_path); } - None - }) + current_dir = path.parent(); + } + None +} + +fn to_url(path: VfsPath) -> Option { + let path = path.as_path()?; + let str_path = path.as_os_str().to_str()?; + Url::from_file_path(str_path).ok() } \ No newline at end of file diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 8689fae0798b..69e7d824680f 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -4,11 +4,11 @@ use std::{collections::HashMap, path::PathBuf}; use ide_db::line_index::WideEncoding; use lsp_types::request::Request; -use lsp_types::PositionEncodingKind; use lsp_types::{ notification::Notification, CodeActionKind, DocumentOnTypeFormattingParams, PartialResultParams, Position, Range, TextDocumentIdentifier, WorkDoneProgressParams, }; +use lsp_types::{PositionEncodingKind, Url}; use serde::{Deserialize, Serialize}; use crate::line_index::PositionEncoding; @@ -32,7 +32,7 @@ pub struct AnalyzerStatusParams { pub struct CrateInfoResult { pub name: Option, pub version: Option, - pub path: String, + pub path: Url, } pub enum FetchDependencyList {} diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 81d3b8da7f90..98ccd50dc040 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -276,6 +276,9 @@ export function openCargoToml(ctx: CtxInit): Cmd { export function revealDependency(ctx: CtxInit): Cmd { return async (editor: RustEditor) => { + if (!ctx.dependencies?.isInitialized()) { + return; + } const documentPath = editor.document.uri.fsPath; const dep = ctx.dependencies?.getDependency(documentPath); if (dep) { diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index 59b3b27731c3..74fbacbb3cd7 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -32,6 +32,10 @@ export class RustDependenciesProvider return filePath.toLowerCase() in this.dependenciesMap; } + isInitialized(): boolean { + return Object.keys(this.dependenciesMap).length !== 0; + } + refresh(): void { this.dependenciesMap = {}; this._onDidChangeTreeData.fire(); @@ -89,7 +93,12 @@ export class RustDependenciesProvider } private toDep(moduleName: string, version: string, path: string): Dependency { - return new Dependency(moduleName, version, path, vscode.TreeItemCollapsibleState.Collapsed); + return new Dependency( + moduleName, + version, + vscode.Uri.parse(path).fsPath, + vscode.TreeItemCollapsibleState.Collapsed + ); } } @@ -101,9 +110,9 @@ export class Dependency extends vscode.TreeItem { public readonly collapsibleState: vscode.TreeItemCollapsibleState ) { super(label, collapsibleState); - this.id = this.dependencyPath.toLowerCase(); - this.description = this.version; this.resourceUri = vscode.Uri.file(dependencyPath); + this.id = this.resourceUri.fsPath.toLowerCase(); + this.description = this.version; if (this.version) { this.tooltip = `${this.label}-${this.version}`; } else { @@ -120,13 +129,13 @@ export class DependencyFile extends vscode.TreeItem { public readonly collapsibleState: vscode.TreeItemCollapsibleState ) { super(vscode.Uri.file(dependencyPath), collapsibleState); - this.id = this.dependencyPath.toLowerCase(); - const isDir = fs.lstatSync(this.dependencyPath).isDirectory(); + this.id = this.resourceUri!.fsPath.toLowerCase(); + const isDir = fs.lstatSync(this.resourceUri!.fsPath).isDirectory(); if (!isDir) { this.command = { command: "vscode.open", title: "Open File", - arguments: [vscode.Uri.file(this.dependencyPath)], + arguments: [this.resourceUri], }; } } diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index b72804e510ce..0d75392c92b1 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -2,6 +2,7 @@ * This file mirrors `crates/rust-analyzer/src/lsp_ext.rs` declarations. */ +import { Uri } from "vscode"; import * as lc from "vscode-languageclient"; // rust-analyzer overrides From 0aed507f1632703a0d3e123fd250fd2eba3ef446 Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Thu, 27 Apr 2023 16:15:54 -0300 Subject: [PATCH 30/31] fixing TS linting, removing import --- editors/code/src/lsp_ext.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 0d75392c92b1..b72804e510ce 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -2,7 +2,6 @@ * This file mirrors `crates/rust-analyzer/src/lsp_ext.rs` declarations. */ -import { Uri } from "vscode"; import * as lc from "vscode-languageclient"; // rust-analyzer overrides From ecfe7c04888a9c2567773370b127d2e6e1cdaa22 Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Tue, 2 May 2023 11:24:08 -0300 Subject: [PATCH 31/31] last fixes after rebase --- crates/rust-analyzer/src/handlers.rs | 64 ++------------------ crates/rust-analyzer/src/handlers/request.rs | 57 ++++++++++++++++- docs/dev/lsp-extensions.md | 2 +- 3 files changed, 61 insertions(+), 62 deletions(-) diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 76ad847b133c..c19be1965444 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -3,16 +3,11 @@ //! `ide` crate. use ide::AssistResolveStrategy; -use lsp_types::{Diagnostic, DiagnosticTag, NumberOrString}; -use vfs::FileId; +use lsp_types::{Diagnostic, DiagnosticTag, NumberOrString, Url}; -use crate::{ - global_state::GlobalStateSnapshot, to_proto, Result, - lsp_ext::{ - CrateInfoResult, FetchDependencyListResult, FetchDependencyListParams, - }, -}; +use vfs::FileId; +use crate::{global_state::GlobalStateSnapshot, to_proto, Result}; pub(crate) mod request; pub(crate) mod notification; @@ -33,11 +28,11 @@ pub(crate) fn publish_diagnostics( severity: Some(to_proto::diagnostic_severity(d.severity)), code: Some(NumberOrString::String(d.code.as_str().to_string())), code_description: Some(lsp_types::CodeDescription { - href: lsp_types::Url::parse(&format!( + href: Url::parse(&format!( "https://rust-analyzer.github.io/manual.html#{}", d.code.as_str() )) - .unwrap(), + .unwrap(), }), source: Some("rust-analyzer".to_string()), message: d.message, @@ -48,52 +43,3 @@ pub(crate) fn publish_diagnostics( .collect(); Ok(diagnostics) } - -pub(crate) fn fetch_dependency_list( - state: GlobalStateSnapshot, - _params: lsp_ext::FetchDependencyListParams, -) -> Result { - let crates = state.analysis.fetch_crates()?; - let crate_infos = crates - .into_iter() - .filter_map(|it| { - let root_file_path = state.file_id_to_file_path(it.root_file_id); - crate_path(root_file_path).and_then(to_url).map(|path| CrateInfoResult { - name: it.name, - version: it.version, - path, - }) - }) - .collect(); - Ok(FetchDependencyListResult { crates: crate_infos }) -} - -/// Searches for the directory of a Rust crate given this crate's root file path. -/// -/// # Arguments -/// -/// * `root_file_path`: The path to the root file of the crate. -/// -/// # Returns -/// -/// An `Option` value representing the path to the directory of the crate with the given -/// name, if such a crate is found. If no crate with the given name is found, this function -/// returns `None`. -fn crate_path(root_file_path: VfsPath) -> Option { - let mut current_dir = root_file_path.parent(); - while let Some(path) = current_dir { - let cargo_toml_path = path.join("../Cargo.toml")?; - if fs::metadata(cargo_toml_path.as_path()?).is_ok() { - let crate_path = cargo_toml_path.parent()?; - return Some(crate_path); - } - current_dir = path.parent(); - } - None -} - -fn to_url(path: VfsPath) -> Option { - let path = path.as_path()?; - let str_path = path.as_os_str().to_str()?; - Url::from_file_path(str_path).ok() -} \ No newline at end of file diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 03e08d9cdfc0..f25dc74a142b 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -2,6 +2,7 @@ //! Protocol. This module specifically handles requests. use std::{ + fs, io::Write as _, process::{self, Stdio}, sync::Arc, @@ -29,7 +30,7 @@ use project_model::{ManifestPath, ProjectWorkspace, TargetKind}; use serde_json::json; use stdx::{format_to, never}; use syntax::{algo, ast, AstNode, TextRange, TextSize}; -use vfs::{AbsPath, AbsPathBuf}; +use vfs::{AbsPath, AbsPathBuf, VfsPath}; use crate::{ cargo_target_spec::CargoTargetSpec, @@ -38,7 +39,10 @@ use crate::{ from_proto, global_state::{GlobalState, GlobalStateSnapshot}, line_index::LineEndings, - lsp_ext::{self, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams}, + lsp_ext::{ + self, CrateInfoResult, FetchDependencyListParams, FetchDependencyListResult, + PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams, + }, lsp_utils::{all_edits_are_disjoint, invalid_params_error}, to_proto, LspError, Result, }; @@ -1881,3 +1885,52 @@ fn run_rustfmt( Ok(Some(to_proto::text_edit_vec(&line_index, diff(&file, &new_text)))) } } + +pub(crate) fn fetch_dependency_list( + state: GlobalStateSnapshot, + _params: FetchDependencyListParams, +) -> Result { + let crates = state.analysis.fetch_crates()?; + let crate_infos = crates + .into_iter() + .filter_map(|it| { + let root_file_path = state.file_id_to_file_path(it.root_file_id); + crate_path(root_file_path).and_then(to_url).map(|path| CrateInfoResult { + name: it.name, + version: it.version, + path, + }) + }) + .collect(); + Ok(FetchDependencyListResult { crates: crate_infos }) +} + +/// Searches for the directory of a Rust crate given this crate's root file path. +/// +/// # Arguments +/// +/// * `root_file_path`: The path to the root file of the crate. +/// +/// # Returns +/// +/// An `Option` value representing the path to the directory of the crate with the given +/// name, if such a crate is found. If no crate with the given name is found, this function +/// returns `None`. +fn crate_path(root_file_path: VfsPath) -> Option { + let mut current_dir = root_file_path.parent(); + while let Some(path) = current_dir { + let cargo_toml_path = path.join("../Cargo.toml")?; + if fs::metadata(cargo_toml_path.as_path()?).is_ok() { + let crate_path = cargo_toml_path.parent()?; + return Some(crate_path); + } + current_dir = path.parent(); + } + None +} + +fn to_url(path: VfsPath) -> Option { + let path = path.as_path()?; + let str_path = path.as_os_str().to_str()?; + Url::from_file_path(str_path).ok() +} diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 0fb92638e0d2..a4ad3e5a553c 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@