Skip to content
This repository was archived by the owner on Sep 9, 2025. It is now read-only.

Commit b17e676

Browse files
author
Hendrik van Antwerpen
authored
Merge pull request #183 from github/add-analyze-options
Add analyze flags (and small TSG fixes)
2 parents b7c2a0d + f351358 commit b17e676

File tree

3 files changed

+87
-14
lines changed

3 files changed

+87
-14
lines changed

languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,12 @@ attribute node_symbol = node => symbol = (source-text node), source_n
8686
node @comment.aliased_type
8787
node @comment.applied_type
8888
node @comment.arg
89-
node @comment.arg_index
9089
node @comment.args
9190
node @comment.async_type
9291
node @comment.await_type
9392
node @comment.callable
9493
node @comment.coargs
9594
node @comment.cocallable
96-
node @comment.comp_index
9795
node @comment.coparam
9896
node @comment.coparams
9997
node @comment.cotype
@@ -234,11 +232,11 @@ attribute node_symbol = node => symbol = (source-text node), source_n
234232

235233
; import builtins
236234
node builtins_ref__ns
237-
attr (builtins_ref__ns) symbol_reference = "%Proj"
235+
attr (builtins_ref__ns) push_symbol = "%Proj"
238236
edge builtins_ref__ns -> ROOT_NODE
239237
;
240238
node builtins_ref
241-
attr (builtins_ref) symbol_reference = "<builtins>"
239+
attr (builtins_ref) push_symbol = "<builtins>"
242240
edge builtins_ref -> builtins_ref__ns
243241
;
244242
edge @prog.lexical_scope -> builtins_ref
@@ -435,6 +433,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n
435433
(generator_function_declaration)
436434
(hash_bang_line)
437435
(if_statement)
436+
(import_alias)
438437
(import_statement)
439438
(interface_declaration)
440439
(internal_module)
@@ -5266,6 +5265,7 @@ if none @is_acc {
52665265

52675266
(type_arguments)@type_args {
52685267
node @type_args.type_args
5268+
attr (@type_args.type_args) is_exported
52695269
}
52705270

52715271
(type_arguments
@@ -5291,18 +5291,21 @@ if none @is_acc {
52915291
; (_)@element_type
52925292
; )@type
52935293

5294-
(array_type
5295-
(_)@element_type
5296-
)@type {
5294+
(array_type)@type {
52975295
node @type.indexable
52985296

5299-
; propagate lexical scope
5300-
edge @element_type.lexical_scope -> @type.lexical_scope
5301-
53025297
; type is indexable element type
53035298
edge @type.type -> @type.indexable
53045299
;
53055300
attr (@type.indexable) pop_symbol = "[]"
5301+
}
5302+
5303+
(array_type
5304+
(_)@element_type
5305+
)@type {
5306+
; propagate lexical scope
5307+
edge @element_type.lexical_scope -> @type.lexical_scope
5308+
53065309
edge @type.indexable -> @element_type.type
53075310
}
53085311

@@ -5900,6 +5903,7 @@ if none @is_acc {
59005903
]@gen_expr {
59015904
node @gen_expr.type_app
59025905
node @gen_expr.inferred_type_args
5906+
attr (@gen_expr.inferred_type_args) is_exported
59035907

59045908
; push inferred type arguments
59055909
edge @gen_expr.applied_type -> @gen_expr.type_app
@@ -6118,6 +6122,7 @@ if none @is_acc {
61186122
node @async.async_type__type_app
61196123
node @async.async_type__type_arg
61206124
node @async.async_type__type_args
6125+
attr (@async.async_type__type_args) is_exported
61216126
node @async.async_type__promise_ref
61226127
node @async.async_type__promise_ref__ns
61236128
node @async.await_type

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

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use stack_graphs::graph::StackGraph;
1313
use stack_graphs::partial::PartialPaths;
1414
use stack_graphs::stitching::Database;
1515
use std::collections::HashMap;
16+
use std::io::Write;
1617
use std::path::Path;
1718
use std::path::PathBuf;
1819
use std::sync::Arc;
@@ -45,6 +46,16 @@ pub struct AnalyzeArgs {
4546
)]
4647
pub source_paths: Vec<PathBuf>,
4748

49+
/// Continue analysis from the given file
50+
#[clap(
51+
long,
52+
value_name = "SOURCE_PATH",
53+
value_hint = ValueHint::AnyPath,
54+
parse(from_os_str),
55+
validator_os = path_exists,
56+
)]
57+
pub continue_from: Option<PathBuf>,
58+
4859
#[clap(long, short = 'v')]
4960
pub verbose: bool,
5061

@@ -55,18 +66,28 @@ pub struct AnalyzeArgs {
5566
parse(try_from_str = duration_from_seconds_str),
5667
)]
5768
pub max_file_time: Option<Duration>,
69+
70+
/// Wait for user input before starting analysis. Useful for profiling.
71+
#[clap(long)]
72+
pub wait_at_start: bool,
5873
}
5974

