=head1 USAGE
+Short overview of the general usage, details below:
+
+ - start fcscs
+ - configure actions (optional)
+ - enable pasting
+ - ...
+ - select mode (optional, URL mode is used on startup):
+ - f: file paths
+ - u: URLs
+ - ...
+ - /: search mode
+ - for `normal' modes:
+ - select match by displayed number or <return> for lowest numbered
+ match
+ - configured action is run, e.g. URL is opened with browser
+ - for `search' mode:
+ - perform incremental search
+ - on <return> go to `normal' mode to select a match
+ - after the match is selected wait for confirmation or extension
+ - confirmation: <return> run previously selected action
+ - extension: change match, e.g. select complete word or line
+
GNU Screen setup (add to F<~/.screenrc>):
bind ^B eval "hardcopy $HOME/.tmp/screen-fcscs" "screen fcscs $HOME/.tmp/screen-fcscs"
fcscs /path/to/screen-or-tmux-fcscs-file
+
+=head1 MODES
+
=cut
my ($x, $y) = input_match_offset_to_coordinates($input->{width},
$offset);
- push @matches, { x => $x, y => $y, string => $1 };
+ push @matches, { x => $x, y => $y, offset => $offset, string => $1 };
}
return @matches;
}
$screen->refresh;
}
+ $screen->draw_matches($config, $matches, []); # remove matches
+
foreach (@{$matches}) {
return { match => $_ } if $_->{id} == $number;
}
return { match => undef };
}
+sub extend_match_regex_left {
+ my ($line, $match, $regex) = @_;
+
+ my $s = reverse substr $line, 0, $match->{x};
+ if ($s =~ /^($regex)/) {
+ $match->{string} = reverse($1) . $match->{string};
+ $match->{x} -= length $1;
+ $match->{offset} -= length $1;
+ }
+ return;
+}
+sub extend_match_regex_right {
+ my ($line, $match, $regex) = @_;
+
+ my $s = substr $line, $match->{x} + length $match->{string};
+ if ($s =~ /^($regex)/) {
+ $match->{string} .= $1;
+ }
+ return;
+}
+sub extend_match {
+ my ($screen, $config, $input, $match) = @_;
+
+ debug $config, 'extend_match', 'started';
+
+ $screen->prompt(name => 'extend', value => undef);
+ $screen->draw_prompt($config);
+
+ delete $match->{id}; # don't draw any match ids
+ $screen->draw_matches($config, [], [$match]);
+ $screen->refresh;
+
+ my $line = $input->{lines}[$match->{y}];
+
+ while (1) {
+ my $match_old = \%{$match};
+
+ my $char = $screen->getch;
+ if ($char eq "\n") { # accept match
+ last;
+
+ } elsif ($char eq 'w') { # select current word (both directions)
+ extend_match_regex_left($line, $match, qr/\w+/);
+ extend_match_regex_right($line, $match, qr/\w+/);
+ } elsif ($char eq 'b') { # select current word (only left)
+ extend_match_regex_left($line, $match, qr/\w+/);
+ } elsif ($char eq 'e') { # select current word (only right)
+ extend_match_regex_right($line, $match, qr/\w+/);
+
+ } elsif ($char eq 'W') { # select current WORD (both directions)
+ extend_match_regex_left($line, $match, qr/\S+/);
+ extend_match_regex_right($line, $match, qr/\S+/);
+ } elsif ($char eq 'B') { # select current WORD (only left)
+ extend_match_regex_left($line, $match, qr/\S+/);
+ } elsif ($char eq 'E') { # select current WORD (only right)
+ extend_match_regex_right($line, $match, qr/\S+/);
+
+ } elsif ($char eq '^') { # select to beginning of line
+ extend_match_regex_left($line, $match, qr/.+/);
+ } elsif ($char eq '$') { # select to end of line
+ extend_match_regex_right($line, $match, qr/.+/);
+
+ # Allow mode changes if not overwritten by local mappings.
+ } elsif (defined $config->{mapping}{mode}{$char}) {
+ $screen->draw_matches($config, [$match_old], []); # clear match
+ return { key => $char };
+
+ } else {
+ next; # ignore unknown mappings
+ }
+
+ $screen->draw_matches($config, [$match_old], [$match]);
+ $screen->refresh;
+ }
+
+ debug $config, 'extend_match', 'done';
+
+ return { match => $match };
+}
+
sub mapping_paste {
my ($key, $screen, $config, $input) = @_;
}
+=head2 NORMAL MODES
+
+Normal modes select matches by calling a function which returns them, e.g. by
+using a regex.
+
+The following normal modes are available:
+
+=over 4
+
+=item B<path mode> select relative/absolute paths
+
+=item B<url mode> select URLs
+
+=back
+
+=cut
sub mapping_mode_path {
my ($key, $screen, $config, $input) = @_;
};
}
+=head2 SEARCH MODE (AND EXTEND MODE)
+
+Search mode is a special mode which lets you type a search string (a Perl
+regex) and then select one of the matches. Afterwards you can extend the
+match. For example select the complete word or to the end of the line. This
+allows quick selection of arbitrary text.
+
+The following mappings are available during the extension mode (not
+configurable at the moment):
+
+=over 4
+
+=item B<w> select current word
+
+=item B<b> extend word to the left
+
+=item B<e> extend word to the right
+
+=item B<W> select current WORD
+
+=item B<B> extend WORD to the left
+
+=item B<E> extend WORD to the right
+
+=item B<^> extend to beginning of line
+
+=item B<$> extend to end of line
+
+=back
+
+C<word> includes any characters matching C<\w+>, C<WORD> any non-whitespace
+characters (C<\S+>), just like in Vim.
+
+=cut
sub mapping_mode_search {
my ($key, $screen, $config, $input) = @_;
return {
select => 'search',
matches => \@last_matches,
+ extend => 1,
handler => $config->{handler}{yank},
};
}
$screen, \%config, $input,
$result->{matches});
$result->{handler} = $tmp->{handler};
+ $result->{extend} = $tmp->{extend};
+ goto RESULT; # reprocess special entries in result
+ }
+ if (defined $result->{extend}) {
+ debug \%config, 'input', 'extending match';
+ $result = extend_match($screen, \%config, $input,
+ $result->{match});
goto RESULT; # reprocess special entries in result
}
if (defined $result->{match}) {