master #7

Merged
candle merged 15 commits from master into release 2025-07-28 21:24:46 +00:00
5 changed files with 168 additions and 63 deletions
Showing only changes of commit 4651d7caf4 - Show all commits

View File

@ -12,3 +12,5 @@ rfd = "0.15.4"
toml = "0.9.2" toml = "0.9.2"
dirs = "6.0" dirs = "6.0"
libc = "0.2.174" libc = "0.2.174"
syntect = "5.2.0"
plist = "1.7.4"

View File

@ -46,6 +46,7 @@ theme = "System"
line_side = false line_side = false
font_family = "Monospace" font_family = "Monospace"
font_size = 16.0 font_size = 16.0
syntax_highlighting = true
``` ```
### Options ### Options
@ -55,6 +56,7 @@ font_size = 16.0
| `auto_hide_toolbar` | `false` | If `true`, the menu bar at the top will be hidden. Move your mouse to the top of the window to reveal it. | | `auto_hide_toolbar` | `false` | If `true`, the menu bar at the top will be hidden. Move your mouse to the top of the window to reveal it. |
| `hide_tab_bar` | 'true' | If `false`, a separate tab bar will be drawn below the toolbar. | | `hide_tab_bar` | 'true' | If `false`, a separate tab bar will be drawn below the toolbar. |
| `show_line_numbers` | `false` | If `true`, line numbers will be displayed on the side specified by `line_side`. | | `show_line_numbers` | `false` | If `true`, line numbers will be displayed on the side specified by `line_side`. |
| `syntax_highlighting` | `false` | If `true`, text will be highlighted based on detected language. |
| `line_side` | `false` | If `false`, line numbers are on the left. If `true`, they are on the right. | | `line_side` | `false` | If `false`, line numbers are on the left. If `true`, they are on the right. |
| `word_wrap` | `false` | If `true`, lines will wrap when they reach the edge of the window. | | `word_wrap` | `false` | If `true`, lines will wrap when they reach the edge of the window. |
| `font_family` | `"Proportional"` | The font family used for the editor text. | | `font_family` | `"Proportional"` | The font family used for the editor text. |
@ -65,9 +67,8 @@ font_size = 16.0
In order of importance. In order of importance.
| Feature | Info | | Feature | Info |
| ------- | ---- | | ------- | ---- |
| **Find/Replace:** | Functioning. |
| **State/Cache:** | A toggleable option to keep an application state and prevent "Quit without saving" warnings. | | **State/Cache:** | A toggleable option to keep an application state and prevent "Quit without saving" warnings. |
| **Syntax Highlighting/LSP:** | Looking at allowing you to use/attach your own tools for this. | | **LSP:** | Looking at allowing you to use/attach your own tools for this. |
| **Choose Font** | More than just Monospace/Proportional. | | **Choose Font** | More than just Monospace/Proportional. |
| **Vim Mode:** | It's in-escapable. | | **Vim Mode:** | It's in-escapable. |
| **CLI Mode:** | 💀 | | **CLI Mode:** | 💀 |

View File

