aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/view_crate_graph.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/view_crate_graph.rs')
-rw-r--r--crates/ide/src/view_crate_graph.rs81
1 files changed, 81 insertions, 0 deletions
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}