use color_eyre::Result; use std::sync::Arc; use crate::shared::state::AppState; pub struct Editor { file_path: String, bucket: String, key: String, content: String, cursor_pos: usize, scroll_offset: usize, modified: bool, } impl Editor { pub async fn load(app_state: &Arc, bucket: &str, path: &str) -> Result { let content = if let Some(drive) = &app_state.drive { match drive.get_object().bucket(bucket).key(path).send().await { Ok(response) => { let bytes = response.body.collect().await?.into_bytes(); String::from_utf8_lossy(&bytes).to_string() } Err(_) => String::new(), } } else { String::new() }; Ok(Self { file_path: format!("{}/{}", bucket, path), bucket: bucket.to_string(), key: path.to_string(), content, cursor_pos: 0, scroll_offset: 0, modified: false, }) } pub async fn save(&mut self, app_state: &Arc) -> Result<()> { if let Some(drive) = &app_state.drive { drive.put_object() .bucket(&self.bucket) .key(&self.key) .body(self.content.as_bytes().to_vec().into()) .send() .await?; self.modified = false; } Ok(()) } pub fn file_path(&self) -> &str { &self.file_path } pub fn render(&self, cursor_blink: bool) -> String { let lines: Vec<&str> = self.content.lines().collect(); let total_lines = lines.len().max(1); let visible_lines = 25; let cursor_line = self.content[..self.cursor_pos].lines().count(); let cursor_col = self.content[..self.cursor_pos] .lines() .last() .map(|line| line.len()) .unwrap_or(0); let start = self.scroll_offset; let end = (start + visible_lines).min(total_lines); let mut display_lines = Vec::new(); for i in start..end { let line_num = i + 1; let line_content = if i < lines.len() { lines[i] } else { "" }; let is_cursor_line = i == cursor_line; let cursor_indicator = if is_cursor_line && cursor_blink { let spaces = " ".repeat(cursor_col); format!("{}█", spaces) } else { String::new() }; display_lines.push(format!(" {:4} │ {}{}", line_num, line_content, cursor_indicator)); } if display_lines.is_empty() { let cursor_indicator = if cursor_blink { "█" } else { "" }; display_lines.push(format!(" 1 │ {}", cursor_indicator)); } display_lines.push("".to_string()); display_lines.push("─────────────────────────────────────────────────────────────".to_string()); let status = if self.modified { "MODIFIED" } else { "SAVED" }; display_lines.push(format!(" {} {} │ Line: {}, Col: {}", status, self.file_path, cursor_line + 1, cursor_col + 1)); display_lines.push(" Ctrl+S: Save │ Ctrl+W: Close │ Esc: Close without saving".to_string()); display_lines.join("\n") } pub fn move_up(&mut self) { if let Some(prev_line_end) = self.content[..self.cursor_pos].rfind('\n') { if let Some(prev_prev_line_end) = self.content[..prev_line_end].rfind('\n') { let target_pos = prev_prev_line_end + 1 + (self.cursor_pos - prev_line_end - 1).min( self.content[prev_prev_line_end + 1..prev_line_end].len() ); self.cursor_pos = target_pos; } else { self.cursor_pos = (self.cursor_pos - prev_line_end - 1).min(prev_line_end); } } } pub fn move_down(&mut self) { if let Some(next_line_start) = self.content[self.cursor_pos..].find('\n') { let current_line_start = self.content[..self.cursor_pos].rfind('\n').map(|pos| pos + 1).unwrap_or(0); let next_line_absolute = self.cursor_pos + next_line_start + 1; if let Some(next_next_line_start) = self.content[next_line_absolute..].find('\n') { let target_pos = next_line_absolute + (self.cursor_pos - current_line_start).min(next_next_line_start); self.cursor_pos = target_pos; } else { let target_pos = next_line_absolute + (self.cursor_pos - current_line_start).min( self.content[next_line_absolute..].len() ); self.cursor_pos = target_pos; } } } pub fn move_left(&mut self) { if self.cursor_pos > 0 { self.cursor_pos -= 1; } } pub fn move_right(&mut self) { if self.cursor_pos < self.content.len() { self.cursor_pos += 1; } } pub fn insert_char(&mut self, c: char) { self.modified = true; self.content.insert(self.cursor_pos, c); self.cursor_pos += 1; } pub fn backspace(&mut self) { if self.cursor_pos > 0 { self.modified = true; self.content.remove(self.cursor_pos - 1); self.cursor_pos -= 1; } } pub fn insert_newline(&mut self) { self.modified = true; self.content.insert(self.cursor_pos, '\n'); self.cursor_pos += 1; } }