Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[URxvt] Added mouseless url yanking.
- Loading branch information
Showing
2 changed files
with
316 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,301 @@ | ||
#!/usr/bin/perl | ||
# Author: Bart Trojanowski <bart@jukie.net> | ||
# Website: http://www.jukie.net/~bart/blog/urxvt-url-yank | ||
# License: GPLv2 | ||
|
||
use strict; | ||
use warnings; | ||
|
||
my $url_matcher = qr{( | ||
(?:https?://|ftp://|news://|mailto:|file://|www\.)[ab-zA-Z0-9\-\@;\/?:&=%\$_.+!*\x27(),~#]+ | ||
[ab-zA-Z0-9\-\@;\/?&=%\$_+!*\x27()~] # exclude some trailing characters (heuristic) | ||
)}x; | ||
|
||
|
||
sub on_start { | ||
my ($term) = @_; | ||
|
||
$term->{have_Clipboard} = eval { require Clipboard; }; | ||
if ($term->{have_Clipboard}) { | ||
import Clipboard; | ||
} | ||
|
||
eval { require Regexp::Common::URI }; | ||
if(!$@) { | ||
require Regexp::Common; | ||
Regexp::Common->import('URI'); | ||
|
||
$url_matcher = $Regexp::Common::RE{URI}{HTTP}; | ||
} | ||
|
||
$term->{browser} = $term->x_resource ("urlLauncher") || "x-www-browser"; | ||
|
||
() | ||
} | ||
|
||
sub on_line_update { | ||
my ($term, $row) = @_; | ||
|
||
# Fetch the line that has changed. | ||
my $line = $term->line($row); | ||
my $text = $line->t; | ||
|
||
# Find and underline URLs. | ||
while ($text =~ /($url_matcher)/g) { | ||
my $url = $1; | ||
my $rend = $line->r; | ||
|
||
# Mark all characters as underlined. we _must_ not toggle underline, as | ||
# we might get called on an already-marked URL. | ||
my $underlineURLs = $term->x_resource ('underlineURLs') || 'false'; | ||
if($underlineURLs eq 'true') { | ||
my ($first, $last) = ($-[1], $+[1] - 1); | ||
|
||
--$last if $url =~ s/["']$//; | ||
|
||
$_ |= urxvt::RS_Uline for @{$rend}[$first .. $last]; | ||
|
||
$line->r($rend); | ||
} | ||
} | ||
|
||
() | ||
} | ||
|
||
sub on_button_release { | ||
my ($term, $event) = @_; | ||
|
||
my $mask = $term->ModLevel3Mask | $term->ModMetaMask | ||
| urxvt::ShiftMask | urxvt::ControlMask; | ||
|
||
if ($event->{button} == 2 && ($event->{state} & $mask) == 0) { | ||
my $row = $event->{row}; | ||
my $col = $event->{col}; | ||
|
||
my $line = $term->line ($row); | ||
my $text = $line->t; | ||
|
||
while ($text =~ /($url_matcher)/g) { | ||
my ($url, $first, $last) = ($1, $-[1], $+[1]); | ||
|
||
if($first <= $col && $last >= $col) { | ||
$url =~ s/["']$//; | ||
$term->exec_async($term->{browser}, $url); | ||
return 1; | ||
} | ||
} | ||
} | ||
|
||
() | ||
} | ||
|
||
|
||
my $mark_mode_active = 0; | ||
my %mod = ( 'control' => 0, 'shift' => 0 ); | ||
my $url_selected = -1; | ||
my @url_db = (); | ||
|
||
|
||
sub do_scan_for_urls { | ||
my ($term) = @_; | ||
|
||
@url_db = (); | ||
|
||
my $row_start = $term->top_row; | ||
my $row_end = $term->nrow; | ||
|
||
for my $row ($row_start .. $row_end) { | ||
|
||
# Fetch the line that has changed. | ||
my $line = $term->line ($row); | ||
my $text = $line->t; | ||
|
||
# Find all urls (if any). | ||
while ($text =~ /($url_matcher)/g) { | ||
my $rend = $line->r; | ||
|
||
my ($url, $first, $last) = ($1, $-[1], $+[1] - 1); | ||
|
||
--$last if $url =~ s/["']$//; | ||
|
||
my %h = ( | ||
row => $row, | ||
col_from => $first, | ||
col_to => $last, | ||
url => $url, | ||
); | ||
|
||
push @url_db, \%h; | ||
} | ||
} | ||
|
||
# 0 for none, positive count otherwise | ||
return $#url_db + 1; | ||
} | ||
|
||
|
||
sub on_user_command { | ||
my ($term, $cmd) = @_; | ||
|
||
activate_mark_mode($term) if $cmd eq 'mark-yank-urls:activate_mark_mode'; | ||
|
||
() | ||
} | ||
|
||
sub on_key_press { | ||
my ($term, $event, $keysym, $octets) = @_; | ||
|
||
if ($keysym == 65507) { # <control> | ||
$mod{control} = 1; | ||
|
||
} elsif ($keysym == 65505) { # <shift> | ||
$mod{shift} = 1; | ||
|
||
} | ||
|
||
# Ignore all input when we are active. | ||
$mark_mode_active && return 1; | ||
|
||
() | ||
} | ||
|
||
sub on_key_release { | ||
my ($term, $event, $keysym, $octets) = @_; | ||
|
||
if ($mark_mode_active) { | ||
my $ch = chr($keysym); | ||
|
||
if ($keysym == 65307) { # <esc> | ||
deactivate_mark_mode ($term); | ||
return 1; | ||
|
||
} elsif ($keysym == 65293) { # <enter> | ||
my $url = get_active_url($term); | ||
$term->exec_async($term->{browser}, $url); | ||
deactivate_mark_mode ($term); | ||
return 1; | ||
|
||
} elsif ($keysym == 65507) { # <control> | ||
$mod{control} = 0; | ||
return 1; | ||
|
||
} elsif ($keysym == 65505) { # <shift> | ||
$mod{shift} = 0; | ||
return 1; | ||
|
||
} elsif ($mod{control} && (($ch eq 'n') || ($ch eq 'p'))) { | ||
# ^n and ^p to cycle list | ||
my $dir = ($ch eq 'n') ? 1 : -1; | ||
move_highlight ($term, $dir); | ||
|
||
} elsif ($ch eq 'y') { # y | ||
do_copy ($term); | ||
deactivate_mark_mode ($term); | ||
return 1; | ||
|
||
} | ||
|
||
return 1; | ||
} | ||
|
||
() | ||
} | ||
|
||
sub get_active_url { | ||
my ($term) = @_; | ||
my $max = $#url_db + 1; | ||
|
||
return if $url_selected < 0 || $url_selected >= $max; | ||
return if not defined $url_db[$url_selected]; | ||
my $o = $url_db[$url_selected]; | ||
my %h = %$o; | ||
|
||
return $h{url}; | ||
} | ||
|
||
sub do_copy { | ||
my ($term) = @_; | ||
|
||
my $text = get_active_url ($term); | ||
|
||
if ($term->{have_Clipboard}) { | ||
Clipboard->copy($text); | ||
} else { | ||
$text =~ s/\(["|><&()]\)/\\$1/; | ||
system ("echo -n \"$text\" | xclip -i"); | ||
} | ||
} | ||
|
||
sub move_highlight { | ||
my ($term, $dir) = @_; | ||
my $max = $#url_db + 1; | ||
|
||
do_highlight ($term, 0); | ||
|
||
$url_selected = ($max + $url_selected + $dir) % $max; | ||
|
||
do_highlight ($term, 1); | ||
|
||
$term->want_refresh; | ||
} | ||
|
||
sub do_highlight { | ||
my ($term, $enable) = @_; | ||
my $max = $#url_db + 1; | ||
|
||
return if $url_selected < 0 || $url_selected >= $max; | ||
return if not defined $url_db[$url_selected]; | ||
|
||
my $o = $url_db[$url_selected]; | ||
my %h = %$o; | ||
|
||
my $row = $h{row}; | ||
my $line = $term->line ($row); | ||
my $text = $line->t; | ||
my $rend = $line->r; | ||
|
||
if ($enable) { | ||
$_ |= urxvt::RS_RVid | ||
for @{$rend}[ $h{col_from} .. $h{col_to}]; | ||
|
||
# make it visible | ||
$term->view_start ( $row < 0 ? $row : 0 ); | ||
|
||
} else { | ||
$_ &= ~urxvt::RS_RVid | ||
for @{$rend}[ $h{col_from} .. $h{col_to}]; | ||
} | ||
|
||
$line->r ($rend); | ||
} | ||
|
||
sub activate_mark_mode { | ||
my ($term) = @_; | ||
|
||
if ($mark_mode_active) { | ||
|
||
move_highlight ($term, -1); | ||
|
||
} elsif ( do_scan_for_urls ($term) ) { | ||
|
||
$term->{save_view_start} = $term->view_start; | ||
|
||
move_highlight ($term, 0); | ||
|
||
$mark_mode_active=1 if ($url_selected > -1); | ||
} | ||
} | ||
|
||
sub deactivate_mark_mode { | ||
my ($term) = @_; | ||
|
||
do_highlight ($term, 0); | ||
|
||
$mark_mode_active = 0; | ||
$url_selected = -1; | ||
|
||
$term->view_start ($term->{save_view_start}); | ||
$term->want_refresh; | ||
} | ||
|
||
# vim: set et ts=4 sw=4: |