The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Perl::Tidy::Sweetened::Keyword::SubSignature;

# ABSTRACT: Perl::Tidy::Sweetened filter plugin to define new subroutine keywords

use 5.010;    # Needed for balanced parens matching with qr/(?-1)/
use strict;
use warnings;
use Carp;

our $VERSION = '0.20';

# Regex to match balanced parans. Reproduced from Regexp::Common to avoid
# adding a non-core dependency.
#   $RE{balanced}{-parens=>'()'};
# The (?-1) construct requires 5.010
our $Clause = '(?:((?:\((?:(?>[^\(\)]+)|(?-1))*\))))';

sub new {
    my ( $class, %args ) = @_;
    croak 'keyword not specified' if not exists $args{keyword};
    croak 'marker not specified'  if not exists $args{marker};
    return bless {%args}, $class;
}

sub keyword { return $_[0]->{keyword} }
sub marker  { return $_[0]->{marker} }

sub emit_sub {
    my ( $self, $subname, $signature, $prelude, $returns ) = @_;

    # Store the signature and returns() for later use
    my $id = $self->{counter}++;
    $self->{store}->{$id} = { signature => $signature, returns => $returns };

    return sprintf 'sub %s %s #__%s %s', $subname, $prelude, $self->marker, $id;
}

sub emit_keyword {
    my ( $self, $subname, $padding, $prelude, $id ) = @_;

    # Get the signature and returns() from store
    my $signature = $self->{store}->{$id}->{signature} // '';
    my $returns   = $self->{store}->{$id}->{returns}   // '';
    $signature = ' ' . $signature if length $signature;
    $returns   = ' ' . $returns   if length $returns;

    return sprintf '%s %s%s%s%s%s',
      $self->keyword,
      $subname,
      $signature,
      $returns,
      $padding,
      $prelude;
}

sub prefilter {
    my ( $self, $code ) = @_;
    my $keyword = $self->keyword;

    $code =~ s{
        ^\s*\K                    # okay to have leading whitespace (preserve)
        $keyword             \s+  # the "func/method" keyword
        (?<subname> \w+)     \s*  # the function name
        (?<params> $Clause)? \s*  # optional parameter list (multi-line ok)
        (?<returns> returns \s* $Clause )?
                                  # optional returns(...)
        (?<prelude> .*?)          # anything else (ie, comments) including brace
        $
    }{
        $self->emit_sub( $+{subname}, $+{params}, $+{prelude}, $+{returns} )
    }egmx;

    return $code;
}

sub postfilter {
    my ( $self, $code ) = @_;
    my $marker = $self->marker;

    # Convert back to method
    $code =~ s{
        ^\s*\K                 # preserve leading whitespace
        sub               \s+  # keyword was convert to sub
        (?<subname> \w+ ) \b   # the method name and a word break
        (?<prelude> .*? ) \s*  # anything orig following the declaration
        \#__$marker \s+        # our magic token
        (?<id> \d+)            # our sub identifier
        [ ]*                   # trailing spaces (not all whitespace)
    }{
        $self->emit_keyword( $+{subname}, '', $+{prelude}, $+{id} );
    }egmx;

    # Check to see if tidy turned it into "sub name\n{ #..."
    $code =~ s{
        ^\s*\K                   # preserve leading whitespace
        sub                 \s+  # method was converted to sub
        (?<subname> \w+)\n  \s*  # the method name and a newline
        \{ (?<prelude> .*?) [ ]* # opening brace on newline followed orig comments
        \#__$marker         \s+  # our magic token
        (?<id> \d+)              # our sub identifier
        [ ]*                     # trailing spaces (not all whitespace)
    }{
        $self->emit_keyword( $+{subname}, ' \{', $+{prelude}, $+{id} );
    }egmx;

    return $code;
}

1;

__END__

=pod

=head1 NAME

Perl::Tidy::Sweetened::Keyword::SubSignature - Perl::Tidy::Sweetened filter plugin to define new subroutine keywords

=head1 VERSION

version 0.20

=head1 SYNOPSIS

    our $plugins = Perl::Tidy::Sweetened::Pluggable->new();

    $plugins->add_filter(
        Perl::Tidy::Sweetened::Keyword::SubSignature->new(
            keyword => 'method',
            marker  => 'METHOD',
        ) );

=head1 DESCRIPTION

This is a Perl::Tidy::Sweetened filter which enables the definition of
arbitrary keywords for subroutines.

=head1 THANKS

See L<Perl::Tidy::Sweetened>

=head1 BUGS

Please report any bugs or suggestions at
L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Perl-Tidy-Sweetened>

=head1 AUTHOR

Mark Grimes, E<lt>mgrimes@cpan.orgE<gt>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2013 by Mark Grimes, E<lt>mgrimes@cpan.orgE<gt>.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut