From 6d6fff474f1110ac64649a0c80c055ca97964f5e Mon Sep 17 00:00:00 2001 From: filipriec Date: Thu, 3 Apr 2025 20:04:01 +0200 Subject: [PATCH] switching now execute action functions left to right --- .../src/functions/modes/read_only/auth_ro.rs | 275 +++++++++--------- client/src/modes/canvas/read_only.rs | 122 ++++---- 2 files changed, 186 insertions(+), 211 deletions(-) diff --git a/client/src/functions/modes/read_only/auth_ro.rs b/client/src/functions/modes/read_only/auth_ro.rs index c0892f7..0310ca0 100644 --- a/client/src/functions/modes/read_only/auth_ro.rs +++ b/client/src/functions/modes/read_only/auth_ro.rs @@ -1,47 +1,8 @@ -// src/functions/modes/read_only/auth_fun.rs +// src/functions/modes/read_only/auth_ro.rs use crate::config::binds::key_sequences::KeySequenceTracker; use crate::state::canvas_state::CanvasState; -use crate::state::pages::auth::AuthState; - -// --- Context-Specific Actions Handled Here --- -const CONTEXT_ACTIONS_LOGIN: &[&str] = &[ - "move_up", - "move_down", - "move_first_line", - "move_last_line", -]; - -pub async fn handle_action( - action: &str, - auth_state: &mut AuthState, - ideal_cursor_column: &mut usize, - key_sequence_tracker: &mut KeySequenceTracker, - command_message: &mut String, -) -> Result> { - if CONTEXT_ACTIONS_LOGIN.contains(&action) { - // Delegate context-specific actions to the original handler - // (or implement the logic directly here if preferred) - crate::tui::functions::login::handle_action( - action, - auth_state, - ideal_cursor_column, - ) - .await - } else { - // Handle generic actions using the local execute_action - execute_action( - action, - auth_state, - ideal_cursor_column, - key_sequence_tracker, - command_message, - ) - .await - } -} - -// --- Generic Action Implementation (Copied and made private) --- +use std::error::Error; #[derive(PartialEq)] enum CharType { @@ -50,26 +11,21 @@ enum CharType { Punctuation, } -async fn execute_action( +pub async fn execute_action( action: &str, state: &mut S, ideal_cursor_column: &mut usize, key_sequence_tracker: &mut KeySequenceTracker, command_message: &mut String, -) -> Result> { +) -> Result> { match action { - // Context actions are handled above - "move_up" | "move_down" | "move_first_line" | "move_last_line" => { - key_sequence_tracker.reset(); - Ok(format!( - "Action '{}' should be handled by context-specific logic", - action - )) - } - // These form-specific actions don't apply to login - "previous_entry" | "next_entry" => { + "previous_entry" | "next_entry" | "move_up" | "move_down" | + "move_first_line" | "move_last_line" => { key_sequence_tracker.reset(); - Ok(format!("Action '{}' not applicable in login context", action)) + Ok(format!( + "Action '{}' should be handled by context-specific logic", + action + )) } "exit_edit_mode" => { key_sequence_tracker.reset(); @@ -86,20 +42,13 @@ async fn execute_action( "move_right" => { let current_input = state.get_current_input(); let current_pos = state.current_cursor_pos(); - // Login fields often allow cursor at the very end for appending - if current_pos < current_input.len() { - let new_pos = current_pos + 1; - state.set_current_cursor_pos(new_pos); - *ideal_cursor_column = new_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; } - // Original logic (prevents going one past the end): - // 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" => { @@ -107,10 +56,7 @@ async fn execute_action( if !current_input.is_empty() { let new_pos = find_next_word_start(current_input, state.current_cursor_pos()); - // Allow cursor at end for login fields - let final_pos = new_pos.min(current_input.len()); - // Original: - // let final_pos = new_pos.min(current_input.len().saturating_sub(1)); + let final_pos = new_pos.min(current_input.len().saturating_sub(1)); state.set_current_cursor_pos(final_pos); *ideal_cursor_column = final_pos; } @@ -128,10 +74,7 @@ async fn execute_action( find_word_end(current_input, new_pos + 1) }; - // Allow cursor at end for login fields - let max_valid_index = current_input.len(); - // Original: - // let max_valid_index = current_input.len().saturating_sub(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; @@ -160,7 +103,7 @@ async fn execute_action( state.set_current_cursor_pos(new_pos); *ideal_cursor_column = new_pos; } - Ok("".to_string()) // Maybe clear msg? + Ok("Moved to previous word end".to_string()) } "move_line_start" => { state.set_current_cursor_pos(0); @@ -169,29 +112,23 @@ async fn execute_action( } "move_line_end" => { let current_input = state.get_current_input(); - // Allow cursor at end for login fields - let new_pos = current_input.len(); - // Original: - // 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; - // } - state.set_current_cursor_pos(new_pos); - *ideal_cursor_column = new_pos; + 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)) - } + key_sequence_tracker.reset(); + Ok(format!("Unknown read-only action: {}", action)) + }, } } - fn get_char_type(c: char) -> CharType { if c.is_whitespace() { CharType::Whitespace @@ -202,91 +139,141 @@ fn get_char_type(c: char) -> CharType { } } -// --- (Copy all find_* helper functions here too) --- fn find_next_word_start(text: &str, current_pos: usize) -> usize { let chars: Vec = text.chars().collect(); - if chars.is_empty() { return 0; } + if chars.is_empty() { + return 0; + } let current_pos = current_pos.min(chars.len()); - if current_pos == chars.len() { return current_pos; } + + 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; } + + 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_next_word_end(text: &str, current_pos: usize) -> usize { let chars: Vec = text.chars().collect(); - if chars.is_empty() { return 0; } + if chars.is_empty() { + return 0; + } + let next_start = find_next_word_start(text, current_pos); - // Allow end position for login - if next_start >= chars.len() { return chars.len(); } - // Original: if next_start >= chars.len() { return chars.len().saturating_sub(1); } + + if next_start >= chars.len() { + return chars.len().saturating_sub(1); + } + let mut pos = next_start; let word_type = get_char_type(chars[pos]); - while pos < chars.len() && get_char_type(chars[pos]) == word_type { pos += 1; } - // Allow end position for login - pos.min(chars.len()) - // Original: pos.saturating_sub(1).min(chars.len().saturating_sub(1)) + + while pos < chars.len() && get_char_type(chars[pos]) == word_type { + pos += 1; + } + + pos.saturating_sub(1).min(chars.len().saturating_sub(1)) } 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; } - // Allow end position for login - let mut pos = current_pos.min(len); - // Original: let mut pos = current_pos.min(len.saturating_sub(1)); - // Handle case where cursor is already at the end - if pos == len { return len; } + if len == 0 { + return 0; + } + + let mut pos = current_pos.min(len - 1); + let original_pos = pos; 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; } - // Allow end position for login - return pos; - // Original: return pos.saturating_sub(1); + while pos < len && get_char_type(chars[pos]) == current_type { + pos += 1; + } + return pos.saturating_sub(1); } + pos = find_next_word_start(text, pos); - // Allow end position for login - if pos >= len { return len; } - // Original: if pos >= len { return len.saturating_sub(1); } + 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; } - // Allow end position for login - pos.min(len) - // Original: pos.saturating_sub(1).min(len.saturating_sub(1)) + 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; } + 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; } + + 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; } + 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 } - 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; } // End of the first word is pos 0 - let word_type = get_char_type(chars[pos]); - // Find the start of the word the cursor is in or just passed - while pos > 0 && get_char_type(chars[pos - 1]) == word_type { pos -= 1; } - // Now pos is at the start of that word. We need the end of the *previous* word. - // Skip backwards over any whitespace before this word. - while pos > 0 && get_char_type(chars[pos - 1]) == CharType::Whitespace { pos -= 1; } - // If we are at the beginning, return 0 - if pos == 0 { return 0; } - // Now pos is at the end of the previous word. - pos - 1 -} + 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 + } +} diff --git a/client/src/modes/canvas/read_only.rs b/client/src/modes/canvas/read_only.rs index a81813d..51130a4 100644 --- a/client/src/modes/canvas/read_only.rs +++ b/client/src/modes/canvas/read_only.rs @@ -6,7 +6,7 @@ use crate::services::grpc_client::GrpcClient; use crate::state::canvas_state::CanvasState; use crate::state::pages::auth::AuthState; use crate::state::pages::form::FormState; -use crate::functions::modes::read_only::form_ro::execute_action; +use crate::functions::modes::read_only::{auth_ro, form_ro}; use crossterm::event::KeyEvent; pub async fn handle_read_only_event( @@ -75,8 +75,7 @@ pub async fn handle_read_only_event( key_sequence_tracker.add_key(key.code); let sequence = key_sequence_tracker.get_sequence(); - if let Some(action) = config.matches_key_sequence_generalized(&sequence) - { + if let Some(action) = config.matches_key_sequence_generalized(&sequence) { let result = if app_state.ui.show_form && CONTEXT_ACTIONS_FORM.contains(&action) { crate::tui::functions::form::handle_action( action, @@ -94,26 +93,24 @@ pub async fn handle_read_only_event( ideal_cursor_column, ) .await? + } else if app_state.ui.show_login { + auth_ro::execute_action( + action, + auth_state, + ideal_cursor_column, + key_sequence_tracker, + command_message, + ) + .await? } else { - if app_state.ui.show_login { - execute_action( - action, - auth_state, - ideal_cursor_column, - key_sequence_tracker, - command_message, - ) - .await? - } else { - execute_action( - action, - form_state, - ideal_cursor_column, - key_sequence_tracker, - command_message, - ) - .await? - } + form_ro::execute_action( + action, + form_state, + ideal_cursor_column, + key_sequence_tracker, + command_message, + ) + .await? }; key_sequence_tracker.reset(); return Ok((false, result)); @@ -124,9 +121,7 @@ pub async fn handle_read_only_event( } if sequence.len() == 1 && !config.is_key_sequence_prefix(&sequence) { - if let Some(action) = - config.get_read_only_action_for_key(key.code, key.modifiers) - { + if let Some(action) = config.get_read_only_action_for_key(key.code, key.modifiers) { let result = if app_state.ui.show_form && CONTEXT_ACTIONS_FORM.contains(&action) { crate::tui::functions::form::handle_action( action, @@ -144,39 +139,34 @@ pub async fn handle_read_only_event( ideal_cursor_column, ) .await? + } else if app_state.ui.show_login { + auth_ro::execute_action( + action, + auth_state, + ideal_cursor_column, + key_sequence_tracker, + command_message, + ) + .await? } else { - if app_state.ui.show_login { - execute_action( - action, - auth_state, - ideal_cursor_column, - key_sequence_tracker, - command_message, - ) - .await? - } else { - execute_action( - action, - form_state, - ideal_cursor_column, - key_sequence_tracker, - command_message, - ) - .await? - } + form_ro::execute_action( + action, + form_state, + ideal_cursor_column, + key_sequence_tracker, + command_message, + ) + .await? }; key_sequence_tracker.reset(); return Ok((false, result)); } } key_sequence_tracker.reset(); - } else { key_sequence_tracker.reset(); - if let Some(action) = - config.get_read_only_action_for_key(key.code, key.modifiers) - { + if let Some(action) = config.get_read_only_action_for_key(key.code, key.modifiers) { let result = if app_state.ui.show_form && CONTEXT_ACTIONS_FORM.contains(&action) { crate::tui::functions::form::handle_action( action, @@ -194,26 +184,24 @@ pub async fn handle_read_only_event( ideal_cursor_column, ) .await? + } else if app_state.ui.show_login { + auth_ro::execute_action( + action, + auth_state, + ideal_cursor_column, + key_sequence_tracker, + command_message, + ) + .await? } else { - if app_state.ui.show_login { - execute_action( - action, - auth_state, - ideal_cursor_column, - key_sequence_tracker, - command_message, - ) - .await? - } else { - execute_action( - action, - form_state, - ideal_cursor_column, - key_sequence_tracker, - command_message, - ) - .await? - } + form_ro::execute_action( + action, + form_state, + ideal_cursor_column, + key_sequence_tracker, + command_message, + ) + .await? }; return Ok((false, result)); }