renamed capital lettered functions and fixed examples

This commit is contained in:
Priec
2025-08-16 23:10:50 +02:00
parent b2aa966588
commit 215be3cf09
10 changed files with 160 additions and 179 deletions

View File

@@ -324,23 +324,23 @@ impl<D: DataProvider> AutoCursorFormEditor<D> {
result result
} }
fn move_WORD_next(&mut self) { fn move_big_word_next(&mut self) {
self.editor.move_WORD_next(); self.editor.move_big_word_next();
self.update_visual_selection(); self.update_visual_selection();
} }
fn move_WORD_prev(&mut self) { fn move_big_word_prev(&mut self) {
self.editor.move_WORD_prev(); self.editor.move_big_word_prev();
self.update_visual_selection(); self.update_visual_selection();
} }
fn move_WORD_end(&mut self) { fn move_big_word_end(&mut self) {
self.editor.move_WORD_end(); self.editor.move_big_word_end();
self.update_visual_selection(); self.update_visual_selection();
} }
fn move_WORD_end_prev(&mut self) { fn move_big_word_end_prev(&mut self) {
self.editor.move_WORD_end_prev(); self.editor.move_big_word_end_prev();
self.update_visual_selection(); self.update_visual_selection();
} }
} }
@@ -564,23 +564,23 @@ fn handle_key_press(
} }
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('W'), _) => { (AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('W'), _) => {
editor.move_WORD_next(); editor.move_big_word_next();
editor.set_debug_message("W: next WORD start".to_string()); editor.set_debug_message("W: next WORD start".to_string());
editor.clear_command_buffer(); editor.clear_command_buffer();
} }
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('B'), _) => { (AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('B'), _) => {
editor.move_WORD_prev(); editor.move_big_word_prev();
editor.set_debug_message("B: previous WORD start".to_string()); editor.set_debug_message("B: previous WORD start".to_string());
editor.clear_command_buffer(); editor.clear_command_buffer();
} }
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('E'), _) => { (AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('E'), _) => {
// Check if this is 'gE' command // Check if this is 'gE' command
if editor.get_command_buffer() == "g" { if editor.get_command_buffer() == "g" {
editor.move_WORD_end_prev(); editor.move_big_word_end_prev();
editor.set_debug_message("gE: previous WORD end".to_string()); editor.set_debug_message("gE: previous WORD end".to_string());
editor.clear_command_buffer(); editor.clear_command_buffer();
} else { } else {
editor.move_WORD_end(); editor.move_big_word_end();
editor.set_debug_message("E: WORD end".to_string()); editor.set_debug_message("E: WORD end".to_string());
editor.clear_command_buffer(); editor.clear_command_buffer();
} }

View File

@@ -241,20 +241,17 @@ impl<D: DataProvider> ValidationFormEditor<D> {
if !self.validation_enabled { if !self.validation_enabled {
return; return;
} }
if let Some(result) = self.editor.current_field_validation() { let result = self.editor.validate_current_field();
match result { match result {
ValidationResult::Valid => { ValidationResult::Valid => {
self.debug_message = format!("Field {}: ✅ Valid", self.editor.current_field() + 1); self.debug_message = format!("Field {}: ✅ Valid", self.editor.current_field() + 1);
} }
ValidationResult::Warning { message } => { ValidationResult::Warning { message } => {
self.debug_message = format!("Field {}: ⚠️ {}", self.editor.current_field() + 1, message); self.debug_message = format!("Field {}: ⚠️ {}", self.editor.current_field() + 1, message);
} }
ValidationResult::Error { message } => { ValidationResult::Error { message } => {
self.debug_message = format!("Field {}: ❌ {}", self.editor.current_field() + 1, message); self.debug_message = format!("Field {}: ❌ {}", self.editor.current_field() + 1, message);
}
} }
} else {
self.debug_message = format!("Field {}: 🔍 Not validated yet", self.editor.current_field() + 1);
} }
} }
@@ -283,25 +280,24 @@ impl<D: DataProvider> ValidationFormEditor<D> {
if result.is_ok() { if result.is_ok() {
self.has_unsaved_changes = true; self.has_unsaved_changes = true;
// Show real-time validation feedback // Show real-time validation feedback
if let Some(validation_result) = self.editor.current_field_validation() { let validation_result = self.editor.validate_current_field();
match validation_result { match validation_result {
ValidationResult::Valid => { ValidationResult::Valid => {
// Don't spam with valid messages, just show character count if applicable // Don't spam with valid messages, just show character count if applicable
if let Some(limits) = self.get_current_field_limits() { if let Some(limits) = self.get_current_field_limits() {
let field_index = self.editor.current_field(); let field_index = self.editor.current_field();
if let Some(status) = limits.status_text( if let Some(status) = limits.status_text(
self.editor.data_provider().field_value(field_index) self.editor.data_provider().field_value(field_index)
) { ) {
self.debug_message = format!("✏️ {}", status); self.debug_message = format!("✏️ {}", status);
}
} }
} }
ValidationResult::Warning { message } => { }
self.debug_message = format!("⚠️ {}", message); ValidationResult::Warning { message } => {
} self.debug_message = format!("⚠️ {}", message);
ValidationResult::Error { message } => { }
self.debug_message = format!("{}", message); ValidationResult::Error { message } => {
} self.debug_message = format!("{}", message);
} }
} }
} }

View File

@@ -129,12 +129,11 @@ impl<D: DataProvider> AdvancedPatternFormEditor<D> {
fn insert_char(&mut self, ch: char) -> anyhow::Result<()> { fn insert_char(&mut self, ch: char) -> anyhow::Result<()> {
let result = self.editor.insert_char(ch); let result = self.editor.insert_char(ch);
if result.is_ok() { if result.is_ok() {
if let Some(validation_result) = self.editor.current_field_validation() { let validation_result = self.editor.validate_current_field();
match validation_result { match validation_result {
ValidationResult::Valid => { self.debug_message = "✅ Character accepted".to_string(); } ValidationResult::Valid => { self.debug_message = "✅ Character accepted".to_string(); }
ValidationResult::Warning { message } => { self.debug_message = format!("⚠️ Warning: {}", message); } ValidationResult::Warning { message } => { self.debug_message = format!("⚠️ Warning: {}", message); }
ValidationResult::Error { message } => { self.debug_message = format!("❌ Pattern violation: {}", message); } ValidationResult::Error { message } => { self.debug_message = format!("❌ Pattern violation: {}", message); }
}
} }
} }
Ok(result?) Ok(result?)
@@ -183,12 +182,11 @@ impl<D: DataProvider> AdvancedPatternFormEditor<D> {
fn update_field_validation_status(&mut self) { fn update_field_validation_status(&mut self) {
if !self.validation_enabled { return; } if !self.validation_enabled { return; }
if let Some(result) = self.editor.current_field_validation() { let result = self.editor.validate_current_field();
match result { match result {
ValidationResult::Valid => { self.debug_message = format!("Field {}: ✅ Pattern valid", self.editor.current_field() + 1); } ValidationResult::Valid => { self.debug_message = format!("Field {}: ✅ Pattern valid", self.editor.current_field() + 1); }
ValidationResult::Warning { message } => { self.debug_message = format!("Field {}: ⚠️ {}", self.editor.current_field() + 1, message); } ValidationResult::Warning { message } => { self.debug_message = format!("Field {}: ⚠️ {}", self.editor.current_field() + 1, message); }
ValidationResult::Error { message } => { self.debug_message = format!("Field {}: ❌ {}", self.editor.current_field() + 1, message); } ValidationResult::Error { message } => { self.debug_message = format!("Field {}: ❌ {}", self.editor.current_field() + 1, message); }
}
} }
} }

View File

@@ -7,10 +7,10 @@ pub mod char;
// Re-export commonly used functions // Re-export commonly used functions
pub use word::{ pub use word::{
find_next_word_start, find_word_end, find_prev_word_start, find_prev_word_end, find_next_word_start, find_word_end, find_prev_word_start, find_prev_word_end,
find_next_WORD_start, find_prev_WORD_start, find_WORD_end, find_prev_WORD_end, find_next_big_word_start, find_prev_big_word_start, find_big_word_end, find_prev_big_word_end,
// Add these new exports: // Add these new exports:
find_last_word_start_in_field, find_last_word_end_in_field, find_last_word_start_in_field, find_last_word_end_in_field,
find_last_WORD_start_in_field, find_last_WORD_end_in_field, find_last_big_word_start_in_field, find_last_big_word_end_in_field,
}; };
pub use line::{line_start_position, line_end_position, safe_cursor_position}; pub use line::{line_start_position, line_end_position, safe_cursor_position};
pub use char::{move_left, move_right, is_valid_cursor_position, clamp_cursor_position}; pub use char::{move_left, move_right, is_valid_cursor_position, clamp_cursor_position};

View File

@@ -161,8 +161,8 @@ pub fn find_prev_word_end(text: &str, current_pos: usize) -> usize {
0 0
} }
/// Find the start of the next WORD (whitespace-separated) /// Find the start of the next big_word (whitespace-separated)
pub fn find_next_WORD_start(text: &str, current_pos: usize) -> usize { pub fn find_next_big_word_start(text: &str, current_pos: usize) -> usize {
let chars: Vec<char> = text.chars().collect(); let chars: Vec<char> = text.chars().collect();
if chars.is_empty() || current_pos >= chars.len() { if chars.is_empty() || current_pos >= chars.len() {
return text.chars().count(); return text.chars().count();
@@ -170,12 +170,12 @@ pub fn find_next_WORD_start(text: &str, current_pos: usize) -> usize {
let mut pos = current_pos; let mut pos = current_pos;
// If we're on non-whitespace, skip to end of current WORD // If we're on non-whitespace, skip to end of current big_word
while pos < chars.len() && !chars[pos].is_whitespace() { while pos < chars.len() && !chars[pos].is_whitespace() {
pos += 1; pos += 1;
} }
// Skip whitespace to find start of next WORD // Skip whitespace to find start of next big_word
while pos < chars.len() && chars[pos].is_whitespace() { while pos < chars.len() && chars[pos].is_whitespace() {
pos += 1; pos += 1;
} }
@@ -183,8 +183,8 @@ pub fn find_next_WORD_start(text: &str, current_pos: usize) -> usize {
pos pos
} }
/// Find the start of the previous WORD (whitespace-separated) /// Find the start of the previous big_word (whitespace-separated)
pub fn find_prev_WORD_start(text: &str, current_pos: usize) -> usize { pub fn find_prev_big_word_start(text: &str, current_pos: usize) -> usize {
let chars: Vec<char> = text.chars().collect(); let chars: Vec<char> = text.chars().collect();
if chars.is_empty() || current_pos == 0 { if chars.is_empty() || current_pos == 0 {
return 0; return 0;
@@ -197,7 +197,7 @@ pub fn find_prev_WORD_start(text: &str, current_pos: usize) -> usize {
pos -= 1; pos -= 1;
} }
// Find start of current WORD by going back while non-whitespace // Find start of current big_word by going back while non-whitespace
while pos > 0 && !chars[pos - 1].is_whitespace() { while pos > 0 && !chars[pos - 1].is_whitespace() {
pos -= 1; pos -= 1;
} }
@@ -205,8 +205,8 @@ pub fn find_prev_WORD_start(text: &str, current_pos: usize) -> usize {
pos pos
} }
/// Find the end of the current/next WORD (whitespace-separated) /// Find the end of the current/next big_word (whitespace-separated)
pub fn find_WORD_end(text: &str, current_pos: usize) -> usize { pub fn find_big_word_end(text: &str, current_pos: usize) -> usize {
let chars: Vec<char> = text.chars().collect(); let chars: Vec<char> = text.chars().collect();
if chars.is_empty() { if chars.is_empty() {
return 0; return 0;
@@ -214,7 +214,7 @@ pub fn find_WORD_end(text: &str, current_pos: usize) -> usize {
let mut pos = current_pos; let mut pos = current_pos;
// If we're on whitespace, skip to start of next WORD // If we're on whitespace, skip to start of next big_word
while pos < chars.len() && chars[pos].is_whitespace() { while pos < chars.len() && chars[pos].is_whitespace() {
pos += 1; pos += 1;
} }
@@ -224,17 +224,17 @@ pub fn find_WORD_end(text: &str, current_pos: usize) -> usize {
return chars.len(); return chars.len();
} }
// Find end of current WORD (last non-whitespace char) // Find end of current big_word (last non-whitespace char)
while pos < chars.len() && !chars[pos].is_whitespace() { while pos < chars.len() && !chars[pos].is_whitespace() {
pos += 1; pos += 1;
} }
// Return position of last character in WORD // Return position of last character in big_word
pos.saturating_sub(1) pos.saturating_sub(1)
} }
/// Find the end of the previous WORD (whitespace-separated) /// Find the end of the previous big_word (whitespace-separated)
pub fn find_prev_WORD_end(text: &str, current_pos: usize) -> usize { pub fn find_prev_big_word_end(text: &str, current_pos: usize) -> usize {
let chars: Vec<char> = text.chars().collect(); let chars: Vec<char> = text.chars().collect();
if chars.is_empty() || current_pos == 0 { if chars.is_empty() || current_pos == 0 {
return 0; return 0;
@@ -252,17 +252,17 @@ pub fn find_prev_WORD_end(text: &str, current_pos: usize) -> usize {
return 0; return 0;
} }
// Skip back to start of current WORD, then forward to end // Skip back to start of current big_word, then forward to end
while pos > 0 && !chars[pos - 1].is_whitespace() { while pos > 0 && !chars[pos - 1].is_whitespace() {
pos -= 1; pos -= 1;
} }
// Now find end of this WORD // Now find end of this big_word
while pos < chars.len() && !chars[pos].is_whitespace() { while pos < chars.len() && !chars[pos].is_whitespace() {
pos += 1; pos += 1;
} }
// Return position of last character in WORD // Return position of last character in big_word
pos.saturating_sub(1) pos.saturating_sub(1)
} }
@@ -341,8 +341,8 @@ pub fn find_last_word_end_in_field(text: &str) -> usize {
pos pos
} }
/// Find the start of the last WORD in a field (for cross-field 'B' movement) /// Find the start of the last big_word in a field (for cross-field 'B' movement)
pub fn find_last_WORD_start_in_field(text: &str) -> usize { pub fn find_last_big_word_start_in_field(text: &str) -> usize {
if text.is_empty() { if text.is_empty() {
return 0; return 0;
} }
@@ -365,11 +365,11 @@ pub fn find_last_WORD_start_in_field(text: &str) -> usize {
} }
// Now we're on a non-whitespace character // Now we're on a non-whitespace character
// Find the start of this WORD by going backwards while chars are non-whitespace // Find the start of this big_word by going backwards while chars are non-whitespace
while pos > 0 { while pos > 0 {
let prev_char = chars[pos - 1]; let prev_char = chars[pos - 1];
// Stop if we hit whitespace (WORD boundary) // Stop if we hit whitespace (big_word boundary)
if prev_char.is_whitespace() { if prev_char.is_whitespace() {
break; break;
} }
@@ -379,8 +379,8 @@ pub fn find_last_WORD_start_in_field(text: &str) -> usize {
pos pos
} }
/// Find the end of the last WORD in a field (for cross-field 'gE' movement) /// Find the end of the last big_word in a field (for cross-field 'gE' movement)
pub fn find_last_WORD_end_in_field(text: &str) -> usize { pub fn find_last_big_word_end_in_field(text: &str) -> usize {
let chars: Vec<char> = text.chars().collect(); let chars: Vec<char> = text.chars().collect();
if chars.is_empty() { if chars.is_empty() {
return 0; return 0;
@@ -398,6 +398,6 @@ pub fn find_last_WORD_end_in_field(text: &str) -> usize {
return 0; return 0;
} }
// We're now at the end of the last WORD // We're now at the end of the last big_word
pos pos
} }

View File

@@ -69,6 +69,7 @@ pub fn render_canvas_with_highlight<T: CanvasTheme, D: DataProvider>(
let is_edit_mode = matches!(ui_state.mode(), crate::canvas::modes::AppMode::Edit); let is_edit_mode = matches!(ui_state.mode(), crate::canvas::modes::AppMode::Edit);
// Precompute completion for active field // Precompute completion for active field
#[cfg(feature = "suggestions")]
let active_completion = if ui_state.is_suggestions_active() let active_completion = if ui_state.is_suggestions_active()
&& ui_state.suggestions.active_field == Some(current_field_idx) && ui_state.suggestions.active_field == Some(current_field_idx)
{ {
@@ -77,6 +78,9 @@ pub fn render_canvas_with_highlight<T: CanvasTheme, D: DataProvider>(
None None
}; };
#[cfg(not(feature = "suggestions"))]
let active_completion: Option<String> = None;
render_canvas_fields( render_canvas_fields(
f, f,
area, area,

View File

@@ -14,7 +14,8 @@ pub struct EditorState {
// Mode state // Mode state
pub(crate) current_mode: AppMode, pub(crate) current_mode: AppMode,
// Suggestions dropdown state // Suggestions dropdown state (only available with suggestions feature)
#[cfg(feature = "suggestions")]
pub(crate) suggestions: SuggestionsUIState, pub(crate) suggestions: SuggestionsUIState,
// Selection state (for vim visual mode) // Selection state (for vim visual mode)
@@ -29,6 +30,7 @@ pub struct EditorState {
pub(crate) computed: Option<crate::computed::ComputedState>, pub(crate) computed: Option<crate::computed::ComputedState>,
} }
#[cfg(feature = "suggestions")]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SuggestionsUIState { pub struct SuggestionsUIState {
pub(crate) is_active: bool, pub(crate) is_active: bool,
@@ -53,6 +55,7 @@ impl EditorState {
cursor_pos: 0, cursor_pos: 0,
ideal_cursor_column: 0, ideal_cursor_column: 0,
current_mode: AppMode::Edit, current_mode: AppMode::Edit,
#[cfg(feature = "suggestions")]
suggestions: SuggestionsUIState { suggestions: SuggestionsUIState {
is_active: false, is_active: false,
is_loading: false, is_loading: false,
@@ -103,11 +106,13 @@ impl EditorState {
} }
/// Check if suggestions dropdown is active (for user's business logic) /// Check if suggestions dropdown is active (for user's business logic)
#[cfg(feature = "suggestions")]
pub fn is_suggestions_active(&self) -> bool { pub fn is_suggestions_active(&self) -> bool {
self.suggestions.is_active self.suggestions.is_active
} }
/// Check if suggestions dropdown is loading (for user's business logic) /// Check if suggestions dropdown is loading (for user's business logic)
#[cfg(feature = "suggestions")]
pub fn is_suggestions_loading(&self) -> bool { pub fn is_suggestions_loading(&self) -> bool {
self.suggestions.is_loading self.suggestions.is_loading
} }
@@ -153,6 +158,7 @@ impl EditorState {
} }
/// Explicitly open suggestions — should only be called on Tab /// Explicitly open suggestions — should only be called on Tab
#[cfg(feature = "suggestions")]
pub(crate) fn open_suggestions(&mut self, field_index: usize) { pub(crate) fn open_suggestions(&mut self, field_index: usize) {
self.suggestions.is_active = true; self.suggestions.is_active = true;
self.suggestions.is_loading = true; self.suggestions.is_loading = true;
@@ -163,6 +169,7 @@ impl EditorState {
} }
/// Explicitly close suggestions — should be called on Esc or field change /// Explicitly close suggestions — should be called on Esc or field change
#[cfg(feature = "suggestions")]
pub(crate) fn close_suggestions(&mut self) { pub(crate) fn close_suggestions(&mut self) {
self.suggestions.is_active = false; self.suggestions.is_active = false;
self.suggestions.is_loading = false; self.suggestions.is_loading = false;

View File

@@ -1,6 +1,7 @@
// src/data_provider.rs // src/data_provider.rs
//! Simplified user interface - only business data, no UI state //! Simplified user interface - only business data, no UI state
#[cfg(feature = "suggestions")]
use anyhow::Result; use anyhow::Result;
#[cfg(feature = "suggestions")] #[cfg(feature = "suggestions")]
use async_trait::async_trait; use async_trait::async_trait;

View File

@@ -196,20 +196,20 @@ impl<D: DataProvider> FormEditor<D> {
self.move_word_end_prev(); self.move_word_end_prev();
} }
pub fn move_WORD_next_with_selection(&mut self) { pub fn move_big_word_next_with_selection(&mut self) {
self.move_WORD_next(); self.move_big_word_next();
} }
pub fn move_WORD_end_with_selection(&mut self) { pub fn move_big_word_end_with_selection(&mut self) {
self.move_WORD_end(); self.move_big_word_end();
} }
pub fn move_WORD_prev_with_selection(&mut self) { pub fn move_big_word_prev_with_selection(&mut self) {
self.move_WORD_prev(); self.move_big_word_prev();
} }
pub fn move_WORD_end_prev_with_selection(&mut self) { pub fn move_big_word_end_prev_with_selection(&mut self) {
self.move_WORD_end_prev(); self.move_big_word_end_prev();
} }
pub fn move_line_start_with_selection(&mut self) { pub fn move_line_start_with_selection(&mut self) {

View File

@@ -7,11 +7,7 @@ use crate::canvas::modes::AppMode;
use crate::editor::FormEditor; use crate::editor::FormEditor;
use crate::DataProvider; use crate::DataProvider;
use crate::canvas::actions::movement::word::{ use crate::canvas::actions::movement::word::{
find_last_WORD_end_in_field, find_last_WORD_start_in_field, find_last_big_word_start_in_field, find_last_word_start_in_field,
find_last_word_end_in_field, find_last_word_start_in_field,
find_next_WORD_start, find_next_word_start, find_prev_WORD_end,
find_prev_WORD_start, find_prev_word_end, find_prev_word_start,
find_WORD_end, find_word_end,
}; };
impl<D: DataProvider> FormEditor<D> { impl<D: DataProvider> FormEditor<D> {
@@ -380,7 +376,6 @@ impl<D: DataProvider> FormEditor<D> {
return; return;
} }
// CHANGE THIS LINE: replace find_prev_word_end_corrected with find_prev_word_end
let new_pos = find_prev_word_end(current_text, current_pos); let new_pos = find_prev_word_end(current_text, current_pos);
// Only try to cross fields if we didn't move at all (stayed at same position) // Only try to cross fields if we didn't move at all (stayed at same position)
@@ -413,30 +408,30 @@ impl<D: DataProvider> FormEditor<D> {
} }
} }
/// Move to start of next WORD (vim W) - can cross field boundaries /// Move to start of next big_word (vim W) - can cross field boundaries
pub fn move_WORD_next(&mut self) { pub fn move_big_word_next(&mut self) {
use crate::canvas::actions::movement::word::find_next_WORD_start; use crate::canvas::actions::movement::word::find_next_big_word_start;
let current_text = self.current_text(); let current_text = self.current_text();
if current_text.is_empty() { if current_text.is_empty() {
// Empty field - try to move to next field // Empty field - try to move to next field
if self.move_down().is_ok() { if self.move_down().is_ok() {
// Successfully moved to next field, try to find first WORD // Successfully moved to next field, try to find first big_word
let new_text = self.current_text(); let new_text = self.current_text();
if !new_text.is_empty() { if !new_text.is_empty() {
let first_WORD_pos = if new_text.chars().next().map_or(false, |c| !c.is_whitespace()) { let first_big_word_pos = if new_text.chars().next().map_or(false, |c| !c.is_whitespace()) {
// Field starts with non-whitespace, go to position 0 // Field starts with non-whitespace, go to position 0
0 0
} else { } else {
// Field starts with whitespace, find first WORD // Field starts with whitespace, find first big_word
find_next_WORD_start(new_text, 0) find_next_big_word_start(new_text, 0)
}; };
let is_edit_mode = self.ui_state.current_mode == AppMode::Edit; let is_edit_mode = self.ui_state.current_mode == AppMode::Edit;
let char_len = new_text.chars().count(); let char_len = new_text.chars().count();
let final_pos = if is_edit_mode { let final_pos = if is_edit_mode {
first_WORD_pos.min(char_len) first_big_word_pos.min(char_len)
} else { } else {
first_WORD_pos.min(char_len.saturating_sub(1)) first_big_word_pos.min(char_len.saturating_sub(1))
}; };
self.ui_state.cursor_pos = final_pos; self.ui_state.cursor_pos = final_pos;
self.ui_state.ideal_cursor_column = final_pos; self.ui_state.ideal_cursor_column = final_pos;
@@ -446,7 +441,7 @@ impl<D: DataProvider> FormEditor<D> {
} }
let current_pos = self.ui_state.cursor_pos; let current_pos = self.ui_state.cursor_pos;
let new_pos = find_next_WORD_start(current_text, current_pos); let new_pos = find_next_big_word_start(current_text, current_pos);
// Check if we've hit the end of the current field // Check if we've hit the end of the current field
if new_pos >= current_text.chars().count() { if new_pos >= current_text.chars().count() {
@@ -459,20 +454,20 @@ impl<D: DataProvider> FormEditor<D> {
self.ui_state.cursor_pos = 0; self.ui_state.cursor_pos = 0;
self.ui_state.ideal_cursor_column = 0; self.ui_state.ideal_cursor_column = 0;
} else { } else {
// Find first WORD in new field // Find first big_word in new field
let first_WORD_pos = if new_text.chars().next().map_or(false, |c| !c.is_whitespace()) { let first_big_word_pos = if new_text.chars().next().map_or(false, |c| !c.is_whitespace()) {
// Field starts with non-whitespace, go to position 0 // Field starts with non-whitespace, go to position 0
0 0
} else { } else {
// Field starts with whitespace, find first WORD // Field starts with whitespace, find first big_word
find_next_WORD_start(new_text, 0) find_next_big_word_start(new_text, 0)
}; };
let is_edit_mode = self.ui_state.current_mode == AppMode::Edit; let is_edit_mode = self.ui_state.current_mode == AppMode::Edit;
let char_len = new_text.chars().count(); let char_len = new_text.chars().count();
let final_pos = if is_edit_mode { let final_pos = if is_edit_mode {
first_WORD_pos.min(char_len) first_big_word_pos.min(char_len)
} else { } else {
first_WORD_pos.min(char_len.saturating_sub(1)) first_big_word_pos.min(char_len.saturating_sub(1))
}; };
self.ui_state.cursor_pos = final_pos; self.ui_state.cursor_pos = final_pos;
self.ui_state.ideal_cursor_column = final_pos; self.ui_state.ideal_cursor_column = final_pos;
@@ -480,7 +475,7 @@ impl<D: DataProvider> FormEditor<D> {
} }
// If move_down() failed, we stay where we are (at end of last field) // If move_down() failed, we stay where we are (at end of last field)
} else { } else {
// Normal WORD movement within current field // Normal big_word movement within current field
let is_edit_mode = self.ui_state.current_mode == AppMode::Edit; let is_edit_mode = self.ui_state.current_mode == AppMode::Edit;
let char_len = current_text.chars().count(); let char_len = current_text.chars().count();
let final_pos = if is_edit_mode { let final_pos = if is_edit_mode {
@@ -494,22 +489,22 @@ impl<D: DataProvider> FormEditor<D> {
} }
} }
/// Move to start of previous WORD (vim B) - can cross field boundaries /// Move to start of previous big_word (vim B) - can cross field boundaries
pub fn move_WORD_prev(&mut self) { pub fn move_big_word_prev(&mut self) {
use crate::canvas::actions::movement::word::find_prev_WORD_start; use crate::canvas::actions::movement::word::find_prev_big_word_start;
let current_text = self.current_text(); let current_text = self.current_text();
if current_text.is_empty() { if current_text.is_empty() {
// Empty field - try to move to previous field and find last WORD // Empty field - try to move to previous field and find last big_word
let current_field = self.ui_state.current_field; let current_field = self.ui_state.current_field;
if self.move_up().is_ok() { if self.move_up().is_ok() {
// Check if we actually moved to a different field // Check if we actually moved to a different field
if self.ui_state.current_field != current_field { if self.ui_state.current_field != current_field {
let new_text = self.current_text(); let new_text = self.current_text();
if !new_text.is_empty() { if !new_text.is_empty() {
let last_WORD_start = find_last_WORD_start_in_field(new_text); let last_big_word_start = find_last_big_word_start_in_field(new_text);
self.ui_state.cursor_pos = last_WORD_start; self.ui_state.cursor_pos = last_big_word_start;
self.ui_state.ideal_cursor_column = last_WORD_start; self.ui_state.ideal_cursor_column = last_big_word_start;
} }
} }
} }
@@ -526,43 +521,43 @@ impl<D: DataProvider> FormEditor<D> {
if self.ui_state.current_field != current_field { if self.ui_state.current_field != current_field {
let new_text = self.current_text(); let new_text = self.current_text();
if !new_text.is_empty() { if !new_text.is_empty() {
let last_WORD_start = find_last_WORD_start_in_field(new_text); let last_big_word_start = find_last_big_word_start_in_field(new_text);
self.ui_state.cursor_pos = last_WORD_start; self.ui_state.cursor_pos = last_big_word_start;
self.ui_state.ideal_cursor_column = last_WORD_start; self.ui_state.ideal_cursor_column = last_big_word_start;
} }
} }
} }
return; return;
} }
// Try to find previous WORD in current field // Try to find previous big_word in current field
let new_pos = find_prev_WORD_start(current_text, current_pos); let new_pos = find_prev_big_word_start(current_text, current_pos);
// Check if we actually moved // Check if we actually moved
if new_pos < current_pos { if new_pos < current_pos {
// Normal WORD movement within current field - we found a previous WORD // Normal big_word movement within current field - we found a previous big_word
self.ui_state.cursor_pos = new_pos; self.ui_state.cursor_pos = new_pos;
self.ui_state.ideal_cursor_column = new_pos; self.ui_state.ideal_cursor_column = new_pos;
} else { } else {
// We didn't move (probably at start of first WORD), try previous field // We didn't move (probably at start of first big_word), try previous field
let current_field = self.ui_state.current_field; let current_field = self.ui_state.current_field;
if self.move_up().is_ok() { if self.move_up().is_ok() {
// Check if we actually moved to a different field // Check if we actually moved to a different field
if self.ui_state.current_field != current_field { if self.ui_state.current_field != current_field {
let new_text = self.current_text(); let new_text = self.current_text();
if !new_text.is_empty() { if !new_text.is_empty() {
let last_WORD_start = find_last_WORD_start_in_field(new_text); let last_big_word_start = find_last_big_word_start_in_field(new_text);
self.ui_state.cursor_pos = last_WORD_start; self.ui_state.cursor_pos = last_big_word_start;
self.ui_state.ideal_cursor_column = last_WORD_start; self.ui_state.ideal_cursor_column = last_big_word_start;
} }
} }
} }
} }
} }
/// Move to end of current/next WORD (vim E) - can cross field boundaries /// Move to end of current/next big_word (vim E) - can cross field boundaries
pub fn move_WORD_end(&mut self) { pub fn move_big_word_end(&mut self) {
use crate::canvas::actions::movement::word::find_WORD_end; use crate::canvas::actions::movement::word::find_big_word_end;
let current_text = self.current_text(); let current_text = self.current_text();
if current_text.is_empty() { if current_text.is_empty() {
@@ -570,14 +565,14 @@ impl<D: DataProvider> FormEditor<D> {
if self.move_down().is_ok() { if self.move_down().is_ok() {
let new_text = self.current_text(); let new_text = self.current_text();
if !new_text.is_empty() { if !new_text.is_empty() {
// Find first WORD end in new field // Find first big_word end in new field
let first_WORD_end = find_WORD_end(new_text, 0); let first_big_word_end = find_big_word_end(new_text, 0);
let is_edit_mode = self.ui_state.current_mode == AppMode::Edit; let is_edit_mode = self.ui_state.current_mode == AppMode::Edit;
let char_len = new_text.chars().count(); let char_len = new_text.chars().count();
let final_pos = if is_edit_mode { let final_pos = if is_edit_mode {
first_WORD_end.min(char_len) first_big_word_end.min(char_len)
} else { } else {
first_WORD_end.min(char_len.saturating_sub(1)) first_big_word_end.min(char_len.saturating_sub(1))
}; };
self.ui_state.cursor_pos = final_pos; self.ui_state.cursor_pos = final_pos;
self.ui_state.ideal_cursor_column = final_pos; self.ui_state.ideal_cursor_column = final_pos;
@@ -588,12 +583,12 @@ impl<D: DataProvider> FormEditor<D> {
let current_pos = self.ui_state.cursor_pos; let current_pos = self.ui_state.cursor_pos;
let char_len = current_text.chars().count(); let char_len = current_text.chars().count();
let new_pos = find_WORD_end(current_text, current_pos); let new_pos = find_big_word_end(current_text, current_pos);
// Check if we didn't move or hit the end of the field // Check if we didn't move or hit the end of the field
if new_pos == current_pos && current_pos + 1 < char_len { if new_pos == current_pos && current_pos + 1 < char_len {
// Try next character and find WORD end from there // Try next character and find big_word end from there
let next_pos = find_WORD_end(current_text, current_pos + 1); let next_pos = find_big_word_end(current_text, current_pos + 1);
if next_pos < char_len { if next_pos < char_len {
let is_edit_mode = self.ui_state.current_mode == AppMode::Edit; let is_edit_mode = self.ui_state.current_mode == AppMode::Edit;
let final_pos = if is_edit_mode { let final_pos = if is_edit_mode {
@@ -610,23 +605,23 @@ impl<D: DataProvider> FormEditor<D> {
// If we're at or near the end of the field, try next field (but don't recurse) // If we're at or near the end of the field, try next field (but don't recurse)
if new_pos >= char_len.saturating_sub(1) { if new_pos >= char_len.saturating_sub(1) {
if self.move_down().is_ok() { if self.move_down().is_ok() {
// Find first WORD end in new field // Find first big_word end in new field
let new_text = self.current_text(); let new_text = self.current_text();
if !new_text.is_empty() { if !new_text.is_empty() {
let first_WORD_end = find_WORD_end(new_text, 0); let first_big_word_end = find_big_word_end(new_text, 0);
let is_edit_mode = self.ui_state.current_mode == AppMode::Edit; let is_edit_mode = self.ui_state.current_mode == AppMode::Edit;
let new_char_len = new_text.chars().count(); let new_char_len = new_text.chars().count();
let final_pos = if is_edit_mode { let final_pos = if is_edit_mode {
first_WORD_end.min(new_char_len) first_big_word_end.min(new_char_len)
} else { } else {
first_WORD_end.min(new_char_len.saturating_sub(1)) first_big_word_end.min(new_char_len.saturating_sub(1))
}; };
self.ui_state.cursor_pos = final_pos; self.ui_state.cursor_pos = final_pos;
self.ui_state.ideal_cursor_column = final_pos; self.ui_state.ideal_cursor_column = final_pos;
} }
} }
} else { } else {
// Normal WORD end movement within current field // Normal big_word end movement within current field
let is_edit_mode = self.ui_state.current_mode == AppMode::Edit; let is_edit_mode = self.ui_state.current_mode == AppMode::Edit;
let final_pos = if is_edit_mode { let final_pos = if is_edit_mode {
new_pos.min(char_len) new_pos.min(char_len)
@@ -639,23 +634,24 @@ impl<D: DataProvider> FormEditor<D> {
} }
} }
/// Move to end of previous WORD (vim gE) - can cross field boundaries /// Move to end of previous big_word (vim gE) - can cross field boundaries
pub fn move_WORD_end_prev(&mut self) { pub fn move_big_word_end_prev(&mut self) {
use crate::canvas::actions::movement::word::{find_prev_WORD_end, find_WORD_end}; use crate::canvas::actions::movement::word::{
find_prev_big_word_end, find_big_word_end,
};
let current_text = self.current_text(); let current_text = self.current_text();
if current_text.is_empty() { if current_text.is_empty() {
// Empty field - try to move to previous field (but don't recurse)
let current_field = self.ui_state.current_field; let current_field = self.ui_state.current_field;
if self.move_up().is_ok() { if self.move_up().is_ok() {
// Check if we actually moved to a different field
if self.ui_state.current_field != current_field { if self.ui_state.current_field != current_field {
let new_text = self.current_text(); let new_text = self.current_text();
if !new_text.is_empty() { if !new_text.is_empty() {
// Find end of last WORD in the field // Find first big_word end in new field
let last_WORD_end = find_last_WORD_end_in_field(new_text); let last_big_word_end = find_big_word_end(new_text, 0);
self.ui_state.cursor_pos = last_WORD_end; self.ui_state.cursor_pos = last_big_word_end;
self.ui_state.ideal_cursor_column = last_WORD_end; self.ui_state.ideal_cursor_column = last_big_word_end;
} }
} }
} }
@@ -663,43 +659,23 @@ impl<D: DataProvider> FormEditor<D> {
} }
let current_pos = self.ui_state.cursor_pos; let current_pos = self.ui_state.cursor_pos;
let new_pos = find_prev_big_word_end(current_text, current_pos);
// Special case: if we're at position 0, jump to previous field (but don't recurse)
if current_pos == 0 {
let current_field = self.ui_state.current_field;
if self.move_up().is_ok() {
// Check if we actually moved to a different field
if self.ui_state.current_field != current_field {
let new_text = self.current_text();
if !new_text.is_empty() {
let last_WORD_end = find_last_WORD_end_in_field(new_text);
self.ui_state.cursor_pos = last_WORD_end;
self.ui_state.ideal_cursor_column = last_WORD_end;
}
}
}
return;
}
let new_pos = find_prev_WORD_end(current_text, current_pos);
// Only try to cross fields if we didn't move at all (stayed at same position) // Only try to cross fields if we didn't move at all (stayed at same position)
if new_pos == current_pos { if new_pos == current_pos {
// We didn't move within the current field, try previous field
let current_field = self.ui_state.current_field; let current_field = self.ui_state.current_field;
if self.move_up().is_ok() { if self.move_up().is_ok() {
// Check if we actually moved to a different field
if self.ui_state.current_field != current_field { if self.ui_state.current_field != current_field {
let new_text = self.current_text(); let new_text = self.current_text();
if !new_text.is_empty() { if !new_text.is_empty() {
let last_WORD_end = find_last_WORD_end_in_field(new_text); let last_big_word_end = find_big_word_end(new_text, 0);
self.ui_state.cursor_pos = last_WORD_end; self.ui_state.cursor_pos = last_big_word_end;
self.ui_state.ideal_cursor_column = last_WORD_end; self.ui_state.ideal_cursor_column = last_big_word_end;
} }
} }
} }
} else { } else {
// Normal WORD movement within current field // Normal big_word movement within current field
let is_edit_mode = self.ui_state.current_mode == AppMode::Edit; let is_edit_mode = self.ui_state.current_mode == AppMode::Edit;
let char_len = current_text.chars().count(); let char_len = current_text.chars().count();
let final_pos = if is_edit_mode { let final_pos = if is_edit_mode {
@@ -707,7 +683,6 @@ impl<D: DataProvider> FormEditor<D> {
} else { } else {
new_pos.min(char_len.saturating_sub(1)) new_pos.min(char_len.saturating_sub(1))
}; };
self.ui_state.cursor_pos = final_pos; self.ui_state.cursor_pos = final_pos;
self.ui_state.ideal_cursor_column = final_pos; self.ui_state.ideal_cursor_column = final_pos;
} }