compiled still not working

This commit is contained in:
Priec
2025-08-21 13:23:21 +02:00
parent f9e0833bcf
commit f2b426851b
4 changed files with 108 additions and 168 deletions

View File

@@ -133,6 +133,21 @@ impl<D: DataProvider> FormEditor<D> {
self.update_inline_completion(); self.update_inline_completion();
} }
pub fn suggestions_prev(&mut self) {
if !self.ui_state.suggestions.is_active || self.suggestions.is_empty() {
return;
}
let current = self.ui_state.suggestions.selected_index.unwrap_or(0);
let prev = if current == 0 {
self.suggestions.len() - 1
} else {
current - 1
};
self.ui_state.suggestions.selected_index = Some(prev);
self.update_inline_completion();
}
pub fn apply_suggestion(&mut self) -> Option<String> { pub fn apply_suggestion(&mut self) -> Option<String> {
if let Some(selected_index) = self.ui_state.suggestions.selected_index { if let Some(selected_index) = self.ui_state.suggestions.selected_index {
if let Some(suggestion) = self.suggestions.get(selected_index).cloned() if let Some(suggestion) = self.suggestions.get(selected_index).cloned()

View File

@@ -8,7 +8,7 @@ license.workspace = true
anyhow = { workspace = true } anyhow = { workspace = true }
async-trait = "0.1.88" async-trait = "0.1.88"
common = { path = "../common" } common = { path = "../common" }
canvas = { path = "../canvas", features = ["gui", "suggestions", "cursor-style"] } canvas = { path = "../canvas", features = ["gui", "suggestions", "cursor-style", "keymap"] }
ratatui = { workspace = true } ratatui = { workspace = true }
crossterm = { workspace = true } crossterm = { workspace = true }

View File

@@ -5,6 +5,7 @@ use std::collections::HashMap;
use std::path::Path; use std::path::Path;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use crossterm::event::{KeyCode, KeyModifiers}; use crossterm::event::{KeyCode, KeyModifiers};
use canvas::CanvasKeyMap;
// NEW: Editor Keybinding Mode Enum // NEW: Editor Keybinding Mode Enum
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
@@ -760,4 +761,43 @@ impl Config {
} }
false false
} }
/// Unified action resolver for app-level actions
pub fn get_app_action(
&self,
key_code: crossterm::event::KeyCode,
modifiers: crossterm::event::KeyModifiers,
) -> Option<&str> {
// First check common actions
if let Some(action) = self.get_common_action(key_code, modifiers) {
return Some(action);
} }
// Then check read-only mode actions
if let Some(action) = self.get_read_only_action_for_key(key_code, modifiers) {
return Some(action);
}
// Then check highlight mode actions
if let Some(action) = self.get_highlight_action_for_key(key_code, modifiers) {
return Some(action);
}
// Then check edit mode actions
if let Some(action) = self.get_edit_action_for_key(key_code, modifiers) {
return Some(action);
}
None
}
pub fn build_canvas_keymap(&self) -> CanvasKeyMap {
CanvasKeyMap::from_mode_maps(
&self.keybindings.read_only,
&self.keybindings.edit,
&self.keybindings.highlight,
)
}
}

View File

