This commit is contained in:
filipriec
2025-04-02 22:26:35 +02:00
parent 9e36385e63
commit 1a09624242
2 changed files with 82 additions and 42 deletions

View File

@@ -319,11 +319,20 @@ async fn execute_edit_action<S: CanvasState>(
"move_word_end" => { "move_word_end" => {
let current_input = state.get_current_input(); let current_input = state.get_current_input();
if !current_input.is_empty() { if !current_input.is_empty() {
let new_pos = let current_pos = state.current_cursor_pos();
find_word_end(current_input, state.current_cursor_pos()); let new_pos = find_word_end(current_input, current_pos);
let final_pos = new_pos.min(current_input.len());
state.set_current_cursor_pos(final_pos); // If already at word end, jump to next word's end
*ideal_cursor_column = final_pos; let final_pos = if new_pos == current_pos {
find_word_end(current_input, new_pos + 1)
} else {
new_pos
};
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()) Ok("".to_string())
} }
@@ -400,19 +409,17 @@ fn find_word_end(text: &str, current_pos: usize) -> usize {
if len == 0 { if len == 0 {
return 0; return 0;
} }
if current_pos >= len {
return len; let mut pos = current_pos.min(len - 1);
// If already at whitespace, find next word first
if get_char_type(chars[pos]) == CharType::Whitespace {
pos = find_next_word_start(text, pos);
} }
let mut pos = current_pos; // Now find end of current/next word
if pos >= len {
if get_char_type(chars[pos]) == CharType::Whitespace { return len.saturating_sub(1);
while pos < len && get_char_type(chars[pos]) == CharType::Whitespace {
pos += 1;
}
if pos == len {
return len;
}
} }
let word_type = get_char_type(chars[pos]); let word_type = get_char_type(chars[pos]);
@@ -420,11 +427,8 @@ fn find_word_end(text: &str, current_pos: usize) -> usize {
pos += 1; pos += 1;
} }
if pos > current_pos && pos > 0 { // Return last character of the word
pos - 1 pos.saturating_sub(1).min(len.saturating_sub(1))
} else {
current_pos.min(len.saturating_sub(1))
}
} }
fn find_prev_word_start(text: &str, current_pos: usize) -> usize { fn find_prev_word_start(text: &str, current_pos: usize) -> usize {

View File

@@ -296,18 +296,21 @@ async fn execute_action<S: CanvasState>(
"move_word_end" => { "move_word_end" => {
let current_input = state.get_current_input(); let current_input = state.get_current_input();
if !current_input.is_empty() { if !current_input.is_empty() {
// 1. Find the index of the last character of the target word let current_pos = state.current_cursor_pos();
let new_pos = let new_pos = find_word_end(current_input, current_pos);
find_word_end(current_input, state.current_cursor_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)
};
// 2. Clamp the position for Read-Only mode
// max_valid_index is the index of the VERY LAST character in the input string
let max_valid_index = current_input.len().saturating_sub(1); let max_valid_index = current_input.len().saturating_sub(1);
let final_pos = new_pos.min(max_valid_index); let clamped_pos = final_pos.min(max_valid_index);
state.set_current_cursor_pos(clamped_pos);
// 3. Set the cursor *ideal_cursor_column = clamped_pos;
state.set_current_cursor_pos(final_pos);
*ideal_cursor_column = final_pos;
} }
Ok("".to_string()) Ok("".to_string())
} }
@@ -394,29 +397,62 @@ fn find_next_word_start(text: &str, current_pos: usize) -> usize {
pos pos
} }
fn find_word_end(text: &str, current_pos: usize) -> usize { fn find_next_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;
} }
let mut pos = current_pos.min(chars.len().saturating_sub(1));
if get_char_type(chars[pos]) == CharType::Whitespace { // Find start of next word
while pos + 1 < chars.len() && get_char_type(chars[pos + 1]) == CharType::Whitespace { 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<char> = 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; pos += 1;
} }
if pos + 1 >= chars.len() || get_char_type(chars[pos + 1]) == CharType::Whitespace { return pos.saturating_sub(1);
return pos;
}
pos += 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]); let word_type = get_char_type(chars[pos]);
while pos + 1 < chars.len() && get_char_type(chars[pos + 1]) == word_type { while pos < len && get_char_type(chars[pos]) == word_type {
pos += 1; pos += 1;
} }
pos pos.saturating_sub(1).min(len.saturating_sub(1))
} }
fn find_prev_word_start(text: &str, current_pos: usize) -> usize { fn find_prev_word_start(text: &str, current_pos: usize) -> usize {