on master help sign in
Pretty print `tree-sitter-subtree` expression (#4295)
commit4174b25b3d0d39c09a6d4bcd7fa22ee98be52ed1 authorFisher Darling <fdarlingco@gmail.com> dateThu 20 Oct 2022, 00:17 (4 years ago) parenta7e7c2c
2 files changed+111−1
helix-core/src/syntax.rs+108 helix-term/src/commands/typed.rs+3−1
helix-core/src/syntax.rs+108
@@ -2030,6 +2030,57 @@ impl<I: Iterator<Item = HighlightEvent>> Iterator for Merge<I> {
20302030 }
20312031}
20322032
2033+pub fn pretty_print_tree<W: fmt::Write>(fmt: &mut W, node: Node) -> fmt::Result {
2034+ pretty_print_tree_impl(fmt, node, true, None, 0)
2035+}
2036+
2037+fn pretty_print_tree_impl<W: fmt::Write>(
2038+ fmt: &mut W,
2039+ node: Node,
2040+ is_root: bool,
2041+ field_name: Option<&str>,
2042+ depth: usize,
2043+) -> fmt::Result {
2044+ fn is_visible(node: Node) -> bool {
2045+ node.is_missing()
2046+ || (node.is_named() && node.language().node_kind_is_visible(node.kind_id()))
2047+ }
2048+
2049+ if is_visible(node) {
2050+ let indentation_columns = depth * 2;
2051+ write!(fmt, "{:indentation_columns$}", "")?;
2052+
2053+ if let Some(field_name) = field_name {
2054+ write!(fmt, "{}: ", field_name)?;
2055+ }
2056+
2057+ write!(fmt, "({}", node.kind())?;
2058+ } else if is_root {
2059+ write!(fmt, "(\"{}\")", node.kind())?;
2060+ }
2061+
2062+ for child_idx in 0..node.child_count() {
2063+ if let Some(child) = node.child(child_idx) {
2064+ if is_visible(child) {
2065+ fmt.write_char('\n')?;
2066+ }
2067+
2068+ pretty_print_tree_impl(
2069+ fmt,
2070+ child,
2071+ false,
2072+ node.field_name_for_child(child_idx as u32),
2073+ depth + 1,
2074+ )?;
2075+ }
2076+ }
2077+
2078+ if is_visible(node) {
2079+ write!(fmt, ")")?;
2080+ }
2081+
2082+ Ok(())
2083+}
20332084#[cfg(test)]
20342085mod test {
20352086 use super::*;
@@ -2201,6 +2252,63 @@ mod test {
22012252 );
22022253 }
22032254
2255+ #[track_caller]
2256+ fn assert_pretty_print(source: &str, expected: &str, start: usize, end: usize) {
2257+ let source = Rope::from_str(source);
2258+
2259+ let loader = Loader::new(Configuration { language: vec![] });
2260+ let language = get_language("Rust").unwrap();
2261+
2262+ let config = HighlightConfiguration::new(language, "", "", "").unwrap();
2263+ let syntax = Syntax::new(&source, Arc::new(config), Arc::new(loader));
2264+
2265+ let root = syntax
2266+ .tree()
2267+ .root_node()
2268+ .descendant_for_byte_range(start, end)
2269+ .unwrap();
2270+
2271+ let mut output = String::new();
2272+ pretty_print_tree(&mut output, root).unwrap();
2273+
2274+ assert_eq!(expected, output);
2275+ }
2276+
2277+ #[test]
2278+ fn test_pretty_print() {
2279+ let source = r#"/// Hello"#;
2280+ assert_pretty_print(source, "(line_comment)", 0, source.len());
2281+
2282+ // A large tree should be indented with fields:
2283+ let source = r#"fn main() {
2284+ println!("Hello, World!");
2285+ }"#;
2286+ assert_pretty_print(
2287+ source,
2288+ concat!(
2289+ "(function_item\n",
2290+ " name: (identifier)\n",
2291+ " parameters: (parameters)\n",
2292+ " body: (block\n",
2293+ " (expression_statement\n",
2294+ " (macro_invocation\n",
2295+ " macro: (identifier)\n",
2296+ " (token_tree\n",
2297+ " (string_literal))))))",
2298+ ),
2299+ 0,
2300+ source.len(),
2301+ );
2302+
2303+ // Selecting a token should print just that token:
2304+ let source = r#"fn main() {}"#;
2305+ assert_pretty_print(source, r#"("fn")"#, 0, 1);
2306+
2307+ // Error nodes are printed as errors:
2308+ let source = r#"}{"#;
2309+ assert_pretty_print(source, "(ERROR)", 0, source.len());
2310+ }
2311+
22042312 #[test]
22052313 fn test_load_runtime_file() {
22062314 // Test to make sure we can load some data from the runtime directory.
helix-term/src/commands/typed.rs+3−1
@@ -1487,7 +1487,9 @@ fn tree_sitter_subtree(
14871487 .root_node()
14881488 .descendant_for_byte_range(from, to)
14891489 {
1490− let contents = format!("```tsq\n{}\n```", selected_node.to_sexp());
1490+ let mut contents = String::from("```tsq\n");
1491+ helix_core::syntax::pretty_print_tree(&mut contents, selected_node)?;
1492+ contents.push_str("\n```");
14911493
14921494 let callback = async move {
14931495 let call: job::Callback =