Allow typing newline with <S-Enter> and enable keyboard enhancement protocol (#272)

This commit is contained in:
Ulyssa 2024-04-21 18:19:53 -07:00 committed by GitHub
parent 7bc34c8145
commit 3971801aa3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 97 additions and 24 deletions

View file

@ -3,13 +3,13 @@
//! The keybindings are set up here. We define some iamb-specific keybindings, but the default Vim //! The keybindings are set up here. We define some iamb-specific keybindings, but the default Vim
//! keys come from [modalkit::env::vim::keybindings]. //! keys come from [modalkit::env::vim::keybindings].
use modalkit::{ use modalkit::{
actions::{MacroAction, WindowAction}, actions::{InsertTextAction, MacroAction, WindowAction},
env::vim::keybindings::{InputStep, VimBindings}, env::vim::keybindings::{InputStep, VimBindings},
env::vim::VimMode, env::vim::VimMode,
env::CommonKeyClass, env::CommonKeyClass,
key::TerminalKey, key::TerminalKey,
keybindings::{EdgeEvent, EdgeRepeat, InputBindings}, keybindings::{EdgeEvent, EdgeRepeat, InputBindings},
prelude::Count, prelude::*,
}; };
use crate::base::{IambAction, IambInfo, Keybindings, MATRIX_ID_WORD}; use crate::base::{IambAction, IambInfo, Keybindings, MATRIX_ID_WORD};
@ -36,6 +36,7 @@ pub fn setup_keybindings() -> Keybindings {
let ctrl_z = "<C-Z>".parse::<TerminalKey>().unwrap(); let ctrl_z = "<C-Z>".parse::<TerminalKey>().unwrap();
let key_m_lc = "m".parse::<TerminalKey>().unwrap(); let key_m_lc = "m".parse::<TerminalKey>().unwrap();
let key_z_lc = "z".parse::<TerminalKey>().unwrap(); let key_z_lc = "z".parse::<TerminalKey>().unwrap();
let shift_enter = "<S-Enter>".parse::<TerminalKey>().unwrap();
let cwz = vec![once(&ctrl_w), once(&key_z_lc)]; let cwz = vec![once(&ctrl_w), once(&key_z_lc)];
let cwcz = vec![once(&ctrl_w), once(&ctrl_z)]; let cwcz = vec![once(&ctrl_w), once(&ctrl_z)];
@ -57,6 +58,17 @@ pub fn setup_keybindings() -> Keybindings {
ism.add_mapping(VimMode::Visual, &cwm, &stoggle); ism.add_mapping(VimMode::Visual, &cwm, &stoggle);
ism.add_mapping(VimMode::Normal, &cwcm, &stoggle); ism.add_mapping(VimMode::Normal, &cwcm, &stoggle);
ism.add_mapping(VimMode::Visual, &cwcm, &stoggle); ism.add_mapping(VimMode::Visual, &cwcm, &stoggle);
let shift_enter = vec![once(&shift_enter)];
let newline = IambStep::new().actions(vec![InsertTextAction::Type(
Char::Single('\n').into(),
MoveDir1D::Previous,
1.into(),
)
.into()]);
ism.add_mapping(VimMode::Insert, &cwm, &newline);
ism.add_mapping(VimMode::Insert, &shift_enter, &newline);
ism ism
} }

View file

@ -48,6 +48,9 @@ use modalkit::crossterm::{
EnableFocusChange, EnableFocusChange,
Event, Event,
KeyEventKind, KeyEventKind,
KeyboardEnhancementFlags,
PopKeyboardEnhancementFlags,
PushKeyboardEnhancementFlags,
}, },
execute, execute,
terminal::{EnterAlternateScreen, LeaveAlternateScreen, SetTitle}, terminal::{EnterAlternateScreen, LeaveAlternateScreen, SetTitle},
@ -250,16 +253,7 @@ impl Application {
settings: ApplicationSettings, settings: ApplicationSettings,
store: AsyncProgramStore, store: AsyncProgramStore,
) -> IambResult<Application> { ) -> IambResult<Application> {
let mut stdout = stdout(); let backend = CrosstermBackend::new(stdout());
crossterm::terminal::enable_raw_mode()?;
crossterm::execute!(stdout, EnterAlternateScreen)?;
crossterm::execute!(stdout, EnableBracketedPaste)?;
crossterm::execute!(stdout, EnableFocusChange)?;
let title = format!("iamb ({})", settings.profile.user_id);
crossterm::execute!(stdout, SetTitle(title))?;
let backend = CrosstermBackend::new(stdout);
let terminal = Terminal::new(backend)?; let terminal = Terminal::new(backend)?;
let mut bindings = crate::keybindings::setup_keybindings(); let mut bindings = crate::keybindings::setup_keybindings();
@ -905,6 +899,70 @@ async fn login_normal(
Ok(()) Ok(())
} }
struct EnableModifyOtherKeys;
struct DisableModifyOtherKeys;
impl crossterm::Command for EnableModifyOtherKeys {
fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
write!(f, "\x1B[>4;2m")
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
Ok(())
}
}
impl crossterm::Command for DisableModifyOtherKeys {
fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
write!(f, "\x1B[>4;0m")
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
Ok(())
}
}
/// Set up the terminal for drawing the TUI, and getting additional info.
fn setup_tty(title: &str, enable_enhanced_keys: bool) -> std::io::Result<()> {
let title = format!("iamb ({})", title);
// Enable raw mode and enter the alternate screen.
crossterm::terminal::enable_raw_mode()?;
crossterm::execute!(stdout(), EnterAlternateScreen)?;
if enable_enhanced_keys {
// Enable the Kitty keyboard enhancement protocol for improved keypresses.
crossterm::queue!(
stdout(),
PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES)
)?;
} else {
crossterm::queue!(stdout(), EnableModifyOtherKeys)?;
}
crossterm::execute!(stdout(), EnableBracketedPaste, EnableFocusChange, SetTitle(title))
}
// Do our best to reverse what we did in setup_tty() when we exit or crash.
fn restore_tty(enable_enhanced_keys: bool) {
if enable_enhanced_keys {
let _ = crossterm::queue!(stdout(), PopKeyboardEnhancementFlags);
}
let _ = crossterm::execute!(
stdout(),
DisableModifyOtherKeys,
DisableBracketedPaste,
DisableFocusChange,
LeaveAlternateScreen,
CursorShow,
);
let _ = crossterm::terminal::disable_raw_mode();
}
async fn run(settings: ApplicationSettings) -> IambResult<()> { async fn run(settings: ApplicationSettings) -> IambResult<()> {
// Get old keys the first time we run w/ the upgraded SDK. // Get old keys the first time we run w/ the upgraded SDK.
let import_keys = check_import_keys(&settings).await?; let import_keys = check_import_keys(&settings).await?;
@ -938,27 +996,30 @@ async fn run(settings: ApplicationSettings) -> IambResult<()> {
Ok(()) => (), Ok(()) => (),
} }
fn restore_tty() { // Set up the terminal for drawing, and cleanup properly on panics.
let _ = crossterm::terminal::disable_raw_mode(); let enable_enhanced_keys = match crossterm::terminal::supports_keyboard_enhancement() {
let _ = crossterm::execute!(stdout(), DisableBracketedPaste); Ok(supported) => supported,
let _ = crossterm::execute!(stdout(), DisableFocusChange); Err(e) => {
let _ = crossterm::execute!(stdout(), LeaveAlternateScreen); tracing::warn!(err = %e,
let _ = crossterm::execute!(stdout(), CursorShow); "Failed to determine whether the terminal supports keyboard enhancements");
} false
},
};
setup_tty(settings.profile.user_id.as_str(), enable_enhanced_keys)?;
// Make sure panics clean up the terminal properly.
let orig_hook = std::panic::take_hook(); let orig_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic_info| { std::panic::set_hook(Box::new(move |panic_info| {
restore_tty(); restore_tty(enable_enhanced_keys);
orig_hook(panic_info); orig_hook(panic_info);
process::exit(1); process::exit(1);
})); }));
// And finally, start running the terminal UI.
let mut application = Application::new(settings, store).await?; let mut application = Application::new(settings, store).await?;
// We can now run the application.
application.run().await?; application.run().await?;
restore_tty();
// Clean up the terminal on exit.
restore_tty(enable_enhanced_keys);
Ok(()) Ok(())
} }