1use std::fmt::Write;
2
3use crate::{ElementId, Parse, SyntaxKind, Tree};
4
5pub fn psi_dump(parse: &Parse, file_name: &str) -> String {
8 let mut printer = Printer::new(parse, file_name);
9 printer.children(&parse.tree, Tree::ROOT, 1);
10 printer.finish()
11}
12
13struct Printer<'a> {
14 out: String,
15 errors: std::slice::Iter<'a, String>,
16}
17
18impl<'a> Printer<'a> {
19 fn new(parse: &'a Parse, file_name: &str) -> Printer<'a> {
20 let mut out = String::new();
21 writeln!(out, "KtFile: {file_name}").unwrap();
22 Printer { out, errors: parse.error_messages.iter() }
23 }
24
25 fn finish(mut self) -> String {
26 self.out.truncate(self.out.trim_end().len());
27 self.out
28 }
29
30 fn children(&mut self, tree: &Tree, e: ElementId, depth: usize) {
31 let mut any = false;
32 for child in tree.children(e) {
33 any = true;
34 if tree.is_token(child) {
35 self.token(tree.kind(child), tree.text_of(child), depth);
36 } else {
37 self.node(tree.kind(child), depth);
38 self.children(tree, child, depth + 1);
39 }
40 }
41 if !any {
42 self.line(depth, format_args!("<empty list>"));
43 }
44 }
45
46 fn token(&mut self, kind: SyntaxKind, text: &str, depth: usize) {
47 let text = escape(text);
48 match kind {
49 SyntaxKind::WHITE_SPACE => self.line(depth, format_args!("PsiWhiteSpace('{text}')")),
50 SyntaxKind::EOL_COMMENT | SyntaxKind::BLOCK_COMMENT | SyntaxKind::SHEBANG_COMMENT => {
51 self.line(depth, format_args!("PsiComment({})('{text}')", kind.debug_name()))
52 }
53 _ => self.line(depth, format_args!("PsiElement({})('{text}')", kind.debug_name())),
54 }
55 }
56
57 fn node(&mut self, kind: SyntaxKind, depth: usize) {
58 if kind == SyntaxKind::ERROR_ELEMENT {
59 let message = self.errors.next().map_or("", String::as_str);
60 self.line(depth, format_args!("PsiErrorElement:{message}"));
61 } else {
62 self.line(depth, format_args!("{}", kind.debug_name()));
63 }
64 }
65
66 fn line(&mut self, depth: usize, content: std::fmt::Arguments) {
67 for _ in 0..depth {
68 self.out.push_str(" ");
69 }
70 self.out.write_fmt(content).unwrap();
71 self.out.push('\n');
72 }
73}
74
75fn escape(text: &str) -> String {
77 text.replace('\n', "\\n").replace('\r', "\\r").replace('\t', "\\t")
78}