From 64000f58755871150667cb4058c19437d4e6d3ae Mon Sep 17 00:00:00 2001 From: Luis Useche Date: Thu, 7 Mar 2024 19:11:47 -0800 Subject: [PATCH] Fix system clipboard with mosh + tmux Some times tmux doesn't pass the clipboard information to the terminal correctly. This is the case of mosh + tmux. The current clipboard doesn't work properly in this case. However, when the tmux option `allow-passthrough` is on, we can escape tmux and pass the OSC52 string directly to the terminal. In this patch, if helix detects that tmux `allow-passthrough` is on, it will use the OSC52 clipboard provider and will escape tmux and pass the OSC52 directly to the terminal. Tested with mosh + tmux with the `allow-passthrough` option on. --- helix-view/src/clipboard.rs | 47 +++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/helix-view/src/clipboard.rs b/helix-view/src/clipboard.rs index 379accc7e..645ad7ab2 100644 --- a/helix-view/src/clipboard.rs +++ b/helix-view/src/clipboard.rs @@ -96,7 +96,39 @@ pub fn get_clipboard_provider() -> Box { Box::new(provider::FallbackProvider::new()) } -#[cfg(not(any(windows, target_arch = "wasm32", target_os = "macos")))] +#[cfg(not(any(windows, target_arch = "wasm32")))] +// Check if we are in tmux and the allow-passthrough is on. +fn tmux_allow_passthrough() -> bool { + use helix_stdx::env::env_var_is_set; + + if !env_var_is_set("TMUX") { + return false; + } + + use std::process::Command; + let result = Command::new("tmux") + .arg("show") + .arg("-gw") + .arg("allow-passthrough") + .output(); + let output = match result { + Ok(out) => out, + Err(_) => return false, + }; + + if !output.status.success() { + return false; + } + + let output = match String::from_utf8(output.stdout) { + Ok(out) => out, + Err(_) => return false, + }; + + output.contains("on") +} + +#[cfg(not(any(windows, target_os = "wasm32", target_os = "macos")))] pub fn get_clipboard_provider() -> Box { use helix_stdx::env::{binary_exists, env_var_is_set}; use provider::command::is_exit_success; @@ -138,7 +170,7 @@ pub fn get_clipboard_provider() -> Box { paste => "termux-clipboard-get"; copy => "termux-clipboard-set"; } - } else if env_var_is_set("TMUX") && binary_exists("tmux") { + } else if env_var_is_set("TMUX") && binary_exists("tmux") && !tmux_allow_passthrough() { command_provider! { paste => "tmux", "save-buffer", "-"; copy => "tmux", "load-buffer", "-w", "-"; @@ -148,7 +180,7 @@ pub fn get_clipboard_provider() -> Box { } } -#[cfg(not(target_os = "windows"))] +#[cfg(not(windows))] pub mod provider { use super::{ClipboardProvider, ClipboardType}; use anyhow::Result; @@ -156,6 +188,7 @@ pub mod provider { #[cfg(feature = "term")] mod osc52 { + use crate::clipboard::tmux_allow_passthrough; use {super::ClipboardType, crate::base64}; #[derive(Debug)] @@ -179,8 +212,14 @@ fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result { ClipboardType::Clipboard => "c", ClipboardType::Selection => "p", }; + // Send an OSC 52 set command: https://terminalguide.namepad.de/seq/osc-52/ - write!(f, "\x1b]52;{};{}\x1b\\", kind, &self.encoded_content) + let mut osc52 = format!("\x1b]52;{};{}\x07", kind, &self.encoded_content); + if tmux_allow_passthrough() { + // If we are inside tmux and we are allow to passthrough, escape it too. + osc52 = format!("\x1bPtmux;\x1b{}\x1b\\", osc52); + } + f.write_str(&osc52) } } }