diff --git a/cli/src/main.rs b/cli/src/main.rs index 2f8c6dd5..620f329f 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -64,6 +64,7 @@ fn run() -> error::Result<()> { .arg(Arg::with_name("debug").long("debug").short("d")) .arg(Arg::with_name("debug-graph").long("debug-graph").short("D")) .arg(Arg::with_name("quiet").long("quiet").short("q")) + .arg(Arg::with_name("stat").long("stat").short("s")) .arg(Arg::with_name("time").long("time").short("t")) .arg(Arg::with_name("allow-cancellation").long("cancel")) .arg(Arg::with_name("timeout").long("timeout").takes_value(true)) @@ -234,11 +235,16 @@ fn run() -> error::Result<()> { let max_path_length = paths.iter().map(|p| p.chars().count()).max().unwrap(); let mut has_error = false; loader.find_all_languages(&config.parser_directories)?; + + let should_track_stats = matches.is_present("stat"); + let mut stats = parse::Stats::default(); + for path in paths { let path = Path::new(&path); let language = select_language(&mut loader, path, ¤t_dir, matches.value_of("scope"))?; - has_error |= parse::parse_file_at_path( + + let this_file_errored = parse::parse_file_at_path( language, path, &edits, @@ -250,7 +256,21 @@ fn run() -> error::Result<()> { debug_graph, allow_cancellation, )?; + + if should_track_stats { + stats.total_parses += 1; + if !this_file_errored { + stats.successful_parses += 1; + } + } + + has_error |= this_file_errored; } + + if should_track_stats { + println!("{}", stats) + } + if has_error { return Error::err(String::new()); } diff --git a/cli/src/parse.rs b/cli/src/parse.rs index 13bac0f3..499bef1f 100644 --- a/cli/src/parse.rs +++ b/cli/src/parse.rs @@ -4,7 +4,7 @@ use std::io::{self, Write}; use std::path::Path; use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::Instant; -use std::{fs, thread, usize}; +use std::{fmt, fs, thread, usize}; use tree_sitter::{InputEdit, Language, LogType, Parser, Point, Tree}; #[derive(Debug)] @@ -14,6 +14,22 @@ pub struct Edit { pub inserted_text: Vec, } +#[derive(Debug, Default)] +pub struct Stats { + pub successful_parses: usize, + pub total_parses: usize, +} + +impl fmt::Display for Stats { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + return writeln!(f, "Total parses: {}; successful parses: {}; failed parses: {}; success percentage: {:.2}%", + self.total_parses, + self.successful_parses, + self.total_parses - self.successful_parses, + (self.successful_parses as f64) / (self.total_parses as f64) * 100.0); + } +} + pub fn parse_file_at_path( language: Language, path: &Path, diff --git a/docs/section-3-creating-parsers.md b/docs/section-3-creating-parsers.md index 694f8dae..77999190 100644 --- a/docs/section-3-creating-parsers.md +++ b/docs/section-3-creating-parsers.md @@ -184,10 +184,10 @@ You can run your parser on an arbitrary file using `tree-sitter parse`. This wil (int_literal [1, 9] - [1, 10])))))) ``` -You can pass any number of file paths and glob patterns to `tree-sitter parse`, and it will parse all of the given files. The command will exit with a non-zero status code if any parse errors occurred. You can also prevent the syntax trees from being printed using the `--quiet` flag. This makes `tree-sitter parse` usable as a secondary testing strategy: you can check that a large number of files parse without error: +You can pass any number of file paths and glob patterns to `tree-sitter parse`, and it will parse all of the given files. The command will exit with a non-zero status code if any parse errors occurred. You can also prevent the syntax trees from being printed using the `--quiet` flag. Additionally, the `--stat` flag prints out aggregated parse success/failure information for all processed files. This makes `tree-sitter parse` usable as a secondary testing strategy: you can check that a large number of files parse without error: ```sh -tree-sitter parse 'examples/**/*.go' --quiet +tree-sitter parse 'examples/**/*.go' --quiet --stat ``` ### Command: `highlight`