Add syntax highlighting to editor and logs

Introduce a `highlight_myc` function to provide syntax highlighting for
the source code editor and trace logs, improving code readability.
This commit is contained in:
Michael Schimmel
2026-02-18 01:08:39 +01:00
parent 799f3a2123
commit 1d1b54ed16
+102 -1
View File
@@ -348,12 +348,18 @@ impl eframe::App for CompilerApp {
},
AppTab::Trace => {
let mut combined = self.trace_logs.join("\n");
let mut layouter = |ui: &egui::Ui, buffer: &dyn egui::TextBuffer, _wrap_width: f32| {
let layout_job = highlight_myc(ui.ctx(), buffer.as_str());
ui.painter().layout_job(layout_job)
};
ui.add_sized(
ui.available_size(),
egui::TextEdit::multiline(&mut combined)
.font(egui::TextStyle::Monospace)
.desired_width(f32::INFINITY)
.interactive(false)
.layouter(&mut layouter)
);
}
}
@@ -387,15 +393,110 @@ impl eframe::App for CompilerApp {
egui::ScrollArea::vertical()
.id_salt("source_code_scroll")
.show(ui, |ui| {
let mut layouter = |ui: &egui::Ui, buffer: &dyn egui::TextBuffer, _wrap_width: f32| {
let layout_job = highlight_myc(ui.ctx(), buffer.as_str());
ui.painter().layout_job(layout_job)
};
ui.add_sized(
ui.available_size(),
egui::TextEdit::multiline(&mut self.source_code)
.id(editor_id)
.font(egui::TextStyle::Monospace)
.desired_width(f32::INFINITY)
.lock_focus(true),
.lock_focus(true)
.layouter(&mut layouter),
);
});
});
}
}
fn highlight_myc(ctx: &egui::Context, code: &str) -> egui::text::LayoutJob {
let mut job = egui::text::LayoutJob::default();
let theme = &ctx.style().visuals;
let color_kw = egui::Color32::from_rgb(86, 156, 214); // Blue
let color_str = egui::Color32::from_rgb(206, 145, 120); // Orange/Brown
let color_num = egui::Color32::from_rgb(181, 206, 168); // Greenish
let color_comment = egui::Color32::from_rgb(106, 153, 85); // Green
let color_bracket = [
egui::Color32::from_rgb(255, 215, 0), // Gold
egui::Color32::from_rgb(218, 112, 214), // Orchid
egui::Color32::from_rgb(24, 131, 215), // Blue
];
let mut chars = code.chars().peekable();
let mut bracket_depth = 0;
while let Some(c) = chars.next() {
let mut text = String::from(c);
let mut color = theme.widgets.noninteractive.text_color();
match c {
';' => {
while let Some(&next) = chars.peek() {
if next == '\n' { break; }
text.push(chars.next().unwrap());
}
color = color_comment;
}
'"' => {
while let Some(next) = chars.next() {
text.push(next);
if next == '"' { break; }
if next == '\\' {
if let Some(escaped) = chars.next() { text.push(escaped); }
}
}
color = color_str;
}
':' => {
while let Some(&next) = chars.peek() {
if next.is_whitespace() || "()[]{}".contains(next) { break; }
text.push(chars.next().unwrap());
}
color = color_kw;
}
'(' | '[' | '{' => {
color = color_bracket[bracket_depth % 3];
bracket_depth += 1;
}
')' | ']' | '}' => {
if bracket_depth > 0 { bracket_depth -= 1; }
color = color_bracket[bracket_depth % 3];
}
'0'..='9' => {
while let Some(&next) = chars.peek() {
if next.is_ascii_digit() || next == '.' {
text.push(chars.next().unwrap());
} else { break; }
}
color = color_num;
}
_ if c.is_alphabetic() || "+-*/<=>!$%&_?.".contains(c) => {
while let Some(&next) = chars.peek() {
if next.is_alphanumeric() || "+-*/<=>!$%&_?.".contains(next) {
text.push(chars.next().unwrap());
} else { break; }
}
if ["fn", "def", "do", "if", "assign", "recur", "cond"].contains(&text.as_str()) {
color = color_kw;
}
}
_ => {}
}
job.append(
&text,
0.0,
egui::TextFormat {
font_id: egui::FontId::monospace(14.0),
color,
..Default::default()
},
);
}
job
}