- Removed the Escalate variant from the Verdict enum and associated parsing logic. - Cleaned up the EditorState struct by removing unused fields and methods. - Simplified MCP connection functions by removing unnecessary disconnect and handle dismiss functions. - Added tick count to MiscState for managing UI updates during processing. - Updated chat and status views to display a spinner during AI processing using the tick count. - Created a README.md file to document the project, its features, architecture, usage, key bindings, agent modes, security measures, installation instructions, and configuration details.
181 lines
5.0 KiB
Rust
181 lines
5.0 KiB
Rust
use crate::app::state::rest::AppStateRest;
|
|
use crate::app::state::types::Overlay;
|
|
|
|
#[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")
|
|
}
|
|
}
|
|
|
|
pub fn handle_editor_input(state: &mut AppStateRest, text: String) {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
state.dirty = true;
|
|
}
|
|
|
|
pub fn handle_editor_dismiss(state: &mut AppStateRest) {
|
|
state.misc.editor = None;
|
|
state.misc.overlay = Overlay::None;
|
|
state.dirty = true;
|
|
}
|