@ -1,5 +1,7 @@
use eframe::egui; use eframe::egui;
use egui_extras::syntax_highlighting::CodeTheme; use plist::{Dictionary, Value};
use std::collections::BTreeMap;
use syntect::highlighting::{Theme as SyntectTheme, ThemeSet, ThemeSettings, Color as SyntectColor, UnderlineOption};
#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize, Default)] #[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize, Default)]
pub enum Theme { pub enum Theme {
@ -196,11 +198,108 @@ fn detect_system_dark_mode() -> bool {
} }
} }
pub fn create_code_theme_from_visuals(visuals: &egui::Visuals, font_size: f32) -> CodeTheme { fn egui_color_to_syntect(color: egui::Color32) -> SyntectColor {
if visuals.dark_mode { SyntectColor {
CodeTheme::dark(font_size) r: color.r(),
} else { g: color.g(),
CodeTheme::light(font_size) b: color.b(),
a: color.a(),
} }
} }
pub fn create_code_theme_from_visuals(visuals: &egui::Visuals, font_size: f32) -> ThemeSet {
let text_color = visuals.override_text_color.unwrap_or(visuals.text_color());
let bg_color = visuals.extreme_bg_color;
let selection_color = visuals.selection.bg_fill;
let comment_color = blend_colors(text_color, bg_color, 0.6);
let keyword_color = if visuals.dark_mode {
blend_colors(egui::Color32::from_rgb(100, 149, 237), text_color, 0.8) // CornflowerBlue-like
} else {
blend_colors(egui::Color32::from_rgb(0, 0, 139), text_color, 0.8) // DarkBlue-like
};
let string_color = if visuals.dark_mode {
blend_colors(egui::Color32::from_rgb(144, 238, 144), text_color, 0.8) // LightGreen-like
} else {
blend_colors(egui::Color32::from_rgb(0, 128, 0), text_color, 0.8) // Green-like
};
let number_color = if visuals.dark_mode {
blend_colors(egui::Color32::from_rgb(255, 165, 0), text_color, 0.8) // Orange-like
} else {
blend_colors(egui::Color32::from_rgb(165, 42, 42), text_color, 0.8) // Brown-like
};
let function_color = if visuals.dark_mode {
blend_colors(egui::Color32::from_rgb(255, 20, 147), text_color, 0.8) // DeepPink-like
} else {
blend_colors(egui::Color32::from_rgb(128, 0, 128), text_color, 0.8) // Purple-like
};
let plist_theme = build_custom_theme_plist("System", &format!("{:?}", bg_color), &format!("{:?}", text_color), &format!("{:?}", comment_color), &format!("{:?}", string_color), &format!("{:?}", keyword_color));
let file = std::fs::File::create("system.tmTheme").unwrap();
let writer = std::io::BufWriter::new(file);
let _ =plist::to_writer_xml(writer, &plist_theme);
let loaded_file = std::fs::File::open("system.tmTheme").unwrap();
let mut loaded_reader = std::io::BufReader::new(loaded_file);
let loaded_theme = ThemeSet::load_from_reader(&mut loaded_reader).unwrap();
let mut set = ThemeSet::new();
set.add_from_folder(".").unwrap();
return set;
}
fn build_custom_theme_plist(
theme_name: &str,
background_color: &str,
foreground_color: &str,
comment_color: &str,
string_color: &str,
keyword_color: &str,
) -> Value {
let mut root_dict = Dictionary::new();
root_dict.insert("name".to_string(), Value::String(theme_name.to_string()));
let mut settings_array = Vec::new();
// Global settings
let mut global_settings_dict = Dictionary::new();
let mut inner_global_settings = Dictionary::new();
inner_global_settings.insert("background".to_string(), Value::String(background_color.to_string()));
inner_global_settings.insert("foreground".to_string(), Value::String(foreground_color.to_string()));
global_settings_dict.insert("settings".to_string(), Value::Dictionary(inner_global_settings));
settings_array.push(Value::Dictionary(global_settings_dict));
// Comment scope
let mut comment_scope_dict = Dictionary::new();
comment_scope_dict.insert("name".to_string(), Value::String("Comment".to_string()));
comment_scope_dict.insert("scope".to_string(), Value::String("comment".to_string()));
let mut comment_settings = Dictionary::new();
comment_settings.insert("foreground".to_string(), Value::String(comment_color.to_string()));
comment_settings.insert("fontStyle".to_string(), Value::String("italic".to_string()));
comment_scope_dict.insert("settings".to_string(), Value::Dictionary(comment_settings));
settings_array.push(Value::Dictionary(comment_scope_dict));
// String scope
let mut string_scope_dict = Dictionary::new();
string_scope_dict.insert("name".to_string(), Value::String("String".to_string()));
string_scope_dict.insert("scope".to_string(), Value::String("string".to_string()));
let mut string_settings = Dictionary::new();
string_settings.insert("foreground".to_string(), Value::String(string_color.to_string()));
string_scope_dict.insert("settings".to_string(), Value::Dictionary(string_settings));
settings_array.push(Value::Dictionary(string_scope_dict));
// Keyword scope
let mut keyword_scope_dict = Dictionary::new();
keyword_scope_dict.insert("name".to_string(), Value::String("Keyword".to_string()));
keyword_scope_dict.insert("scope".to_string(), Value::String("keyword".to_string()));
let mut keyword_settings = Dictionary::new();
keyword_settings.insert("foreground".to_string(), Value::String(keyword_color.to_string()));
keyword_scope_dict.insert("settings".to_string(), Value::Dictionary(keyword_settings));
settings_array.push(Value::Dictionary(keyword_scope_dict));
// Add more scopes as needed...
root_dict.insert("settings".to_string(), Value::Array(settings_array));
Value::Dictionary(root_dict)
}

