diff --git a/Cargo.lock b/Cargo.lock index 3a95911..bbe6f51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,11 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bitflags" version = "1.2.1" @@ -138,6 +143,7 @@ dependencies = [ name = "thumbs" version = "0.4.1" dependencies = [ + "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -176,6 +182,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +"checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum hermit-abi 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" diff --git a/src/main.rs b/src/main.rs index 3bd1721..d33e078 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,6 @@ mod view; use self::clap::{App, Arg}; use clap::crate_version; use std::fs::OpenOptions; -use std::io::prelude::*; use std::io::{self, Read, Write}; fn app_args<'a>() -> clap::ArgMatches<'a> { @@ -85,12 +84,6 @@ fn app_args<'a>() -> clap::ArgMatches<'a> { .long("unique") .short("u"), ) - .arg( - Arg::with_name("osc52") - .help("Print OSC52 copy escape sequence in addition to running the pick command") - .long("osc52") - .short("o"), - ) .arg( Arg::with_name("position") .help("Hint position") @@ -131,7 +124,6 @@ fn main() { let multi = args.is_present("multi"); let reverse = args.is_present("reverse"); let unique = args.is_present("unique"); - let osc52 = args.is_present("osc52"); let contrast = args.is_present("contrast"); let regexp = if let Some(items) = args.values_of("regexp") { items.collect::>() @@ -190,32 +182,6 @@ fn main() { .collect::>() .join("\n"); - if osc52 { - let base64_text = base64::encode(text.as_bytes()); - let osc_seq = format!("\x1b]52;0;{}\x07", base64_text); - let tmux_seq = format!("\x1bPtmux;{}\x1b\\", osc_seq.replace("\x1b", "\x1b\x1b")); - - // When the user selects a match: - // 1. The `rustbox` object created in the `viewbox` above is dropped. - // 2. During its `drop`, the `rustbox` object sends a CSI 1049 escape - // sequence to tmux. - // 3. This escape sequence causes the `window_pane_alternate_off` function - // in tmux to be called. - // 4. In `window_pane_alternate_off`, tmux sets the needs-redraw flag in the - // pane. - // 5. If we print the OSC copy escape sequence before the redraw is completed, - // tmux will *not* send the sequence to the host terminal. See the following - // call chain in tmux: `input_dcs_dispatch` -> `screen_write_rawstring` - // -> `tty_write` -> `tty_client_ready`. In this case, `tty_client_ready` - // will return false, thus preventing the escape sequence from being sent. - // - // Therefore, for now we wait a little bit here for the redraw to finish. - std::thread::sleep(std::time::Duration::from_millis(100)); - - std::io::stdout().write_all(tmux_seq.as_bytes()).unwrap(); - std::io::stdout().flush().unwrap(); - } - if let Some(target) = target { let mut file = OpenOptions::new() .create(true) diff --git a/src/swapper.rs b/src/swapper.rs index f5994fa..cf07313 100644 --- a/src/swapper.rs +++ b/src/swapper.rs @@ -1,5 +1,8 @@ extern crate clap; +use std::fs::File; + +use std::io::Write; use self::clap::{App, Arg}; use clap::crate_version; use regex::Regex; @@ -47,6 +50,7 @@ pub struct Swapper<'a> { dir: String, command: String, upcase_command: String, + osc52: bool, active_pane_id: Option, active_pane_height: Option, active_pane_scroll_position: Option, @@ -57,7 +61,7 @@ pub struct Swapper<'a> { } impl<'a> Swapper<'a> { - fn new(executor: Box<&'a mut dyn Executor>, dir: String, command: String, upcase_command: String) -> Swapper { + fn new(executor: Box<&'a mut dyn Executor>, dir: String, command: String, upcase_command: String, osc52: bool) -> Swapper { let since_the_epoch = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("Time went backwards"); @@ -68,6 +72,7 @@ impl<'a> Swapper<'a> { dir, command, upcase_command, + osc52, active_pane_id: None, active_pane_height: None, active_pane_scroll_position: None, @@ -247,12 +252,44 @@ impl<'a> Swapper<'a> { self.executor.execute(params); } + pub fn send_osc52(&mut self) { + } + + pub fn execute_command(&mut self) { let content = self.content.clone().unwrap(); let mut splitter = content.splitn(2, ':'); if let Some(upcase) = splitter.next() { if let Some(text) = splitter.next() { + if self.osc52 { + let base64_text = base64::encode(text.as_bytes()); + let osc_seq = format!("\x1b]52;0;{}\x07", base64_text); + let tmux_seq = format!("\x1bPtmux;{}\x1b\\", osc_seq.replace("\x1b", "\x1b\x1b")); + + // FIXME: Review if this comment is still rellevant + // + // When the user selects a match: + // 1. The `rustbox` object created in the `viewbox` above is dropped. + // 2. During its `drop`, the `rustbox` object sends a CSI 1049 escape + // sequence to tmux. + // 3. This escape sequence causes the `window_pane_alternate_off` function + // in tmux to be called. + // 4. In `window_pane_alternate_off`, tmux sets the needs-redraw flag in the + // pane. + // 5. If we print the OSC copy escape sequence before the redraw is completed, + // tmux will *not* send the sequence to the host terminal. See the following + // call chain in tmux: `input_dcs_dispatch` -> `screen_write_rawstring` + // -> `tty_write` -> `tty_client_ready`. In this case, `tty_client_ready` + // will return false, thus preventing the escape sequence from being sent. + // + // Therefore, for now we wait a little bit here for the redraw to finish. + std::thread::sleep(std::time::Duration::from_millis(100)); + + std::io::stdout().write_all(tmux_seq.as_bytes()).unwrap(); + std::io::stdout().flush().unwrap(); + } + let execute_command = if upcase.trim_end() == "true" { self.upcase_command.clone() } else { @@ -302,7 +339,7 @@ mod tests { fn retrieve_active_pane() { let last_command_outputs = vec!["%97:100:24:1:active\n%106:100:24:1:nope\n%107:100:24:1:nope\n".to_string()]; let mut executor = TestShell::new(last_command_outputs); - let mut swapper = Swapper::new(Box::new(&mut executor), "".to_string(), "".to_string(), "".to_string()); + let mut swapper = Swapper::new(Box::new(&mut executor), "".to_string(), "".to_string(), "".to_string(), false); swapper.capture_active_pane(); @@ -318,7 +355,7 @@ mod tests { "%106:100:24:1:nope\n%98:100:24:1:active\n%107:100:24:1:nope\n".to_string(), ]; let mut executor = TestShell::new(last_command_outputs); - let mut swapper = Swapper::new(Box::new(&mut executor), "".to_string(), "".to_string(), "".to_string()); + let mut swapper = Swapper::new(Box::new(&mut executor), "".to_string(), "".to_string(), "".to_string(), false); swapper.capture_active_pane(); swapper.execute_thumbs(); @@ -352,6 +389,12 @@ fn app_args<'a>() -> clap::ArgMatches<'a> { .long("upcase-command") .default_value("tmux set-buffer {} && tmux paste-buffer"), ) + .arg( + Arg::with_name("osc52") + .help("Print OSC52 copy escape sequence in addition to running the pick command") + .long("osc52") + .short("o"), + ) .get_matches() } @@ -360,6 +403,7 @@ fn main() -> std::io::Result<()> { let dir = args.value_of("dir").unwrap(); let command = args.value_of("command").unwrap(); let upcase_command = args.value_of("upcase_command").unwrap(); + let osc52 = args.is_present("osc52"); if dir.is_empty() { panic!("Invalid tmux-thumbs execution. Are you trying to execute tmux-thumbs directly?") @@ -371,6 +415,7 @@ fn main() -> std::io::Result<()> { dir.to_string(), command.to_string(), upcase_command.to_string(), + osc52, ); swapper.capture_active_pane(); diff --git a/tmux-thumbs.sh b/tmux-thumbs.sh index ec6b736..7be8a3f 100755 --- a/tmux-thumbs.sh +++ b/tmux-thumbs.sh @@ -4,6 +4,14 @@ PARAMS=() +function add-boolean-param { + VALUE=$(tmux show -vg @thumbs-$1 2> /dev/null) + + if [[ "${VALUE}" == "1" ]]; then + PARAMS+=("--$1") + fi +} + function add-option-param { VALUE=$(tmux show -vg @thumbs-$1 2> /dev/null) @@ -14,6 +22,7 @@ function add-option-param { add-option-param "command" add-option-param "upcase-command" +add-boolean-param "osc52" # Remove empty arguments from PARAMS. # Otherwise, they would choke up tmux-thumbs when passed to it.