2026-07-11 18:23:01 +07:00
|
|
|
use crate::app::state::rest::AppStateRest;
|
|
|
|
|
use crate::app::state::types::Overlay;
|
|
|
|
|
|
2026-07-11 21:25:30 +07:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct EditorState {
|
|
|
|
|
pub path: String,
|
|
|
|
|
pub content: Vec<String>,
|
|
|
|
|
pub undo_stack: Vec<Vec<String>>,
|
|
|
|
|
pub cursor_line: usize,
|
|
|
|
|
pub cursor_col: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for EditorState {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
EditorState {
|
|
|
|
|
path: String::new(),
|
|
|
|
|
content: vec![String::new()],
|
|
|
|
|
undo_stack: Vec::new(),
|
|
|
|
|
cursor_line: 0,
|
|
|
|
|
cursor_col: 0,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl EditorState {
|
|
|
|
|
pub fn open(path: String, existing_content: Option<Vec<String>>) -> Self {
|
|
|
|
|
let content = existing_content.unwrap_or_else(|| vec![String::new()]);
|
|
|
|
|
EditorState {
|
|
|
|
|
path,
|
|
|
|
|
content,
|
|
|
|
|
..Default::default()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn change_line(&mut self, text: String) {
|
|
|
|
|
self.save_undo();
|
|
|
|
|
if self.cursor_line < self.content.len() {
|
|
|
|
|
self.content[self.cursor_line] = text;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn insert_line_after(&mut self) {
|
|
|
|
|
self.save_undo();
|
|
|
|
|
let pos = (self.cursor_line + 1).min(self.content.len());
|
|
|
|
|
self.content.insert(pos, String::new());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn delete_current_line(&mut self) {
|
|
|
|
|
if self.content.len() <= 1 {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
self.save_undo();
|
|
|
|
|
self.content.remove(self.cursor_line);
|
|
|
|
|
if self.cursor_line >= self.content.len() {
|
|
|
|
|
self.cursor_line = self.content.len() - 1;
|
|
|
|
|
}
|
|
|
|
|
self.cursor_col = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn save_undo(&mut self) {
|
|
|
|
|
self.undo_stack.push(self.content.clone());
|
|
|
|
|
if self.undo_stack.len() > 50 {
|
|
|
|
|
self.undo_stack.remove(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn undo(&mut self) {
|
|
|
|
|
if let Some(prev) = self.undo_stack.pop() {
|
|
|
|
|
self.content = prev;
|
|
|
|
|
self.cursor_line = self.cursor_line.min(self.content.len().saturating_sub(1));
|
|
|
|
|
self.cursor_col = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cursor_up(&mut self) {
|
|
|
|
|
if self.cursor_line > 0 {
|
|
|
|
|
self.cursor_line -= 1;
|
|
|
|
|
}
|
|
|
|
|
self.cursor_col = self.cursor_col.min(
|
|
|
|
|
self.content.get(self.cursor_line).map(|l| l.len()).unwrap_or(0),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cursor_down(&mut self) {
|
|
|
|
|
if self.cursor_line + 1 < self.content.len() {
|
|
|
|
|
self.cursor_line += 1;
|
|
|
|
|
}
|
|
|
|
|
self.cursor_col = self.cursor_col.min(
|
|
|
|
|
self.content.get(self.cursor_line).map(|l| l.len()).unwrap_or(0),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cursor_left(&mut self) {
|
|
|
|
|
if self.cursor_col > 0 {
|
|
|
|
|
self.cursor_col -= 1;
|
|
|
|
|
} else if self.cursor_line > 0 {
|
|
|
|
|
self.cursor_line -= 1;
|
|
|
|
|
self.cursor_col = self.content.get(self.cursor_line).map(|l| l.len()).unwrap_or(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cursor_right(&mut self) {
|
|
|
|
|
if let Some(line) = self.content.get(self.cursor_line) {
|
|
|
|
|
if self.cursor_col < line.len() {
|
|
|
|
|
self.cursor_col += 1;
|
|
|
|
|
} else if self.cursor_line + 1 < self.content.len() {
|
|
|
|
|
self.cursor_line += 1;
|
|
|
|
|
self.cursor_col = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn insert_char(&mut self, c: char) {
|
|
|
|
|
self.save_undo();
|
|
|
|
|
if let Some(line) = self.content.get_mut(self.cursor_line) {
|
|
|
|
|
line.insert(self.cursor_col, c);
|
|
|
|
|
self.cursor_col += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn delete_left(&mut self) {
|
|
|
|
|
self.save_undo();
|
|
|
|
|
if let Some(line) = self.content.get_mut(self.cursor_line) {
|
|
|
|
|
if self.cursor_col > 0 {
|
|
|
|
|
self.cursor_col -= 1;
|
|
|
|
|
line.remove(self.cursor_col);
|
|
|
|
|
} else if self.cursor_line > 0 {
|
|
|
|
|
let prev_len = self.content[self.cursor_line - 1].len();
|
|
|
|
|
let rest = self.content.remove(self.cursor_line);
|
|
|
|
|
self.cursor_line -= 1;
|
|
|
|
|
self.cursor_col = prev_len;
|
|
|
|
|
self.content[self.cursor_line].push_str(&rest);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn join_lines(&mut self) {
|
|
|
|
|
if self.cursor_line + 1 >= self.content.len() {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
self.save_undo();
|
|
|
|
|
let next = self.content.remove(self.cursor_line + 1);
|
|
|
|
|
self.content[self.cursor_line].push_str(&next);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn as_string(&self) -> String {
|
|
|
|
|
self.content.join("\n")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-11 18:23:01 +07:00
|
|
|
pub fn handle_editor_input(state: &mut AppStateRest, text: String) {
|
2026-07-11 21:25:30 +07:00
|
|
|
let editor = &mut state.misc.editor;
|
|
|
|
|
if editor.is_none() {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
let ed = editor.as_mut().unwrap();
|
|
|
|
|
for c in text.chars() {
|
|
|
|
|
match c {
|
|
|
|
|
'\n' | '\r' => {
|
|
|
|
|
ed.insert_line_after();
|
|
|
|
|
ed.cursor_down();
|
|
|
|
|
ed.cursor_col = 0;
|
|
|
|
|
}
|
|
|
|
|
'\t' => {
|
|
|
|
|
ed.insert_char(' ');
|
|
|
|
|
ed.insert_char(' ');
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
|
ed.insert_char(c);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-07-11 18:23:01 +07:00
|
|
|
state.dirty = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn handle_editor_dismiss(state: &mut AppStateRest) {
|
2026-07-11 21:25:30 +07:00
|
|
|
state.misc.editor = None;
|
2026-07-11 18:23:01 +07:00
|
|
|
state.misc.overlay = Overlay::None;
|
|
|
|
|
state.dirty = true;
|
|
|
|
|
}
|