From 704bb7401a97591bce350362b9b0825c481add71 Mon Sep 17 00:00:00 2001 From: filipriec Date: Fri, 4 Apr 2025 23:37:19 +0200 Subject: [PATCH] copied form_e to auth_e --- client/src/functions/modes/edit/auth_e.rs | 431 ++++++++++++++++++++++ 1 file changed, 431 insertions(+) diff --git a/client/src/functions/modes/edit/auth_e.rs b/client/src/functions/modes/edit/auth_e.rs index e69de29..18f12cd 100644 --- a/client/src/functions/modes/edit/auth_e.rs +++ b/client/src/functions/modes/edit/auth_e.rs @@ -0,0 +1,431 @@ +// src/functions/modes/edit/auth_e.rs + +use crate::config::binds::config::Config; +use crate::services::grpc_client::GrpcClient; +use crate::state::canvas_state::CanvasState; +use crate::state::pages::form::FormState; +use crate::tui::functions::common::form::{revert, save}; +use crossterm::event::{KeyCode, KeyEvent}; +use std::any::Any; + +pub async fn execute_common_action( + action: &str, + state: &mut S, + grpc_client: &mut GrpcClient, + is_saved: &mut bool, + current_position: &mut u64, + total_count: u64, +) -> Result> { + match action { + "save" | "revert" => { + if !state.has_unsaved_changes() { + return Ok("No changes to save or revert.".to_string()); + } + if let Some(form_state) = + (state as &mut dyn Any).downcast_mut::() + { + match action { + "save" => { + save( + form_state, + grpc_client, + is_saved, + current_position, + total_count, + ) + .await + } + "revert" => { + revert( + form_state, + grpc_client, + current_position, + total_count, + ) + .await + } + _ => unreachable!(), + } + } else { + Ok(format!( + "Action '{}' not implemented for this state type.", + action + )) + } + } + _ => Ok(format!("Common action '{}' not handled here.", action)), + } +} + +pub async fn execute_edit_action( + action: &str, + key: KeyEvent, + state: &mut S, + ideal_cursor_column: &mut usize, + _grpc_client: &mut GrpcClient, + _is_saved: &mut bool, + _current_position: &mut u64, + _total_count: u64, +) -> Result> { + match action { + "insert_char" => { + if let KeyCode::Char(c) = key.code { + let cursor_pos = state.current_cursor_pos(); + let field_value = state.get_current_input_mut(); + let mut chars: Vec = field_value.chars().collect(); + if cursor_pos <= chars.len() { + chars.insert(cursor_pos, c); + *field_value = chars.into_iter().collect(); + state.set_current_cursor_pos(cursor_pos + 1); + state.set_has_unsaved_changes(true); + *ideal_cursor_column = state.current_cursor_pos(); + } + } else { + return Ok("Error: insert_char called without a char key." + .to_string()); + } + Ok("".to_string()) + } + + "delete_char_backward" => { + if state.current_cursor_pos() > 0 { + let cursor_pos = state.current_cursor_pos(); + let field_value = state.get_current_input_mut(); + let mut chars: Vec = field_value.chars().collect(); + if cursor_pos <= chars.len() { + chars.remove(cursor_pos - 1); + *field_value = chars.into_iter().collect(); + let new_pos = cursor_pos - 1; + state.set_current_cursor_pos(new_pos); + state.set_has_unsaved_changes(true); + *ideal_cursor_column = new_pos; + } + } + Ok("".to_string()) + } + + "delete_char_forward" => { + let cursor_pos = state.current_cursor_pos(); + let field_value = state.get_current_input_mut(); + let mut chars: Vec = field_value.chars().collect(); + if cursor_pos < chars.len() { + chars.remove(cursor_pos); + *field_value = chars.into_iter().collect(); + state.set_has_unsaved_changes(true); + *ideal_cursor_column = cursor_pos; + } + Ok("".to_string()) + } + + "next_field" => { + let num_fields = state.fields().len(); + if num_fields > 0 { + let current_field = state.current_field(); + let new_field = (current_field + 1) % num_fields; + state.set_current_field(new_field); + let current_input = state.get_current_input(); + let max_pos = current_input.len(); + state.set_current_cursor_pos( + (*ideal_cursor_column).min(max_pos), + ); + } + Ok("".to_string()) + } + + "prev_field" => { + let num_fields = state.fields().len(); + if num_fields > 0 { + let current_field = state.current_field(); + let new_field = if current_field == 0 { + num_fields - 1 + } else { + current_field - 1 + }; + state.set_current_field(new_field); + let current_input = state.get_current_input(); + let max_pos = current_input.len(); + state.set_current_cursor_pos( + (*ideal_cursor_column).min(max_pos), + ); + } + Ok("".to_string()) + } + + "move_left" => { + let new_pos = state.current_cursor_pos().saturating_sub(1); + state.set_current_cursor_pos(new_pos); + *ideal_cursor_column = new_pos; + Ok("".to_string()) + } + + "move_right" => { + let current_input = state.get_current_input(); + let current_pos = state.current_cursor_pos(); + if current_pos < current_input.len() { + let new_pos = current_pos + 1; + state.set_current_cursor_pos(new_pos); + *ideal_cursor_column = new_pos; + } + Ok("".to_string()) + } + + "move_up" => { + let num_fields = state.fields().len(); + if num_fields > 0 { + let current_field = state.current_field(); + let new_field = if current_field == 0 { + num_fields - 1 + } else { + current_field - 1 + }; + state.set_current_field(new_field); + let current_input = state.get_current_input(); + let max_pos = current_input.len(); + state.set_current_cursor_pos( + (*ideal_cursor_column).min(max_pos), + ); + } + Ok("".to_string()) + } + + "move_down" => { + let num_fields = state.fields().len(); + if num_fields > 0 { + let new_field = (state.current_field() + 1) % num_fields; + state.set_current_field(new_field); + let current_input = state.get_current_input(); + let max_pos = current_input.len(); + state.set_current_cursor_pos( + (*ideal_cursor_column).min(max_pos), + ); + } + Ok("".to_string()) + } + + "move_line_start" => { + state.set_current_cursor_pos(0); + *ideal_cursor_column = 0; + Ok("".to_string()) + } + + "move_line_end" => { + let current_input = state.get_current_input(); + let new_pos = current_input.len(); + state.set_current_cursor_pos(new_pos); + *ideal_cursor_column = new_pos; + Ok("".to_string()) + } + + "move_first_line" => { + let num_fields = state.fields().len(); + if num_fields > 0 { + state.set_current_field(0); + let current_input = state.get_current_input(); + let max_pos = current_input.len(); + state.set_current_cursor_pos( + (*ideal_cursor_column).min(max_pos), + ); + } + Ok("Moved to first field".to_string()) + } + + "move_last_line" => { + let num_fields = state.fields().len(); + if num_fields > 0 { + let new_field = num_fields - 1; + state.set_current_field(new_field); + let current_input = state.get_current_input(); + let max_pos = current_input.len(); + state.set_current_cursor_pos( + (*ideal_cursor_column).min(max_pos), + ); + } + Ok("Moved to last field".to_string()) + } + + "move_word_next" => { + let current_input = state.get_current_input(); + if !current_input.is_empty() { + let new_pos = find_next_word_start( + current_input, + state.current_cursor_pos(), + ); + let final_pos = new_pos.min(current_input.len()); + state.set_current_cursor_pos(final_pos); + *ideal_cursor_column = final_pos; + } + Ok("".to_string()) + } + + "move_word_end" => { + let current_input = state.get_current_input(); + if !current_input.is_empty() { + let current_pos = state.current_cursor_pos(); + let new_pos = find_word_end(current_input, current_pos); + + let final_pos = if new_pos == current_pos { + find_word_end(current_input, new_pos + 1) + } else { + new_pos + }; + + let max_valid_index = current_input.len().saturating_sub(1); + let clamped_pos = final_pos.min(max_valid_index); + state.set_current_cursor_pos(clamped_pos); + *ideal_cursor_column = clamped_pos; + } + Ok("".to_string()) + } + + "move_word_prev" => { + let current_input = state.get_current_input(); + if !current_input.is_empty() { + let new_pos = find_prev_word_start( + current_input, + state.current_cursor_pos(), + ); + state.set_current_cursor_pos(new_pos); + *ideal_cursor_column = new_pos; + } + Ok("".to_string()) + } + + "move_word_end_prev" => { + let current_input = state.get_current_input(); + if !current_input.is_empty() { + let new_pos = find_prev_word_end( + current_input, + state.current_cursor_pos(), + ); + state.set_current_cursor_pos(new_pos); + *ideal_cursor_column = new_pos; + } + Ok("Moved to previous word end".to_string()) + } + + _ => Ok(format!("Unknown or unhandled edit action: {}", action)), + } +} + +#[derive(PartialEq)] +enum CharType { + Whitespace, + Alphanumeric, + Punctuation, +} + +fn get_char_type(c: char) -> CharType { + if c.is_whitespace() { + CharType::Whitespace + } else if c.is_alphanumeric() { + CharType::Alphanumeric + } else { + CharType::Punctuation + } +} + +fn find_next_word_start(text: &str, current_pos: usize) -> usize { + let chars: Vec = text.chars().collect(); + let len = chars.len(); + if len == 0 || current_pos >= len { + return len; + } + + let mut pos = current_pos; + let initial_type = get_char_type(chars[pos]); + + while pos < len && get_char_type(chars[pos]) == initial_type { + pos += 1; + } + + while pos < len && get_char_type(chars[pos]) == CharType::Whitespace { + pos += 1; + } + + pos +} + +fn find_word_end(text: &str, current_pos: usize) -> usize { + let chars: Vec = text.chars().collect(); + let len = chars.len(); + if len == 0 { + return 0; + } + + let mut pos = current_pos.min(len - 1); + + if get_char_type(chars[pos]) == CharType::Whitespace { + pos = find_next_word_start(text, pos); + } + + if pos >= len { + return len.saturating_sub(1); + } + + let word_type = get_char_type(chars[pos]); + while pos < len && get_char_type(chars[pos]) == word_type { + pos += 1; + } + + pos.saturating_sub(1).min(len.saturating_sub(1)) +} + +fn find_prev_word_start(text: &str, current_pos: usize) -> usize { + let chars: Vec = text.chars().collect(); + if chars.is_empty() || current_pos == 0 { + return 0; + } + + let mut pos = current_pos.saturating_sub(1); + + while pos > 0 && get_char_type(chars[pos]) == CharType::Whitespace { + pos -= 1; + } + + if pos == 0 && get_char_type(chars[pos]) == CharType::Whitespace { + return 0; + } + + let word_type = get_char_type(chars[pos]); + while pos > 0 && get_char_type(chars[pos - 1]) == word_type { + pos -= 1; + } + + pos +} + +fn find_prev_word_end(text: &str, current_pos: usize) -> usize { + let chars: Vec = text.chars().collect(); + let len = chars.len(); + if len == 0 || current_pos == 0 { + return 0; + } + + let mut pos = current_pos.saturating_sub(1); + + while pos > 0 && get_char_type(chars[pos]) == CharType::Whitespace { + pos -= 1; + } + + if pos == 0 && get_char_type(chars[pos]) == CharType::Whitespace { + return 0; + } + if pos == 0 && get_char_type(chars[pos]) != CharType::Whitespace { + return 0; + } + + let word_type = get_char_type(chars[pos]); + while pos > 0 && get_char_type(chars[pos - 1]) == word_type { + pos -= 1; + } + + while pos > 0 && get_char_type(chars[pos - 1]) == CharType::Whitespace { + pos -= 1; + } + + if pos > 0 { + pos - 1 + } else { + 0 + } +}