View File

@ -4,7 +4,6 @@ use egui_extras::syntax_highlighting::{self};
use super::find_highlight; use super::find_highlight;
pub(super) fn editor_view_ui(ui: &mut egui::Ui, app: &mut TextEditor) -> egui::Response { pub(super) fn editor_view_ui(ui: &mut egui::Ui, app: &mut TextEditor) -> egui::Response {
let _current_match_position = app.get_current_match_position(); let _current_match_position = app.get_current_match_position();
let show_find = app.show_find; let show_find = app.show_find;
@ -101,11 +100,15 @@ pub(super) fn editor_view_ui(ui: &mut egui::Ui, app: &mut TextEditor) -> egui::R
}; };
let language = super::languages::get_language_from_extension(active_tab.file_path.as_deref()); let language = super::languages::get_language_from_extension(active_tab.file_path.as_deref());
let theme = crate::app::theme::create_code_theme_from_visuals(ui.visuals(), font_size);
let mut layouter = |ui: &egui::Ui, string: &dyn egui::TextBuffer, wrap_width: f32| { let mut layouter = |ui: &egui::Ui, string: &dyn egui::TextBuffer, wrap_width: f32| {
// let syntect_theme =
// crate::app::theme::create_code_theme_from_visuals(ui.visuals(), font_size);
let text = string.as_str(); let text = string.as_str();
let theme = egui_extras::syntax_highlighting::CodeTheme::dark(font_size);
let mut layout_job = if syntax_highlighting_enabled && language != "txt" { let mut layout_job = if syntax_highlighting_enabled && language != "txt" {
// let mut settings = egui_extras::syntax_highlighting::SyntectSettings::default();
// settings.ts = syntect_theme;
// syntax_highlighting::highlight_with(ui.ctx(), &ui.style().clone(), &theme, text, &language, &settings)
syntax_highlighting::highlight(ui.ctx(), &ui.style().clone(), &theme, text, &language) syntax_highlighting::highlight(ui.ctx(), &ui.style().clone(), &theme, text, &language)
} else { } else {
syntax_highlighting::highlight(ui.ctx(), &ui.style().clone(), &theme, text, "") syntax_highlighting::highlight(ui.ctx(), &ui.style().clone(), &theme, text, "")

View File

@ -1,5 +1,11 @@
pub fn get_language_from_extension(file_path: Option<&std::path::Path>) -> String { pub fn get_language_from_extension(file_path: Option<&std::path::Path>) -> String {
if let Some(path) = file_path { let default_lang = "txt".to_string();
let path = match file_path {
Some(p) => p,
None => return default_lang,
};
if let Some(extension) = path.extension().and_then(|ext| ext.to_str()) { if let Some(extension) = path.extension().and_then(|ext| ext.to_str()) {
match extension.to_lowercase().as_str() { match extension.to_lowercase().as_str() {
"rs" => "rs".to_string(), "rs" => "rs".to_string(),
@ -33,23 +39,17 @@ pub fn get_language_from_extension(file_path: Option<&std::path::Path>) -> Strin
"vim" => "vim".to_string(), "vim" => "vim".to_string(),
"dockerfile" => "dockerfile".to_string(), "dockerfile" => "dockerfile".to_string(),
"makefile" => "makefile".to_string(), "makefile" => "makefile".to_string(),
_ => "txt".to_string(), _ => default_lang,
} }
} else { } else if let Some(filename) = path.file_name().and_then(|name| name.to_str()) {
// Check filename for special cases
if let Some(filename) = path.file_name().and_then(|name| name.to_str()) {
match filename.to_lowercase().as_str() { match filename.to_lowercase().as_str() {
"dockerfile" => "dockerfile".to_string(), "dockerfile" => "dockerfile".to_string(),
"makefile" => "makefile".to_string(), "makefile" => "makefile".to_string(),
"cargo.toml" | "pyproject.toml" => "toml".to_string(), "cargo.toml" | "pyproject.toml" => "toml".to_string(),
"package.json" => "json".to_string(), "package.json" => "json".to_string(),
_ => "txt".to_string(), _ => default_lang,
} }
} else { } else {
"txt".to_string() default_lang
}
}
} else {
"txt".to_string()
} }
} }