aboutsummaryrefslogtreecommitdiff
path: root/crates/ide
diff options
context:
space:
mode:
authorJonas Schievink <[email protected]>2021-05-11 15:15:31 +0100
committerJonas Schievink <[email protected]>2021-05-11 15:15:31 +0100
commita85a2c4d151d9d2e8fb016d76aad99a6ca88bc75 (patch)
tree636c0727298fd9d60a7b2a639c49dca8ba73d813 /crates/ide
parent9fa9d166d8141bb9ca4fcf0544c49b903fb85e09 (diff)
Allow viewing the crate graph in a webview
Diffstat (limited to 'crates/ide')
-rw-r--r--crates/ide/Cargo.toml1
-rw-r--r--crates/ide/src/lib.rs5
-rw-r--r--crates/ide/src/view_crate_graph.rs81
3 files changed, 87 insertions, 0 deletions
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index f04bcf531..88f3d09d3 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -20,6 +20,7 @@ oorandom = "11.1.2"
20pulldown-cmark-to-cmark = "6.0.0" 20pulldown-cmark-to-cmark = "6.0.0"
21pulldown-cmark = { version = "0.8.0", default-features = false } 21pulldown-cmark = { version = "0.8.0", default-features = false }
22url = "2.1.1" 22url = "2.1.1"
23dot = "0.1.4"
23 24
24stdx = { path = "../stdx", version = "0.0.0" } 25stdx = { path = "../stdx", version = "0.0.0" }
25syntax = { path = "../syntax", version = "0.0.0" } 26syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 8e5b72044..34360501a 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -49,6 +49,7 @@ mod syntax_tree;
49mod typing; 49mod typing;
50mod markdown_remove; 50mod markdown_remove;
51mod doc_links; 51mod doc_links;
52mod view_crate_graph;
52 53
53use std::sync::Arc; 54use std::sync::Arc;
54 55
@@ -287,6 +288,10 @@ impl Analysis {
287 self.with_db(|db| view_hir::view_hir(&db, position)) 288 self.with_db(|db| view_hir::view_hir(&db, position))
288 } 289 }
289 290
291 pub fn view_crate_graph(&self) -> Cancelable<Result<String, String>> {
292 self.with_db(|db| view_crate_graph::view_crate_graph(&db))
293 }
294
290 pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<ExpandedMacro>> { 295 pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<ExpandedMacro>> {
291 self.with_db(|db| expand_macro::expand_macro(db, position)) 296 self.with_db(|db| expand_macro::expand_macro(db, position))
292 } 297 }
diff --git a/crates/ide/src/view_crate_graph.rs b/crates/ide/src/view_crate_graph.rs
new file mode 100644
index 000000000..4da4ce2b3
--- /dev/null
+++ b/crates/ide/src/view_crate_graph.rs
@@ -0,0 +1,81 @@
1use std::{
2 error::Error,
3 io::{Read, Write},
4 process::{Command, Stdio},
5 sync::Arc,
6};
7
8use dot::Id;
9use ide_db::{
10 base_db::{CrateGraph, CrateId, Dependency, SourceDatabase},
11 RootDatabase,
12};
13
14// Feature: View Crate Graph
15//
16// Renders the currently loaded crate graph as an SVG graphic. Requires the `dot` tool to be
17// installed.
18//
19// |===
20// | Editor | Action Name
21//
22// | VS Code | **Rust Analyzer: View Crate Graph**
23// |===
24pub(crate) fn view_crate_graph(db: &RootDatabase) -> Result<String, String> {
25 let mut dot = Vec::new();
26 let graph = DotCrateGraph(db.crate_graph());
27 dot::render(&graph, &mut dot).unwrap();
28
29 render_svg(&dot).map_err(|e| e.to_string())
30}
31
32fn render_svg(dot: &[u8]) -> Result<String, Box<dyn Error>> {
33 // We shell out to `dot` to render to SVG, as there does not seem to be a pure-Rust renderer.
34 let child = Command::new("dot")
35 .arg("-Tsvg")
36 .stdin(Stdio::piped())
37 .stdout(Stdio::piped())
38 .spawn()
39 .map_err(|err| format!("failed to spawn `dot -Tsvg`: {}", err))?;
40 child.stdin.unwrap().write_all(&dot)?;
41
42 let mut svg = String::new();
43 child.stdout.unwrap().read_to_string(&mut svg)?;
44 Ok(svg)
45}
46
47struct DotCrateGraph(Arc<CrateGraph>);
48
49type Edge<'a> = (CrateId, &'a Dependency);
50
51impl<'a> dot::GraphWalk<'a, CrateId, Edge<'a>> for DotCrateGraph {
52 fn nodes(&'a self) -> dot::Nodes<'a, CrateId> {
53 self.0.iter().collect()
54 }
55
56 fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> {
57 self.0
58 .iter()
59 .flat_map(|krate| self.0[krate].dependencies.iter().map(move |dep| (krate, dep)))
60 .collect()
61 }
62
63 fn source(&'a self, edge: &Edge<'a>) -> CrateId {
64 edge.0
65 }
66
67 fn target(&'a self, edge: &Edge<'a>) -> CrateId {
68 edge.1.crate_id
69 }
70}
71
72impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph {
73 fn graph_id(&'a self) -> Id<'a> {
74 Id::new("rust_analyzer_crate_graph").unwrap()
75 }
76
77 fn node_id(&'a self, n: &CrateId) -> Id<'a> {
78 let name = self.0[*n].display_name.as_ref().map_or("_missing_name_", |name| &*name);
79 Id::new(name).unwrap()
80 }
81}