suggestion works, but not exactly, needs more stuff
This commit is contained in:
@@ -70,7 +70,7 @@ prev_field = ["shift+enter"]
|
|||||||
exit = ["esc", "ctrl+e"]
|
exit = ["esc", "ctrl+e"]
|
||||||
delete_char_forward = ["delete"]
|
delete_char_forward = ["delete"]
|
||||||
delete_char_backward = ["backspace"]
|
delete_char_backward = ["backspace"]
|
||||||
move_left = [""]
|
move_left = ["left"]
|
||||||
move_right = ["right"]
|
move_right = ["right"]
|
||||||
suggestion_down = ["ctrl+n", "tab"]
|
suggestion_down = ["ctrl+n", "tab"]
|
||||||
suggestion_up = ["ctrl+p", "shift+tab"]
|
suggestion_up = ["ctrl+p", "shift+tab"]
|
||||||
|
|||||||
@@ -1,432 +1,159 @@
|
|||||||
// src/functions/modes/edit/form_e.rs
|
// src/functions/modes/edit/form_e.rs
|
||||||
|
|
||||||
|
use crate::modes::handlers::event::EventOutcome;
|
||||||
use crate::services::grpc_client::GrpcClient;
|
use crate::services::grpc_client::GrpcClient;
|
||||||
use crate::state::pages::canvas_state::CanvasState;
|
use crate::state::pages::canvas_state::CanvasState;
|
||||||
use crate::state::pages::form::FormState;
|
use crate::state::pages::form::FormState;
|
||||||
use crate::tui::functions::common::form::{revert, save};
|
use crate::tui::functions::common::form;
|
||||||
use crate::tui::functions::common::form::SaveOutcome;
|
|
||||||
use crate::modes::handlers::event::EventOutcome;
|
|
||||||
use crossterm::event::{KeyCode, KeyEvent};
|
|
||||||
use std::any::Any;
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use crossterm::event::{KeyCode, KeyEvent};
|
||||||
|
|
||||||
pub async fn execute_common_action<S: CanvasState + Any>(
|
pub async fn execute_common_action(
|
||||||
action: &str,
|
action: &str,
|
||||||
state: &mut S,
|
state: &mut FormState,
|
||||||
grpc_client: &mut GrpcClient,
|
grpc_client: &mut GrpcClient,
|
||||||
) -> Result<EventOutcome> {
|
) -> Result<EventOutcome> {
|
||||||
match action {
|
match action {
|
||||||
"save" | "revert" => {
|
"save" => {
|
||||||
if !state.has_unsaved_changes() {
|
let outcome = form::save(state, grpc_client).await?;
|
||||||
return Ok(EventOutcome::Ok("No changes to save or revert.".to_string()));
|
let message = match outcome {
|
||||||
}
|
form::SaveOutcome::NoChange => "No changes to save".to_string(),
|
||||||
if let Some(form_state) =
|
form::SaveOutcome::UpdatedExisting => "Entry updated successfully".to_string(),
|
||||||
(state as &mut dyn Any).downcast_mut::<FormState>()
|
form::SaveOutcome::CreatedNew(id) => format!("New entry {} created", id),
|
||||||
{
|
};
|
||||||
match action {
|
Ok(EventOutcome::DataSaved(outcome, message))
|
||||||
"save" => {
|
|
||||||
let save_result = save(
|
|
||||||
form_state,
|
|
||||||
grpc_client,
|
|
||||||
).await;
|
|
||||||
|
|
||||||
match save_result {
|
|
||||||
Ok(save_outcome) => {
|
|
||||||
let message = match save_outcome {
|
|
||||||
SaveOutcome::NoChange => "No changes to save.".to_string(),
|
|
||||||
SaveOutcome::UpdatedExisting => "Entry updated.".to_string(),
|
|
||||||
SaveOutcome::CreatedNew(_) => "New entry created.".to_string(),
|
|
||||||
};
|
|
||||||
Ok(EventOutcome::DataSaved(save_outcome, message))
|
|
||||||
}
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"revert" => {
|
|
||||||
let revert_result = revert(
|
|
||||||
form_state,
|
|
||||||
grpc_client,
|
|
||||||
).await;
|
|
||||||
|
|
||||||
match revert_result {
|
|
||||||
Ok(message) => Ok(EventOutcome::Ok(message)),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(EventOutcome::Ok(format!(
|
|
||||||
"Action '{}' not implemented for this state type.",
|
|
||||||
action
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => Ok(EventOutcome::Ok(format!("Common action '{}' not handled here.", action))),
|
"revert" => {
|
||||||
|
let message = form::revert(state, grpc_client).await?;
|
||||||
|
Ok(EventOutcome::Ok(message))
|
||||||
|
}
|
||||||
|
_ => Ok(EventOutcome::Ok(format!(
|
||||||
|
"Unknown common action: {}",
|
||||||
|
action
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn execute_edit_action<S: CanvasState>(
|
pub async fn execute_edit_action(
|
||||||
action: &str,
|
action: &str,
|
||||||
key: KeyEvent,
|
key: KeyEvent,
|
||||||
state: &mut S,
|
state: &mut FormState,
|
||||||
ideal_cursor_column: &mut usize,
|
ideal_cursor_column: &mut usize,
|
||||||
) -> Result<String> {
|
) -> Result<String> {
|
||||||
|
// --- Autocomplete Interaction Logic ---
|
||||||
|
if state.autocomplete_active {
|
||||||
|
match action {
|
||||||
|
"suggestion_down" => {
|
||||||
|
// ... logic is correct ...
|
||||||
|
if !state.autocomplete_suggestions.is_empty() {
|
||||||
|
let current = state.selected_suggestion_index.unwrap_or(0);
|
||||||
|
let next = (current + 1) % state.autocomplete_suggestions.len();
|
||||||
|
state.selected_suggestion_index = Some(next);
|
||||||
|
}
|
||||||
|
return Ok(String::new());
|
||||||
|
}
|
||||||
|
"suggestion_up" => {
|
||||||
|
// ... logic is correct ...
|
||||||
|
if !state.autocomplete_suggestions.is_empty() {
|
||||||
|
let current = state.selected_suggestion_index.unwrap_or(0);
|
||||||
|
let prev = if current == 0 {
|
||||||
|
state.autocomplete_suggestions.len() - 1
|
||||||
|
} else {
|
||||||
|
current - 1
|
||||||
|
};
|
||||||
|
state.selected_suggestion_index = Some(prev);
|
||||||
|
}
|
||||||
|
return Ok(String::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- THIS IS THE FIX ---
|
||||||
|
// We only handle "exit" if autocomplete is active.
|
||||||
|
"exit" => {
|
||||||
|
state.deactivate_autocomplete();
|
||||||
|
return Ok("Autocomplete cancelled".to_string());
|
||||||
|
}
|
||||||
|
// --- END OF FIX ---
|
||||||
|
|
||||||
|
"enter_decider" => {
|
||||||
|
// ... logic is correct ...
|
||||||
|
if let Some(selected_idx) = state.selected_suggestion_index {
|
||||||
|
if let Some(selection) = state.autocomplete_suggestions.get(selected_idx).cloned() {
|
||||||
|
*state.get_current_input_mut() = selection;
|
||||||
|
state.deactivate_autocomplete();
|
||||||
|
state.set_has_unsaved_changes(true);
|
||||||
|
return Ok("Selection made".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Default Edit Actions ---
|
||||||
match action {
|
match action {
|
||||||
|
"next_field" => {
|
||||||
|
let next_field = (state.current_field() + 1) % state.fields.len();
|
||||||
|
state.set_current_field(next_field);
|
||||||
|
let len = state.get_current_input().len();
|
||||||
|
state.set_current_cursor_pos(len);
|
||||||
|
*ideal_cursor_column = len;
|
||||||
|
Ok(String::new())
|
||||||
|
}
|
||||||
|
"prev_field" => {
|
||||||
|
let prev_field = if state.current_field() == 0 {
|
||||||
|
state.fields().len() - 1
|
||||||
|
} else {
|
||||||
|
state.current_field() - 1
|
||||||
|
};
|
||||||
|
state.set_current_field(prev_field);
|
||||||
|
let len = state.get_current_input().len();
|
||||||
|
state.set_current_cursor_pos(len);
|
||||||
|
*ideal_cursor_column = len;
|
||||||
|
Ok(String::new())
|
||||||
|
}
|
||||||
"insert_char" => {
|
"insert_char" => {
|
||||||
if let KeyCode::Char(c) = key.code {
|
if let KeyCode::Char(c) = key.code {
|
||||||
let cursor_pos = state.current_cursor_pos();
|
let current_pos = state.current_cursor_pos();
|
||||||
let field_value = state.get_current_input_mut();
|
state.get_current_input_mut().insert(current_pos, c);
|
||||||
let mut chars: Vec<char> = field_value.chars().collect();
|
state.set_current_cursor_pos(current_pos + 1);
|
||||||
if cursor_pos <= chars.len() {
|
|
||||||
chars.insert(cursor_pos, c);
|
|
||||||
*field_value = chars.into_iter().collect();
|
|
||||||
state.set_current_cursor_pos(cursor_pos + 1);
|
|
||||||
state.set_has_unsaved_changes(true);
|
|
||||||
*ideal_cursor_column = state.current_cursor_pos();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Ok("Error: insert_char called without a char key."
|
|
||||||
.to_string());
|
|
||||||
}
|
|
||||||
Ok("".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
"delete_char_backward" => {
|
|
||||||
if state.current_cursor_pos() > 0 {
|
|
||||||
let cursor_pos = state.current_cursor_pos();
|
|
||||||
let field_value = state.get_current_input_mut();
|
|
||||||
let mut chars: Vec<char> = field_value.chars().collect();
|
|
||||||
if cursor_pos <= chars.len() {
|
|
||||||
chars.remove(cursor_pos - 1);
|
|
||||||
*field_value = chars.into_iter().collect();
|
|
||||||
let new_pos = cursor_pos - 1;
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
state.set_has_unsaved_changes(true);
|
|
||||||
*ideal_cursor_column = new_pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok("".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
"delete_char_forward" => {
|
|
||||||
let cursor_pos = state.current_cursor_pos();
|
|
||||||
let field_value = state.get_current_input_mut();
|
|
||||||
let mut chars: Vec<char> = field_value.chars().collect();
|
|
||||||
if cursor_pos < chars.len() {
|
|
||||||
chars.remove(cursor_pos);
|
|
||||||
*field_value = chars.into_iter().collect();
|
|
||||||
state.set_has_unsaved_changes(true);
|
state.set_has_unsaved_changes(true);
|
||||||
*ideal_cursor_column = cursor_pos;
|
|
||||||
}
|
}
|
||||||
Ok("".to_string())
|
Ok(String::new())
|
||||||
}
|
}
|
||||||
|
"delete_char_backward" => {
|
||||||
"next_field" => {
|
let current_pos = state.current_cursor_pos();
|
||||||
let num_fields = state.fields().len();
|
if current_pos > 0 {
|
||||||
if num_fields > 0 {
|
state.get_current_input_mut().remove(current_pos - 1);
|
||||||
let current_field = state.current_field();
|
state.set_current_cursor_pos(current_pos - 1);
|
||||||
let new_field = (current_field + 1) % num_fields;
|
state.set_has_unsaved_changes(true);
|
||||||
state.set_current_field(new_field);
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let max_pos = current_input.len();
|
|
||||||
state.set_current_cursor_pos(
|
|
||||||
(*ideal_cursor_column).min(max_pos),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Ok("".to_string())
|
Ok(String::new())
|
||||||
}
|
}
|
||||||
|
"delete_char_forward" => {
|
||||||
"prev_field" => {
|
let current_pos = state.current_cursor_pos();
|
||||||
let num_fields = state.fields().len();
|
if current_pos < state.get_current_input().len() {
|
||||||
if num_fields > 0 {
|
state.get_current_input_mut().remove(current_pos);
|
||||||
let current_field = state.current_field();
|
state.set_has_unsaved_changes(true);
|
||||||
let new_field = if current_field == 0 {
|
|
||||||
num_fields - 1
|
|
||||||
} else {
|
|
||||||
current_field - 1
|
|
||||||
};
|
|
||||||
state.set_current_field(new_field);
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let max_pos = current_input.len();
|
|
||||||
state.set_current_cursor_pos(
|
|
||||||
(*ideal_cursor_column).min(max_pos),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Ok("".to_string())
|
Ok(String::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
"move_left" => {
|
"move_left" => {
|
||||||
let new_pos = state.current_cursor_pos().saturating_sub(1);
|
let current_pos = state.current_cursor_pos();
|
||||||
|
let new_pos = current_pos.saturating_sub(1);
|
||||||
state.set_current_cursor_pos(new_pos);
|
state.set_current_cursor_pos(new_pos);
|
||||||
*ideal_cursor_column = new_pos;
|
*ideal_cursor_column = new_pos;
|
||||||
Ok("".to_string())
|
Ok(String::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
"move_right" => {
|
"move_right" => {
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let current_pos = state.current_cursor_pos();
|
let current_pos = state.current_cursor_pos();
|
||||||
if current_pos < current_input.len() {
|
let len = state.get_current_input().len();
|
||||||
|
if current_pos < len {
|
||||||
let new_pos = current_pos + 1;
|
let new_pos = current_pos + 1;
|
||||||
state.set_current_cursor_pos(new_pos);
|
state.set_current_cursor_pos(new_pos);
|
||||||
*ideal_cursor_column = new_pos;
|
*ideal_cursor_column = new_pos;
|
||||||
}
|
}
|
||||||
Ok("".to_string())
|
Ok(String::new())
|
||||||
}
|
}
|
||||||
|
_ => Ok(format!("Unknown edit action: {}", action)),
|
||||||
"move_up" => {
|
|
||||||
let num_fields = state.fields().len();
|
|
||||||
if num_fields > 0 {
|
|
||||||
let current_field = state.current_field();
|
|
||||||
let new_field = current_field.saturating_sub(1);
|
|
||||||
state.set_current_field(new_field);
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let max_pos = current_input.len();
|
|
||||||
state.set_current_cursor_pos(
|
|
||||||
(*ideal_cursor_column).min(max_pos),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok("".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
"move_down" => {
|
|
||||||
let num_fields = state.fields().len();
|
|
||||||
if num_fields > 0 {
|
|
||||||
let new_field = (state.current_field() + 1).min(num_fields - 1);
|
|
||||||
state.set_current_field(new_field);
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let max_pos = current_input.len();
|
|
||||||
state.set_current_cursor_pos(
|
|
||||||
(*ideal_cursor_column).min(max_pos),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok("".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();
|
|
||||||
let new_pos = current_input.len();
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
*ideal_cursor_column = new_pos;
|
|
||||||
Ok("".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
"move_first_line" => {
|
|
||||||
let num_fields = state.fields().len();
|
|
||||||
if num_fields > 0 {
|
|
||||||
state.set_current_field(0);
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let max_pos = current_input.len();
|
|
||||||
state.set_current_cursor_pos(
|
|
||||||
(*ideal_cursor_column).min(max_pos),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok("Moved to first field".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
"move_last_line" => {
|
|
||||||
let num_fields = state.fields().len();
|
|
||||||
if num_fields > 0 {
|
|
||||||
let new_field = num_fields - 1;
|
|
||||||
state.set_current_field(new_field);
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let max_pos = current_input.len();
|
|
||||||
state.set_current_cursor_pos(
|
|
||||||
(*ideal_cursor_column).min(max_pos),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok("Moved to last field".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());
|
|
||||||
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);
|
|
||||||
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
|
|
||||||
"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())
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => Ok(format!("Unknown or unhandled edit action: {}", action)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
|
||||||
enum CharType {
|
|
||||||
Whitespace,
|
|
||||||
Alphanumeric,
|
|
||||||
Punctuation,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<char> = text.chars().collect();
|
|
||||||
let len = chars.len();
|
|
||||||
if len == 0 || current_pos >= len {
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut pos = current_pos;
|
|
||||||
let initial_type = get_char_type(chars[pos]);
|
|
||||||
|
|
||||||
while pos < len && get_char_type(chars[pos]) == initial_type {
|
|
||||||
pos += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while pos < len && get_char_type(chars[pos]) == CharType::Whitespace {
|
|
||||||
pos += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if get_char_type(chars[pos]) == CharType::Whitespace {
|
|
||||||
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<char> = 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[pos]) == 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_prev_word_end(text: &str, current_pos: usize) -> usize {
|
|
||||||
let chars: Vec<char> = text.chars().collect();
|
|
||||||
let len = chars.len();
|
|
||||||
if len == 0 || 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[pos]) == CharType::Whitespace {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if pos == 0 && get_char_type(chars[pos]) != 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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user