summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTharre <tharre3@gmail.com>2018-05-09 03:11:58 +0200
committerTharre <tharre3@gmail.com>2018-05-09 17:28:39 +0200
commit6cbeea113bb806067d1931d4dbcedc2b6c229bfb (patch)
treece2c371572a918f0cbeaeab186221420a18025bc
parentc9d6df5d6652e24a07d3a5fac558e6f746b1363f (diff)
downloaddotfiles-6cbeea113bb806067d1931d4dbcedc2b6c229bfb.tar.gz
dotfiles-6cbeea113bb806067d1931d4dbcedc2b6c229bfb.tar.xz
dotfiles-6cbeea113bb806067d1931d4dbcedc2b6c229bfb.zip
tmux: add tmux-url-select
-rw-r--r--.tmux.conf1
-rw-r--r--bin/.gitignore1
-rwxr-xr-xbin/tmux-url-select214
3 files changed, 216 insertions, 0 deletions
diff --git a/.tmux.conf b/.tmux.conf
index 86822a5..26fdfa2 100644
--- a/.tmux.conf
+++ b/.tmux.conf
@@ -13,5 +13,6 @@ set-option -g set-titles on
set-option -g set-titles-string "#W"
bind-key r source-file ~/.tmux.conf \; display "Config reloaded!"
+bind z run tmux-url-select
if-shell "test -e ~/.tmux.conf.local" "source-file ~/.tmux.conf.local"
diff --git a/bin/.gitignore b/bin/.gitignore
index 5c976d1..34e412a 100644
--- a/bin/.gitignore
+++ b/bin/.gitignore
@@ -7,5 +7,6 @@
!set_xdg_dirs
!setup_borgbackup
!suspend_after
+!tmux-url-select
!update_dotfiles
!ut8font_test
diff --git a/bin/tmux-url-select b/bin/tmux-url-select
new file mode 100755
index 0000000..2a8b839
--- /dev/null
+++ b/bin/tmux-url-select
@@ -0,0 +1,214 @@
+#!/usr/bin/env perl
+#
+# tmux-url-select
+# mit licensed
+#
+# https://github.com/dequis/tmux-url-select
+
+use strict;
+use warnings;
+
+### config
+
+use constant COMMAND => 'xdg-open %s';
+use constant YANK_COMMAND => 'echo %s | xclip -i';
+
+use constant SHOW_STATUS_BAR => 1;
+use constant VERBOSE_MESSAGES => 0;
+use constant TMUX_WINDOW_TITLE => 'Select URL';
+use constant TMUX_WINDOW_ID => 9999;
+use constant HIDE_WINDOW => 1;
+
+use constant PROMPT_COLOR => "\033[42;30m";
+use constant ACTIVE_LINK_HIGHLIGHT => "\033[44;4m";
+use constant NORMAL_LINK_HIGHLIGHT => "\033[94;1;4m";
+
+# other options:
+# - blue background, underlined: \033[44;4m
+# - 256 color term light blue: \033[38;5;39m
+# - bold bright blue: \033[94;1;4m
+# - bright blue background: \033[104;4m
+# - just underlined: \033[4m
+
+# regex stolen from urxvtperls url-select.pl
+my $url_pattern = qr{(
+ (?:https?://|ftp://|news://|git://|mailto:|file://|www\.)
+ [\w\-\@;\/?:&=%\$_.+!*\x27(),~#\x1b\[\]]+[\w\-\@;\/?&=%\$_+!*\x27(~]
+)}x;
+
+### config end
+
+my $raw_buffer;
+my $buffer;
+my $buffer_first_newline_position;
+my @matches;
+my $match_count;
+my $selection = 0;
+
+# terminal helper functions
+
+sub clear {
+ print "\033[H\033[2J";
+}
+
+sub display_status_bar {
+ my $is_first_line = shift;
+ my $position = $is_first_line ? "2;2" : "1;2";
+ print sprintf("\033[%sH%s URL select: (%s/%s) [j/k/y/o/q/enter] \033[0m", $position, PROMPT_COLOR, $selection+1, $match_count);
+}
+
+sub display_highlighted_buffer {
+ my $i = 0;
+ my $is_first_line = 0;
+ my $cb = sub {
+ if ($i++ == $selection) {
+ $is_first_line = 1 if ($+[0] < $buffer_first_newline_position);
+ return ACTIVE_LINK_HIGHLIGHT."$1\033[0m";
+ return;
+ }
+ return NORMAL_LINK_HIGHLIGHT."$1\033[0m" if NORMAL_LINK_HIGHLIGHT;
+ return $1;
+ };
+ print $buffer =~ s/($url_pattern)/&$cb()/ger;
+ return $is_first_line;
+}
+
+sub display_stuff {
+ clear();
+ my $is_first_line = display_highlighted_buffer();
+ display_status_bar($is_first_line) if SHOW_STATUS_BAR;
+}
+
+# tmux command helpers
+
+sub tmux_display_message {
+ system 'tmux', 'display-message', shift;
+}
+
+sub tmux_switch_to_last {
+ system 'tmux', 'last-window';
+}
+
+sub tmux_select_my_window {
+ system "tmux", "select-window", "-t", TMUX_WINDOW_ID;
+}
+
+sub tmux_capture_pane {
+ system "tmux", "capture-pane", "-eJ";
+}
+
+sub tmux_get_buffer {
+ return `tmux show-buffer`;
+}
+
+sub tmux_open_inner_window {
+ system "tmux", "new-window", "-dn", "", "-t", TMUX_WINDOW_ID, "$0 inner";
+ system "tmux", "setw", "-qt", TMUX_WINDOW_ID, "window-status-format", "";
+ system "tmux", "setw", "-qt", TMUX_WINDOW_ID, "window-status-current-format", "";
+}
+
+# other shell helpers
+
+sub enable_canonical_mode {
+ # "canonical mode" to read char by char, thanks roger.
+ system "stty", "-icanon", "cbreak", "min", "1", "-echo";
+}
+
+sub single_quote_escape {
+ return "'".(shift =~ s/\'/%27/gr)."'";
+}
+
+# actions
+
+sub fix_url {
+ my $url = shift;
+ # some silly url openers think ^www. urls are files
+ $url = "http://".$url if $url =~ /^www\./;
+ # clear out color codes
+ $url =~ s/\x1b\[[0-9;]*m//g;
+ return $url;
+}
+
+sub safe_exec {
+ my ($command, $message) = @_;
+ $SIG{CHLD} = 'IGNORE';
+ $SIG{HUP} = 'IGNORE';
+
+ unless (fork) {
+ tmux_display_message($message) if VERBOSE_MESSAGES;
+ exec $command;
+ }
+}
+
+sub launch_url {
+ my $url = fix_url(shift);
+ tmux_switch_to_last() if shift;
+
+ my $command = sprintf(COMMAND, single_quote_escape($url));
+ safe_exec($command, "Launched ". $url);
+}
+
+sub yank_url {
+ my $url = fix_url(shift);
+ tmux_switch_to_last() if shift;
+ my $command = sprintf(YANK_COMMAND, single_quote_escape($url));
+ safe_exec($command, "Yanked ". $url);
+}
+
+# main functions
+
+sub main_inner {
+ $raw_buffer = tmux_get_buffer();
+ system "tmux delete-buffer";
+
+ $buffer = $raw_buffer =~ s/\n$//r;
+ $buffer_first_newline_position = index($raw_buffer, "\n");
+
+ @matches = ($buffer =~ /$url_pattern/g);
+ $match_count = @matches;
+ exit 1 if !$match_count;
+
+ $selection = $#matches;
+
+ display_stuff();
+
+ enable_canonical_mode();
+
+ # switch to the tmux-url-select window now to avoid 'flickering'
+ tmux_select_my_window();
+
+ # main loop
+ while(defined($_ = getc)) {
+ $selection++ if /[jB]/;
+ $selection-- if /[kA]/;
+ $selection = ($_-1) if /[0-9]/;
+ $selection %= $match_count;
+ my $do_return = /[qyo\n]/;
+ yank_url($matches[$selection], $do_return) if /[yY]/;
+ launch_url($matches[$selection], $do_return) if /[\noO]/;
+ return if $do_return;
+ display_stuff();
+ }
+}
+
+sub main {
+ tmux_capture_pane();
+
+ @matches = tmux_get_buffer() =~ /$url_pattern/g;
+ $match_count = @matches;
+
+ if (!$match_count) {
+ tmux_display_message("No URLs");
+ system "tmux delete-buffer";
+ exit 0;
+ }
+
+ # open window here, backgrounded
+ tmux_open_inner_window();
+}
+
+if (!@ARGV) {
+ main();
+} elsif ($ARGV[0] eq "inner") {
+ main_inner();
+}