Skip to main content

cairo_lang_parser/
colored_printer.rs

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