Add save functionality and shortcuts
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
;; Closure Scope Test
|
;; Closure Scope Test
|
||||||
;; Benchmark: 7.9us
|
;; Benchmark: 9.0us
|
||||||
;; Output: 15
|
;; Output: 15
|
||||||
(do
|
(do
|
||||||
(def make-adder (fn [x]
|
(def make-adder (fn [x]
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
;; Complex Data Structure Test
|
;; Complex Data Structure Test
|
||||||
;; Benchmark: 66.8us
|
;; Benchmark: 72.7us
|
||||||
;; Output: {:input 10, :name "Fibonacci", :output 55}
|
;; Output: {:input 10, :name "Fibonacci", :output 55}
|
||||||
(do
|
(do
|
||||||
(def fib (fn [n]
|
(def fib (fn [n]
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
;; Fibonacci Recursive
|
;; Fibonacci Recursive
|
||||||
;; Benchmark: 62.9us
|
;; Benchmark: 68.3us
|
||||||
;; Output: 55
|
;; Output: 55
|
||||||
(do
|
(do
|
||||||
(def fib (fn [n]
|
(def fib (fn [n]
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
;; Higher-Order Function Example
|
;; Higher-Order Function Example
|
||||||
;; Benchmark: 7.4us
|
;; Benchmark: 8.4us
|
||||||
;; Output: 25
|
;; Output: 25
|
||||||
(do
|
(do
|
||||||
(def apply (fn [f x] (f x)))
|
(def apply (fn [f x] (f x)))
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
;; Benchmark: 2.7ms
|
||||||
|
;; Output: "done"
|
||||||
(do
|
(do
|
||||||
(def count_down (fn [n]
|
(def count_down (fn [n]
|
||||||
(if (< n 1)
|
(if (< n 1)
|
||||||
"done"
|
"done"
|
||||||
(count_down (- n 1)))))
|
(count_down (- n 1)))))
|
||||||
|
|
||||||
(count_down 1000000)
|
(count_down 10000)
|
||||||
)
|
)
|
||||||
|
|||||||
+109
-6
@@ -1,5 +1,6 @@
|
|||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use myc::ast::environment::Environment;
|
use myc::ast::environment::Environment;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
fn main() -> eframe::Result {
|
fn main() -> eframe::Result {
|
||||||
let options = eframe::NativeOptions {
|
let options = eframe::NativeOptions {
|
||||||
@@ -15,11 +16,15 @@ fn main() -> eframe::Result {
|
|||||||
|
|
||||||
struct Example {
|
struct Example {
|
||||||
name: String,
|
name: String,
|
||||||
|
path: PathBuf,
|
||||||
content: String,
|
content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CompilerApp {
|
struct CompilerApp {
|
||||||
source_code: String,
|
source_code: String,
|
||||||
|
current_example_path: Option<PathBuf>,
|
||||||
|
save_as_name: String,
|
||||||
|
show_save_as: bool,
|
||||||
output_log: String,
|
output_log: String,
|
||||||
env: Environment,
|
env: Environment,
|
||||||
is_first_frame: bool,
|
is_first_frame: bool,
|
||||||
@@ -34,9 +39,10 @@ impl Default for CompilerApp {
|
|||||||
.filter_map(|e| e.ok())
|
.filter_map(|e| e.ok())
|
||||||
.filter(|e| e.path().extension().map_or(false, |ext| ext == "myc"))
|
.filter(|e| e.path().extension().map_or(false, |ext| ext == "myc"))
|
||||||
.filter_map(|e| {
|
.filter_map(|e| {
|
||||||
|
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(e.path()).ok()?;
|
let content = std::fs::read_to_string(&path).ok()?;
|
||||||
Some(Example { name, content })
|
Some(Example { name, path, content })
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
@@ -53,6 +59,9 @@ impl Default for CompilerApp {
|
|||||||
(fib 10)
|
(fib 10)
|
||||||
)
|
)
|
||||||
"#),
|
"#),
|
||||||
|
current_example_path: None,
|
||||||
|
save_as_name: String::new(),
|
||||||
|
show_save_as: false,
|
||||||
output_log: String::from("Ready to compile..."),
|
output_log: String::from("Ready to compile..."),
|
||||||
env: Environment::new(),
|
env: Environment::new(),
|
||||||
is_first_frame: true,
|
is_first_frame: true,
|
||||||
@@ -81,6 +90,66 @@ impl CompilerApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn save(&mut self) {
|
||||||
|
if let Some(path) = &self.current_example_path {
|
||||||
|
match std::fs::write(path, &self.source_code) {
|
||||||
|
Ok(_) => {
|
||||||
|
self.output_log = format!("Successfully saved to {:?}", path);
|
||||||
|
// Update the cached content in examples list so it stays in sync
|
||||||
|
if let Some(ex) = self.examples.iter_mut().find(|e| e.path == *path) {
|
||||||
|
ex.content = self.source_code.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
self.output_log = format!("Failed to save: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.output_log = "No file loaded to save. Use 'Examples' to load one.".to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_as(&mut self) {
|
||||||
|
if self.save_as_name.trim().is_empty() {
|
||||||
|
self.output_log = "Error: Please enter a filename.".to_string();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut filename = self.save_as_name.trim().to_string();
|
||||||
|
if !filename.ends_with(".myc") {
|
||||||
|
filename.push_str(".myc");
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = PathBuf::from("examples").join(filename);
|
||||||
|
match std::fs::write(&path, &self.source_code) {
|
||||||
|
Ok(_) => {
|
||||||
|
self.output_log = format!("Successfully saved new file to {:?}", path);
|
||||||
|
self.current_example_path = Some(path);
|
||||||
|
self.show_save_as = false;
|
||||||
|
self.save_as_name.clear();
|
||||||
|
self.refresh_examples();
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
self.output_log = format!("Failed to save: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn refresh_examples(&mut self) {
|
||||||
|
if let Ok(entries) = std::fs::read_dir("examples") {
|
||||||
|
self.examples = entries
|
||||||
|
.filter_map(|e| e.ok())
|
||||||
|
.filter(|e| e.path().extension().map_or(false, |ext| ext == "myc"))
|
||||||
|
.filter_map(|e| {
|
||||||
|
let path = e.path();
|
||||||
|
let name = e.file_name().into_string().ok()?;
|
||||||
|
let content = std::fs::read_to_string(&path).ok()?;
|
||||||
|
Some(Example { name, path, content })
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn run_all_tests(&mut self) {
|
fn run_all_tests(&mut self) {
|
||||||
use myc::ast::tester;
|
use myc::ast::tester;
|
||||||
let results = tester::run_functional_tests();
|
let results = tester::run_functional_tests();
|
||||||
@@ -125,6 +194,7 @@ impl eframe::App for CompilerApp {
|
|||||||
for example in &self.examples {
|
for example in &self.examples {
|
||||||
if ui.button(&example.name).clicked() {
|
if ui.button(&example.name).clicked() {
|
||||||
self.source_code = example.content.clone();
|
self.source_code = example.content.clone();
|
||||||
|
self.current_example_path = Some(example.path.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -139,10 +209,25 @@ impl eframe::App for CompilerApp {
|
|||||||
|
|
||||||
// Compile Button (at the top of the bottom panel)
|
// Compile Button (at the top of the bottom panel)
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if ui.button("Compile").clicked() {
|
if ui.button("Compile (Shift+Enter)").clicked() {
|
||||||
self.compile();
|
self.compile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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| {
|
||||||
|
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() {
|
if ui.button("Test All").clicked() {
|
||||||
self.run_all_tests();
|
self.run_all_tests();
|
||||||
}
|
}
|
||||||
@@ -168,7 +253,20 @@ impl eframe::App for CompilerApp {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.add_space(5.0);
|
if self.show_save_as {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("New Filename:");
|
||||||
|
ui.text_edit_singleline(&mut self.save_as_name);
|
||||||
|
if ui.button("Save Now").clicked() {
|
||||||
|
self.save_as();
|
||||||
|
}
|
||||||
|
if ui.button("Cancel").clicked() {
|
||||||
|
self.show_save_as = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui.add_space(5.0);
|
||||||
|
}
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label("Output / Logs:");
|
ui.label("Output / Logs:");
|
||||||
if ui.button("Copy to Clipboard").clicked() {
|
if ui.button("Copy to Clipboard").clicked() {
|
||||||
@@ -198,11 +296,16 @@ 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 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(&shortcut)) {
|
if ui.input_mut(|i| i.consume_shortcut(&compile_shortcut)) {
|
||||||
self.compile();
|
self.compile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let save_shortcut = egui::KeyboardShortcut::new(egui::Modifiers::COMMAND, egui::Key::S);
|
||||||
|
if ui.input_mut(|i| i.consume_shortcut(&save_shortcut)) {
|
||||||
|
self.save();
|
||||||
|
}
|
||||||
|
|
||||||
// Set initial focus
|
// Set initial focus
|
||||||
if self.is_first_frame {
|
if self.is_first_frame {
|
||||||
ui.ctx().memory_mut(|m| m.request_focus(editor_id));
|
ui.ctx().memory_mut(|m| m.request_focus(editor_id));
|
||||||
|
|||||||
Reference in New Issue
Block a user