// src/functions/modes/read_only/auth_ro.rs use crate::config::binds::key_sequences::KeySequenceTracker; use crate::state::pages::canvas_state::CanvasState; use crate::state::app::state::AppState; use std::error::Error; #[derive(PartialEq)] enum CharType { Whitespace, Alphanumeric, Punctuation, } pub async fn execute_action( action: &str, app_state: &mut AppState, state: &mut S, ideal_cursor_column: &mut usize, key_sequence_tracker: &mut KeySequenceTracker, command_message: &mut String, ) -> Result> { match action { "previous_entry" | "next_entry" => { key_sequence_tracker.reset(); Ok(format!( "Action '{}' should be handled by context-specific logic", action )) } "move_up" => { key_sequence_tracker.reset(); let num_fields = state.fields().len(); if num_fields == 0 { return Ok("No fields to navigate.".to_string()); } let current_field = state.current_field(); let new_field = current_field.saturating_sub(1); state.set_current_field(new_field); let current_input = state.get_current_input(); let max_cursor_pos = if current_input.is_empty() { 0 } else { current_input.len().saturating_sub(1) }; let new_pos = (*ideal_cursor_column).min(max_cursor_pos); state.set_current_cursor_pos(new_pos); Ok("move up from functions/modes/read_only/auth_ro.rs".to_string()) } "move_down" => { key_sequence_tracker.reset(); let num_fields = state.fields().len(); if num_fields == 0 { return Ok("No fields to navigate.".to_string()); } let current_field = state.current_field(); let last_field_index = num_fields - 1; if current_field == last_field_index { // Already on the last field, move focus outside app_state.ui.focus_outside_canvas = true; app_state.focused_button_index= 0; key_sequence_tracker.reset(); Ok("Focus moved below canvas".to_string()) } else { // Move to the next field within the canvas let new_field = (current_field + 1).min(last_field_index); state.set_current_field(new_field); let current_input = state.get_current_input(); let max_cursor_pos = if current_input.is_empty() { 0 } else { current_input.len().saturating_sub(1) }; let new_pos = (*ideal_cursor_column).min(max_cursor_pos); state.set_current_cursor_pos(new_pos); Ok("".to_string()) // Clear previous debug message } } "move_first_line" => { key_sequence_tracker.reset(); let num_fields = state.fields().len(); if num_fields == 0 { return Ok("No fields to navigate to.".to_string()); } state.set_current_field(0); let current_input = state.get_current_input(); let max_cursor_pos = if current_input.is_empty() { 0 } else { current_input.len().saturating_sub(1) }; let new_pos = (*ideal_cursor_column).min(max_cursor_pos); state.set_current_cursor_pos(new_pos); *ideal_cursor_column = new_pos; Ok("".to_string()) } "move_last_line" => { key_sequence_tracker.reset(); let num_fields = state.fields().len(); if num_fields == 0 { return Ok("No fields to navigate to.".to_string()); } let last_field_index = num_fields - 1; state.set_current_field(last_field_index); let current_input = state.get_current_input(); let max_cursor_pos = if current_input.is_empty() { 0 } else { current_input.len().saturating_sub(1) }; let new_pos = (*ideal_cursor_column).min(max_cursor_pos); state.set_current_cursor_pos(new_pos); *ideal_cursor_column = new_pos; Ok("".to_string()) } "exit_edit_mode" => { key_sequence_tracker.reset(); command_message.clear(); Ok("".to_string()) } "move_left" => { let current_pos = state.current_cursor_pos(); let new_pos = current_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_input.is_empty() && current_pos < current_input.len().saturating_sub(1) { let new_pos = current_pos + 1; state.set_current_cursor_pos(new_pos); *ideal_cursor_column = new_pos; } Ok("".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().saturating_sub(1)); 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 { new_pos } else { find_word_end(current_input, new_pos + 1) }; 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()) } "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(); if !current_input.is_empty() { let new_pos = current_input.len().saturating_sub(1); state.set_current_cursor_pos(new_pos); *ideal_cursor_column = new_pos; } else { state.set_current_cursor_pos(0); *ideal_cursor_column = 0; } Ok("".to_string()) } _ => { key_sequence_tracker.reset(); Ok(format!("Unknown read-only action: {}", action)) }, } } 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(); if chars.is_empty() { return 0; } let current_pos = current_pos.min(chars.len()); if current_pos == chars.len() { return current_pos; } let mut pos = current_pos; let initial_type = get_char_type(chars[pos]); while pos < chars.len() && get_char_type(chars[pos]) == initial_type { pos += 1; } while pos < chars.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); let current_type = get_char_type(chars[pos]); if current_type != CharType::Whitespace { while pos < len && get_char_type(chars[pos]) == current_type { pos += 1; } return pos.saturating_sub(1); } 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 get_char_type(chars[pos]) != CharType::Whitespace { let word_type = get_char_type(chars[pos]); while pos > 0 && get_char_type(chars[pos - 1]) == word_type { pos -= 1; } } if pos == 0 && get_char_type(chars[0]) == CharType::Whitespace { 0 } else { pos } } fn find_prev_word_end(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[0]) == CharType::Whitespace { return 0; } if pos == 0 && get_char_type(chars[0]) != 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 } }