mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 04:23:25 +00:00
Allow viewing the crate graph in a webview
This commit is contained in:
parent
9fa9d166d8
commit
a85a2c4d15
11 changed files with 125 additions and 0 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -319,6 +319,12 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc4b29f4b9bb94bf267d57269fd0706d343a160937108e9619fe380645428abb"
|
||||
|
||||
[[package]]
|
||||
name = "dot"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a74b6c4d4a1cff5f454164363c16b72fa12463ca6b31f4b5f2035a65fa3d5906"
|
||||
|
||||
[[package]]
|
||||
name = "drop_bomb"
|
||||
version = "0.1.5"
|
||||
|
@ -588,6 +594,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"cfg",
|
||||
"cov-mark",
|
||||
"dot",
|
||||
"either",
|
||||
"expect-test",
|
||||
"hir",
|
||||
|
|
|
@ -20,6 +20,7 @@ oorandom = "11.1.2"
|
|||
pulldown-cmark-to-cmark = "6.0.0"
|
||||
pulldown-cmark = { version = "0.8.0", default-features = false }
|
||||
url = "2.1.1"
|
||||
dot = "0.1.4"
|
||||
|
||||
stdx = { path = "../stdx", version = "0.0.0" }
|
||||
syntax = { path = "../syntax", version = "0.0.0" }
|
||||
|
|
|
@ -49,6 +49,7 @@ mod syntax_tree;
|
|||
mod typing;
|
||||
mod markdown_remove;
|
||||
mod doc_links;
|
||||
mod view_crate_graph;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -287,6 +288,10 @@ impl Analysis {
|
|||
self.with_db(|db| view_hir::view_hir(&db, position))
|
||||
}
|
||||
|
||||
pub fn view_crate_graph(&self) -> Cancelable<Result<String, String>> {
|
||||
self.with_db(|db| view_crate_graph::view_crate_graph(&db))
|
||||
}
|
||||
|
||||
pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<ExpandedMacro>> {
|
||||
self.with_db(|db| expand_macro::expand_macro(db, position))
|
||||
}
|
||||
|
|
81
crates/ide/src/view_crate_graph.rs
Normal file
81
crates/ide/src/view_crate_graph.rs
Normal file
|
@ -0,0 +1,81 @@
|
|||
use std::{
|
||||
error::Error,
|
||||
io::{Read, Write},
|
||||
process::{Command, Stdio},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use dot::Id;
|
||||
use ide_db::{
|
||||
base_db::{CrateGraph, CrateId, Dependency, SourceDatabase},
|
||||
RootDatabase,
|
||||
};
|
||||
|
||||
// Feature: View Crate Graph
|
||||
//
|
||||
// Renders the currently loaded crate graph as an SVG graphic. Requires the `dot` tool to be
|
||||
// installed.
|
||||
//
|
||||
// |===
|
||||
// | Editor | Action Name
|
||||
//
|
||||
// | VS Code | **Rust Analyzer: View Crate Graph**
|
||||
// |===
|
||||
pub(crate) fn view_crate_graph(db: &RootDatabase) -> Result<String, String> {
|
||||
let mut dot = Vec::new();
|
||||
let graph = DotCrateGraph(db.crate_graph());
|
||||
dot::render(&graph, &mut dot).unwrap();
|
||||
|
||||
render_svg(&dot).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
fn render_svg(dot: &[u8]) -> Result<String, Box<dyn Error>> {
|
||||
// We shell out to `dot` to render to SVG, as there does not seem to be a pure-Rust renderer.
|
||||
let child = Command::new("dot")
|
||||
.arg("-Tsvg")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.map_err(|err| format!("failed to spawn `dot -Tsvg`: {}", err))?;
|
||||
child.stdin.unwrap().write_all(&dot)?;
|
||||
|
||||
let mut svg = String::new();
|
||||
child.stdout.unwrap().read_to_string(&mut svg)?;
|
||||
Ok(svg)
|
||||
}
|
||||
|
||||
struct DotCrateGraph(Arc<CrateGraph>);
|
||||
|
||||
type Edge<'a> = (CrateId, &'a Dependency);
|
||||
|
||||
impl<'a> dot::GraphWalk<'a, CrateId, Edge<'a>> for DotCrateGraph {
|
||||
fn nodes(&'a self) -> dot::Nodes<'a, CrateId> {
|
||||
self.0.iter().collect()
|
||||
}
|
||||
|
||||
fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> {
|
||||
self.0
|
||||
.iter()
|
||||
.flat_map(|krate| self.0[krate].dependencies.iter().map(move |dep| (krate, dep)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn source(&'a self, edge: &Edge<'a>) -> CrateId {
|
||||
edge.0
|
||||
}
|
||||
|
||||
fn target(&'a self, edge: &Edge<'a>) -> CrateId {
|
||||
edge.1.crate_id
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph {
|
||||
fn graph_id(&'a self) -> Id<'a> {
|
||||
Id::new("rust_analyzer_crate_graph").unwrap()
|
||||
}
|
||||
|
||||
fn node_id(&'a self, n: &CrateId) -> Id<'a> {
|
||||
let name = self.0[*n].display_name.as_ref().map_or("_missing_name_", |name| &*name);
|
||||
Id::new(name).unwrap()
|
||||
}
|
||||
}
|
|
@ -117,6 +117,12 @@ pub(crate) fn handle_view_hir(
|
|||
Ok(res)
|
||||
}
|
||||
|
||||
pub(crate) fn handle_view_crate_graph(snap: GlobalStateSnapshot, (): ()) -> Result<String> {
|
||||
let _p = profile::span("handle_view_crate_graph");
|
||||
let res = snap.analysis.view_crate_graph()??;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub(crate) fn handle_expand_macro(
|
||||
snap: GlobalStateSnapshot,
|
||||
params: lsp_ext::ExpandMacroParams,
|
||||
|
|
|
@ -61,6 +61,14 @@ impl Request for ViewHir {
|
|||
const METHOD: &'static str = "rust-analyzer/viewHir";
|
||||
}
|
||||
|
||||
pub enum ViewCrateGraph {}
|
||||
|
||||
impl Request for ViewCrateGraph {
|
||||
type Params = ();
|
||||
type Result = String;
|
||||
const METHOD: &'static str = "rust-analyzer/viewCrateGraph";
|
||||
}
|
||||
|
||||
pub enum ExpandMacro {}
|
||||
|
||||
impl Request for ExpandMacro {
|
||||
|
|
|
@ -513,6 +513,7 @@ impl GlobalState {
|
|||
.on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)
|
||||
.on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
|
||||
.on::<lsp_ext::ViewHir>(handlers::handle_view_hir)
|
||||
.on::<lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph)
|
||||
.on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro)
|
||||
.on::<lsp_ext::ParentModule>(handlers::handle_parent_module)
|
||||
.on::<lsp_ext::Runnables>(handlers::handle_runnables)
|
||||
|
|
|
@ -109,6 +109,11 @@
|
|||
"title": "View Hir",
|
||||
"category": "Rust Analyzer"
|
||||
},
|
||||
{
|
||||
"command": "rust-analyzer.viewCrateGraph",
|
||||
"title": "View Crate Graph",
|
||||
"category": "Rust Analyzer"
|
||||
},
|
||||
{
|
||||
"command": "rust-analyzer.expandMacro",
|
||||
"title": "Expand macro recursively",
|
||||
|
|
|
@ -429,6 +429,14 @@ export function viewHir(ctx: Ctx): Cmd {
|
|||
};
|
||||
}
|
||||
|
||||
export function viewCrateGraph(ctx: Ctx): Cmd {
|
||||
return async () => {
|
||||
const panel = vscode.window.createWebviewPanel("rust-analyzer.crate-graph", "rust-analyzer crate graph", vscode.ViewColumn.Two);
|
||||
const svg = await ctx.client.sendRequest(ra.viewCrateGraph);
|
||||
panel.webview.html = svg;
|
||||
};
|
||||
}
|
||||
|
||||
// Opens the virtual file that will show the syntax tree
|
||||
//
|
||||
// The contents of the file come from the `TextDocumentContentProvider`
|
||||
|
|
|
@ -27,6 +27,8 @@ export const syntaxTree = new lc.RequestType<SyntaxTreeParams, string, void>("ru
|
|||
|
||||
export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>("rust-analyzer/viewHir");
|
||||
|
||||
export const viewCrateGraph = new lc.RequestType0<string, void>("rust-analyzer/viewCrateGraph");
|
||||
|
||||
export interface ExpandMacroParams {
|
||||
textDocument: lc.TextDocumentIdentifier;
|
||||
position: lc.Position;
|
||||
|
|
|
@ -106,6 +106,7 @@ async function tryActivate(context: vscode.ExtensionContext) {
|
|||
ctx.registerCommand('parentModule', commands.parentModule);
|
||||
ctx.registerCommand('syntaxTree', commands.syntaxTree);
|
||||
ctx.registerCommand('viewHir', commands.viewHir);
|
||||
ctx.registerCommand('viewCrateGraph', commands.viewCrateGraph);
|
||||
ctx.registerCommand('expandMacro', commands.expandMacro);
|
||||
ctx.registerCommand('run', commands.run);
|
||||
ctx.registerCommand('copyRunCommandLine', commands.copyRunCommandLine);
|
||||
|
|
Loading…
Reference in a new issue