Skip to content

Commit e46c9a2

Browse files
committed
New set of available keys (Ctrl, Alt)
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.
1 parent 50b8cc8 commit e46c9a2

File tree

5 files changed

+123
-60
lines changed

5 files changed

+123
-60
lines changed

README.md

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,19 @@ These are the list of matched patterns that will be highlighted by default. If
3333
you want to highlight a pattern that is not in this list you can add one or
3434
more with `--regexp` parameter.
3535

36+
37+
## Key shortcuts
38+
39+
While in **[thumbs]** mode, you can use the following shortcuts:
40+
41+
* <kbd>a</kbd>-<kbd>z</kbd>: copies selected match to the clipboard
42+
* <kbd>CTRL</kbd> + <kbd>a</kbd>-<kbd>z</kbd>: copies selected match to the clipboard and triggers [@thumbs-ctrl-action](#thumbs-ctrl-action).
43+
* <kbd>SHIFT</kbd> + <kbd>a</kbd>-<kbd>z</kbd>: copies selected match to the clipboard and triggers [@thumbs-shift-action](#thumbs-shift-action).
44+
* <kbd>ALT</kbd> + <kbd>a</kbd>-<kbd>z</kbd>: copies selected match to the clipboard and triggers [@thumbs-alt-action](#thumbs-alt-action).
45+
* <kbd>CTRL</kbd> + <kbd>c</kbd>: exit **[thumbs]** mode
46+
* <kbd>ESC</kbd>: exit help or **[thumbs]** mode
47+
* <kbd>?</kbd>: show help.
48+
3649
## Demo
3750

3851
[![demo](https://asciinema.org/a/232775.png?ts=1)](https://asciinema.org/a/232775?autoplay=1)
@@ -93,9 +106,13 @@ NOTE: for changes to take effect, you'll need to source again your `.tmux.conf`
93106
* [@thumbs-unique](#thumbs-unique)
94107
* [@thumbs-position](#thumbs-position)
95108
* [@thumbs-regexp-N](#thumbs-regexp-N)
96-
* [@thumbs-command](#thumbs-command)
97-
* [@thumbs-upcase-command](#thumbs-upcase-command)
98-
* [@thumbs-multi-command](#thumbs-multi-command)
109+
110+
* [@thumbs-main-action](#thumbs-main-action)
111+
* [@thumbs-shift-action](#thumbs-shift-action)
112+
* [@thumbs-ctrl-action](#thumbs-ctrl-action)
113+
* [@thumbs-alt-action](#thumbs-alt-action)
114+
* [@thumbs-multi-action](#thumbs-multi-action)
115+
99116
* [@thumbs-bg-color](#thumbs-bg-color)
100117
* [@thumbs-fg-color](#thumbs-fg-color)
101118
* [@thumbs-hint-bg-color](#thumbs-hint-bg-color)
@@ -106,6 +123,9 @@ NOTE: for changes to take effect, you'll need to source again your `.tmux.conf`
106123
* [@thumbs-multi-bg-color](#thumbs-multi-bg-color)
107124
* [@thumbs-contrast](#thumbs-contrast)
108125
* [@thumbs-osc52](#thumbs-osc52)
126+
* deprecated: [@thumbs-command](#thumbs-command)
127+
* deprecated: [@thumbs-upcase-command](#thumbs-upcase-command)
128+
* deprecated: [@thumbs-multi-command](#thumbs-multi-command)
109129

110130
### @thumbs-key
111131

src/main.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ fn app_args<'a>() -> clap::ArgMatches<'a> {
3737
)
3838
.arg(
3939
Arg::with_name("format")
40-
.help("Specifies the out format for the picked hint. (%U: Upcase, %H: Hint)")
40+
.help("Specifies the out format for the picked hint. (%K: Key, %H: Hint)")
4141
.long("format")
4242
.short("f")
4343
.default_value("%H"),
@@ -198,12 +198,10 @@ fn main() {
198198
if !selected.is_empty() {
199199
let output = selected
200200
.iter()
201-
.map(|(text, upcase)| {
202-
let upcase_value = if *upcase { "true" } else { "false" };
203-
201+
.map(|(text, modkey)| {
204202
let mut output = format.to_string();
205203

206-
output = str::replace(&output, "%U", upcase_value);
204+
output = str::replace(&output, "%K", modkey);
207205
output = str::replace(&output, "%H", text.as_str());
208206
output
209207
})

src/swapper.rs

Lines changed: 72 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,11 @@ fn dbg(msg: &str) {
5858
pub struct Swapper<'a> {
5959
executor: Box<&'a mut dyn Executor>,
6060
dir: String,
61-
command: String,
62-
upcase_command: String,
63-
multi_command: String,
61+
main_action: String,
62+
shift_action: String,
63+
ctrl_action: String,
64+
alt_action: String,
65+
multi_action: String,
6466
osc52: bool,
6567
active_pane_id: Option<String>,
6668
active_pane_height: Option<i32>,
@@ -75,9 +77,11 @@ impl<'a> Swapper<'a> {
7577
fn new(
7678
executor: Box<&'a mut dyn Executor>,
7779
dir: String,
78-
command: String,
79-
upcase_command: String,
80-
multi_command: String,
80+
main_action: String,
81+
shift_action: String,
82+
ctrl_action: String,
83+
alt_action: String,
84+
multi_action: String,
8185
osc52: bool,
8286
) -> Swapper {
8387
let since_the_epoch = SystemTime::now()
@@ -88,9 +92,11 @@ impl<'a> Swapper<'a> {
8892
Swapper {
8993
executor,
9094
dir,
91-
command,
92-
upcase_command,
93-
multi_command,
95+
main_action,
96+
shift_action,
97+
ctrl_action,
98+
alt_action,
99+
multi_action,
94100
osc52,
95101
active_pane_id: None,
96102
active_pane_height: None,
@@ -215,7 +221,7 @@ impl<'a> Swapper<'a> {
215221
};
216222

217223
let pane_command = format!(
218-
"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}",
224+
"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}",
219225
active_pane_id = active_pane_id,
220226
scroll_params = scroll_params,
221227
height = self.active_pane_height.unwrap_or(i32::MAX),
@@ -320,7 +326,7 @@ impl<'a> Swapper<'a> {
320326
.collect::<Vec<&str>>()
321327
.join(" ");
322328

323-
self.execute_final_command(&text, &self.multi_command.clone());
329+
self.execute_final_command(&text, "MAIN", &self.multi_action.clone());
324330

325331
return;
326332
}
@@ -330,7 +336,7 @@ impl<'a> Swapper<'a> {
330336

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

333-
if let Some(upcase) = splitter.next() {
339+
if let Some(modkey) = splitter.next() {
334340
if let Some(text) = splitter.next() {
335341
if self.osc52 {
336342
let base64_text = base64::encode(text.as_bytes());
@@ -360,10 +366,13 @@ impl<'a> Swapper<'a> {
360366
std::io::stdout().flush().unwrap();
361367
}
362368

363-
let execute_command = if upcase.trim_end() == "true" {
364-
self.upcase_command.clone()
365-
} else {
366-
self.command.clone()
369+
let modkey = modkey.trim_end();
370+
371+
let execute_command = match modkey {
372+
"SHIFT" => self.shift_action.clone(),
373+
"CTRL" => self.ctrl_action.clone(),
374+
"ALT" => self.alt_action.clone(),
375+
_ => self.main_action.clone(),
367376
};
368377

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

397-
pub fn execute_final_command(&mut self, text: &str, execute_command: &str) {
406+
pub fn execute_final_command(&mut self, text: &str, modkey: &str, execute_command: &str) {
398407
let final_command = str::replace(execute_command, "{}", "${THUMB}");
399408
let retrieve_command = vec![
400409
"bash",
401410
"-c",
402-
"THUMB=\"$1\"; eval \"$2\"",
411+
"THUMB=\"$1\"; MODIFIER=\"$2\"; eval \"$3\"",
403412
"--",
404413
text,
414+
modkey,
405415
final_command.as_str(),
406416
];
407417

@@ -450,6 +460,8 @@ mod tests {
450460
"".to_string(),
451461
"".to_string(),
452462
"".to_string(),
463+
"".to_string(),
464+
"".to_string(),
453465
false,
454466
);
455467

@@ -473,6 +485,8 @@ mod tests {
473485
"".to_string(),
474486
"".to_string(),
475487
"".to_string(),
488+
"".to_string(),
489+
"".to_string(),
476490
false,
477491
);
478492

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

493-
let user_command = "echo \"{}\"".to_string();
494-
let upcase_command = "open \"{}\"".to_string();
495-
let multi_command = "open \"{}\"".to_string();
507+
let main_action = "echo \"{}\"".to_string();
508+
let shift_action = "open \"{}\"".to_string();
509+
let ctrl_action = "echo \"{}\"".to_string();
510+
let alt_action = "echo \"{}\"".to_string();
511+
let multi_action = "open \"{}\"".to_string();
496512
let mut swapper = Swapper::new(
497513
Box::new(&mut executor),
498514
"".to_string(),
499-
user_command,
500-
upcase_command,
501-
multi_command,
515+
main_action,
516+
shift_action,
517+
ctrl_action,
518+
alt_action,
519+
multi_action,
502520
false,
503521
);
504522

@@ -539,21 +557,33 @@ fn app_args<'a>() -> clap::ArgMatches<'a> {
539557
.default_value(""),
540558
)
541559
.arg(
542-
Arg::with_name("command")
560+
Arg::with_name("main-action")
543561
.help("Command to execute after choose a hint")
544-
.long("command")
562+
.long("main-action")
545563
.default_value("tmux set-buffer -- \"{}\" && tmux display-message \"Copied {}\""),
546564
)
547565
.arg(
548-
Arg::with_name("upcase_command")
549-
.help("Command to execute after choose a hint, in upcase")
550-
.long("upcase-command")
566+
Arg::with_name("shift-action")
567+
.help("Command to execute after choose a hint with SHIFT key pressed (Upcase)")
568+
.long("shift-action")
551569
.default_value("tmux set-buffer -- \"{}\" && tmux paste-buffer && tmux display-message \"Copied {}\""),
552570
)
553571
.arg(
554-
Arg::with_name("multi_command")
572+
Arg::with_name("ctrl-action")
573+
.help("Command to execute after choose a hint with CTRL key pressed")
574+
.long("ctrl-action")
575+
.default_value("tmux display-message \"No CTRL action configured! Hint: {}\""),
576+
)
577+
.arg(
578+
Arg::with_name("alt-action")
579+
.help("Command to execute after choose a hint with ALT key pressed")
580+
.long("alt-action")
581+
.default_value("tmux display-message \"No ALT action configured! Hint: {}\" && echo \"FOO $MODIFIER ~ $HINT BAR\" > /tmp/tx.txt"),
582+
)
583+
.arg(
584+
Arg::with_name("multi-action")
555585
.help("Command to execute after choose multiple hints")
556-
.long("multi-command")
586+
.long("multi-action")
557587
.default_value("tmux set-buffer -- \"{}\" && tmux paste-buffer && tmux display-message \"Multi copied {}\""),
558588
)
559589
.arg(
@@ -568,9 +598,11 @@ fn app_args<'a>() -> clap::ArgMatches<'a> {
568598
fn main() -> std::io::Result<()> {
569599
let args = app_args();
570600
let dir = args.value_of("dir").unwrap();
571-
let command = args.value_of("command").unwrap();
572-
let upcase_command = args.value_of("upcase_command").unwrap();
573-
let multi_command = args.value_of("multi_command").unwrap();
601+
let main_action = args.value_of("main-action").unwrap();
602+
let shift_action = args.value_of("shift-action").unwrap();
603+
let ctrl_action = args.value_of("ctrl-action").unwrap();
604+
let alt_action = args.value_of("alt-action").unwrap();
605+
let multi_action = args.value_of("multi-action").unwrap();
574606
let osc52 = args.is_present("osc52");
575607

576608
if dir.is_empty() {
@@ -581,9 +613,11 @@ fn main() -> std::io::Result<()> {
581613
let mut swapper = Swapper::new(
582614
Box::new(&mut executor),
583615
dir.to_string(),
584-
command.to_string(),
585-
upcase_command.to_string(),
586-
multi_command.to_string(),
616+
main_action.to_string(),
617+
shift_action.to_string(),
618+
ctrl_action.to_string(),
619+
alt_action.to_string(),
620+
multi_action.to_string(),
587621
osc52,
588622
);
589623

src/view.rs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::*;
2-
use std::char;
32
use std::io::{stdout, Read, Write};
3+
use std::{char, vec};
44
use termion::async_stdin;
55
use termion::event::Key;
66
use termion::input::TermRead;
@@ -25,7 +25,7 @@ pub struct View<'a> {
2525
background_color: Box<dyn color::Color>,
2626
hint_background_color: Box<dyn color::Color>,
2727
hint_foreground_color: Box<dyn color::Color>,
28-
chosen: Vec<(String, bool)>,
28+
chosen: Vec<(String, String)>,
2929
}
3030

3131
enum CaptureEvent {
@@ -196,10 +196,10 @@ impl<'a> View<'a> {
196196

197197
loop {
198198
match stdin.keys().next() {
199-
Some(key) => {
200-
match key {
201-
Ok(key) => {
202-
match key {
199+
Some(original_key) => {
200+
match original_key {
201+
Ok(original_key) => {
202+
match original_key {
203203
Key::Esc => {
204204
if self.multi && !typed_hint.is_empty() {
205205
typed_hint.clear();
@@ -222,11 +222,11 @@ impl<'a> View<'a> {
222222
Key::Backspace => {
223223
typed_hint.pop();
224224
}
225-
Key::Char(ch) => {
225+
Key::Char(ch) | Key::Alt(ch) | Key::Ctrl(ch) => {
226226
match ch {
227227
'\n' => match self.matches.iter().enumerate().find(|&h| h.0 == self.skip) {
228228
Some(hm) => {
229-
self.chosen.push((hm.1.text.to_string(), false));
229+
self.chosen.push((hm.1.text.to_string(), "MAIN".to_string()));
230230

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

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

254+
let modkey = if original_key == Key::Alt(ch) {
255+
"ALT".to_string()
256+
} else if original_key == Key::Ctrl(ch) {
257+
"CTRL".to_string()
258+
} else if key != lower_key {
259+
"SHIFT".to_string()
260+
} else {
261+
"MAIN".to_string()
262+
};
263+
254264
match selection {
255265
Some(mat) => {
256-
self.chosen.push((mat.text.to_string(), key != lower_key));
266+
self.chosen.push((mat.text.to_string(), modkey));
257267

258268
if self.multi {
259269
typed_hint.clear();
@@ -293,7 +303,7 @@ impl<'a> View<'a> {
293303
CaptureEvent::Exit
294304
}
295305

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

tmux-thumbs.sh

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,10 @@ function add-param() {
4545
fi
4646
}
4747

48-
add-param command string
49-
add-param upcase-command string
50-
add-param multi-command string
51-
add-param osc52 boolean
48+
add-param main-action string
49+
add-param shift-action string
50+
add-param ctrl-action string
51+
add-param alt-action string
52+
add-param osc52 boolean
5253

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

0 commit comments

Comments
 (0)