diff --git a/AGENTS.md b/AGENTS.md index 13aeca1..6746dfc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,7 +19,7 @@ Priorities, in order: 3. Focused, maintainable changes with good regression coverage. 4. Performance only after correctness and testability are preserved. -## Core Rules +## Core Rucargo run --features gui --bin geolog-guiles - Use English for code, comments, docs, tests, and commit-facing text. - Prefer small, targeted changes over broad rewrites. diff --git a/src/gui/panels/editor.rs b/src/gui/panels/editor.rs index b76709b..cbf3478 100644 --- a/src/gui/panels/editor.rs +++ b/src/gui/panels/editor.rs @@ -53,12 +53,18 @@ impl EditorPanel { fn show_editor_content(&mut self, ui: &mut egui::Ui, state: &mut GuiState) { // Use a monospace font for code let font_id = egui::FontId::monospace(14.0); + let mut layouter = |ui: &egui::Ui, text: &dyn egui::TextBuffer, wrap_width: f32| { + let mut layout_job = geolog_highlighter(ui, text.as_str(), wrap_width); + layout_job.wrap.max_width = wrap_width; + ui.fonts_mut(|fonts| fonts.layout_job(layout_job)) + }; // Main editor area - simplified layout without line numbers column // to avoid layout instability from horizontal split calculations let text_edit = egui::TextEdit::multiline(&mut state.editor_content) .font(font_id) .code_editor() + .layouter(&mut layouter) .desired_width(f32::INFINITY) .desired_rows(20) .lock_focus(true); @@ -84,24 +90,42 @@ impl Default for EditorPanel { } /// Simple syntax highlighting for Geolog code -/// Returns a layouter function for egui's code_editor -#[allow(dead_code)] -fn geolog_highlighter( - _ui: &egui::Ui, - text: &str, - _wrap_width: f32, -) -> egui::text::LayoutJob { +fn geolog_highlighter(ui: &egui::Ui, text: &str, wrap_width: f32) -> egui::text::LayoutJob { let mut job = egui::text::LayoutJob::default(); + job.wrap.max_width = wrap_width; let keywords = [ - "theory", "instance", "query", "forall", "exists", "Sort", "Prop", "true", "false", + "namespace", + "theory", + "instance", + "query", + "forall", + "exists", + "chase", + "Sort", + "Prop", + "true", + "false", ]; let font_id = egui::FontId::monospace(14.0); - let default_color = egui::Color32::WHITE; - let keyword_color = egui::Color32::from_rgb(86, 156, 214); // Blue - let comment_color = egui::Color32::from_rgb(106, 153, 85); // Green - let string_color = egui::Color32::from_rgb(206, 145, 120); // Orange + let default_format = egui::TextFormat::simple(font_id.clone(), ui.visuals().text_color()); + let keyword_format = egui::TextFormat { + font_id: font_id.clone(), + color: egui::Color32::from_rgb(0, 92, 197), + ..Default::default() + }; + let comment_format = egui::TextFormat { + font_id: font_id.clone(), + color: egui::Color32::from_rgb(35, 110, 55), + italics: true, + ..Default::default() + }; + let string_format = egui::TextFormat { + font_id: font_id.clone(), + color: egui::Color32::from_rgb(145, 70, 15), + ..Default::default() + }; let mut chars = text.char_indices().peekable(); @@ -116,11 +140,7 @@ fn geolog_highlighter( } } let end = chars.peek().map(|(i, _)| *i).unwrap_or(text.len()); - job.append( - &text[start..end], - 0.0, - egui::TextFormat::simple(font_id.clone(), comment_color), - ); + job.append(&text[start..end], 0.0, comment_format.clone()); continue; } @@ -136,11 +156,7 @@ fn geolog_highlighter( } } let end = chars.peek().map(|(i, _)| *i).unwrap_or(text.len()); - job.append( - &text[start..end], - 0.0, - egui::TextFormat::simple(font_id.clone(), string_color), - ); + job.append(&text[start..end], 0.0, string_format.clone()); continue; } @@ -153,27 +169,54 @@ fn geolog_highlighter( let end = chars.peek().map(|(i, _)| *i).unwrap_or(text.len()); let word = &text[start..end]; - let color = if keywords.contains(&word) { - keyword_color + let format = if keywords.contains(&word) { + keyword_format.clone() } else { - default_color + default_format.clone() }; - job.append( - word, - 0.0, - egui::TextFormat::simple(font_id.clone(), color), - ); + job.append(word, 0.0, format); continue; } // Default: single character - job.append( - &text[i..i + c.len_utf8()], - 0.0, - egui::TextFormat::simple(font_id.clone(), default_color), - ); + job.append(&text[i..i + c.len_utf8()], 0.0, default_format.clone()); } job } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn highlighter_marks_keywords_comments_and_strings() { + egui::__run_test_ui(|ui| { + let job = geolog_highlighter( + ui, + "theory Demo { // note\n label = \"hi\";\n}", + f32::INFINITY, + ); + + assert!(!job.sections.is_empty()); + + let has_keyword = job + .sections + .iter() + .any(|section| section.format.color == egui::Color32::from_rgb(0, 92, 197)); + let has_comment = job + .sections + .iter() + .any(|section| section.format.color == egui::Color32::from_rgb(35, 110, 55)); + let has_string = job + .sections + .iter() + .any(|section| section.format.color == egui::Color32::from_rgb(145, 70, 15)); + + assert!(has_keyword); + assert!(has_comment); + assert!(has_string); + }); + } +}