… | |
… | |
2 | |
2 | |
3 | # Author: Tim Pope <rxvt-unicodeNOSPAM@tpope.info> |
3 | # Author: Tim Pope <rxvt-unicodeNOSPAM@tpope.info> |
4 | |
4 | |
5 | my $url = |
5 | my $url = |
6 | qr{ |
6 | qr{ |
7 | (?:https?://|ftp://|news://|mailto:|file://|\bwww\.)[ab-zA-Z0-9\-\@;\/?:&=%\$_.+!*\x27(),~#]+ |
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 |
8 | [ab-zA-Z0-9\-\@;\/?:&=%\$_+*()~] # exclude some trailing characters (heuristic) |
11 | [a-zA-Z0-9\-\@;\/?:&=%\$_+*~] # exclude some trailing characters (heuristic) |
|
|
12 | )+ |
9 | }x; |
13 | }x; |
|
|
14 | |
|
|
15 | sub on_user_command { |
|
|
16 | my ($self, $cmd) = @_; |
|
|
17 | if($cmd =~ s/^matcher\b//) { |
|
|
18 | $self->most_recent; |
|
|
19 | } |
|
|
20 | () |
|
|
21 | } |
|
|
22 | |
|
|
23 | sub most_recent { |
|
|
24 | my ($self) = shift; |
|
|
25 | my $row = $self->nrow; |
|
|
26 | my @exec; |
|
|
27 | while($row-- > $self->top_row) { |
|
|
28 | #my $line = $self->line ($row); |
|
|
29 | #my $text = $line->t; |
|
|
30 | @exec = $self->command_for($row); |
|
|
31 | last if(@exec); |
|
|
32 | } |
|
|
33 | if(@exec) { |
|
|
34 | return $self->exec_async (@exec); |
|
|
35 | } |
|
|
36 | () |
|
|
37 | } |
10 | |
38 | |
11 | sub my_resource { |
39 | sub my_resource { |
12 | my $self = shift; |
40 | my $self = shift; |
13 | $self->x_resource("$self->{name}.$_[0]"); |
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 | } |
14 | } |
57 | } |
15 | |
58 | |
16 | sub on_start { |
59 | sub on_start { |
17 | my ($self) = @_; |
60 | my ($self) = @_; |
18 | |
61 | |
… | |
… | |
46 | for (my $idx = 0; defined (my $res = $self->my_resource("pattern.$idx") || $defaults[$idx]); $idx++) { |
89 | for (my $idx = 0; defined (my $res = $self->my_resource("pattern.$idx") || $defaults[$idx]); $idx++) { |
47 | $res = $self->locale_decode ($res); |
90 | $res = $self->locale_decode ($res); |
48 | utf8::encode $res; |
91 | utf8::encode $res; |
49 | my $launcher = $self->my_resource("launcher.$idx"); |
92 | my $launcher = $self->my_resource("launcher.$idx"); |
50 | $launcher =~ s/\$&|\$\{&\}/\${0}/g if ($launcher); |
93 | $launcher =~ s/\$&|\$\{&\}/\${0}/g if ($launcher); |
|
|
94 | my $rend = $self->parse_rend($self->my_resource("rend.$idx")); |
51 | push @matchers, [qr($res)x,$launcher]; |
95 | unshift @matchers, [qr($res)x,$launcher,$rend]; |
52 | } |
96 | } |
53 | $self->{matchers} = \@matchers; |
97 | $self->{matchers} = \@matchers; |
54 | |
98 | |
55 | () |
99 | () |
56 | } |
100 | } |
… | |
… | |
64 | my $i = 0; |
108 | my $i = 0; |
65 | |
109 | |
66 | # find all urls (if any) |
110 | # find all urls (if any) |
67 | for my $matcher (@{$self->{matchers}}) { |
111 | for my $matcher (@{$self->{matchers}}) { |
68 | while ($text =~ /$matcher->[0]/g) { |
112 | while ($text =~ /$matcher->[0]/g) { |
|
|
113 | #print "$&\n"; |
69 | my $rend = $line->r; |
114 | my $rend = $line->r; |
70 | |
115 | |
71 | # mark all characters as underlined. we _must_ not toggle underline, |
116 | # mark all characters as underlined. we _must_ not toggle underline, |
72 | # as we might get called on an already-marked url. |
117 | # as we might get called on an already-marked url. |
73 | $_ |= urxvt::RS_Uline |
118 | &{$matcher->[2]} |
74 | for @{$rend}[ $-[0] .. $+[0] - 1]; |
119 | for @{$rend}[ $-[0] .. $+[0] - 1]; |
75 | |
120 | |
76 | $line->r ($rend); |
121 | $line->r ($rend); |
77 | } |
122 | } |
78 | } |
123 | } |
… | |
… | |
97 | my $launcher = $matcher->[1] || $self->{launcher}; |
142 | my $launcher = $matcher->[1] || $self->{launcher}; |
98 | while (($text =~ /$matcher->[0]/g)) { |
143 | while (($text =~ /$matcher->[0]/g)) { |
99 | my $match = $&; |
144 | my $match = $&; |
100 | my @begin = @-; |
145 | my @begin = @-; |
101 | my @end = @+; |
146 | my @end = @+; |
102 | if ($-[0] <= $col && $+[0] >= $col) { |
147 | if (!defined($col) || ($-[0] <= $col && $+[0] >= $col)) { |
103 | if ($launcher !~ /\$/) { |
148 | if ($launcher !~ /\$/) { |
104 | return ($launcher,$match); |
149 | return ($launcher,$match); |
105 | } else { |
150 | } else { |
106 | # It'd be nice to just access a list like ($&,$1,$2...), |
151 | # It'd be nice to just access a list like ($&,$1,$2...), |
107 | # but alas, m//g behaves differently in list context. |
152 | # but alas, m//g behaves differently in list context. |
… | |
… | |
117 | () |
162 | () |
118 | } |
163 | } |
119 | |
164 | |
120 | sub on_button_press { |
165 | sub on_button_press { |
121 | my ($self, $event) = @_; |
166 | my ($self, $event) = @_; |
122 | if($self->valid_button($event)) { |
167 | if($self->valid_button($event) |
|
|
168 | && (my @exec = $self->command_for($event->{row},$event->{col}))) { |
123 | $self->{row} = $event->{row}; |
169 | $self->{row} = $event->{row}; |
124 | $self->{col} = $event->{col}; |
170 | $self->{col} = $event->{col}; |
|
|
171 | $self->{cmd} = \@exec; |
|
|
172 | return 1; |
125 | } else { |
173 | } else { |
126 | delete $self->{row}; |
174 | delete $self->{row}; |
127 | delete $self->{col}; |
175 | delete $self->{col}; |
|
|
176 | delete $self->{cmd}; |
128 | } |
177 | } |
129 | |
178 | |
130 | () |
179 | () |
131 | } |
180 | } |
132 | |
181 | |
133 | sub on_button_release { |
182 | sub on_button_release { |
134 | my ($self, $event) = @_; |
183 | my ($self, $event) = @_; |
135 | |
184 | |
136 | my $row = delete $self->{row}; |
185 | my $row = delete $self->{row}; |
137 | my $col = delete $self->{col}; |
186 | my $col = delete $self->{col}; |
|
|
187 | my $cmd = delete $self->{cmd}; |
138 | |
188 | |
|
|
189 | return if !defined $row; |
|
|
190 | |
139 | if(defined($row) && $row == $event->{row} && abs($col-$event->{col}) < 2) { |
191 | if($row == $event->{row} && abs($col-$event->{col}) < 2 |
|
|
192 | && join("\x00", @$cmd) eq join("\x00", $self->command_for($row,$col))) { |
140 | if($self->valid_button($event)) { |
193 | if($self->valid_button($event)) { |
141 | |
194 | |
142 | my @exec = $self->command_for($row,$col); |
195 | $self->exec_async (@$cmd); |
143 | if(@exec) { |
196 | |
144 | return $self->exec_async (@exec); |
|
|
145 | } |
197 | } |
146 | |
|
|
147 | } |
|
|
148 | } |
198 | } |
149 | |
199 | |
150 | () |
200 | 1; |
151 | } |
201 | } |
152 | |
202 | |
153 | # vim:set sw=3 sts=3 et: |
203 | # vim:set sw=3 sts=3 et: |