aboutsummaryrefslogtreecommitdiff
path: root/.urxvt/mark-and-yank
diff options
context:
space:
mode:
authorRyan Kavanagh <ryanakca@kubuntu.org>2011-08-24 20:15:31 -0400
committerRyan Kavanagh <ryanakca@kubuntu.org>2011-08-25 07:43:14 -0400
commit44465839834a816f4dafa25287e0e6508b70bc2a (patch)
treea6d3f36590c3bbbb1fbd0d27e615e8b6e9a0d5d9 /.urxvt/mark-and-yank
parentAdded config for mail screen (diff)
Added custom perl extensions
Diffstat (limited to '')
-rw-r--r--.urxvt/mark-and-yank653
1 files changed, 653 insertions, 0 deletions
diff --git a/.urxvt/mark-and-yank b/.urxvt/mark-and-yank
new file mode 100644
index 0000000..e888507
--- /dev/null
+++ b/.urxvt/mark-and-yank
@@ -0,0 +1,653 @@
+#! perl
+# ------------------------------------------------------------------------
+# Author: Bart Trojanowski <bart@jukie.net>
+#
+# This script intends to provide functionality similar to screen's
+# copy-mode, but instead copy to the X clipboard. In order to copy to
+# the clipboard we need either the Clipboard.pm from CPAN or the xclip
+# command line utility.
+#
+# More details here: http://www.jukie.net/~bart/blog/tag/urxvt
+#
+# This script is based on the mark-urls script from the rxvt-unicode
+# distribution.
+#
+# ------------------------------------------------------------------------
+# configuration
+#
+# Put this in your .Xdefaults
+#
+# URxvt.keysym.M-y: perl:mark-and-yank:activate_mark_mode
+# URxvt.keysym.M-u: perl:mark-and-yank:activate_mark_url_mode
+# URxvt.perl-lib: /home/jukie/bart/.urxvt/
+# URxvt.perl-ext: mark-and-yank
+# URxvt.urlLauncher: firefox
+#
+# you might have to edit the perl-lib line.
+#
+# ------------------------------------------------------------------------
+
+use List::Util qw(first max maxstr min minstr reduce shuffle sum);
+
+
+# same url as used in "selection"
+my $url_matcher =
+ qr{(
+ (?:https?://|ftp://|news://|mailto:|file://)[ab-zA-Z0-9\-\@;\/?:&=%\$_.+!*\x27(),~#]+
+ [ab-zA-Z0-9\-\@;\/?:&=%\$_+!*\x27()~] # exclude some trailing characters (heuristic)
+ )}x;
+
+sub canonicalise_coordinates {
+ if ($_[0] > $_[2] || ($_[0] == $_[2] && $_[1] > $_[3])) {
+ (@_[2,3],@_[0,1])
+ } else {
+ (@_);
+ }
+}
+
+sub clamp { min( max( $_[0], $_[1] ), $_[2] ) }
+
+sub on_start {
+ my ($term) = @_;
+
+ $term->{have_Clipboard} = eval { require Clipboard; };
+ if ($term->{have_Clipboard}) {
+ import Clipboard;
+ }
+
+ $term->{browser} = $term->x_resource ("urlLauncher") || "x-www-browser";
+
+ ()
+}
+
+sub open_url {
+ my ($term, $url) = @_;
+
+ $term->exec_async (split(/[[:blank:]]/, $term->{browser}), $url);
+}
+
+sub on_line_update {
+ my ($term, $row) = @_;
+
+ return unless ($term->x_resource ("underlineURLs") eq "true");
+
+ # 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;
+
+ # mark all characters as underlined. we _must_ not toggle underline,
+ # as we might get called on an already-marked url.
+ $_ |= urxvt::RS_Uline
+ for @{$rend}[ $-[1] .. $+[1] - 1];
+
+ $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) {
+ if ($-[1] <= $col && $+[1] >= $col) {
+ open_url($term, $1);
+ return 1;
+ }
+ }
+ }
+
+ ()
+}
+
+# ------------------------------------------------------------------------
+
+my %key2mod = (
+ 65505 => urxvt::ShiftMask,
+ 65507 => urxvt::ControlMask,
+ 65513 => urxvt::Mod1Mask, # Alt
+ 65514 => urxvt::Mod1Mask, # Alt
+ 65515 => urxvt::Mod4Mask, # Super
+ 65516 => urxvt::Mod4Mask, # Super
+);
+my $mod = 0;
+
+#my %mod = ( 'control' => 0, 'shift' => 0 );
+
+my $mark_mode_active = 0;
+my @backup_cursor = ();
+my @visual_start = ();
+my $visual_mode = 0; # 'v', 'V', or '^v'
+my @cursor = ();
+my $url_selected = -1;
+my @url_db = ();
+my $first_mark_set = 0;
+my $msg_timeout = 2;
+
+# ------------------------------------------------------------------------
+
+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<=$row_end; $row++) {
+
+ # 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 $url = $1;
+
+ my %h = ( 'row' => $row,
+ 'col_from' => $-[1],
+ 'col_to' => $+[1] - 1,
+ 'url' => $url);
+ push @url_db, \%h;
+ }
+ }
+
+ # 0 for none, positive count otherwise
+ return $#url_db + 1;
+}
+
+sub status_message {
+ my ($self, $text, $timeout) = @_;
+ $timeout = $msg_timeout unless defined $timeout;
+ $self->{msg} = {
+ ov => $self->overlay (0, -1, length($text)+1, 1, urxvt::OVERLAY_RSTYLE, 1),
+ to => urxvt::timer
+ ->new
+ ->start (urxvt::NOW + $timeout)
+ ->cb (sub {
+ delete $self->{msg};
+ }),
+ };
+ $self->{msg}{ov}->set(0,0,$text);
+}
+
+sub on_user_command {
+ my ($term, $cmd) = @_;
+
+ if ($cmd eq "mark-and-yank:activate_mark_mode") {
+ activate_mark_mode($term);
+
+ } elsif ($cmd eq "mark-and-yank:activate_mark_url_mode") {
+ activate_mark_url_mode($term);
+ }
+
+ status_message ($term, "urxvt copy mode started");
+
+ ()
+}
+
+# ------------------------------------------------------------------------
+
+sub on_key_press {
+ my ($term, $event, $keysym, $octets) = @_;
+
+ foreach my $key (keys %key2mod) {
+ if ($keysym == $key) {
+ $mod |= $key2mod{$key};
+ return ();
+ }
+ }
+
+ # ignore all input when we are active
+ $mark_mode_active && return 1;
+
+ ()
+}
+
+sub on_key_release {
+ my ($term, $event, $keysym) = @_;
+
+ foreach my $key (keys %key2mod) {
+ if ($keysym == $key) {
+ $mod &= ~$key2mod{$key};
+ return ();
+ }
+ }
+
+ return () unless ($mark_mode_active);
+
+ my $ch = chr($keysym);
+
+ if ($mod & urxvt::ShiftMask && $ch =~ m/[[:alpha:]]/) {
+ $ch = uc $ch;
+ $mod &= ~urxvt::ShiftMask;
+ }
+
+ if (!$mod && $keysym == 65307) { # <esc>
+ deactivate_mark_mode ($term);
+ visual_mode_disable ($term, @cursor);
+
+ } elsif (($mod & urxvt::ControlMask) && $ch eq 'c') {# ^c - abort
+ deactivate_mark_mode ($term);
+ visual_mode_disable ($term, @cursor);
+
+ } elsif (!$mod && $keysym == 65293) { # <enter>
+ if ($first_mark_set) {
+ do_copy($term, @visual_start, @cursor);
+
+ deactivate_mark_mode ($term);
+ visual_mode_disable ($term, @cursor);
+ } else {
+ my %url = get_active_url($term);
+
+ if (not %url) {
+ $first_mark_set = 1;
+
+ visual_mode_enable ($term, 'v', @cursor);
+ } else {
+ my $urltext = $url{url};
+
+ $urltext =~ s/\(["|><&()]\)/\\$1/;
+ open_url($term, $urltext);
+
+ deactivate_mark_mode ($term);
+ visual_mode_disable ($term, @cursor);
+ }
+ }
+
+ } elsif (!$mod && $keysym == 32) { # <space>
+ if ($first_mark_set) {
+ do_copy($term, @visual_start, @cursor);
+
+ deactivate_mark_mode ($term);
+ visual_mode_disable ($term, @cursor);
+ } else {
+
+ $first_mark_set = 1;
+
+ visual_mode_enable ($term, 'v', @cursor);
+ }
+
+ } elsif (!$mod && $ch eq 'o') { # o - go to other end of region
+ if ($first_mark_set) {
+ my @dest = @visual_start;
+ @visual_start = @cursor;
+ @cursor = @dest;
+
+ $term->screen_cur (@dest);
+ $term->want_refresh;
+ }
+
+ } elsif (($mod & urxvt::ControlMask) && $ch eq 'w') {# w - copy the word under the cursor
+ my ($y1, $x1, $y2, $x2) = (@cursor, @cursor);
+
+ --$x1 while substr($term->ROW_t($y1), $x1 - 1, 1) =~ m/\w/;
+ ++$x2 while substr($term->ROW_t($y2), $x2 + 1, 1) =~ m/\w/;
+
+ do_copy($term, $y1, $x1, $y2, $x2);
+
+ deactivate_mark_mode ($term);
+ visual_mode_disable ($term, @cursor);
+
+ } elsif ($ch eq 'Y') { # Y - yank from cursor to EOL
+ do_copy($term, @cursor, $cursor[0], $term->ROW_l($cursor[0]) - $cursor[0]);
+
+ deactivate_mark_mode ($term);
+ visual_mode_disable ($term, @cursor);
+
+ } elsif (!$mod && $ch eq 'y') { # y - yank selected
+ my %url = get_active_url ($term);
+
+ do_copy($term, $url{row}, $url{col_from}, $url{row}, $url{col_to}) if %url;
+
+ deactivate_mark_mode ($term);
+ visual_mode_disable ($term, @cursor);
+
+ } elsif (($mod & urxvt::ControlMask) && (($ch eq 'n') || ($ch eq 'p'))) {
+ # ^n and ^p to cycle list
+ my $dir = ($ch eq 'n') ? 1 : -1;
+ move_url_highlight ($term, $dir);
+ visual_mode_disable ($term, @cursor);
+
+ } elsif (($mod & urxvt::ControlMask) && (($ch eq 'f') || ($ch eq 'b'))) {
+ # ^f and ^b to scroll
+ my $ofs = ($ch eq 'f') ? 1 : -1;
+ visual_mode_update ($term, \@cursor, [$cursor[0] + $ofs*($term->nrow - 1), $cursor[1]]);
+
+ } elsif (!$mod && $ch eq 'h') { # left
+ if ($cursor[1] > 0) {
+ visual_mode_update ($term, \@cursor, [$cursor[0], $cursor[1] - 1]);
+ }
+
+ } elsif (!$mod && $ch eq 'j') { # down
+ if ($cursor[0] < $term->nrow) {
+ visual_mode_update ($term, \@cursor, [$cursor[0] + 1, $cursor[1]]);
+ }
+
+ } elsif (!$mod && $ch eq 'k') { # up
+ if ($cursor[0] > $term->top_row) {
+ visual_mode_update ($term, \@cursor, [$cursor[0] - 1, $cursor[1]]);
+ }
+
+ } elsif (!$mod && $ch eq 'l') { # right
+ if ($cursor[1] < ($term->ncol - 1)) {
+ visual_mode_update ($term, \@cursor, [$cursor[0], $cursor[1] + 1]);
+ }
+
+ } elsif ($ch eq 'H') {
+ visual_mode_update ($term, \@cursor, [0, $cursor[1]]);
+
+ } elsif ($ch eq 'M') {
+ visual_mode_update ($term, \@cursor, [$term->nrow / 2, $cursor[1]]);
+
+ } elsif ($ch eq 'L') {
+ visual_mode_update ($term, \@cursor, [$term->nrow, $cursor[1]]);
+
+ } elsif ($ch eq '^') {
+ $term->ROW_t($cursor[0]) =~ m/^\s*/;
+ visual_mode_update ($term, \@cursor, [$cursor[0], $+[0]]);
+
+ } elsif ($ch eq '0') {
+ visual_mode_update ($term, \@cursor, [$cursor[0], 0]);
+
+ } elsif ($ch eq '$') {
+ visual_mode_update ($term, \@cursor, [$cursor[0], $term->ROW_l($cursor[0]) - 1]);
+
+ } elsif ($ch eq 'g') {
+ visual_mode_update ($term, \@cursor, [$term->top_row, 0]);
+
+ } elsif ($ch eq 'G') {
+ visual_mode_update ($term, \@cursor, [$term->nrow - 1, $term->ROW_l($term->nrow - 1) - 1]);
+
+ } elsif ($ch eq 'w') {
+ my @dest = @cursor;
+ my $line = $term->ROW_t($dest[0]);
+ ++$dest[1] while substr($line, $dest[1], 1) =~ m/\w/;
+ until (substr($line, $dest[1], 1) =~ m/\w/) {
+ ++$dest[1];
+ next if $dest[1] <= $term->ROW_l($dest[0]);
+ ++$dest[0];
+ return 1 if $dest[0] >= $term->nrow;
+ $dest[1] = 0;
+ $line = $term->ROW_t($dest[0]);
+ }
+
+ visual_mode_update ($term, \@cursor, \@dest);
+
+ } elsif ($ch eq 'e') {
+ my @dest = @cursor;
+ my $line = $term->ROW_t($dest[0]);
+ ++$dest[1];
+ ++$dest[1] while substr($line, $dest[1], 1) =~ m/\W/;
+ until (substr($line, $dest[1], 1) =~ m/\w/) {
+ ++$dest[1];
+ next if $dest[1] <= $term->ROW_l($dest[0]);
+ ++$dest[0];
+ return 1 if $dest[0] >= $term->nrow;
+ $dest[1] = 0;
+ $line = $term->ROW_t($dest[0]);
+ }
+ ++$dest[1] while substr($line, $dest[1] + 1, 1) =~ m/\w/;
+
+ visual_mode_update ($term, \@cursor, \@dest);
+
+ } elsif ($ch eq 'b') {
+ my @dest = @cursor;
+ my $line = $term->ROW_t($dest[0]);
+ # unless at the beginning of a word, jump to the beginning
+ --$dest[1] while ($dest[1] >= 1 && substr($line, $dest[1] - 1, 1) =~ m/\w/);
+ if ($dest[1] == $cursor[1]) { # at beginning of a word
+ # skip non-word characters
+ until ($dest[1] >= 1 && substr($line, $dest[1] - 1, 1) =~ m/\w/) {
+ --$dest[1];
+ next if $dest[1] > 0;
+ --$dest[0];
+ return 1 if $dest[0] < 0;
+ $dest[1] = $term->ROW_l($dest[0]);
+ $line = $term->ROW_t($dest[0]);
+ }
+ --$dest[1] while $dest[1] >= 1 && substr($line, $dest[1] - 1, 1) =~ m/\w/;
+ }
+
+ visual_mode_update ($term, \@cursor, \@dest);
+
+ } elsif ($ch eq 'W') {
+ my @dest = @cursor;
+ my $line = $term->ROW_t($dest[0]);
+ ++$dest[1] while substr($line, $dest[1], 1) =~ m/\S/;
+ until (substr($line, $dest[1], 1) =~ m/\S/) {
+ ++$dest[1];
+ next if $dest[1] <= $term->ROW_l($dest[0]);
+ ++$dest[0];
+ return 1 if $dest[0] >= $term->nrow;
+ $dest[1] = 0;
+ $line = $term->ROW_t($dest[0]);
+ }
+
+ visual_mode_update ($term, \@cursor, \@dest);
+
+ } elsif ($ch eq 'E') {
+ my @dest = @cursor;
+ my $line = $term->ROW_t($dest[0]);
+ ++$dest[1];
+ ++$dest[1] while substr($line, $dest[1], 1) =~ m/\s/;
+ until (substr($line, $dest[1], 1) =~ m/\S/) {
+ ++$dest[1];
+ next if $dest[1] <= $term->ROW_l($dest[0]);
+ ++$dest[0];
+ return 1 if $dest[0] >= $term->nrow;
+ $dest[1] = 0;
+ $line = $term->ROW_t($dest[0]);
+ }
+ ++$dest[1] while substr($line, $dest[1] + 1, 1) =~ m/\S/;
+
+ visual_mode_update ($term, \@cursor, \@dest);
+
+ } elsif ($ch eq 'B') {
+ my @dest = @cursor;
+ my $line = $term->ROW_t($dest[0]);
+ --$dest[1] while ($dest[1] >= 1 && substr($line, $dest[1] - 1, 1) =~ m/\S/);
+ until ($dest[1] >= 1 && substr($line, $dest[1] - 1, 1) =~ m/\S/) {
+ --$dest[1];
+ next if $dest[1] > 0;
+ --$dest[0];
+ return 1 if $dest[0] < 0;
+ $dest[1] = $term->ROW_l($dest[0]);
+ $line = $term->ROW_t($dest[0]);
+ }
+ --$dest[1] while $dest[1] >= 1 && substr($line, $dest[1] - 1, 1) =~ m/\S/;
+
+ visual_mode_update ($term, \@cursor, \@dest);
+ }
+
+ 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];
+
+ return %{$url_db[$url_selected]};
+}
+
+sub do_copy {
+ my $term = shift;
+
+ my ($y1, $x1, $y2, $x2) = canonicalise_coordinates(@_);
+
+ $term->selection_beg($y1, $x1);
+ $term->selection_end($y2, 1 + $x2);
+ $term->selection_make(urxvt::CurrentTime);
+
+ my $text = $term->selection();
+
+ if ($term->{have_Clipboard}) {
+ Clipboard->copy($text);
+ } else {
+ my $pid = open(XCLIP, "|xclip -i");
+ print XCLIP $text;
+ close(XCLIP)
+ }
+
+ status_message ($term, "copied ".length($text)." characters into clipboard");
+}
+
+# ------------------------------------------------------------------------
+
+sub move_url_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 visual_mode_enable {
+ my ($term, $mode, @cur) = @_;
+
+ $visual_mode = $mode;
+ @visual_start = @cur;
+}
+
+sub visual_mode_disable {
+ my ($term, @cursor) = @_;
+
+ if ($visual_mode) {
+ $term->scr_xor_span(canonicalise_coordinates(@visual_start, @cursor));
+
+ $visual_mode = 0;
+
+ $term->want_refresh;
+ }
+}
+
+sub visual_mode_update {
+ my ($term, $oldref, $newref) = @_;
+ my @old = @$oldref;
+ my @new = @$newref;
+
+ @cursor = @new;
+
+ $cursor[0] = clamp $term->top_row, $cursor[0], $term->nrow - 1;
+
+ if ($visual_mode eq 'v') {
+ $term->scr_xor_span(canonicalise_coordinates(@old, @new));
+ }
+
+ $term->screen_cur (@cursor);
+
+ $term->view_start (clamp $cursor[0] - $term->nrow + 1,
+ $term->view_start,
+ min 0, $cursor[0]);
+
+ $term->want_refresh;
+}
+
+# ------------------------------------------------------------------------
+
+sub activate_mark_mode {
+ my ($term) = @_;
+
+ print "title: " . ($term->resource("title")) . "\n";
+ print "name: " . ($term->resource("name")) . "\n";
+ print "term_name: " . ($term->resource("term_name")) . "\n";
+ print "color: " . ($term->resource("color")) . "\n";
+
+ my ($row, $col) = $term->screen_cur;
+ print "cursor: $row / $col \n";
+
+ $mark_mode_active = 1;
+ @backup_cursor = @cursor = $term->screen_cur;
+}
+
+sub activate_mark_url_mode {
+ my ($term) = @_;
+
+ if ($mark_mode_active) {
+
+ move_url_highlight ($term, -1);
+
+ } elsif ( do_scan_for_urls ($term) ) {
+
+ $term->{save_view_start} = $term->view_start;
+
+ move_url_highlight ($term, 0);
+
+ if ($url_selected > -1) {
+ $mark_mode_active = 1;
+ @backup_cursor = @cursor = $term->screen_cur;
+ }
+ }
+}
+
+sub deactivate_mark_mode {
+ my ($term) = @_;
+
+ do_highlight ($term, 0);
+
+ $mark_mode_active = 0;
+ $term->screen_cur (@backup_cursor);
+ $first_mark_set = 0;
+ $url_selected = -1;
+
+ $term->view_start ($term->{save_view_start});
+ $term->want_refresh;
+}
+
+# vim: set et ts=4 sw=4: