add more files
[misc.git] / perl / mark-yank-urls
1 #!/usr/bin/perl
2 # Author: Bart Trojanowski <bart@jukie.net>
3 # Website: http://www.jukie.net/~bart/blog/urxvt-url-yank
4 # License: GPLv2
5
6 use strict;
7 use warnings;
8
9 my $url_matcher = qr{(?:
10 (?:https?://|ftp://|news://|mailto:|file://|www\.)[ab-zA-Z0-9\-\@;\/?:&=%\$_.+!*\x27(),~#]+
11 [ab-zA-Z0-9\-\@;\/?&=%\$_+!*\x27()~] # exclude some trailing characters (heuristic)
12 )|\#[0-9]{4,}
13 }x;
14
15
16 sub on_start {
17 my ($term) = @_;
18
19 $term->{have_Clipboard} = eval { require Clipboard; };
20 if ($term->{have_Clipboard}) {
21 import Clipboard;
22 }
23
24 eval { require Regexp::Common::URI };
25 if(!$@) {
26 require Regexp::Common;
27 Regexp::Common->import('URI');
28
29 $url_matcher = $Regexp::Common::RE{URI}{HTTP};
30 }
31
32 $term->{browser} = $term->x_resource ("urlLauncher") || "x-www-browser";
33
34 ()
35 }
36
37 sub on_line_update {
38 my ($term, $row) = @_;
39
40 # Fetch the line that has changed.
41 my $line = $term->line($row);
42 my $text = $line->t;
43
44 # Find and underline URLs.
45 while ($text =~ /($url_matcher)/g) {
46 my $url = $1;
47 my $rend = $line->r;
48
49 # Mark all characters as underlined. we _must_ not toggle underline, as
50 # we might get called on an already-marked URL.
51 my $underlineURLs = $term->x_resource ('underlineURLs') || 'false';
52 if($underlineURLs eq 'true') {
53 my ($first, $last) = ($-[1], $+[1] - 1);
54
55 --$last if $url =~ s/["']$//;
56
57 $_ |= urxvt::RS_Uline for @{$rend}[$first .. $last];
58
59 $line->r($rend);
60 }
61 }
62
63 ()
64 }
65
66 sub on_button_release {
67 my ($term, $event) = @_;
68
69 my $mask = $term->ModLevel3Mask | $term->ModMetaMask
70 | urxvt::ShiftMask | urxvt::ControlMask;
71
72 if ($event->{button} == 2 && ($event->{state} & $mask) == 0) {
73 my $row = $event->{row};
74 my $col = $event->{col};
75
76 my $line = $term->line ($row);
77 my $text = $line->t;
78
79 while ($text =~ /($url_matcher)/g) {
80 my ($url, $first, $last) = ($1, $-[1], $+[1]);
81
82 if($first <= $col && $last >= $col) {
83 $url =~ s/["']$//;
84 $url =~ s/^#/http:\/\/bugs.debian.org\//;
85 $term->exec_async($term->{browser}, $url);
86 return 1;
87 }
88 }
89 }
90
91 ()
92 }
93
94
95 my $mark_mode_active = 0;
96 my %mod = ( 'control' => 0, 'shift' => 0 );
97 my $url_selected = -1;
98 my @url_db = ();
99
100
101 sub do_scan_for_urls {
102 my ($term) = @_;
103
104 @url_db = ();
105
106 my $row_start = $term->top_row;
107 my $row_end = $term->nrow;
108
109 for my $row ($row_start .. $row_end) {
110
111 # Fetch the line that has changed.
112 my $line = $term->line ($row);
113 my $text = $line->t;
114
115 # Find all urls (if any).
116 while ($text =~ /($url_matcher)/g) {
117 my $rend = $line->r;
118
119 my ($url, $first, $last) = ($1, $-[1], $+[1] - 1);
120
121 --$last if $url =~ s/["']$//;
122
123 my %h = (
124 row => $row,
125 col_from => $first,
126 col_to => $last,
127 url => $url,
128 );
129
130 push @url_db, \%h;
131 }
132 }
133
134 # 0 for none, positive count otherwise
135 return $#url_db + 1;
136 }
137
138
139 sub on_user_command {
140 my ($term, $cmd) = @_;
141
142 activate_mark_mode($term) if $cmd eq 'mark-yank-urls:activate_mark_mode';
143
144 ()
145 }
146
147 sub on_key_press {
148 my ($term, $event, $keysym, $octets) = @_;
149
150 if ($keysym == 65507) { # <control>
151 $mod{control} = 1;
152
153 } elsif ($keysym == 65505) { # <shift>
154 $mod{shift} = 1;
155
156 }
157
158 # Ignore all input when we are active.
159 $mark_mode_active && return 1;
160
161 ()
162 }
163
164 sub on_key_release {
165 my ($term, $event, $keysym, $octets) = @_;
166
167 if ($mark_mode_active) {
168 my $ch = chr($keysym);
169
170 if ($keysym == 65307) { # <esc>
171 deactivate_mark_mode ($term);
172 return 1;
173
174 } elsif ($keysym == 65293) { # <enter>
175 my $url = get_active_url($term);
176 $url =~ s/^#/http:\/\/bugs.debian.org\//;
177 $term->exec_async($term->{browser}, $url);
178 deactivate_mark_mode ($term);
179 return 1;
180
181 } elsif ($keysym == 65507) { # <control>
182 $mod{control} = 0;
183 return 1;
184
185 } elsif ($keysym == 65505) { # <shift>
186 $mod{shift} = 0;
187 return 1;
188
189 } elsif ($mod{control} && (($ch eq 'n') || ($ch eq 'p'))) {
190 # ^n and ^p to cycle list
191 my $dir = ($ch eq 'n') ? 1 : -1;
192 move_highlight ($term, $dir);
193
194 } elsif ($ch eq 'y') { # y
195 do_copy ($term);
196 deactivate_mark_mode ($term);
197 return 1;
198
199 }
200
201 return 1;
202 }
203
204 ()
205 }
206
207 sub get_active_url {
208 my ($term) = @_;
209 my $max = $#url_db + 1;
210
211 return if $url_selected < 0 || $url_selected >= $max;
212 return if not defined $url_db[$url_selected];
213 my $o = $url_db[$url_selected];
214 my %h = %$o;
215
216 return $h{url};
217 }
218
219 sub do_copy {
220 my ($term) = @_;
221
222 my $text = get_active_url ($term);
223
224 if ($term->{have_Clipboard}) {
225 Clipboard->copy($text);
226 } else {
227 $text =~ s/\(["|><&()]\)/\\$1/;
228 system ("echo -n \"$text\" | xclip -i");
229 }
230 }
231
232 sub move_highlight {
233 my ($term, $dir) = @_;
234 my $max = $#url_db + 1;
235
236 do_highlight ($term, 0);
237
238 $url_selected = ($max + $url_selected + $dir) % $max;
239
240 do_highlight ($term, 1);
241
242 $term->want_refresh;
243 }
244
245 sub do_highlight {
246 my ($term, $enable) = @_;
247 my $max = $#url_db + 1;
248
249 return if $url_selected < 0 || $url_selected >= $max;
250 return if not defined $url_db[$url_selected];
251
252 my $o = $url_db[$url_selected];
253 my %h = %$o;
254
255 my $row = $h{row};
256 my $line = $term->line ($row);
257 my $text = $line->t;
258 my $rend = $line->r;
259
260 if ($enable) {
261 $_ |= urxvt::RS_RVid
262 for @{$rend}[ $h{col_from} .. $h{col_to}];
263
264 # make it visible
265 $term->view_start ( $row < 0 ? $row : 0 );
266
267 } else {
268 $_ &= ~urxvt::RS_RVid
269 for @{$rend}[ $h{col_from} .. $h{col_to}];
270 }
271
272 $line->r ($rend);
273 }
274
275 sub activate_mark_mode {
276 my ($term) = @_;
277
278 if ($mark_mode_active) {
279
280 move_highlight ($term, -1);
281
282 } elsif ( do_scan_for_urls ($term) ) {
283
284 $term->{save_view_start} = $term->view_start;
285
286 move_highlight ($term, 0);
287
288 $mark_mode_active=1 if ($url_selected > -1);
289 }
290 }
291
292 sub deactivate_mark_mode {
293 my ($term) = @_;
294
295 do_highlight ($term, 0);
296
297 $mark_mode_active = 0;
298 $url_selected = -1;
299
300 $term->view_start ($term->{save_view_start});
301 $term->want_refresh;
302 }
303
304 # vim: set et ts=4 sw=4: