diff --git a/eden/scm/lib/third-party/streampager/src/prompt.rs b/eden/scm/lib/third-party/streampager/src/prompt.rs index 14493dc897b54..0528533364c37 100644 --- a/eden/scm/lib/third-party/streampager/src/prompt.rs +++ b/eden/scm/lib/third-party/streampager/src/prompt.rs @@ -5,7 +5,7 @@ use std::fmt::Write; use termwiz::cell::{AttributeChange, CellAttributes}; use termwiz::color::{AnsiColor, ColorAttribute}; -use termwiz::input::KeyEvent; +use termwiz::input::{KeyCode, KeyEvent}; use termwiz::surface::change::Change; use termwiz::surface::Position; use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; @@ -371,8 +371,21 @@ impl Prompt { const CTRL: Modifiers = Modifiers::CTRL; const NONE: Modifiers = Modifiers::NONE; const ALT: Modifiers = Modifiers::ALT; - let value_width = width - self.prompt.width() - 4; - let action = match (key.modifiers, key.key) { + let mut cands = Vec::with_capacity(4); + cands.push(key.clone()); + if key.modifiers.contains(Modifiers::SHIFT) { + if let KeyCode::Char(c) = key.key { + let mut ns = key.modifiers; + ns.remove(Modifiers::SHIFT); + cands.push(KeyEvent { key: KeyCode::Char(c), modifiers: ns }); + if c.is_ascii_lowercase() { + cands.push(KeyEvent { key: KeyCode::Char(c.to_ascii_uppercase()), modifiers: ns }); + } + } + } + for (mods, k) in cands.into_iter().map(|ke| (ke.modifiers, ke.key)) { + let value_width = width - self.prompt.width() - 4; + let action = match (mods, k.clone()) { (NONE, Enter) | (CTRL, Char('j')) | (CTRL, Char('m')) => { // Finish. let _ = self.history.save(); @@ -410,10 +423,12 @@ impl Prompt { (CTRL, Char('t')) => self.state_mut().transpose_chars(), (NONE, UpArrow) => self.history.previous(), (NONE, DownArrow) => self.history.next(), - _ => return DisplayAction::None, + _ => continue, }; - self.state_mut().clamp_offset(value_width); - action + self.state_mut().clamp_offset(value_width); + return action; + } + DisplayAction::None } /// Paste some text into the prompt. diff --git a/eden/scm/lib/third-party/streampager/src/screen.rs b/eden/scm/lib/third-party/streampager/src/screen.rs index cea50925280c2..419795dc3629b 100644 --- a/eden/scm/lib/third-party/streampager/src/screen.rs +++ b/eden/scm/lib/third-party/streampager/src/screen.rs @@ -1241,19 +1241,44 @@ impl Screen { key: KeyEvent, event_sender: &EventSender, ) -> DisplayAction { - if let Some(binding) = self.keymap.get(key.modifiers, key.key) { - match binding { - Binding::Action(action) => { - let action = action.clone(); - return self.dispatch_action(action, event_sender); + // Ghostty with modifyOtherKeys enabled (termwiz enables ESC[>4;2m in set_raw_mode) + // encodes Shift+G as ESC[27;2;71~ which termwiz decodes as Char('G')+SHIFT. + // Our keymap stores 'G' as Char('G')+NONE, so we normalize by stripping SHIFT. + for candidate in Self::normalize_key_candidates(key) { + if let Some(binding) = self.keymap.get(candidate.modifiers, candidate.key) { + match binding { + Binding::Action(action) => { + let action = action.clone(); + return self.dispatch_action(action, event_sender); + } + Binding::Custom(b) => { + b.run(self.file.index()); + return DisplayAction::Render; + } + Binding::Unrecognized(_) => {} } - Binding::Custom(b) => b.run(self.file.index()), - Binding::Unrecognized(_) => {} } } DisplayAction::Render } + fn normalize_key_candidates(key: KeyEvent) -> Vec { + use termwiz::input::{KeyCode, Modifiers}; + let mut out = Vec::with_capacity(4); + out.push(key.clone()); + if key.modifiers.contains(Modifiers::SHIFT) { + if let KeyCode::Char(c) = key.key { + let mut no_shift = key.modifiers; + no_shift.remove(Modifiers::SHIFT); + out.push(KeyEvent { key: KeyCode::Char(c), modifiers: no_shift }); + if c.is_ascii_lowercase() { + out.push(KeyEvent { key: KeyCode::Char(c.to_ascii_uppercase()), modifiers: no_shift }); + } + } + } + out + } + /// Append a digit to the repeat count. pub(crate) fn append_digit_to_repeat_count(&mut self, digit: usize) { assert!(digit < 10);