terminal checkpoint before async

This commit is contained in:
Penwing 2024-01-24 13:26:52 +01:00
parent 0d38cd53fa
commit ec1b855686
7 changed files with 153 additions and 56 deletions

View file

@ -25,4 +25,10 @@ Added indent recognition (when there is a line break, the indentation level is k
# 1.0.3 : # 1.0.3 :
Added testing Added testing
Added Ctrl+T : turn 4 spaces into tab across the whole document Added Ctrl+T : turn 4 spaces into tab across the whole document
Added Time debug Added Time debug
Added Tree toggle for performance
Added Alt+Arrows to move through tabs
Added Zoom
Added cd
Added terminal color

View file

@ -1 +1 @@
{"tabs":["/home/penwing/Documents/notes/victory2.txt"],"theme":6} {"tabs":["/home/penwing/Documents/notes/victory2.txt","/home/penwing/Documents/projects/rust/calcifer/src/calcifer/code_editor/themes/sonokai.rs"],"theme":4}

View file

@ -3,6 +3,7 @@ use egui::{text::CCursor, text_edit::CCursorRange};
use std::{env, path::Path, path::PathBuf, cmp::max, io, fs, cmp::min}; use std::{env, path::Path, path::PathBuf, cmp::max, io, fs, cmp::min};
use crate::tools; use crate::tools;
use crate::TIME_LABELS; use crate::TIME_LABELS;
use crate::PATH_ROOT;
pub mod code_editor; pub mod code_editor;
use code_editor::CodeEditor; use code_editor::CodeEditor;
@ -16,6 +17,13 @@ impl super::Calcifer {
.resizable(false) .resizable(false)
.show(ctx, |ui| { .show(ctx, |ui| {
ui.horizontal(|ui| { ui.horizontal(|ui| {
if ui.add(egui::Button::new("open file")).clicked() {
if let Some(path) = rfd::FileDialog::new().set_directory(Path::new(&PATH_ROOT)).pick_file() {
self.selected_tab = self.open_file(&path);
}
}
ui.separator();
ui.label("Theme "); ui.label("Theme ");
egui::ComboBox::from_label("") egui::ComboBox::from_label("")
.selected_text(format!("{}", self.theme.name)) .selected_text(format!("{}", self.theme.name))
@ -28,15 +36,17 @@ impl super::Calcifer {
}); });
ui.separator(); ui.separator();
ui.checkbox(&mut self.debug_display, "Debug display"); self.tree_display = self.toggle(ui, self.tree_display, "Tree");
ui.separator();
self.debug_display = self.toggle(ui, self.debug_display, "Debug");
ui.separator(); ui.separator();
if self.debug_display { if self.debug_display {
let combined_string: Vec<String> = TIME_LABELS.into_iter().zip(self.time_watch.clone().into_iter()) let combined_string: Vec<String> = TIME_LABELS.into_iter().zip(self.time_watch.clone().into_iter())
.map(|(s, v)| format!("{} : {:.1} ms", s, v)).collect(); .map(|(s, v)| format!("{} : {:.1} ms", s, v)).collect();
let mut result = combined_string.join(" ; "); let mut result = combined_string.join(" ; ");
result.push_str(&format!(" total : {:.1}", self.time_watch.clone().iter().sum::<f32>())); result.push_str(&format!(" total : {:.1}", self.time_watch.clone().iter().sum::<f32>()));
ui.label(result); ui.label(result);
} }
}); });
@ -44,15 +54,13 @@ impl super::Calcifer {
} }
pub fn draw_tree_panel(&mut self, ctx: &egui::Context) { pub fn draw_tree_panel(&mut self, ctx: &egui::Context) {
if !self.tree_display {
return
}
egui::SidePanel::left("file_tree_panel").show(ctx, |ui| { egui::SidePanel::left("file_tree_panel").show(ctx, |ui| {
ui.heading("Bookshelf"); ui.heading("Bookshelf");
if ui.add(egui::Button::new("open file")).clicked() {
if let Some(path) = rfd::FileDialog::new().pick_file() {
self.selected_tab = self.open_file(&path);
}
}
ui.separator(); ui.separator();
let _ = self.list_files(ui, Path::new("/home/penwing/Documents/")); let _ = self.list_files(ui, Path::new(&PATH_ROOT));
ui.separator(); ui.separator();
}); });
} }
@ -63,11 +71,15 @@ impl super::Calcifer {
.min_height(0.0) .min_height(0.0)
.show(ctx, |ui| { .show(ctx, |ui| {
ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| { ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| {
let command_color = egui::Color32::from_hex(self.theme.functions).expect("Theme color issue (functions)");
let entry_color = egui::Color32::from_hex(self.theme.literals).expect("Theme color issue (literals)");
let bg_color = egui::Color32::from_hex(self.theme.bg).expect("Theme color issue (bg)");
ui.label(""); ui.label("");
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.style_mut().visuals.extreme_bg_color = egui::Color32::from_hex(self.theme.bg).expect("Could not convert color"); ui.style_mut().visuals.extreme_bg_color = bg_color;
let Self { command, .. } = self; let Self { command, .. } = self;
ui.label(format!("{}>", env::current_dir().expect("Could not find Shell Environnment").file_name().expect("Could not get Shell Environnment Name").to_string_lossy().to_string())); ui.colored_label(command_color.clone(), tools::format_path(&env::current_dir().expect("Could not find Shell Environnment"), 2));
let response = ui.add(egui::TextEdit::singleline(command).desired_width(f32::INFINITY).lock_focus(true)); let response = ui.add(egui::TextEdit::singleline(command).desired_width(f32::INFINITY).lock_focus(true));
if response.lost_focus() && ctx.input(|i| i.key_pressed(egui::Key::Enter)) { if response.lost_focus() && ctx.input(|i| i.key_pressed(egui::Key::Enter)) {
@ -83,10 +95,10 @@ impl super::Calcifer {
ui.horizontal_wrapped(|ui| { ui.horizontal_wrapped(|ui| {
ui.spacing_mut().item_spacing.y = 0.0; ui.spacing_mut().item_spacing.y = 0.0;
for entry in &self.command_history { for entry in &self.command_history {
ui.label(format!("{}> {}", entry.env, entry.command)); ui.colored_label(command_color, format!("{}> {}", entry.env, entry.command));
ui.end_row(); ui.end_row();
if entry.output != "" { if entry.output != "" {
ui.label(&entry.output); ui.colored_label(entry_color, &entry.output);
ui.end_row(); ui.end_row();
} }
if entry.error != "" { if entry.error != "" {
@ -138,9 +150,16 @@ impl super::Calcifer {
pub fn draw_content_panel(&mut self, ctx: &egui::Context) { pub fn draw_content_panel(&mut self, ctx: &egui::Context) {
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
ui.horizontal(|ui| { ui.horizontal(|ui| {
if ui.add(egui::Button::new("open in terminal")).clicked() {
let mut path = self.tabs[self.selected_tab.to_index()].path.clone();
path.pop();
tools::run_command(format!("cd {}", path.display()));
}
ui.label("Picked file:"); ui.label("Picked file:");
ui.monospace(self.tabs[self.selected_tab.to_index()].path.to_string_lossy().to_string()); ui.monospace(self.tabs[self.selected_tab.to_index()].path.to_string_lossy().to_string());
}); });
ui.separator();
if self.selected_tab == tools::TabNumber::None { if self.selected_tab == tools::TabNumber::None {
return return
@ -165,7 +184,7 @@ impl super::Calcifer {
CodeEditor::default().id_source("code editor") CodeEditor::default().id_source("code editor")
.with_rows(max(45,lines)) .with_rows(max(45,lines))
.with_fontsize(14.0) .with_fontsize(self.font_size)
.with_theme(self.theme) .with_theme(self.theme)
.with_syntax(tools::to_syntax(&current_tab.language)) .with_syntax(tools::to_syntax(&current_tab.language))
.with_numlines(true) .with_numlines(true)
@ -249,6 +268,15 @@ impl super::Calcifer {
current_tab.code = current_tab.code.replace(" ", "\t") current_tab.code = current_tab.code.replace(" ", "\t")
} }
pub fn move_through_tabs(&mut self, forward : bool) {
let new_index = if forward {
(self.selected_tab.to_index() + 1) % self.tabs.len()
} else {
self.selected_tab.to_index().checked_sub(1).unwrap_or(self.tabs.len() - 1)
};
self.selected_tab = tools::TabNumber::from_index(new_index);
}
fn list_files(&mut self, ui: &mut egui::Ui, path: &Path) -> io::Result<()> { fn list_files(&mut self, ui: &mut egui::Ui, path: &Path) -> io::Result<()> {
if let Some(name) = path.file_name() { if let Some(name) = path.file_name() {
if path.is_dir() { if path.is_dir() {
@ -300,4 +328,17 @@ impl super::Calcifer {
self.tabs.remove(index); self.tabs.remove(index);
return tools::TabNumber::from_index(min(index, self.tabs.len() - 1)) return tools::TabNumber::from_index(min(index, self.tabs.len() - 1))
} }
fn toggle(&self, ui: &mut egui::Ui, display : bool, title : &str) -> bool {
let text = if display.clone() {
format!("hide {}", title)
} else {
format!("show {}", title)
};
if ui.add(egui::Button::new(text)).clicked() {
return !display
}
return display
}
} }

1
src/calcifer_save.json Normal file
View file

@ -0,0 +1 @@
{"tabs":["/home/penwing/Documents/notes/victory2.txt","/home/penwing/Documents/projects/rust/calcifer/src/calcifer/code_editor/themes/sonokai.rs"],"theme":6}

View file

@ -13,6 +13,7 @@ const RED : egui::Color32 = egui::Color32::from_rgb(235, 108, 99);
const SAVE_PATH : &str = "calcifer_save.json"; const SAVE_PATH : &str = "calcifer_save.json";
const TIME_LABELS : [&str; 5] = ["settings", "tree", "terminal", "tabs", "content"]; const TIME_LABELS : [&str; 5] = ["settings", "tree", "terminal", "tabs", "content"];
const MAX_FPS : f32 = 30.0; const MAX_FPS : f32 = 30.0;
const PATH_ROOT : &str = "/home/penwing/Documents/";
fn main() -> Result<(), eframe::Error> { fn main() -> Result<(), eframe::Error> {
@ -55,6 +56,7 @@ struct Calcifer {
command_history: Vec<tools::CommandEntry>, command_history: Vec<tools::CommandEntry>,
theme: ColorTheme, theme: ColorTheme,
font_size: f32,
search: tools::search::SearchWindow, search: tools::search::SearchWindow,
searching: bool, searching: bool,
@ -62,6 +64,8 @@ struct Calcifer {
debug_display: bool, debug_display: bool,
time_watch: Vec<f32>, time_watch: Vec<f32>,
next_frame: time::Instant, next_frame: time::Instant,
tree_display: bool,
} }
@ -75,6 +79,7 @@ impl Default for Calcifer {
command_history: Vec::new(), command_history: Vec::new(),
theme: DEFAULT_THEMES[0], theme: DEFAULT_THEMES[0],
font_size: 14.0,
search: tools::search::SearchWindow::default(), search: tools::search::SearchWindow::default(),
searching: false, searching: false,
@ -82,6 +87,8 @@ impl Default for Calcifer {
debug_display: false, debug_display: false,
time_watch: vec![0.0; TIME_LABELS.len()], time_watch: vec![0.0; TIME_LABELS.len()],
next_frame: time::Instant::now(), next_frame: time::Instant::now(),
tree_display: false,
} }
} }
} }
@ -94,17 +101,33 @@ impl eframe::App for Calcifer {
let mut watch = time::Instant::now(); let mut watch = time::Instant::now();
if ctx.input( |i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl) {
self.handle_save_file(self.save_tab());
}
if ctx.input( |i| i.key_pressed(egui::Key::T) && i.modifiers.ctrl) { if ctx.input( |i| i.key_pressed(egui::Key::T) && i.modifiers.ctrl) {
self.indent_with_tabs(); self.indent_with_tabs();
} }
if ctx.input( |i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl) {
self.handle_save_file(self.save_tab());
}
if ctx.input( |i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl && i.modifiers.shift) { if ctx.input( |i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl && i.modifiers.shift) {
self.handle_save_file(self.save_tab_as()); self.handle_save_file(self.save_tab_as());
} }
if ctx.input( |i| i.key_pressed(egui::Key::ArrowLeft) && i.modifiers.alt) {
self.move_through_tabs(false);
}
if ctx.input( |i| i.key_pressed(egui::Key::ArrowRight) && i.modifiers.alt) {
self.move_through_tabs(true);
}
if ctx.input( |i| i.zoom_delta() > 1.0) {
self.font_size = (self.font_size * 1.1).min(30.0);
}
if ctx.input( |i| i.zoom_delta() < 1.0) {
self.font_size = (self.font_size / 1.1).max(10.0);
}
if ctx.input( |i| i.key_pressed(egui::Key::F) && i.modifiers.ctrl) { if ctx.input( |i| i.key_pressed(egui::Key::F) && i.modifiers.ctrl) {
self.searching = !self.searching.clone(); self.searching = !self.searching.clone();

View file

@ -1,4 +1,4 @@
use std::{process::Command, cmp::Ordering, env, path::PathBuf, fs::read_to_string, fs::write}; use std::{env, process::Command, cmp::Ordering, path::PathBuf, path::Path, fs::read_to_string, fs::write, path::Component, ffi::OsStr};
use crate::calcifer::code_editor::Syntax; use crate::calcifer::code_editor::Syntax;
use eframe::egui; use eframe::egui;
use egui::text_edit::CCursorRange; use egui::text_edit::CCursorRange;
@ -179,15 +179,27 @@ pub fn to_syntax(language : &str) -> Syntax {
pub fn run_command(cmd : String) -> CommandEntry { pub fn run_command(cmd : String) -> CommandEntry {
let mut entry = CommandEntry::default(); let mut entry = CommandEntry::default();
let output = Command::new("sh") if &cmd[..2] == "cd" {
.arg("-c") let path_append = cmd[3..].replace("~", "/home/penwing");
.arg(cmd.clone()) let path = Path::new(&path_append);
.output() entry.command = cmd;
.expect("failed to execute process");
if format!("{}", path.display()) != "/" {
entry.command = cmd; if !env::set_current_dir(path).is_ok() {
entry.output = (&String::from_utf8_lossy(&output.stdout)).to_string(); entry.error = format!("Could not find path : {}", path.display());
entry.error = (&String::from_utf8_lossy(&output.stderr)).to_string(); }
}
} else {
let output = Command::new("sh")
.arg("-c")
.arg(cmd.clone())
.output()
.expect("failed to execute process");
entry.command = cmd;
entry.output = (&String::from_utf8_lossy(&output.stdout)).to_string();
entry.error = (&String::from_utf8_lossy(&output.stderr)).to_string();
}
entry entry
} }
@ -209,5 +221,20 @@ pub fn sort_directories_first(a: &std::fs::DirEntry, b: &std::fs::DirEntry) -> O
} }
pub fn format_path(path: &Path, n_parents: usize) -> String {
let components: Vec<&OsStr> = path
.components()
.rev()
.take(n_parents + 1) // Take up to three components (root, parent, file/folder)
.filter_map(|component| match component {
Component::RootDir | Component::CurDir => None,
_ => Some(component.as_os_str()),
})
.collect();
format!("{}>", components.iter().rev().map(|&c| c.to_string_lossy()).collect::<Vec<_>>().join("/"))
}
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;

View file

@ -33,7 +33,7 @@ impl Default for Selection {
pub struct SearchWindow { pub struct SearchWindow {
search_text: String, search_text: String,
searched_text: String, searched_text: String,
replace_text: String, replace_text: String,
@ -50,9 +50,9 @@ pub struct SearchWindow {
impl Default for SearchWindow { impl Default for SearchWindow {
fn default() -> Self { fn default() -> Self {
Self { Self {
search_text: "".into(), search_text: "".into(),
searched_text: "".into(), searched_text: "".into(),
replace_text: "".into(), replace_text: "".into(),
@ -65,29 +65,29 @@ impl Default for SearchWindow {
tab_selected: true, tab_selected: true,
result_selected: true, result_selected: true,
} }
} }
} }
impl Demo for SearchWindow { impl Demo for SearchWindow {
fn name(&self) -> &str { //'static fn name(&self) -> &str { //'static
"Search" "Search"
} }
fn show(&mut self, ctx: &egui::Context, open: &mut bool, tabs: &mut Vec<Tab>, selected_tab: &mut TabNumber) { fn show(&mut self, ctx: &egui::Context, open: &mut bool, tabs: &mut Vec<Tab>, selected_tab: &mut TabNumber) {
egui::Window::new(self.name()) egui::Window::new(self.name())
.open(open) .open(open)
.vscroll(true) .vscroll(true)
.hscroll(true) .hscroll(true)
.show(ctx, |ui| self.ui(ui, tabs, selected_tab)); .show(ctx, |ui| self.ui(ui, tabs, selected_tab));
} }
} }
impl View for SearchWindow { impl View for SearchWindow {
fn ui(&mut self, ui: &mut egui::Ui, tabs: &mut Vec<Tab>, selected_tab: &mut TabNumber) { fn ui(&mut self, ui: &mut egui::Ui, tabs: &mut Vec<Tab>, selected_tab: &mut TabNumber) {
ui.set_min_width(250.0); ui.set_min_width(250.0);
let mut action : Action = Action::None; let mut action : Action = Action::None;
@ -128,9 +128,9 @@ impl View for SearchWindow {
self.searched_text = "".into(); self.searched_text = "".into();
} }
egui::CollapsingHeader::new("Replace") egui::CollapsingHeader::new("Replace")
.default_open(false) .default_open(false)
.show(ui, |ui| { .show(ui, |ui| {
ui.horizontal(|ui| { ui.horizontal(|ui| {
let Self { replace_text, .. } = self; let Self { replace_text, .. } = self;
ui.add(egui::TextEdit::singleline(replace_text).desired_width(120.0).lock_focus(true)); ui.add(egui::TextEdit::singleline(replace_text).desired_width(120.0).lock_focus(true));
@ -138,7 +138,7 @@ impl View for SearchWindow {
action = Action::Replace; action = Action::Replace;
} }
}); });
}); });
match action { match action {
Action::Update => self.search(tabs, selected_tab), Action::Update => self.search(tabs, selected_tab),
@ -147,7 +147,7 @@ impl View for SearchWindow {
Action::Replace => self.replace(tabs, selected_tab), Action::Replace => self.replace(tabs, selected_tab),
Action::None => (), Action::None => (),
} }
} }
} }
impl SearchWindow { impl SearchWindow {
@ -227,10 +227,9 @@ impl SearchWindow {
self.search(tabs, selected_tab); self.search(tabs, selected_tab);
} }
println!("trying to replace {} with {}", self.search_text, self.replace_text);
for element in &self.results { for element in &self.results {
tabs[element.tab.to_index()].code = tabs[element.tab.to_index()].code.replace(&self.search_text, &self.replace_text); tabs[element.tab.to_index()].code = tabs[element.tab.to_index()].code.replace(&self.search_text, &self.replace_text);
tabs[element.tab.to_index()].saved = false;
} }
} }
} }