Skip to content

Commit a48ec87

Browse files
committed
Use TreeCursor to pretty-print :tree-sitter-subtree
The current `:tree-sitter-subtree` has a bug for field-names when the field name belongs to an unnamed child node. Take this ruby example: def self.method_name true end The subtree given by tree-sitter-cli is: (singleton_method [2, 0] - [4, 3] object: (self [2, 4] - [2, 8]) name: (identifier [2, 9] - [2, 20]) body: (body_statement [3, 2] - [3, 6] (true [3, 2] - [3, 6]))) But the `:tree-sitter-subtree` output was (singleton_method object: (self) body: (identifier) (body_statement (true))) The `singleton_method` rule defines the `name` and `body` fields in an unnamed helper rule `_method_rest` and the old implementation of `pretty_print_tree_impl` would pass the `field_name` down from the named `singleton_method` node. To fix it we switch to the [TreeCursor] API which is recommended by the tree-sitter docs for traversing the tree. `TreeCursor::field_name` accurately determines the field name for the current cursor position even when the node is unnamed. [TreeCursor]: https://docs.rs/tree-sitter/0.20.9/tree_sitter/struct.TreeCursor.html
1 parent 3814987 commit a48ec87

File tree

1 file changed

+65
-30
lines changed

1 file changed

+65
-30
lines changed

helix-core/src/syntax.rs

Lines changed: 65 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,7 +1260,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
12601260
use std::{iter, mem, ops, str, usize};
12611261
use tree_sitter::{
12621262
Language as Grammar, Node, Parser, Point, Query, QueryCaptures, QueryCursor, QueryError,
1263-
QueryMatch, Range, TextProvider, Tree,
1263+
QueryMatch, Range, TextProvider, Tree, TreeCursor,
12641264
};
12651265

12661266
const CANCELLATION_CHECK_INTERVAL: usize = 100;
@@ -2130,57 +2130,67 @@ impl<I: Iterator<Item = HighlightEvent>> Iterator for Merge<I> {
21302130
}
21312131
}
21322132

2133+
fn node_is_visible(node: &Node) -> bool {
2134+
node.is_missing() || (node.is_named() && node.language().node_kind_is_visible(node.kind_id()))
2135+
}
2136+
21332137
pub fn pretty_print_tree<W: fmt::Write>(fmt: &mut W, node: Node) -> fmt::Result {
2134-
pretty_print_tree_impl(fmt, node, true, None, 0)
2138+
if node.child_count() == 0 {
2139+
if node_is_visible(&node) {
2140+
write!(fmt, "({})", node.kind())
2141+
} else {
2142+
write!(fmt, "\"{}\"", node.kind())
2143+
}
2144+
} else {
2145+
pretty_print_tree_impl(fmt, &mut node.walk(), 0)
2146+
}
21352147
}
21362148

