Skip to content

Commit

Permalink
New set of available keys (Ctrl, Alt)
Browse files Browse the repository at this point in the history
A part of the common `tmux-thumbs` hints + the shift ones, we want to
introduce a new set of combinations with `ctrl` and `alt` keys.

This is a breaking change because the configuration options has been
renamed.
  • Loading branch information
fcsonline committed Jun 26, 2022
1 parent 50b8cc8 commit e46c9a2
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 60 deletions.
26 changes: 23 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,19 @@ These are the list of matched patterns that will be highlighted by default. If
you want to highlight a pattern that is not in this list you can add one or
more with `--regexp` parameter.


## Key shortcuts

While in **[thumbs]** mode, you can use the following shortcuts:

* <kbd>a</kbd>-<kbd>z</kbd>: copies selected match to the clipboard
* <kbd>CTRL</kbd> + <kbd>a</kbd>-<kbd>z</kbd>: copies selected match to the clipboard and triggers [@thumbs-ctrl-action](#thumbs-ctrl-action).
* <kbd>SHIFT</kbd> + <kbd>a</kbd>-<kbd>z</kbd>: copies selected match to the clipboard and triggers [@thumbs-shift-action](#thumbs-shift-action).
* <kbd>ALT</kbd> + <kbd>a</kbd>-<kbd>z</kbd>: copies selected match to the clipboard and triggers [@thumbs-alt-action](#thumbs-alt-action).
* <kbd>CTRL</kbd> + <kbd>c</kbd>: exit **[thumbs]** mode
* <kbd>ESC</kbd>: exit help or **[thumbs]** mode
* <kbd>?</kbd>: show help.

## Demo

[![demo](https://asciinema.org/a/232775.png?ts=1)](https://asciinema.org/a/232775?autoplay=1)
Expand Down Expand Up @@ -93,9 +106,13 @@ NOTE: for changes to take effect, you'll need to source again your `.tmux.conf`
* [@thumbs-unique](#thumbs-unique)
* [@thumbs-position](#thumbs-position)
* [@thumbs-regexp-N](#thumbs-regexp-N)
* [@thumbs-command](#thumbs-command)
* [@thumbs-upcase-command](#thumbs-upcase-command)
* [@thumbs-multi-command](#thumbs-multi-command)

* [@thumbs-main-action](#thumbs-main-action)
* [@thumbs-shift-action](#thumbs-shift-action)
* [@thumbs-ctrl-action](#thumbs-ctrl-action)
* [@thumbs-alt-action](#thumbs-alt-action)
* [@thumbs-multi-action](#thumbs-multi-action)

* [@thumbs-bg-color](#thumbs-bg-color)
* [@thumbs-fg-color](#thumbs-fg-color)
* [@thumbs-hint-bg-color](#thumbs-hint-bg-color)
Expand All @@ -106,6 +123,9 @@ NOTE: for changes to take effect, you'll need to source again your `.tmux.conf`
* [@thumbs-multi-bg-color](#thumbs-multi-bg-color)
* [@thumbs-contrast](#thumbs-contrast)
* [@thumbs-osc52](#thumbs-osc52)
* deprecated: [@thumbs-command](#thumbs-command)
* deprecated: [@thumbs-upcase-command](#thumbs-upcase-command)
* deprecated: [@thumbs-multi-command](#thumbs-multi-command)

### @thumbs-key

Expand Down
8 changes: 3 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn app_args<'a>() -> clap::ArgMatches<'a> {
)
.arg(
Arg::with_name("format")
.help("Specifies the out format for the picked hint. (%U: Upcase, %H: Hint)")
.help("Specifies the out format for the picked hint. (%K: Key, %H: Hint)")
.long("format")
.short("f")
.default_value("%H"),
Expand Down Expand Up @@ -198,12 +198,10 @@ fn main() {
if !selected.is_empty() {
let output = selected
.iter()
.map(|(text, upcase)| {
let upcase_value = if *upcase { "true" } else { "false" };

.map(|(text, modkey)| {
let mut output = format.to_string();

output = str::replace(&output, "%U", upcase_value);
output = str::replace(&output, "%K", modkey);
output = str::replace(&output, "%H", text.as_str());
output
})
Expand Down
110 changes: 72 additions & 38 deletions src/swapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@ fn dbg(msg: &str) {
pub struct Swapper<'a> {
executor: Box<&'a mut dyn Executor>,
dir: String,
command: String,
upcase_command: String,
multi_command: String,
main_action: String,
shift_action: String,
ctrl_action: String,
alt_action: String,
multi_action: String,
osc52: bool,
active_pane_id: Option<String>,
active_pane_height: Option<i32>,
Expand All @@ -75,9 +77,11 @@ impl<'a> Swapper<'a> {
fn new(
executor: Box<&'a mut dyn Executor>,
dir: String,
command: String,
upcase_command: String,
multi_command: String,
main_action: String,
shift_action: String,
ctrl_action: String,
alt_action: String,
multi_action: String,
osc52: bool,
) -> Swapper {
let since_the_epoch = SystemTime::now()
Expand All @@ -88,9 +92,11 @@ impl<'a> Swapper<'a> {
Swapper {
executor,
dir,
command,
upcase_command,
multi_command,
main_action,
shift_action,
ctrl_action,
alt_action,
multi_action,
osc52,
active_pane_id: None,
active_pane_height: None,
Expand Down Expand Up @@ -215,7 +221,7 @@ impl<'a> Swapper<'a> {
};

let pane_command = format!(
"tmux capture-pane -t {active_pane_id} -p{scroll_params} | tail -n {height} | {dir}/target/release/thumbs -f '%U:%H' -t {tmp} {args}; tmux swap-pane -t {active_pane_id}; {zoom_command} tmux wait-for -S {signal}",
"tmux capture-pane -t {active_pane_id} -p{scroll_params} | tail -n {height} | {dir}/target/release/thumbs -f '%K:%H' -t {tmp} {args}; tmux swap-pane -t {active_pane_id}; {zoom_command} tmux wait-for -S {signal}",
active_pane_id = active_pane_id,
scroll_params = scroll_params,
height = self.active_pane_height.unwrap_or(i32::MAX),
Expand Down Expand Up @@ -320,7 +326,7 @@ impl<'a> Swapper<'a> {
.collect::<Vec<&str>>()
.join(" ");

self.execute_final_command(&text, &self.multi_command.clone());
self.execute_final_command(&text, "MAIN", &self.multi_action.clone());

return;
}
Expand All @@ -330,7 +336,7 @@ impl<'a> Swapper<'a> {

let mut splitter = item.splitn(2, ':');

if let Some(upcase) = splitter.next() {
if let Some(modkey) = splitter.next() {
if let Some(text) = splitter.next() {
if self.osc52 {
let base64_text = base64::encode(text.as_bytes());
Expand Down Expand Up @@ -360,10 +366,13 @@ impl<'a> Swapper<'a> {
std::io::stdout().flush().unwrap();
}

let execute_command = if upcase.trim_end() == "true" {
self.upcase_command.clone()
} else {
self.command.clone()
let modkey = modkey.trim_end();

let execute_command = match modkey {
"SHIFT" => self.shift_action.clone(),
"CTRL" => self.ctrl_action.clone(),
"ALT" => self.alt_action.clone(),
_ => self.main_action.clone(),
};

// The command we run has two arguments:
Expand All @@ -389,19 +398,20 @@ impl<'a> Swapper<'a> {
// Ideally user commands would just use "${THUMB}" to begin with rather than having any
// sort of ad-hoc string splicing here at all, and then they could specify the quoting they
// want, but that would break backwards compatibility.
self.execute_final_command(text.trim_end(), &execute_command);
self.execute_final_command(text.trim_end(), modkey, &execute_command);
}
}
}

pub fn execute_final_command(&mut self, text: &str, execute_command: &str) {
pub fn execute_final_command(&mut self, text: &str, modkey: &str, execute_command: &str) {
let final_command = str::replace(execute_command, "{}", "${THUMB}");
let retrieve_command = vec![
"bash",
"-c",
"THUMB=\"$1\"; eval \"$2\"",
"THUMB=\"$1\"; MODIFIER=\"$2\"; eval \"$3\"",
"--",
text,
modkey,
final_command.as_str(),
];

Expand Down Expand Up @@ -450,6 +460,8 @@ mod tests {
"".to_string(),
"".to_string(),
"".to_string(),
"".to_string(),
"".to_string(),
false,
);

Expand All @@ -473,6 +485,8 @@ mod tests {
"".to_string(),
"".to_string(),
"".to_string(),
"".to_string(),
"".to_string(),
false,
);

Expand All @@ -490,15 +504,19 @@ mod tests {
let last_command_outputs = vec!["Blah blah blah, the ignored user script output".to_string()];
let mut executor = TestShell::new(last_command_outputs);

let user_command = "echo \"{}\"".to_string();
let upcase_command = "open \"{}\"".to_string();
let multi_command = "open \"{}\"".to_string();
let main_action = "echo \"{}\"".to_string();
let shift_action = "open \"{}\"".to_string();
let ctrl_action = "echo \"{}\"".to_string();
let alt_action = "echo \"{}\"".to_string();
let multi_action = "open \"{}\"".to_string();
let mut swapper = Swapper::new(
Box::new(&mut executor),
"".to_string(),
user_command,
upcase_command,
multi_command,
main_action,
shift_action,
ctrl_action,
alt_action,
multi_action,
false,
);

Expand Down Expand Up @@ -539,21 +557,33 @@ fn app_args<'a>() -> clap::ArgMatches<'a> {
.default_value(""),
)
.arg(
Arg::with_name("command")
Arg::with_name("main-action")
.help("Command to execute after choose a hint")
.long("command")
.long("main-action")
.default_value("tmux set-buffer -- \"{}\" && tmux display-message \"Copied {}\""),
)
.arg(
Arg::with_name("upcase_command")
.help("Command to execute after choose a hint, in upcase")
.long("upcase-command")
Arg::with_name("shift-action")
.help("Command to execute after choose a hint with SHIFT key pressed (Upcase)")
.long("shift-action")
.default_value("tmux set-buffer -- \"{}\" && tmux paste-buffer && tmux display-message \"Copied {}\""),
)
.arg(
Arg::with_name("multi_command")
Arg::with_name("ctrl-action")
.help("Command to execute after choose a hint with CTRL key pressed")
.long("ctrl-action")
.default_value("tmux display-message \"No CTRL action configured! Hint: {}\""),
)
.arg(
Arg::with_name("alt-action")
.help("Command to execute after choose a hint with ALT key pressed")
.long("alt-action")
.default_value("tmux display-message \"No ALT action configured! Hint: {}\" && echo \"FOO $MODIFIER ~ $HINT BAR\" > /tmp/tx.txt"),
)
.arg(
Arg::with_name("multi-action")
.help("Command to execute after choose multiple hints")
.long("multi-command")
.long("multi-action")
.default_value("tmux set-buffer -- \"{}\" && tmux paste-buffer && tmux display-message \"Multi copied {}\""),
)
.arg(
Expand All @@ -568,9 +598,11 @@ fn app_args<'a>() -> clap::ArgMatches<'a> {
fn main() -> std::io::Result<()> {
let args = app_args();
let dir = args.value_of("dir").unwrap();
let command = args.value_of("command").unwrap();
let upcase_command = args.value_of("upcase_command").unwrap();
let multi_command = args.value_of("multi_command").unwrap();
let main_action = args.value_of("main-action").unwrap();
let shift_action = args.value_of("shift-action").unwrap();
let ctrl_action = args.value_of("ctrl-action").unwrap();
let alt_action = args.value_of("alt-action").unwrap();
let multi_action = args.value_of("multi-action").unwrap();
let osc52 = args.is_present("osc52");

if dir.is_empty() {
Expand All @@ -581,9 +613,11 @@ fn main() -> std::io::Result<()> {
let mut swapper = Swapper::new(
Box::new(&mut executor),
dir.to_string(),
command.to_string(),
upcase_command.to_string(),
multi_command.to_string(),
main_action.to_string(),
shift_action.to_string(),
ctrl_action.to_string(),
alt_action.to_string(),
multi_action.to_string(),
osc52,
);

Expand Down
30 changes: 20 additions & 10 deletions src/view.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::*;
use std::char;
use std::io::{stdout, Read, Write};
use std::{char, vec};
use termion::async_stdin;
use termion::event::Key;
use termion::input::TermRead;
Expand All @@ -25,7 +25,7 @@ pub struct View<'a> {
background_color: Box<dyn color::Color>,
hint_background_color: Box<dyn color::Color>,
hint_foreground_color: Box<dyn color::Color>,
chosen: Vec<(String, bool)>,
chosen: Vec<(String, String)>,
}

enum CaptureEvent {
Expand Down Expand Up @@ -196,10 +196,10 @@ impl<'a> View<'a> {

loop {
match stdin.keys().next() {
Some(key) => {
match key {
Ok(key) => {
match key {
Some(original_key) => {
match original_key {
Ok(original_key) => {
match original_key {
Key::Esc => {
if self.multi && !typed_hint.is_empty() {
typed_hint.clear();
Expand All @@ -222,11 +222,11 @@ impl<'a> View<'a> {
Key::Backspace => {
typed_hint.pop();
}
Key::Char(ch) => {
Key::Char(ch) | Key::Alt(ch) | Key::Ctrl(ch) => {
match ch {
'\n' => match self.matches.iter().enumerate().find(|&h| h.0 == self.skip) {
Some(hm) => {
self.chosen.push((hm.1.text.to_string(), false));
self.chosen.push((hm.1.text.to_string(), "MAIN".to_string()));

if !self.multi {
return CaptureEvent::Hint;
Expand All @@ -251,9 +251,19 @@ impl<'a> View<'a> {

let selection = self.matches.iter().find(|mat| mat.hint == Some(typed_hint.clone()));

let modkey = if original_key == Key::Alt(ch) {
"ALT".to_string()
} else if original_key == Key::Ctrl(ch) {
"CTRL".to_string()
} else if key != lower_key {
"SHIFT".to_string()
} else {
"MAIN".to_string()
};

match selection {
Some(mat) => {
self.chosen.push((mat.text.to_string(), key != lower_key));
self.chosen.push((mat.text.to_string(), modkey));

if self.multi {
typed_hint.clear();
Expand Down Expand Up @@ -293,7 +303,7 @@ impl<'a> View<'a> {
CaptureEvent::Exit
}

pub fn present(&mut self) -> Vec<(String, bool)> {
pub fn present(&mut self) -> Vec<(String, String)> {
let mut stdin = async_stdin();
let mut stdout = AlternateScreen::from(stdout().into_raw_mode().unwrap());

Expand Down
9 changes: 5 additions & 4 deletions tmux-thumbs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ function add-param() {
fi
}

add-param command string
add-param upcase-command string
add-param multi-command string
add-param osc52 boolean
add-param main-action string
add-param shift-action string
add-param ctrl-action string
add-param alt-action string
add-param osc52 boolean

"${TMUX_THUMBS_BINARY}" "${PARAMS[@]}" || true

0 comments on commit e46c9a2

Please sign in to comment.