Hi!
In mutt's maillist thread "select wrapped lines / click long url / bug 3453"
http://comments.gmane.org/gmane.mail.mutt.user/39602 I've tried to find a
solution to easily handle long urls in mutt (which wrap over several lines),
and finally come to switching from xterm to urxvt and patching "matcher"
plugin to let it recognize such long urls.
While issue itself is probably mutt's bug, it's 2 years old and unlikely
will be fixed. Problem is, mutt output wrapped lines in such a way, what
terminal handle single wrapped line as multiple lines. This is somehow
related to ncurses and won't happens in bash/less/etc.
One of urxvt users in that maillist ask to post this patch in urxvt
maillist too, so it won't be lost. (I'm the one of "these people" who
failed to communicate with Marc ;-) many years ago, so I don't really expect
this patch/feature to be accepted in official urxvt, but at least here it
will be ease to find by urxvt users.)
I'm using urxvt just a few days, and didn't looked in matcher plugin's
"list" feature yet, so I've implemented long urls support only for
highlighting (underline) and clicking features.
Another limitation is using space char as limiter when scanning for
previous/next lines which may contain beginning/continuation of url in
current line. While this is ok for urls, matcher itself is more general
and may use patterns which allow spaces inside - such patterns won't be
recognized in wrapped (by mutt and similar ncurses apps) lines.
Attached patch is for rxvt-unicode-9.15.
--
WBR, Alex.
--- matcher.orig 2012-10-24 05:38:42.747463293 +0300
+++ matcher 2012-10-24 20:36:13.653745332 +0300
@@ -202,24 +202,22 @@
sub on_line_update {
my ($self, $row) = @_;
- # fetch the line that has changed
- my $line = $self->line ($row);
- my $text = $line->t;
- my $i = 0;
+ # 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}}) {
- while ($text =~ /$matcher->[0]/g) {
- #print "$&\n";
- 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}[ $-[0] .. $+[0] - 1];
-
- $line->r ($rend);
- }
+ $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);
+ }
+ });
}
()
@@ -235,28 +233,45 @@
sub command_for {
my ($self, $row, $col) = @_;
- my $line = $self->line ($row);
- my $text = $line->t;
+
+ # 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};
- while (($text =~ /$matcher->[0]/g)) {
- my $match = $&;
- my @begin = @-;
- my @end = @+;
- if (!defined($col) || ($-[0] <= $col && $+[0] >= $col)) {
+ 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 !~ /\$/) {
- return ($launcher,$match);
+ @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.
- my @exec = map { s/\$(\d+)|\$\{(\d+)\}/
+ @exec = map { s/\$(\d+)|\$\{(\d+)\}/
substr($text,$begin[$1||$2],$end[$1||$2]-$begin[$1||$2])
/egx; $_ } split(/\s+/, $launcher);
- return @exec;
}
- }
- }
+ }
+ });
+ return @exec if @exec;
}
()
@@ -300,4 +315,63 @@
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:
_______________________________________________
rxvt-unicode mailing list
[email protected]
http://lists.schmorp.de/cgi-bin/mailman/listinfo/rxvt-unicode