package Padre::Document::Perl::Syntax;

use 5.008;
use strict;
use warnings;
use Padre::Constant          ();
use Padre::Util              ();
use Padre::Task::Syntax      ();
use Parse::ErrorString::Perl ();

our $VERSION = '1.00';
our @ISA     = 'Padre::Task::Syntax';

sub new {
	my $class = shift;
	my %args  = @_;

	if ( defined $ENV{PADRE_IS_TEST} ) {

		# Note: $ENV{PADRE_IS_TEST} is defined in t/44-perl-syntax.t
		# Run with console Perl to prevent failures while testing
		require Padre::Perl;
		$args{perl} = Padre::Perl::cperl();
	} else {

		# Otherwise run with user-preferred interpreter
		$args{perl} = $args{document}->get_interpreter;
	}

	$class->SUPER::new(%args);
}

sub syntax {
	my $self = shift;
	my $text = shift;

	# Localise newlines using Adam's magic "Universal Newline"
	# regex conveniently stolen from File::LocalizeNewlines.
	# (i.e. "conveniently" avoiding a bunch of dependencies)
	$text =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg;

	# Execute the syntax check
	my $stderr   = '';
	my $filename = undef;
	SCOPE: {

		# Create a temporary file with the Perl text
		require File::Temp;
		my $file = File::Temp->new( UNLINK => 1 );
		$filename = $file->filename;
		binmode( $file, ':encoding(UTF-8)' );

		# If this is a module, we will need to overwrite %INC to avoid the module
		# loading another module, which loads the system installed equivalent
		# of the package we are currently compile-testing.
		if ( $text =~ /^\s*package ([\w:]+)/ ) {
			my $module_file = $1 . '.pm';
			$module_file =~ s/::/\//g;
			$file->print("BEGIN {\n");
			$file->print("\t\$INC{'$module_file'} = '$file';\n");
			$file->print("}\n");
			$file->print("#line 1\n");
		}

		# Toggle syntax checking for certain regions
		$file->print( $self->_parse_comment_pragmas($text) );
		$file->close;

		my @cmd = ( $self->{perl} );

		# Append Perl command line options
		if ( $self->{project} ) {
			push @cmd, '-Ilib';
		}

		# Open a temporary file for standard error redirection
		my $err = File::Temp->new( UNLINK => 1 );
		$err->close;

		# Redirect perl's output to temporary file
		# NOTE: Please DO NOT use -Mdiagnostics since it will wrap
		# error messages on multiple lines and that would
		# complicate parsing (azawawi)
		push @cmd,
			(
			'-c',
			'<' . $file->filename,
			'2>' . $err->filename,
			);

		# We need shell redirection (list context does not give that)
		# Run command in directory
		Padre::Util::run_in_directory( join( ' ', @cmd ), $self->{project} );

		# Slurp Perl's stderr...
		open my $fh, '<', $err->filename or die $!;
		local $/ = undef;
		$stderr = <$fh>;
		close $fh;
	}

	# Short circuit if the syntax is OK and no other errors/warnings are present
	return [] if $stderr eq "- syntax OK\n";

	# Since we're not going to use -Mdiagnostics,
	# we will simply reuse Padre::ErrorString::Perl for Perl error parsing
	my @issues = Parse::ErrorString::Perl->new->parse_string($stderr);

	# We need the 'at' or 'near' clauses appended to the issue because
	# it is more meaningful
	for my $issue (@issues) {
		if ( defined( $issue->{at} ) ) {
			$issue->{message} .= ', at ' . $issue->{at};
		} elsif ( defined( $issue->{near} ) ) {
			$issue->{message} .= ', near "' . $issue->{near} . '"';
		}
	}

	return {
		issues => \@issues,
		stderr => $stderr,
	};
}

#
# Parses syntax checking comments blocks
# To disable Padre syntax check, please use:
# 	## no padre_syntax_check
# To enable again:
# 	## use padre_syntax_check
#
sub _parse_comment_pragmas {
	my $self = shift;
	my $text = shift;
	my $n    = "\\cM?\\cJ";

	if ( $text =~ /$n\s*\#\#\s+(use|no)\s+padre_syntax_check\s*/ ) {

		# Only process when there is 'use|no padre_syntax_check'
		# comment pragmas
		my $ignore = 0;
		my $output = '';
		for my $line ( split /^/, $text ) {
			if ( $line =~ /^\s*\#\#\s+(use|no)\s+padre_syntax_check\s*$/ ) {
				$ignore = ( $1 eq 'no' ) ? 1 : 0;
			} else {
				$output .= $ignore ? "# $line" : $line;
			}
		}

		# Return processed text
		return $output;
	}

	return $text;
}

1;

# Copyright 2008-2013 The Padre development team as listed in Padre.pm.
# LICENSE
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl 5 itself.