Adds OSC52 copy escape sequence support

Currently I can only get the copied content in the tmux copy buffer, not in the
system clipboard. This commit adds an option to print the copied text as a
OSC52 copy escape sequence, which in supported terminals (tested in iTerm) will
be copied to the system clipboard.
This commit is contained in:
Jiahao Li 2019-11-23 17:49:15 -05:00 committed by Ferran Basora
parent 794621f526
commit 25ed6dc78f
3 changed files with 49 additions and 1 deletions

View File

@ -12,6 +12,7 @@ license = "MIT"
termion = "1.5" termion = "1.5"
regex = "1.3.1" regex = "1.3.1"
clap = "2.33.0" clap = "2.33.0"
base64 = "0.11.0"
[[bin]] [[bin]]
name = "thumbs" name = "thumbs"

View File

@ -93,6 +93,7 @@ NOTE: for changes to take effect, you'll need to source again your `.tmux.conf`
* [@thumbs-select-fg-color](#thumbs-select-fg-color) * [@thumbs-select-fg-color](#thumbs-select-fg-color)
* [@thumbs-select-bg-color](#thumbs-select-bg-color) * [@thumbs-select-bg-color](#thumbs-select-bg-color)
* [@thumbs-contrast](#thumbs-contrast) * [@thumbs-contrast](#thumbs-contrast)
* [@thumbs-osc52](#thumbs-osc52)
### @thumbs-key ### @thumbs-key
@ -273,6 +274,18 @@ For example:
set -g @thumbs-contrast 1 set -g @thumbs-contrast 1
``` ```
### @thumbs-osc52
`default: 0`
If this is set to `1`, `tmux-thumbs` will print a OSC52 copy escape sequence when you select a match, in addition to running the pick command. This sequence, in terminals that support it (e.g. iTerm), allows the content to be copied into the system clipboard in addition to the tmux copy buffer.
For example:
```
set -g @thumbs-osc52 1
```
#### Colors #### Colors
This is the list of available colors: This is the list of available colors:

View File

@ -1,3 +1,4 @@
extern crate base64;
extern crate clap; extern crate clap;
extern crate termion; extern crate termion;
@ -10,7 +11,7 @@ use self::clap::{App, Arg};
use clap::crate_version; use clap::crate_version;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::prelude::*; use std::io::prelude::*;
use std::io::{self, Read}; use std::io::{self, Read, Write};
fn app_args<'a>() -> clap::ArgMatches<'a> { fn app_args<'a>() -> clap::ArgMatches<'a> {
App::new("thumbs") App::new("thumbs")
@ -84,6 +85,12 @@ fn app_args<'a>() -> clap::ArgMatches<'a> {
.long("unique") .long("unique")
.short("u"), .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(
Arg::with_name("position") Arg::with_name("position")
.help("Hint position") .help("Hint position")
@ -124,6 +131,7 @@ fn main() {
let multi = args.is_present("multi"); let multi = args.is_present("multi");
let reverse = args.is_present("reverse"); let reverse = args.is_present("reverse");
let unique = args.is_present("unique"); let unique = args.is_present("unique");
let osc52 = args.is_present("osc52");
let contrast = args.is_present("contrast"); let contrast = args.is_present("contrast");
let regexp = if let Some(items) = args.values_of("regexp") { let regexp = if let Some(items) = args.values_of("regexp") {
items.collect::<Vec<_>>() items.collect::<Vec<_>>()
@ -182,6 +190,32 @@ fn main() {
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n"); .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 { if let Some(target) = target {
let mut file = OpenOptions::new() let mut file = OpenOptions::new()
.create(true) .create(true)