rl_lang/repl/
syntax_highlighting.rs1use ratatui::{
27 style::{Color, Modifier, Style},
28 text::Span,
29};
30
31use crate::{
32 lexer::{tokenizer::Tokenizer, tokentypes::TokenType},
33 utils::source::SourceFile,
34};
35
36fn token_color(tt: &TokenType) -> Style {
38 match tt {
39 TokenType::If
41 | TokenType::Else
42 | TokenType::While
43 | TokenType::For
44 | TokenType::Break
45 | TokenType::Continue
46 | TokenType::Return => Style::default()
47 .fg(Color::Cyan)
48 .add_modifier(Modifier::BOLD),
49
50 TokenType::Dec | TokenType::Const | TokenType::Fn | TokenType::Array => Style::default()
52 .fg(Color::Cyan)
53 .add_modifier(Modifier::ITALIC),
54
55 TokenType::Get | TokenType::From => {
57 Style::default().fg(Color::Cyan).add_modifier(Modifier::DIM)
58 }
59
60 TokenType::Int
62 | TokenType::Float
63 | TokenType::Bool
64 | TokenType::String
65 | TokenType::Char
66 | TokenType::Byte
67 | TokenType::Error => Style::default()
68 .fg(Color::LightBlue)
69 .add_modifier(Modifier::ITALIC),
70
71 TokenType::Or | TokenType::And => Style::default()
73 .fg(Color::Yellow)
74 .add_modifier(Modifier::BOLD),
75
76 TokenType::NumberLiteral(_) | TokenType::FloatLiteral(_) => {
78 Style::default().fg(Color::Magenta)
79 }
80
81 TokenType::StringLiteral(_) => Style::default().fg(Color::Yellow),
83
84 TokenType::CharacterLiteral(_) => Style::default().fg(Color::LightYellow),
86
87 TokenType::BoolLiteral(_) => Style::default()
89 .fg(Color::Magenta)
90 .add_modifier(Modifier::ITALIC),
91
92 TokenType::Null => Style::default()
94 .fg(Color::DarkGray)
95 .add_modifier(Modifier::ITALIC),
96
97 TokenType::Arrow => Style::default()
99 .fg(Color::White)
100 .add_modifier(Modifier::DIM),
101
102 TokenType::Plus
104 | TokenType::Minus
105 | TokenType::Star
106 | TokenType::Slash
107 | TokenType::Bang
108 | TokenType::PlusEqual
109 | TokenType::MinusEqual
110 | TokenType::StarEqual
111 | TokenType::SlashEqual => Style::default().fg(Color::White),
112
113 TokenType::Compare
115 | TokenType::BangEqual
116 | TokenType::Greater
117 | TokenType::GreaterEqual
118 | TokenType::Less
119 | TokenType::LessEqual => Style::default().fg(Color::LightCyan),
120
121 TokenType::Assign => Style::default().fg(Color::White),
123
124 TokenType::Comma
126 | TokenType::Semicolon
127 | TokenType::Colon
128 | TokenType::ColonColon
129 | TokenType::Dot
130 | TokenType::DotDot
131 | TokenType::Hash
132 | TokenType::BangHash => Style::default().fg(Color::DarkGray),
133
134 TokenType::LeftBrace
136 | TokenType::RightBrace
137 | TokenType::LeftParen
138 | TokenType::RightParen
139 | TokenType::LeftBracket
140 | TokenType::RightBracket => Style::default().fg(Color::DarkGray),
141
142 TokenType::Identifier(_) => Style::default().fg(Color::White),
144
145 _ => Style::default().fg(Color::White),
146 }
147}
148
149pub fn highlight(input: &str) -> Vec<Span<'static>> {
153 let source = SourceFile::new("<hl>", input.to_string());
154 let tokens = match Tokenizer::lex(source) {
155 Ok(t) => t,
156 Err(_) => {
157 return vec![Span::styled(
158 input.to_string(),
159 Style::default().fg(Color::Red),
160 )];
161 }
162 };
163
164 let mut spans = Vec::new();
165 let mut last = 0usize;
166 let chars: Vec<char> = input.chars().collect();
167
168 for tok in &tokens {
169 let start = tok.span.start;
170 let end = tok.span.end;
171
172 if start > last {
173 let gap: String = chars[last..start].iter().collect();
174 spans.push(Span::raw(gap));
175 }
176
177 let text: String = chars[start..end].iter().collect();
178 let style = token_color(&tok.token); spans.push(Span::styled(text, style));
180 last = end;
181 }
182
183 if last < chars.len() {
184 let tail: String = chars[last..].iter().collect();
185 spans.push(Span::raw(tail));
186 }
187
188 spans
189}