tui: clarify panel focus and composer keys
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum ComposerEditAction {
|
||||
InsertChar(char),
|
||||
InsertNewline,
|
||||
DeleteBefore,
|
||||
DeleteAfter,
|
||||
DeleteWordBefore,
|
||||
MoveLeft,
|
||||
MoveRight,
|
||||
MoveWordLeft,
|
||||
MoveWordRight,
|
||||
MoveStart,
|
||||
MoveHome,
|
||||
MoveEnd,
|
||||
MoveUp,
|
||||
MoveDown,
|
||||
}
|
||||
|
||||
impl ComposerEditAction {
|
||||
pub(crate) fn is_modifier_action(self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::InsertNewline
|
||||
| Self::DeleteWordBefore
|
||||
| Self::MoveWordLeft
|
||||
| Self::MoveWordRight
|
||||
| Self::MoveStart
|
||||
| Self::MoveEnd
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Shared readline-style composer editing keymap used by the normal Pod TUI
|
||||
/// and the workspace panel. Callers still own higher-level routing such as
|
||||
/// completion popups, Enter submission, Tab target switching, Esc focus, and
|
||||
/// row/list navigation.
|
||||
pub(crate) fn composer_edit_action(key: KeyEvent) -> Option<ComposerEditAction> {
|
||||
let ctrl = key.modifiers.contains(KeyModifiers::CONTROL);
|
||||
let alt = key.modifiers.contains(KeyModifiers::ALT);
|
||||
|
||||
match key.code {
|
||||
KeyCode::Enter if alt && !ctrl => Some(ComposerEditAction::InsertNewline),
|
||||
KeyCode::Char('a') if ctrl && !alt => Some(ComposerEditAction::MoveStart),
|
||||
KeyCode::Char('e') if ctrl && !alt => Some(ComposerEditAction::MoveEnd),
|
||||
KeyCode::Left if ctrl || alt => Some(ComposerEditAction::MoveWordLeft),
|
||||
KeyCode::Right if ctrl || alt => Some(ComposerEditAction::MoveWordRight),
|
||||
KeyCode::Backspace if ctrl || alt => Some(ComposerEditAction::DeleteWordBefore),
|
||||
KeyCode::Char('w') if ctrl && !alt => Some(ComposerEditAction::DeleteWordBefore),
|
||||
KeyCode::Backspace if !ctrl && !alt => Some(ComposerEditAction::DeleteBefore),
|
||||
KeyCode::Delete if !ctrl && !alt => Some(ComposerEditAction::DeleteAfter),
|
||||
KeyCode::Left if !ctrl && !alt => Some(ComposerEditAction::MoveLeft),
|
||||
KeyCode::Right if !ctrl && !alt => Some(ComposerEditAction::MoveRight),
|
||||
KeyCode::Up if !ctrl && !alt => Some(ComposerEditAction::MoveUp),
|
||||
KeyCode::Down if !ctrl && !alt => Some(ComposerEditAction::MoveDown),
|
||||
KeyCode::Home if !alt => Some(ComposerEditAction::MoveHome),
|
||||
KeyCode::End if !alt => Some(ComposerEditAction::MoveEnd),
|
||||
KeyCode::Char(c) if !ctrl && !alt => Some(ComposerEditAction::InsertChar(c)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn key(code: KeyCode) -> KeyEvent {
|
||||
KeyEvent::new(code, KeyModifiers::NONE)
|
||||
}
|
||||
|
||||
fn modified(code: KeyCode, modifiers: KeyModifiers) -> KeyEvent {
|
||||
KeyEvent::new(code, modifiers)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn maps_word_motion_and_word_delete_keys() {
|
||||
assert_eq!(
|
||||
composer_edit_action(modified(KeyCode::Left, KeyModifiers::CONTROL)),
|
||||
Some(ComposerEditAction::MoveWordLeft)
|
||||
);
|
||||
assert_eq!(
|
||||
composer_edit_action(modified(KeyCode::Right, KeyModifiers::ALT)),
|
||||
Some(ComposerEditAction::MoveWordRight)
|
||||
);
|
||||
assert_eq!(
|
||||
composer_edit_action(modified(KeyCode::Backspace, KeyModifiers::CONTROL)),
|
||||
Some(ComposerEditAction::DeleteWordBefore)
|
||||
);
|
||||
assert_eq!(
|
||||
composer_edit_action(modified(KeyCode::Char('w'), KeyModifiers::CONTROL)),
|
||||
Some(ComposerEditAction::DeleteWordBefore)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leaves_enter_tab_esc_and_control_letters_for_callers() {
|
||||
assert_eq!(composer_edit_action(key(KeyCode::Enter)), None);
|
||||
assert_eq!(composer_edit_action(key(KeyCode::Tab)), None);
|
||||
assert_eq!(composer_edit_action(key(KeyCode::Esc)), None);
|
||||
assert_eq!(
|
||||
composer_edit_action(modified(KeyCode::Char('j'), KeyModifiers::CONTROL)),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plain_letters_remain_text_input() {
|
||||
assert_eq!(
|
||||
composer_edit_action(key(KeyCode::Char('j'))),
|
||||
Some(ComposerEditAction::InsertChar('j'))
|
||||
);
|
||||
assert_eq!(
|
||||
composer_edit_action(key(KeyCode::Char('o'))),
|
||||
Some(ComposerEditAction::InsertChar('o'))
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user