441 lines
22 KiB
Rust
441 lines
22 KiB
Rust
// src/functions/modes/navigation/add_logic_nav.rs
|
|
use crate::config::binds::config::{Config, EditorKeybindingMode};
|
|
use crate::state::{
|
|
app::state::AppState,
|
|
pages::add_logic::{AddLogicFocus, AddLogicState},
|
|
app::buffer::AppView,
|
|
app::buffer::BufferState,
|
|
};
|
|
use crossterm::event::{KeyEvent, KeyCode, KeyModifiers};
|
|
use crate::services::GrpcClient;
|
|
use tokio::sync::mpsc;
|
|
use anyhow::Result;
|
|
use crate::components::common::text_editor::TextEditor;
|
|
use crate::services::ui_service::UiService;
|
|
use tui_textarea::CursorMove; // Ensure this import is present
|
|
|
|
pub type SaveLogicResultSender = mpsc::Sender<Result<String>>;
|
|
|
|
pub fn handle_add_logic_navigation(
|
|
key_event: KeyEvent,
|
|
config: &Config,
|
|
app_state: &mut AppState,
|
|
add_logic_state: &mut AddLogicState,
|
|
is_edit_mode: &mut bool,
|
|
buffer_state: &mut BufferState,
|
|
grpc_client: GrpcClient,
|
|
_save_logic_sender: SaveLogicResultSender, // Marked as unused
|
|
command_message: &mut String,
|
|
) -> bool {
|
|
// === FULLSCREEN SCRIPT EDITING - COMPLETE ISOLATION ===
|
|
if add_logic_state.current_focus == AddLogicFocus::InsideScriptContent {
|
|
// === AUTOCOMPLETE HANDLING ===
|
|
if add_logic_state.script_editor_autocomplete_active {
|
|
match key_event.code {
|
|
// ... (Char, Backspace, Tab, Down, Up cases remain the same) ...
|
|
KeyCode::Char(c) if c.is_alphanumeric() || c == '_' => {
|
|
add_logic_state.script_editor_filter_text.push(c);
|
|
add_logic_state.update_script_editor_suggestions();
|
|
{
|
|
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
|
|
TextEditor::handle_input(
|
|
&mut editor_borrow,
|
|
key_event,
|
|
&add_logic_state.editor_keybinding_mode,
|
|
&mut add_logic_state.vim_state,
|
|
);
|
|
}
|
|
*command_message = format!("Filtering: @{}", add_logic_state.script_editor_filter_text);
|
|
return true;
|
|
}
|
|
KeyCode::Backspace => {
|
|
if !add_logic_state.script_editor_filter_text.is_empty() {
|
|
add_logic_state.script_editor_filter_text.pop();
|
|
add_logic_state.update_script_editor_suggestions();
|
|
{
|
|
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
|
|
TextEditor::handle_input(
|
|
&mut editor_borrow,
|
|
key_event,
|
|
&add_logic_state.editor_keybinding_mode,
|
|
&mut add_logic_state.vim_state,
|
|
);
|
|
}
|
|
*command_message = if add_logic_state.script_editor_filter_text.is_empty() {
|
|
"Autocomplete: @".to_string()
|
|
} else {
|
|
format!("Filtering: @{}", add_logic_state.script_editor_filter_text)
|
|
};
|
|
} else {
|
|
let should_deactivate = if let Some((trigger_line, trigger_col)) = add_logic_state.script_editor_trigger_position {
|
|
let current_cursor = {
|
|
let editor_borrow = add_logic_state.script_content_editor.borrow();
|
|
editor_borrow.cursor()
|
|
};
|
|
current_cursor.0 == trigger_line && current_cursor.1 == trigger_col + 1
|
|
} else {
|
|
false
|
|
};
|
|
if should_deactivate {
|
|
add_logic_state.deactivate_script_editor_autocomplete();
|
|
*command_message = "Autocomplete cancelled".to_string();
|
|
}
|
|
{
|
|
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
|
|
TextEditor::handle_input(
|
|
&mut editor_borrow,
|
|
key_event,
|
|
&add_logic_state.editor_keybinding_mode,
|
|
&mut add_logic_state.vim_state,
|
|
);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
KeyCode::Tab | KeyCode::Down => {
|
|
if !add_logic_state.script_editor_suggestions.is_empty() {
|
|
let current = add_logic_state.script_editor_selected_suggestion_index.unwrap_or(0);
|
|
let next = (current + 1) % add_logic_state.script_editor_suggestions.len();
|
|
add_logic_state.script_editor_selected_suggestion_index = Some(next);
|
|
*command_message = format!("Selected: {}", add_logic_state.script_editor_suggestions[next]);
|
|
}
|
|
return true;
|
|
}
|
|
KeyCode::Up => {
|
|
if !add_logic_state.script_editor_suggestions.is_empty() {
|
|
let current = add_logic_state.script_editor_selected_suggestion_index.unwrap_or(0);
|
|
let prev = if current == 0 {
|
|
add_logic_state.script_editor_suggestions.len() - 1
|
|
} else {
|
|
current - 1
|
|
};
|
|
add_logic_state.script_editor_selected_suggestion_index = Some(prev);
|
|
*command_message = format!("Selected: {}", add_logic_state.script_editor_suggestions[prev]);
|
|
}
|
|
return true;
|
|
}
|
|
KeyCode::Enter => {
|
|
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() {
|
|
let trigger_pos = add_logic_state.script_editor_trigger_position;
|
|
let filter_len = add_logic_state.script_editor_filter_text.len();
|
|
|
|
add_logic_state.deactivate_script_editor_autocomplete();
|
|
add_logic_state.has_unsaved_changes = true;
|
|
|
|
if let Some(pos) = trigger_pos {
|
|
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
|
|
|
|
if suggestion == "sql" {
|
|
replace_autocomplete_text(&mut editor_borrow, pos, filter_len, "sql");
|
|
editor_borrow.insert_str("('')");
|
|
// Move cursor back twice to be between the single quotes
|
|
editor_borrow.move_cursor(CursorMove::Back); // Before ')'
|
|
editor_borrow.move_cursor(CursorMove::Back); // Before ''' (inside '')
|
|
*command_message = "Inserted: @sql('')".to_string();
|
|
} else {
|
|
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());
|
|
|
|
let profile_name = add_logic_state.profile_name.clone();
|
|
let table_name_for_fetch = 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_for_fetch).await {
|
|
Ok(_columns) => {
|
|
// Result handled by main UI loop
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
add_logic_state.deactivate_script_editor_autocomplete();
|
|
{
|
|
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
|
|
TextEditor::handle_input(
|
|
&mut editor_borrow,
|
|
key_event,
|
|
&add_logic_state.editor_keybinding_mode,
|
|
&mut add_logic_state.vim_state,
|
|
);
|
|
}
|
|
return true;
|
|
}
|
|
KeyCode::Esc => {
|
|
add_logic_state.deactivate_script_editor_autocomplete();
|
|
*command_message = "Autocomplete cancelled".to_string();
|
|
}
|
|
_ => {
|
|
add_logic_state.deactivate_script_editor_autocomplete();
|
|
*command_message = "Autocomplete cancelled".to_string();
|
|
{
|
|
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
|
|
TextEditor::handle_input(
|
|
&mut editor_borrow,
|
|
key_event,
|
|
&add_logic_state.editor_keybinding_mode,
|
|
&mut add_logic_state.vim_state,
|
|
);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if key_event.code == KeyCode::Char('@') && key_event.modifiers == KeyModifiers::NONE {
|
|
let should_trigger = match add_logic_state.editor_keybinding_mode {
|
|
EditorKeybindingMode::Vim => *is_edit_mode,
|
|
_ => true,
|
|
};
|
|
if should_trigger {
|
|
let cursor_before = {
|
|
let editor_borrow = add_logic_state.script_content_editor.borrow();
|
|
editor_borrow.cursor()
|
|
};
|
|
{
|
|
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
|
|
TextEditor::handle_input(
|
|
&mut editor_borrow,
|
|
key_event,
|
|
&add_logic_state.editor_keybinding_mode,
|
|
&mut add_logic_state.vim_state,
|
|
);
|
|
}
|
|
add_logic_state.script_editor_trigger_position = Some(cursor_before);
|
|
add_logic_state.script_editor_autocomplete_active = true;
|
|
add_logic_state.script_editor_filter_text.clear();
|
|
add_logic_state.update_script_editor_suggestions();
|
|
add_logic_state.has_unsaved_changes = true;
|
|
*command_message = "Autocomplete: @ (Tab/↑↓ to navigate, Enter to select, Esc to cancel)".to_string();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if key_event.code == KeyCode::Esc && key_event.modifiers == KeyModifiers::NONE {
|
|
match add_logic_state.editor_keybinding_mode {
|
|
EditorKeybindingMode::Vim => {
|
|
if *is_edit_mode {
|
|
{
|
|
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
|
|
TextEditor::handle_input(
|
|
&mut editor_borrow,
|
|
key_event,
|
|
&add_logic_state.editor_keybinding_mode,
|
|
&mut add_logic_state.vim_state,
|
|
);
|
|
}
|
|
if TextEditor::is_vim_normal_mode(&add_logic_state.vim_state) {
|
|
*is_edit_mode = false;
|
|
*command_message = "VIM: Normal Mode. Esc again to exit script.".to_string();
|
|
}
|
|
} else {
|
|
add_logic_state.current_focus = AddLogicFocus::ScriptContentPreview;
|
|
app_state.ui.focus_outside_canvas = true;
|
|
*is_edit_mode = false;
|
|
*command_message = "Exited script editing.".to_string();
|
|
}
|
|
}
|
|
_ => {
|
|
if *is_edit_mode {
|
|
*is_edit_mode = false;
|
|
*command_message = "Exited script edit. Esc again to exit script.".to_string();
|
|
} else {
|
|
add_logic_state.current_focus = AddLogicFocus::ScriptContentPreview;
|
|
app_state.ui.focus_outside_canvas = true;
|
|
*is_edit_mode = false;
|
|
*command_message = "Exited script editing.".to_string();
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
let changed = {
|
|
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
|
|
TextEditor::handle_input(
|
|
&mut editor_borrow,
|
|
key_event,
|
|
&add_logic_state.editor_keybinding_mode,
|
|
&mut add_logic_state.vim_state,
|
|
)
|
|
};
|
|
if changed {
|
|
add_logic_state.has_unsaved_changes = true;
|
|
}
|
|
if add_logic_state.editor_keybinding_mode == EditorKeybindingMode::Vim {
|
|
*is_edit_mode = !TextEditor::is_vim_normal_mode(&add_logic_state.vim_state);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
let action = config.get_general_action(key_event.code, key_event.modifiers);
|
|
let current_focus = add_logic_state.current_focus;
|
|
let mut handled = true;
|
|
let mut new_focus = current_focus;
|
|
|
|
match action.as_deref() {
|
|
Some("exit_table_scroll") => {
|
|
handled = false;
|
|
}
|
|
Some("move_up") => {
|
|
match current_focus {
|
|
AddLogicFocus::InputLogicName => {}
|
|
AddLogicFocus::InputTargetColumn => new_focus = AddLogicFocus::InputLogicName,
|
|
AddLogicFocus::InputDescription => new_focus = AddLogicFocus::InputTargetColumn,
|
|
AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::InputDescription,
|
|
AddLogicFocus::SaveButton => new_focus = AddLogicFocus::ScriptContentPreview,
|
|
AddLogicFocus::CancelButton => new_focus = AddLogicFocus::SaveButton,
|
|
_ => handled = false,
|
|
}
|
|
}
|
|
Some("move_down") => {
|
|
match current_focus {
|
|
AddLogicFocus::InputLogicName => new_focus = AddLogicFocus::InputTargetColumn,
|
|
AddLogicFocus::InputTargetColumn => new_focus = AddLogicFocus::InputDescription,
|
|
AddLogicFocus::InputDescription => {
|
|
add_logic_state.last_canvas_field = 2;
|
|
new_focus = AddLogicFocus::ScriptContentPreview;
|
|
},
|
|
AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::SaveButton,
|
|
AddLogicFocus::SaveButton => new_focus = AddLogicFocus::CancelButton,
|
|
AddLogicFocus::CancelButton => {}
|
|
_ => handled = false,
|
|
}
|
|
}
|
|
Some("next_option") => {
|
|
match current_focus {
|
|
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription =>
|
|
{ new_focus = AddLogicFocus::ScriptContentPreview; }
|
|
AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::SaveButton,
|
|
AddLogicFocus::SaveButton => new_focus = AddLogicFocus::CancelButton,
|
|
AddLogicFocus::CancelButton => { }
|
|
_ => handled = false,
|
|
}
|
|
}
|
|
Some("previous_option") => {
|
|
match current_focus {
|
|
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription =>
|
|
{ }
|
|
AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::InputDescription,
|
|
AddLogicFocus::SaveButton => new_focus = AddLogicFocus::ScriptContentPreview,
|
|
AddLogicFocus::CancelButton => new_focus = AddLogicFocus::SaveButton,
|
|
_ => handled = false,
|
|
}
|
|
}
|
|
Some("next_field") => {
|
|
new_focus = match current_focus {
|
|
AddLogicFocus::InputLogicName => AddLogicFocus::InputTargetColumn,
|
|
AddLogicFocus::InputTargetColumn => AddLogicFocus::InputDescription,
|
|
AddLogicFocus::InputDescription => AddLogicFocus::ScriptContentPreview,
|
|
AddLogicFocus::ScriptContentPreview => AddLogicFocus::SaveButton,
|
|
AddLogicFocus::SaveButton => AddLogicFocus::CancelButton,
|
|
AddLogicFocus::CancelButton => AddLogicFocus::InputLogicName,
|
|
_ => current_focus,
|
|
};
|
|
}
|
|
Some("prev_field") => {
|
|
new_focus = match current_focus {
|
|
AddLogicFocus::InputLogicName => AddLogicFocus::CancelButton,
|
|
AddLogicFocus::InputTargetColumn => AddLogicFocus::InputLogicName,
|
|
AddLogicFocus::InputDescription => AddLogicFocus::InputTargetColumn,
|
|
AddLogicFocus::ScriptContentPreview => AddLogicFocus::InputDescription,
|
|
AddLogicFocus::SaveButton => AddLogicFocus::ScriptContentPreview,
|
|
AddLogicFocus::CancelButton => AddLogicFocus::SaveButton,
|
|
_ => current_focus,
|
|
};
|
|
}
|
|
Some("select") => {
|
|
match current_focus {
|
|
AddLogicFocus::ScriptContentPreview => {
|
|
new_focus = AddLogicFocus::InsideScriptContent;
|
|
*is_edit_mode = false;
|
|
app_state.ui.focus_outside_canvas = false;
|
|
let mode_hint = match add_logic_state.editor_keybinding_mode {
|
|
EditorKeybindingMode::Vim => "VIM mode - 'i'/'a'/'o' to edit",
|
|
_ => "Enter/Ctrl+E to edit",
|
|
};
|
|
*command_message = format!("Fullscreen script editing. {} or Esc to exit.", mode_hint);
|
|
}
|
|
AddLogicFocus::SaveButton => {
|
|
*command_message = "Save logic action".to_string();
|
|
}
|
|
AddLogicFocus::CancelButton => {
|
|
buffer_state.update_history(AppView::Admin);
|
|
app_state.ui.show_add_logic = false;
|
|
*command_message = "Cancelled Add Logic".to_string();
|
|
*is_edit_mode = false;
|
|
}
|
|
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription => {
|
|
*is_edit_mode = !*is_edit_mode;
|
|
*command_message = format!("Field edit mode: {}", if *is_edit_mode { "ON" } else { "OFF" });
|
|
}
|
|
_ => handled = false,
|
|
}
|
|
}
|
|
Some("toggle_edit_mode") => {
|
|
match current_focus {
|
|
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription => {
|
|
*is_edit_mode = !*is_edit_mode;
|
|
*command_message = format!("Canvas field edit mode: {}", if *is_edit_mode { "ON" } else { "OFF" });
|
|
}
|
|
_ => {
|
|
*command_message = "Cannot toggle edit mode here.".to_string();
|
|
}
|
|
}
|
|
}
|
|
_ => handled = false,
|
|
}
|
|
|
|
if handled && current_focus != new_focus {
|
|
add_logic_state.current_focus = new_focus;
|
|
let new_is_canvas_input_focus = matches!(new_focus,
|
|
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription
|
|
);
|
|
if new_is_canvas_input_focus {
|
|
*is_edit_mode = false;
|
|
app_state.ui.focus_outside_canvas = false;
|
|
} else {
|
|
app_state.ui.focus_outside_canvas = true;
|
|
if matches!(new_focus, AddLogicFocus::ScriptContentPreview) {
|
|
*is_edit_mode = false;
|
|
}
|
|
}
|
|
}
|
|
handled
|
|
}
|
|
|
|
fn replace_autocomplete_text(
|
|
editor: &mut tui_textarea::TextArea,
|
|
trigger_pos: (usize, usize),
|
|
filter_len: usize,
|
|
replacement: &str,
|
|
) {
|
|
// use tui_textarea::CursorMove; // Already imported at the top of the module
|
|
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));
|
|
for _ in 0..filter_len {
|
|
editor.delete_next_char();
|
|
}
|
|
editor.insert_str(replacement);
|
|
}
|