Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/keybindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
//! The keybindings are set up here. We define some iamb-specific keybindings, but the default Vim
//! keys come from [modalkit::env::vim::keybindings].
use modalkit::{
actions::{MacroAction, WindowAction},
actions::{InsertTextAction, MacroAction, WindowAction},
env::vim::keybindings::{InputStep, VimBindings},
env::vim::VimMode,
env::CommonKeyClass,
key::TerminalKey,
keybindings::{EdgeEvent, EdgeRepeat, InputBindings},
prelude::Count,
prelude::*,
};

use crate::base::{IambAction, IambInfo, Keybindings, MATRIX_ID_WORD};
Expand All @@ -36,6 +36,7 @@ pub fn setup_keybindings() -> Keybindings {
let ctrl_z = "<C-Z>".parse::<TerminalKey>().unwrap();
let key_m_lc = "m".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 cwcz = vec![once(&ctrl_w), once(&ctrl_z)];
Expand All @@ -57,6 +58,17 @@ pub fn setup_keybindings() -> Keybindings {
ism.add_mapping(VimMode::Visual, &cwm, &stoggle);
ism.add_mapping(VimMode::Normal, &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
}

Expand Down
105 changes: 83 additions & 22 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ use modalkit::crossterm::{
EnableFocusChange,
Event,
KeyEventKind,
KeyboardEnhancementFlags,
PopKeyboardEnhancementFlags,
PushKeyboardEnhancementFlags,
},
execute,
terminal::{EnterAlternateScreen, LeaveAlternateScreen, SetTitle},
Expand Down Expand Up @@ -250,16 +253,7 @@ impl Application {
settings: ApplicationSettings,
store: AsyncProgramStore,
) -> IambResult<Application> {
let mut stdout = 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 backend = CrosstermBackend::new(stdout());
let terminal = Terminal::new(backend)?;

let mut bindings = crate::keybindings::setup_keybindings();
Expand Down Expand Up @@ -905,6 +899,70 @@ async fn login_normal(
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<()> {
// Get old keys the first time we run w/ the upgraded SDK.
let import_keys = check_import_keys(&settings).await?;
Expand Down Expand Up @@ -938,27 +996,30 @@ async fn run(settings: ApplicationSettings) -> IambResult<()> {
Ok(()) => (),
}

fn restore_tty() {
let _ = crossterm::terminal::disable_raw_mode();
let _ = crossterm::execute!(stdout(), DisableBracketedPaste);
let _ = crossterm::execute!(stdout(), DisableFocusChange);
let _ = crossterm::execute!(stdout(), LeaveAlternateScreen);
let _ = crossterm::execute!(stdout(), CursorShow);
}
// Set up the terminal for drawing, and cleanup properly on panics.
let enable_enhanced_keys = match crossterm::terminal::supports_keyboard_enhancement() {
Ok(supported) => supported,
Err(e) => {
tracing::warn!(err = %e,
"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();
std::panic::set_hook(Box::new(move |panic_info| {
restore_tty();
restore_tty(enable_enhanced_keys);
orig_hook(panic_info);
process::exit(1);
}));

// And finally, start running the terminal UI.
let mut application = Application::new(settings, store).await?;

// We can now run the application.
application.run().await?;
restore_tty();

// Clean up the terminal on exit.
restore_tty(enable_enhanced_keys);

Ok(())
}
Expand Down
Loading