From e725c70570505abc9de8b69019787a0193a56294 Mon Sep 17 00:00:00 2001 From: filipriec Date: Thu, 3 Apr 2025 19:50:35 +0200 Subject: [PATCH] splitting read_only mode --- .../src/functions/modes/read_only/form_ro.rs | 193 ++++++------ client/src/modes/canvas/read_only.rs | 283 +----------------- 2 files changed, 107 insertions(+), 369 deletions(-) diff --git a/client/src/functions/modes/read_only/form_ro.rs b/client/src/functions/modes/read_only/form_ro.rs index 88434d1..50a6f8a 100644 --- a/client/src/functions/modes/read_only/form_ro.rs +++ b/client/src/functions/modes/read_only/form_ro.rs @@ -1,56 +1,8 @@ // src/functions/modes/read_only/form_ro.rs use crate::config::binds::key_sequences::KeySequenceTracker; -use crate::services::grpc_client::GrpcClient; use crate::state::canvas_state::CanvasState; -use crate::state::pages::form::FormState; - -// --- Context-Specific Actions Handled Here --- -const CONTEXT_ACTIONS_FORM: &[&str] = &[ - "previous_entry", - "next_entry", - "move_up", - "move_down", - "move_first_line", - "move_last_line", -]; - -pub async fn handle_action( - action: &str, - form_state: &mut FormState, - grpc_client: &mut GrpcClient, - current_position: &mut u64, - total_count: u64, - ideal_cursor_column: &mut usize, - key_sequence_tracker: &mut KeySequenceTracker, - command_message: &mut String, -) -> Result> { - if CONTEXT_ACTIONS_FORM.contains(&action) { - // Delegate context-specific actions to the original handler - // (or implement the logic directly here if preferred) - crate::tui::functions::form::handle_action( - action, - form_state, - grpc_client, - current_position, - total_count, - ideal_cursor_column, - ) - .await - } else { - // Handle generic actions using the local execute_action - execute_action( - action, - form_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 { @@ -59,16 +11,14 @@ 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, this case should ideally not be hit - // for these actions, but included for robustness. "previous_entry" | "next_entry" | "move_up" | "move_down" | "move_first_line" | "move_last_line" => { key_sequence_tracker.reset(); @@ -78,7 +28,6 @@ async fn execute_action( )) } "exit_edit_mode" => { - // This action might not make sense here if only called from read-only key_sequence_tracker.reset(); command_message.clear(); Ok("".to_string()) @@ -154,7 +103,7 @@ async fn execute_action( state.set_current_cursor_pos(new_pos); *ideal_cursor_column = new_pos; } - Ok("Moved to previous word end".to_string()) // Maybe clear msg? + Ok("Moved to previous word end".to_string()) } "move_line_start" => { state.set_current_cursor_pos(0); @@ -174,9 +123,9 @@ async fn execute_action( 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)) + }, } } @@ -192,69 +141,139 @@ fn get_char_type(c: char) -> CharType { 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(...) // Keep this helper if needed by find_word_end -// ... (keep all find_* helper functions: find_next_word_end, find_word_end, find_prev_word_start, find_prev_word_end) 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); - 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; } + + 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; } - let mut pos = current_pos.min(len.saturating_sub(1)); // Use saturating_sub + 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; } + 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); } + 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; } + 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; } - 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 } -} + 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 670c161..a81813d 100644 --- a/client/src/modes/canvas/read_only.rs +++ b/client/src/modes/canvas/read_only.rs @@ -6,15 +6,9 @@ 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 crossterm::event::KeyEvent; -#[derive(PartialEq)] -enum CharType { - Whitespace, - Alphanumeric, - Punctuation, -} - pub async fn handle_read_only_event( app_state: &crate::state::state::AppState, key: KeyEvent, @@ -241,278 +235,3 @@ pub async fn handle_read_only_event( Ok((false, command_message.clone())) } - -async fn execute_action( - action: &str, - state: &mut S, - ideal_cursor_column: &mut usize, - key_sequence_tracker: &mut KeySequenceTracker, - command_message: &mut String, -) -> Result> { - match action { - "previous_entry" | "next_entry" | "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 - )) - } - "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); - - // Only move if we're not already at the found position - let final_pos = if new_pos != current_pos { - new_pos - } else { - // If already at a word end, jump to next word's end - 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_next_word_end(text: &str, current_pos: usize) -> usize { - let chars: Vec = text.chars().collect(); - if chars.is_empty() { - return 0; - } - - // Find start of next word - let next_start = find_next_word_start(text, current_pos); - - // Find end of that word - 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; - } - - 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; - } - - let mut pos = current_pos.min(len - 1); - let original_pos = pos; - - // First try to find end of current word - let current_type = get_char_type(chars[pos]); - if current_type != CharType::Whitespace { - // Move forward to word end - while pos < len && get_char_type(chars[pos]) == current_type { - pos += 1; - } - return pos.saturating_sub(1); - } - - // If in whitespace, find next word's end - 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 - } -} -