From 3d4c3d33652cf0eb73cef301598ac6fd10a02b5a Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Wed, 2 Jun 2021 11:11:04 +0200 Subject: Rename the extension to reselect. url-picker is no longer appropriate as other patterns beside URLs will be supported. --- reselect | 234 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 reselect (limited to 'reselect') diff --git a/reselect b/reselect new file mode 100644 index 0000000..ba14191 --- /dev/null +++ b/reselect @@ -0,0 +1,234 @@ +#! perl + +# Author: Chip Camden + +my $re_url = + qr{ + (?:https?://|ftp://|news://|mailto:|file://|\bwww\.) + [\w\-\@;\/?:&=%\$.+!*\x27,~#]* + ( + \([\w\-\@;\/?:&=%\$.+!*\x27,~#]*\)| # Allow a pair of matched parentheses + [\w\-\@;\/?:&=%\$+*~] # exclude some trailing characters (heuristic) + )+ + }x; + +sub find_matches { + my ($text, $rowmap) = @_; + + my @overlays; + while ($text =~ /$re_url/g) { + my $ndx = $-[0]; + my $href = $&; + my $col = 0; + my $row = 0; + + for my $key (keys %$rowmap) { + my $value = $rowmap->{$key}; + my ($start, $end) = @$value; + if (($start <= $ndx) && ($end >= $ndx)) { + $row = $key; + $col = $ndx - $start; + last; + } + } + my @ov = ($col, $row, $href); + push(@overlays, \@ov) if ($row >= 0); + } + + return @overlays; +} + +sub build_overlays { + my ($self, $text, $rowmap) = @_; + + my $label_rend = $self->get_rend("label", urxvt::OVERLAY_RSTYLE); + my $num = 0; + my $labels = {}; + my $hrefs = {}; + + my @overlays = find_matches($text, $rowmap); + @overlays = reverse @overlays if ($self->{descending}); + + for my $ov (@overlays) { + my ($col, $row, $href) = @$ov; + $num++; + my $overlay = $self->overlay($col, $row, $self->strwidth($num), + 1, $label_rend, 0); + $overlay->set(0, 0, $num); + + $labels->{$num} = $overlay; + $hrefs->{$num} = $href; + } + + return ($num, $labels, $hrefs) +}; + +sub on_user_command { + my ($self, $cmd) = @_; + if ($cmd =~ s/^reselect\b//) { + my $rowmap = {}; + my $row = 0; + my $base_col = 0; + my $text = ''; + + my ($brow, $bcol) = $self->selection_beg(); + my ($erow, $ecol) = $self->selection_end(); + my $issel = ($ecol > $bcol) || ($erow > $brow); + if ($issel) { # restrict to selection if one exists + ($row, $base_col) = ($brow - $self->view_start, $bcol); + for (split(/\n/, $self->selection())) { + my $start = length($text) - $base_col; + $text .= $_; + $rowmap->{$row} = [$start, (length($text)-1)]; + $base_col = 0; + $row++; + } + } else { # no selection, use visible terminal + for (0..($self->nrow - 1)) { + $row = $_; + my $start = length($text); + $text .= $self->ROW_t($row + $self->view_start); + $rowmap->{$row} = [$start, (length($text)-1)]; + } + } + + my ($num, $labels, $hrefs) = $self->build_overlays($text, $rowmap); + + if ($num < 1) { + my $desc = $issel ? "in visible selected text" : "on visible screen"; + $self->status_msg("url-picker: no URLs found $desc"); + } else { + my $url_picker = {}; + $url_picker->{prompt} = $self->overlay( + 0, -1, 8, 1, $self->get_rend("prompt", urxvt::OVERLAY_RSTYLE), 0 + ); + $url_picker->{prompt}->set(0,0,"Follow:"); + $url_picker->{labels} = $labels; + $url_picker->{hrefs} = $hrefs; + $url_picker->{num} = $num; + $url_picker->{buffer} = ''; + my ($crow,$ccol) = $self->screen_cur; + $url_picker->{crow} = $crow; + $url_picker->{ccol} = $ccol; + $self->{url_picker} = $url_picker; + $self->update($url_picker); + } + } + () +} + +sub on_key_press { + my ($self, $event, $keysym) = @_; + if ($self->{url_picker_msg}) { + $self->{url_picker_msg} = (); + } + my $p = $self->{url_picker}; + if ($p) { + if ($keysym == 0xff1b || $keysym == 113) { # escape or q + $self->screen_cur($p->{crow},$p->{ccol}); + $self->{url_picker} = (); + } elsif ($keysym == 0xff08) { # backspace + if (length($p->{buffer}) > 0) { + $p->{buffer} = substr($p->{buffer},0,-1); + $self->update($p); + } + } elsif (($keysym >= 48) && ($keysym <= 57)) { + $p->{buffer} = $p->{buffer} . ($keysym - 48); + $self->update($p); + } elsif ($keysym == 0xff0d) { # CR + my $num = $p->{buffer}; + my $hrefs = $p->{hrefs}; + if (($num > 0) && ($num <= $p->{num})) { + my $href = $hrefs->{$num}; + $self->launch($href); + } + } + return 1; + } + () +} + +sub update { + my ($self, $p) = @_; + $p->{typing} = $self->overlay( + 8, -1, length($p->{buffer}), 1, $self->get_rend("input", urxvt::DEFAULT_RSTYLE), 0 + ); + $p->{typing}->set(0,0,$p->{buffer}); + my $ndx = 0; + my $labels = $p->{labels}; + my $hrefs = $p->{hrefs}; + my $len = length($p->{buffer}); + my $size = $p->{num}; + my @matches; + while (++$ndx <= $size) { + my $overlay = $labels->{$ndx}; + if (($len == 0) || + (($len <= length($ndx)) && (substr($ndx,0,$len) eq $p->{buffer}))) { + $overlay->show; + unshift @matches,$hrefs->{$ndx}; + } else { + $overlay->hide; + } + } + # Auto-launch a single url only if the launchsingle resource is true + # (default), or if some digits were input + if (scalar(@matches) == 1 && ($self->{launchsingle} || $len > 0)) { + $self->launch(@matches[0]); + } else { + $self->screen_cur($self->nrow,8+$len); + } +} + +sub launch { + my ($self, $href) = @_; + my $p = $self->{url_picker}; + $self->screen_cur($p->{crow},$p->{ccol}); + $self->{url_picker} = (); + my $launcher = $self->{launcher}; + $self->status_msg($href); + $self->exec_async ($launcher,$href); +} + +sub status_msg { + my ($self, $msg) = @_; + $self->{url_picker_msg} = $self->overlay(0, -1, length($msg), 1, + $self->get_rend("status",urxvt::OVERLAY_RSTYLE), 0); + $self->{url_picker_msg}->set(0,0,$msg); + $self->{url_picker_timer} = urxvt::timer + ->new + ->after (5) + ->cb (sub { + $self->{url_picker_msg} = (); + $self->{url_pickertimer} = (); + }); +} + +sub get_rend { + my ($self, $name, $default) = @_; + urxvt::SET_COLOR $default, + $self->my_resource("$name.foregroundColor") || urxvt::GET_BASEFG $default, + $self->my_resource("$name.backgroundColor") || urxvt::GET_BASEBG $default; +} + +sub on_key_release { + my ($self, $event, $keysym) = @_; + $self->{url_picker}; +} + +sub my_resource { + my ($self, $name) = @_; + $self->x_resource ("$self->{name}.$name"); +} + +sub on_start { + my ($self) = @_; + + ($self->{name} = __PACKAGE__) =~ s/.*:://; + $self->{name} =~ tr/_/-/; + $self->{launcher} = $self->my_resource("launcher") || + $self->x_resource("url-launcher") || + "sensible-browser"; + $self->{launchsingle} = ($self->my_resource("launchsingle") ne "false"); + $self->{descending} = ($self->my_resource("order") eq "descending"); + $self->{url_picker} = (); +} -- cgit v1.2.3