Skip to content

Commit 9de0462

Browse files
Merge pull request #262 from github/visualize-command
Add `visualize` command
2 parents 2f513a8 + 230c1ae commit 9de0462

File tree

8 files changed

+155
-23
lines changed

8 files changed

+155
-23
lines changed

stack-graphs/src/storage.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -473,12 +473,18 @@ impl SQLiteReader {
473473
/// Returns a [`Files`][] value that can be used to iterate over all descendants of a
474474
/// file or directory in the database.
475475
pub fn list_file_or_directory<'a>(
476-
&'a mut self,
476+
&'a self,
477+
file_or_directory: &Path,
478+
) -> Result<Files<'a, [String; 1]>> {
479+
Self::list_file_or_directory_inner(&self.conn, file_or_directory)
480+
}
481+
482+
fn list_file_or_directory_inner<'a>(
483+
conn: &'a Connection,
477484
file_or_directory: &Path,
478485
) -> Result<Files<'a, [String; 1]>> {
479486
let file_or_directory = file_or_directory.to_string_lossy().to_string();
480-
self.conn
481-
.prepare("SELECT file, tag, error FROM graphs WHERE path_descendant_of(file, ?)")
487+
conn.prepare("SELECT file, tag, error FROM graphs WHERE path_descendant_of(file, ?)")
482488
.map(|stmt| Files(stmt, [file_or_directory]))
483489
.map_err(|e| e.into())
484490
}
@@ -507,6 +513,24 @@ impl SQLiteReader {
507513
Ok(())
508514
}
509515

516+
pub fn load_graph_for_file_or_directory(
517+
&mut self,
518+
file_or_directory: &Path,
519+
cancellation_flag: &dyn CancellationFlag,
520+
) -> Result<()> {
521+
for file in Self::list_file_or_directory_inner(&self.conn, file_or_directory)?.try_iter()? {
522+
cancellation_flag.check("loading graphs")?;
523+
let file = file?;
524+
Self::load_graph_for_file_inner(
525+
&file.path.to_string_lossy(),
526+
&mut self.graph,
527+
&mut self.loaded_graphs,
528+
&self.conn,
529+
)?;
530+
}
531+
Ok(())
532+
}
533+
510534
/// Ensure the paths starting a the given node are loaded.
511535
fn load_paths_for_node(
512536
&mut self,

stack-graphs/src/visualization.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ use crate::serde::Filter;
1717
use crate::stitching::Database;
1818

1919
static CSS: &'static str = include_str!("visualization/visualization.css");
20-
static D3: &'static str = include_str!("visualization/d3.v7.min.js");
21-
static D3_DAG: &'static str = include_str!("visualization/d3-dag.v0.10.0.min.js");
20+
static D3: &'static str = include_str!("visualization/d3.min.js");
21+
static D3_DAG: &'static str = include_str!("visualization/d3-dag.min.js");
2222
static JS: &'static str = include_str!("visualization/visualization.js");
2323

2424
static PKG: &'static str = env!("CARGO_PKG_NAME");

stack-graphs/src/visualization/d3-dag.min.js

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

stack-graphs/src/visualization/d3-dag.v0.10.0.min.js

Lines changed: 0 additions & 16 deletions
This file was deleted.

stack-graphs/src/visualization/d3.min.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

stack-graphs/src/visualization/d3.v7.min.js

Lines changed: 0 additions & 2 deletions
This file was deleted.

tree-sitter-stack-graphs/src/cli.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pub mod query;
7070
pub mod status;
7171
pub mod test;
7272
mod util;
73+
pub mod visualize;
7374

7475
pub mod path_loading {
7576
use std::path::PathBuf;
@@ -87,6 +88,7 @@ pub mod path_loading {
8788
use crate::cli::r#match::MatchArgs;
8889
use crate::cli::status::StatusArgs;
8990
use crate::cli::test::TestArgs;
91+
use crate::cli::visualize::VisualizeArgs;
9092

9193
use super::database::DatabaseArgs;
9294

@@ -102,6 +104,7 @@ pub mod path_loading {
102104
Query(Query),
103105
Status(Status),
104106
Test(Test),
107+
Visualize(Visualize),
105108
}
106109

107110
impl Subcommands {
@@ -117,6 +120,7 @@ pub mod path_loading {
117120
Self::Query(cmd) => cmd.run(default_db_path),
118121
Self::Status(cmd) => cmd.run(default_db_path),
119122
Self::Test(cmd) => cmd.run(),
123+
Self::Visualize(cmd) => cmd.run(default_db_path),
120124
}
121125
}
122126
}
@@ -269,6 +273,22 @@ pub mod path_loading {
269273
self.test_args.run(loader)
270274
}
271275
}
276+
277+
/// Visualize command
278+
#[derive(clap::Parser)]
279+
pub struct Visualize {
280+
#[clap(flatten)]
281+
db_args: DatabaseArgs,
282+
#[clap(flatten)]
283+
visualize_args: VisualizeArgs,
284+
}
285+
286+
impl Visualize {
287+
pub fn run(self, default_db_path: PathBuf) -> anyhow::Result<()> {
288+
let db_path = self.db_args.get_or(default_db_path);
289+
self.visualize_args.run(&db_path)
290+
}
291+
}
272292
}
273293

