fix(tui): preserve Shift+Enter in tmux csi-u panes

This commit is contained in:
Felipe Coury
2026-05-09 12:10:10 -03:00
parent 0c70698e24
commit cb9e52bbd1

View File

@@ -125,16 +125,81 @@ pub(super) fn enable_keyboard_enhancement() {
let _ = execute!(
stdout(),
DisableModifyOtherKeys,
PushKeyboardEnhancementFlags(
KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
| KeyboardEnhancementFlags::REPORT_EVENT_TYPES
| KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
)
);
if tmux_should_enable_modify_other_keys() {
let _ = execute!(stdout(), EnableModifyOtherKeys);
}
}
fn running_in_tmux_session() -> bool {
tmux_session_detected(
std::env::var("TMUX").ok().as_deref(),
std::env::var("TMUX_PANE").ok().as_deref(),
)
}
fn tmux_session_detected(tmux: Option<&str>, tmux_pane: Option<&str>) -> bool {
tmux.is_some() || tmux_pane.is_some()
}
fn tmux_should_enable_modify_other_keys() -> bool {
tmux_should_enable_modify_other_keys_for(
running_in_tmux_session(),
read_tmux_extended_keys_format().as_deref(),
)
}
fn tmux_should_enable_modify_other_keys_for(
running_in_tmux_session: bool,
extended_keys_format: Option<&str>,
) -> bool {
// If tmux cannot be queried, still request mode 2; otherwise csi-u panes
// can stay in VT10x. Explicit xterm format is avoided because crossterm
// does not parse tmux's xterm-style extended-key sequences as Enter.
running_in_tmux_session && matches!(extended_keys_format, Some("csi-u") | None)
}
fn read_tmux_extended_keys_format() -> Option<String> {
for args in [
["display-message", "-p", "#{extended-keys-format}"],
["show-options", "-gqv", "extended-keys-format"],
] {
let output = std::process::Command::new("tmux")
.args(args)
.stdin(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.output()
.ok()?;
if !output.status.success() {
continue;
}
if let Some(value) = String::from_utf8(output.stdout)
.ok()
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())
{
return Some(value);
}
}
None
}
pub(super) fn restore_keyboard_enhancement_stack() {
let _ = execute!(stdout(), PopKeyboardEnhancementFlags);
let _ = execute!(
stdout(),
PopKeyboardEnhancementFlags,
DisableModifyOtherKeys
);
}
pub(super) fn reset_keyboard_reporting_after_exit() {
@@ -168,6 +233,28 @@ impl Command for ResetKeyboardEnhancementFlags {
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct EnableModifyOtherKeys;
impl Command for EnableModifyOtherKeys {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str("\x1b[>4;2m")
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"modifyOtherKeys enable is not implemented for the legacy Windows API",
))
}
#[cfg(windows)]
fn is_ansi_code_supported(&self) -> bool {
false
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct DisableModifyOtherKeys;
@@ -193,9 +280,12 @@ impl Command for DisableModifyOtherKeys {
#[cfg(test)]
mod tests {
use super::DisableModifyOtherKeys;
use super::EnableModifyOtherKeys;
use super::ResetKeyboardEnhancementFlags;
use super::keyboard_enhancement_disabled_for;
use super::parse_bool_env;
use super::tmux_session_detected;
use super::tmux_should_enable_modify_other_keys_for;
use super::vscode_terminal_detected;
use crossterm::Command;
use pretty_assertions::assert_eq;
@@ -268,11 +358,51 @@ mod tests {
));
}
#[test]
fn tmux_session_detection_accepts_tmux_or_tmux_pane() {
assert!(tmux_session_detected(
Some("/tmp/tmux-501/default,1,0"),
/*tmux_pane*/ None
));
assert!(tmux_session_detected(/*tmux*/ None, Some("%0")));
assert!(!tmux_session_detected(
/*tmux*/ None, /*tmux_pane*/ None
));
}
#[test]
fn tmux_modify_other_keys_requests_csi_u_or_unknown_format() {
assert!(tmux_should_enable_modify_other_keys_for(
/*running_in_tmux_session*/ true,
Some("csi-u")
));
assert!(tmux_should_enable_modify_other_keys_for(
/*running_in_tmux_session*/ true, /*extended_keys_format*/ None
));
assert!(!tmux_should_enable_modify_other_keys_for(
/*running_in_tmux_session*/ true,
Some("xterm")
));
assert!(!tmux_should_enable_modify_other_keys_for(
/*running_in_tmux_session*/ true,
Some("")
));
assert!(!tmux_should_enable_modify_other_keys_for(
/*running_in_tmux_session*/ false,
Some("csi-u")
));
}
#[test]
fn reset_keyboard_enhancement_flags_clears_all_pushed_levels() {
assert_eq!(ansi_for(ResetKeyboardEnhancementFlags), "\x1b[<u");
}
#[test]
fn enable_modify_other_keys_requests_xterm_keyboard_reporting() {
assert_eq!(ansi_for(EnableModifyOtherKeys), "\x1b[>4;2m");
}
#[test]
fn disable_modify_other_keys_resets_xterm_keyboard_reporting() {
assert_eq!(ansi_for(DisableModifyOtherKeys), "\x1b[>4;0m");