handler_paste: print URL in debug output
[fcscs/fcscs.git] / bin / fcscs
index 054842aeebd915e25deedbc36a876f72655bc253..e2a89b71b3d649fc9af254b3c1e0571cf129eee2 100755 (executable)
--- a/bin/fcscs
+++ b/bin/fcscs
@@ -385,8 +385,11 @@ sub get_regex_matches {
 
     my @matches;
     while ($input->{string} =~ /$regex/g) {
+        my $offset = $-[1];
+        die "Match group required in regex '$regex'" if not defined $offset;
+
         my ($x, $y) = input_match_offset_to_coordinates($input->{width},
-                                                        $-[0]);
+                                                        $offset);
         push @matches, { x => $x, y => $y, string => $1 };
     }
     return @matches;
@@ -458,6 +461,9 @@ sub run_in_background {
         my $pid = fork;
         defined $pid or die $!;
         if ($pid == 0) { # child
+            # Disable debug mode as writing will fail with closed STDERR.
+            $config->{setting}{debug} = 0;
+
             $sub->();
         }
         exit;
@@ -542,7 +548,7 @@ sub mapping_paste {
 
     debug $config, 'mapping_paste', 'started';
 
-    $config->{state}{handler} = \&handler_paste;
+    $config->{state}{handler} = $config->{handler}{paste};
 
     $screen->prompt(flags => 'P'); # paste
     $screen->draw_prompt($config);
@@ -555,7 +561,7 @@ sub mapping_yank {
 
     debug $config, 'mapping_yank', 'started';
 
-    $config->{state}{handler} = \&handler_yank;
+    $config->{state}{handler} = $config->{handler}{yank};
 
     $screen->prompt(flags => 'Y'); # yank
     $screen->draw_prompt($config);
@@ -574,7 +580,7 @@ sub mapping_mode_path {
     return {
         select  => 'path select',
         matches => \@matches,
-        handler => \&handler_yank,
+        handler => $config->{handler}{yank},
     };
 }
 sub mapping_mode_url {
@@ -586,7 +592,7 @@ sub mapping_mode_url {
     return {
         select  => 'url select',
         matches => \@matches,
-        handler => \&handler_url,
+        handler => $config->{handler}{url},
     };
 }
 
@@ -645,7 +651,7 @@ sub mapping_mode_search {
     return {
         select  => 'search',
         matches => \@last_matches,
-        handler => \&handler_yank,
+        handler => $config->{handler}{yank},
     };
 }
 
@@ -667,7 +673,7 @@ sub handler_yank {
     # Use a temporary file to prevent leaking the yanked data to other users
     # with the command line, e.g. ps aux or top.
     my ($fh, $tmp) = File::Temp::tempfile(); # dies on its own
-    print $fh $screen->encode($match->{string});
+    print $fh $screen->encode($match->{value});
     close $fh or die $!;
 
     if ($config->{setting}{multiplexer} eq 'screen') {
@@ -723,16 +729,12 @@ sub handler_paste {
 sub handler_url {
     my ($screen, $config, $match) = @_;
 
-    debug $config, 'handler_url', 'started';
+    debug $config, 'handler_url', "opening $match->{value}";
 
     run_in_background($config, sub {
-        my $url = defined $match->{url}
-                ? $match->{url}
-                : $match->{string};
-
         my @cmd = map { $screen->encode($_) } (
             @{$config->{setting}{browser}},
-            $url,
+            $match->{value},
         );
         run_command($config, \@cmd);
     });
@@ -952,7 +954,7 @@ my %setting = (
 Example:
 
     # Select all non-whitespace characters when searching for paths.
-    $config{regex}{path} = qr{\S+};
+    $config{regex}{path} = qr{(\S+)};
 
 =cut
 my %regex = (
@@ -961,6 +963,44 @@ my %regex = (
     path => qr{(~?[a-zA-Z0-9_./-]*/[a-zA-Z0-9_./-]+)},
 );
 
+=head2 HANDLERS
+
+Handlers are used to perform actions on the selected string.
+
+The following handlers are available, defaults in parentheses.
+
+=over
+
+=item B<yank>  used to yank (copy) selection to paste buffer (C<\&handler_yank>)
+
+=item B<paste> used to paste selection into window (C<\&handler_paste>)
+
+=item B<url>   used to open URLs (e.g. in a browser) (C<\&handler_url>)
+
+=back
+
+Example:
+
+    # Download YouTube videos with a custom wrapper, handle all other URLs
+    # with the default URL handler.
+    $config{handler}{url} = sub {
+        my ($screen, $config, $match) = @_;
+
+        if ($match->{value} =~ m{^https://www.youtube.com/}) {
+            return run_in_background($config, sub {
+                run_command($config, ['youtube-dl-wrapper', $match->{value}]);
+            });
+        }
+        handler_url(@_);
+    };
+
+=cut
+my %handler = (
+    yank  => \&handler_yank,
+    paste => \&handler_paste,
+    url   => \&handler_url,
+);
+
 my %state = (
     initial => 1, # used by select_match() for 'initial_mode'
     handler => undef,
@@ -992,9 +1032,7 @@ Used as mappings, see L</MAPPINGS> above.
     handler_paste()
     handler_url()
 
-Used as handler to yank, paste selection or open URL in browser. They are
-either set by the matching mapping function (C<mapping_paste()>, etc.) or
-configured by the current mode.
+Used as handler to yank, paste selection or open URL in browser.
 
     get_regex_matches()
     select_match()
@@ -1049,6 +1087,7 @@ $config{mapping}{simple} = \%mapping_simple;
 $config{attribute}       = \%attribute;
 $config{setting}         = \%setting;
 $config{regex}           = \%regex;
+$config{handler}         = \%handler;
 $config{state}           = \%state;
 
 package Fcscs {
@@ -1167,10 +1206,14 @@ RESULT:
             goto RESULT; # reprocess special entries in result
         }
         if (defined $result->{match}) {
+            if (not defined $result->{match}->{value}) {
+                $result->{match}->{value} = $result->{match}->{string};
+            }
+
             debug \%config, 'input', 'running handler';
             my $handler = $config{state}{handler};                 # set by user
             $handler = $result->{handler} unless defined $handler; # set by mapping
-            $handler = \&handler_yank     unless defined $handler; # fallback
+            $handler = $config{handler}{yank} unless defined $handler; # fallback
             $handler->($screen, \%config, $result->{match});
             last;
         }