better format
This commit is contained in:
parent
fd1e818447
commit
c8ce57781e
|
@ -1,18 +1,17 @@
|
|||
use eframe::egui;
|
||||
use egui::{text::CCursor, text_edit::CCursorRange, Rangef};
|
||||
use std::{env, path::Path, cmp::max};
|
||||
use std::{cmp::max, env, path::Path};
|
||||
|
||||
use crate::tools;
|
||||
use crate::Calcifer;
|
||||
use crate::PATH_ROOT;
|
||||
use crate::MAX_TABS;
|
||||
use crate::PATH_ROOT;
|
||||
|
||||
pub mod code_editor;
|
||||
use code_editor::CodeEditor;
|
||||
|
||||
mod app_base;
|
||||
|
||||
|
||||
impl Calcifer {
|
||||
pub fn draw_settings(&mut self, ctx: &egui::Context) {
|
||||
egui::SidePanel::left("settings")
|
||||
|
@ -21,7 +20,10 @@ impl Calcifer {
|
|||
.show(ctx, |ui| {
|
||||
ui.vertical(|ui| {
|
||||
if ui.add(egui::Button::new("📁")).clicked() {
|
||||
if let Some(path) = rfd::FileDialog::new().set_directory(Path::new(&PATH_ROOT)).pick_file() {
|
||||
if let Some(path) = rfd::FileDialog::new()
|
||||
.set_directory(Path::new(&PATH_ROOT))
|
||||
.pick_file()
|
||||
{
|
||||
self.open_file(Some(&path));
|
||||
}
|
||||
}
|
||||
|
@ -41,10 +43,9 @@ impl Calcifer {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
pub fn draw_tree_panel(&mut self, ctx: &egui::Context) {
|
||||
if !self.tree_visible {
|
||||
return
|
||||
return;
|
||||
}
|
||||
egui::SidePanel::left("file_tree_panel").show(ctx, |ui| {
|
||||
ui.heading("Bookshelf");
|
||||
|
@ -63,20 +64,25 @@ impl Calcifer {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
pub fn draw_terminal_panel(&mut self, ctx: &egui::Context) {
|
||||
if !self.terminal_visible {
|
||||
return
|
||||
return;
|
||||
}
|
||||
egui::TopBottomPanel::bottom("terminal")
|
||||
.default_height(super::TERMINAL_HEIGHT.clone())
|
||||
.height_range(Rangef::new(super::TERMINAL_RANGE.start, super::TERMINAL_RANGE.end))
|
||||
.height_range(Rangef::new(
|
||||
super::TERMINAL_RANGE.start,
|
||||
super::TERMINAL_RANGE.end,
|
||||
))
|
||||
.resizable(true)
|
||||
.show(ctx, |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)");
|
||||
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("");
|
||||
|
||||
|
@ -86,24 +92,39 @@ impl Calcifer {
|
|||
}
|
||||
ui.style_mut().visuals.extreme_bg_color = bg_color;
|
||||
let Self { command, .. } = self;
|
||||
ui.colored_label(command_color.clone(), tools::format_path(&env::current_dir().expect("Could not find Shell Environnment")));
|
||||
let response = ui.add(egui::TextEdit::singleline(command).desired_width(f32::INFINITY).lock_focus(true));
|
||||
ui.colored_label(
|
||||
command_color.clone(),
|
||||
tools::format_path(
|
||||
&env::current_dir().expect("Could not find Shell Environnment"),
|
||||
),
|
||||
);
|
||||
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)) {
|
||||
self.command_history.push(tools::send_command(self.command.clone()));
|
||||
self.command_history
|
||||
.push(tools::send_command(self.command.clone()));
|
||||
self.command = "".into();
|
||||
response.request_focus();
|
||||
}
|
||||
});
|
||||
ui.separator();
|
||||
egui::ScrollArea::vertical().stick_to_bottom(true).show(ui, |ui| {
|
||||
egui::ScrollArea::vertical()
|
||||
.stick_to_bottom(true)
|
||||
.show(ui, |ui| {
|
||||
ui.with_layout(egui::Layout::top_down(egui::Align::LEFT), |ui| {
|
||||
ui.separator();
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.spacing_mut().item_spacing.y = 0.0;
|
||||
for entry in &mut self.command_history {
|
||||
entry.update();
|
||||
ui.colored_label(command_color, format!("\n{} {}", entry.env, entry.command));
|
||||
ui.colored_label(
|
||||
command_color,
|
||||
format!("\n{} {}", entry.env, entry.command),
|
||||
);
|
||||
ui.end_row();
|
||||
if entry.output != "" {
|
||||
ui.colored_label(entry_color, &entry.output);
|
||||
|
@ -121,23 +142,33 @@ impl Calcifer {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
pub fn draw_tab_panel(&mut self, ctx: &egui::Context) {
|
||||
egui::TopBottomPanel::top("tabs")
|
||||
.resizable(false)
|
||||
.show(ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.style_mut().visuals.selection.bg_fill = egui::Color32::from_hex(self.theme.functions).expect("Could not convert color");
|
||||
ui.style_mut().visuals.hyperlink_color = egui::Color32::from_hex(self.theme.functions).expect("Could not convert color");
|
||||
ui.style_mut().visuals.selection.bg_fill =
|
||||
egui::Color32::from_hex(self.theme.functions)
|
||||
.expect("Could not convert color");
|
||||
ui.style_mut().visuals.hyperlink_color =
|
||||
egui::Color32::from_hex(self.theme.functions)
|
||||
.expect("Could not convert color");
|
||||
for (index, tab) in self.tabs.clone().iter().enumerate() {
|
||||
let mut title = tab.get_name();
|
||||
if !tab.saved {
|
||||
title += " ~";
|
||||
}
|
||||
if self.selected_tab == tools::TabNumber::from_index(index) {
|
||||
ui.style_mut().visuals.override_text_color = Some(egui::Color32::from_hex(self.theme.bg).expect("Could not convert color"));
|
||||
ui.style_mut().visuals.override_text_color = Some(
|
||||
egui::Color32::from_hex(self.theme.bg)
|
||||
.expect("Could not convert color"),
|
||||
);
|
||||
}
|
||||
ui.selectable_value(&mut self.selected_tab, tools::TabNumber::from_index(index), title);
|
||||
ui.selectable_value(
|
||||
&mut self.selected_tab,
|
||||
tools::TabNumber::from_index(index),
|
||||
title,
|
||||
);
|
||||
|
||||
ui.style_mut().visuals.override_text_color = None;
|
||||
|
||||
|
@ -165,7 +196,6 @@ impl Calcifer {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
pub fn draw_content_panel(&mut self, ctx: &egui::Context) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
|
@ -176,7 +206,12 @@ impl Calcifer {
|
|||
}
|
||||
|
||||
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();
|
||||
|
@ -185,7 +220,6 @@ impl Calcifer {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
fn draw_code_file(&mut self, ui: &mut egui::Ui) {
|
||||
let current_tab = &mut self.tabs[self.selected_tab.to_index()];
|
||||
let lines = current_tab.code.chars().filter(|&c| c == '\n').count() + 1;
|
||||
|
@ -199,18 +233,27 @@ impl Calcifer {
|
|||
self.search_menu.result_selected = true;
|
||||
}
|
||||
|
||||
CodeEditor::default().id_source("code editor")
|
||||
CodeEditor::default()
|
||||
.id_source("code editor")
|
||||
.with_rows(max(45, lines))
|
||||
.with_fontsize(self.font_size)
|
||||
.with_theme(self.theme)
|
||||
.with_syntax(tools::to_syntax(¤t_tab.language))
|
||||
.with_numlines(true)
|
||||
.show(ui, &mut current_tab.code, &mut current_tab.saved, &mut current_tab.last_cursor, &mut current_tab.scroll_offset, override_cursor);
|
||||
.show(
|
||||
ui,
|
||||
&mut current_tab.code,
|
||||
&mut current_tab.saved,
|
||||
&mut current_tab.last_cursor,
|
||||
&mut current_tab.scroll_offset,
|
||||
override_cursor,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn draw_windows(&mut self, ctx: &egui::Context) {
|
||||
if self.search_menu.visible {
|
||||
self.search_menu.show(ctx, &mut self.tabs, &mut self.selected_tab);
|
||||
self.search_menu
|
||||
.show(ctx, &mut self.tabs, &mut self.selected_tab);
|
||||
}
|
||||
if self.close_tab_confirm.visible {
|
||||
self.close_tab_confirm.show(ctx);
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
use std::{path::PathBuf, fs, path::Path, cmp::min, io};
|
||||
use eframe::egui;
|
||||
use egui::Color32;
|
||||
use std::{cmp::min, fs, io, path::Path, path::PathBuf};
|
||||
|
||||
use crate::Calcifer;
|
||||
use crate::tools;
|
||||
use crate::PATH_ROOT;
|
||||
use crate::Calcifer;
|
||||
use crate::DEFAULT_THEMES;
|
||||
use crate::MAX_TABS;
|
||||
use crate::PATH_ROOT;
|
||||
use crate::SAVE_PATH;
|
||||
use crate::TIME_LABELS;
|
||||
|
||||
|
||||
impl Calcifer {
|
||||
pub fn handle_confirm(&mut self) {
|
||||
if self.close_tab_confirm.proceed {
|
||||
|
@ -25,30 +24,41 @@ impl Calcifer {
|
|||
}
|
||||
|
||||
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" {
|
||||
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) {
|
||||
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())
|
||||
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 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
|
||||
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);
|
||||
|
@ -59,7 +69,6 @@ impl Calcifer {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
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)],
|
||||
|
@ -69,7 +78,13 @@ impl Calcifer {
|
|||
};
|
||||
|
||||
for path in app_state.tabs {
|
||||
if path.file_name().expect("Could not get Tab Name").to_string_lossy().to_string() != "untitled" {
|
||||
if path
|
||||
.file_name()
|
||||
.expect("Could not get Tab Name")
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
!= "untitled"
|
||||
{
|
||||
new.open_file(Some(&path));
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +96,6 @@ impl Calcifer {
|
|||
new
|
||||
}
|
||||
|
||||
|
||||
pub fn save_state(&self) {
|
||||
let mut state_theme: usize = 0;
|
||||
if let Some(theme) = DEFAULT_THEMES.iter().position(|&r| r == self.theme) {
|
||||
|
@ -101,22 +115,26 @@ impl Calcifer {
|
|||
let _ = tools::save_state(&app_state, 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
|
||||
.to_index()
|
||||
.checked_sub(1)
|
||||
.unwrap_or(self.tabs.len() - 1)
|
||||
};
|
||||
self.selected_tab = tools::TabNumber::from_index(new_index);
|
||||
}
|
||||
|
||||
|
||||
pub 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();
|
||||
let mut paths: Vec<_> = fs::read_dir(&path)
|
||||
.expect("Failed to read dir")
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
|
||||
paths.sort_by(|a, b| tools::sort_directories_first(a, b));
|
||||
|
||||
|
@ -133,7 +151,6 @@ impl Calcifer {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
pub fn open_file(&mut self, path_option: Option<&Path>) {
|
||||
if self.tabs.len() < MAX_TABS {
|
||||
if let Some(path) = path_option {
|
||||
|
@ -145,44 +162,52 @@ impl Calcifer {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn delete_tab(&mut self, index: usize) {
|
||||
self.tabs.remove(index);
|
||||
self.selected_tab = tools::TabNumber::from_index(min(index, self.tabs.len() - 1));
|
||||
}
|
||||
|
||||
|
||||
pub fn toggle(&self, ui: &mut egui::Ui, display: bool, title: &str) -> bool {
|
||||
let bg_color: Color32;
|
||||
let text_color: Color32;
|
||||
|
||||
if display.clone() {
|
||||
bg_color = Color32::from_hex(self.theme.functions).expect("Could not convert color to hex (functions)");
|
||||
text_color = Color32::from_hex(self.theme.bg).expect("Could not convert color to hex (bg)");
|
||||
bg_color = Color32::from_hex(self.theme.functions)
|
||||
.expect("Could not convert color to hex (functions)");
|
||||
text_color =
|
||||
Color32::from_hex(self.theme.bg).expect("Could not convert color to hex (bg)");
|
||||
} else {
|
||||
bg_color = Color32::from_hex(self.theme.bg).expect("Could not convert color to hex (bg)");
|
||||
text_color = Color32::from_hex(self.theme.literals).expect("Could not convert color to hex (literals)");
|
||||
bg_color =
|
||||
Color32::from_hex(self.theme.bg).expect("Could not convert color to hex (bg)");
|
||||
text_color = Color32::from_hex(self.theme.literals)
|
||||
.expect("Could not convert color to hex (literals)");
|
||||
};
|
||||
|
||||
ui.style_mut().visuals.override_text_color = Some(text_color);
|
||||
|
||||
if ui.add(egui::Button::new(title).fill(bg_color)).clicked() {
|
||||
return !display
|
||||
return !display;
|
||||
}
|
||||
ui.style_mut().visuals.override_text_color = None;
|
||||
|
||||
return display
|
||||
return display;
|
||||
}
|
||||
|
||||
pub fn profiler(&self) -> String {
|
||||
if !self.profiler_visible {
|
||||
return "".to_string()
|
||||
return "".to_string();
|
||||
}
|
||||
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();
|
||||
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();
|
||||
|
||||
let mut result = combined_string.join(" ; ");
|
||||
result.push_str(&format!(" total : {:.1} ms", self.time_watch.clone().iter().sum::<f32>()));
|
||||
return result
|
||||
result.push_str(&format!(
|
||||
" total : {:.1} ms",
|
||||
self.time_watch.clone().iter().sum::<f32>()
|
||||
));
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
use super::syntax::{Syntax, TokenType, QUOTES, SEPARATORS};
|
||||
use std::mem;
|
||||
use super::CodeEditor;
|
||||
use eframe::egui::text::LayoutJob;
|
||||
use eframe::egui;
|
||||
use eframe::egui::text::LayoutJob;
|
||||
use std::mem;
|
||||
|
||||
#[derive(Default, Debug, PartialEq, PartialOrd, Eq, Ord)]
|
||||
/// Lexer and Token
|
||||
|
@ -222,8 +222,6 @@ impl Token {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl eframe::egui::util::cache::ComputerMut<(&CodeEditor, &str), LayoutJob> for Token {
|
||||
fn compute(&mut self, (cache, text): (&CodeEditor, &str)) -> LayoutJob {
|
||||
self.highlight(cache, text)
|
||||
|
@ -236,7 +234,6 @@ pub fn highlight(ctx: &egui::Context, cache: &CodeEditor, text: &str) -> LayoutJ
|
|||
ctx.memory_mut(|mem| mem.caches.cache::<HighlightCache>().get((cache, text)))
|
||||
}
|
||||
|
||||
|
||||
impl CodeEditor {
|
||||
fn append(&self, job: &mut LayoutJob, token: &Token) {
|
||||
job.append(token.buffer(), 0.0, self.format(token.ty()));
|
||||
|
|
|
@ -5,14 +5,13 @@ mod syntax;
|
|||
pub mod themes;
|
||||
|
||||
use eframe::egui;
|
||||
use egui::{text_edit::CCursorRange, text::CCursor};
|
||||
use egui::{text::CCursor, text_edit::CCursorRange};
|
||||
use highlighting::highlight;
|
||||
use std::cmp::{max, min};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Bound, RangeBounds};
|
||||
pub use syntax::{Syntax, TokenType};
|
||||
pub use themes::ColorTheme;
|
||||
use std::cmp::{min, max};
|
||||
use std::ops::{Bound, RangeBounds};
|
||||
|
||||
|
||||
trait StringUtils {
|
||||
fn substring(&self, start: usize, len: usize) -> &str;
|
||||
|
@ -26,22 +25,28 @@ impl StringUtils for str {
|
|||
let mut byte_start = 0;
|
||||
let mut it = self.chars();
|
||||
loop {
|
||||
if char_pos == start { break; }
|
||||
if char_pos == start {
|
||||
break;
|
||||
}
|
||||
if let Some(c) = it.next() {
|
||||
char_pos += 1;
|
||||
byte_start += c.len_utf8();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
else { break; }
|
||||
}
|
||||
char_pos = 0;
|
||||
let mut byte_end = byte_start;
|
||||
loop {
|
||||
if char_pos == len { break; }
|
||||
if char_pos == len {
|
||||
break;
|
||||
}
|
||||
if let Some(c) = it.next() {
|
||||
char_pos += 1;
|
||||
byte_end += c.len_utf8();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
else { break; }
|
||||
}
|
||||
&self[byte_start..byte_end]
|
||||
}
|
||||
|
@ -64,7 +69,6 @@ impl StringUtils for str {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
/// CodeEditor struct which stores settings for highlighting.
|
||||
pub struct CodeEditor {
|
||||
|
@ -237,7 +241,15 @@ impl CodeEditor {
|
|||
}
|
||||
|
||||
/// Show Code Editor
|
||||
pub fn show(&mut self, ui: &mut egui::Ui, text: &mut String, saved: &mut bool, last_cursor: &mut Option<CCursorRange>, vertical_offset: &mut f32, override_cursor: Option<CCursorRange>) {
|
||||
pub fn show(
|
||||
&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
text: &mut String,
|
||||
saved: &mut bool,
|
||||
last_cursor: &mut Option<CCursorRange>,
|
||||
vertical_offset: &mut f32,
|
||||
override_cursor: Option<CCursorRange>,
|
||||
) {
|
||||
//let mut text_edit_output: Option<TextEditOutput> = None;
|
||||
let mut code_editor = |ui: &mut egui::Ui| {
|
||||
ui.horizontal_top(|h| {
|
||||
|
@ -267,33 +279,50 @@ impl CodeEditor {
|
|||
let mut get_new_cursor: bool = true;
|
||||
let mut extend: isize = 0;
|
||||
|
||||
if output.response.has_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
|
||||
if output.response.has_focus()
|
||||
&& ui.input(|i| i.key_pressed(egui::Key::Enter))
|
||||
{
|
||||
if let Some(range) = last_cursor {
|
||||
(*text, extend) = self.new_line(range.clone(), text.clone());
|
||||
get_new_cursor = false;
|
||||
}
|
||||
}
|
||||
|
||||
if output.response.has_focus() && ui.input(|i| i.key_pressed(egui::Key::E) && i.modifiers.ctrl) {
|
||||
if output.response.has_focus()
|
||||
&& ui.input(|i| i.key_pressed(egui::Key::E) && i.modifiers.ctrl)
|
||||
{
|
||||
if let Some(range) = last_cursor {
|
||||
(*text, extend) = self.toggle_start_of_line(range.clone(), text.clone(), "//");
|
||||
(*text, extend) =
|
||||
self.toggle_start_of_line(range.clone(), text.clone(), "//");
|
||||
get_new_cursor = false;
|
||||
}
|
||||
}
|
||||
|
||||
if output.response.has_focus() && ui.input(|i| i.key_pressed(egui::Key::Tab)) {
|
||||
if output.response.has_focus()
|
||||
&& ui.input(|i| i.key_pressed(egui::Key::Tab))
|
||||
{
|
||||
if let Some(range) = last_cursor {
|
||||
if range.primary.index != range.secondary.index {
|
||||
(*text, extend) = self.add_start_of_line(range.clone(), previous_text.clone(), "\t");
|
||||
(*text, extend) = self.add_start_of_line(
|
||||
range.clone(),
|
||||
previous_text.clone(),
|
||||
"\t",
|
||||
);
|
||||
get_new_cursor = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if output.response.has_focus() && ui.input(|i| i.key_pressed(egui::Key::Tab) && i.modifiers.shift) {
|
||||
if output.response.has_focus()
|
||||
&& ui.input(|i| i.key_pressed(egui::Key::Tab) && i.modifiers.shift)
|
||||
{
|
||||
if let Some(range) = last_cursor {
|
||||
if range.primary.index != range.secondary.index {
|
||||
(*text, extend) = self.remove_start_of_line(range.clone(), previous_text.clone(), "\t");
|
||||
(*text, extend) = self.remove_start_of_line(
|
||||
range.clone(),
|
||||
previous_text.clone(),
|
||||
"\t",
|
||||
);
|
||||
get_new_cursor = false;
|
||||
}
|
||||
}
|
||||
|
@ -307,8 +336,10 @@ impl CodeEditor {
|
|||
*last_cursor = output.state.clone().ccursor_range();
|
||||
} else {
|
||||
if let Some(cursor_range) = last_cursor.clone() {
|
||||
let mut start = min(cursor_range.primary.index, cursor_range.secondary.index);
|
||||
let end = max(cursor_range.primary.index, cursor_range.secondary.index);
|
||||
let mut start =
|
||||
min(cursor_range.primary.index, cursor_range.secondary.index);
|
||||
let end =
|
||||
max(cursor_range.primary.index, cursor_range.secondary.index);
|
||||
let extended = match end as isize + extend {
|
||||
// Check for overflow or negative result
|
||||
value if value < 0 => 0,
|
||||
|
@ -348,8 +379,15 @@ impl CodeEditor {
|
|||
//text_edit_output.expect("TextEditOutput should exist at this point")
|
||||
}
|
||||
|
||||
fn toggle_start_of_line(&self, cursor_range : CCursorRange, text : String, head : &str) -> (String, isize) {
|
||||
let mut substring = self.get_selection_substring(text.clone(), cursor_range.clone()).clone();
|
||||
fn toggle_start_of_line(
|
||||
&self,
|
||||
cursor_range: CCursorRange,
|
||||
text: String,
|
||||
head: &str,
|
||||
) -> (String, isize) {
|
||||
let mut substring = self
|
||||
.get_selection_substring(text.clone(), cursor_range.clone())
|
||||
.clone();
|
||||
let mut new_text: String = "".into();
|
||||
let extend: isize;
|
||||
|
||||
|
@ -364,12 +402,18 @@ impl CodeEditor {
|
|||
new_text.push_str(&substring[1].clone());
|
||||
new_text.push_str(&substring[2].clone());
|
||||
|
||||
return (new_text, extend)
|
||||
return (new_text, extend);
|
||||
}
|
||||
|
||||
|
||||
fn add_start_of_line(&self, cursor_range : CCursorRange, text : String, head : &str) -> (String, isize) {
|
||||
let mut substring = self.get_selection_substring(text.clone(), cursor_range.clone()).clone();
|
||||
fn add_start_of_line(
|
||||
&self,
|
||||
cursor_range: CCursorRange,
|
||||
text: String,
|
||||
head: &str,
|
||||
) -> (String, isize) {
|
||||
let mut substring = self
|
||||
.get_selection_substring(text.clone(), cursor_range.clone())
|
||||
.clone();
|
||||
let mut new_text: String = "".into();
|
||||
|
||||
let extend: isize = self.delta_char(substring[1].clone(), head);
|
||||
|
@ -379,11 +423,18 @@ impl CodeEditor {
|
|||
new_text.push_str(&substring[1].clone());
|
||||
new_text.push_str(&substring[2].clone());
|
||||
|
||||
return (new_text, extend)
|
||||
return (new_text, extend);
|
||||
}
|
||||
|
||||
fn remove_start_of_line(&self, cursor_range : CCursorRange, text : String, head : &str) -> (String, isize) {
|
||||
let mut substring = self.get_selection_substring(text.clone(), cursor_range.clone()).clone();
|
||||
fn remove_start_of_line(
|
||||
&self,
|
||||
cursor_range: CCursorRange,
|
||||
text: String,
|
||||
head: &str,
|
||||
) -> (String, isize) {
|
||||
let mut substring = self
|
||||
.get_selection_substring(text.clone(), cursor_range.clone())
|
||||
.clone();
|
||||
let mut new_text: String = "".into();
|
||||
|
||||
let extend: isize = -self.delta_char(substring[1].clone(), head);
|
||||
|
@ -393,7 +444,7 @@ impl CodeEditor {
|
|||
new_text.push_str(&substring[1].clone());
|
||||
new_text.push_str(&substring[2].clone());
|
||||
|
||||
return (new_text, extend)
|
||||
return (new_text, extend);
|
||||
}
|
||||
|
||||
fn get_selection_substring(&self, text: String, cursor_range: CCursorRange) -> Vec<String> {
|
||||
|
@ -408,11 +459,19 @@ impl CodeEditor {
|
|||
|
||||
let last_char = end;
|
||||
|
||||
return vec![text.slice(..first_char).to_string(), text.slice(first_char..last_char).to_string(), text.slice(last_char..).to_string()];
|
||||
return vec![
|
||||
text.slice(..first_char).to_string(),
|
||||
text.slice(first_char..last_char).to_string(),
|
||||
text.slice(last_char..).to_string(),
|
||||
];
|
||||
}
|
||||
|
||||
fn delta_char(&self, text: String, modifier: &str) -> isize {
|
||||
(modifier.len() * text.match_indices(&"\n".to_string()).collect::<Vec<_>>().len()) as isize
|
||||
(modifier.len()
|
||||
* text
|
||||
.match_indices(&"\n".to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.len()) as isize
|
||||
}
|
||||
|
||||
fn new_line(&self, cursor_range: CCursorRange, text: String) -> (String, isize) {
|
||||
|
@ -427,7 +486,11 @@ impl CodeEditor {
|
|||
while last_line_break > 0 && text.char_at(last_line_break) != '\n' {
|
||||
last_line_break -= 1;
|
||||
}
|
||||
let indent_depth = text.slice(last_line_break..cursor).match_indices(&"\t".to_string()).collect::<Vec<_>>().len();
|
||||
let indent_depth = text
|
||||
.slice(last_line_break..cursor)
|
||||
.match_indices(&"\t".to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.len();
|
||||
|
||||
let new_indent_depth = indent_depth.clone();
|
||||
|
||||
|
@ -435,6 +498,9 @@ impl CodeEditor {
|
|||
new_text.push_str(&"\t".repeat(new_indent_depth.clone()));
|
||||
new_text.push_str(text.clone().slice((cursor + 1)..));
|
||||
|
||||
return (new_text.clone().to_string(), (new_indent_depth + 1) as isize);
|
||||
return (
|
||||
new_text.clone().to_string(),
|
||||
(new_indent_depth + 1) as isize,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#![allow(dead_code)]
|
||||
pub mod ayu;
|
||||
pub mod fantasy;
|
||||
pub mod github;
|
||||
pub mod gruvbox;
|
||||
pub mod sonokai;
|
||||
pub mod fantasy;
|
||||
|
||||
use super::syntax::TokenType;
|
||||
|
||||
|
|
39
src/main.rs
39
src/main.rs
|
@ -1,12 +1,12 @@
|
|||
mod tools;
|
||||
mod calcifer;
|
||||
mod tools;
|
||||
|
||||
use eframe::egui;
|
||||
use calcifer::code_editor::ColorTheme;
|
||||
use std::{path::Path, sync::Arc, time, thread, ops::Range};
|
||||
use eframe::egui;
|
||||
use egui::FontFamily::Proportional;
|
||||
use egui::FontId;
|
||||
use egui::TextStyle::{Small, Button, Body, Heading, Monospace};
|
||||
use egui::TextStyle::{Body, Button, Heading, Monospace, Small};
|
||||
use std::{ops::Range, path::Path, sync::Arc, thread, time};
|
||||
|
||||
use calcifer::code_editor::themes::DEFAULT_THEMES;
|
||||
|
||||
|
@ -28,13 +28,14 @@ use build::TITLE;
|
|||
const TERMINAL_HEIGHT: f32 = 200.0;
|
||||
const TERMINAL_RANGE: Range<f32> = 100.0..500.0;
|
||||
const RED: egui::Color32 = egui::Color32::from_rgb(235, 108, 99);
|
||||
const TIME_LABELS : [&str; 7] = ["input", "settings", "tree", "terminal", "tabs", "content", "windows"];
|
||||
const TIME_LABELS: [&str; 7] = [
|
||||
"input", "settings", "tree", "terminal", "tabs", "content", "windows",
|
||||
];
|
||||
const MAX_FPS: f32 = 30.0;
|
||||
const PATH_ROOT: &str = "/home/penwing/Documents/";
|
||||
const DISPLAY_PATH_DEPTH: usize = 3;
|
||||
const MAX_TABS: usize = 20;
|
||||
|
||||
|
||||
fn main() -> Result<(), eframe::Error> {
|
||||
let icon_data = tools::load_icon();
|
||||
let options = eframe::NativeOptions {
|
||||
|
@ -62,7 +63,6 @@ fn main() -> Result<(), eframe::Error> {
|
|||
)
|
||||
}
|
||||
|
||||
|
||||
struct Calcifer {
|
||||
selected_tab: tools::TabNumber,
|
||||
tabs: Vec<tools::Tab>,
|
||||
|
@ -90,7 +90,6 @@ struct Calcifer {
|
|||
next_frame: time::Instant,
|
||||
}
|
||||
|
||||
|
||||
impl Default for Calcifer {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
@ -107,9 +106,15 @@ impl Default for Calcifer {
|
|||
profiler_visible: false,
|
||||
terminal_visible: false,
|
||||
|
||||
close_tab_confirm: tools::confirm::ConfirmWindow::new("You have some unsaved changes, Do you still want to close this document ?", "Confirm Close"),
|
||||
close_tab_confirm: tools::confirm::ConfirmWindow::new(
|
||||
"You have some unsaved changes, Do you still want to close this document ?",
|
||||
"Confirm Close",
|
||||
),
|
||||
tab_to_close: 0,
|
||||
refresh_confirm: tools::confirm::ConfirmWindow::new("You have some unsaved changes, Do you still want to refresh this document ?", "Confirm Refresh"),
|
||||
refresh_confirm: tools::confirm::ConfirmWindow::new(
|
||||
"You have some unsaved changes, Do you still want to refresh this document ?",
|
||||
"Confirm Refresh",
|
||||
),
|
||||
exit_confirm: tools::confirm::ConfirmWindow::new("", "Confirm Exit"),
|
||||
|
||||
search_menu: tools::search::SearchWindow::default(),
|
||||
|
@ -122,10 +127,11 @@ impl Default for Calcifer {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl eframe::App for Calcifer {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
thread::sleep(time::Duration::from_secs_f32(((1.0/MAX_FPS) - self.next_frame.elapsed().as_secs_f32()).max(0.0)));
|
||||
thread::sleep(time::Duration::from_secs_f32(
|
||||
((1.0 / MAX_FPS) - self.next_frame.elapsed().as_secs_f32()).max(0.0),
|
||||
));
|
||||
self.next_frame = time::Instant::now();
|
||||
|
||||
let mut watch = time::Instant::now();
|
||||
|
@ -141,7 +147,9 @@ impl eframe::App for Calcifer {
|
|||
.into();
|
||||
ctx.set_style(style);
|
||||
|
||||
if ctx.input( |i| i.key_pressed(egui::Key::R) && i.modifiers.ctrl) && !self.refresh_confirm.visible {
|
||||
if ctx.input(|i| i.key_pressed(egui::Key::R) && i.modifiers.ctrl)
|
||||
&& !self.refresh_confirm.visible
|
||||
{
|
||||
if self.tabs[self.selected_tab.to_index()].saved {
|
||||
self.tabs[self.selected_tab.to_index()].refresh();
|
||||
} else {
|
||||
|
@ -191,7 +199,10 @@ impl eframe::App for Calcifer {
|
|||
unsaved_tabs_names.push_str(&self.tabs[*index].get_name());
|
||||
}
|
||||
egui::Context::send_viewport_cmd(ctx, egui::ViewportCommand::CancelClose);
|
||||
self.exit_confirm.prompt = format!("You have some unsaved changes :\n{}\nDo you still want to exit ?", unsaved_tabs_names);
|
||||
self.exit_confirm.prompt = format!(
|
||||
"You have some unsaved changes :\n{}\nDo you still want to exit ?",
|
||||
unsaved_tabs_names
|
||||
);
|
||||
self.exit_confirm.ask();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use eframe::egui;
|
||||
|
||||
|
||||
pub struct ConfirmWindow {
|
||||
pub visible: bool,
|
||||
pub proceed: bool,
|
||||
|
@ -8,7 +7,6 @@ pub struct ConfirmWindow {
|
|||
id: String,
|
||||
}
|
||||
|
||||
|
||||
impl ConfirmWindow {
|
||||
pub fn new(prompt: &str, id: &str) -> Self {
|
||||
Self {
|
||||
|
@ -19,7 +17,6 @@ impl ConfirmWindow {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn show(&mut self, ctx: &egui::Context) {
|
||||
let mut visible = self.visible.clone();
|
||||
egui::Window::new(self.id.clone())
|
||||
|
@ -30,7 +27,6 @@ impl ConfirmWindow {
|
|||
self.visible = self.visible.clone() && visible;
|
||||
}
|
||||
|
||||
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
ui.set_min_width(250.0);
|
||||
ui.label(self.prompt.clone());
|
||||
|
@ -50,7 +46,6 @@ impl ConfirmWindow {
|
|||
self.proceed = false;
|
||||
}
|
||||
|
||||
|
||||
pub fn close(&mut self) {
|
||||
self.visible = false;
|
||||
self.proceed = false;
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
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};
|
||||
use crate::DISPLAY_PATH_DEPTH;
|
||||
use eframe::egui;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
cmp::Ordering, ffi::OsStr, fs, fs::read_to_string, fs::OpenOptions, io::Write, path::Component,
|
||||
path::Path, path::PathBuf,
|
||||
};
|
||||
use toml::Value;
|
||||
|
||||
//my tools;
|
||||
pub mod search;
|
||||
pub mod confirm;
|
||||
pub mod search;
|
||||
pub mod settings;
|
||||
pub mod shortcuts;
|
||||
|
||||
|
@ -17,14 +20,12 @@ pub use terminal::*;
|
|||
pub mod tabs;
|
||||
pub use tabs::*;
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
pub struct AppState {
|
||||
pub tabs: Vec<PathBuf>,
|
||||
pub theme: usize,
|
||||
}
|
||||
|
||||
|
||||
pub fn save_state(state: &AppState, file_path: &str) -> Result<(), std::io::Error> {
|
||||
let serialized_state = serde_json::to_string(state)?;
|
||||
|
||||
|
@ -45,14 +46,12 @@ pub fn save_state(state: &AppState, file_path: &str) -> Result<(), std::io::Erro
|
|||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
pub fn load_state(file_path: &str) -> Result<AppState, std::io::Error> {
|
||||
let serialized_state = read_to_string(file_path)?;
|
||||
|
||||
Ok(serde_json::from_str(&serialized_state)?)
|
||||
}
|
||||
|
||||
|
||||
pub fn load_icon() -> egui::IconData {
|
||||
let (icon_rgba, icon_width, icon_height) = {
|
||||
let icon = include_bytes!("../../assets/icon.png");
|
||||
|
@ -71,7 +70,6 @@ pub fn load_icon() -> egui::IconData {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn to_syntax(language: &str) -> Syntax {
|
||||
match language {
|
||||
"py" => Syntax::python(),
|
||||
|
@ -80,7 +78,6 @@ pub fn to_syntax(language : &str) -> Syntax {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn sort_directories_first(a: &std::fs::DirEntry, b: &std::fs::DirEntry) -> Ordering {
|
||||
let a_is_dir = a.path().is_dir();
|
||||
let b_is_dir = b.path().is_dir();
|
||||
|
@ -96,7 +93,6 @@ pub fn sort_directories_first(a: &std::fs::DirEntry, b: &std::fs::DirEntry) -> O
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn format_path(path: &Path) -> String {
|
||||
let components: Vec<&OsStr> = path
|
||||
.components()
|
||||
|
@ -108,13 +104,21 @@ pub fn format_path(path: &Path) -> String {
|
|||
})
|
||||
.collect();
|
||||
|
||||
format!("{}>", components.iter().rev().map(|&c| c.to_string_lossy()).collect::<Vec<_>>().join("/"))
|
||||
format!(
|
||||
"{}>",
|
||||
components
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|&c| c.to_string_lossy())
|
||||
.collect::<Vec<_>>()
|
||||
.join("/")
|
||||
)
|
||||
}
|
||||
|
||||
pub fn version() -> String {
|
||||
// Read the contents of the Cargo.toml file
|
||||
if !Path::new("Cargo.toml").exists() {
|
||||
return "".to_string()
|
||||
return "".to_string();
|
||||
}
|
||||
let toml_content = fs::read_to_string("Cargo.toml").expect("Failed to read Cargo.toml");
|
||||
|
||||
|
@ -126,13 +130,12 @@ pub fn version() -> String {
|
|||
if let Some(version) = package.get("version") {
|
||||
if let Some(version_string) = version.as_str() {
|
||||
println!("Version: {}", version_string);
|
||||
return format!(" v{}", version_string)
|
||||
return format!(" v{}", version_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
return "".to_string()
|
||||
return "".to_string();
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use std::{cmp::min};
|
||||
use eframe::egui;
|
||||
use std::cmp::min;
|
||||
|
||||
use crate::RED;
|
||||
use crate::tools::{tabs::Tab, tabs::TabNumber};
|
||||
|
||||
use crate::RED;
|
||||
|
||||
enum Action {
|
||||
Next,
|
||||
|
@ -13,7 +12,6 @@ enum Action {
|
|||
None,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Selection {
|
||||
pub tab: TabNumber,
|
||||
|
@ -21,7 +19,6 @@ pub struct Selection {
|
|||
pub end: usize,
|
||||
}
|
||||
|
||||
|
||||
impl Default for Selection {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
@ -32,7 +29,6 @@ impl Default for Selection {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct SearchWindow {
|
||||
pub visible: bool,
|
||||
|
||||
|
@ -52,7 +48,6 @@ pub struct SearchWindow {
|
|||
row_height: f32,
|
||||
}
|
||||
|
||||
|
||||
impl Default for SearchWindow {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
@ -76,7 +71,6 @@ impl Default for SearchWindow {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl SearchWindow {
|
||||
pub fn show(&mut self, ctx: &egui::Context, tabs: &mut Vec<Tab>, selected_tab: &mut TabNumber) {
|
||||
let mut visible = self.visible.clone();
|
||||
|
@ -88,7 +82,6 @@ impl SearchWindow {
|
|||
self.visible = self.visible.clone() && visible;
|
||||
}
|
||||
|
||||
|
||||
fn ui(&mut self, ui: &mut egui::Ui, tabs: &mut Vec<Tab>, selected_tab: &mut TabNumber) {
|
||||
ui.set_min_width(250.0);
|
||||
|
||||
|
@ -100,7 +93,11 @@ impl SearchWindow {
|
|||
ui.horizontal(|ui| {
|
||||
let Self { search_text, .. } = self;
|
||||
|
||||
let response = ui.add(egui::TextEdit::singleline(search_text).desired_width(120.0).lock_focus(true));
|
||||
let response = ui.add(
|
||||
egui::TextEdit::singleline(search_text)
|
||||
.desired_width(120.0)
|
||||
.lock_focus(true),
|
||||
);
|
||||
if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
|
||||
action = Action::Update;
|
||||
}
|
||||
|
@ -117,10 +114,17 @@ impl SearchWindow {
|
|||
action = Action::Previous;
|
||||
}
|
||||
|
||||
if self.search_text == self.searched_text && self.search_text.len() > 0 && self.results.len() == 0 {
|
||||
if self.search_text == self.searched_text
|
||||
&& self.search_text.len() > 0
|
||||
&& self.results.len() == 0
|
||||
{
|
||||
ui.colored_label(RED, " 0/0 ");
|
||||
} else {
|
||||
ui.label(format!(" {}/{} ", min(self.current_result + 1, self.results.len()), self.results.len()));
|
||||
ui.label(format!(
|
||||
" {}/{} ",
|
||||
min(self.current_result + 1, self.results.len()),
|
||||
self.results.len()
|
||||
));
|
||||
}
|
||||
|
||||
if ui.add(egui::Button::new(">")).clicked() {
|
||||
|
@ -139,7 +143,11 @@ impl SearchWindow {
|
|||
.show(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
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),
|
||||
);
|
||||
if ui.add(egui::Button::new("Replace")).clicked() {
|
||||
action = Action::Replace;
|
||||
}
|
||||
|
@ -155,30 +163,31 @@ impl SearchWindow {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn get_cursor_start(&self) -> usize {
|
||||
self.results[self.current_result].start.clone()
|
||||
}
|
||||
|
||||
|
||||
pub fn get_cursor_end(&self) -> usize {
|
||||
self.results[self.current_result].end.clone()
|
||||
}
|
||||
|
||||
|
||||
fn search(&mut self, tabs: &mut Vec<Tab>, selected_tab: &mut TabNumber) {
|
||||
if self.search_text.len() == 0 {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
let mut search_results: Vec<Selection> = vec![];
|
||||
|
||||
if self.across_documents {
|
||||
for (index, tab) in tabs.iter().enumerate() {
|
||||
search_results.extend(self.match_text(tab.code.clone(), TabNumber::from_index(index)));
|
||||
search_results
|
||||
.extend(self.match_text(tab.code.clone(), TabNumber::from_index(index)));
|
||||
}
|
||||
} else {
|
||||
search_results.extend(self.match_text(tabs[selected_tab.to_index()].code.clone(), selected_tab.clone()));
|
||||
search_results.extend(self.match_text(
|
||||
tabs[selected_tab.to_index()].code.clone(),
|
||||
selected_tab.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
self.searched_text = self.search_text.clone();
|
||||
|
@ -190,35 +199,41 @@ impl SearchWindow {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
fn match_text(&self, tab_text: String, tab_number: TabNumber) -> Vec<Selection> {
|
||||
let matches = tab_text.match_indices(&self.search_text.clone()).map(|(i, _)| Selection {
|
||||
let matches = tab_text
|
||||
.match_indices(&self.search_text.clone())
|
||||
.map(|(i, _)| Selection {
|
||||
tab: tab_number.clone(),
|
||||
start: i,
|
||||
end: i + self.search_text.len(),
|
||||
}).collect();
|
||||
})
|
||||
.collect();
|
||||
|
||||
matches
|
||||
}
|
||||
|
||||
|
||||
fn find_result(&mut self, tabs: &mut Vec<Tab>, selected_tab: &mut TabNumber, direction: i32) {
|
||||
if self.searched_text != self.search_text {
|
||||
self.search(tabs, &mut *selected_tab);
|
||||
} else if self.results.len() > 0 {
|
||||
self.current_result = (self.current_result as i32 + direction + self.results.len() as i32) as usize % self.results.len();
|
||||
self.current_result =
|
||||
(self.current_result as i32 + direction + self.results.len() as i32) as usize
|
||||
% self.results.len();
|
||||
self.result_selected = false;
|
||||
*selected_tab = self.results[self.current_result].tab.clone();
|
||||
|
||||
let target = self.results[self.current_result].start;
|
||||
let code = tabs[selected_tab.to_index()].code.clone();
|
||||
let (upstream, _downstream) = code.split_at(target);
|
||||
let row = upstream.match_indices(&"\n".to_string()).collect::<Vec<_>>().len();
|
||||
tabs[selected_tab.to_index()].scroll_offset = self.row_height * row.saturating_sub(5) as f32;
|
||||
let row = upstream
|
||||
.match_indices(&"\n".to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.len();
|
||||
tabs[selected_tab.to_index()].scroll_offset =
|
||||
self.row_height * row.saturating_sub(5) as f32;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn replace(&mut self, tabs: &mut Vec<Tab>, selected_tab: &mut TabNumber) {
|
||||
if self.searched_text != self.search_text {
|
||||
self.search(tabs, &mut *selected_tab);
|
||||
|
@ -229,7 +244,9 @@ impl SearchWindow {
|
|||
if done.contains(&element.tab) {
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
done.push(element.tab.clone())
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use eframe::egui;
|
||||
use crate::ColorTheme;
|
||||
use crate::DEFAULT_THEMES;
|
||||
|
||||
use eframe::egui;
|
||||
|
||||
pub struct SettingsWindow {
|
||||
pub visible: bool,
|
||||
|
@ -9,7 +8,6 @@ pub struct SettingsWindow {
|
|||
pub theme: ColorTheme,
|
||||
}
|
||||
|
||||
|
||||
impl SettingsWindow {
|
||||
pub fn new(theme: ColorTheme) -> Self {
|
||||
Self {
|
||||
|
@ -19,7 +17,6 @@ impl SettingsWindow {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn show(&mut self, ctx: &egui::Context) {
|
||||
let mut visible = self.visible.clone();
|
||||
egui::Window::new("Settings")
|
||||
|
@ -30,7 +27,6 @@ impl SettingsWindow {
|
|||
self.visible = self.visible.clone() && visible;
|
||||
}
|
||||
|
||||
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
ui.set_min_width(250.0);
|
||||
ui.horizontal(|ui| {
|
||||
|
|
|
@ -1,18 +1,13 @@
|
|||
use eframe::egui;
|
||||
|
||||
|
||||
pub struct ShortcutsWindow {
|
||||
pub visible: bool,
|
||||
}
|
||||
|
||||
|
||||
impl ShortcutsWindow {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
visible: false,
|
||||
Self { visible: false }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn show(&mut self, ctx: &egui::Context) {
|
||||
let mut visible = self.visible.clone();
|
||||
|
@ -20,11 +15,10 @@ impl ShortcutsWindow {
|
|||
.open(&mut visible)
|
||||
.vscroll(true)
|
||||
.hscroll(true)
|
||||
.show(ctx, |ui| self.ui(ui,));
|
||||
.show(ctx, |ui| self.ui(ui));
|
||||
self.visible = self.visible.clone() && visible;
|
||||
}
|
||||
|
||||
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
ui.set_min_width(250.0);
|
||||
ui.label("Ctrl+S : save file");
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use std::{fs::read_to_string, path::PathBuf};
|
||||
use eframe::egui::text_edit::CCursorRange;
|
||||
use std::{fs::read_to_string, path::PathBuf};
|
||||
|
||||
use crate::MAX_TABS;
|
||||
|
||||
|
@ -25,7 +25,6 @@ impl TabNumber {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Tab {
|
||||
pub path: PathBuf,
|
||||
|
@ -49,25 +48,31 @@ impl Default for Tab {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl Tab {
|
||||
pub fn new(path: PathBuf) -> Self {
|
||||
Self {
|
||||
path: path.clone().into(),
|
||||
code: read_to_string(path.clone()).expect("Not able to read the file").replace(&" ".repeat(4), "\t"),
|
||||
code: read_to_string(path.clone())
|
||||
.expect("Not able to read the file")
|
||||
.replace(&" ".repeat(4), "\t"),
|
||||
language: path.to_str().unwrap().split('.').last().unwrap().into(),
|
||||
saved: true,
|
||||
scroll_offset: 0.0,
|
||||
last_cursor: None,
|
||||
}
|
||||
|
||||
}
|
||||
pub fn get_name(&self) -> String {
|
||||
self.path.file_name().expect("Could not get Tab Name").to_string_lossy().to_string()
|
||||
self.path
|
||||
.file_name()
|
||||
.expect("Could not get Tab Name")
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
pub fn refresh(&mut self) {
|
||||
self.code = read_to_string(self.path.clone()).expect("Not able to read the file").replace(&" ".repeat(4), "\t");
|
||||
self.code = read_to_string(self.path.clone())
|
||||
.expect("Not able to read the file")
|
||||
.replace(&" ".repeat(4), "\t");
|
||||
self.saved = true;
|
||||
println!("refreshed {}", self.path.display());
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use crate::tools::format_path;
|
||||
use nix::fcntl::fcntl;
|
||||
use nix::fcntl::FcntlArg;
|
||||
use nix::fcntl::OFlag;
|
||||
use std::io::BufRead;
|
||||
use std::io::BufReader;
|
||||
use std::io::Read;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::process::Stdio;
|
||||
use std::{env, path::Path, process::Command};
|
||||
use std::os::fd::AsRawFd;
|
||||
use nix::fcntl::OFlag;
|
||||
use nix::fcntl::FcntlArg;
|
||||
use nix::fcntl::fcntl;
|
||||
|
||||
pub struct CommandEntry {
|
||||
pub env: String,
|
||||
|
@ -105,8 +105,10 @@ pub fn execute(
|
|||
let stdout_fd = stdout.as_raw_fd();
|
||||
let stderr_fd = stderr.as_raw_fd();
|
||||
|
||||
fcntl(stdout_fd, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)).expect("Failed to set non-blocking mode");
|
||||
fcntl(stderr_fd, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)).expect("Failed to set non-blocking mode");
|
||||
fcntl(stdout_fd, FcntlArg::F_SETFL(OFlag::O_NONBLOCK))
|
||||
.expect("Failed to set non-blocking mode");
|
||||
fcntl(stderr_fd, FcntlArg::F_SETFL(OFlag::O_NONBLOCK))
|
||||
.expect("Failed to set non-blocking mode");
|
||||
|
||||
return (BufReader::new(stdout), BufReader::new(stderr));
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#[cfg(test)]
|
||||
|
||||
|
||||
mod tests {
|
||||
|
||||
use crate::tools::*;
|
||||
|
|
Loading…
Reference in a new issue