App-Critique
view release on metacpan or search on metacpan
lib/App/Critique/Command/process.pm view on Meta::CPAN
my @tracked_files = $session->tracked_files;
# TODO:
# not all these options can be given together, so
# we should do some validation for that.
# - SL
if ( $opt->back ) {
$session->dec_file_idx;
$tracked_files[ $session->current_file_idx ]->forget_all;
}
if ( $opt->next ) {
$session->inc_file_idx;
}
if ( $opt->reset ) {
$session->reset_file_idx;
$_->forget_all foreach @tracked_files;
}
if ( my $idx = $opt->goto ) {
$session->set_file_idx( $idx );
}
if ( $session->current_file_idx == scalar @tracked_files ) {
info(HR_DARK);
info('All files have already been processed.');
info(HR_LIGHT);
info('- run `critique status` to see more information');
info('- run `critique process --reset` to review all files again');
info(HR_DARK);
return;
}
my ($idx, $file);
MAIN:
while (1) {
info(HR_DARK);
$idx = $session->current_file_idx;
$file = $tracked_files[ $idx ];
my $path = $file->relative_path( $session->git_work_tree_root );
info('Running Perl::Critic against (%s)', $path);
info(HR_LIGHT);
# perlcritic can fail, so lets guard against it and let the user
# decide if they want to carry on
my @violations;
eval {
@violations = $self->discover_violations( $session, $file, $opt );
1;
} or do {
info(HR_ERROR);
warn($@);
info(HR_LIGHT);
my $should_review = prompt_yn(
BOLD(sprintf 'A error has occurred do you want to continue?'),
{ default => 'y' }
);
unless ( $should_review ) { exit }
next;
};
# remember it the first time we use it
# but do not update it for each re-process
# which we do after each edit
$file->remember('violations' => scalar @violations)
unless $file->recall('violations');
if ( @violations == 0 ) {
info(ITALIC('No violations found, proceeding to next file.'));
next MAIN;
}
else {
my $should_review = prompt_yn(
BOLD(sprintf 'Found %d violations, would you like to review them?', (scalar @violations)),
{ default => 'y' }
);
if ( $should_review ) {
my ($reviewed, $edited) = (
$file->recall('reviewed') // 0,
$file->recall('edited') // 0,
);
foreach my $violation ( @violations ) {
$self->display_violation( $session, $file, $violation, $opt );
$reviewed++;
my $should_edit = prompt_yn(
BOLD('Would you like to fix this violation?'),
{ default => 'y' }
);
my $did_commit = 0;
if ( $should_edit ) {
$did_commit = $self->edit_violation( $session, $file, $violation );
$edited++ if $did_commit;
}
# keep state on disc ...
$file->remember('reviewed', $reviewed);
$file->remember('edited', $edited);
$self->cautiously_store_session( $session, $opt, $args );
if ( $did_commit ) {
info(HR_LIGHT);
info('File was edited, re-processing is required');
redo MAIN;
}
}
}
}
} continue {
$session->inc_file_idx;
$self->cautiously_store_session( $session, $opt, $args );
if ( ($idx + 1) == scalar @tracked_files ) {
info(HR_LIGHT);
info('Processing complete, run `status` to see results.');
last MAIN;
}
}
}
sub discover_violations {
my ($self, $session, $file, $opt) = @_;
my @violations = $session->perl_critic->critique( $file->path->stringify );
return @violations;
}
sub display_violation {
my ($self, $session, $file, $violation, $opt) = @_;
my $rel_filename = Path::Tiny::path( $violation->filename )->relative( $session->git_work_tree_root );
info(HR_DARK);
info(BOLD('Violation: %s'), $violation->description);
info(HR_DARK);
info('%s', $violation->explanation);
#if ( $opt->verbose ) {
# info(HR_LIGHT);
lib/App/Critique/Command/process.pm view on Meta::CPAN
sub edit_violation {
my ($self, $session, $file, $violation) = @_;
my $git = $session->git_wrapper;
my $rel_filename = $violation->filename;
my $abs_filename = Path::Tiny::path( $violation->filename )->relative( $session->git_work_tree_root );
my $policy = $violation->policy;
my $rewriter = $policy->can('rewriter') ? $policy->rewriter( $violation ) : undef;
my $cmd_fmt = $App::Critique::CONFIG{EDITOR};
my @cmd_args = (
$rel_filename,
$violation->line_number,
$violation->column_number
);
my $cmd = sprintf $cmd_fmt => @cmd_args;
EDIT:
if ( $rewriter && $rewriter->can_rewrite ) {
info(HR_LIGHT);
info('... attempting to re-write violation.');
my $document;
eval {
$document = $rewriter->rewrite;
1;
} or do {
error('Unable to re-write violation(%s) because (%s)', $violation, $@);
};
info(BOLD('Violation re-written successfully!'));
info('... attempting to save file(%s)', $abs_filename);
eval {
$document->save( $abs_filename );
1;
} or do {
error('Unable to save file(%s) because (%s)', $abs_filename, $@);
};
info(BOLD('File(%s) saved successfully!'), $abs_filename);
}
else {
system $cmd;
}
my $statuses = $git->status;
my @changed = $statuses->get('changed');
my $did_edit = scalar grep { my $from = $_->from; $abs_filename =~ /$from/ } @changed;
if ( $did_edit ) {
info(HR_DARK);
info('Changes detected, generating diff.');
info(HR_LIGHT);
info('%s', join "\n" => $git->RUN('diff', { 'color' => $App::Critique::CONFIG{COLOR} }));
my $policy_name = $violation->policy;
$policy_name =~ s/^Perl\:\:Critic\:\:Policy\:\://;
my $commit_msg = sprintf "%s - critique(%s)" => $violation->description, $policy_name;
CHOOSE:
info(HR_LIGHT);
my $commit_this_change = prompt_str(
(
BOLD('Commit Message:').
"\n\n ".(join "\n " => split /\n/ => $commit_msg)."\n\n".
BOLD("Press ENTER to accept this message, enter text to be appended to the commit message, or (n)o for more options.\n")
),
);
if ( !$commit_this_change ) {
info(HR_DARK);
info('Adding and commiting file (%s) to git', $abs_filename);
info(HR_LIGHT);
info('%s', join "\n" => $git->add($rel_filename, { v => 1 }));
info('%s', join "\n" => $git->commit({ v => 1, message => $commit_msg }));
my ($sha) = $git->rev_parse('HEAD');
$file->remember('shas' => [ @{ $file->recall('shas') || [] }, $sha ]);
$file->remember('commited' => ($file->recall('commited') || 0) + 1);
return 1;
}
elsif ( lc($commit_this_change) eq 'n' ) {
info(HR_LIGHT);
my $what_now = prompt_str(
BOLD('What would you like to do? edit the (f)ile, edit the (c)ommit message or (a)ppend the commit message'),
{ valid => sub { $_[0] =~ m/[fca]{1}/ } }
);
if ( $what_now eq 'c' ) {
info(HR_LIGHT);
$commit_msg = prompt_str( BOLD('Please write a commit message') );
$commit_msg =~ s/\\n/\n/g; # un-escape any newlines ...
goto CHOOSE;
}
elsif ( $what_now eq 'a' ) {
info(HR_LIGHT);
$commit_msg .= "\n\n" . prompt_str( BOLD('Please append the commit message') );
goto CHOOSE;
}
elsif ( $what_now eq 'f' ) {
goto EDIT;
}
}
else {
$commit_msg .= "\n\n" . $commit_this_change;
goto CHOOSE;
}
}
else {
RETRY:
info(HR_LIGHT);
my $what_now = prompt_str(
BOLD('No edits found, would like to (e)dit again, (s)kip this violation or (b)lame the file?'),
{ valid => sub { $_[0] =~ m/[esb]{1}/ } }
);
if ( $what_now eq 'e' ) {
goto EDIT;
}
elsif ( $what_now eq 's' ) {
return 0;
}
elsif ( $what_now eq 'b' ) {
info(HR_LIGHT);
info('%s', $self->blame_violation(
$session,
$rel_filename,
$violation->line_number
));
goto RETRY;
}
}
return 0;
}
1;
=pod
=head1 NAME
App::Critique::Command::process - Critique all the files.
=head1 VERSION
version 0.05
=head1 DESCRIPTION
This command will start or resume the critique session, allowing you to
step through the files and critique them. This current state of this
processing will be stored in the critique session file and so can be
stopped and resumed at any time.
Note, this is an interactive command, so ...
=head1 AUTHOR
Stevan Little <stevan@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2016 by Stevan Little.
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
__END__
( run in 0.667 second using v1.01-cache-2.11-cpan-6aa56a78535 )