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:
+102
-1
@@ -348,12 +348,18 @@ impl eframe::App for CompilerApp {
|
|||||||
},
|
},
|
||||||
AppTab::Trace => {
|
AppTab::Trace => {
|
||||||
let mut combined = self.trace_logs.join("\n");
|
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.add_sized(
|
||||||
ui.available_size(),
|
ui.available_size(),
|
||||||
egui::TextEdit::multiline(&mut combined)
|
egui::TextEdit::multiline(&mut combined)
|
||||||
.font(egui::TextStyle::Monospace)
|
.font(egui::TextStyle::Monospace)
|
||||||
.desired_width(f32::INFINITY)
|
.desired_width(f32::INFINITY)
|
||||||
.interactive(false)
|
.interactive(false)
|
||||||
|
.layouter(&mut layouter)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -387,15 +393,110 @@ impl eframe::App for CompilerApp {
|
|||||||
egui::ScrollArea::vertical()
|
egui::ScrollArea::vertical()
|
||||||
.id_salt("source_code_scroll")
|
.id_salt("source_code_scroll")
|
||||||
.show(ui, |ui| {
|
.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.add_sized(
|
||||||
ui.available_size(),
|
ui.available_size(),
|
||||||
egui::TextEdit::multiline(&mut self.source_code)
|
egui::TextEdit::multiline(&mut self.source_code)
|
||||||
.id(editor_id)
|
.id(editor_id)
|
||||||
.font(egui::TextStyle::Monospace)
|
.font(egui::TextStyle::Monospace)
|
||||||
.desired_width(f32::INFINITY)
|
.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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user