editor go brr?

This commit is contained in:
2025-06-16 23:38:36 +01:00
parent 7d17107a8c
commit 75246f5e73
6 changed files with 207 additions and 117 deletions
+2
View File
@@ -10,7 +10,9 @@ path = "src/lib.rs"
[dependencies]
common = { path = "../common" }
assembler = { path = "../assembler" }
dsa_editor = { path = "../dsa_editor" }
eframe = "0.31.1"
egui = "0.31.1"
rfd = "0.15.3"
dirs = "6.0.0"
+105 -108
View File
@@ -1,15 +1,19 @@
use std::sync::mpsc::Sender;
use std::{ffi::OsStr, path::PathBuf, sync::mpsc::Sender};
use egui::{Align, Context, Layout, Ui};
use assembler::lexer::Symbol;
use common::prelude::Instruction;
use egui::{Align, Context, Key, Layout, Ui};
use rfd::FileDialog;
use dsa_editor::{CodeEditor, ColorTheme, Syntax};
use crate::emulator::{
system::model::{Command, State},
ui::interface::Component,
};
pub struct Editor {
filename: String,
path: PathBuf,
text: String,
output: Vec<u8>,
sender: Sender<Command>,
@@ -37,6 +41,11 @@ impl Component for Editor {
fn render(&mut self, state: &mut State, ui: &mut Ui, ctx: &Context) {
ui.vertical(|ui| {
// Top bar
if ui.input(|i| i.key_pressed(Key::S) && i.modifiers.ctrl) {
self.save();
};
self.render_toolbar(state, ui, ctx);
ui.add_space(4.0); // Add some spacing instead of just a separator
@@ -61,9 +70,9 @@ impl Component for Editor {
impl Editor {
#[must_use]
pub const fn new(sender: Sender<Command>) -> Self {
pub fn new(sender: Sender<Command>) -> Self {
Self {
filename: String::new(),
path: PathBuf::new(),
text: String::new(),
output: Vec::new(),
sender,
@@ -76,11 +85,68 @@ impl Editor {
}
}
fn filename(&self) -> &str {
self.path
.file_name()
.unwrap_or(OsStr::new("Unnamed!"))
.to_str()
.unwrap()
}
fn extension(&self) -> &str {
self.path
.extension()
.unwrap_or(OsStr::new("Unknown!"))
.to_str()
.unwrap()
}
fn save(&mut self) {
let work_dir = std::env::current_dir().unwrap_or_else(|_| {
dirs::home_dir().expect(
"Couldn't get your current working directory or your home directory.",
)
});
if let Some(path) = FileDialog::new()
.add_filter("damn simple files", &["dsa", "dsb", "dsc", "dsd"])
.add_filter("all", &["*"])
.set_directory(&work_dir)
.save_file()
{
if let Err(why) = std::fs::write(&path, &self.text) {
self.error = Some(format!("Failed to save file: {why}"));
} else {
self.path = path;
}
}
}
fn open(&mut self) {
let work_dir = std::env::current_dir().unwrap_or_else(|_| {
dirs::home_dir().expect(
"Couldn't get your current working directory or your home directory.",
)
});
if let Some(path) = FileDialog::new()
.add_filter("damn simple files", &["dsa", "dsb", "dsc", "dsd"])
.add_filter("all", &["*"])
.set_directory(&work_dir)
.pick_file()
{
if let Ok(contents) = std::fs::read_to_string(&path) {
self.path = path;
self.text = contents;
}
}
}
fn render_output(&self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
// Output area with synchronized scrolling
egui::ScrollArea::vertical()
.id_salt("output_scroll")
.max_width(300.0)
.max_width(350.0)
.show(ui, |ui| {
if self.output.is_empty() {
ui.label(
@@ -92,8 +158,8 @@ impl Editor {
}
egui::Grid::new("output_grid")
.spacing([5.0, 2.0]) // Horizontal and vertical spacing
.num_columns(4)
.spacing([20.0, 2.0]) // Horizontal and vertical spacing
.striped(false)
.show(ui, |ui| {
// Process bytes in chunks of 4
@@ -107,7 +173,7 @@ impl Editor {
bytes[i] = byte;
}
}
let value = u32::from_le_bytes(bytes);
let value = u32::from_be_bytes(bytes);
// Address column
ui.with_layout(
@@ -144,9 +210,14 @@ impl Editor {
.color(egui::Color32::from_rgb(255, 200, 200)),
);
// Decimal column
// Instruction column
let instruction = match Instruction::decode(value) {
Ok(instruction) => instruction.to_string(),
Err(_) => format!("{value:10}"),
};
ui.label(
egui::RichText::new(format!("{value:10}"))
egui::RichText::new(instruction)
.font(egui::FontId::monospace(12.0))
.color(egui::Color32::from_rgb(200, 255, 200)),
);
@@ -159,75 +230,26 @@ impl Editor {
fn render_editor(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
let available_width = ui.available_width();
let syntax = match self.extension() {
"dsa" => Syntax::dsa(),
_ => Syntax::dsa(),
};
// Main editor area with synchronized scrolling
egui::ScrollArea::vertical()
.max_width(available_width - 400.0)
.id_salt("editor_scroll")
.show(ui, |ui| {
ui.horizontal(|ui| {
// Line numbers column
let line_count = self.text.lines().count();
ui.vertical(|ui| {
ui.set_width(50.0);
ui.style_mut().visuals.widgets.inactive.bg_fill =
egui::Color32::from_gray(30);
// Calculate line height to match text editor
let line_height =
ui.text_style_height(&egui::TextStyle::Monospace);
for line_num in 1..=line_count {
let line_response = ui.allocate_response(
egui::vec2(50.0, line_height),
egui::Sense::hover(),
);
ui.painter().text(
line_response.rect.left_center() + egui::vec2(5.0, 0.0),
egui::Align2::LEFT_CENTER,
format!("{line_num:3}"),
egui::FontId::monospace(12.0),
ui.style().visuals.text_color(),
);
}
});
ui.separator();
// Text editor area
ui.vertical(|ui| {
let available_size = ui.available_size();
let response = ui.add_sized(
available_size,
egui::TextEdit::multiline(&mut self.text)
.font(egui::TextStyle::Monospace)
.margin(egui::vec2(5.0, 0.0))
.code_editor(),
);
// Update cursor position when text changes
if response.changed() {
// Simple but functional cursor tracking
let lines = self.text.lines().collect::<Vec<_>>();
self.cursor_line = lines.len().max(1);
// Get the length of the last line for column position
if let Some(last_line) = lines.last() {
self.cursor_col = last_line.chars().count() + 1;
} else {
self.cursor_col = 1;
}
}
});
});
});
CodeEditor::default()
.id_source("editor")
.with_fontsize(12.0)
.with_rows(0)
.with_theme(ColorTheme::default())
.with_syntax(syntax)
.with_numlines(true)
.desired_width(available_width - 450.0)
.show(ui, &mut self.text);
}
fn render_toolbar(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
ui.horizontal(|ui| {
// current filename
ui.label(format!("File: {}", self.filename));
ui.label(format!("File type: {}", self.extension()));
ui.label(format!("Filename: {}", self.filename()));
// error display
ui.label(
@@ -243,52 +265,21 @@ impl Editor {
});
ui.horizontal(|ui| {
let work_dir = std::env::current_dir().unwrap_or_else(|_| {
dirs::home_dir().expect(
"Couldn't get your current working directory or your home directory.",
)
});
ui.spacing_mut().button_padding = egui::vec2(8.0, 4.0);
ui.spacing_mut().item_spacing.x = 6.0;
// Opens a file
if ui.button("Open").clicked() {
if let Some(path) = FileDialog::new()
.add_filter("dsafiles", &["dsa", "dsb", "dsc", "dsd"])
.add_filter("all", &["*"])
.set_directory(&work_dir)
.pick_file()
{
if let Ok(content) = std::fs::read_to_string(&path) {
self.text = content;
self.filename = path.display().to_string();
}
}
self.output = Vec::new();
self.open();
}
// Saves the current file
if ui.button("Save").clicked() {
if let Some(path) = FileDialog::new()
.add_filter("dsafiles", &["dsa", "dsb", "dsc", "dsd"])
.add_filter("all", &["*"])
.set_directory(&work_dir)
.save_file()
{
if let Err(why) = std::fs::write(&path, &self.text) {
self.error = Some(format!("Failed to save file: {why}"));
} else {
self.filename = path.display().to_string();
}
}
self.save();
}
// builds the current file
if ui.button("Build").clicked() {
let _instructions = assembler::assemble(&self.text);
// TODO: uncomment this once assembler works!!!
// let instructions = assembler::assemble(&self.text);
// self.output = instructions
@@ -296,7 +287,13 @@ impl Editor {
// .flat_map(|i| i.encode().to_le_bytes().to_vec())
// .collect();
self.output = vec![0x00; 256];
self.output = vec![
0x2e, 0xe0, 0x00, 0x00, 0x2e, 0xe1, 0x00, 0x01, 0x2e, 0xe2, 0x00,
0x06, 0x2e, 0xf6, 0x00, 0x00, 0x64, 0x01, 0x18, 0x00, 0x04, 0x37,
0x00, 0x00, 0x04, 0x77, 0x08, 0x00, 0x58, 0x57, 0x10, 0x00, 0x50,
0x56, 0xb8, 0x00, 0x42, 0xf7, 0x00, 0x04, 0x04, 0x37, 0x80, 0x00,
0x92, 0xf7, 0xb8, 0x00,
];
}
// Loads the generated binary into the assembler at the provided offset