ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/perl/matcher
Revision: 1.3
Committed: Sun Jun 10 23:10:01 2007 UTC (16 years, 11 months ago) by root
Branch: MAIN
Changes since 1.2: +24 -24 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 #! perl
2
3 # Author: Tim Pope <rxvt-unicodeNOSPAM@tpope.info>
4
5 my $url =
6 qr{
7 (?:https?://|ftp://|news://|mailto:|file://|\bwww\.)
8 [a-zA-Z0-9\-\@;\/?:&=%\$_.+!*\x27,~#]*
9 (
10 \([a-zA-Z0-9\-\@;\/?:&=%\$_.+!*\x27,~#]*\)| # Allow a pair of matched parentheses
11 [a-zA-Z0-9\-\@;\/?:&=%\$_+*~] # exclude some trailing characters (heuristic)
12 )+
13 }x;
14
15 #sub on_user_command {
16 # my ($self, $cmd) = @_;
17 # if($cmd =~ s/^matcher\b//) {
18 # $self->most_recent;
19 # }
20 # my $row = $self->nrow;
21 # my @exec;
22 # while($row-- > $self->top_row) {
23 # #my $line = $self->line ($row);
24 # #my $text = $line->t;
25 # @exec = $self->command_for($row);
26 # last if(@exec);
27 # }
28 # if(@exec) {
29 # return $self->exec_async (@exec);
30 # }
31 # ()
32 #}
33 #
34 #sub most_recent {
35 # my ($self) = shift;
36 # ()
37 #}
38
39 sub my_resource {
40 my $self = shift;
41 $self->x_resource ("$self->{name}.$_[0]");
42 }
43
44 # turn a rendition spec in the resource into a sub that implements it on $_
45 sub parse_rend {
46 my ($self, $str) = @_;
47 my ($mask, $fg, $bg, $failed) = $str ? urxvt::rend2mask($str)
48 : (urxvt::RS_Uline, undef, undef, []);
49 warn "Failed to parse rendition string: " . join(',', @$failed) if @$failed;
50 my @rend;
51 push @rend, sub { $_ |= $mask } if $mask;
52 push @rend, sub { $_ = urxvt::SET_FGCOLOR($_, $fg) } if defined $fg;
53 push @rend, sub { $_ = urxvt::SET_BGCOLOR($_, $bg) } if defined $bg;
54 sub {
55 for my $s ( @rend ) { &$s };
56 }
57 }
58
59 sub on_start {
60 my ($self) = @_;
61
62 ($self->{name} = __PACKAGE__) =~ s/.*:://;
63 $self->{name} =~ tr/_/-/;
64 $self->{launcher} = $self->my_resource("launcher") ||
65 $self->x_resource("urlLauncher") ||
66 "sensible-browser";
67
68 $self->{button} = 2;
69 $self->{state} = 0;
70 if($self->{argv}[0] || $self->my_resource("button")) {
71 my @mods = split('', $self->{argv}[0] || $self->my_resource("button"));
72 for my $mod (@mods) {
73 if($mod =~ /^\d+$/) {
74 $self->{button} = $mod;
75 } elsif($mod eq "C") {
76 $self->{state} |= urxvt::ControlMask;
77 } elsif($mod eq "S") {
78 $self->{state} |= urxvt::ShiftMask;
79 } elsif($mod eq "M") {
80 $self->{state} |= $self->ModMetaMask;
81 } elsif($mod ne "-" && $mod ne " ") {
82 warn("$mod is invalid in $self->{name}<$self->{argv}[0]>\n");
83 }
84 }
85 }
86
87 my @defaults = ($url);
88 my @matchers;
89 for (my $idx = 0; defined (my $res = $self->my_resource("pattern.$idx") || $defaults[$idx]); $idx++) {
90 $res = $self->locale_decode ($res);
91 utf8::encode $res;
92 my $launcher = $self->my_resource("launcher.$idx");
93 $launcher =~ s/\$&|\$\{&\}/\${0}/g if ($launcher);
94 my $rend = $self->parse_rend($self->my_resource("rend.$idx"));
95 unshift @matchers, [qr($res)x,$launcher,$rend];
96 }
97 $self->{matchers} = \@matchers;
98
99 ()
100 }
101
102 sub on_line_update {
103 my ($self, $row) = @_;
104
105 # fetch the line that has changed
106 my $line = $self->line ($row);
107 my $text = $line->t;
108 my $i = 0;
109
110 # find all urls (if any)
111 for my $matcher (@{$self->{matchers}}) {
112 while ($text =~ /$matcher->[0]/g) {
113 #print "$&\n";
114 my $rend = $line->r;
115
116 # mark all characters as underlined. we _must_ not toggle underline,
117 # as we might get called on an already-marked url.
118 &{$matcher->[2]}
119 for @{$rend}[ $-[0] .. $+[0] - 1];
120
121 $line->r ($rend);
122 }
123 }
124
125 ()
126 }
127
128 sub valid_button {
129 my ($self, $event) = @_;
130 my $mask = $self->ModLevel3Mask | $self->ModMetaMask
131 | urxvt::ShiftMask | urxvt::ControlMask;
132 return ($event->{button} == $self->{button} &&
133 ($event->{state} & $mask) == $self->{state});
134 }
135
136 sub command_for {
137 my ($self, $row, $col) = @_;
138 my $line = $self->line ($row);
139 my $text = $line->t;
140
141 for my $matcher (@{$self->{matchers}}) {
142 my $launcher = $matcher->[1] || $self->{launcher};
143 while (($text =~ /$matcher->[0]/g)) {
144 my $match = $&;
145 my @begin = @-;
146 my @end = @+;
147 if (!defined($col) || ($-[0] <= $col && $+[0] >= $col)) {
148 if ($launcher !~ /\$/) {
149 return ($launcher,$match);
150 } else {
151 # It'd be nice to just access a list like ($&,$1,$2...),
152 # but alas, m//g behaves differently in list context.
153 my @exec = map { s/\$(\d+)|\$\{(\d+)\}/
154 substr($text,$begin[$1||$2],$end[$1||$2]-$begin[$1||$2])
155 /egx; $_ } split(/\s+/, $launcher);
156 return @exec;
157 }
158 }
159 }
160 }
161
162 ()
163 }
164
165 sub on_button_press {
166 my ($self, $event) = @_;
167 if($self->valid_button($event)) {
168 $self->{row} = $event->{row};
169 $self->{col} = $event->{col};
170 } else {
171 delete $self->{row};
172 delete $self->{col};
173 }
174
175 ()
176 }
177
178 sub on_button_release {
179 my ($self, $event) = @_;
180
181 my $row = delete $self->{row};
182 my $col = delete $self->{col};
183
184 if(defined($row) && $row == $event->{row} && abs($col-$event->{col}) < 2) {
185 if($self->valid_button($event)) {
186
187 my @exec = $self->command_for($row,$col);
188 if(@exec) {
189 return $self->exec_async (@exec);
190 }
191
192 }
193 }
194
195 ()
196 }
197
198 # vim:set sw=3 sts=3 et: