renamed capital lettered functions and fixed examples
This commit is contained in:
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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); }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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};
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user