@@ -39,6 +39,7 @@ use crate::tui::{
}; };
use crate::ui::handlers::context::UiContext; use crate::ui::handlers::context::UiContext;
use crate::ui::handlers::rat_state::UiStateHandler; use crate::ui::handlers::rat_state::UiStateHandler;
use canvas::KeyEventOutcome;
use anyhow::Result; use anyhow::Result;
use common::proto::komp_ac::search::search_response::Hit; use common::proto::komp_ac::search::search_response::Hit;
use crossterm::event::KeyModifiers; use crossterm::event::KeyModifiers;
@@ -650,80 +651,29 @@ impl EventHandler {
} }
AppMode::ReadOnly => { AppMode::ReadOnly => {
// Handle highlight mode transitions (delegated to FormEditor) // First let the canvas editor try to handle the key
if config.get_read_only_action_for_key(key_code, modifiers) if app_state.ui.show_form {
== Some("enter_highlight_mode_linewise")
&& ModeManager::can_enter_highlight_mode(current_mode)
{
if let Some(editor) = &mut app_state.form_editor { if let Some(editor) = &mut app_state.form_editor {
editor.enter_highlight_line_mode(); match editor.handle_key_event(key_event) {
KeyEventOutcome::Consumed(Some(msg)) => {
return Ok(EventOutcome::Ok(msg));
}
KeyEventOutcome::Consumed(None) => {
return Ok(EventOutcome::Ok(String::new()));
}
KeyEventOutcome::Pending => {
// Waiting for multi-key sequence (e.g. after pressing 'g')
return Ok(EventOutcome::Ok(String::new()));
}
KeyEventOutcome::NotMatched => {
// Fall through to client-level actions
}
} }
self.command_message = "-- LINE HIGHLIGHT --".to_string();
return Ok(EventOutcome::Ok(self.command_message.clone()));
} else if config.get_read_only_action_for_key(key_code, modifiers)
== Some("enter_highlight_mode")
&& ModeManager::can_enter_highlight_mode(current_mode)
{
if let Some(editor) = &mut app_state.form_editor {
editor.enter_highlight_mode();
} }
self.command_message = "-- HIGHLIGHT --".to_string();
return Ok(EventOutcome::Ok(self.command_message.clone()));
} }
// Handle edit mode transitions // Entering command mode is still a client-level action
else if config.get_read_only_action_for_key(key_code, modifiers).as_deref() if config.get_app_action(key_code, modifiers) == Some("enter_command_mode")
== Some("enter_edit_mode_before")
&& ModeManager::can_enter_edit_mode(current_mode)
{
if let Some(editor) = &mut app_state.form_editor {
editor.enter_edit_mode();
}
self.is_edit_mode = true;
self.edit_mode_cooldown = true;
self.command_message = "Edit mode".to_string();
return Ok(EventOutcome::Ok(self.command_message.clone()));
} else if config.get_read_only_action_for_key(key_code, modifiers).as_deref()
== Some("enter_edit_mode_after")
&& ModeManager::can_enter_edit_mode(current_mode)
{
let current_input = Self::get_current_input_for_state(
app_state,
login_state,
register_state,
form_state,
);
let current_cursor_pos =
Self::get_cursor_pos_for_mixed_state(app_state, login_state, form_state);
// Move cursor forward if possible
if !current_input.is_empty() && current_cursor_pos < current_input.len() {
let new_cursor_pos = current_cursor_pos + 1;
Self::set_current_cursor_pos_for_state(
app_state,
login_state,
register_state,
form_state,
new_cursor_pos,
);
self.ideal_cursor_column = Self::get_current_cursor_pos_for_state(
app_state,
login_state,
register_state,
form_state,
);
}
if let Some(editor) = &mut app_state.form_editor {
editor.enter_edit_mode();
}
self.is_edit_mode = true;
self.edit_mode_cooldown = true;
app_state.ui.focus_outside_canvas = false;
self.command_message = "Edit mode (after cursor)".to_string();
return Ok(EventOutcome::Ok(self.command_message.clone()));
} else if config.get_read_only_action_for_key(key_code, modifiers)
== Some("enter_command_mode")
&& ModeManager::can_enter_command_mode(current_mode) && ModeManager::can_enter_command_mode(current_mode)
{ {
if let Some(editor) = &mut app_state.form_editor { if let Some(editor) = &mut app_state.form_editor {
@@ -736,7 +686,7 @@ impl EventHandler {
} }
// Handle common actions (save, quit, etc.) // Handle common actions (save, quit, etc.)
if let Some(action) = config.get_common_action(key_code, modifiers) { if let Some(action) = config.get_app_action(key_code, modifiers) {
match action { match action {
"save" | "force_quit" | "save_and_quit" | "revert" => { "save" | "force_quit" | "save_and_quit" | "revert" => {
return self return self
@@ -755,37 +705,27 @@ impl EventHandler {
} }
} }
// Try canvas action for form first
if app_state.ui.show_form {
if let Some(editor) = &mut app_state.form_editor {
if let Ok(Some(canvas_message)) =
self.handle_form_canvas_action(key_event, editor, config, false).await
{
return Ok(EventOutcome::Ok(canvas_message));
}
}
}
return Ok(EventOutcome::Ok(self.command_message.clone())); return Ok(EventOutcome::Ok(self.command_message.clone()));
} }
AppMode::Highlight => { AppMode::Highlight => {
if config.get_highlight_action_for_key(key_code, modifiers) if app_state.ui.show_form {
== Some("exit_highlight_mode")
{
if let Some(editor) = &mut app_state.form_editor { if let Some(editor) = &mut app_state.form_editor {
editor.exit_highlight_mode(); match editor.handle_key_event(key_event) {
KeyEventOutcome::Consumed(Some(msg)) => {
return Ok(EventOutcome::Ok(msg));
}
KeyEventOutcome::Consumed(None) => {
return Ok(EventOutcome::Ok(String::new()));
}
KeyEventOutcome::Pending => {
return Ok(EventOutcome::Ok(String::new()));
}
KeyEventOutcome::NotMatched => {
// Fall through to client-level actions
}
} }
self.command_message = "Exited highlight mode".to_string();
return Ok(EventOutcome::Ok(self.command_message.clone()));
} else if config.get_highlight_action_for_key(key_code, modifiers)
== Some("enter_highlight_mode_linewise")
{
if let Some(editor) = &mut app_state.form_editor {
editor.enter_highlight_line_mode();
} }
self.command_message = "-- LINE HIGHLIGHT --".to_string();
return Ok(EventOutcome::Ok(self.command_message.clone()));
} }
return Ok(EventOutcome::Ok(self.command_message.clone())); return Ok(EventOutcome::Ok(self.command_message.clone()));
@@ -793,7 +733,7 @@ impl EventHandler {
AppMode::Edit => { AppMode::Edit => {
// Handle common actions (save, quit, etc.) // Handle common actions (save, quit, etc.)
if let Some(action) = config.get_common_action(key_code, modifiers) { if let Some(action) = config.get_app_action(key_code, modifiers) {
match action { match action {
"save" | "force_quit" | "save_and_quit" | "revert" => { "save" | "force_quit" | "save_and_quit" | "revert" => {
return self return self
@@ -812,19 +752,24 @@ impl EventHandler {
} }
} }
// Try canvas action for form first // Let the canvas editor handle edit-mode keys
if app_state.ui.show_form { if app_state.ui.show_form {
if let Some(editor) = &mut app_state.form_editor { if let Some(editor) = &mut app_state.form_editor {
if let Ok(Some(canvas_message)) = self.handle_form_canvas_action( match editor.handle_key_event(key_event) {
key_event, KeyEventOutcome::Consumed(Some(msg)) => {
editor, self.command_message = msg.clone();
config, return Ok(EventOutcome::Ok(msg));
true, }
).await { KeyEventOutcome::Consumed(None) => {
if !canvas_message.is_empty() { return Ok(EventOutcome::Ok(String::new()));
self.command_message = canvas_message.clone(); }
KeyEventOutcome::Pending => {
// Rare in edit mode, but allow multi-key sequences if defined
return Ok(EventOutcome::Ok(String::new()));
}
KeyEventOutcome::NotMatched => {
// Fall through to client-level actions
} }
return Ok(EventOutcome::Ok(canvas_message));
} }
} }
} }
@@ -973,66 +918,6 @@ impl EventHandler {
matches!(command, "w" | "q" | "q!" | "wq" | "r") matches!(command, "w" | "q" | "q!" | "wq" | "r")
} }
async fn handle_form_canvas_action(
&mut self,
key_event: KeyEvent,
editor: &mut FormEditor<FormState>,
config: &Config,
is_edit_mode: bool,
) -> Result<Option<String>> {
use crossterm::event::KeyCode;
if is_edit_mode {
if let KeyCode::Char(c) = key_event.code {
if key_event.modifiers.is_empty() || key_event.modifiers == KeyModifiers::SHIFT {
editor.insert_char(c)?;
return Ok(Some(format!("Inserted '{}'", c)));
}
}
}
// Use your config to resolve actions
if let Some(action) = config.get_edit_action_for_key(key_event.code, key_event.modifiers) {
match action {
"delete_char_backward" => { editor.delete_backward()?; return Ok(Some("Deleted backward".to_string())); }
"delete_char_forward" => { editor.delete_forward()?; return Ok(Some("Deleted forward".to_string())); }
"move_left" => { editor.move_left()?; return Ok(Some("Moved left".to_string())); }
"move_right" => { editor.move_right()?; return Ok(Some("Moved right".to_string())); }
"move_up" => { editor.move_up()?; return Ok(Some("Moved up".to_string())); }
"move_down" => { editor.move_down()?; return Ok(Some("Moved down".to_string())); }
"move_line_start" => { editor.move_line_start(); return Ok(Some("Line start".to_string())); }
"move_line_end" => { editor.move_line_end(); return Ok(Some("Line end".to_string())); }
"move_word_next" => { editor.move_word_next(); return Ok(Some("Next word".to_string())); }
"move_word_prev" => { editor.move_word_prev(); return Ok(Some("Prev word".to_string())); }
"move_word_end" => { editor.move_word_end(); return Ok(Some("Word end".to_string())); }
"move_word_end_prev" => { editor.move_word_end_prev(); return Ok(Some("Prev word end".to_string())); }
"next_field" => { editor.next_field()?; return Ok(Some("Next field".to_string())); }
"prev_field" => { editor.prev_field()?; return Ok(Some("Prev field".to_string())); }
"open_suggestions" => {
let field_index = editor.current_field();
editor.open_suggestions(field_index);
return Ok(Some("Opened suggestions".to_string()));
}
"apply_suggestion" | "enter_decider" => {
if let Some(s) = editor.apply_suggestion() {
return Ok(Some(format!("Applied suggestion: {}", s)));
} else {
return Ok(Some("No suggestion applied".to_string()));
}
}
"exit" | "exit_edit_mode" => {
editor.exit_edit_mode()?;
return Ok(Some("Exited edit mode".to_string()));
}
_ => {}
}
}
Ok(None)
}
async fn handle_core_action( async fn handle_core_action(
&mut self, &mut self,
action: &str, action: &str,