better autocomplete

This commit is contained in:
filipriec
2025-05-26 20:43:58 +02:00
parent 43b064673b
commit b770240f0d
2 changed files with 84 additions and 152 deletions

View File

@@ -12,6 +12,7 @@ use tokio::sync::mpsc;
use anyhow::Result; use anyhow::Result;
use crate::components::common::text_editor::TextEditor; use crate::components::common::text_editor::TextEditor;
use crate::services::ui_service::UiService; use crate::services::ui_service::UiService;
use tui_textarea::CursorMove;
pub type SaveLogicResultSender = mpsc::Sender<Result<String>>; pub type SaveLogicResultSender = mpsc::Sender<Result<String>>;
@@ -23,21 +24,18 @@ pub fn handle_add_logic_navigation(
is_edit_mode: &mut bool, is_edit_mode: &mut bool,
buffer_state: &mut BufferState, buffer_state: &mut BufferState,
grpc_client: GrpcClient, grpc_client: GrpcClient,
save_logic_sender: SaveLogicResultSender, _save_logic_sender: SaveLogicResultSender, // Marked as unused
command_message: &mut String, command_message: &mut String,
) -> bool { ) -> bool {
// === FULLSCREEN SCRIPT EDITING - COMPLETE ISOLATION === // === FULLSCREEN SCRIPT EDITING - COMPLETE ISOLATION ===
if add_logic_state.current_focus == AddLogicFocus::InsideScriptContent { if add_logic_state.current_focus == AddLogicFocus::InsideScriptContent {
// === AUTOCOMPLETE HANDLING === // === AUTOCOMPLETE HANDLING ===
if add_logic_state.script_editor_autocomplete_active { if add_logic_state.script_editor_autocomplete_active {
match key_event.code { match key_event.code {
// ... (Char, Backspace, Tab, Down, Up cases remain the same) ...
KeyCode::Char(c) if c.is_alphanumeric() || c == '_' => { KeyCode::Char(c) if c.is_alphanumeric() || c == '_' => {
// Update filter text first
add_logic_state.script_editor_filter_text.push(c); add_logic_state.script_editor_filter_text.push(c);
add_logic_state.update_script_editor_suggestions(); add_logic_state.update_script_editor_suggestions();
// Then handle editor input
{ {
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
TextEditor::handle_input( TextEditor::handle_input(
@@ -46,18 +44,14 @@ pub fn handle_add_logic_navigation(
&add_logic_state.editor_keybinding_mode, &add_logic_state.editor_keybinding_mode,
&mut add_logic_state.vim_state, &mut add_logic_state.vim_state,
); );
} // Drop editor borrow }
*command_message = format!("Filtering: @{}", add_logic_state.script_editor_filter_text); *command_message = format!("Filtering: @{}", add_logic_state.script_editor_filter_text);
return true; return true;
} }
KeyCode::Backspace => { KeyCode::Backspace => {
if !add_logic_state.script_editor_filter_text.is_empty() { if !add_logic_state.script_editor_filter_text.is_empty() {
// Remove last character from filter
add_logic_state.script_editor_filter_text.pop(); add_logic_state.script_editor_filter_text.pop();
add_logic_state.update_script_editor_suggestions(); add_logic_state.update_script_editor_suggestions();
// Then handle editor input
{ {
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
TextEditor::handle_input( TextEditor::handle_input(
@@ -66,15 +60,13 @@ pub fn handle_add_logic_navigation(
&add_logic_state.editor_keybinding_mode, &add_logic_state.editor_keybinding_mode,
&mut add_logic_state.vim_state, &mut add_logic_state.vim_state,
); );
} // Drop editor borrow }
*command_message = if add_logic_state.script_editor_filter_text.is_empty() { *command_message = if add_logic_state.script_editor_filter_text.is_empty() {
"Autocomplete: @".to_string() "Autocomplete: @".to_string()
} else { } else {
format!("Filtering: @{}", add_logic_state.script_editor_filter_text) format!("Filtering: @{}", add_logic_state.script_editor_filter_text)
}; };
} else { } else {
// Check if we're deleting the @ trigger
let should_deactivate = if let Some((trigger_line, trigger_col)) = add_logic_state.script_editor_trigger_position { let should_deactivate = if let Some((trigger_line, trigger_col)) = add_logic_state.script_editor_trigger_position {
let current_cursor = { let current_cursor = {
let editor_borrow = add_logic_state.script_content_editor.borrow(); let editor_borrow = add_logic_state.script_content_editor.borrow();
@@ -84,13 +76,10 @@ pub fn handle_add_logic_navigation(
} else { } else {
false false
}; };
if should_deactivate { if should_deactivate {
add_logic_state.deactivate_script_editor_autocomplete(); add_logic_state.deactivate_script_editor_autocomplete();
*command_message = "Autocomplete cancelled".to_string(); *command_message = "Autocomplete cancelled".to_string();
} }
// Handle editor input
{ {
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
TextEditor::handle_input( TextEditor::handle_input(
@@ -99,22 +88,20 @@ pub fn handle_add_logic_navigation(
&add_logic_state.editor_keybinding_mode, &add_logic_state.editor_keybinding_mode,
&mut add_logic_state.vim_state, &mut add_logic_state.vim_state,
); );
} // Drop editor borrow }
} }
return true; return true;
} }
KeyCode::Tab | KeyCode::Down => { KeyCode::Tab | KeyCode::Down => {
// Navigate suggestions down
if !add_logic_state.script_editor_suggestions.is_empty() { if !add_logic_state.script_editor_suggestions.is_empty() {
let current = add_logic_state.script_editor_selected_suggestion_index.unwrap_or(0); let current = add_logic_state.script_editor_selected_suggestion_index.unwrap_or(0);
let next = (current + 1) % add_logic_state.script_editor_suggestions.len(); let next = (current + 1) % add_logic_state.script_editor_suggestions.len();
add_logic_state.script_editor_selected_suggestion_index = Some(next); add_logic_state.script_editor_selected_suggestion_index = Some(next);
*command_message = format!("Selected: {}", add_logic_state.script_editor_suggestions[next]); *command_message = format!("Selected: {}", add_logic_state.script_editor_suggestions[next]);
} }
return true; // Consume the key return true;
} }
KeyCode::Up => { KeyCode::Up => {
// Navigate suggestions up
if !add_logic_state.script_editor_suggestions.is_empty() { if !add_logic_state.script_editor_suggestions.is_empty() {
let current = add_logic_state.script_editor_selected_suggestion_index.unwrap_or(0); let current = add_logic_state.script_editor_selected_suggestion_index.unwrap_or(0);
let prev = if current == 0 { let prev = if current == 0 {
@@ -125,74 +112,63 @@ pub fn handle_add_logic_navigation(
add_logic_state.script_editor_selected_suggestion_index = Some(prev); add_logic_state.script_editor_selected_suggestion_index = Some(prev);
*command_message = format!("Selected: {}", add_logic_state.script_editor_suggestions[prev]); *command_message = format!("Selected: {}", add_logic_state.script_editor_suggestions[prev]);
} }
return true; // Consume the key return true;
} }
KeyCode::Enter => { KeyCode::Enter => {
// Select current suggestion
if let Some(selected_idx) = add_logic_state.script_editor_selected_suggestion_index { if let Some(selected_idx) = add_logic_state.script_editor_selected_suggestion_index {
if let Some(suggestion) = add_logic_state.script_editor_suggestions.get(selected_idx).cloned() { if let Some(suggestion) = add_logic_state.script_editor_suggestions.get(selected_idx).cloned() {
// Get trigger position and filter length
let trigger_pos = add_logic_state.script_editor_trigger_position; let trigger_pos = add_logic_state.script_editor_trigger_position;
let filter_len = add_logic_state.script_editor_filter_text.len(); let filter_len = add_logic_state.script_editor_filter_text.len();
// Check if this is a table name selection
let is_table_selection = add_logic_state.is_table_name_suggestion(&suggestion);
// Deactivate current autocomplete first
add_logic_state.deactivate_script_editor_autocomplete(); add_logic_state.deactivate_script_editor_autocomplete();
add_logic_state.has_unsaved_changes = true; add_logic_state.has_unsaved_changes = true;
// Replace text in editor
if let Some(pos) = trigger_pos { if let Some(pos) = trigger_pos {
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
replace_autocomplete_text(
&mut editor_borrow, if suggestion == "sql" {
pos, replace_autocomplete_text(&mut editor_borrow, pos, filter_len, "sql");
filter_len, editor_borrow.insert_str("('')");
&suggestion, editor_borrow.move_cursor(CursorMove::Back); // Move cursor between ''
); *command_message = "Inserted: @sql('')".to_string();
// If it's a table selection, append "." and trigger column autocomplete
if is_table_selection {
editor_borrow.insert_str(".");
// Get the new cursor position (after table name and dot)
let new_cursor = editor_borrow.cursor();
drop(editor_borrow); // Release the borrow
// Set up for column autocomplete
add_logic_state.script_editor_trigger_position = Some(new_cursor);
add_logic_state.script_editor_autocomplete_active = true;
add_logic_state.script_editor_filter_text.clear();
add_logic_state.trigger_column_autocomplete_for_table(suggestion.clone());
// Initiate async column fetch
let profile_name = add_logic_state.profile_name.clone();
let table_name = suggestion.clone();
let mut client_clone = grpc_client.clone();
tokio::spawn(async move {
match UiService::fetch_columns_for_table(&mut client_clone, &profile_name, &table_name).await {
Ok(columns) => {
// Note: In a real implementation, you'd need to send this back to the main thread
// For now, we'll handle this synchronously in the main thread
}
Err(e) => {
tracing::error!("Failed to fetch columns for {}.{}: {}", profile_name, table_name, e);
}
}
});
*command_message = format!("Selected table '{}', fetching columns...", suggestion);
} else { } else {
*command_message = format!("Inserted: {}", suggestion); let is_table_selection = add_logic_state.is_table_name_suggestion(&suggestion);
replace_autocomplete_text(&mut editor_borrow, pos, filter_len, &suggestion);
if is_table_selection {
editor_borrow.insert_str(".");
let new_cursor = editor_borrow.cursor();
drop(editor_borrow); // Release borrow before calling add_logic_state methods
add_logic_state.script_editor_trigger_position = Some(new_cursor);
add_logic_state.script_editor_autocomplete_active = true;
add_logic_state.script_editor_filter_text.clear();
add_logic_state.trigger_column_autocomplete_for_table(suggestion.clone());
// Asynchronous column fetch (remains the same)
let profile_name = add_logic_state.profile_name.clone();
let table_name_for_fetch = suggestion.clone(); // Use a new variable for clarity
let mut client_clone = grpc_client.clone();
tokio::spawn(async move {
match UiService::fetch_columns_for_table(&mut client_clone, &profile_name, &table_name_for_fetch).await {
Ok(_columns) => {
// Result handled by main UI loop checking script_editor_awaiting_column_autocomplete
}
Err(e) => {
tracing::error!("Failed to fetch columns for {}.{}: {}", profile_name, table_name_for_fetch, e);
}
}
});
*command_message = format!("Selected table '{}', fetching columns...", suggestion);
} else {
*command_message = format!("Inserted: {}", suggestion);
}
} }
} }
return true; // Consume the key return true;
} }
} }
// Fallthrough: No suggestion selected, pass Enter to editor
// If no suggestion selected, pass Enter to editor
add_logic_state.deactivate_script_editor_autocomplete(); add_logic_state.deactivate_script_editor_autocomplete();
{ {
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
@@ -205,20 +181,15 @@ pub fn handle_add_logic_navigation(
} }
return true; return true;
} }
// ... (Esc and _ cases remain the same) ...
KeyCode::Esc => { KeyCode::Esc => {
// Cancel autocomplete first
add_logic_state.deactivate_script_editor_autocomplete(); add_logic_state.deactivate_script_editor_autocomplete();
*command_message = "Autocomplete cancelled".to_string(); *command_message = "Autocomplete cancelled".to_string();
// Fall through to normal Esc handling
// Then handle normal Esc behavior (vim mode, exit script, etc.)
// Fall through to normal Esc handling below
} }
_ => { _ => {
// Other keys deactivate autocomplete and pass through
add_logic_state.deactivate_script_editor_autocomplete(); add_logic_state.deactivate_script_editor_autocomplete();
*command_message = "Autocomplete cancelled".to_string(); *command_message = "Autocomplete cancelled".to_string();
// Pass key to editor
{ {
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
TextEditor::handle_input( TextEditor::handle_input(
@@ -233,22 +204,17 @@ pub fn handle_add_logic_navigation(
} }
} }
// === AUTOCOMPLETE TRIGGER === // ... (AUTOCOMPLETE TRIGGER, Escape to exit fullscreen, ALL OTHER KEYS remain the same) ...
if key_event.code == KeyCode::Char('@') && key_event.modifiers == KeyModifiers::NONE { if key_event.code == KeyCode::Char('@') && key_event.modifiers == KeyModifiers::NONE {
// Only trigger in insert mode for Vim, or always for other modes
let should_trigger = match add_logic_state.editor_keybinding_mode { let should_trigger = match add_logic_state.editor_keybinding_mode {
EditorKeybindingMode::Vim => *is_edit_mode, // Only in Vim insert mode EditorKeybindingMode::Vim => *is_edit_mode,
_ => true, // Always for non-Vim modes when editing _ => true,
}; };
if should_trigger { if should_trigger {
// Get cursor position before inserting @
let cursor_before = { let cursor_before = {
let editor_borrow = add_logic_state.script_content_editor.borrow(); let editor_borrow = add_logic_state.script_content_editor.borrow();
editor_borrow.cursor() editor_borrow.cursor()
}; };
// Handle editor input first
{ {
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
TextEditor::handle_input( TextEditor::handle_input(
@@ -257,26 +223,21 @@ pub fn handle_add_logic_navigation(
&add_logic_state.editor_keybinding_mode, &add_logic_state.editor_keybinding_mode,
&mut add_logic_state.vim_state, &mut add_logic_state.vim_state,
); );
} // Drop editor borrow }
// Activate autocomplete at the @ position
add_logic_state.script_editor_trigger_position = Some(cursor_before); add_logic_state.script_editor_trigger_position = Some(cursor_before);
add_logic_state.script_editor_autocomplete_active = true; add_logic_state.script_editor_autocomplete_active = true;
add_logic_state.script_editor_filter_text.clear(); add_logic_state.script_editor_filter_text.clear();
add_logic_state.update_script_editor_suggestions(); add_logic_state.update_script_editor_suggestions(); // This will now use the new logic
add_logic_state.has_unsaved_changes = true; add_logic_state.has_unsaved_changes = true;
*command_message = "Autocomplete: @ (Tab/↑↓ to navigate, Enter to select, Esc to cancel)".to_string(); *command_message = "Autocomplete: @ (Tab/↑↓ to navigate, Enter to select, Esc to cancel)".to_string();
return true; return true;
} }
} }
// Handle ONLY Escape to exit fullscreen mode
if key_event.code == KeyCode::Esc && key_event.modifiers == KeyModifiers::NONE { if key_event.code == KeyCode::Esc && key_event.modifiers == KeyModifiers::NONE {
match add_logic_state.editor_keybinding_mode { match add_logic_state.editor_keybinding_mode {
EditorKeybindingMode::Vim => { EditorKeybindingMode::Vim => {
if *is_edit_mode { if *is_edit_mode {
// First escape: try to go to Vim Normal mode
{ {
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
TextEditor::handle_input( TextEditor::handle_input(
@@ -291,7 +252,6 @@ pub fn handle_add_logic_navigation(
*command_message = "VIM: Normal Mode. Esc again to exit script.".to_string(); *command_message = "VIM: Normal Mode. Esc again to exit script.".to_string();
} }
} else { } else {
// Second escape: exit fullscreen
add_logic_state.current_focus = AddLogicFocus::ScriptContentPreview; add_logic_state.current_focus = AddLogicFocus::ScriptContentPreview;
app_state.ui.focus_outside_canvas = true; app_state.ui.focus_outside_canvas = true;
*is_edit_mode = false; *is_edit_mode = false;
@@ -303,7 +263,6 @@ pub fn handle_add_logic_navigation(
*is_edit_mode = false; *is_edit_mode = false;
*command_message = "Exited script edit. Esc again to exit script.".to_string(); *command_message = "Exited script edit. Esc again to exit script.".to_string();
} else { } else {
// Exit fullscreen
add_logic_state.current_focus = AddLogicFocus::ScriptContentPreview; add_logic_state.current_focus = AddLogicFocus::ScriptContentPreview;
app_state.ui.focus_outside_canvas = true; app_state.ui.focus_outside_canvas = true;
*is_edit_mode = false; *is_edit_mode = false;
@@ -314,7 +273,6 @@ pub fn handle_add_logic_navigation(
return true; return true;
} }
// ALL OTHER KEYS: Pass directly to textarea without any interference
let changed = { let changed = {
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
TextEditor::handle_input( TextEditor::handle_input(
@@ -327,17 +285,14 @@ pub fn handle_add_logic_navigation(
if changed { if changed {
add_logic_state.has_unsaved_changes = true; add_logic_state.has_unsaved_changes = true;
} }
// Update edit mode status for Vim
if add_logic_state.editor_keybinding_mode == EditorKeybindingMode::Vim { if add_logic_state.editor_keybinding_mode == EditorKeybindingMode::Vim {
*is_edit_mode = !TextEditor::is_vim_normal_mode(&add_logic_state.vim_state); *is_edit_mode = !TextEditor::is_vim_normal_mode(&add_logic_state.vim_state);
} }
return true;
return true; // Always consume the event in fullscreen mode
} }
// === END FULLSCREEN ISOLATION === // === END FULLSCREEN ISOLATION ===
// Regular navigation logic for non-fullscreen elements // ... (Regular navigation logic for non-fullscreen elements remains the same) ...
let action = config.get_general_action(key_event.code, key_event.modifiers); let action = config.get_general_action(key_event.code, key_event.modifiers);
let current_focus = add_logic_state.current_focus; let current_focus = add_logic_state.current_focus;
let mut handled = true; let mut handled = true;
@@ -345,14 +300,11 @@ pub fn handle_add_logic_navigation(
match action.as_deref() { match action.as_deref() {
Some("exit_table_scroll") => { Some("exit_table_scroll") => {
// This shouldn't happen since we handle InsideScriptContent above
handled = false; handled = false;
} }
Some("move_up") => { Some("move_up") => {
match current_focus { match current_focus {
AddLogicFocus::InputLogicName => { AddLogicFocus::InputLogicName => {}
// Stay at top
}
AddLogicFocus::InputTargetColumn => new_focus = AddLogicFocus::InputLogicName, AddLogicFocus::InputTargetColumn => new_focus = AddLogicFocus::InputLogicName,
AddLogicFocus::InputDescription => new_focus = AddLogicFocus::InputTargetColumn, AddLogicFocus::InputDescription => new_focus = AddLogicFocus::InputTargetColumn,
AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::InputDescription, AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::InputDescription,
@@ -371,9 +323,7 @@ pub fn handle_add_logic_navigation(
}, },
AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::SaveButton, AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::SaveButton,
AddLogicFocus::SaveButton => new_focus = AddLogicFocus::CancelButton, AddLogicFocus::SaveButton => new_focus = AddLogicFocus::CancelButton,
AddLogicFocus::CancelButton => { AddLogicFocus::CancelButton => {}
// Stay at bottom
}
_ => handled = false, _ => handled = false,
} }
} }
@@ -383,14 +333,14 @@ pub fn handle_add_logic_navigation(
{ new_focus = AddLogicFocus::ScriptContentPreview; } { new_focus = AddLogicFocus::ScriptContentPreview; }
AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::SaveButton, AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::SaveButton,
AddLogicFocus::SaveButton => new_focus = AddLogicFocus::CancelButton, AddLogicFocus::SaveButton => new_focus = AddLogicFocus::CancelButton,
AddLogicFocus::CancelButton => { /* Stay at last */ } AddLogicFocus::CancelButton => { }
_ => handled = false, _ => handled = false,
} }
} }
Some("previous_option") => { Some("previous_option") => {
match current_focus { match current_focus {
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription => AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription =>
{ /* Stay at first */ } { }
AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::InputDescription, AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::InputDescription,
AddLogicFocus::SaveButton => new_focus = AddLogicFocus::ScriptContentPreview, AddLogicFocus::SaveButton => new_focus = AddLogicFocus::ScriptContentPreview,
AddLogicFocus::CancelButton => new_focus = AddLogicFocus::SaveButton, AddLogicFocus::CancelButton => new_focus = AddLogicFocus::SaveButton,
@@ -423,8 +373,8 @@ pub fn handle_add_logic_navigation(
match current_focus { match current_focus {
AddLogicFocus::ScriptContentPreview => { AddLogicFocus::ScriptContentPreview => {
new_focus = AddLogicFocus::InsideScriptContent; new_focus = AddLogicFocus::InsideScriptContent;
*is_edit_mode = false; // Start in preview mode *is_edit_mode = false;
app_state.ui.focus_outside_canvas = false; // Script is like canvas app_state.ui.focus_outside_canvas = false;
let mode_hint = match add_logic_state.editor_keybinding_mode { let mode_hint = match add_logic_state.editor_keybinding_mode {
EditorKeybindingMode::Vim => "VIM mode - 'i'/'a'/'o' to edit", EditorKeybindingMode::Vim => "VIM mode - 'i'/'a'/'o' to edit",
_ => "Enter/Ctrl+E to edit", _ => "Enter/Ctrl+E to edit",
@@ -463,29 +413,23 @@ pub fn handle_add_logic_navigation(
if handled && current_focus != new_focus { if handled && current_focus != new_focus {
add_logic_state.current_focus = new_focus; add_logic_state.current_focus = new_focus;
// Set edit mode and canvas focus based on new focus
let new_is_canvas_input_focus = matches!(new_focus, let new_is_canvas_input_focus = matches!(new_focus,
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription
); );
if new_is_canvas_input_focus { if new_is_canvas_input_focus {
// Entering canvas - start in readonly mode
*is_edit_mode = false; *is_edit_mode = false;
app_state.ui.focus_outside_canvas = false; app_state.ui.focus_outside_canvas = false;
} else { } else {
// Outside canvas
app_state.ui.focus_outside_canvas = true; app_state.ui.focus_outside_canvas = true;
if matches!(new_focus, AddLogicFocus::ScriptContentPreview) { if matches!(new_focus, AddLogicFocus::ScriptContentPreview) {
*is_edit_mode = false; *is_edit_mode = false;
} }
} }
} }
handled handled
} }
// Helper function for text replacement // ... (replace_autocomplete_text helper function remains the same) ...
fn replace_autocomplete_text( fn replace_autocomplete_text(
editor: &mut tui_textarea::TextArea, editor: &mut tui_textarea::TextArea,
trigger_pos: (usize, usize), trigger_pos: (usize, usize),
@@ -493,16 +437,10 @@ fn replace_autocomplete_text(
replacement: &str, replacement: &str,
) { ) {
use tui_textarea::CursorMove; use tui_textarea::CursorMove;
// Move cursor to the position right after the @ symbol (where filter text starts)
let filter_start_pos = (trigger_pos.0, trigger_pos.1 + 1); let filter_start_pos = (trigger_pos.0, trigger_pos.1 + 1);
editor.move_cursor(CursorMove::Jump(filter_start_pos.0 as u16, filter_start_pos.1 as u16)); editor.move_cursor(CursorMove::Jump(filter_start_pos.0 as u16, filter_start_pos.1 as u16));
// Delete only the filter text (not the @ symbol)
for _ in 0..filter_len { for _ in 0..filter_len {
editor.delete_next_char(); editor.delete_next_char();
} }
// Insert replacement text (this will be appended to the @ symbol)
editor.insert_str(replacement); editor.insert_str(replacement);
} }

View File

@@ -135,16 +135,17 @@ impl AddLogicState {
pub fn update_script_editor_suggestions(&mut self) { pub fn update_script_editor_suggestions(&mut self) {
let mut suggestions = vec!["sql".to_string()]; let mut suggestions = vec!["sql".to_string()];
// Add actual table name if available (current table) if self.selected_table_name.is_some() {
if let Some(ref table_name) = self.selected_table_name { suggestions.extend(self.table_columns_for_suggestions.clone());
suggestions.push(table_name.clone());
} }
// Add column names from the current table let current_selected_table_name = self.selected_table_name.as_deref();
suggestions.extend(self.table_columns_for_suggestions.clone()); suggestions.extend(
self.same_profile_table_names
// Add table names from SAME profile only .iter()
suggestions.extend(self.same_profile_table_names.clone()); .filter(|tn| Some(tn.as_str()) != current_selected_table_name)
.cloned()
);
if self.script_editor_filter_text.is_empty() { if self.script_editor_filter_text.is_empty() {
self.script_editor_suggestions = suggestions; self.script_editor_suggestions = suggestions;
@@ -169,6 +170,18 @@ impl AddLogicState {
} }
} }
/// Checks if a suggestion is a table name (for triggering column autocomplete)
pub fn is_table_name_suggestion(&self, suggestion: &str) -> bool {
// Not "sql"
if suggestion == "sql" {
return false;
}
if self.table_columns_for_suggestions.contains(&suggestion.to_string()) {
return false;
}
self.same_profile_table_names.contains(&suggestion.to_string())
}
/// Sets table columns for autocomplete suggestions /// Sets table columns for autocomplete suggestions
pub fn set_table_columns(&mut self, columns: Vec<String>) { pub fn set_table_columns(&mut self, columns: Vec<String>) {
self.table_columns_for_suggestions = columns.clone(); self.table_columns_for_suggestions = columns.clone();
@@ -187,25 +200,6 @@ impl AddLogicState {
self.same_profile_table_names = table_names; self.same_profile_table_names = table_names;
} }
/// Checks if a suggestion is a table name (for triggering column autocomplete)
pub fn is_table_name_suggestion(&self, suggestion: &str) -> bool {
if suggestion == "sql" {
return false;
}
if let Some(ref current_table) = self.selected_table_name {
if suggestion == current_table {
return false;
}
}
if self.table_columns_for_suggestions.contains(&suggestion.to_string()) {
return false;
}
self.same_profile_table_names.contains(&suggestion.to_string())
}
/// Triggers waiting for column autocomplete for a specific table /// Triggers waiting for column autocomplete for a specific table
pub fn trigger_column_autocomplete_for_table(&mut self, table_name: String) { pub fn trigger_column_autocomplete_for_table(&mut self, table_name: String) {
self.script_editor_awaiting_column_autocomplete = Some(table_name); self.script_editor_awaiting_column_autocomplete = Some(table_name);