Merge #9828
9828: Remove dependency on the system graphviz when rendering crate graph r=lnicola a=p32blo This PR removes the need for having `graphviz` installed on the user system by using the `d3-graphviz` npm package. The responsibility of rendering the svg output is moved to the extension while the rust side only handles the generation of the dot file. This change also brings the following additional features: - Allow zooming the view - Ctrl+click to reset the zoom - Adjust the color scheme to dark themes - Works on any platform without installing graphviz locally --- I’m not sure if this fits what you had in mind for the crates graph feature but I decided to submit it anyway to see if this is useful to anyone else. A potential downside might be that it increases the extension size ( haven’t checked) but this feature already required the installation of graphviz on the user side, so the cost is just moved explicitly to the extension. Feel free to make any suggestion or comments. Co-authored-by: André Oliveira <p32blo@gmail.com>
This commit is contained in:
commit
6c80c42c57
@ -3,8 +3,8 @@
|
||||
//! `ide` crate.
|
||||
|
||||
use std::{
|
||||
io::{Read, Write as _},
|
||||
process::{self, Command, Stdio},
|
||||
io::Write as _,
|
||||
process::{self, Stdio},
|
||||
};
|
||||
|
||||
use ide::{
|
||||
@ -132,19 +132,7 @@ pub(crate) fn handle_view_crate_graph(
|
||||
) -> Result<String> {
|
||||
let _p = profile::span("handle_view_crate_graph");
|
||||
let dot = snap.analysis.view_crate_graph(params.full)??;
|
||||
|
||||
// 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`: {}", err))?;
|
||||
child.stdin.unwrap().write_all(dot.as_bytes())?;
|
||||
|
||||
let mut svg = String::new();
|
||||
child.stdout.unwrap().read_to_string(&mut svg)?;
|
||||
Ok(svg)
|
||||
Ok(dot)
|
||||
}
|
||||
|
||||
pub(crate) fn handle_expand_macro(
|
||||
|
@ -3,6 +3,10 @@
|
||||
!out
|
||||
out/**
|
||||
!out/src
|
||||
!node_modules/d3/dist/d3.min.js
|
||||
!node_modules/@hpcc-js/wasm/dist/index.min.js
|
||||
!node_modules/@hpcc-js/wasm/dist/graphvizlib.wasm
|
||||
!node_modules/d3-graphviz/build/d3-graphviz.min.js
|
||||
!package.json
|
||||
!package-lock.json
|
||||
!ra_syntax_tree.tmGrammar.json
|
||||
|
960
editors/code/package-lock.json
generated
960
editors/code/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -38,7 +38,9 @@
|
||||
"dependencies": {
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"vscode-languageclient": "^7.1.0-next.5"
|
||||
"vscode-languageclient": "^7.1.0-next.5",
|
||||
"d3": "^7.0.0",
|
||||
"d3-graphviz": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/glob": "^7.1.4",
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as lc from 'vscode-languageclient';
|
||||
import * as ra from './lsp_ext';
|
||||
import * as path from 'path';
|
||||
|
||||
import { Ctx, Cmd } from './ctx';
|
||||
import { applySnippetWorkspaceEdit, applySnippetTextEdits } from './snippets';
|
||||
@ -472,12 +473,59 @@ export function viewItemTree(ctx: Ctx): Cmd {
|
||||
|
||||
function crateGraph(ctx: Ctx, full: boolean): Cmd {
|
||||
return async () => {
|
||||
const panel = vscode.window.createWebviewPanel("rust-analyzer.crate-graph", "rust-analyzer crate graph", vscode.ViewColumn.Two);
|
||||
const nodeModulesPath = vscode.Uri.file(path.join(ctx.extensionPath, "node_modules"));
|
||||
|
||||
const panel = vscode.window.createWebviewPanel("rust-analyzer.crate-graph", "rust-analyzer crate graph", vscode.ViewColumn.Two, {
|
||||
enableScripts: true,
|
||||
retainContextWhenHidden: true,
|
||||
localResourceRoots: [nodeModulesPath]
|
||||
});
|
||||
const params = {
|
||||
full: full,
|
||||
};
|
||||
const svg = await ctx.client.sendRequest(ra.viewCrateGraph, params);
|
||||
panel.webview.html = svg;
|
||||
|
||||
const dot = await ctx.client.sendRequest(ra.viewCrateGraph, params);
|
||||
const uri = panel.webview.asWebviewUri(nodeModulesPath);
|
||||
|
||||
const html = `
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<head>
|
||||
<style>
|
||||
/* Fill the entire view */
|
||||
html, body { margin:0; padding:0; overflow:hidden }
|
||||
svg { position:fixed; top:0; left:0; height:100%; width:100% }
|
||||
|
||||
/* Disable the graphviz backgroud and fill the polygons */
|
||||
.graph > polygon { display:none; }
|
||||
:is(.node,.edge) polygon { fill: white; }
|
||||
|
||||
/* Invert the line colours for dark themes */
|
||||
body:not(.vscode-light) .edge path { stroke: white; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript" src="${uri}/d3/dist/d3.min.js"></script>
|
||||
<script type="javascript/worker" src="${uri}/@hpcc-js/wasm/dist/index.min.js"></script>
|
||||
<script type="text/javascript" src="${uri}/d3-graphviz/build/d3-graphviz.min.js"></script>
|
||||
<div id="graph"></div>
|
||||
<script>
|
||||
let graph = d3.select("#graph")
|
||||
.graphviz()
|
||||
.fit(true)
|
||||
.zoomScaleExtent([0.1, Infinity])
|
||||
.renderDot(\`${dot}\`);
|
||||
|
||||
d3.select(window).on("click", (event) => {
|
||||
if (event.ctrlKey) {
|
||||
graph.resetZoom(d3.transition().duration(100));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
`;
|
||||
|
||||
panel.webview.html = html;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,10 @@ export class Ctx {
|
||||
this.pushCleanup(d);
|
||||
}
|
||||
|
||||
get extensionPath(): string {
|
||||
return this.extCtx.extensionPath;
|
||||
}
|
||||
|
||||
get globalState(): vscode.Memento {
|
||||
return this.extCtx.globalState;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user