1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use std::collections::HashMap;

use colored::*;
use itertools::Itertools;
use tree_sitter::{Node, TreeCursor};

pub fn useful_children<'a, 'tree>(
    node: &'a Node<'tree>,
    cursor: &'a mut TreeCursor<'tree>,
) -> impl Iterator<Item = Node<'tree>> + 'a {
    node.children(cursor).enumerate().filter_map(move |(i, n)| {
        if n.is_named() || node.field_name_for_child(i as u32).is_some() {
            Some(n)
        } else {
            None
        }
    })
}

#[allow(dead_code)]
fn dump_node_internal(
    node: &Node,
    prefix: &str,
    content: &str,
    field_name: Option<&str>,
    is_last: bool,
    is_init: bool,
) {
    let node_text = node.utf8_text(content.as_bytes()).unwrap();
    let start = node.start_position();
    let end = node.end_position();
    let kind = node.kind();
    println!(
        "{}{}{}: `{}` {} - {}{}",
        prefix,
        if is_init {
            ""
        } else if is_last {
            "└──"
        } else {
            "├──"
        },
        match field_name {
            Some(name) => name.bold().yellow(),
            None => "[ANON]".normal(),
        },
        kind.bold(),
        start,
        end,
        if node.child_count() == 0 || !node_text.contains('\n') {
            format!(" {} {}", "->".cyan(), node_text.bold()).bold()
        } else {
            "".to_owned().normal()
        }
    );
    let node_to_idx: HashMap<_, _> = {
        let mut cursor = node.walk();
        node.children(&mut cursor)
            .enumerate()
            .map(|(x, y)| (y, x))
            .collect()
    };
    let nodes: Vec<_> = {
        let mut cursor = node.walk();
        useful_children(node, &mut cursor).collect_vec()
    };
    let prefix = format!("{}{}   ", prefix, if is_last { " " } else { "│" });
    for i in nodes.into_iter().with_position() {
        match i {
            itertools::Position::First(n) | itertools::Position::Middle(n) => {
                dump_node_internal(
                    &n,
                    &prefix,
                    content,
                    node.field_name_for_child(node_to_idx[&n] as u32),
                    false,
                    false,
                );
            }
            itertools::Position::Last(n) | itertools::Position::Only(n) => {
                dump_node_internal(
                    &n,
                    &prefix,
                    content,
                    node.field_name_for_child(node_to_idx[&n] as u32),
                    true,
                    false,
                );
            }
        }
    }
}

#[allow(dead_code)]
pub fn dump_node(node: &Node, content: &str) {
    dump_node_internal(node, "", content, None, true, true);
}