Skip to main content

ktrs_syntax/
dump.rs

1use std::fmt::Write;
2
3use crate::{ElementId, Parse, SyntaxKind, Tree};
4
5/// Renders `parse` exactly like IntelliJ's `DebugUtil.psiToString(file, true, false)`,
6/// the format of the compiler's parser fixtures and of `tools/psi-dump`.
7pub 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
75/// `DebugUtil.fixWhiteSpaces`.
76fn escape(text: &str) -> String {
77    text.replace('\n', "\\n").replace('\r', "\\r").replace('\t', "\\t")
78}