6075
impl AnalyzeArgs {
6176
pub fn new(source_paths: Vec<PathBuf>) -> Self {
6277
Self {
6378
source_paths,
79+
continue_from: None,
6480
verbose: false,
6581
max_file_time: None,
82+
wait_at_start: false,
6683
}
6784
}
6885

6986
pub fn run(&self, loader: &mut Loader) -> anyhow::Result<()> {
87+
if self.wait_at_start {
88+
self.wait_for_input()?;
89+
}
90+
let mut seen_mark = false;
7091
for source_path in &self.source_paths {
7192
if source_path.is_dir() {
7293
let source_root = source_path;
@@ -77,35 +98,58 @@ impl AnalyzeArgs {
7798
.filter(|e| e.file_type().is_file())
7899
{
79100
let source_path = source_entry.path();
80-
self.analyze_file_with_context(source_root, source_path, loader)?;
101+
self.analyze_file_with_context(
102+
source_root,
103+
source_path,
104+
loader,
105+
&mut seen_mark,
106+
)?;
81107
}
82108
} else {
83109
let source_root = source_path.parent().unwrap();
84-
self.analyze_file_with_context(source_root, source_path, loader)?;
110+
if self.should_skip(source_path, &mut seen_mark) {
111+
continue;
112+
}
113+
self.analyze_file_with_context(source_root, source_path, loader, &mut seen_mark)?;
85114
}
86115
}
87116
Ok(())
88117
}
89118

119+
fn wait_for_input(&self) -> anyhow::Result<()> {
120+
print!("<press ENTER to continue>");
121+
std::io::stdout().flush()?;
122+
let mut input = String::new();
123+
std::io::stdin().read_line(&mut input)?;
124+
Ok(())
125+
}
126+
90127
/// Analyze file and add error context to any failures that are returned.
91128
fn analyze_file_with_context(
92129
&self,
93130
source_root: &Path,
94131
source_path: &Path,
95132
loader: &mut Loader,
133+
seen_mark: &mut bool,
96134
) -> anyhow::Result<()> {
97-
self.analyze_file(source_root, source_path, loader)
98-
.with_context(|| format!("Error analyzing file {}", source_path.display()))
135+
self.analyze_file(source_root, source_path, loader, seen_mark)
136+
.with_context(|| format!("Error analyzing file {}. To continue analysis from this file later, add: --continue-from {}", source_path.display(), source_path.display()))
99137
}
100138

101139
fn analyze_file(
102140
&self,
103141
source_root: &Path,
104142
source_path: &Path,
105143
loader: &mut Loader,
144+
seen_mark: &mut bool,
106145
) -> anyhow::Result<()> {
107146
let mut file_status = FileStatusLogger::new(source_path, self.verbose);
108147

148+
if self.should_skip(source_path, seen_mark) {
149+
file_status.info("skipped")?;
150+
return Ok(());
151+
}
152+
109153
let mut file_reader = FileReader::new();
110154
let lc = match loader.load_for_file(source_path, &mut file_reader, &NoCancellation) {
111155
Ok(Some(sgl)) => sgl,
@@ -192,4 +236,21 @@ impl AnalyzeArgs {
192236
file_status.ok("success")?;
193237
Ok(())
194238
}
239+
240+
/// Determines if a path should be skipped because we have not seen the
241+
/// continue_from mark yet. The `seen_mark` parameter is necessary to keep
242+
/// track of the mark between the calls in one run.
243+
fn should_skip(&self, path: &Path, seen_mark: &mut bool) -> bool {
244+
if *seen_mark {
245+
return false; // return early and skip match
246+
}
247+
if let Some(mark) = &self.continue_from {
248+
if path == mark {
249+
*seen_mark = true; // this is the mark, we have seen it
250+
}
251+
} else {
252+
*seen_mark = true; // early return from now on
253+
}
254+
return !*seen_mark; // skip if we haven't seen the mark yet
255+
}
195256
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,13 @@ impl<'a> FileStatusLogger<'a> {
189189
Ok(())
190190
}
191191

192+
pub fn info(&mut self, status: &str) -> Result<(), anyhow::Error> {
193+
self.print_path(true)?;
194+
println!("{}", status.dimmed());
195+
self.path_logged = false;
196+
Ok(())
197+
}
198+
192199
pub fn warn(&mut self, status: &str) -> Result<(), anyhow::Error> {
193200
self.print_path(true)?;
194201
println!("{}", status.yellow());

0 commit comments

Comments
 (0)