cairo_lang_parser/
colored_printer.rs

1use cairo_lang_syntax::node::SyntaxNode;
2use cairo_lang_syntax::node::db::SyntaxGroup;
3use cairo_lang_syntax::node::green::GreenNodeDetails;
4use cairo_lang_syntax::node::kind::SyntaxKind;
5use colored::{ColoredString, Colorize};
6use smol_str::SmolStr;
7
8struct ColoredPrinter<'a> {
9    db: &'a dyn SyntaxGroup,
10    /// Whether to also print empty and missing tokens/nodes
11    verbose: bool,
12    result: String,
13}
14impl ColoredPrinter<'_> {
15    fn print(&mut self, syntax_node: &SyntaxNode) {
16        let node = syntax_node.green_node(self.db);
17        match &node.details {
18            GreenNodeDetails::Token(text) => {
19                if self.verbose && node.kind == SyntaxKind::TokenMissing {
20                    self.result.push_str(format!("{}", "<m>".red()).as_str());
21                } else {
22                    self.result.push_str(set_color(text.clone(), node.kind).to_string().as_str());
23                }
24            }
25            GreenNodeDetails::Node { .. } => {
26                if self.verbose && is_missing_kind(node.kind) {
27                    self.result.push_str(format!("{}", "<m>".red()).as_str());
28                } else if self.verbose && is_empty_kind(node.kind) {
29                    self.result.push_str(format!("{}", "<e>".red()).as_str());
30                } else {
31                    for child in self.db.get_children(syntax_node.clone()).iter() {
32                        self.print(child);
33                    }
34                }
35            }
36        }
37    }
38}
39
40// TODO(yuval): autogenerate both.
41fn is_missing_kind(kind: SyntaxKind) -> bool {
42    matches!(kind, SyntaxKind::ExprMissing | SyntaxKind::StatementMissing)
43}
44
45// TODO(yuval): Move to SyntaxKind.
46pub fn is_empty_kind(kind: SyntaxKind) -> bool {
47    matches!(
48        kind,
49        SyntaxKind::OptionStructArgExprEmpty
50            | SyntaxKind::OptionTypeClauseEmpty
51            | SyntaxKind::OptionReturnTypeClauseEmpty
52            | SyntaxKind::OptionTerminalSemicolonEmpty
53            | SyntaxKind::OptionTerminalColonColonEmpty
54            | SyntaxKind::OptionWrappedGenericParamListEmpty
55    )
56}
57
58fn set_color(text: SmolStr, kind: SyntaxKind) -> ColoredString {
59    // TODO(yuval): use tags on SyntaxKind
60    match kind {
61        SyntaxKind::TokenIdentifier => text.truecolor(255, 255, 100), // Yellow
62        SyntaxKind::TokenPlus
63        | SyntaxKind::TokenMinus
64        | SyntaxKind::TokenMul
65        | SyntaxKind::TokenDiv
66        | SyntaxKind::TokenMod
67        | SyntaxKind::TokenDot => text.bright_magenta(),
68        SyntaxKind::TokenLiteralNumber
69        | SyntaxKind::TokenFalse
70        | SyntaxKind::TokenTrue
71        | SyntaxKind::TokenShortString
72        | SyntaxKind::TokenString => text.bright_cyan(),
73        SyntaxKind::TokenExtern
74        | SyntaxKind::TokenType
75        | SyntaxKind::TokenFunction
76        | SyntaxKind::TokenModule
77        | SyntaxKind::TokenEnum
78        | SyntaxKind::TokenStruct
79        | SyntaxKind::TokenTrait
80        | SyntaxKind::TokenImpl => text.bright_blue(),
81        SyntaxKind::TokenOf
82        | SyntaxKind::TokenLet
83        | SyntaxKind::TokenReturn
84        | SyntaxKind::TokenMatch
85        | SyntaxKind::TokenIf
86        | SyntaxKind::TokenElse
87        | SyntaxKind::TokenUse
88        | SyntaxKind::TokenImplicits
89        | SyntaxKind::TokenRef
90        | SyntaxKind::TokenMut
91        | SyntaxKind::TokenNoPanic => text.bright_blue(),
92        SyntaxKind::TokenArrow
93        | SyntaxKind::TokenMatchArrow
94        | SyntaxKind::TokenColon
95        | SyntaxKind::TokenColonColon
96        | SyntaxKind::TokenDotDot
97        | SyntaxKind::TokenDotDotEq
98        | SyntaxKind::TokenSemicolon
99        | SyntaxKind::TokenAnd
100        | SyntaxKind::TokenAndAnd
101        | SyntaxKind::TokenOr
102        | SyntaxKind::TokenOrOr
103        | SyntaxKind::TokenXor
104        | SyntaxKind::TokenNot
105        | SyntaxKind::TokenQuestionMark
106        | SyntaxKind::TokenUnderscore
107        | SyntaxKind::TokenHash => text.truecolor(255, 180, 255), // Pink
108        SyntaxKind::TokenEq
109        | SyntaxKind::TokenEqEq
110        | SyntaxKind::TokenGE
111        | SyntaxKind::TokenGT
112        | SyntaxKind::TokenLE
113        | SyntaxKind::TokenLT
114        | SyntaxKind::TokenNeq => {
115            text.truecolor(255, 165, 0) // Orange
116        }
117        SyntaxKind::TokenLBrace
118        | SyntaxKind::TokenRBrace
119        | SyntaxKind::TokenLBrack
120        | SyntaxKind::TokenRBrack
121        | SyntaxKind::TokenLParen
122        | SyntaxKind::TokenRParen
123        | SyntaxKind::TokenComma => text.clear(),
124        SyntaxKind::TokenEndOfFile => text.clear(),
125        SyntaxKind::TokenBadCharacters => text.red(),
126        SyntaxKind::TokenMissing => text.clear(),
127        SyntaxKind::TokenSkipped => text.on_red(), // red background
128        SyntaxKind::TokenSingleLineComment
129        | SyntaxKind::TokenWhitespace
130        | SyntaxKind::TokenNewline
131        | SyntaxKind::TokenEmpty => text.clear(),
132        // TODO(yuval): Can this be made exhaustive?
133        _ => panic!("Unexpected syntax kind: {kind:?}"),
134    }
135}
136
137pub fn print_colored(db: &dyn SyntaxGroup, syntax_root: &SyntaxNode, verbose: bool) -> String {
138    let mut printer = ColoredPrinter { db, verbose, result: Default::default() };
139    printer.print(syntax_root);
140    printer.result
141}