on master help sign in
Use TreeCursor to pretty-print :tree-sitter-subtree (#4606)
9434635Michael Davis
helix-core/src/syntax.rs+71−30
@@ -1283,7 +1283,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
1283use std::{iter, mem, ops, str, usize};
1283use std::{iter, mem, ops, str, usize};
1284use tree_sitter::{
1284use tree_sitter::{
1285 Language as Grammar, Node, Parser, Point, Query, QueryCaptures, QueryCursor, QueryError,
1285 Language as Grammar, Node, Parser, Point, Query, QueryCaptures, QueryCursor, QueryError,
1286− QueryMatch, Range, TextProvider, Tree,
1286+ QueryMatch, Range, TextProvider, Tree, TreeCursor,
1287};
1287};
1288
1288
1289const CANCELLATION_CHECK_INTERVAL: usize = 100;
1289const CANCELLATION_CHECK_INTERVAL: usize = 100;
@@ -2153,57 +2153,68 @@ impl<I: Iterator<Item = HighlightEvent>> Iterator for Merge<I> {
2153 }
2153 }
2154}
2154}
2155
2155
2156+fn node_is_visible(node: &Node) -> bool {
2157+ node.is_missing() || (node.is_named() && node.language().node_kind_is_visible(node.kind_id()))
2158+}
2159+
2156pub fn pretty_print_tree<W: fmt::Write>(fmt: &mut W, node: Node) -> fmt::Result {
2160pub fn pretty_print_tree<W: fmt::Write>(fmt: &mut W, node: Node) -> fmt::Result {
2161+ if node.child_count() == 0 {
2162+ if node_is_visible(&node) {
2163+ write!(fmt, "({})", node.kind())
2164+ } else {
2165+ write!(fmt, "\"{}\"", node.kind())
2166+ }
2167+ } else {
2157− pretty_print_tree_impl(fmt, node, true, None, 0)
2168+ pretty_print_tree_impl(fmt, &mut node.walk(), 0)
2169+ }
2158}
2170}
2159
2171
2160fn pretty_print_tree_impl<W: fmt::Write>(
2172fn pretty_print_tree_impl<W: fmt::Write>(
2161 fmt: &mut W,
2173 fmt: &mut W,
2162− node: Node,
2174+ cursor: &mut TreeCursor,
2163− is_root: bool,
2164− field_name: Option<&str>,
2165 depth: usize,
2175 depth: usize,
2166) -> fmt::Result {
2176) -> fmt::Result {
2167− fn is_visible(node: Node) -> bool {
2177+ let node = cursor.node();
2168− node.is_missing()
2178+ let visible = node_is_visible(&node);
2169− || (node.is_named() && node.language().node_kind_is_visible(node.kind_id()))
2170− }
2171
2179
2172− if is_visible(node) {
2180+ if visible {
2173 let indentation_columns = depth * 2;
2181 let indentation_columns = depth * 2;
2174 write!(fmt, "{:indentation_columns$}", "")?;
2182 write!(fmt, "{:indentation_columns$}", "")?;
2175
2183
2176− if let Some(field_name) = field_name {
2184+ if let Some(field_name) = cursor.field_name() {
2177 write!(fmt, "{}: ", field_name)?;
2185 write!(fmt, "{}: ", field_name)?;
2178 }
2186 }
2179
2187
2180 write!(fmt, "({}", node.kind())?;
2188 write!(fmt, "({}", node.kind())?;
2181− } else if is_root {
2182− write!(fmt, "(\"{}\")", node.kind())?;
2183 }
2189 }
2184
2190
2185− for child_idx in 0..node.child_count() {
2191+ // Handle children.
2186− if let Some(child) = node.child(child_idx) {
2192+ if cursor.goto_first_child() {
2187− if is_visible(child) {
2193+ loop {
2194+ if node_is_visible(&cursor.node()) {
2188 fmt.write_char('\n')?;
2195 fmt.write_char('\n')?;
2189 }
2196 }
2190
2197
2191− pretty_print_tree_impl(
2198+ pretty_print_tree_impl(fmt, cursor, depth + 1)?;
2192− fmt,
2199+
2193− child,
2200+ if !cursor.goto_next_sibling() {
2194− false,
2201+ break;
2195− node.field_name_for_child(child_idx as u32),
2202+ }
2196− depth + 1,
2197− )?;
2198 }
2203 }
2204+
2205+ let moved = cursor.goto_parent();
2206+ // The parent of the first child must exist, and must be `node`.
2207+ debug_assert!(moved);
2208+ debug_assert!(cursor.node() == node);
2199 }
2209 }
2200
2210
2201− if is_visible(node) {
2211+ if visible {
2202− write!(fmt, ")")?;
2212+ fmt.write_char(')')?;
2203 }
2213 }
2204
2214
2205 Ok(())
2215 Ok(())
2206}
2216}
2217+
2207#[cfg(test)]
2218#[cfg(test)]
2208mod test {
2219mod test {
2209 use super::*;
2220 use super::*;
@@ -2376,11 +2387,17 @@ mod test {
2376 }
2387 }
2377
2388
2378 #[track_caller]
2389 #[track_caller]
2379− fn assert_pretty_print(source: &str, expected: &str, start: usize, end: usize) {
2390+ fn assert_pretty_print(
2391+ language_name: &str,
2392+ source: &str,
2393+ expected: &str,
2394+ start: usize,
2395+ end: usize,
2396+ ) {
2380 let source = Rope::from_str(source);
2397 let source = Rope::from_str(source);
2381
2398
2382 let loader = Loader::new(Configuration { language: vec![] });
2399 let loader = Loader::new(Configuration { language: vec![] });
2383− let language = get_language("rust").unwrap();
2400+ let language = get_language(language_name).unwrap();
2384
2401
2385 let config = HighlightConfiguration::new(language, "", "", "").unwrap();
2402 let config = HighlightConfiguration::new(language, "", "", "").unwrap();
2386 let syntax = Syntax::new(&source, Arc::new(config), Arc::new(loader));
2403 let syntax = Syntax::new(&source, Arc::new(config), Arc::new(loader));
@@ -2400,13 +2417,14 @@ mod test {
2400 #[test]
2417 #[test]
2401 fn test_pretty_print() {
2418 fn test_pretty_print() {
2402 let source = r#"/// Hello"#;
2419 let source = r#"/// Hello"#;
2403− assert_pretty_print(source, "(line_comment)", 0, source.len());
2420+ assert_pretty_print("rust", source, "(line_comment)", 0, source.len());
2404
2421
2405 // A large tree should be indented with fields:
2422 // A large tree should be indented with fields:
2406 let source = r#"fn main() {
2423 let source = r#"fn main() {
2407 println!("Hello, World!");
2424 println!("Hello, World!");
2408 }"#;
2425 }"#;
2409 assert_pretty_print(
2426 assert_pretty_print(
2427+ "rust",
2410 source,
2428 source,
2411 concat!(
2429 concat!(
2412 "(function_item\n",
2430 "(function_item\n",
@@ -2425,11 +2443,34 @@ mod test {
2425
2443
2426 // Selecting a token should print just that token:
2444 // Selecting a token should print just that token:
2427 let source = r#"fn main() {}"#;
2445 let source = r#"fn main() {}"#;
2428− assert_pretty_print(source, r#"("fn")"#, 0, 1);
2446+ assert_pretty_print("rust", source, r#""fn""#, 0, 1);
2429
2447
2430 // Error nodes are printed as errors:
2448 // Error nodes are printed as errors:
2431 let source = r#"}{"#;
2449 let source = r#"}{"#;
2432− assert_pretty_print(source, "(ERROR)", 0, source.len());
2450+ assert_pretty_print("rust", source, "(ERROR)", 0, source.len());
2451+
2452+ // Fields broken under unnamed nodes are determined correctly.
2453+ // In the following source, `object` belongs to the `singleton_method`
2454+ // rule but `name` and `body` belong to an unnamed helper `_method_rest`.
2455+ // This can cause a bug with a pretty-printing implementation that
2456+ // uses `Node::field_name_for_child` to determine field names but is
2457+ // fixed when using `TreeCursor::field_name`.
2458+ let source = "def self.method_name
2459+ true
2460+ end";
2461+ assert_pretty_print(
2462+ "ruby",
2463+ source,
2464+ concat!(
2465+ "(singleton_method\n",
2466+ " object: (self)\n",
2467+ " name: (identifier)\n",
2468+ " body: (body_statement\n",
2469+ " (true)))"
2470+ ),
2471+ 0,
2472+ source.len(),
2473+ );
2433 }
2474 }
2434
2475
2435 #[test]
2476 #[test]