citadel

My dotfiles, scripts and nix configs
git clone git://jb55.com/citadel
Log | Files | Refs | README | LICENSE

url-picker (6691B)


      1 #! perl
      2 
      3 # Author: Chip Camden <sterling@camdensoftware.com>
      4 
      5 my $url =
      6    qr{
      7       (?:https?://|ftp://|news://|mailto:|file://|\bwww\.)
      8       [a-zA-Z0-9\-\@;\/?:&=%\$_.+!*\x27,~#]*
      9       (
     10          # Allow a pair of matched parentheses
     11          \([a-zA-Z0-9\-\@;\/?:&=%\$_.+!*\x27,~#]*\)|
     12          # exclude some trailing characters (heuristic)
     13          [a-zA-Z0-9\-\@;\/?:&=%\$_+*~]
     14       )+
     15    }x;
     16 
     17 sub on_user_command {
     18   my ($self, $cmd) = @_;
     19   if ($cmd =~ s/^url-picker\b//) {
     20     my $labels = {};
     21     my $hrefs = {};
     22     my $rowmap = {};
     23     my $num = 0;
     24     my $row = 0;
     25     my $base_col = 0;
     26     my $text = '';
     27     my $label_rend = $self->get_rend("label", urxvt::OVERLAY_RSTYLE);
     28     my $label_urls = sub {
     29                        my @overlays;
     30                        while ($text =~ /$url/g) {
     31                          my $ndx = $-[0];
     32                          my $href = $&;
     33                          my $col = 0;
     34                          for my $key (keys %$rowmap) {
     35                            my $value = $rowmap->{$key};
     36                            my ($start, $end) = @$value;
     37                            if (($start <= $ndx) && ($end >= $ndx)) {
     38                              $row = $key;
     39                              $col = $ndx - $start;
     40                              last;
     41                            }
     42                          }
     43                          my @ov = ($col, $row, $href);
     44                          push(@overlays, \@ov) if ($row >= 0);
     45                        }
     46                        @overlays = reverse @overlays if ($self->{descending});
     47                        for my $ov (@overlays) {
     48                          my ($col, $row, $href) = @$ov;
     49                          $num++;
     50                          my $overlay = $self->overlay(
     51                            $col, $row, $self->strwidth($num), 1, $label_rend, 0
     52                          );
     53                          $overlay->set(0,0,$num);
     54                          $labels->{$num} = $overlay;
     55                          $hrefs->{$num} = $href;
     56                        }
     57                      };
     58     my ($brow, $bcol) = $self->selection_beg();
     59     my ($erow, $ecol) = $self->selection_end();
     60     my $issel = ($ecol > $bcol) || ($erow > $brow);
     61     if ($issel) {         # restrict to selection if one exists
     62       ($row, $base_col) = ($brow - $self->view_start, $bcol);
     63       for (split(/\n/, $self->selection())) {
     64         my $start = length($text) - $base_col;
     65         $text .= $_;
     66         $rowmap->{$row} = [$start, (length($text)-1)];
     67         $base_col = 0;
     68         $row++;
     69       }
     70     } else {            # no selection, use visible terminal
     71       for (0..($self->nrow - 1)) {
     72         $row = $_;
     73         my $start = length($text);
     74         $text .= $self->ROW_t($row + $self->view_start);
     75         $rowmap->{$row} = [$start, (length($text)-1)];
     76       }
     77     }
     78     $label_urls->();
     79 
     80     if ($num < 1) {
     81       my $desc = $issel ? "in visible selected text" : "on visible screen";
     82       $self->status_msg("url-picker: no URLs found $desc");
     83     } else {
     84       my $url_picker = {};
     85       $url_picker->{prompt} = $self->overlay(
     86         0, -1, 8, 1, $self->get_rend("prompt", urxvt::OVERLAY_RSTYLE), 0
     87       );
     88       $url_picker->{prompt}->set(0,0,"Follow:");
     89       $url_picker->{labels} = $labels;
     90       $url_picker->{hrefs} = $hrefs;
     91       $url_picker->{num} = $num;
     92       $url_picker->{buffer} = '';
     93       my ($crow,$ccol) = $self->screen_cur;
     94       $url_picker->{crow} = $crow;
     95       $url_picker->{ccol} = $ccol;
     96       $self->{url_picker} = $url_picker;
     97       $self->update($url_picker);
     98     }
     99   }
    100   ()
    101 }
    102 
    103 sub on_key_press {
    104   my ($self, $event, $keysym) = @_;
    105   my $p = $self->{url_picker};
    106   if ($p) {
    107     if ($keysym == 0xff1b) {           # escape
    108       $self->screen_cur($p->{crow},$p->{ccol});
    109       $self->{url_picker} = ();
    110     } elsif ($keysym == 0xff08) {      # backspace
    111       if (length($p->{buffer}) > 0) {
    112         $p->{buffer} = substr($p->{buffer},0,-1);
    113         $self->update($p);
    114       }
    115     } elsif (($keysym >= 48) && ($keysym <= 57)) {
    116       $p->{buffer} = $p->{buffer} . ($keysym - 48);
    117       $self->update($p);
    118     } elsif ($keysym == 0xff0d) {      # CR
    119       my $num = $p->{buffer};
    120       my $hrefs = $p->{hrefs};
    121       if (($num > 0) && ($num <= $p->{num})) {
    122         my $href = $hrefs->{$num};
    123         $self->launch($href);
    124       }
    125     }
    126     return 1;
    127   }
    128   ()
    129 }
    130 
    131 sub update {
    132   my ($self, $p) = @_;
    133   $p->{typing} = $self->overlay(
    134     8, -1, length($p->{buffer}), 1, $self->get_rend("input", urxvt::DEFAULT_RSTYLE), 0
    135   );
    136   $p->{typing}->set(0,0,$p->{buffer});
    137   my $ndx = 0;
    138   my $labels = $p->{labels};
    139   my $hrefs = $p->{hrefs};
    140   my $len = length($p->{buffer});
    141   my $size = $p->{num};
    142   my @matches;
    143   while (++$ndx <= $size) {
    144     my $overlay = $labels->{$ndx};
    145     if (($len == 0) ||
    146         (($len <= length($ndx)) && (substr($ndx,0,$len) eq $p->{buffer}))) {
    147       $overlay->show;
    148       unshift @matches,$hrefs->{$ndx};
    149     } else {
    150       $overlay->hide;
    151     }
    152   }
    153   if (scalar(@matches) == 1) {
    154     $self->launch(@matches[0]);
    155   } else {
    156     $self->screen_cur($self->nrow,8+$len);
    157   }
    158 }
    159 
    160 sub launch {
    161   my ($self, $href) = @_;
    162   my $p = $self->{url_picker};
    163   $self->screen_cur($p->{crow},$p->{ccol});
    164   $self->{url_picker} = ();
    165   my $launcher = $self->{launcher};
    166   $self->status_msg($href);
    167   $self->exec_async ($launcher,$href);
    168 }
    169 
    170 sub status_msg {
    171   my ($self, $msg) = @_;
    172   $self->{url_picker_msg} = $self->overlay(0,-1,length($msg),1,$self->get_rend("status",urxvt::OVERLAY_RSTYLE),0);
    173   $self->{url_picker_msg}->set(0,0,$msg);
    174   $self->{url_picker_timer} = urxvt::timer
    175                             ->new
    176                             ->after (5)
    177                             ->cb (sub {
    178                                      $self->{url_picker_msg} = ();
    179                                      $self->{url_pickertimer} = ();
    180                                    });
    181 }
    182 
    183 sub get_rend {
    184   my ($self, $name, $default) = @_;
    185   urxvt::SET_COLOR $default,
    186     $self->my_resource("$name.foregroundColor") || urxvt::GET_BASEFG $default,
    187     $self->my_resource("$name.backgroundColor") || urxvt::GET_BASEBG $default;
    188 }
    189 
    190 sub on_key_release {
    191   my ($self, $event, $keysym) = @_;
    192   $self->{url_picker};
    193 }
    194 
    195 sub my_resource {
    196   my ($self, $name) = @_;
    197   $self->x_resource ("$self->{name}.$name");
    198 }
    199 
    200 sub on_start {
    201   my ($self) = @_;
    202 
    203   ($self->{name} = __PACKAGE__) =~ s/.*:://;
    204   $self->{name} =~ tr/_/-/;
    205   $self->{launcher} = $self->my_resource("launcher") ||
    206                       $self->x_resource("url-launcher") ||
    207                       "sensible-browser";
    208   $self->{descending} = ($self->my_resource("order") eq "descending");
    209   $self->{url_picker} = ();
    210 }
    211 
    212 # vim:set sw=3 sts=3 et: