Refactor UI layout and shortcuts
The UI layout has been significantly refactored to use `egui::TopBottomPanel` and `egui::SidePanel` for a more organized structure. A new menu bar has been introduced with File, Build, and Tools menus, along with a toolbar for quick access to common actions. Keyboard shortcuts have been updated and consolidated: - `Shift + Enter` now compiles the code and is accessible from anywhere in the app. - `Ctrl + S` saves the current file. The `Save As` functionality has been moved into a dedicated group within the bottom panel and is now triggered by a button in the File menu. The output log and trace logs now use `egui::ScrollArea::both` for better scrolling experience. Minor adjustments to default panel heights and scroll area behaviors have been made for improved usability.
This commit is contained in:
+260
-135
@@ -51,14 +51,19 @@ impl Default for CompilerApp {
|
|||||||
let path = e.path();
|
let path = e.path();
|
||||||
let name = e.file_name().into_string().ok()?;
|
let name = e.file_name().into_string().ok()?;
|
||||||
let content = std::fs::read_to_string(&path).ok()?;
|
let content = std::fs::read_to_string(&path).ok()?;
|
||||||
Some(Example { name, path, content })
|
Some(Example {
|
||||||
|
name,
|
||||||
|
path,
|
||||||
|
content,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
source_code: String::from(r#"
|
source_code: String::from(
|
||||||
|
r#"
|
||||||
(do
|
(do
|
||||||
(def fib (fn [n]
|
(def fib (fn [n]
|
||||||
(if (< n 2)
|
(if (< n 2)
|
||||||
@@ -67,11 +72,12 @@ impl Default for CompilerApp {
|
|||||||
|
|
||||||
(fib 10)
|
(fib 10)
|
||||||
)
|
)
|
||||||
"#),
|
"#,
|
||||||
|
),
|
||||||
current_example_path: None,
|
current_example_path: None,
|
||||||
save_as_name: String::new(),
|
save_as_name: String::new(),
|
||||||
show_save_as: false,
|
show_save_as: false,
|
||||||
output_log: String::from("Ready to compile..."),
|
output_log: String::from(""),
|
||||||
trace_logs: Vec::new(),
|
trace_logs: Vec::new(),
|
||||||
active_tab: AppTab::Output,
|
active_tab: AppTab::Output,
|
||||||
debug_enabled: false,
|
debug_enabled: false,
|
||||||
@@ -86,10 +92,10 @@ impl CompilerApp {
|
|||||||
fn compile(&mut self) {
|
fn compile(&mut self) {
|
||||||
let start_total = std::time::Instant::now();
|
let start_total = std::time::Instant::now();
|
||||||
self.trace_logs.clear();
|
self.trace_logs.clear();
|
||||||
|
|
||||||
// Reset environment for a fresh run
|
// Reset environment for a fresh run
|
||||||
self.env = Environment::new();
|
self.env = Environment::new();
|
||||||
|
|
||||||
let res = (|| -> Result<(myc::ast::types::Value, std::time::Duration, std::time::Duration, std::time::Duration), String> {
|
let res = (|| -> Result<(myc::ast::types::Value, std::time::Duration, std::time::Duration, std::time::Duration), String> {
|
||||||
let start_compile = std::time::Instant::now();
|
let start_compile = std::time::Instant::now();
|
||||||
let compiled = self.env.compile(&self.source_code)?;
|
let compiled = self.env.compile(&self.source_code)?;
|
||||||
@@ -104,14 +110,14 @@ impl CompilerApp {
|
|||||||
let mut vm = myc::ast::vm::VM::new(self.env.global_values.clone());
|
let mut vm = myc::ast::vm::VM::new(self.env.global_values.clone());
|
||||||
let mut observer = myc::ast::vm::TracingObserver::new();
|
let mut observer = myc::ast::vm::TracingObserver::new();
|
||||||
let res = vm.run_with_observer(&mut observer, &linked);
|
let res = vm.run_with_observer(&mut observer, &linked);
|
||||||
|
|
||||||
// Batch-truncate logs for GUI performance (once after execution)
|
// Batch-truncate logs for GUI performance (once after execution)
|
||||||
let mut logs = observer.logs;
|
let mut logs = observer.logs;
|
||||||
if logs.len() > 1000 {
|
if logs.len() > 1000 {
|
||||||
logs.truncate(1000);
|
logs.truncate(1000);
|
||||||
logs.push("... [Trace truncated to 1,000 entries] ...".to_string());
|
logs.push("... [Trace truncated to 1,000 entries] ...".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
for line in &mut logs {
|
for line in &mut logs {
|
||||||
if line.len() > 255
|
if line.len() > 255
|
||||||
&& let Some((idx, _)) = line.char_indices().nth(255) {
|
&& let Some((idx, _)) = line.char_indices().nth(255) {
|
||||||
@@ -137,12 +143,7 @@ impl CompilerApp {
|
|||||||
let now = chrono::Local::now().format("%H:%M:%S").to_string();
|
let now = chrono::Local::now().format("%H:%M:%S").to_string();
|
||||||
self.output_log = format!(
|
self.output_log = format!(
|
||||||
"Execution Successful.\nResult: {}\n\nPhases:\n - Compile: {:?}\n - Link: {:?}\n - Run: {:?}\n\nTotal Duration: {:?}\nFinished at: {}",
|
"Execution Successful.\nResult: {}\n\nPhases:\n - Compile: {:?}\n - Link: {:?}\n - Run: {:?}\n\nTotal Duration: {:?}\nFinished at: {}",
|
||||||
val,
|
val, d_compile, d_link, d_run, d_total, now,
|
||||||
d_compile,
|
|
||||||
d_link,
|
|
||||||
d_run,
|
|
||||||
d_total,
|
|
||||||
now,
|
|
||||||
);
|
);
|
||||||
if !self.debug_enabled {
|
if !self.debug_enabled {
|
||||||
self.active_tab = AppTab::Output;
|
self.active_tab = AppTab::Output;
|
||||||
@@ -159,10 +160,7 @@ impl CompilerApp {
|
|||||||
self.env = Environment::new();
|
self.env = Environment::new();
|
||||||
match self.env.dump_ast(&self.source_code) {
|
match self.env.dump_ast(&self.source_code) {
|
||||||
Ok(dump) => {
|
Ok(dump) => {
|
||||||
self.output_log = format!(
|
self.output_log = format!("--- BOUND AST DUMP ---\n\n{}", dump);
|
||||||
"--- BOUND AST DUMP ---\n\n{}",
|
|
||||||
dump
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.output_log = format!("Error during AST Binding: {}", e);
|
self.output_log = format!("Error during AST Binding: {}", e);
|
||||||
@@ -224,7 +222,11 @@ impl CompilerApp {
|
|||||||
let path = e.path();
|
let path = e.path();
|
||||||
let name = e.file_name().into_string().ok()?;
|
let name = e.file_name().into_string().ok()?;
|
||||||
let content = std::fs::read_to_string(&path).ok()?;
|
let content = std::fs::read_to_string(&path).ok()?;
|
||||||
Some(Example { name, path, content })
|
Some(Example {
|
||||||
|
name,
|
||||||
|
path,
|
||||||
|
content,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
@@ -236,8 +238,15 @@ impl CompilerApp {
|
|||||||
let mut log = String::from("FUNCTIONAL TEST RESULTS:\n\n");
|
let mut log = String::from("FUNCTIONAL TEST RESULTS:\n\n");
|
||||||
let mut passed = 0;
|
let mut passed = 0;
|
||||||
for res in &results {
|
for res in &results {
|
||||||
log.push_str(&format!("{}: {} - {}\n", if res.success { "✅" } else { "❌" }, res.name, res.message));
|
log.push_str(&format!(
|
||||||
if res.success { passed += 1; }
|
"{}: {} - {}\n",
|
||||||
|
if res.success { "✅" } else { "❌" },
|
||||||
|
res.name,
|
||||||
|
res.message
|
||||||
|
));
|
||||||
|
if res.success {
|
||||||
|
passed += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
log.push_str(&format!("\nTotal: {}/{}", passed, results.len()));
|
log.push_str(&format!("\nTotal: {}/{}", passed, results.len()));
|
||||||
self.output_log = log;
|
self.output_log = log;
|
||||||
@@ -246,17 +255,28 @@ impl CompilerApp {
|
|||||||
fn run_benchmarks(&mut self, update: bool) {
|
fn run_benchmarks(&mut self, update: bool) {
|
||||||
use myc::utils::tester;
|
use myc::utils::tester;
|
||||||
if cfg!(debug_assertions) && !update {
|
if cfg!(debug_assertions) && !update {
|
||||||
self.output_log = String::from("⚠️ WARNING: Benchmarks in Debug mode are inaccurate!\nUse Release build for real measurements.\n\n");
|
self.output_log = String::from(
|
||||||
|
"⚠️ WARNING: Benchmarks in Debug mode are inaccurate!\nUse Release build for real measurements.\n\n",
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
self.output_log = String::from(if update { "UPDATING BASELINES...\n\n" } else { "RUNNING BENCHMARKS...\n\n" });
|
self.output_log = String::from(if update {
|
||||||
|
"UPDATING BASELINES...\n\n"
|
||||||
|
} else {
|
||||||
|
"RUNNING BENCHMARKS...\n\n"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let results = tester::run_benchmarks(update);
|
let results = tester::run_benchmarks(update);
|
||||||
let mut log = self.output_log.clone();
|
let mut log = self.output_log.clone();
|
||||||
|
|
||||||
for res in results {
|
for res in results {
|
||||||
let diff = res.diff_pct.map_or(String::new(), |d| format!(" ({:+.1}%)", d));
|
let diff = res
|
||||||
log.push_str(&format!("{}: {} - {:?}{}\n", res.status, res.name, res.median, diff));
|
.diff_pct
|
||||||
|
.map_or(String::new(), |d| format!(" ({:+.1}%)", d));
|
||||||
|
log.push_str(&format!(
|
||||||
|
"{}: {} - {:?}{}\n",
|
||||||
|
res.status, res.name, res.median, diff
|
||||||
|
));
|
||||||
}
|
}
|
||||||
self.output_log = log;
|
self.output_log = log;
|
||||||
}
|
}
|
||||||
@@ -264,6 +284,123 @@ impl CompilerApp {
|
|||||||
|
|
||||||
impl eframe::App for CompilerApp {
|
impl eframe::App for CompilerApp {
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
|
// Top Panel: Menu Bar & Toolbar
|
||||||
|
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||||
|
egui::MenuBar::new().ui(ui, |ui: &mut egui::Ui| {
|
||||||
|
ui.menu_button("File", |ui: &mut egui::Ui| {
|
||||||
|
let file_name = self
|
||||||
|
.current_example_path
|
||||||
|
.as_ref()
|
||||||
|
.map(|p| {
|
||||||
|
p.file_name()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned()
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| "None".to_string());
|
||||||
|
|
||||||
|
if ui.button(format!("Save {} (Ctrl+S)", file_name)).clicked() {
|
||||||
|
self.save();
|
||||||
|
ui.close();
|
||||||
|
}
|
||||||
|
if ui.button("Save As...").clicked() {
|
||||||
|
self.show_save_as = true;
|
||||||
|
ui.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.menu_button("Build", |ui: &mut egui::Ui| {
|
||||||
|
if ui.button("Compile (Shift+Enter)").clicked() {
|
||||||
|
self.compile();
|
||||||
|
ui.close();
|
||||||
|
}
|
||||||
|
if ui.button("Dump AST").clicked() {
|
||||||
|
self.dump_ast();
|
||||||
|
ui.close();
|
||||||
|
}
|
||||||
|
ui.separator();
|
||||||
|
ui.checkbox(&mut self.debug_enabled, "Debug Mode");
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.menu_button("Tools", |ui: &mut egui::Ui| {
|
||||||
|
if ui.button("Test All").clicked() {
|
||||||
|
self.run_all_tests();
|
||||||
|
ui.close();
|
||||||
|
}
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
let is_debug = cfg!(debug_assertions);
|
||||||
|
ui.add_enabled_ui(!is_debug, |ui: &mut egui::Ui| {
|
||||||
|
let btn = ui.button("Run Benchmarks");
|
||||||
|
if is_debug {
|
||||||
|
btn.on_hover_text("Benchmarks are only available in Release builds.");
|
||||||
|
} else if btn.clicked() {
|
||||||
|
self.run_benchmarks(false);
|
||||||
|
ui.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
let btn_base = ui.button("Update Baselines");
|
||||||
|
if is_debug {
|
||||||
|
btn_base.on_hover_text(
|
||||||
|
"Updating baselines is only allowed in Release builds.",
|
||||||
|
);
|
||||||
|
} else if btn_base.clicked() {
|
||||||
|
self.run_benchmarks(true);
|
||||||
|
ui.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
// Toolbar (Speed Buttons)
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.style_mut().spacing.item_spacing.x = 4.0; // tighter spacing for speed buttons
|
||||||
|
|
||||||
|
if ui
|
||||||
|
.button("▶ Run")
|
||||||
|
.on_hover_text("Compile and Run (Shift+Enter)")
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui
|
||||||
|
.button("💾 Save")
|
||||||
|
.on_hover_text("Save current file (Ctrl+S)")
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
if ui
|
||||||
|
.button("🔍 AST")
|
||||||
|
.on_hover_text("Dump Bound AST")
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.dump_ast();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
ui.toggle_value(&mut self.debug_enabled, "🐛 Debug");
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
if ui
|
||||||
|
.button("🧪 Test")
|
||||||
|
.on_hover_text("Run all functional tests")
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.run_all_tests();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui.add_space(2.0);
|
||||||
|
});
|
||||||
|
|
||||||
// Left Side Panel: Examples
|
// Left Side Panel: Examples
|
||||||
egui::SidePanel::left("examples_panel")
|
egui::SidePanel::left("examples_panel")
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
@@ -271,84 +408,39 @@ impl eframe::App for CompilerApp {
|
|||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
ui.heading("Examples");
|
ui.heading("Examples");
|
||||||
ui.add_space(5.0);
|
ui.add_space(5.0);
|
||||||
for example in &self.examples {
|
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||||
if ui.button(&example.name).clicked() {
|
ui.with_layout(egui::Layout::top_down_justified(egui::Align::LEFT), |ui| {
|
||||||
self.source_code = example.content.clone();
|
for example in &self.examples {
|
||||||
self.current_example_path = Some(example.path.clone());
|
if ui.button(&example.name).clicked() {
|
||||||
}
|
self.source_code = example.content.clone();
|
||||||
}
|
self.current_example_path = Some(example.path.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Bottom Panel: Output Log & Controls (Resizable)
|
// Bottom Panel: Output Log & Controls (Resizable)
|
||||||
egui::TopBottomPanel::bottom("bottom_panel")
|
egui::TopBottomPanel::bottom("bottom_panel")
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
.min_height(150.0)
|
.min_height(100.0)
|
||||||
.default_height(250.0)
|
.default_height(200.0)
|
||||||
|
.max_height(ctx.available_rect().height() * 0.5)
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
ui.add_space(5.0);
|
ui.add_space(5.0);
|
||||||
|
|
||||||
// Compile Button (at the top of the bottom panel)
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
if ui.button("Compile (Shift+Enter)").clicked() {
|
|
||||||
self.compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.checkbox(&mut self.debug_enabled, "Debug Mode");
|
|
||||||
|
|
||||||
if ui.button("Dump AST").clicked() {
|
|
||||||
self.dump_ast();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(path) = &self.current_example_path {
|
|
||||||
let file_name = path.file_name().unwrap_or_default().to_string_lossy();
|
|
||||||
if ui.button(format!("Save {} (Ctrl+S)", file_name)).clicked() {
|
|
||||||
self.save();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ui.add_enabled_ui(false, |ui| {
|
|
||||||
let _ = ui.button("Save (None loaded)");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if ui.button("Save As...").clicked() {
|
|
||||||
self.show_save_as = !self.show_save_as;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ui.button("Test All").clicked() {
|
|
||||||
self.run_all_tests();
|
|
||||||
}
|
|
||||||
|
|
||||||
let is_debug = cfg!(debug_assertions);
|
|
||||||
|
|
||||||
ui.add_enabled_ui(!is_debug, |ui| {
|
|
||||||
let btn = ui.button("Run Benchmarks");
|
|
||||||
if is_debug {
|
|
||||||
btn.on_hover_text("Benchmarks are only available in Release builds for accuracy.");
|
|
||||||
} else if btn.clicked() {
|
|
||||||
self.run_benchmarks(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.add_enabled_ui(!is_debug, |ui| {
|
|
||||||
let btn = ui.button("Update Baselines");
|
|
||||||
if is_debug {
|
|
||||||
btn.on_hover_text("Updating baselines is only allowed in Release builds.");
|
|
||||||
} else if btn.clicked() {
|
|
||||||
self.run_benchmarks(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if self.show_save_as {
|
if self.show_save_as {
|
||||||
ui.horizontal(|ui| {
|
ui.group(|ui| {
|
||||||
ui.label("New Filename:");
|
ui.horizontal(|ui| {
|
||||||
ui.text_edit_singleline(&mut self.save_as_name);
|
ui.label("New Filename:");
|
||||||
if ui.button("Save Now").clicked() {
|
ui.text_edit_singleline(&mut self.save_as_name);
|
||||||
self.save_as();
|
if ui.button("Save Now").clicked() {
|
||||||
}
|
self.save_as();
|
||||||
if ui.button("Cancel").clicked() {
|
}
|
||||||
self.show_save_as = false;
|
if ui.button("Cancel").clicked() {
|
||||||
}
|
self.show_save_as = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
ui.add_space(5.0);
|
ui.add_space(5.0);
|
||||||
}
|
}
|
||||||
@@ -365,33 +457,48 @@ impl eframe::App for CompilerApp {
|
|||||||
};
|
};
|
||||||
ui.ctx().copy_text(text);
|
ui.ctx().copy_text(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ui.button("Clear Log").clicked() {
|
||||||
|
self.output_log.clear();
|
||||||
|
self.trace_logs.clear();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ui.add_space(5.0);
|
||||||
|
|
||||||
// Log Output Area
|
// Log Output Area
|
||||||
egui::ScrollArea::vertical()
|
match self.active_tab {
|
||||||
.id_salt("output_log_scroll")
|
AppTab::Output => {
|
||||||
.auto_shrink([false, true]) // Don't shrink horizontally, fill width
|
egui::ScrollArea::both()
|
||||||
.show(ui, |ui| {
|
.id_salt("output_log_scroll")
|
||||||
match self.active_tab {
|
.auto_shrink(false)
|
||||||
AppTab::Output => {
|
.show(ui, |ui| {
|
||||||
ui.add_sized(
|
ui.add(
|
||||||
ui.available_size(),
|
|
||||||
egui::TextEdit::multiline(&mut self.output_log)
|
egui::TextEdit::multiline(&mut self.output_log)
|
||||||
.font(egui::TextStyle::Monospace)
|
.font(egui::TextStyle::Monospace)
|
||||||
.desired_width(f32::INFINITY)
|
.hint_text("Ready to compile...")
|
||||||
.interactive(true)
|
.code_editor()
|
||||||
|
.lock_focus(false)
|
||||||
|
.frame(false)
|
||||||
|
.desired_width(f32::INFINITY),
|
||||||
);
|
);
|
||||||
},
|
});
|
||||||
AppTab::Trace => {
|
}
|
||||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
AppTab::Trace => {
|
||||||
ui.with_layout(egui::Layout::top_down_justified(egui::Align::LEFT), |ui| {
|
egui::ScrollArea::both()
|
||||||
|
.id_salt("trace_scroll")
|
||||||
|
.auto_shrink(false)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.style_mut().override_text_style =
|
||||||
|
Some(egui::TextStyle::Monospace);
|
||||||
|
ui.with_layout(egui::Layout::top_down(egui::Align::LEFT), |ui| {
|
||||||
for line in &self.trace_logs {
|
for line in &self.trace_logs {
|
||||||
ui.label(line);
|
ui.label(line);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Central Panel: Source Code Editor (Fills remaining space)
|
// Central Panel: Source Code Editor (Fills remaining space)
|
||||||
@@ -402,7 +509,8 @@ impl eframe::App for CompilerApp {
|
|||||||
let editor_id = ui.id().with("source_code_editor");
|
let editor_id = ui.id().with("source_code_editor");
|
||||||
|
|
||||||
// Handle Shift+Enter shortcut BEFORE the editor handles it
|
// Handle Shift+Enter shortcut BEFORE the editor handles it
|
||||||
let compile_shortcut = egui::KeyboardShortcut::new(egui::Modifiers::SHIFT, egui::Key::Enter);
|
let compile_shortcut =
|
||||||
|
egui::KeyboardShortcut::new(egui::Modifiers::SHIFT, egui::Key::Enter);
|
||||||
if ui.input_mut(|i| i.consume_shortcut(&compile_shortcut)) {
|
if ui.input_mut(|i| i.consume_shortcut(&compile_shortcut)) {
|
||||||
self.compile();
|
self.compile();
|
||||||
}
|
}
|
||||||
@@ -418,21 +526,24 @@ impl eframe::App for CompilerApp {
|
|||||||
self.is_first_frame = false;
|
self.is_first_frame = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
egui::ScrollArea::vertical()
|
let mut layouter = |ui: &egui::Ui, buffer: &dyn egui::TextBuffer, _wrap_width: f32| {
|
||||||
.id_salt("source_code_scroll")
|
let mut layout_job = highlight_myc(ui.ctx(), buffer.as_str());
|
||||||
.show(ui, |ui| {
|
layout_job.wrap.max_width = f32::INFINITY; // Disable wrapping for code editor
|
||||||
let mut layouter = |ui: &egui::Ui, buffer: &dyn egui::TextBuffer, _wrap_width: f32| {
|
ui.painter().layout_job(layout_job)
|
||||||
let layout_job = highlight_myc(ui.ctx(), buffer.as_str());
|
};
|
||||||
ui.painter().layout_job(layout_job)
|
|
||||||
};
|
|
||||||
|
|
||||||
ui.add_sized(
|
egui::ScrollArea::both()
|
||||||
ui.available_size(),
|
.id_salt("source_editor_scroll")
|
||||||
|
.auto_shrink(false)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.add(
|
||||||
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)
|
.code_editor()
|
||||||
.lock_focus(true)
|
.lock_focus(true)
|
||||||
|
.desired_width(f32::INFINITY)
|
||||||
|
.frame(false)
|
||||||
.layouter(&mut layouter),
|
.layouter(&mut layouter),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -443,10 +554,10 @@ impl eframe::App for CompilerApp {
|
|||||||
fn highlight_myc(ctx: &egui::Context, code: &str) -> egui::text::LayoutJob {
|
fn highlight_myc(ctx: &egui::Context, code: &str) -> egui::text::LayoutJob {
|
||||||
let mut job = egui::text::LayoutJob::default();
|
let mut job = egui::text::LayoutJob::default();
|
||||||
let theme = &ctx.style().visuals;
|
let theme = &ctx.style().visuals;
|
||||||
|
|
||||||
let color_kw = egui::Color32::from_rgb(86, 156, 214); // Blue
|
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_str = egui::Color32::from_rgb(206, 145, 120); // Orange/Brown
|
||||||
let color_num = egui::Color32::from_rgb(181, 206, 168); // Greenish
|
let color_num = egui::Color32::from_rgb(181, 206, 168); // Greenish
|
||||||
let color_comment = egui::Color32::from_rgb(106, 153, 85); // Green
|
let color_comment = egui::Color32::from_rgb(106, 153, 85); // Green
|
||||||
let color_bracket = [
|
let color_bracket = [
|
||||||
egui::Color32::from_rgb(255, 215, 0), // Gold
|
egui::Color32::from_rgb(255, 215, 0), // Gold
|
||||||
@@ -464,7 +575,9 @@ fn highlight_myc(ctx: &egui::Context, code: &str) -> egui::text::LayoutJob {
|
|||||||
match c {
|
match c {
|
||||||
';' => {
|
';' => {
|
||||||
while let Some(&next) = chars.peek() {
|
while let Some(&next) = chars.peek() {
|
||||||
if next == '\n' { break; }
|
if next == '\n' {
|
||||||
|
break;
|
||||||
|
}
|
||||||
text.push(chars.next().unwrap());
|
text.push(chars.next().unwrap());
|
||||||
}
|
}
|
||||||
color = color_comment;
|
color = color_comment;
|
||||||
@@ -472,8 +585,12 @@ fn highlight_myc(ctx: &egui::Context, code: &str) -> egui::text::LayoutJob {
|
|||||||
'"' => {
|
'"' => {
|
||||||
while let Some(next) = chars.next() {
|
while let Some(next) = chars.next() {
|
||||||
text.push(next);
|
text.push(next);
|
||||||
if next == '"' { break; }
|
if next == '"' {
|
||||||
if next == '\\' && let Some(escaped) = chars.next() {
|
break;
|
||||||
|
}
|
||||||
|
if next == '\\'
|
||||||
|
&& let Some(escaped) = chars.next()
|
||||||
|
{
|
||||||
text.push(escaped);
|
text.push(escaped);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -481,7 +598,9 @@ fn highlight_myc(ctx: &egui::Context, code: &str) -> egui::text::LayoutJob {
|
|||||||
}
|
}
|
||||||
':' => {
|
':' => {
|
||||||
while let Some(&next) = chars.peek() {
|
while let Some(&next) = chars.peek() {
|
||||||
if next.is_whitespace() || "()[]{}".contains(next) { break; }
|
if next.is_whitespace() || "()[]{}".contains(next) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
text.push(chars.next().unwrap());
|
text.push(chars.next().unwrap());
|
||||||
}
|
}
|
||||||
color = color_kw;
|
color = color_kw;
|
||||||
@@ -501,7 +620,9 @@ fn highlight_myc(ctx: &egui::Context, code: &str) -> egui::text::LayoutJob {
|
|||||||
while let Some(&next) = chars.peek() {
|
while let Some(&next) = chars.peek() {
|
||||||
if next.is_ascii_digit() || next == '.' {
|
if next.is_ascii_digit() || next == '.' {
|
||||||
text.push(chars.next().unwrap());
|
text.push(chars.next().unwrap());
|
||||||
} else { break; }
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
color = color_num;
|
color = color_num;
|
||||||
}
|
}
|
||||||
@@ -509,9 +630,13 @@ fn highlight_myc(ctx: &egui::Context, code: &str) -> egui::text::LayoutJob {
|
|||||||
while let Some(&next) = chars.peek() {
|
while let Some(&next) = chars.peek() {
|
||||||
if next.is_alphanumeric() || "+-*/<=>!$%&_?.".contains(next) {
|
if next.is_alphanumeric() || "+-*/<=>!$%&_?.".contains(next) {
|
||||||
text.push(chars.next().unwrap());
|
text.push(chars.next().unwrap());
|
||||||
} else { break; }
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ["fn", "def", "do", "if", "assign", "recur", "cond", "macro"].contains(&text.as_str()) {
|
if ["fn", "def", "do", "if", "assign", "recur", "cond", "macro"]
|
||||||
|
.contains(&text.as_str())
|
||||||
|
{
|
||||||
color = color_kw;
|
color = color_kw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user