From d5b1e26d708ea064897f528efa2c739d0373903b Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Thu, 14 Nov 2024 22:55:00 -0800 Subject: [PATCH] Add a debug helper for syntax tree as DOT --- src/main.rs | 23 +++++++++++++++++++++++ src/options.rs | 22 ++++++++++++++++++++++ src/parse/syntax.rs | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/src/main.rs b/src/main.rs index 170df6897e..271b73c555 100644 --- a/src/main.rs +++ b/src/main.rs @@ -161,6 +161,29 @@ fn main() { } } } + Mode::DumpSyntaxDotty { + path, + ignore_comments, + language_overrides, + } => { + let path = Path::new(&path); + let bytes = read_or_die(path); + let src = String::from_utf8_lossy(&bytes).to_string(); + + let language = guess(path, &src, &language_overrides); + match language { + Some(lang) => { + let ts_lang = tsp::from_language(lang); + let arena = Arena::new(); + let ast = tsp::parse(&arena, &src, &ts_lang, ignore_comments); + init_all_info(&ast, &[]); + syntax::print_as_dot(&ast); + } + None => { + eprintln!("No tree-sitter parser for file: {:?}", path); + } + } + } Mode::ListLanguages { use_color, language_overrides, diff --git a/src/options.rs b/src/options.rs index be9d3a055f..b3c6268a79 100644 --- a/src/options.rs +++ b/src/options.rs @@ -123,6 +123,15 @@ fn app() -> clap::Command<'static> { "Parse a single file with tree-sitter and display the difftastic syntax tree.", ).help_heading("DEBUG OPTIONS"), ) + .arg( + Arg::new("dump-syntax-dotty") + .long("dump-syntax-dotty") + .takes_value(true) + .value_name("PATH") + .long_help( + "Parse a single file with tree-sitter and display the difftastic syntax tree, as a dotty graph.", + ).help_heading("DEBUG OPTIONS"), + ) .arg( Arg::new("dump-ts") .long("dump-ts") @@ -485,6 +494,11 @@ pub(crate) enum Mode { ignore_comments: bool, language_overrides: Vec<(LanguageOverride, Vec)>, }, + DumpSyntaxDotty { + path: String, + ignore_comments: bool, + language_overrides: Vec<(LanguageOverride, Vec)>, + }, } fn common_path_suffix(lhs_path: &Path, rhs_path: &Path) -> Option { @@ -644,6 +658,14 @@ pub(crate) fn parse_args() -> Mode { }; } + if let Some(path) = matches.value_of("dump-syntax-dotty") { + return Mode::DumpSyntaxDotty { + path: path.to_owned(), + ignore_comments, + language_overrides, + }; + } + if let Some(path) = matches.value_of("dump-ts") { return Mode::DumpTreeSitter { path: path.to_owned(), diff --git a/src/parse/syntax.rs b/src/parse/syntax.rs index 746dcc60eb..47676655c3 100644 --- a/src/parse/syntax.rs +++ b/src/parse/syntax.rs @@ -369,6 +369,43 @@ pub(crate) fn init_all_info<'a>(lhs_roots: &[&'a Syntax<'a>], rhs_roots: &[&'a S init_next_prev(rhs_roots); } +pub(crate) fn print_as_dot<'a>(roots: &[&'a Syntax<'a>]) { + println!("digraph {{"); + print_as_dot_(roots); + println!("}}"); +} + +fn print_as_dot_<'a>(nodes: &[&'a Syntax<'a>]) { + for node in nodes { + let label = match node { + List { + open_content, + close_content, + .. + } => { + if open_content != "" { + &format!("[label=\"{open_content}{close_content}\"]") + } else { + &"[style=dotted]".to_owned() + } + } + Atom { content, .. } => { + let content = content.replace("\"", "\\\""); + &format!("[label=\"{content}\"]") + } + }; + + println!(" id{} {};", node.id().get(), label); + + if let List { children, .. } = node { + for child in children { + println!(" id{} -> id{};", node.id().get(), child.id().get()); + } + print_as_dot_(children); + } + } +} + fn init_info<'a>(lhs_roots: &[&'a Syntax<'a>], rhs_roots: &[&'a Syntax<'a>]) { let mut id = NonZeroU32::new(1).unwrap(); init_info_on_side(lhs_roots, &mut id);