274294
pub mod provided_languages {
@@ -287,6 +307,7 @@ pub mod provided_languages {
287307
use crate::cli::r#match::MatchArgs;
288308
use crate::cli::status::StatusArgs;
289309
use crate::cli::test::TestArgs;
310+
use crate::cli::visualize::VisualizeArgs;
290311
use crate::loader::LanguageConfiguration;
291312

292313
use super::database::DatabaseArgs;
@@ -303,6 +324,7 @@ pub mod provided_languages {
303324
Query(Query),
304325
Status(Status),
305326
Test(Test),
327+
Visualize(Visualize),
306328
}
307329

308330
impl Subcommands {
@@ -322,6 +344,7 @@ pub mod provided_languages {
322344
Self::Query(cmd) => cmd.run(default_db_path),
323345
Self::Status(cmd) => cmd.run(default_db_path),
324346
Self::Test(cmd) => cmd.run(configurations),
347+
Self::Visualize(cmd) => cmd.run(default_db_path),
325348
}
326349
}
327350
}
@@ -482,4 +505,20 @@ pub mod provided_languages {
482505
self.test_args.run(loader)
483506
}
484507
}
508+
509+
/// Visualize command
510+
#[derive(clap::Parser)]
511+
pub struct Visualize {
512+
#[clap(flatten)]
513+
db_args: DatabaseArgs,
514+
#[clap(flatten)]
515+
visualize_args: VisualizeArgs,
516+
}
517+
518+
impl Visualize {
519+
pub fn run(self, default_db_path: PathBuf) -> anyhow::Result<()> {
520+
let db_path = self.db_args.get_or(default_db_path);
521+
self.visualize_args.run(&db_path)
522+
}
523+
}
485524
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// -*- coding: utf-8 -*-
2+
// ------------------------------------------------------------------------------------------------
3+
// Copyright © 2023, stack-graphs authors.
4+
// Licensed under either of Apache License, Version 2.0, or MIT license, at your option.
5+
// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details.
6+
// ------------------------------------------------------------------------------------------------
7+
8+
use clap::Args;
9+
use clap::ValueHint;
10+
use stack_graphs::serde::NoFilter;
11+
use stack_graphs::stitching::Database;
12+
use stack_graphs::storage::SQLiteReader;
13+
use stack_graphs::NoCancellation;
14+
use std::path::Path;
15+
use std::path::PathBuf;
16+
17+
/// Visualize database
18+
#[derive(Args)]
19+
#[clap(after_help = r#"LIMITATIONS:
20+
Visualizations will only work for very small stack graphs. This command is
21+
useful for debugging minimal examples, but running it on any real-world code
22+
will most likely result in HTML files that will not load in any browser.
23+
"#)]
24+
pub struct VisualizeArgs {
25+
/// Source file or directory paths.
26+
#[clap(
27+
value_name = "SOURCE_PATH",
28+
value_hint = ValueHint::AnyPath,
29+
)]
30+
pub source_paths: Vec<PathBuf>,
31+
32+
#[clap(
33+
long,
34+
short = 'o',
35+
value_name = "OUTPUT_PATH",
36+
value_hint = ValueHint::AnyPath,
37+
default_value = "stack-graph.html",
38+
)]
39+
pub output: PathBuf,
40+
}
41+
42+
impl VisualizeArgs {
43+
pub fn run(self, db_path: &Path) -> anyhow::Result<()> {
44+
let cancellation_flag = &NoCancellation;
45+
let mut db = SQLiteReader::open(&db_path)?;
46+
for source_path in &self.source_paths {
47+
let source_path = source_path.canonicalize()?;
48+
db.load_graph_for_file_or_directory(&source_path, cancellation_flag)?;
49+
}
50+
let (graph, _, _) = db.get();
51+
let starting_nodes = graph
52+
.iter_nodes()
53+
.filter(|n| graph[*n].is_reference())
54+
.collect::<Vec<_>>();
55+
let mut complete_paths_db = Database::new();
56+
db.find_all_complete_partial_paths(starting_nodes, cancellation_flag, |g, ps, p| {
57+
complete_paths_db.add_partial_path(g, ps, p.clone());
58+
})?;
59+
let (graph, partials, _) = db.get();
60+
let html =
61+
graph.to_html_string("stack-graph", partials, &mut complete_paths_db, &NoFilter)?;
62+
if let Some(dir) = self.output.parent() {
63+
std::fs::create_dir_all(dir)?;
64+
}
65+
std::fs::write(&self.output, html)?;
66+
println!("Visualization at {}", self.output.display());
67+
Ok(())
68+
}
69+
}

0 commit comments

Comments
 (0)