From 2d1aa9518358960dc7772e40a648708a8cc0ab92 Mon Sep 17 00:00:00 2001 From: Fisher Darling Date: Sat, 15 Oct 2022 15:38:53 +0200 Subject: [PATCH 1/4] custom node formatting rather than --- helix-term/src/commands/typed.rs | 54 ++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 78d25c1a5ba8..09b55d7de235 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -1,4 +1,4 @@ -use std::ops::Deref; +use std::{fmt::Write, ops::Deref}; use super::*; @@ -1473,7 +1473,57 @@ fn tree_sitter_subtree( .root_node() .descendant_for_byte_range(from, to) { - let contents = format!("```tsq\n{}\n```", selected_node.to_sexp()); + fn pretty_print_node( + node: Node<'_>, + sexpr: &mut String, + is_root: bool, + field_name: Option<&str>, + depth: usize, + ) -> anyhow::Result<()> { + fn is_visible(node: Node<'_>) -> bool { + node.is_missing() + || (node.is_named() && node.language().node_kind_is_visible(node.kind_id())) + } + + if is_visible(node) { + write!(sexpr, "{:depth$}", "")?; + + if let Some(field_name) = field_name { + write!(sexpr, "{}: ", field_name)?; + } + + write!(sexpr, "({}", node.kind())?; + } else if is_root { + write!(sexpr, "(\"{}\")", node.kind())?; + } + + for child_idx in 0..node.child_count() { + if let Some(child) = node.child(child_idx) { + if is_visible(child) { + sexpr.push('\n'); + } + + pretty_print_node( + child, + sexpr, + false, + node.field_name_for_child(child_idx as u32), + depth + 2, + )?; + } + } + + if is_visible(node) { + write!(sexpr, ")")?; + } + + Ok(()) + } + + let mut sexpr = String::new(); + pretty_print_node(selected_node, &mut sexpr, true, None, 0)?; + + let contents = format!("```tsq\n{}\n```", sexpr); let callback = async move { let call: job::Callback = From 5f0c21b3b535eff94ce1f3d8f8ff28da4590394d Mon Sep 17 00:00:00 2001 From: Fisher Darling Date: Sun, 16 Oct 2022 10:51:37 +0200 Subject: [PATCH 2/4] move pretty_print to syntax and add unit tests --- helix-core/src/syntax.rs | 108 +++++++++++++++++++++++++++++++ helix-term/src/commands/typed.rs | 57 ++-------------- 2 files changed, 113 insertions(+), 52 deletions(-) diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 8c51d5ebb049..569f50249899 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -2024,6 +2024,57 @@ impl> Iterator for Merge { } } +pub fn pretty_print_tree(fmt: &mut W, node: Node<'_>) -> fmt::Result { + pretty_print_tree_impl(fmt, node, true, None, 0) +} + +fn pretty_print_tree_impl( + fmt: &mut W, + node: Node<'_>, + is_root: bool, + field_name: Option<&str>, + depth: usize, +) -> fmt::Result { + fn is_visible(node: Node<'_>) -> bool { + node.is_missing() + || (node.is_named() && node.language().node_kind_is_visible(node.kind_id())) + } + + if is_visible(node) { + let indentation_columns = depth * 2; + write!(fmt, "{:indentation_columns$}", "")?; + + if let Some(field_name) = field_name { + write!(fmt, "{}: ", field_name)?; + } + + write!(fmt, "({}", node.kind())?; + } else if is_root { + write!(fmt, "(\"{}\")", node.kind())?; + } + + for child_idx in 0..node.child_count() { + if let Some(child) = node.child(child_idx) { + if is_visible(child) { + fmt.write_char('\n')?; + } + + pretty_print_tree_impl( + fmt, + child, + false, + node.field_name_for_child(child_idx as u32), + depth + 1, + )?; + } + } + + if is_visible(node) { + write!(fmt, ")")?; + } + + Ok(()) +} #[cfg(test)] mod test { use super::*; @@ -2195,6 +2246,63 @@ mod test { ); } + #[track_caller] + fn assert_pretty_print(source: &str, expected: &str, start: usize, end: usize) { + let source = Rope::from_str(source); + + let loader = Loader::new(Configuration { language: vec![] }); + let language = get_language("Rust").unwrap(); + + let config = HighlightConfiguration::new(language, "", "", "").unwrap(); + let syntax = Syntax::new(&source, Arc::new(config), Arc::new(loader)); + + let root = syntax + .tree() + .root_node() + .descendant_for_byte_range(start, end) + .unwrap(); + + let mut output = String::new(); + pretty_print_tree(&mut output, root).unwrap(); + + assert_eq!(expected, output); + } + + #[test] + fn test_pretty_print() { + let source = r#"/// Hello"#; + assert_pretty_print(source, "(line_comment)", 0, source.len()); + + // A large tree should be indented with fields: + let source = r#"fn main() { + println!("Hello, World!"); + }"#; + assert_pretty_print( + source, + concat!( + "(function_item\n", + " name: (identifier)\n", + " parameters: (parameters)\n", + " body: (block\n", + " (expression_statement\n", + " (macro_invocation\n", + " macro: (identifier)\n", + " (token_tree\n", + " (string_literal))))))", + ), + 0, + source.len(), + ); + + // Selecting a token should print just that token: + let source = r#"fn main() {}"#; + assert_pretty_print(source, r#"("fn")"#, 0, 1); + + // Error nodes are printed as errors: + let source = r#"}{"#; + assert_pretty_print(source, "(ERROR)", 0, source.len()); + } + #[test] fn test_load_runtime_file() { // Test to make sure we can load some data from the runtime directory. diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 09b55d7de235..b27416fb661d 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -1,7 +1,8 @@ -use std::{fmt::Write, ops::Deref}; +use std::ops::Deref; use super::*; +use helix_core::syntax::pretty_print_tree; use helix_view::{ apply_transaction, editor::{Action, CloseError, ConfigEvent}, @@ -1473,57 +1474,9 @@ fn tree_sitter_subtree( .root_node() .descendant_for_byte_range(from, to) { - fn pretty_print_node( - node: Node<'_>, - sexpr: &mut String, - is_root: bool, - field_name: Option<&str>, - depth: usize, - ) -> anyhow::Result<()> { - fn is_visible(node: Node<'_>) -> bool { - node.is_missing() - || (node.is_named() && node.language().node_kind_is_visible(node.kind_id())) - } - - if is_visible(node) { - write!(sexpr, "{:depth$}", "")?; - - if let Some(field_name) = field_name { - write!(sexpr, "{}: ", field_name)?; - } - - write!(sexpr, "({}", node.kind())?; - } else if is_root { - write!(sexpr, "(\"{}\")", node.kind())?; - } - - for child_idx in 0..node.child_count() { - if let Some(child) = node.child(child_idx) { - if is_visible(child) { - sexpr.push('\n'); - } - - pretty_print_node( - child, - sexpr, - false, - node.field_name_for_child(child_idx as u32), - depth + 2, - )?; - } - } - - if is_visible(node) { - write!(sexpr, ")")?; - } - - Ok(()) - } - - let mut sexpr = String::new(); - pretty_print_node(selected_node, &mut sexpr, true, None, 0)?; - - let contents = format!("```tsq\n{}\n```", sexpr); + let mut contents = String::from("```tsq\n"); + pretty_print_tree(&mut contents, selected_node)?; + contents.push_str("\n```"); let callback = async move { let call: job::Callback = From c25023cb4d0d1f89c5f258c939ede091309d877a Mon Sep 17 00:00:00 2001 From: Fisher Darling Date: Sun, 16 Oct 2022 19:13:20 +0200 Subject: [PATCH 3/4] fully qualify pretty_print call --- helix-term/src/commands/typed.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index b27416fb661d..51e7b9d752aa 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -2,7 +2,6 @@ use std::ops::Deref; use super::*; -use helix_core::syntax::pretty_print_tree; use helix_view::{ apply_transaction, editor::{Action, CloseError, ConfigEvent}, @@ -1475,7 +1474,7 @@ fn tree_sitter_subtree( .descendant_for_byte_range(from, to) { let mut contents = String::from("```tsq\n"); - pretty_print_tree(&mut contents, selected_node)?; + helix_core::syntax::pretty_print_tree(&mut contents, selected_node)?; contents.push_str("\n```"); let callback = async move { From 491b89f8b50dda654c71c8fe883d123da47ddebe Mon Sep 17 00:00:00 2001 From: Fisher Darling Date: Wed, 19 Oct 2022 10:00:22 +0200 Subject: [PATCH 4/4] remove anonymous lifetimes --- helix-core/src/syntax.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 569f50249899..c50020a2dff6 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -2024,18 +2024,18 @@ impl> Iterator for Merge { } } -pub fn pretty_print_tree(fmt: &mut W, node: Node<'_>) -> fmt::Result { +pub fn pretty_print_tree(fmt: &mut W, node: Node) -> fmt::Result { pretty_print_tree_impl(fmt, node, true, None, 0) } fn pretty_print_tree_impl( fmt: &mut W, - node: Node<'_>, + node: Node, is_root: bool, field_name: Option<&str>, depth: usize, ) -> fmt::Result { - fn is_visible(node: Node<'_>) -> bool { + fn is_visible(node: Node) -> bool { node.is_missing() || (node.is_named() && node.language().node_kind_is_visible(node.kind_id())) }