Hi

Why don't you send this to the urxvt mailing list. I for example will be
glad if this comes up in the next release and it will be a shame if it
gets lost in this mailing list. rxvt-unic...@lists.schmorp.de
seems to be the mailing list on the official page - you can try there.

Best, Nikola

On Wed, Oct 24, 2012 at 08:38:02PM +0300, Alex Efros wrote:
> Hi!
> 
> On Wed, Oct 24, 2012 at 06:09:03AM +0300, Alex Efros wrote:
> > Looks like mark-urls was removed and replaced by matcher in current urxvt
> > version. Anyway, both doesn't support urls wrapped in several lines.
> > Chip Camden already mention this in previous email, but I've just tested
> > both and confirm this. Probably matcher can be modified (as Chip Camden
> > suggest) to support wrapped lines, but right now it doesn't work in this 
> > way.
> 
> I've implemented this feature in matcher plugin. Modified version and
> patch attached (I'm using rxvt-unicode-9.15).
> 
> I've implemented only url-highlighting and clicking support. It looks like
> matcher also support some "list" feature, but I've no idea what is it for
> and thus I didn't tried to add multiline match support for it.
> 
> It would be nice to have some notification when url clicked and opened in
> browser, because this happens in background and inside mutt I didn't know
> is it was successfully opened or I clicked on wrong place and nothing
> happens - probably some status bar notification pop-up for 1 sec is the
> best. Is it possible to implement this in urxvt?
> 
> 
> Is it possible to somehow install modified version of matcher in my home
> dir instead of replacing system-wide version?
> 
> If there is recommended way to send patches to urxvt please let me know.
> 
> Now, I have to figure out how to configure urxvt in same way as my xterm
> was configured…
> 
> -- 
>                       WBR, Alex.


> #! perl
> 
> # Author: Tim Pope <rxvt-unicodenos...@tpope.org>
> #          Bob Farrell <robertanthonyfarr...@gmail.com>
> 
> my $url =
>    qr{
>       (?:https?://|ftp://|news://|mailto:|file://|\bwww\.)
>       [a-zA-Z0-9\-\@;\/?:&=%\$_.+!*\x27,~#]*
>       (
>          \([a-zA-Z0-9\-\@;\/?:&=%\$_.+!*\x27,~#]*\)| # Allow a pair of 
> matched parentheses
>          [a-zA-Z0-9\-\@;\/?:&=%\$_+*~]  # exclude some trailing characters 
> (heuristic)
>       )+
>    }x;
> 
> sub on_key_press {
>    my ($self, $event, $keysym, $octets) = @_;
> 
>    if (! $self->{showing} ) {
>       return;
>    }
> 
>    my $i = ($keysym == 96 ? 0 : $keysym - 48);
>    if (($i > scalar(@{$self->{urls}})) || ($i < 0)) {
>       $self->matchlist();
>       return;
>    }
> 
>    my @args = ($self->{urls}[ -$i-1 ]);
>    $self->matchlist();
> 
>    $self->exec_async( $self->{launcher}, @args );
> }
> 
> sub on_user_command {
>    my ($self, $cmd) = @_;
> 
>    if($cmd =~ s/^matcher:list\b//) {
>       $self->matchlist();
>    } else {
>       if($cmd =~ s/^matcher:last\b//) {
>          $self->most_recent;
>       }
>    # For backward compatibility
>     else {
>       if($cmd =~ s/^matcher\b//) {
>          $self->most_recent;
>       }
>    }
>   }
>    ()
> }
> 
> sub matchlist {
>    my ($self) = @_;
>    if ( $self->{showing} ) {
>      $self->{url_overlay}->hide();
>      $self->{showing} = 0;
>      return;
>    }
>   @{$self->{urls}} = ();
>   my $line;
>   for (my $i = 0; $i < $self->nrow; $i ++) {
>      $line = $self->line($i);
>      next if ($line->beg != $i);
>      for my $url ($self->get_urls_from_line($line->t)) {
>         if (scalar(@{$self->{urls}}) == 10) {
>             shift @{$self->{urls}};
>         }
>         push @{$self->{urls}}, $url;
>      }
>   }
> 
>   if (! scalar(@{$self->{urls}})) {
>     return;
>   }
> 
>   my $max = 0;
>   my $i = scalar( @{$self->{urls}} ) - 1 ;;
> 
>   my @temp = ();
> 
>   for my $url (@{$self->{urls}}) {
>      my $url = "$i-$url";
>      my $xpos = 0;
> 
>      if ($self->ncol + (length $url) >= $self->ncol) {
>         $url = substr( $url, 0, $self->ncol );
>      }
> 
>      push @temp, $url;
> 
>      if( length $url > $max ) {
>         $max = length $url;
>      }
> 
>      $i--;
>   }
> 
>   @temp = reverse @temp;
> 
>   $self->{url_overlay} = $self->overlay(0, 0, $max, scalar( @temp ), 
> urxvt::OVERLAY_RSTYLE, 2);
>   my $i = 0;
>   for my $url (@temp) {
>      $self->{url_overlay}->set( 0, $i, $url, [(urxvt::OVERLAY_RSTYLE) x 
> length $url]);
>       $self->{showing} = 1;
>      $i++;
>   }
> 
> }
> 
> sub most_recent {
>    my ($self) = shift;
>    my $row = $self->nrow;
>    my @exec;
>    while($row-- > $self->top_row) {
>       @exec = $self->command_for($row);
>       last if(@exec);
>    }
>    if(@exec) {
>       return $self->exec_async (@exec);
>    }
>    ()
> }
> 
> sub my_resource {
>    my $self = shift;
>    $self->x_resource ("$self->{name}.$_[0]");
> }
> 
> # turn a rendition spec in the resource into a sub that implements it on $_
> sub parse_rend {
>    my ($self, $str) = @_;
>    my ($mask, $fg, $bg, $failed) = $str ? urxvt::rend2mask($str)
>                                         : (urxvt::RS_Uline, undef, undef, []);
>    warn "Failed to parse rendition string: " . join(',', @$failed) if 
> @$failed;
>    my @rend;
>    push @rend, sub { $_ |= $mask } if $mask;
>    push @rend, sub { $_ = urxvt::SET_FGCOLOR($_, $fg) } if defined $fg;
>    push @rend, sub { $_ = urxvt::SET_BGCOLOR($_, $bg) } if defined $bg;
>    sub {
>       for my $s ( @rend ) { &$s };
>    }
> }
> 
> sub on_start {
>    my ($self) = @_;
> 
>    ($self->{name} = __PACKAGE__) =~ s/.*:://;
>    $self->{name} =~ tr/_/-/;
>    $self->{launcher} = $self->my_resource("launcher") ||
>                       $self->x_resource("urlLauncher") ||
>                       "sensible-browser";
> 
>    $self->{urls} = [];
>    $self->{showing} = 0;
>    $self->{button}  = 2;
>    $self->{state}   = 0;
>    if($self->{argv}[0] || $self->my_resource("button")) {
>       my @mods = split('', $self->{argv}[0] || $self->my_resource("button"));
>       for my $mod (@mods) {
>          if($mod =~ /^\d+$/) {
>             $self->{button} = $mod;
>          } elsif($mod eq "C") {
>             $self->{state} |= urxvt::ControlMask;
>          } elsif($mod eq "S") {
>             $self->{state} |= urxvt::ShiftMask;
>          } elsif($mod eq "M") {
>             $self->{state} |= $self->ModMetaMask;
>          } elsif($mod ne "-" && $mod ne " ") {
>             warn("$mod is invalid in $self->{name}<$self->{argv}[0]>\n");
>          }
>       }
>    }
> 
>    my @defaults = ($url);
>    my @matchers;
>    for (my $idx = 0; defined (my $res = $self->my_resource("pattern.$idx") || 
> $defaults[$idx]); $idx++) {
>       $res = $self->locale_decode ($res);
>       utf8::encode $res;
>       my $launcher = $self->my_resource("launcher.$idx");
>       $launcher =~ s/\$&|\$\{&\}/\${0}/g if ($launcher);
>       my $rend = $self->parse_rend($self->my_resource("rend.$idx"));
>       unshift @matchers, [qr($res)x,$launcher,$rend];
>    }
>    $self->{matchers} = \@matchers;
> 
>    ()
> }
> 
> sub get_urls_from_line {
>    my ($self, $line) = @_;
>    my @urls;
>    for my $matcher (@{$self->{matchers}}) {
>      while ($line =~ /$matcher->[0]/g) {
>         push @urls, substr( $line, $-[0], $+[0] - $-[0] );
>      }
>    }
>    return @urls;
> }
> 
> sub on_line_update {
>    my ($self, $row) = @_;
> 
>    # fetch the line (enlarged to adjoining lines) that has changed
>    my ($text, $prev_cols, $next_cols, @lines) = $self->enlarge($row);
> 
>    # find all urls (if any)
>    for my $matcher (@{$self->{matchers}}) {
>       $self->match($matcher->[0], $text, $prev_cols, $next_cols, \@lines, sub 
> {
>        for (@_) {
>           my ($line, $from, $to) = @$_;
>           my $rend = $line->r;
>           # mark all characters as underlined. we _must_ not toggle underline,
>           # as we might get called on an already-marked url.
>           &{$matcher->[2]}
>               for @{$rend}[ $from .. $to - 1];
>           $line->r($rend);
>        }
>       });
>    }
> 
>    ()
> }
> 
> sub valid_button {
>    my ($self, $event) = @_;
>    my $mask = $self->ModLevel3Mask | $self->ModMetaMask
>    | urxvt::ShiftMask | urxvt::ControlMask;
>    return ($event->{button} == $self->{button} &&
>       ($event->{state} & $mask) == $self->{state});
> }
> 
> sub command_for {
>    my ($self, $row, $col) = @_;
> 
>    # fetch the line (enlarged to adjoining lines) that has changed
>    my ($text, $prev_cols, $next_cols, @lines) = $self->enlarge($row);
> 
>    for my $matcher (@{$self->{matchers}}) {
>       my $launcher = $matcher->[1] || $self->{launcher};
>       my @exec;
>       $self->match($matcher->[0], $text, $prev_cols, $next_cols, \@lines, sub 
> {
>        my $hit = 0;
>        my $match = q{};
>        for (@_) {
>           my ($line, $from, $to) = @$_;
>           my $text = $line->t;
>           $match .= substr $text, $from, $to-$from;
>           if ($line->beg <= $row && $row <= $line->end) {
>              $hit ||= !defined $col;
>              if ($row < $line->end) {
>                 $hit ||= 1;
>              } else {
>                 $hit ||= $from <= $col && $col < $to;
>              }
>           }
>        }
>        if ($hit) {
>             if ($launcher !~ /\$/) {
>                @exec = ($launcher,$match);
>             } else {
>              $match =~ /$matcher->[0]/;
>              my @begin = @-;
>              my @end = @+;
>                # It'd be nice to just access a list like ($&,$1,$2...),
>                # but alas, m//g behaves differently in list context.
>                @exec = map { s/\$(\d+)|\$\{(\d+)\}/
>                   substr($text,$begin[$1||$2],$end[$1||$2]-$begin[$1||$2])
>                   /egx; $_ } split(/\s+/, $launcher);
>             }
>        }
>       });
>       return @exec if @exec;
>    }
> 
>    ()
> }
> 
> sub on_button_press {
>    my ($self, $event) = @_;
>    if($self->valid_button($event)
>       && (my @exec = $self->command_for($event->{row},$event->{col}))) {
>       $self->{row} = $event->{row};
>       $self->{col} = $event->{col};
>       $self->{cmd} = \@exec;
>       return 1;
>    } else {
>       delete $self->{row};
>       delete $self->{col};
>       delete $self->{cmd};
>    }
> 
>    ()
> }
> 
> sub on_button_release {
>    my ($self, $event) = @_;
> 
>    my $row = delete $self->{row};
>    my $col = delete $self->{col};
>    my $cmd = delete $self->{cmd};
> 
>    return if !defined $row;
> 
>    if($row == $event->{row} && abs($col-$event->{col}) < 2
>       && join("\x00", @$cmd) eq join("\x00", $self->command_for($row,$col))) {
>       if($self->valid_button($event)) {
> 
>        $self->exec_async (@$cmd);
> 
>       }
>    }
> 
>    1;
> }
> 
> sub enlarge {
>    my ($self, $row) = @_;
> 
>    my $line = $self->line($row);
>    my $text = $line->t;
> 
>    # enlarge this line with prev&next lines up to nearest line with space char
>    my ($prev_cols, $next_cols) = (0, 0);
>    my (@prev_lines,@next_lines);
>    if ($line->l && $text !~ /\A\s/ms) {
>       for my $prev_row (reverse 0 .. $row-1) {
>        my $l = $self->line($prev_row);
>        my $t = $l->t;
>        last if $l->l < $self->ncol;
>        unshift @prev_lines, $l;
>        $prev_cols += $l->l;
>        $text = $t . $text;
>        last if $t =~ /\s/ms;
>       }
>    }
>    if ($line->l == $self->ncol && $text !~ /\s\z/ms) {
>       for my $next_row ($row+1 .. $self->nrow-1) {
>        my $l = $self->line($next_row);
>        my $t = $l->t;
>        push @next_lines, $l;
>        $next_cols += $l->l;
>        $text .= $t;
>        last if $l->l < $self->ncol;
>        last if $t =~ /\s/ms;
>       }
>    }
> 
>    my @lines = (@prev_lines, $line, @next_lines);
>    return ($text, $prev_cols, $next_cols, @lines);
> }
> 
> sub match {
>    my ($self, $re, $text, $prev_cols, $next_cols, $lines, $cb) = @_;
>    while ($text =~ /$re/g) {
>       my ($beg, $end) = ($-[0], $+[0]);
>       # skip matches outside this line
>       next if $end <= $prev_cols;
>       next if $beg >= (length $text) - $next_cols;
>       # detect match boundaries over lines and send them to user's callback
>       my @parts;
>       for my $line (@$lines) {
>          if ($beg < $line->l && 0 < $end) {
>             my $from = $beg     < 0     ? 0        : $beg;
>             my $to   = $line->l < $end          ? $line->l : $end;
>           push @parts, [$line, $from, $to];
>          }
>          $beg -= $line->l;
>          $end -= $line->l;
>       }
>       $cb->(@parts);
>    }
>    return;
> }
> 
> # vim:set sw=3 sts=3 et:

Reply via email to