Allow viewing the crate graph in a webview

This commit is contained in:
Jonas Schievink 2021-05-11 16:15:31 +02:00
parent 9fa9d166d8
commit a85a2c4d15
11 changed files with 125 additions and 0 deletions

7
Cargo.lock generated
View file

@ -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",

View file

@ -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" }

View file

@ -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))
}

View 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()
}
}

View file

@ -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,

View file

@ -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 {

View file

@ -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)

View file

@ -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",

View file

@ -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`

View file

@ -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;

View file

@ -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);