21372149
fn pretty_print_tree_impl<W: fmt::Write>(
21382150
fmt: &mut W,
2139-
node: Node,
2140-
is_root: bool,
2141-
field_name: Option<&str>,
2151+
cursor: &mut TreeCursor,
21422152
depth: usize,
21432153
) -> fmt::Result {
2144-
fn is_visible(node: Node) -> bool {
2145-
node.is_missing()
2146-
|| (node.is_named() && node.language().node_kind_is_visible(node.kind_id()))
2147-
}
2154+
let node = cursor.node();
2155+
let visible = node_is_visible(&node);
21482156

2149-
if is_visible(node) {
2157+
if visible {
21502158
let indentation_columns = depth * 2;
21512159
write!(fmt, "{:indentation_columns$}", "")?;
21522160

2153-
if let Some(field_name) = field_name {
2161+
if let Some(field_name) = cursor.field_name() {
21542162
write!(fmt, "{}: ", field_name)?;
21552163
}
21562164

21572165
write!(fmt, "({}", node.kind())?;
2158-
} else if is_root {
2159-
write!(fmt, "(\"{}\")", node.kind())?;
21602166
}
21612167

2162-
for child_idx in 0..node.child_count() {
2163-
if let Some(child) = node.child(child_idx) {
2164-
if is_visible(child) {
2168+
// Handle children.
2169+
if cursor.goto_first_child() {
2170+
loop {
2171+
if node_is_visible(&cursor.node()) {
21652172
fmt.write_char('\n')?;
21662173
}
21672174

2168-
pretty_print_tree_impl(
2169-
fmt,
2170-
child,
2171-
false,
2172-
node.field_name_for_child(child_idx as u32),
2173-
depth + 1,
2174-
)?;
2175+
pretty_print_tree_impl(fmt, cursor, depth + 1)?;
2176+
2177+
if !cursor.goto_next_sibling() {
2178+
break;
2179+
}
21752180
}
2181+
2182+
// The parent of the first child must exist, and must be `node`.
2183+
debug_assert!(cursor.goto_parent());
2184+
debug_assert!(cursor.node() == node);
21762185
}
21772186

2178-
if is_visible(node) {
2179-
write!(fmt, ")")?;
2187+
if visible {
2188+
fmt.write_char(')')?;
21802189
}
21812190

21822191
Ok(())
21832192
}
2193+
21842194
#[cfg(test)]
21852195
mod test {
21862196
use super::*;
@@ -2353,11 +2363,17 @@ mod test {
23532363
}
23542364

23552365
#[track_caller]
2356-
fn assert_pretty_print(source: &str, expected: &str, start: usize, end: usize) {
2366+
fn assert_pretty_print(
2367+
language_name: &str,
2368+
source: &str,
2369+
expected: &str,
2370+
start: usize,
2371+
end: usize,
2372+
) {
23572373
let source = Rope::from_str(source);
23582374

23592375
let loader = Loader::new(Configuration { language: vec![] });
2360-
let language = get_language("rust").unwrap();
2376+
let language = get_language(language_name).unwrap();
23612377

23622378
let config = HighlightConfiguration::new(language, "", "", "").unwrap();
23632379
let syntax = Syntax::new(&source, Arc::new(config), Arc::new(loader));
@@ -2377,13 +2393,14 @@ mod test {
23772393
#[test]
23782394
fn test_pretty_print() {
23792395
let source = r#"/// Hello"#;
2380-
assert_pretty_print(source, "(line_comment)", 0, source.len());
2396+
assert_pretty_print("rust", source, "(line_comment)", 0, source.len());
23812397

23822398
// A large tree should be indented with fields:
23832399
let source = r#"fn main() {
23842400
println!("Hello, World!");
23852401
}"#;
23862402
assert_pretty_print(
2403+
"rust",
23872404
source,
23882405
concat!(
23892406
"(function_item\n",
@@ -2402,11 +2419,29 @@ mod test {
24022419

24032420
// Selecting a token should print just that token:
24042421
let source = r#"fn main() {}"#;
2405-
assert_pretty_print(source, r#"("fn")"#, 0, 1);
2422+
assert_pretty_print("rust", source, r#""fn""#, 0, 1);
24062423

24072424
// Error nodes are printed as errors:
24082425
let source = r#"}{"#;
2409-
assert_pretty_print(source, "(ERROR)", 0, source.len());
2426+
assert_pretty_print("rust", source, "(ERROR)", 0, source.len());
2427+
2428+
// Fields broken under unnamed nodes are determined correctly.
2429+
let source = "def self.method_name
2430+
true
2431+
end";
2432+
assert_pretty_print(
2433+
"ruby",
2434+
source,
2435+
concat!(
2436+
"(singleton_method\n",
2437+
" object: (self)\n",
2438+
" name: (identifier)\n",
2439+
" body: (body_statement\n",
2440+
" (true)))"
2441+
),
2442+
0,
2443+
source.len(),
2444+
);
24102445
}
24112446

24122447
#[test]

0 commit comments

Comments
 (0)