diff options
| -rw-r--r-- | .tmux.conf | 1 | ||||
| -rw-r--r-- | bin/.gitignore | 1 | ||||
| -rwxr-xr-x | bin/tmux-url-select | 214 | 
3 files changed, 216 insertions, 0 deletions
@@ -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(); +}  | 
