async terminal and better state saves
This commit is contained in:
parent
338c5302e3
commit
030be79192
65
Cargo.lock
generated
65
Cargo.lock
generated
|
@ -574,8 +574,8 @@ version = "1.0.4"
|
|||
dependencies = [
|
||||
"eframe",
|
||||
"egui_extras",
|
||||
"env_logger",
|
||||
"image",
|
||||
"nix 0.27.1",
|
||||
"rfd",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -1035,18 +1035,6 @@ dependencies = [
|
|||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece"
|
||||
dependencies = [
|
||||
"humantime",
|
||||
"is-terminal",
|
||||
"log",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "epaint"
|
||||
version = "0.25.0"
|
||||
|
@ -1554,12 +1542,6 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "icrate"
|
||||
version = "0.0.4"
|
||||
|
@ -1629,17 +1611,6 @@ dependencies = [
|
|||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"rustix 0.38.30",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
|
@ -1873,6 +1844,17 @@ dependencies = [
|
|||
"memoffset 0.7.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nohash-hasher"
|
||||
version = "0.2.0"
|
||||
|
@ -2642,15 +2624,6 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.56"
|
||||
|
@ -2976,7 +2949,7 @@ checksum = "19152ddd73f45f024ed4534d9ca2594e0ef252c1847695255dae47f34df9fbe4"
|
|||
dependencies = [
|
||||
"cc",
|
||||
"downcast-rs",
|
||||
"nix",
|
||||
"nix 0.26.4",
|
||||
"scoped-tls",
|
||||
"smallvec",
|
||||
"wayland-sys",
|
||||
|
@ -2989,7 +2962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "1ca7d52347346f5473bf2f56705f360e8440873052e575e55890c4fa57843ed3"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"nix",
|
||||
"nix 0.26.4",
|
||||
"wayland-backend",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
@ -3011,7 +2984,7 @@ version = "0.31.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a44aa20ae986659d6c77d64d808a046996a932aa763913864dc40c359ef7ad5b"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"nix 0.26.4",
|
||||
"wayland-client",
|
||||
"xcursor",
|
||||
]
|
||||
|
@ -3467,7 +3440,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "b1641b26d4dec61337c35a1b1aaf9e3cba8f46f0b43636c609ab0291a648040a"
|
||||
dependencies = [
|
||||
"gethostname 0.3.0",
|
||||
"nix",
|
||||
"nix 0.26.4",
|
||||
"winapi",
|
||||
"winapi-wsapoll",
|
||||
"x11rb-protocol 0.12.0",
|
||||
|
@ -3494,7 +3467,7 @@ version = "0.12.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82d6c3f9a0fb6701fab8f6cea9b0c0bd5d6876f1f89f7fada07e558077c344bc"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"nix 0.26.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3515,7 +3488,7 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"nix 0.26.4",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
|
@ -3568,7 +3541,7 @@ dependencies = [
|
|||
"futures-sink",
|
||||
"futures-util",
|
||||
"hex",
|
||||
"nix",
|
||||
"nix 0.26.4",
|
||||
"once_cell",
|
||||
"ordered-stream",
|
||||
"rand",
|
||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -7,14 +7,14 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
eframe = "0.25.0"
|
||||
env_logger = { version = "0.10.1", default-features = false, features = [
|
||||
"auto-color",
|
||||
"humantime",
|
||||
] }
|
||||
# env_logger = { version = "0.10.1", default-features = false, features = [
|
||||
# "auto-color",
|
||||
# "humantime",
|
||||
# ] }
|
||||
rfd = "0.12.1"
|
||||
egui_extras = "0.25.0"
|
||||
image = "0.24.8"
|
||||
serde = { version = "1.0.195", features = ["derive"] }
|
||||
serde_json = "1.0.111"
|
||||
|
||||
nix = { version = "0.27.1", features = ["fs"] }
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"tabs":["/home/penwing/Documents/notes/victory2.txt"],"theme":6}
|
||||
{"tabs":["/home/penwing/Documents/projects/rust/calcifer/README.md","/home/penwing/Documents/projects/rust/calcifer/Cargo.toml","/home/penwing/Documents/projects/rust/calcifer/src/main.rs","/home/penwing/Documents/projects/rust/calcifer/src/calcifer.rs","/home/penwing/Documents/projects/rust/calcifer/src/tools/mod.rs","/home/penwing/Documents/projects/rust/calcifer/src/tools/search.rs","/home/penwing/Documents/projects/rust/calcifer/src/calcifer/app_base.rs","/home/penwing/Documents/projects/rust/calcifer/src/tools/tabs.rs","/home/penwing/Documents/projects/rust/calcifer/src/tools/confirm.rs"],"theme":5}
|
|
@ -100,7 +100,8 @@ impl Calcifer {
|
|||
ui.separator();
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.spacing_mut().item_spacing.y = 0.0;
|
||||
for entry in &self.command_history {
|
||||
for entry in &mut self.command_history {
|
||||
entry.update();
|
||||
ui.colored_label(command_color, format!("\n{} {}", entry.env, entry.command));
|
||||
ui.end_row();
|
||||
if entry.output != "" {
|
||||
|
|
|
@ -1,144 +0,0 @@
|
|||
// Hello there, Master
|
||||
|
||||
impl super::Calcifer {
|
||||
pub fn handle_confirm(&mut self) {
|
||||
if self.close_tab_confirm.proceed {
|
||||
self.close_tab_confirm.close();
|
||||
self.delete_tab(self.tab_to_close);
|
||||
}
|
||||
}
|
||||
pub fn save_tab(&self) -> Option<PathBuf> {
|
||||
if self.tabs[self.selected_tab.to_index()].path.file_name().expect("Could not get Tab Name").to_string_lossy().to_string() == "untitled" {
|
||||
return self.save_tab_as();
|
||||
} else {
|
||||
if let Err(err) = fs::write(&self.tabs[self.selected_tab.to_index()].path, &self.tabs[self.selected_tab.to_index()].code) {
|
||||
eprintln!("Error writing file: {}", err);
|
||||
return None;
|
||||
}
|
||||
return Some(self.tabs[self.selected_tab.to_index()].path.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_tab_as(&self) -> Option<PathBuf> {
|
||||
if let Some(path) = rfd::FileDialog::new().set_directory(Path::new(&PATH_ROOT)).save_file() {
|
||||
if let Err(err) = fs::write(&path, &self.tabs[self.selected_tab.to_index()].code) {
|
||||
eprintln!("Error writing file: {}", err);
|
||||
return None;
|
||||
}
|
||||
return Some(path);
|
||||
}
|
||||
return None
|
||||
}
|
||||
|
||||
pub fn handle_save_file(&mut self, path_option : Option<PathBuf>) {
|
||||
if let Some(path) = path_option {
|
||||
println!("File saved successfully at: {:?}", path);
|
||||
self.tabs[self.selected_tab.to_index()].path = path;
|
||||
self.tabs[self.selected_tab.to_index()].saved = true;
|
||||
} else {
|
||||
println!("File save failed.");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_app_state(app_state: tools::AppState) -> Self {
|
||||
let mut new = Self {
|
||||
theme: DEFAULT_THEMES[min(app_state.theme, DEFAULT_THEMES.len() - 1)],
|
||||
tabs: Vec::new(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
for path in app_state.tabs {
|
||||
if path.file_name().expect("Could not get Tab Name").to_string_lossy().to_string() != "untitled" {
|
||||
new.open_file(Some(&path));
|
||||
}
|
||||
}
|
||||
|
||||
if new.tabs == vec![] {
|
||||
new.open_file(None);
|
||||
}
|
||||
|
||||
new
|
||||
}
|
||||
|
||||
pub fn save_state(&self) {
|
||||
let mut state_theme : usize = 0;
|
||||
if let Some(theme) = DEFAULT_THEMES.iter().position(|&r| r == self.theme) {
|
||||
state_theme = theme;
|
||||
}
|
||||
|
||||
let mut state_tabs = vec![];
|
||||
|
||||
for tab in &self.tabs {
|
||||
state_tabs.push(tab.path.clone());
|
||||
}
|
||||
let app_state = tools::AppState {
|
||||
tabs: state_tabs,
|
||||
theme: state_theme,
|
||||
};
|
||||
|
||||
let _ = tools::save_state(&app_state, super::SAVE_PATH);
|
||||
}
|
||||
|
||||
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<()> {
|
||||
if let Some(name) = path.file_name() {
|
||||
if path.is_dir() {
|
||||
egui::CollapsingHeader::new(name.to_string_lossy()).show(ui, |ui| {
|
||||
let mut paths: Vec<_> = fs::read_dir(&path).expect("Failed to read dir").map(|r| r.unwrap()).collect();
|
||||
|
||||
// Sort the vector using the custom sorting function
|
||||
paths.sort_by(|a, b| tools::sort_directories_first(a, b));
|
||||
|
||||
for result in paths {
|
||||
//let result = path_result.expect("Failed to get path");
|
||||
//let full_path = result.path();
|
||||
let _ = self.list_files(ui, &result.path());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
//ui.label(name.to_string_lossy());
|
||||
if ui.button(name.to_string_lossy()).clicked() {
|
||||
self.open_file(Some(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn open_file(&mut self, path_option: Option<&Path>) {
|
||||
if self.tabs.len() < MAX_TABS {
|
||||
if let Some(path) = path_option {
|
||||
self.tabs.push(tools::Tab::new(path.to_path_buf()));
|
||||
} else {
|
||||
self.tabs.push(tools::Tab::default());
|
||||
}
|
||||
self.selected_tab = tools::TabNumber::from_index(self.tabs.len() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
fn delete_tab(&mut self, index : usize) {
|
||||
self.tabs.remove(index);
|
||||
self.selected_tab = 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
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ use calcifer::code_editor::themes::DEFAULT_THEMES;
|
|||
|
||||
const TERMINAL_HEIGHT : f32 = 200.0;
|
||||
const RED : egui::Color32 = egui::Color32::from_rgb(235, 108, 99);
|
||||
const SAVE_PATH : &str = "calcifer_save.json";
|
||||
const SAVE_PATH : &str = "/home/penwing/Documents/.saves/calcifer_save.json";
|
||||
const TIME_LABELS : [&str; 5] = ["settings", "tree", "terminal", "tabs", "content"];
|
||||
const MAX_FPS : f32 = 30.0;
|
||||
const PATH_ROOT : &str = "/home/penwing/Documents/";
|
||||
|
@ -20,7 +20,7 @@ const MAX_TABS : usize = 20;
|
|||
fn main() -> Result<(), eframe::Error> {
|
||||
let icon_data = tools::load_icon();
|
||||
|
||||
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
||||
//env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
||||
let options = eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default()
|
||||
.with_inner_size([1200.0, 800.0])
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{cmp::Ordering, path::PathBuf, path::Path, fs::read_to_string, fs::write, path::Component, ffi::OsStr};
|
||||
use std::{cmp::Ordering, path::PathBuf, path::Path, fs, fs::read_to_string, io::Write, path::Component, ffi::OsStr, fs::OpenOptions};
|
||||
use crate::calcifer::code_editor::Syntax;
|
||||
use eframe::egui;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
@ -25,7 +25,17 @@ pub struct AppState {
|
|||
pub fn save_state(state: &AppState, file_path: &str) -> Result<(), std::io::Error> {
|
||||
let serialized_state = serde_json::to_string(state)?;
|
||||
|
||||
write(file_path, serialized_state)?;
|
||||
if let Some(parent_dir) = Path::new(file_path).parent() {
|
||||
fs::create_dir_all(parent_dir)?;
|
||||
}
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(file_path)?;
|
||||
|
||||
file.write_all(serialized_state.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,68 +1,92 @@
|
|||
use crate::tools::format_path;
|
||||
use std::{process::Command, env, path::Path};
|
||||
use std::io::BufReader;
|
||||
use std::io::BufRead;
|
||||
use std::process::Stdio;
|
||||
use nix::fcntl::{fcntl, FcntlArg, OFlag};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct CommandEntry {
|
||||
pub env: String,
|
||||
pub command: String,
|
||||
pub output: String,
|
||||
pub error: String,
|
||||
pub output_buffer: BufReader<std::process::ChildStdout>,
|
||||
}
|
||||
|
||||
|
||||
impl CommandEntry {
|
||||
pub fn new(command: String) -> Self {
|
||||
let stdout_reader = execute(command.clone());
|
||||
|
||||
CommandEntry {
|
||||
env: format_path(&env::current_dir().expect("Could not find Shell Environnment")),
|
||||
command,
|
||||
output: String::new(),
|
||||
error: String::new(),
|
||||
output_buffer: stdout_reader,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> Self {
|
||||
let output = Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(self.command.clone())
|
||||
.output()
|
||||
.expect("failed to execute process");
|
||||
self.output = (&String::from_utf8_lossy(&output.stdout)).trim_end_matches('\n').to_string();
|
||||
self.error = (&String::from_utf8_lossy(&output.stderr)).trim_end_matches('\n').to_string();
|
||||
|
||||
self.clone()
|
||||
pub fn update(&mut self) {
|
||||
let mut output = String::new();
|
||||
let _ = self.output_buffer.read_line(&mut output);
|
||||
if !output.is_empty() {
|
||||
self.output += &output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn run_command(command: String) -> CommandEntry {
|
||||
let mut entry = CommandEntry::new(command);
|
||||
|
||||
if entry.command.len() < 2 {
|
||||
return entry.run();
|
||||
if command.len() < 2 {
|
||||
return CommandEntry::new(command);
|
||||
}
|
||||
|
||||
if &entry.command[..2] != "cd" {
|
||||
return entry.run()
|
||||
if &command[..2] != "cd" {
|
||||
return CommandEntry::new(command)
|
||||
}
|
||||
|
||||
if entry.command.len() < 4 {
|
||||
entry.error = "Invalid cd, should provide path".to_string();
|
||||
if command.len() < 4 {
|
||||
let mut entry = CommandEntry::new("echo Invalid cd, should provide path >&2".to_string());
|
||||
entry.command = command;
|
||||
return entry
|
||||
}
|
||||
|
||||
let path_append = entry.command[3..].replace("~", "/home/penwing");
|
||||
let path_append = command[3..].replace("~", "/home/penwing");
|
||||
let path = Path::new(&path_append);
|
||||
|
||||
if format!("{}", path.display()) == "/" {
|
||||
entry.error = "Root access denied".to_string();
|
||||
let mut entry = CommandEntry::new("echo Root access denied >&2".to_string());
|
||||
entry.command = command;
|
||||
return entry
|
||||
}
|
||||
|
||||
if env::set_current_dir(path).is_ok() {
|
||||
entry.output = format!("Moved to : {}", path.display());
|
||||
let mut entry = CommandEntry::new(format!("echo Moved to : {}", path.display()));
|
||||
entry.command = command;
|
||||
return entry
|
||||
} else {
|
||||
entry.error = format!("Could not find path : {}", path.display());
|
||||
let mut entry = CommandEntry::new(format!("echo Could not find path : {} >&2", path.display()));
|
||||
entry.command = command;
|
||||
return entry
|
||||
}
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
|
||||
pub fn execute(command: String) -> BufReader<std::process::ChildStdout> {
|
||||
let mut child = Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(command.clone())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.expect("failed to execute process");
|
||||
|
||||
let stdout = child.stdout.take().unwrap();
|
||||
let stdout_fd = stdout.as_raw_fd();
|
||||
|
||||
fcntl(stdout_fd, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)).expect("Failed to set non-blocking mode");
|
||||
|
||||
return BufReader::new(stdout);
|
||||
}
|
Loading…
Reference in a new issue