package LatexIndent::FileContents; # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # See http://www.gnu.org/licenses/. # # Chris Hughes, 2017 # # For all communication, please visit: https://github.com/cmhughes/latexindent.pl use strict; use warnings; use LatexIndent::Tokens qw/%tokens/; use LatexIndent::GetYamlSettings qw/%mainSettings/; use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active $is_m_switch_active/; use LatexIndent::LogFile qw/$logger/; use LatexIndent::Environment qw/$environmentBasicRegExp/; use LatexIndent::IfElseFi qw/$ifElseFiBasicRegExp/; use LatexIndent::Special qw/$specialBeginAndBracesBracketsBasicRegExp/; use LatexIndent::Heading qw/$allHeadingsRegexp/; use LatexIndent::Verbatim qw/%verbatimStorage/; use Data::Dumper; use Exporter qw/import/; our @EXPORT_OK = qw/find_file_contents_environments_and_preamble/; our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321 our $fileContentsCounter; sub find_file_contents_environments_and_preamble { my $self = shift; # store the file contents blocks in an array which, depending on the value # of indentPreamble, will be put into the verbatim hash, or otherwise # stored as children to be operated upon my @fileContentsStorageArray; # fileContents environments $logger->trace('*Searching for FILE CONTENTS environments (see fileContentsEnvironments)') if $is_t_switch_active; $logger->trace( Dumper( \%{ $mainSettings{fileContentsEnvironments} } ) ) if ($is_tt_switch_active); while ( my ( $fileContentsEnv, $yesno ) = each %{ $mainSettings{fileContentsEnvironments} } ) { if ( !$yesno ) { $logger->trace(" *not* looking for $fileContentsEnv as $fileContentsEnv:$yesno"); next; } $logger->trace("looking for $fileContentsEnv environments") if $is_t_switch_active; # the trailing * needs some care if ( $fileContentsEnv =~ m/\*$/ ) { $fileContentsEnv =~ s/\*$//; $fileContentsEnv .= '\*'; } my $fileContentsRegExp = qr/ ( \\begin\{ ($fileContentsEnv) # environment name captured into $2 \} # begin statement captured into $1 ) ( .*? # non-greedy match (body) into $3 ) ( \\end\{\2\} # end statement captured into $4 \h* # possible horizontal spaces ) (\R)? # possibly followed by a line break /sx; while ( ${$self}{body} =~ m/$fileContentsRegExp/sx ) { # create a new Environment object my $fileContentsBlock = LatexIndent::FileContents->new( begin => $1, body => $3, end => $4, name => $2, linebreaksAtEnd => { begin => 0, body => 0, end => $5 ? 1 : 0, }, modifyLineBreaksYamlName => "filecontents", ); # give unique id $fileContentsBlock->create_unique_id; # text wrapping can make the ID split across lines ${$fileContentsBlock}{idRegExp} = ${$fileContentsBlock}{id}; if ( $is_m_switch_active and ${ $mainSettings{modifyLineBreaks}{textWrapOptions} }{huge} ne "overflow" ) { my $IDwithLineBreaks = join( "\\R?\\h*", split( //, ${$fileContentsBlock}{id} ) ); ${$fileContentsBlock}{idRegExp} = qr/$IDwithLineBreaks/s; } # the replacement text can be just the ID, but the ID might have a line break at the end of it $fileContentsBlock->get_replacement_text; # count body line breaks $fileContentsBlock->count_body_line_breaks; # the above regexp, when used below, will remove the trailing linebreak in ${$self}{linebreaksAtEnd}{end} # so we compensate for it here $fileContentsBlock->adjust_replacement_text_line_breaks_at_end; # store the fileContentsBlock, and determine location afterwards push( @fileContentsStorageArray, $fileContentsBlock ); # log file output $logger->trace("FILECONTENTS environment found: ${$fileContentsBlock}{name}") if $is_t_switch_active; # remove the environment block, and replace with unique ID ${$self}{body} =~ s/$fileContentsRegExp/${$fileContentsBlock}{replacementText}/sx; $logger->trace("replaced with ID: ${$fileContentsBlock}{id}") if $is_tt_switch_active; } } # determine if body of document contains \begin{document} -- if it does, then assume # that the body has a preamble my $preambleRegExp = qr/ (.*?) (\R*\h*)? # linebreaks at end of body into $2 \\begin\{document\} /sx; my $preamble = q(); my $needToStorePreamble = 0; # try and find the preamble my $lookForPreamble = ${ $mainSettings{lookForPreamble} }{ ${$self}{fileExtension} }; $lookForPreamble = 1 if ( ${$self}{fileName} eq "-" and ${ $mainSettings{lookForPreamble} }{STDIN} ); if ( ${$self}{body} =~ m/$preambleRegExp/sx and $lookForPreamble ) { $logger->trace( "\\begin{document} found in body (after searching for filecontents)-- assuming that a preamble exists") if $is_t_switch_active; # create a preamble object $preamble = LatexIndent::Preamble->new( begin => q(), body => $1, end => q(), name => "preamble", linebreaksAtEnd => { begin => 0, body => $2 ? 1 : 0, end => 0, }, afterbit => ( $2 ? $2 : q() ) . "\\begin{document}", modifyLineBreaksYamlName => "preamble", ); # give unique id $preamble->create_unique_id; # text wrapping can make the ID split across lines ${$preamble}{idRegExp} = ${$preamble}{id}; if ( $is_m_switch_active and ${ $mainSettings{modifyLineBreaks}{textWrapOptions} }{huge} ne "overflow" ) { my $IDwithLineBreaks = join( "\\R?\\h*", split( //, ${$preamble}{id} ) ); ${$preamble}{idRegExp} = qr/$IDwithLineBreaks/s; } # get the replacement_text $preamble->get_replacement_text; # log file output $logger->trace("preamble found: preamble") if $is_t_switch_active; # remove the environment block, and replace with unique ID ${$self}{body} =~ s/$preambleRegExp/${$preamble}{replacementText}/sx; $logger->trace("replaced with ID: ${$preamble}{replacementText}") if $is_tt_switch_active; # indentPreamble set to 1 if ( $mainSettings{indentPreamble} ) { $logger->trace("storing ${$preamble}{id} for indentation (see indentPreamble)") if $is_tt_switch_active; $needToStorePreamble = 1; } else { # indentPreamble set to 0 $logger->trace( "NOT storing ${$preamble}{id} for indentation -- will store as VERBATIM object (because indentPreamble:0)" ) if $is_t_switch_active; $preamble->unprotect_blank_lines if ( $is_m_switch_active and ${ $mainSettings{modifyLineBreaks} }{preserveBlankLines} ); $verbatimStorage{ ${$preamble}{id} } = $preamble; } } else { ${$self}{preamblePresent} = 0; } # loop through the fileContents array, check if it's in the preamble foreach (@fileContentsStorageArray) { my $indentThisChild = 0; # verbatim children go in special hash if ( $preamble ne '' and ${$preamble}{body} =~ m/${$_}{id}/ ) { $logger->trace("filecontents (${$_}{id}) is within preamble") if $is_t_switch_active; # indentPreamble set to 1 if ( $mainSettings{indentPreamble} ) { $logger->trace("storing ${$_}{id} for indentation (indentPreamble is 1)") if $is_t_switch_active; $indentThisChild = 1; } else { # indentPreamble set to 0 $logger->trace("Storing ${$_}{id} as a VERBATIM object (indentPreamble is 0)") if $is_t_switch_active; $verbatimStorage{ ${$_}{id} } = $_; } } else { $logger->trace("storing ${$_}{id} for indentation (${$_}{name} found outside of preamble)") if $is_t_switch_active; $indentThisChild = 1; } # store the child, if necessary if ($indentThisChild) { $_->remove_leading_space; $_->yaml_get_indentation_settings_for_this_object; $_->tasks_particular_to_each_object; push( @{ ${$self}{children} }, $_ ); # possible decoration in log file $logger->trace( ${ $mainSettings{logFilePreferences} }{showDecorationFinishCodeBlockTrace} ) if ${ $mainSettings{logFilePreferences} }{showDecorationFinishCodeBlockTrace}; } } if ($needToStorePreamble) { $preamble->dodge_double_backslash; $preamble->remove_leading_space; # text wrapping $preamble->text_wrap() if ( $is_m_switch_active and ${ $mainSettings{modifyLineBreaks}{textWrapOptions} }{columns} != 0 ); $preamble->find_commands_or_key_equals_values_braces if ( $mainSettings{preambleCommandsBeforeEnvironments} ); $preamble->tasks_particular_to_each_object; push( @{ ${$self}{children} }, $preamble ); } return; } sub create_unique_id { my $self = shift; $fileContentsCounter++; ${$self}{id} = "$tokens{filecontents}$fileContentsCounter$tokens{endOfToken}"; return; } sub tasks_particular_to_each_object { my $self = shift; # search for environments $self->find_environments if ${$self}{body} =~ m/$environmentBasicRegExp/s; # search for ifElseFi blocks $self->find_ifelsefi if ${$self}{body} =~ m/$ifElseFiBasicRegExp/s; # search for headings (part, chapter, section, setc) $self->find_heading if ${$self}{body} =~ m/$allHeadingsRegexp/s; # search for commands and special code blocks $self->find_commands_or_key_equals_values_braces_and_special if ${$self}{body} =~ m/$specialBeginAndBracesBracketsBasicRegExp/s; } 1;