summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/latexindent/LatexIndent
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/scripts/latexindent/LatexIndent')
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/AlignmentAtAmpersand.pm255
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/Arguments.pm380
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/BackUpFileProcedure.pm135
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/BlankLines.pm108
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/Braces.pm188
-rwxr-xr-xMaster/texmf-dist/scripts/latexindent/LatexIndent/Command.pm178
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/Document.pm335
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/DoubleBackSlash.pm45
-rwxr-xr-xMaster/texmf-dist/scripts/latexindent/LatexIndent/Environment.pm128
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/FileContents.pm230
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/FileExtension.pm99
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/GetYamlSettings.pm471
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/Heading.pm240
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/HiddenChildren.pm190
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/HorizontalWhiteSpace.pm62
-rwxr-xr-xMaster/texmf-dist/scripts/latexindent/LatexIndent/IfElseFi.pm276
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/Indent.pm371
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/Item.pm149
-rwxr-xr-xMaster/texmf-dist/scripts/latexindent/LatexIndent/KeyEqualsValuesBraces.pm122
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/LogFile.pm166
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/MandatoryArgument.pm130
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/ModifyLineBreaks.pm174
-rwxr-xr-xMaster/texmf-dist/scripts/latexindent/LatexIndent/NamedGroupingBracesBrackets.pm71
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/OptionalArgument.pm115
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/Preamble.pm64
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/RoundBrackets.pm100
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/Special.pm184
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/Switches.pm37
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/Tokens.pm68
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/TrailingComments.pm112
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/UnNamedGroupingBracesBrackets.pm135
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/Verbatim.pm296
32 files changed, 5614 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/AlignmentAtAmpersand.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/AlignmentAtAmpersand.pm
new file mode 100644
index 00000000000..3f86cffd10f
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/AlignmentAtAmpersand.pm
@@ -0,0 +1,255 @@
+package LatexIndent::AlignmentAtAmpersand;
+# 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::TrailingComments qw/$trailingCommentRegExp/;
+use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use LatexIndent::GetYamlSettings qw/%masterSettings/;
+use LatexIndent::Tokens qw/%tokens/;
+use Data::Dumper;
+use Exporter qw/import/;
+our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321
+our @EXPORT_OK = qw/align_at_ampersand find_aligned_block/;
+our $alignmentBlockCounter;
+
+sub find_aligned_block{
+ my $self = shift;
+
+ return unless (${$self}{body} =~ m/(?!<\\)%\*\h*\\begin\{/s);
+
+ # aligned block
+ # %* \begin{tabular}
+ # 1 & 2 & 3 & 4 \\
+ # 5 & & 6 & \\
+ # %* \end{tabular}
+ $self->logger('looking for ALIGNED blocks marked by comments','heading')if($is_t_switch_active);
+ $self->logger(Dumper(\%{$masterSettings{lookForAlignDelims}})) if($is_t_switch_active);
+ while( my ($alignmentBlock,$yesno)= each %{$masterSettings{lookForAlignDelims}}){
+ if(ref $yesno eq "HASH"){
+ $yesno = (defined ${$yesno}{delims} ) ? ${$yesno}{delims} : 1;
+ }
+ if($yesno){
+ $self->logger("looking for $alignmentBlock:$yesno environments");
+
+ my $noIndentRegExp = qr/
+ (
+ (?!<\\)
+ %
+ \*
+ \h* # possible horizontal spaces
+ \\begin\{
+ $alignmentBlock # environment name captured into $2
+ \} # %* \begin{alignmentBlock} statement
+ )
+ (
+ .*?
+ )
+ \R
+ \h*
+ (
+ (?!<\\)
+ %\* # %
+ \h* # possible horizontal spaces
+ \\end\{$alignmentBlock\} # \end{alignmentBlock}
+ ) # %* \end{<something>} statement
+ #\R
+ /sx;
+
+ while( ${$self}{body} =~ m/$noIndentRegExp/sx){
+
+ ${$self}{body} =~ s/
+ $noIndentRegExp
+ /
+ # create a new Environment object
+ my $alignmentBlock = LatexIndent::AlignmentAtAmpersand->new( begin=>$1,
+ body=>$2,
+ end=>$3,
+ name=>$alignmentBlock,
+ modifyLineBreaksYamlName=>"environments",
+ linebreaksAtEnd=>{
+ begin=>1,
+ body=>1,
+ end=>0,
+ },
+ );
+
+ # the settings and storage of most objects has a lot in common
+ $self->get_settings_and_store_new_object($alignmentBlock);
+ ${@{${$self}{children}}[-1]}{replacementText};
+ /xseg;
+ }
+ } else {
+ $self->logger("*not* looking for $alignmentBlock as $alignmentBlock:$yesno");
+ }
+ }
+ return;
+}
+
+sub modify_line_breaks_settings{
+ return;
+}
+
+sub tasks_particular_to_each_object{
+ my $self = shift;
+# $self->remove_leading_space;
+}
+
+sub create_unique_id{
+ my $self = shift;
+
+ $alignmentBlockCounter++;
+ ${$self}{id} = "$tokens{alignmentBlock}$alignmentBlockCounter";
+ return;
+}
+
+sub align_at_ampersand{
+ my $self = shift;
+ return if(${$self}{bodyLineBreaks}==0);
+
+ # calculate the maximum number of ampersands in a row in the body
+ my $maximumNumberOfAmpersands = 0;
+ foreach(split("\n",${$self}{body})){
+ my $numberOfAmpersands = () = $_ =~ /(?<!\\)&/g;
+ $maximumNumberOfAmpersands = $numberOfAmpersands if($numberOfAmpersands>$maximumNumberOfAmpersands);
+ }
+
+ # create an array of zeros
+ my @columnSizes = (0) x ($maximumNumberOfAmpersands+1);
+
+ # array for the new body
+ my @formattedBody;
+
+ # now loop back through the body, and store the maximum column size
+ foreach(split("\n",${$self}{body})){
+ # remove \\ and anything following it
+ my $endPiece;
+ if($_ =~ m/(\\\\.*)/){
+ $_ =~ s/(\\\\.*)//;
+ $endPiece = $1;
+ }
+
+ my $numberOfAmpersands = () = $_ =~ /(?<!\\)&/g;
+ if($numberOfAmpersands == $maximumNumberOfAmpersands){
+ # remove any trailing comments
+ my $trailingComments;
+ if($_ =~ m/$trailingCommentRegExp/ ){
+ $_ =~ s/($trailingCommentRegExp)//;
+ $trailingComments = $1;
+ }
+
+ # if the line finishes with an &, then add an empty space,
+ # otherwise the column count is off
+ $_ .= ($_ =~ m/(?<!\\)&$/ ? " ":q());
+
+ # loop through the columns
+ my $columnCount = 0;
+ my $strippedRow = '';
+ foreach my $column (split(/(?<!\\)&/,$_)){
+ # remove leading space
+ $column =~ s/^\h*//;
+
+ # remove trailing space
+ $column =~ s/\h*$//;
+
+ # if a column has finished with a \ then we need to add a trailing space,
+ # otherwise the \ can be put next to &. See test-cases/texexchange/112343-gonzalo for example
+ $column .= ($column =~ m/\\$/ ? " ": q());
+
+ # store the column size
+ $columnSizes[$columnCount] = length($column) if(length($column)>$columnSizes[$columnCount]);
+
+ # put the row back together, using " " if the column is empty
+ $strippedRow .= ($columnCount>0 ? "&" : q() ).(length($column)>0 ? $column: " ");
+
+ # move on to the next column
+ $columnCount++;
+ }
+
+ # store the information
+ push(@formattedBody,{
+ row=>$strippedRow,
+ format=>1,
+ endPiece=>($endPiece ? $endPiece :q() ),
+ trailingComment=>($trailingComments ? $trailingComments :q() )});
+ } else {
+ # otherwise simply store the row
+ push(@formattedBody,{
+ row=>$_.($endPiece ? $endPiece : q() ),
+ format=>0});
+ }
+ }
+
+ # output some of the info so far to the log file
+ $self->logger("Column sizes of horizontally stripped formatted block (${$self}{name}): @columnSizes") if $is_t_switch_active;
+
+ # README: printf( formatting, expression)
+ #
+ # formatting has the form %-50s & %-20s & %-19s
+ # (the numbers have been made up for example)
+ # the - symbols mean that each column should be left-aligned
+ # the numbers represent how wide each column is
+ # the s represents string
+ # the & needs to be inserted
+
+ # join up the maximum string lengths using "s %-"
+ my $fmtstring = join("s & %-",@columnSizes);
+
+ # add %- to the beginning and an s to the end
+ $fmtstring = "%-".$fmtstring."s ";
+
+ # log file info
+ $self->logger("Formatting string is: $fmtstring",'heading') if $is_t_switch_active;
+
+ # finally, reformat the body
+ foreach(@formattedBody){
+ if(${$_}{format} and ${$_}{row} !~ m/^\h*$/){
+ # format the row, and put the trailing \\ and trailing comments back into the row
+ ${$_}{row} = sprintf($fmtstring,split(/(?<!\\)&/,${$_}{row})).(${$_}{endPiece} ? ${$_}{endPiece} :q() ).(${$_}{trailingComment}? ${$_}{trailingComment} : q() );
+
+ # possibly remove space ahead of \\
+ ${$_}{row} =~ s/\h*\\\\/\\\\/ if(!${$self}{alignDoubleBackSlash});
+
+ # possibly insert spaces infront of \\
+ if(defined ${$self}{spacesBeforeDoubleBackSlash} and ${$self}{spacesBeforeDoubleBackSlash}>=0 and !${$self}{alignDoubleBackSlash}){
+ my $horizontalSpaceToInsert = " "x (${$self}{spacesBeforeDoubleBackSlash});
+ ${$_}{row} =~ s/\h*\\\\/$horizontalSpaceToInsert\\\\/;
+ }
+ }
+
+ # if we have an empty row, it's possible that it originally had an end piece (e.g \\) and/or trailing comments
+ if(${$_}{row} =~ m/^\h*$/){
+ ${$_}{row} .= (${$_}{endPiece} ? ${$_}{endPiece} :q() ).(${$_}{trailingComment}? ${$_}{trailingComment} : q() );
+ }
+ }
+
+ # to the log file
+ if($is_tt_switch_active){
+ $self->logger(${$_}{row},'ttrace') for @formattedBody;
+ }
+
+ # delete the original body
+ ${$self}{body} = q();
+
+ # update the body
+ ${$self}{body} .= ${$_}{row}."\n" for @formattedBody;
+
+ # if the \end{} statement didn't originally have a line break before it, we need to remove the final
+ # line break added by the above
+ ${$self}{body} =~ s/\h*\R$//s if !${$self}{linebreaksAtEnd}{body};
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/Arguments.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/Arguments.pm
new file mode 100644
index 00000000000..569cead9584
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/Arguments.pm
@@ -0,0 +1,380 @@
+package LatexIndent::Arguments;
+# 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::TrailingComments qw/$trailingCommentRegExp/;
+use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use LatexIndent::GetYamlSettings qw/%masterSettings/;
+use Data::Dumper;
+use Exporter qw/import/;
+our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321
+our @EXPORT_OK = qw/get_arguments_regexp find_opt_mand_arguments get_numbered_arg_regexp construct_arguments_regexp $optAndMandRegExp/;
+our $ArgumentCounter;
+our $optAndMandRegExp;
+our $optAndMandRegExpWithLineBreaks;
+
+sub construct_arguments_regexp{
+ my $self = shift;
+
+ $optAndMandRegExp = $self->get_arguments_regexp;
+
+ $optAndMandRegExpWithLineBreaks = $self->get_arguments_regexp(mode=>"lineBreaksAtEnd");
+}
+
+sub indent{
+ my $self = shift;
+ $self->logger("Arguments object doesn't receive any direct indentation, but its children will...",'heading') if $is_t_switch_active;
+ return;
+}
+
+sub find_opt_mand_arguments{
+ my $self = shift;
+
+ $self->logger("Searching ${$self}{name} for optional and mandatory arguments",'heading') if $is_t_switch_active;
+
+ # blank line token
+ my $blankLineToken = $tokens{blanklines};
+
+ # the command object allows ()
+ my $objectDependentOptAndMandRegExp = (defined ${$self}{optAndMandArgsRegExp} ? ${$self}{optAndMandArgsRegExp} : $optAndMandRegExpWithLineBreaks);
+
+ if(${$self}{body} =~ m/^$objectDependentOptAndMandRegExp\h*($trailingCommentRegExp)?/){
+ $self->logger("Optional/Mandatory arguments found in ${$self}{name}: $1",'heading') if $is_t_switch_active;
+
+ # create a new Arguments object
+ # The arguments object is a little different to most
+ # other objects, as it is created purely for its children,
+ # so some of the properties common to other objects, such
+ # as environment, ifelsefi, etc do not exist for Arguments;
+ # they will, however, exist for its children: OptionalArgument, MandatoryArgument
+ my $arguments = LatexIndent::Arguments->new(begin=>"",
+ name=>${$self}{name}.":arguments",
+ parent=>${$self}{name},
+ body=>$1,
+ linebreaksAtEnd=>{
+ end=>$2?1:0,
+ },
+ end=>"",
+ regexp=>$objectDependentOptAndMandRegExp,
+ endImmediatelyFollowedByComment=>$2?0:($3?1:0),
+ );
+
+ # give unique id
+ $arguments->create_unique_id;
+
+ # determine which comes first, optional or mandatory
+ if(${$arguments}{body} =~ m/.*?((?<!\\)\{|\[)/s){
+
+ if($1 eq "\["){
+ $self->logger("Searching for optional arguments, and then mandatory (optional found first)") if $is_t_switch_active;
+ # look for optional arguments
+ $arguments->find_optional_arguments;
+
+ # look for mandatory arguments
+ $arguments->find_mandatory_arguments;
+ } else {
+ $self->logger("Searching for mandatory arguments, and then optional (mandatory found first)") if $is_t_switch_active;
+ # look for mandatory arguments
+ $arguments->find_mandatory_arguments;
+
+ # look for optional arguments
+ $arguments->find_optional_arguments;
+ }
+
+ } else {
+ $self->logger("Searching for round brackets ONLY") if $is_t_switch_active;
+ # look for round brackets
+ $arguments->find_round_brackets;
+ }
+
+ # examine *first* child
+ # situation: parent BodyStartsOnOwnLine >= 1, but first child has BeginStartsOnOwnLine == 0 || BeginStartsOnOwnLine == undef
+ # problem: the *body* of parent actually starts after the arguments
+ # solution: remove the linebreak at the end of the begin statement of the parent
+ if(defined ${$self}{BodyStartsOnOwnLine} and ${$self}{BodyStartsOnOwnLine}>=1){
+ if( !(defined ${${${$arguments}{children}}[0]}{BeginStartsOnOwnLine} and ${${${$arguments}{children}}[0]}{BeginStartsOnOwnLine}>=1)
+ and ${$self}{body} !~ m/^$blankLineToken/){
+ my $BodyStringLogFile = ${$self}{aliases}{BodyStartsOnOwnLine}||"BodyStartsOnOwnLine";
+ my $BeginStringLogFile = ${${${$arguments}{children}}[0]}{aliases}{BeginStartsOnOwnLine}||"BeginStartsOnOwnLine";
+ $self->logger("$BodyStringLogFile = 1 (in ${$self}{name}), but first argument should not begin on its own line (see $BeginStringLogFile)") if $is_t_switch_active;
+ $self->logger("Removing line breaks at the end of ${$self}{begin}") if $is_t_switch_active;
+ ${$self}{begin} =~ s/\R*$//s;
+ ${$self}{linebreaksAtEnd}{begin} = 0;
+ }
+ }
+
+ # situation: preserveBlankLines is active, so the body may well begin with a blank line token
+ # which means that ${$self}{linebreaksAtEnd}{begin} *should be* 1
+ if(${${${$arguments}{children}}[0]}{body} =~ m/^($blankLineToken)/){
+ $self->logger("Updating {linebreaksAtEnd}{begin} for ${$self}{name} as $blankLineToken or blank line found at beginning of argument child") if $is_t_switch_active;
+ ${$self}{linebreaksAtEnd}{begin} = 1
+ }
+
+ # examine *first* child
+ # situation: parent BodyStartsOnOwnLine == -1, but first child has BeginStartsOnOwnLine == 1
+ # problem: the *body* of parent actually starts after the arguments
+ # solution: add a linebreak at the end of the begin statement of the parent so that
+ # the child settings are obeyed.
+ # BodyStartsOnOwnLine == 0 will actually be controlled by the last arguments'
+ # settings of EndFinishesWithLineBreak
+ if( ${$self}{linebreaksAtEnd}{begin} == 0
+ and ((defined ${$self}{BodyStartsOnOwnLine} and ${$self}{BodyStartsOnOwnLine}==-1)
+ or !(defined ${$self}{BodyStartsOnOwnLine}))
+ ){
+ if(defined ${${${$arguments}{children}}[0]}{BeginStartsOnOwnLine} and ${${${$arguments}{children}}[0]}{BeginStartsOnOwnLine}>=1){
+ my $BodyStringLogFile = ${$self}{aliases}{BodyStartsOnOwnLine}||"BodyStartsOnOwnLine";
+ my $BeginStringLogFile = ${${${$arguments}{children}}[0]}{aliases}{BeginStartsOnOwnLine}||"BeginStartsOnOwnLine";
+ my $BodyValue = (defined ${$self}{BodyStartsOnOwnLine}) ? ${$self}{BodyStartsOnOwnLine} : "0";
+ $self->logger("$BodyStringLogFile = $BodyValue (in ${$self}{name}), but first argument *should* begin on its own line (see $BeginStringLogFile)") if $is_t_switch_active;
+
+ # possibly add a comment at the end of the begin statement
+ my $trailingCommentToken = q();
+ if(${${${$arguments}{children}}[0]}{BeginStartsOnOwnLine}==1){
+ $self->logger("Adding line breaks at the end of ${$self}{begin} (first argument, see $BeginStringLogFile == ${${${$arguments}{children}}[0]}{BeginStartsOnOwnLine})") if $is_t_switch_active;
+ } elsif(${${${$arguments}{children}}[0]}{BeginStartsOnOwnLine}==2){
+ $self->logger("Adding a % at the end of begin, ${$self}{begin} followed by a linebreak ($BeginStringLogFile == 2)") if $is_t_switch_active;
+ $trailingCommentToken = "%".$self->add_comment_symbol;
+ $self->logger("Removing trailing space on ${$self}{begin}") if $is_t_switch_active;
+ ${$self}{begin} =~ s/\h*$//s;
+ }
+
+ # modification
+ ${$self}{begin} .= "$trailingCommentToken\n";
+ ${$self}{linebreaksAtEnd}{begin} = 1;
+ }
+ }
+
+ # the replacement text can be just the ID, but the ID might have a line break at the end of it
+ ${$arguments}{replacementText} = ${$arguments}{id};
+
+ # children need to receive ancestor information, see test-cases/commands/commands-triple-nested.tex
+ foreach (@{${$arguments}{children}}){
+ $self->logger("Updating argument children of ${$self}{name} to include ${$self}{id} in ancestors") if $is_t_switch_active;
+ push(@{${$_}{ancestors}},{ancestorID=>${$self}{id},ancestorIndentation=>${$self}{indentation},type=>"natural"});
+ }
+
+ # the argument object only needs a trailing line break if the *last* child
+ # did not add one at the end, and if BodyStartsOnOwnLine >= 1
+ if( (defined ${${${$arguments}{children}}[-1]}{EndFinishesWithLineBreak} and ${${${$arguments}{children}}[-1]}{EndFinishesWithLineBreak}<1)
+ and (defined ${$self}{BodyStartsOnOwnLine} and ${$self}{BodyStartsOnOwnLine}>=1) ){
+ $self->logger("Updating replacementtext to include a linebreak for arguments in ${$self}{name}") if $is_t_switch_active;
+ ${$arguments}{replacementText} .= "\n" if(${$arguments}{linebreaksAtEnd}{end});
+ }
+
+ # store children in special hash
+ push(@{${$self}{children}},$arguments);
+
+ # remove the environment block, and replace with unique ID
+ ${$self}{body} =~ s/${$arguments}{regexp}/${$arguments}{replacementText}/;
+
+ # delete the regexp, as there's no need for it
+ delete ${${${$self}{children}}[-1]}{regexp};
+
+ $self->logger(Dumper(\%{$arguments}),'ttrace') if($is_tt_switch_active);
+ $self->logger("replaced with ID: ${$arguments}{id}") if $is_t_switch_active;
+ } else {
+ $self->logger("... no arguments found") if $is_t_switch_active;
+ }
+
+}
+
+
+sub create_unique_id{
+ my $self = shift;
+
+ $ArgumentCounter++;
+ ${$self}{id} = "$tokens{arguments}$ArgumentCounter$tokens{endOfToken}";
+ return;
+}
+
+sub get_numbered_arg_regexp{
+
+ # for example #1 #2, etc
+ my $numberedArgRegExp = qr/(?:#\d\h*;?,?\/?)+/;
+ return $numberedArgRegExp;
+}
+
+sub get_arguments_regexp{
+
+ my $self = shift;
+ my %input = @_;
+
+ # blank line token
+ my $blankLineToken = $tokens{blanklines};
+
+ # some calls to this routine need to account for the linebreaks at the end, some do not
+ my $lineBreaksAtEnd = (defined ${input}{mode} and ${input}{mode} eq 'lineBreaksAtEnd')?'\R*':q();
+
+ # for example #1 #2, etc
+ my $numberedArgRegExp = $self->get_numbered_arg_regexp;
+
+ # beamer special
+ my $beamerRegExp = qr/\<.*?\>/;
+
+ # commands are allowed strings between arguments, e.g node, decoration, etc, specified in stringsAllowedBetweenArguments
+ my $stringsBetweenArguments = q();
+
+ if(defined ${input}{stringBetweenArguments} and ${input}{stringBetweenArguments}==1){
+ # grab the strings allowed between arguments
+ my %stringsAllowedBetweenArguments = %{${$masterSettings{commandCodeBlocks}}{stringsAllowedBetweenArguments}};
+
+ while( my ($allowedStringName,$allowedStringValue)= each %stringsAllowedBetweenArguments){
+ # change + and - to escaped characters
+ $allowedStringName =~ s/\+/\\+/g;
+ $allowedStringName =~ s/\-/\\-/g;
+
+ # form the regexp
+ $stringsBetweenArguments .= ($stringsBetweenArguments eq '' ? q() : "|").$allowedStringName if $allowedStringValue;
+ }
+
+ # report to log file
+ $self->logger("Strings allowed between arguments $stringsBetweenArguments (see stringsAllowedBetweenArguments)",'heading') if $is_t_switch_active;
+ }
+
+ if(defined ${input}{roundBrackets} and ${input}{roundBrackets}==1){
+ # arguments regexp
+ return qr/
+ ( # capture into $1
+ (?:
+ (?:\h|\R|$blankLineToken|$trailingCommentRegExp|$numberedArgRegExp|$beamerRegExp|_|\^|$stringsBetweenArguments)*
+ (?:
+ (?:
+ \h* # 0 or more spaces
+ (?<!\\) # not immediately pre-ceeded by \
+ \[
+ (?:
+ (?!
+ (?:(?<!\\)\[|(?<!\\)\{)
+ ).
+ )*? # not including [, but \[ ok
+ (?<!\\) # not immediately pre-ceeded by \
+ \] # [optional arguments]
+ )
+ | # OR
+ (?:
+ \h* # 0 or more spaces
+ (?<!\\) # not immediately pre-ceeded by \
+ \{
+ (?:
+ (?!
+ (?:(?<!\\)\{|(?<!\\)\[)
+ ).
+ )*? # not including {, but \{ ok
+ (?<!\\) # not immediately pre-ceeded by \
+ \} # {mandatory arguments}
+ )
+ | # OR
+ (?:
+ \h* # 0 or more spaces
+ (?<!\\) # not immediately pre-ceeded by \
+ \(\$
+ (?:
+ (?!
+ (?:(?<!\\)\$)
+ ).
+ )*? # not including $
+ \$
+ (?<!\\) # not immediately pre-ceeded by \
+ \) # {mandatory arguments}
+ )
+ | # OR
+ (?:
+ \h* # 0 or more spaces
+ (?<!\\) # not immediately pre-ceeded by \
+ \(
+ (?:
+ (?!
+ (?:(?<!\\)\()
+ ).
+ )*? # not including {, but \{ ok
+ (?<!\\) # not immediately pre-ceeded by \
+ \) # {mandatory arguments}
+ )
+ )
+ )
+ + # at least one of the above
+ # NOT followed by
+ (?!
+ (?:
+ (?:\h|\R|$blankLineToken|$trailingCommentRegExp|$numberedArgRegExp|$beamerRegExp|$stringsBetweenArguments)* # 0 or more h-space, blanklines, trailing comments
+ (?:
+ (?:(?<!\\)\[)
+ |
+ (?:(?<!\\)\{)
+ |
+ (?:(?<!\\)\()
+ )
+ )
+ )
+ \h*
+ ($lineBreaksAtEnd)
+ )
+ /sx;
+ } else {
+ return qr/
+ ( # capture into $1
+ (?:
+ (?:\h|\R|$blankLineToken|$trailingCommentRegExp|$numberedArgRegExp|$beamerRegExp|_|\^|$stringsBetweenArguments)*
+ (?:
+ (?:
+ \h* # 0 or more spaces
+ (?<!\\) # not immediately pre-ceeded by \
+ \[
+ (?:
+ (?!
+ (?:(?<!\\)\[|(?<!\\)\{)
+ ).
+ )*? # not including [, but \[ ok
+ (?<!\\) # not immediately pre-ceeded by \
+ \] # [optional arguments]
+ )
+ | # OR
+ (?:
+ \h* # 0 or more spaces
+ (?<!\\) # not immediately pre-ceeded by \
+ \{
+ (?:
+ (?!
+ (?:(?<!\\)\{|(?<!\\)\[)
+ ).
+ )*? # not including {, but \{ ok
+ (?<!\\) # not immediately pre-ceeded by \
+ \} # {mandatory arguments}
+ )
+ )
+ )
+ + # at least one of the above
+ # NOT followed by
+ (?!
+ (?:
+ (?:\h|\R|$blankLineToken|$trailingCommentRegExp|$numberedArgRegExp|$beamerRegExp)* # 0 or more h-space, blanklines, trailing comments
+ (?:
+ (?:(?<!\\)\[)
+ |
+ (?:(?<!\\)\{)
+ )
+ )
+ )
+ \h*
+ ($lineBreaksAtEnd)
+ )
+ /sx;
+ }
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/BackUpFileProcedure.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/BackUpFileProcedure.pm
new file mode 100644
index 00000000000..dcb3b5bb74e
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/BackUpFileProcedure.pm
@@ -0,0 +1,135 @@
+package LatexIndent::BackUpFileProcedure;
+# 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::GetYamlSettings qw/%masterSettings/;
+use LatexIndent::Switches qw/%switches/;
+use File::Basename; # to get the filename and directory path
+use File::Copy; # to copy the original file to backup (if overwrite option set)
+use Exporter qw/import/;
+our @EXPORT_OK = qw/create_back_up_file/;
+
+# copy main file to a back up in the case of the overwrite switch being active
+
+sub create_back_up_file{
+ my $self = shift;
+
+ return unless($switches{overwrite});
+
+ # if we want to over write the current file create a backup first
+ $self->logger("Backup procedure (-w flag active):",'heading');
+
+ my $fileName = ${$self}{fileName};
+
+ # grab the file extension preferences
+ my %fileExtensionPreference= %{$masterSettings{fileExtensionPreference}};
+
+ # sort the file extensions by preference
+ my @fileExtensions = sort { $fileExtensionPreference{$a} <=> $fileExtensionPreference{$b} } keys(%fileExtensionPreference);
+
+ # backup file name is the base name
+ my $backupFile = basename(${$self}{fileName},@fileExtensions);
+
+ # add the user's backup directory to the backup path
+ $backupFile = "${$self}{cruftDirectory}/$backupFile";
+
+ # local variables, determined from the YAML settings
+ my $onlyOneBackUp = $masterSettings{onlyOneBackUp};
+ my $maxNumberOfBackUps = $masterSettings{maxNumberOfBackUps};
+ my $cycleThroughBackUps= $masterSettings{cycleThroughBackUps};
+ my $backupExtension= $masterSettings{backupExtension};
+
+ # if both ($onlyOneBackUp and $maxNumberOfBackUps) then we have
+ # a conflict- er on the side of caution and turn off onlyOneBackUp
+ if($onlyOneBackUp and $maxNumberOfBackUps>1) {
+ $self->logger("WARNING: onlyOneBackUp=$onlyOneBackUp and maxNumberOfBackUps: $maxNumberOfBackUps");
+ $self->logger("setting onlyOneBackUp=0 which will allow you to reach $maxNumberOfBackUps back ups");
+ $onlyOneBackUp = 0;
+ }
+
+ # if the user has specified that $maxNumberOfBackUps = 1 then
+ # they only want one backup
+ if($maxNumberOfBackUps==1) {
+ $onlyOneBackUp=1 ;
+ $self->logger("FYI: you set maxNumberOfBackUps=1, so I'm setting onlyOneBackUp: 1 ");
+ } elsif($maxNumberOfBackUps<=0 and !$onlyOneBackUp) {
+ $onlyOneBackUp=0 ;
+ $maxNumberOfBackUps=-1;
+ }
+
+ # if onlyOneBackUp is set, then the backup file will
+ # be overwritten each time
+ if($onlyOneBackUp) {
+ $backupFile .= $backupExtension;
+ $self->logger("copying $fileName to $backupFile");
+ $self->logger("$backupFile was overwritten (see onlyOneBackUp)") if (-e $backupFile);
+ } else {
+ # start with a backup file .bak0 (or whatever $backupExtension is present)
+ my $backupCounter = 0;
+ $backupFile .= $backupExtension.$backupCounter;
+
+ # if it exists, then keep going: .bak0, .bak1, ...
+ while (-e $backupFile or $maxNumberOfBackUps>1) {
+ if($backupCounter==$maxNumberOfBackUps) {
+ $self->logger("maxNumberOfBackUps reached ($maxNumberOfBackUps, see maxNumberOfBackUps)");
+
+ # some users may wish to cycle through back up files, e.g:
+ # copy myfile.bak1 to myfile.bak0
+ # copy myfile.bak2 to myfile.bak1
+ # copy myfile.bak3 to myfile.bak2
+ #
+ # current back up is stored in myfile.bak4
+ if($cycleThroughBackUps) {
+ $self->logger("cycleThroughBackUps detected (see cycleThroughBackUps) ");
+ for(my $i=1;$i<=$maxNumberOfBackUps;$i++) {
+ # remove number from backUpFile
+ my $oldBackupFile = $backupFile;
+ $oldBackupFile =~ s/$backupExtension.*/$backupExtension/;
+ my $newBackupFile = $oldBackupFile;
+
+ # add numbers back on
+ $oldBackupFile .= $i;
+ $newBackupFile .= $i-1;
+
+ # check that the oldBackupFile exists
+ if(-e $oldBackupFile){
+ $self->logger(" copying $oldBackupFile to $newBackupFile ");
+ copy($oldBackupFile,$newBackupFile) or die "Could not write to backup file $backupFile. Please check permissions. Exiting.";
+ }
+ }
+ }
+
+ # rest maxNumberOfBackUps
+ $maxNumberOfBackUps=1 ;
+ last; # break out of the loop
+ } elsif(!(-e $backupFile)) {
+ $maxNumberOfBackUps=1;
+ last; # break out of the loop
+ }
+ $self->logger(" $backupFile already exists, incrementing by 1... (see maxNumberOfBackUps and onlyOneBackUp)");
+ $backupCounter++;
+ $backupFile =~ s/$backupExtension.*/$backupExtension$backupCounter/;
+ }
+ $self->logger("copying $fileName to $backupFile");
+ }
+
+ # output these lines to the log file
+ $self->logger("Backup file: ",$backupFile,"");
+ $self->logger("Overwriting file: ",$fileName,"");
+ copy($fileName,$backupFile) or die "Could not write to backup file $backupFile. Please check permissions. Exiting.";
+}
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/BlankLines.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/BlankLines.pm
new file mode 100644
index 00000000000..f4572dd6797
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/BlankLines.pm
@@ -0,0 +1,108 @@
+package LatexIndent::BlankLines;
+# 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/%masterSettings/;
+use LatexIndent::Switches qw/$is_m_switch_active $is_t_switch_active $is_tt_switch_active/;
+use Exporter qw/import/;
+our @EXPORT_OK = qw/protect_blank_lines unprotect_blank_lines condense_blank_lines/;
+
+sub protect_blank_lines{
+ return unless $is_m_switch_active;
+ my $self = shift;
+
+ unless(${$masterSettings{modifyLineBreaks}}{preserveBlankLines}){
+ $self->logger("Blank lines will not be protected (preserveBlankLines=0)",'heading') if $is_t_switch_active;
+ return
+ }
+
+ $self->logger("Protecting blank lines (see preserveBlankLines)",'heading') if $is_t_switch_active;
+ ${$self}{body} =~ s/^(\h*)?\R/$tokens{blanklines}\n/mg;
+ return;
+}
+
+sub condense_blank_lines{
+ return unless $is_m_switch_active;
+
+ return unless ${$masterSettings{modifyLineBreaks}}{condenseMultipleBlankLinesInto}>0;
+
+ my $self = shift;
+
+ # if preserveBlankLines is set to 0, then the blank-line-token will not be present
+ # in the document -- we change that here
+ if(${$masterSettings{modifyLineBreaks}}{preserveBlankLines}==0){
+ # turn the switch on
+ ${$masterSettings{modifyLineBreaks}}{preserveBlankLines}=1;
+
+ # log file information
+ $self->logger("Updating body to inclued blank line token, this requires preserveBlankLines = 1",'ttrace') if($is_tt_switch_active);
+ $self->logger("(any blanklines that could have been removed, would have done so by this point)",'ttrace') if($is_tt_switch_active);
+
+ # make the call
+ $self->protect_blank_lines ;
+ $self->logger("body now looks like:\n${$self}{body}",'ttrace') if($is_tt_switch_active);
+ }
+
+ # grab the value from the settings
+ my $condenseMultipleBlankLinesInto = ${$masterSettings{modifyLineBreaks}}{condenseMultipleBlankLinesInto};
+
+ # grab the blank-line-token
+ my $blankLineToken = $tokens{blanklines};
+
+ # condense!
+ $self->logger("Condensing multiple blank lines into $condenseMultipleBlankLinesInto (see condenseMultipleBlankLinesInto)",'heading') if $is_t_switch_active;
+ my $replacementToken = $blankLineToken;
+ for (my $i=1; $i<$condenseMultipleBlankLinesInto; $i++ ){
+ $replacementToken .= "\n$blankLineToken";
+ }
+
+ $self->logger("blank line replacement token: $replacementToken",'ttrace') if($is_tt_switch_active);
+ ${$self}{body} =~ s/($blankLineToken\h*\R*\h*){1,}$blankLineToken/$replacementToken/mgs;
+ $self->logger("body now looks like:\n${$self}{body}",'ttrace') if($is_tt_switch_active);
+ return;
+}
+
+sub unprotect_blank_lines{
+ return unless $is_m_switch_active;
+
+ return unless ${$masterSettings{modifyLineBreaks}}{preserveBlankLines};
+ my $self = shift;
+
+ $self->logger("Unprotecting blank lines (see preserveBlankLines)",'heading') if $is_t_switch_active;
+ my $blankLineToken = $tokens{blanklines};
+
+ # loop through the body, looking for the blank line token
+ while(${$self}{body} =~ m/$blankLineToken/m){
+ # when the blank line token occupies the whole line
+ if(${$self}{body} =~ m/^\h*$blankLineToken$/m){
+ $self->logger("Replacing purely blank lines",'heading') if $is_tt_switch_active;
+ ${$self}{body} =~ s/^\h*$blankLineToken$//mg;
+ $self->logger("body now looks like:\n${$self}{body}",'ttrace') if($is_tt_switch_active);
+ }
+ # otherwise the blank line has been deleted, so we compensate with an extra
+ if(${$self}{body} =~ m/(^\h*)?$blankLineToken/m){
+ $self->logger("Replacing blank line token that doesn't take up whole line",'heading') if $is_tt_switch_active;
+ ${$self}{body} =~ s/(^\h*)?$blankLineToken/$1?"\n".$1:"\n"/me;
+ $self->logger("body now looks like:\n${$self}{body}",'ttrace') if($is_tt_switch_active);
+ }
+ }
+ $self->logger("Finished unprotecting lines (see preserveBlankLines)",'heading') if $is_t_switch_active;
+ $self->logger("body now looks like ${$self}{body}",'ttrace') if($is_tt_switch_active);
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/Braces.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/Braces.pm
new file mode 100644
index 00000000000..c5122fd1275
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/Braces.pm
@@ -0,0 +1,188 @@
+package LatexIndent::Braces;
+# 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::TrailingComments qw/$trailingCommentRegExp/;
+use LatexIndent::Command qw/$commandRegExp $commandRegExpTrailingComment $optAndMandAndRoundBracketsRegExpLineBreaks/;
+use LatexIndent::KeyEqualsValuesBraces qw/$key_equals_values_bracesRegExp $key_equals_values_bracesRegExpTrailingComment/;
+use LatexIndent::NamedGroupingBracesBrackets qw/$grouping_braces_regexp $grouping_braces_regexpTrailingComment/;
+use LatexIndent::UnNamedGroupingBracesBrackets qw/$un_named_grouping_braces_RegExp $un_named_grouping_braces_RegExp_trailing_comment/;
+use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use Data::Dumper;
+use Exporter qw/import/;
+our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321
+our @EXPORT_OK = qw/find_commands_or_key_equals_values_braces/;
+our $commandCounter;
+
+sub find_commands_or_key_equals_values_braces{
+
+ my $self = shift;
+
+ $self->logger("Searching for commands with optional and/or mandatory arguments AND key = {value}",'heading') if $is_t_switch_active ;
+
+ # match either a \\command or key={value}
+ while( ${$self}{body} =~ m/$commandRegExpTrailingComment/
+ or
+ ${$self}{body} =~ m/$key_equals_values_bracesRegExpTrailingComment/
+ or
+ ${$self}{body} =~ m/$grouping_braces_regexpTrailingComment/
+ or
+ ${$self}{body} =~ m/$un_named_grouping_braces_RegExp_trailing_comment/
+ ){
+ if(${$self}{body} =~ m/$commandRegExpTrailingComment/){
+
+ # global substitution
+ ${$self}{body} =~ s/
+ $commandRegExpTrailingComment
+ /
+ # log file output
+ $self->logger("command found: $2",'heading') if $is_t_switch_active ;
+
+ # create a new command object
+ my $command = LatexIndent::Command->new(begin=>$1.$2.($3?$3:q()).($4?$4:q()),
+ name=>$2,
+ body=>$5.($8?$8:($10?$10:q())), # $8 is linebreak, $10 is trailing comment
+ end=>q(),
+ linebreaksAtEnd=>{
+ begin=>$4?1:0,
+ end=>$8?1:0, # $8 is linebreak before comment check, $10 is after
+ },
+ modifyLineBreaksYamlName=>"commands",
+ endImmediatelyFollowedByComment=>$8?0:($10?1:0),
+ aliases=>{
+ # begin statements
+ BeginStartsOnOwnLine=>"CommandStartsOnOwnLine",
+ # body statements
+ BodyStartsOnOwnLine=>"CommandNameFinishesWithLineBreak",
+ },
+ optAndMandArgsRegExp=>$optAndMandAndRoundBracketsRegExpLineBreaks,
+ );
+
+ # the settings and storage of most objects has a lot in common
+ $self->get_settings_and_store_new_object($command);
+ ${@{${$self}{children}}[-1]}{replacementText}.($8?($10?$10:q()):q());
+ /xseg;
+
+ } elsif (${$self}{body} =~ m/$key_equals_values_bracesRegExpTrailingComment/){
+
+ # global substitution
+ ${$self}{body} =~ s/
+ $key_equals_values_bracesRegExpTrailingComment
+ /
+ # log file output
+ $self->logger("key_equals_values_braces found: $3",'heading') if $is_t_switch_active ;
+
+ # create a new key_equals_values_braces object
+ my $key_equals_values_braces = LatexIndent::KeyEqualsValuesBraces->new(
+ begin=>($2?$2:q()).$3.$4.($5?$5:q()),
+ name=>$3,
+ body=>$6.($9?$9:($10?$10:q()).($11?$11:q())), # $9 is linebreak before comment check, $11 is trailing comment
+ end=>q(),
+ linebreaksAtEnd=>{
+ begin=>$5?1:0,
+ end=>$9?1:0, # $9 is linebreak before comment check
+ },
+ modifyLineBreaksYamlName=>"keyEqualsValuesBracesBrackets",
+ beginningbit=>$1,
+ endImmediatelyFollowedByComment=>$9?0:($11?1:0),
+ aliases=>{
+ # begin statements
+ BeginStartsOnOwnLine=>"KeyStartsOnOwnLine",
+ # body statements
+ BodyStartsOnOwnLine=>"EqualsFinishesWithLineBreak",
+ },
+ additionalAssignments=>["EqualsStartsOnOwnLine"],
+ );
+
+ # the settings and storage of most objects has a lot in common
+ $self->get_settings_and_store_new_object($key_equals_values_braces);
+ ${@{${$self}{children}}[-1]}{replacementText}.($9?($11?$11:q()):q());
+ /xseg;
+
+ } elsif (${$self}{body} =~ m/$grouping_braces_regexpTrailingComment/){
+
+ # global substitution
+ ${$self}{body} =~ s/
+ $grouping_braces_regexpTrailingComment
+ /
+ # log file output
+ $self->logger("named grouping braces found: $2",'heading') if $is_t_switch_active ;
+
+ # create a new key_equals_values_braces object
+ my $grouping_braces = LatexIndent::NamedGroupingBracesBrackets->new(
+ begin=>$2.($3?$3:q()).($4?$4:q()),
+ name=>$2,
+ body=>$5.($8?$8:($9?$9:q())),
+ end=>q(),
+ linebreaksAtEnd=>{
+ begin=>$4?1:0,
+ end=>$8?1:0,
+ },
+ modifyLineBreaksYamlName=>"namedGroupingBracesBrackets",
+ beginningbit=>$1,
+ endImmediatelyFollowedByComment=>$8?0:($9?1:0),
+ aliases=>{
+ # begin statements
+ BeginStartsOnOwnLine=>"NameStartsOnOwnLine",
+ # body statements
+ BodyStartsOnOwnLine=>"NameFinishesWithLineBreak",
+ },
+ );
+
+ # the settings and storage of most objects has a lot in common
+ $self->get_settings_and_store_new_object($grouping_braces);
+ ${@{${$self}{children}}[-1]}{replacementText}.($8?($9?$9:q()):q());
+ /xseg;
+
+ } elsif (${$self}{body} =~ m/$un_named_grouping_braces_RegExp_trailing_comment/) {
+ # global substitution
+ ${$self}{body} =~ s/
+ $un_named_grouping_braces_RegExp_trailing_comment
+ /
+ # log file output
+ $self->logger("UNnamed grouping braces found: (no name, by definition!)",'heading') if $is_t_switch_active ;
+
+ # create a new Un-named-grouping-braces-brackets object
+ my $un_named_grouping_braces = LatexIndent::UnNamedGroupingBracesBrackets->new(
+ begin=>q(),
+ name=>"always-un-named",
+ body=>$3.($6?$6:($8?$8:q())),
+ end=>q(),
+ linebreaksAtEnd=>{
+ begin=>$2?1:0,
+ end=>$6?1:0,
+ },
+ modifyLineBreaksYamlName=>"UnNamedGroupingBracesBrackets",
+ beginningbit=>$1.($2?$2:q()),
+ endImmediatelyFollowedByComment=>$6?0:($8?1:0),
+ # begin statements
+ BeginStartsOnOwnLine=>0,
+ # body statements
+ BodyStartsOnOwnLine=>0,
+ );
+
+ # the settings and storage of most objects has a lot in common
+ $self->get_settings_and_store_new_object($un_named_grouping_braces);
+ ${@{${$self}{children}}[-1]}{replacementText}.($6?($8?$8:q()):q());
+ /xseg;
+
+ }
+ }
+ return;
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/Command.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/Command.pm
new file mode 100755
index 00000000000..022064fd1d7
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/Command.pm
@@ -0,0 +1,178 @@
+package LatexIndent::Command;
+# 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::TrailingComments qw/$trailingCommentRegExp/;
+use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use LatexIndent::GetYamlSettings qw/%masterSettings/;
+use Data::Dumper;
+use Exporter qw/import/;
+our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321
+our @EXPORT_OK = qw/construct_command_regexp $commandRegExp $commandRegExpTrailingComment $optAndMandAndRoundBracketsRegExpLineBreaks/;
+our $commandCounter;
+our $commandRegExp;
+our $commandRegExpTrailingComment;
+our $optAndMandAndRoundBracketsRegExp;
+our $optAndMandAndRoundBracketsRegExpLineBreaks;
+
+# store the regular expresssion for matching and replacing
+sub construct_command_regexp{
+ my $self = shift;
+
+ $optAndMandAndRoundBracketsRegExp = $self->get_arguments_regexp(
+ roundBrackets=>${$masterSettings{commandCodeBlocks}}{roundParenthesesAllowed},
+ stringBetweenArguments=>1);
+
+ $optAndMandAndRoundBracketsRegExpLineBreaks = $self->get_arguments_regexp(
+ roundBrackets=>${$masterSettings{commandCodeBlocks}}{roundParenthesesAllowed},
+ mode=>"lineBreaksAtEnd",
+ stringBetweenArguments=>1);
+
+ # construct the command regexp
+ $commandRegExp = qr/
+ (\\|@)
+ (
+ [+a-zA-Z@\*0-9_\:]+? # lowercase|uppercase letters, @, *, numbers
+ )
+ (\h*)
+ (\R*)?
+ ($optAndMandAndRoundBracketsRegExp)
+ (\R)?
+ /sx;
+
+ # command regexp with trailing comment
+ $commandRegExpTrailingComment = qr/$commandRegExp(\h*)((?:$trailingCommentRegExp\h*)*)/;
+
+}
+
+sub tasks_particular_to_each_object{
+ my $self = shift;
+
+ # check for adding/removing linebreaks before =
+ $self->check_linebreaks_before_equals;
+
+ # search for arguments
+ $self->find_opt_mand_arguments;
+
+ # situation: ${${$self}{linebreaksAtEnd}}{end} == 1, and the argument container object
+ # still contains a linebreak at the end; in this case, we need to remove the linebreak from
+ # the container object
+ if(${${$self}{linebreaksAtEnd}}{end} == 1
+ and ${${${$self}{children}}[0]}{body} =~ m/\R$/s
+ and !${$self}{endImmediatelyFollowedByComment}){
+ $self->logger("Removing linebreak from argument container of ${$self}{name}") if $is_t_switch_active;
+ ${${${$self}{children}}[0]}{body} =~ s/\R$//s;
+ ${${${${$self}{children}}[0]}{linebreaksAtEnd}}{body} = 0;
+ }
+
+ # situation: ${${$self}{linebreaksAtEnd}}{end} == 1 and the last argument specifies
+ # EndFinishesWithLineBreaks = 0 (see test-cases/commands/just-one-command-mod10.tex)
+ if(${${$self}{linebreaksAtEnd}}{end} == 1
+ and defined ${${${${$self}{children}}[0]}{children}[-1]}{EndFinishesWithLineBreak}
+ and ${${${${$self}{children}}[0]}{children}[-1]}{EndFinishesWithLineBreak} == -1
+ ){
+ $self->logger("Switching linebreaksAtEnd{end} to be 0 in command ${$self}{name} as last argument specifies EndFinishesWithLineBreak == 0") if $is_t_switch_active;
+ ${${$self}{linebreaksAtEnd}}{end} = 0;
+ ${$self}{EndFinishesWithLineBreak} = -1;
+ }
+
+ # if the last argument finishes with a linebreak, it won't get interpreted at
+ # the right time (see test-cases/commands/commands-one-line-nested-simple-mod1.tex for example)
+ # so this little bit fixes it
+ if(${${${${${$self}{children}}[0]}{children}[-1]}{linebreaksAtEnd}}{end} and ${${$self}{linebreaksAtEnd}}{end} == 0
+ and defined ${${${${$self}{children}}[0]}{children}[-1]}{EndFinishesWithLineBreak}
+ and ${${${${$self}{children}}[0]}{children}[-1]}{EndFinishesWithLineBreak} >= 1
+ and !${$self}{endImmediatelyFollowedByComment}){
+
+ # update the Command object
+ $self->logger("Adjusting linebreaksAtEnd in command ${$self}{name}") if $is_t_switch_active;
+ ${${$self}{linebreaksAtEnd}}{end} = ${${${${${$self}{children}}[0]}{children}[-1]}{linebreaksAtEnd}}{end};
+ ${$self}{replacementText} .= "\n";
+
+ # update the argument object
+ $self->logger("Adjusting argument object in command, ${$self}{name}") if $is_t_switch_active;
+ ${${${${$self}{children}}[0]}{linebreaksAtEnd}}{body} = 0;
+ ${${${$self}{children}}[0]}{body} =~ s/\R$//s;
+
+ # update the last mandatory/optional argument
+ $self->logger("Adjusting last argument in command, ${$self}{name}") if $is_t_switch_active;
+ ${${${${${$self}{children}}[0]}{children}[-1]}{linebreaksAtEnd}}{end} = 0;
+ ${${${${$self}{children}}[0]}{children}[-1]}{EndFinishesWithLineBreak} = -1;
+ ${${${${$self}{children}}[0]}{children}[-1]}{replacementText} =~ s/\R$//s;
+
+ # output to log file
+ $self->logger(Dumper(${${${$self}{children}}[0]}{children}[-1])) if $is_t_switch_active;
+ }
+
+ # situation: ${${$self}{linebreaksAtEnd}}{end} == 1 and the last argument has added
+ # a line break, which can result in a bogus blank line (see test-cases/commands/just-one-command.tex with mand-args-mod1.yaml)
+ if(${${$self}{linebreaksAtEnd}}{end} == 1
+ and defined ${${${${$self}{children}}[0]}{children}[-1]}{EndFinishesWithLineBreak}
+ and ${${${${$self}{children}}[0]}{children}[-1]}{EndFinishesWithLineBreak} >= 1
+ and ${${${${$self}{children}}[0]}{children}[-1]}{replacementText}=~m/\R$/s
+ and !${$self}{endImmediatelyFollowedByComment}){
+
+ # last argument adjustment
+ $self->logger("Adjusting last argument in command, ${$self}{name} to avoid double line break") if $is_t_switch_active;
+ ${${${${$self}{children}}[0]}{children}[-1]}{replacementText}=~s/\R$//s;
+ ${${${${${$self}{children}}[0]}{children}[-1]}{linebreaksAtEnd}}{end} = 0;
+
+ # argument object adjustment
+ $self->logger("Adjusting argument object in command, ${$self}{name} to avoid double line break") if $is_t_switch_active;
+ ${${${${$self}{children}}[0]}{linebreaksAtEnd}}{body} = 0;
+ ${${${$self}{children}}[0]}{body}=~s/\R$//s;
+ }
+
+ # the arguments body might finish with horizontal space, in which case, we need to transfer this
+ # to the parent object replacement text.
+ #
+ # see ../test-cases/texexchange/5461.tex which was the first example to demonstrate the need for this
+ if(!${${${$self}{children}}[0]}{endImmediatelyFollowedByComment} and ${${${$self}{children}}[0]}{body} =~ m/\h*$/ and ${$self}{replacementText} !~ m/\R$/){
+ $self->logger("${$self}{name}: trailling horizontal space found in arguments -- removing it from arguments, adding to replacement text") if $is_t_switch_active;
+ ${${${$self}{children}}[0]}{body} =~ s/(\h*)$//s;
+ ${$self}{replacementText} .= "$1";
+ }
+
+ # search for ifElseFi blocks
+ $self->find_ifelsefi;
+
+ # search for special begin/end
+ $self->find_special;
+
+}
+
+sub check_linebreaks_before_equals{
+ # empty routine, which allows the above routine to function (this routine kicks in for KeyEqualsValuesBraces)
+ return;
+}
+
+sub create_unique_id{
+ my $self = shift;
+
+ $commandCounter++;
+ ${$self}{id} = "$tokens{command}$commandCounter";
+ return;
+}
+
+sub align_at_ampersand{
+ # need an empty routine here for commands; see
+ # test-cases/matrix1.tex for example
+ return;
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/Document.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/Document.pm
new file mode 100644
index 00000000000..7c951ec7995
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/Document.pm
@@ -0,0 +1,335 @@
+package LatexIndent::Document;
+# 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 Data::Dumper;
+
+# gain access to subroutines in the following modules
+use LatexIndent::Switches qw/storeSwitches %switches $is_m_switch_active $is_t_switch_active $is_tt_switch_active/;
+use LatexIndent::LogFile qw/logger output_logfile processSwitches/;
+use LatexIndent::GetYamlSettings qw/readSettings modify_line_breaks_settings get_indentation_settings_for_this_object get_every_or_custom_value get_indentation_information get_object_attribute_for_indentation_settings alignment_at_ampersand_settings/;
+use LatexIndent::FileExtension qw/file_extension_check/;
+use LatexIndent::BackUpFileProcedure qw/create_back_up_file/;
+use LatexIndent::BlankLines qw/protect_blank_lines unprotect_blank_lines condense_blank_lines/;
+use LatexIndent::ModifyLineBreaks qw/modify_line_breaks_body modify_line_breaks_end remove_line_breaks_begin adjust_line_breaks_end_parent/;
+use LatexIndent::TrailingComments qw/remove_trailing_comments put_trailing_comments_back_in add_comment_symbol construct_trailing_comment_regexp/;
+use LatexIndent::HorizontalWhiteSpace qw/remove_trailing_whitespace remove_leading_space/;
+use LatexIndent::Indent qw/indent wrap_up_statement determine_total_indentation indent_begin indent_body indent_end_statement final_indentation_check get_surrounding_indentation indent_children_recursively check_for_blank_lines_at_beginning put_blank_lines_back_in_at_beginning add_surrounding_indentation_to_begin_statement/;
+use LatexIndent::Tokens qw/token_check %tokens/;
+use LatexIndent::HiddenChildren qw/find_surrounding_indentation_for_children update_family_tree get_family_tree check_for_hidden_children/;
+use LatexIndent::AlignmentAtAmpersand qw/align_at_ampersand find_aligned_block/;
+use LatexIndent::DoubleBackSlash qw/dodge_double_backslash un_dodge_double_backslash/;
+
+# code blocks
+use LatexIndent::Verbatim qw/put_verbatim_back_in find_verbatim_environments find_noindent_block find_verbatim_commands put_verbatim_commands_back_in/;
+use LatexIndent::Environment qw/find_environments/;
+use LatexIndent::IfElseFi qw/find_ifelsefi/;
+use LatexIndent::Arguments qw/get_arguments_regexp find_opt_mand_arguments get_numbered_arg_regexp construct_arguments_regexp/;
+use LatexIndent::OptionalArgument qw/find_optional_arguments/;
+use LatexIndent::MandatoryArgument qw/find_mandatory_arguments get_mand_arg_reg_exp/;
+use LatexIndent::RoundBrackets qw/find_round_brackets/;
+use LatexIndent::Item qw/find_items construct_list_of_items/;
+use LatexIndent::Braces qw/find_commands_or_key_equals_values_braces/;
+use LatexIndent::Command qw/construct_command_regexp/;
+use LatexIndent::KeyEqualsValuesBraces qw/construct_key_equals_values_regexp/;
+use LatexIndent::NamedGroupingBracesBrackets qw/construct_grouping_braces_brackets_regexp/;
+use LatexIndent::UnNamedGroupingBracesBrackets qw/construct_unnamed_grouping_braces_brackets_regexp/;
+use LatexIndent::Special qw/find_special construct_special_begin/;
+use LatexIndent::Heading qw/find_heading construct_headings_levels/;
+use LatexIndent::FileContents qw/find_file_contents_environments_and_preamble/;
+use LatexIndent::Preamble;
+
+sub new{
+ # Create new objects, with optional key/value pairs
+ # passed as initializers.
+ #
+ # See Programming Perl, pg 319
+ my $invocant = shift;
+ my $class = ref($invocant) || $invocant;
+ my $self = {@_};
+ bless ($self,$class);
+ return $self;
+}
+
+sub latexindent{
+ my $self = shift;
+ $self->storeSwitches;
+ $self->processSwitches;
+ $self->readSettings;
+ $self->file_extension_check;
+ $self->operate_on_file;
+}
+
+sub operate_on_file{
+ my $self = shift;
+
+ $self->create_back_up_file;
+ $self->token_check;
+ $self->construct_regular_expressions;
+ $self->find_noindent_block;
+ $self->find_verbatim_commands;
+ $self->find_aligned_block;
+ $self->remove_trailing_comments;
+ $self->find_verbatim_environments;
+ $self->protect_blank_lines;
+ $self->remove_trailing_whitespace(when=>"before");
+ $self->find_file_contents_environments_and_preamble;
+ $self->dodge_double_backslash;
+ $self->remove_leading_space;
+ $self->process_body_of_text;
+ $self->remove_trailing_whitespace(when=>"after");
+ $self->condense_blank_lines;
+ $self->unprotect_blank_lines;
+ $self->un_dodge_double_backslash;
+ $self->put_verbatim_back_in;
+ $self->put_trailing_comments_back_in;
+ $self->put_verbatim_commands_back_in;
+ $self->output_indented_text;
+ $self->output_logfile;
+ return
+}
+
+sub construct_regular_expressions{
+ my $self = shift;
+ $self->construct_trailing_comment_regexp;
+ $self->construct_list_of_items;
+ $self->construct_special_begin;
+ $self->construct_headings_levels;
+ $self->construct_arguments_regexp;
+ $self->construct_command_regexp;
+ $self->construct_key_equals_values_regexp;
+ $self->construct_grouping_braces_brackets_regexp;
+ $self->construct_unnamed_grouping_braces_brackets_regexp;
+
+}
+
+sub output_indented_text{
+ my $self = shift;
+
+ # output to screen, unless silent mode
+ print ${$self}{body} unless $switches{silentMode};
+
+ $self->logger("Output routine",'heading');
+
+ # if -overwrite is active then output to original fileName
+ if($switches{overwrite}) {
+ $self->logger("Overwriting file ${$self}{fileName}");
+ open(OUTPUTFILE,">",${$self}{fileName});
+ print OUTPUTFILE ${$self}{body};
+ close(OUTPUTFILE);
+ } elsif($switches{outputToFile}) {
+ $self->logger("Outputting to file $switches{outputToFile}");
+ open(OUTPUTFILE,">",$switches{outputToFile});
+ print OUTPUTFILE ${$self}{body};
+ close(OUTPUTFILE);
+ } else {
+ $self->logger("Not outputting to file; see -w and -o switches for more options.");
+ }
+ return;
+}
+
+sub process_body_of_text{
+ my $self = shift;
+
+ # find objects recursively
+ $self->logger('Phase 1: searching for objects','heading');
+ $self->find_objects;
+
+ # find all hidden child
+ $self->logger('Phase 2: finding surrounding indentation','heading');
+ $self->find_surrounding_indentation_for_children;
+
+ # indentation recursively
+ $self->logger('Phase 3: indenting objects','heading');
+ $self->indent_children_recursively;
+
+ # final indentation check
+ $self->logger('Phase 4: final indentation check','heading');
+ $self->final_indentation_check;
+
+ return;
+}
+
+sub find_objects{
+ my $self = shift;
+
+ # search for environments
+ $self->logger('looking for ENVIRONMENTS');
+ $self->find_environments;
+
+ # search for ifElseFi blocks
+ $self->logger('looking for IFELSEFI');
+ $self->find_ifelsefi;
+
+ # search for headings (part, chapter, section, setc)
+ $self->logger('looking for HEADINGS (chapter, section, part, etc)');
+ $self->find_heading;
+
+ # search for commands with arguments
+ $self->logger('looking for COMMANDS and key = {value}');
+ $self->find_commands_or_key_equals_values_braces;
+
+ # search for special begin/end
+ $self->logger('looking for SPECIAL begin/end');
+ $self->find_special;
+
+ # if there are no children, return
+ if(${$self}{children}){
+ $self->logger("Objects have been found.",'heading');
+ } else {
+ $self->logger("No objects found.");
+ return;
+ }
+
+ # logfile information
+ $self->logger(Dumper(\%{$self}),'ttrace') if($is_tt_switch_active);
+ $self->logger("Operating on: ${$self}{name}",'heading');
+ $self->logger("Number of children:",'heading');
+ $self->logger(scalar (@{${$self}{children}}));
+
+ return;
+}
+
+sub tasks_particular_to_each_object{
+ my $self = shift;
+ $self->logger("There are no tasks particular to ${$self}{name}") if $is_t_switch_active;
+}
+
+sub get_settings_and_store_new_object{
+ my $self = shift;
+
+ # grab the object to be operated upon
+ my ($latexIndentObject) = @_;
+
+ # there are a number of tasks common to each object
+ $latexIndentObject->tasks_common_to_each_object(%{$self});
+
+ # tasks particular to each object
+ $latexIndentObject->tasks_particular_to_each_object;
+
+ # store children in special hash
+ push(@{${$self}{children}},$latexIndentObject);
+
+}
+
+sub tasks_common_to_each_object{
+ my $self = shift;
+
+ # grab the parent information
+ my %parent = @_;
+
+ # update/create the ancestor information
+ if($parent{ancestors}){
+ $self->logger("Ancestors *have* been found for ${$self}{name}") if($is_t_switch_active);
+ push(@{${$self}{ancestors}},@{$parent{ancestors}});
+ } else {
+ $self->logger("No ancestors found for ${$self}{name}") if($is_t_switch_active);
+ if(defined $parent{id} and $parent{id} ne ''){
+ $self->logger("Creating ancestors with $parent{id} as the first one") if($is_t_switch_active);
+ push(@{${$self}{ancestors}},{ancestorID=>$parent{id},ancestorIndentation=>\$parent{indentation},type=>"natural",name=>${$self}{name}});
+ }
+ }
+
+ # natural ancestors
+ ${$self}{naturalAncestors} = q();
+ if(${$self}{ancestors}){
+ ${$self}{naturalAncestors} .= "---".${$_}{ancestorID}."\n" for @{${$self}{ancestors}};
+ }
+
+ # in what follows, $self can be an environment, ifElseFi, etc
+
+ # count linebreaks in body
+ my $bodyLineBreaks = 0;
+ $bodyLineBreaks++ while(${$self}{body} =~ m/\R/sxg);
+ ${$self}{bodyLineBreaks} = $bodyLineBreaks;
+
+ # get settings for this object
+ $self->get_indentation_settings_for_this_object;
+
+ # give unique id
+ $self->create_unique_id;
+
+ # add trailing text to the id to stop, e.g LATEX-INDENT-ENVIRONMENT1 matching LATEX-INDENT-ENVIRONMENT10
+ ${$self}{id} .= $tokens{endOfToken};
+
+ # the replacement text can be just the ID, but the ID might have a line break at the end of it
+ $self->get_replacement_text;
+
+ # the above regexp, when used below, will remove the trailing linebreak in ${$self}{linebreaksAtEnd}{end}
+ # so we compensate for it here
+ $self->adjust_replacement_text_line_breaks_at_end;
+
+ # modify line breaks on body and end statements
+ $self->modify_line_breaks_body;
+
+ # modify line breaks end statements
+ $self->modify_line_breaks_end;
+
+ # check the body for current children
+ $self->check_for_hidden_children;
+
+ return;
+}
+
+sub get_replacement_text{
+ my $self = shift;
+
+ # the replacement text can be just the ID, but the ID might have a line break at the end of it
+ ${$self}{replacementText} = ${$self}{id};
+ return;
+}
+
+sub adjust_replacement_text_line_breaks_at_end{
+ my $self = shift;
+
+ # the above regexp, when used below, will remove the trailing linebreak in ${$self}{linebreaksAtEnd}{end}
+ # so we compensate for it here
+ $self->logger("Putting linebreak after replacementText for ${$self}{name}") if($is_t_switch_active);
+ if(defined ${$self}{horizontalTrailingSpace}){
+ ${$self}{replacementText} .= ${$self}{horizontalTrailingSpace} unless(!${$self}{endImmediatelyFollowedByComment} and defined ${$self}{EndFinishesWithLineBreak} and ${$self}{EndFinishesWithLineBreak}==2);
+ }
+ ${$self}{replacementText} .= "\n" if(${$self}{linebreaksAtEnd}{end});
+
+}
+
+sub count_body_line_breaks{
+ my $self = shift;
+
+ my $oldBodyLineBreaks = (defined ${$self}{bodyLineBreaks})? ${$self}{bodyLineBreaks} : 0;
+
+ # count linebreaks in body
+ my $bodyLineBreaks = 0;
+ $bodyLineBreaks++ while(${$self}{body} =~ m/\R/sxg);
+ ${$self}{bodyLineBreaks} = $bodyLineBreaks;
+ $self->logger("bodyLineBreaks ${$self}{bodyLineBreaks}") if((${$self}{bodyLineBreaks} != $oldBodyLineBreaks) and $is_t_switch_active);
+}
+
+sub wrap_up_tasks{
+ my $self = shift;
+
+ # most recent child object
+ my $child = @{${$self}{children}}[-1];
+
+ # check if the last object was the last thing in the body, and if it has adjusted linebreaks
+ $self->adjust_line_breaks_end_parent;
+
+ $self->logger(Dumper(\%{$child})) if($is_tt_switch_active);
+ $self->logger("replaced with ID: ${$child}{id}") if $is_t_switch_active;
+
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/DoubleBackSlash.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/DoubleBackSlash.pm
new file mode 100644
index 00000000000..8b8be3e25ec
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/DoubleBackSlash.pm
@@ -0,0 +1,45 @@
+package LatexIndent::DoubleBackSlash;
+# 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::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use LatexIndent::Tokens qw/%tokens/;
+use Exporter qw/import/;
+our @EXPORT_OK = qw/dodge_double_backslash un_dodge_double_backslash/;
+
+# some code can contain, e.g
+# cycle list={blue,mark=none\\},
+# see test-cases/texexchange/29293-christian-feuersanger.tex
+#
+# This is problematic, as the argument regexp won't count the right } because it has a
+# backslash immediately infront of it!
+sub dodge_double_backslash{
+ my $self = shift;
+
+ ${$self}{body} =~ s/(?:\\\\(\{|\}|\]))/$tokens{doubleBackSlash}$1/sg;
+ return;
+}
+
+# this routine replaces the token with the \\\\
+sub un_dodge_double_backslash{
+ my $self = shift;
+
+ ${$self}{body} =~ s/$tokens{doubleBackSlash}/\\\\/sg;
+ return;
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/Environment.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/Environment.pm
new file mode 100755
index 00000000000..1b0ad27d550
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/Environment.pm
@@ -0,0 +1,128 @@
+package LatexIndent::Environment;
+# 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::TrailingComments qw/$trailingCommentRegExp/;
+use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use Data::Dumper;
+use Exporter qw/import/;
+our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321
+our @EXPORT_OK = qw/find_environments/;
+our $environmentCounter;
+
+# store the regular expresssion for matching and replacing the \begin{}...\end{} statements
+our $environmentRegExp = qr/
+ (
+ \\begin\{
+ (
+ [a-zA-Z@\*0-9_\\]+ # lowercase|uppercase letters, @, *, numbers
+ ) # environment name captured into $2
+ \} # \begin{<something>} statement
+ \h* # horizontal space
+ (\R*)? # possible line breaks (into $3)
+ ) # begin statement captured into $1
+ (
+ (?: # cluster-only (), don't capture
+ (?! # don't include \begin in the body
+ (?:\\begin) # cluster-only (), don't capture
+ ). # any character, but not \\begin
+ )*? # non-greedy
+ (\R*)? # possible line breaks (into $5)
+ ) # environment body captured into $4
+ (
+ \\end\{\2\} # \end{<something>} statement
+ ) # captured into $6
+ (\h*)? # possibly followed by horizontal space
+ (\R)? # possibly followed by a line break
+ /sx;
+
+sub find_environments{
+ my $self = shift;
+
+
+ while( ${$self}{body} =~ m/$environmentRegExp\h*($trailingCommentRegExp)?/){
+
+ # global substitution
+ ${$self}{body} =~ s/
+ $environmentRegExp(\h*)($trailingCommentRegExp)?
+ /
+ # log file output
+ $self->logger("environment found: $2",'heading') if $is_t_switch_active;
+
+ # create a new Environment object
+ my $env = LatexIndent::Environment->new(begin=>$1,
+ name=>$2,
+ body=>$4,
+ end=>$6,
+ linebreaksAtEnd=>{
+ begin=>$3?1:0,
+ body=>$5?1:0,
+ end=>$8?1:0,
+ },
+ modifyLineBreaksYamlName=>"environments",
+ endImmediatelyFollowedByComment=>$8?0:($10?1:0),
+ horizontalTrailingSpace=>$7?$7:q(),
+ );
+
+ # the settings and storage of most objects has a lot in common
+ $self->get_settings_and_store_new_object($env);
+ ${@{${$self}{children}}[-1]}{replacementText}.($9?$9:q()).($10?$10:q());
+ /xseg;
+ $self->adjust_line_breaks_end_parent;
+ }
+ return;
+}
+
+sub tasks_particular_to_each_object{
+ my $self = shift;
+
+ # if the environment is empty, we may need to update linebreaksAtEnd{body}
+ if(${$self}{body} =~ m/^\h*$/s and ${${$self}{linebreaksAtEnd}}{begin}){
+ $self->logger("empty environment body (${$self}{name}), updating linebreaksAtEnd{body} to be 1") if($is_t_switch_active);
+ ${${$self}{linebreaksAtEnd}}{body} = 1;
+ }
+
+ # search for items as the first order of business
+ $self->find_items;
+
+ # search for headings (important to do this before looking for commands!)
+ $self->find_heading;
+
+ # search for commands, keys, named grouping braces
+ $self->find_commands_or_key_equals_values_braces;
+
+ # search for arguments
+ $self->find_opt_mand_arguments;
+
+ # search for ifElseFi blocks
+ $self->find_ifelsefi;
+
+ # search for special begin/end
+ $self->find_special;
+}
+
+sub create_unique_id{
+ my $self = shift;
+
+ $environmentCounter++;
+ ${$self}{id} = "$tokens{environment}$environmentCounter";
+ return;
+}
+
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/FileContents.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/FileContents.pm
new file mode 100644
index 00000000000..dc8aa53f206
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/FileContents.pm
@@ -0,0 +1,230 @@
+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/%masterSettings/;
+use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+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
+ $self->logger('looking for FILE CONTENTS environments (see fileContentsEnvironments)','heading');
+ $self->logger(Dumper(\%{$masterSettings{fileContentsEnvironments}})) if($is_t_switch_active);
+ while( my ($fileContentsEnv,$yesno)= each %{$masterSettings{fileContentsEnvironments}}){
+ if($yesno){
+ $self->logger("looking for $fileContentsEnv:$yesno environments");
+
+ # the trailing * needs some care
+ if($fileContentsEnv =~ m/\*$/){
+ $fileContentsEnv =~ s/\*$//;
+ $fileContentsEnv .= '\*';
+ }
+
+ my $fileContentsRegExp = qr/
+ (
+ \\begin\{
+ $fileContentsEnv
+ \}
+ )
+ (
+ .*?
+ )
+ (
+ \\end\{$fileContentsEnv\}
+ \h*
+ )
+ (\R)?
+ /sx;
+
+ while( ${$self}{body} =~ m/$fileContentsRegExp/sx){
+
+ # create a new Environment object
+ my $fileContentsBlock = LatexIndent::FileContents->new( begin=>$1,
+ body=>$2,
+ end=>$3,
+ name=>$fileContentsEnv,
+ linebreaksAtEnd=>{
+ begin=>0,
+ body=>0,
+ end=>$4?1:0,
+ },
+ modifyLineBreaksYamlName=>"filecontents",
+ );
+ # give unique id
+ $fileContentsBlock->create_unique_id;
+
+ # 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
+ $self->logger("FILECONTENTS environment found: $fileContentsEnv");
+
+ # remove the environment block, and replace with unique ID
+ ${$self}{body} =~ s/$fileContentsRegExp/${$fileContentsBlock}{replacementText}/sx;
+
+ $self->logger("replaced with ID: ${$fileContentsBlock}{id}");
+ }
+ } else {
+ $self->logger("*not* looking for $fileContentsEnv as $fileContentsEnv:$yesno");
+ }
+ }
+
+ # determine if body of document contains \begin{document} -- if it does, then assume
+ # that the body has a preamble
+ my $preambleRegExp = qr/
+ (.*?)
+ (\R*)? # linebreaks at end of body into $2
+ \\begin\{document\}
+ /sx;
+ my $preamble = q();
+
+ my $needToStorePreamble = 0;
+
+ # try and find the preamble
+ if( ${$self}{body} =~ m/$preambleRegExp/sx and ${$masterSettings{lookForPreamble}}{${$self}{fileExtension}}){
+
+ $self->logger("\\begin{document} found in body (after searching for filecontents)-- assuming that a preamble exists");
+
+ # 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;
+
+ # get the replacement_text
+ $preamble->get_replacement_text;
+
+ # log file output
+ $self->logger("preamble found: $preamble");
+
+ # remove the environment block, and replace with unique ID
+ ${$self}{body} =~ s/$preambleRegExp/${$preamble}{replacementText}/sx;
+
+ $self->logger("replaced with ID: ${$preamble}{replacementText}");
+ # indentPreamble set to 1
+ if($masterSettings{indentPreamble}){
+ $self->logger("storing ${$preamble}{id} for indentation (see indentPreamble)");
+ $needToStorePreamble = 1;
+ } else {
+ # indentPreamble set to 0
+ $self->logger("NOT storing ${$preamble}{id} for indentation -- will store as VERBATIM object (see indentPreamble)");
+ $preamble->unprotect_blank_lines;
+ ${$self}{verbatim}{${$preamble}{id}} = $preamble;
+ }
+ }
+
+ # 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}/){
+ $self->logger("filecontents (${$_}{id}) is within preamble");
+ # indentPreamble set to 1
+ if($masterSettings{indentPreamble}){
+ $self->logger("storing ${$_}{id} for indentation (indentPreamble is 1)");
+ $indentThisChild = 1;
+ } else {
+ # indentPreamble set to 0
+ $self->logger("Storing ${$_}{id} as a VERBATIM object (indentPreamble is 0)");
+ ${$self}{verbatim}{${$_}{id}}=$_;
+ }
+ } else {
+ $self->logger("storing ${$_}{id} for indentation (${$_}{name} found outside of preamble)");
+ $indentThisChild = 1;
+ }
+ # store the child, if necessary
+ if($indentThisChild){
+ $_->remove_leading_space;
+ $_->get_indentation_settings_for_this_object;
+ $_->tasks_particular_to_each_object;
+ push(@{${$self}{children}},$_);
+ }
+ }
+
+ if($needToStorePreamble){
+ $preamble->dodge_double_backslash;
+ $preamble->remove_leading_space;
+ $preamble->find_commands_or_key_equals_values_braces if($masterSettings{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;
+
+ # search for ifElseFi blocks
+ $self->find_ifelsefi;
+
+ # search for headings (part, chapter, section, setc)
+ $self->find_heading;
+
+ # search for commands with arguments
+ $self->find_commands_or_key_equals_values_braces;
+
+ # search for special begin/end
+ $self->find_special;
+
+
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/FileExtension.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/FileExtension.pm
new file mode 100644
index 00000000000..8dcb66dd80c
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/FileExtension.pm
@@ -0,0 +1,99 @@
+package LatexIndent::FileExtension;
+# 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::GetYamlSettings qw/%masterSettings/;
+use File::Basename; # to get the filename and directory path
+use Exporter qw/import/;
+our @EXPORT_OK = qw/file_extension_check/;
+
+sub file_extension_check{
+ my $self = shift;
+
+ # grab the filename
+ my $fileName = ${$self}{fileName};
+
+ # grab the file extension preferences
+ my %fileExtensionPreference= %{$masterSettings{fileExtensionPreference}};
+
+ # sort the file extensions by preference
+ my @fileExtensions = sort { $fileExtensionPreference{$a} <=> $fileExtensionPreference{$b} } keys(%fileExtensionPreference);
+
+ # get the base file name, allowing for different extensions (possibly no extension)
+ my ($dir, $name, $ext) = fileparse($fileName, @fileExtensions);
+
+ # check to make sure given file type is supported
+ if( -e $fileName and !$ext ){
+ my $message = "The file $fileName exists , but the extension does not correspond to any given in fileExtensionPreference; consinder updating fileExtensionPreference.";
+ $self->logger($message,'heading');
+ $self->output_logfile;
+ die($message);
+ }
+
+ # if no extension, search according to fileExtensionPreference
+ if (!$ext) {
+ $self->logger("File extension work:",'heading');
+ $self->logger("latexindent called to act upon $fileName with an, as yet, unrecognised file extension;");
+ $self->logger("searching for file with an extension in the following order (see fileExtensionPreference):");
+ $self->logger(join("\n",@fileExtensions));
+
+ my $fileFound = 0;
+ # loop through the known file extensions (see @fileExtensions)
+ foreach (@fileExtensions ){
+ if ( -e $fileName.$_ ) {
+ $self->logger("$fileName$_ found!");
+ $fileName .= $_;
+ $self->logger("Updated fileName to $fileName");
+ ${$self}{fileName} = $fileName ;
+ $fileFound = 1;
+ $ext = $_;
+ last;
+ }
+ }
+ unless($fileFound){
+ $self->logger("I couldn't find a match for $fileName in fileExtensionPreference (see defaultSettings.yaml)");
+ foreach (@fileExtensions ){
+ $self->logger("I searched for $fileName$_");
+ }
+ $self->logger("but couldn't find any of them.");
+ $self->logger("Consider updating fileExtensionPreference. Error: Exiting, no indendation done.");
+ $self->output_logfile;
+ die "I couldn't find a match for $fileName in fileExtensionPreference.\nExiting, no indendation done.";
+ }
+ } else {
+ # if the file has a recognised extension, check that the file exists
+ unless( -e $fileName ){
+ my $message = "Error: I couldn't find $fileName, are you sure it exists?. No indentation done. Exiting.";
+ $self->logger($message);
+ $self->output_logfile;
+ die $message;
+ }
+ }
+
+ # store the file extension
+ ${$self}{fileExtension} = $ext;
+
+ # read the file into the Document body
+ my @lines;
+ open(MAINFILE, $fileName) or die "Could not open input file, $fileName";
+ push(@lines,$_) while(<MAINFILE>);
+ close(MAINFILE);
+
+ # the all-important step: update the body
+ ${$self}{body} = join("",@lines);
+}
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/GetYamlSettings.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/GetYamlSettings.pm
new file mode 100644
index 00000000000..4c93bc80426
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/GetYamlSettings.pm
@@ -0,0 +1,471 @@
+package LatexIndent::GetYamlSettings;
+# 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 LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use strict;
+use warnings;
+use LatexIndent::Switches qw/%switches $is_m_switch_active $is_t_switch_active $is_tt_switch_active/;
+use YAML::Tiny; # interpret defaultSettings.yaml and other potential settings files
+use File::Basename; # to get the filename and directory path
+use File::HomeDir;
+use Exporter qw/import/;
+our @EXPORT_OK = qw/readSettings modify_line_breaks_settings get_indentation_settings_for_this_object get_every_or_custom_value get_indentation_information get_object_attribute_for_indentation_settings alignment_at_ampersand_settings %masterSettings/;
+
+# Read in defaultSettings.YAML file
+our $defaultSettings;
+
+# master yaml settings is a hash, global to this module
+our %masterSettings;
+
+# previously found settings is a hash, global to this module
+our %previouslyFoundSettings;
+
+sub readSettings{
+ my $self = shift;
+
+ $defaultSettings = YAML::Tiny->new;
+ $defaultSettings = YAML::Tiny->read( "$FindBin::RealBin/defaultSettings.yaml" );
+ $self->logger("YAML settings read",'heading');
+ $self->logger("Reading defaultSettings.yaml from $FindBin::RealBin/defaultSettings.yaml");
+
+ # if latexindent.exe is invoked from TeXLive, then defaultSettings.yaml won't be in
+ # the same directory as it; we need to navigate to it
+ if(!$defaultSettings) {
+ $defaultSettings = YAML::Tiny->read( "$FindBin::RealBin/../../texmf-dist/scripts/latexindent/defaultSettings.yaml");
+ $self->logger("Reading defaultSettings.yaml (2nd attempt, TeXLive, Windows) from $FindBin::RealBin/../../texmf-dist/scripts/latexindent/defaultSettings.yaml");
+ }
+
+ # need to exit if we can't get defaultSettings.yaml
+ die "Could not open defaultSettings.yaml" if(!$defaultSettings);
+
+ # master yaml settings is a hash, global to this module
+ our %masterSettings = %{$defaultSettings->[0]};
+
+ # scalar to read user settings
+ my $userSettings;
+
+ # array to store the paths to user settings
+ my @absPaths;
+
+ # we'll need the home directory a lot in what follows
+ my $homeDir = File::HomeDir->my_home;
+
+ # get information about user settings- first check if indentconfig.yaml exists
+ my $indentconfig = "$homeDir/indentconfig.yaml";
+
+ # if indentconfig.yaml doesn't exist, check for the hidden file, .indentconfig.yaml
+ $indentconfig = "$homeDir/.indentconfig.yaml" if(! -e $indentconfig);
+
+ # messages for indentconfig.yaml and/or .indentconfig.yaml
+ if ( -e $indentconfig and !$switches{onlyDefault}) {
+ $self->logger("Reading path information from $indentconfig");
+ # if both indentconfig.yaml and .indentconfig.yaml exist
+ if ( -e File::HomeDir->my_home . "/indentconfig.yaml" and -e File::HomeDir->my_home . "/.indentconfig.yaml") {
+ $self->logger("$homeDir/.indentconfig.yaml has been found, but $indentconfig takes priority");
+ } elsif ( -e File::HomeDir->my_home . "/indentconfig.yaml" ) {
+ $self->logger("(Alternatively $homeDir/.indentconfig.yaml can be used)");
+ } elsif ( -e File::HomeDir->my_home . "/.indentconfig.yaml" ) {
+ $self->logger("(Alternatively $homeDir/indentconfig.yaml can be used)");
+ }
+
+ # read the absolute paths from indentconfig.yaml
+ $userSettings = YAML::Tiny->read( "$indentconfig" );
+
+ # output the contents of indentconfig to the log file
+ $self->logger(Dump \%{$userSettings->[0]});
+
+ # update the absolute paths
+ @absPaths = @{$userSettings->[0]->{paths}};
+ } else {
+ if($switches{onlyDefault}) {
+ $self->logger("Only default settings requested, not reading USER settings from $indentconfig");
+ $self->logger("Ignoring $switches{readLocalSettings} (you used the -d switch)") if($switches{readLocalSettings});
+ $switches{readLocalSettings}=0;
+ } else {
+ # give the user instructions on where to put indentconfig.yaml or .indentconfig.yaml
+ $self->logger("Home directory is $homeDir (didn't find either indentconfig.yaml or .indentconfig.yaml)");
+ $self->logger("To specify user settings you would put indentconfig.yaml here: $homeDir/indentconfig.yaml");
+ $self->logger("Alternatively, you can use the hidden file .indentconfig.yaml as: $homeDir/.indentconfig.yaml");
+ }
+ }
+
+ # get information about LOCAL settings, assuming that $readLocalSettings exists
+ my $directoryName = dirname (${$self}{fileName});
+
+ # local settings can be separated by ,
+ # e.g
+ # -l = myyaml1.yaml,myyaml2.yaml
+ # and in which case, we need to read them all
+ my @localSettings;
+ if($switches{readLocalSettings} =~ m/,/){
+ $self->logger("Multiple localSettings found, separated by commas:",'heading');
+ @localSettings = split(/,/,$switches{readLocalSettings});
+ } else {
+ push(@localSettings,$switches{readLocalSettings}) if($switches{readLocalSettings});
+ }
+
+ # add local settings to the paths, if appropriate
+ foreach (@localSettings) {
+ if ( (-e "$directoryName/$_") and !(-z "$directoryName/$_")) {
+ $self->logger("Adding $directoryName/$_ to YAML read paths");
+ push(@absPaths,"$directoryName/$_");
+ } elsif ( !(-e "$directoryName/$_") ) {
+ $self->logger("WARNING yaml file not found: $directoryName/$_ not found");
+ $self->logger("Proceeding without it.");
+ }
+ }
+
+ # read in the settings from each file
+ foreach my $settings (@absPaths) {
+ # check that the settings file exists and that it isn't empty
+ if (-e $settings and !(-z $settings)) {
+ $self->logger("Reading USER settings from $settings");
+ $userSettings = YAML::Tiny->read( "$settings" );
+
+ # if we can read userSettings
+ if($userSettings) {
+ # update the MASTER setttings to include updates from the userSettings
+ while(my($firstLevelKey, $firstLevelValue) = each %{$userSettings->[0]}) {
+ # the update approach is slightly different for hashes vs scalars/arrays
+ if (ref($firstLevelValue) eq "HASH") {
+ while(my ($secondLevelKey,$secondLevelValue) = each %{$userSettings->[0]{$firstLevelKey}}) {
+ if (ref $secondLevelValue eq "HASH"){
+ # if masterSettings already contains a *scalar* value in secondLevelKey
+ # then we need to delete it (test-cases/headings-first.tex with indentRules1.yaml first demonstrated this)
+ if(ref $masterSettings{$firstLevelKey}{$secondLevelKey} ne "HASH"){
+ $self->logger("masterSettings{$firstLevelKey}{$secondLevelKey} currently contains a *scalar* value, but it needs to be updated with a hash (see $settings); deleting the scalar") if($is_t_switch_active);
+ delete $masterSettings{$firstLevelKey}{$secondLevelKey} ;
+ }
+ while(my ($thirdLevelKey,$thirdLevelValue) = each %{$secondLevelValue}) {
+ if (ref $thirdLevelValue eq "HASH"){
+ # similarly for third level
+ if (ref $masterSettings{$firstLevelKey}{$secondLevelKey}{$thirdLevelKey} ne "HASH"){
+ $self->logger("masterSettings{$firstLevelKey}{$secondLevelKey}{$thirdLevelKey} currently contains a *scalar* value, but it needs to be updated with a hash (see $settings); deleting the scalar") if($is_t_switch_active);
+ delete $masterSettings{$firstLevelKey}{$secondLevelKey}{$thirdLevelKey} ;
+ }
+ while(my ($fourthLevelKey,$fourthLevelValue) = each %{$thirdLevelValue}) {
+ $masterSettings{$firstLevelKey}{$secondLevelKey}{$thirdLevelKey}{$fourthLevelKey} = $fourthLevelValue;
+ }
+ } else {
+ $masterSettings{$firstLevelKey}{$secondLevelKey}{$thirdLevelKey} = $thirdLevelValue;
+ }
+ }
+ } else {
+ $masterSettings{$firstLevelKey}{$secondLevelKey} = $secondLevelValue;
+ }
+ }
+ } else {
+ $masterSettings{$firstLevelKey} = $firstLevelValue;
+ }
+ }
+
+ # output settings to $logfile
+ if($masterSettings{logFilePreferences}{showEveryYamlRead}){
+ $self->logger(Dump \%{$userSettings->[0]});
+ } else {
+ $self->logger("Not showing settings in the log file (see showEveryYamlRead and showAmalgamatedSettings).");
+ }
+ } else {
+ # otherwise print a warning that we can not read userSettings.yaml
+ $self->logger("WARNING $settings contains invalid yaml format- not reading from it");
+ }
+ } else {
+ # otherwise keep going, but put a warning in the log file
+ $self->logger("WARNING: $homeDir/indentconfig.yaml");
+ if (-z $settings) {
+ $self->logger("specifies $settings but this file is EMPTY -- not reading from it");
+ } else {
+ $self->logger("specifies $settings but this file does not exist - unable to read settings from this file");
+ }
+ }
+ }
+
+ # some users may wish to see showAmalgamatedSettings
+ # which details the overall state of the settings modified
+ # from the default in various user files
+ if($masterSettings{logFilePreferences}{showAmalgamatedSettings}){
+ $self->logger("Amalgamated/overall settings to be used:",'heading');
+ $self->logger(Dump \%masterSettings);
+ }
+
+ return;
+}
+
+sub get_indentation_settings_for_this_object{
+ my $self = shift;
+
+ # create a name for previously found settings
+ my $storageName = ${$self}{name}.${$self}{modifyLineBreaksYamlName};
+
+ # check for storage of repeated objects
+ if ($previouslyFoundSettings{$storageName}){
+ $self->logger("Using stored settings for $storageName") if($is_t_switch_active);
+ } else {
+ my $name = ${$self}{name};
+ $self->logger("Storing settings for $storageName") if($is_t_switch_active);
+
+ # check for noAdditionalIndent and indentRules
+ # otherwise use defaultIndent
+ my $indentation = $self->get_indentation_information;
+
+ # check for alignment at ampersand settings
+ $self->alignment_at_ampersand_settings;
+
+ # check for line break settings
+ $self->modify_line_breaks_settings;
+
+ # store the settings
+ %{${previouslyFoundSettings}{$storageName}} = (
+ indentation=>$indentation,
+ BeginStartsOnOwnLine=>${$self}{BeginStartsOnOwnLine},
+ BodyStartsOnOwnLine=>${$self}{BodyStartsOnOwnLine},
+ EndStartsOnOwnLine=>${$self}{EndStartsOnOwnLine},
+ EndFinishesWithLineBreak=>${$self}{EndFinishesWithLineBreak},
+ );
+
+ # don't forget alignment settings!
+ ${${previouslyFoundSettings}{$storageName}}{lookForAlignDelims} = ${$self}{lookForAlignDelims} if(defined ${$self}{lookForAlignDelims});
+ ${${previouslyFoundSettings}{$storageName}}{alignDoubleBackSlash} = ${$self}{alignDoubleBackSlash} if(defined ${$self}{alignDoubleBackSlash});
+ ${${previouslyFoundSettings}{$storageName}}{spacesBeforeDoubleBackSlash} = ${$self}{spacesBeforeDoubleBackSlash} if(defined ${$self}{spacesBeforeDoubleBackSlash});
+
+ # some objects, e.g ifElseFi, can have extra assignments, e.g ElseStartsOnOwnLine
+ # these need to be stored as well!
+ foreach (@{${$self}{additionalAssignments}}){
+ ${${previouslyFoundSettings}{$storageName}}{$_} = ${$self}{$_};
+ }
+
+ }
+
+ # append indentation settings to the current object
+ while( my ($key,$value)= each %{${previouslyFoundSettings}{$storageName}}){
+ ${$self}{$key} = $value;
+ }
+
+ return;
+}
+
+sub alignment_at_ampersand_settings{
+ my $self = shift;
+
+ # if the YamlName is, for example, optionalArguments, mandatoryArguments, heading, then we'll be looking for information about the *parent*
+ my $name = (defined ${$self}{nameForIndentationSettings}) ? ${$self}{nameForIndentationSettings} : ${$self}{name};
+
+ # check, for example,
+ # lookForAlignDelims:
+ # tabular: 1
+ # or
+ #
+ # lookForAlignDelims:
+ # tabular:
+ # delims: 1
+ # alignDoubleBackSlash: 1
+ # spacesBeforeDoubleBackSlash: 2
+ return unless ${$masterSettings{lookForAlignDelims}}{$name};
+
+ ## check, for example,
+ ## lookForAlignDelims:
+ ## tabular:
+ ## body: 1
+ #return unless $self->get_object_attribute_for_indentation_settings;
+
+ if(ref ${$masterSettings{lookForAlignDelims}}{$name} eq "HASH"){
+ ${$self}{lookForAlignDelims} = (defined ${${$masterSettings{lookForAlignDelims}}{$name}}{delims} ) ? ${${$masterSettings{lookForAlignDelims}}{$name}}{delims} : 1;
+ ${$self}{alignDoubleBackSlash} = (defined ${${$masterSettings{lookForAlignDelims}}{$name}}{alignDoubleBackSlash} ) ? ${${$masterSettings{lookForAlignDelims}}{$name}}{alignDoubleBackSlash} : 1;
+ ${$self}{spacesBeforeDoubleBackSlash} = (defined ${${$masterSettings{lookForAlignDelims}}{$name}}{spacesBeforeDoubleBackSlash} ) ? ${${$masterSettings{lookForAlignDelims}}{$name}}{spacesBeforeDoubleBackSlash} : -1;
+ } else {
+ ${$self}{lookForAlignDelims} = 1;
+ ${$self}{alignDoubleBackSlash} = 1;
+ ${$self}{spacesBeforeDoubleBackSlash} = -1;
+ }
+ return;
+}
+
+sub modify_line_breaks_settings{
+ # return with undefined values unless the -m switch is active
+ return unless $is_m_switch_active;
+
+ my $self = shift;
+
+ # details to the log file
+ $self->logger("-m modifylinebreaks switch active, looking for settings for ${$self}{name} ",'heading') if $is_t_switch_active;
+
+ # some objects, e.g ifElseFi, can have extra assignments, e.g ElseStartsOnOwnLine
+ my @toBeAssignedTo = ${$self}{additionalAssignments} ? @{${$self}{additionalAssignments}} : ();
+
+ # the following will *definitley* be in the array, so let's add them
+ push(@toBeAssignedTo,("BeginStartsOnOwnLine","BodyStartsOnOwnLine","EndStartsOnOwnLine","EndFinishesWithLineBreak"));
+
+ # we can effeciently loop through the following
+ foreach (@toBeAssignedTo){
+ $self->get_every_or_custom_value(
+ toBeAssignedTo=>$_,
+ toBeAssignedToAlias=> ${$self}{aliases}{$_} ? ${$self}{aliases}{$_} : $_,
+ );
+ }
+ return;
+}
+
+sub get_every_or_custom_value{
+ my $self = shift;
+ my %input = @_;
+
+ my $toBeAssignedTo = $input{toBeAssignedTo};
+ my $toBeAssignedToAlias = $input{toBeAssignedToAlias};
+
+ # alias
+ if(${$self}{aliases}{$toBeAssignedTo}){
+ $self->logger("aliased $toBeAssignedTo using ${$self}{aliases}{$toBeAssignedTo}") if($is_t_switch_active);
+ }
+
+ # name of the object in the modifyLineBreaks yaml (e.g environments, ifElseFi, etc)
+ my $YamlName = ${$self}{modifyLineBreaksYamlName};
+
+ # if the YamlName is either optionalArguments or mandatoryArguments, then we'll be looking for information about the *parent*
+ my $name = ($YamlName =~ m/Arguments/) ? ${$self}{parent} : ${$self}{name};
+
+ # these variables just ease the notation what follows
+ my $everyValue = ${${$masterSettings{modifyLineBreaks}}{$YamlName}}{$toBeAssignedToAlias};
+ my $customValue = ${${${$masterSettings{modifyLineBreaks}}{$YamlName}}{$name}}{$toBeAssignedToAlias};
+
+ # check for the *custom* value
+ if (defined $customValue){
+ $self->logger("$name: $toBeAssignedToAlias=$customValue, (*custom* value) adjusting $toBeAssignedTo") if($is_t_switch_active);
+ ${$self}{$toBeAssignedTo} = $customValue !=0 ? $customValue : undef;
+ } else {
+ # check for the *every* value
+ if (defined $everyValue and $everyValue != 0){
+ $self->logger("$name: $toBeAssignedToAlias=$everyValue, (*every* value) adjusting $toBeAssignedTo") if($is_t_switch_active);
+ ${$self}{$toBeAssignedTo} = $everyValue;
+ }
+ }
+ return;
+}
+
+sub get_indentation_information{
+ my $self = shift;
+
+ #**************************************
+ # SEARCHING ORDER:
+ # noAdditionalIndent *per-name* basis
+ # indentRules *per-name* basis
+ # noAdditionalIndentGlobal
+ # indentRulesGlobal
+ #**************************************
+
+ # noAdditionalIndent can be a scalar or a hash, e.g
+ #
+ # noAdditionalIndent:
+ # myexample: 1
+ #
+ # OR
+ #
+ # noAdditionalIndent:
+ # myexample:
+ # body: 1
+ # optionalArguments: 1
+ # mandatoryArguments: 1
+ #
+ # specifying as a scalar with no field (e.g myexample: 1)
+ # will be interpreted as noAdditionalIndent for *every*
+ # field, so the body, optional arguments and mandatory arguments
+ # will *all* receive noAdditionalIndent
+ #
+ # indentRules can also be a scalar or a hash, e.g
+ # indentRules:
+ # myexample: "\t"
+ #
+ # OR
+ #
+ # indentRules:
+ # myexample:
+ # body: " "
+ # optionalArguments: "\t \t"
+ # mandatoryArguments: ""
+ #
+ # specifying as a scalar with no field will
+ # mean that *every* field will receive the same treatment
+
+ # if the YamlName is, for example, optionalArguments, mandatoryArguments, heading, then we'll be looking for information about the *parent*
+ my $name = (defined ${$self}{nameForIndentationSettings}) ? ${$self}{nameForIndentationSettings} : ${$self}{name};
+
+ # if the YamlName is not optionalArguments, mandatoryArguments, heading (possibly others) then assume we're looking for 'body'
+ my $YamlName = $self->get_object_attribute_for_indentation_settings;
+
+ my $indentationInformation;
+ foreach my $indentationAbout ("noAdditionalIndent","indentRules"){
+ # check that the 'thing' is defined
+ if(defined ${$masterSettings{$indentationAbout}}{$name}){
+ if(ref ${$masterSettings{$indentationAbout}}{$name} eq "HASH"){
+ $self->logger("$indentationAbout indentation specified with multiple fields for $name, searching for $name: $YamlName (see $indentationAbout)") if $is_t_switch_active ;
+ $indentationInformation = ${${$masterSettings{$indentationAbout}}{$name}}{$YamlName};
+ } else {
+ $indentationInformation = ${$masterSettings{$indentationAbout}}{$name};
+ $self->logger("$indentationAbout indentation specified for $name (for *all* fields, body, optionalArguments, mandatoryArguments, afterHeading), using '$indentationInformation' (see $indentationAbout)") if $is_t_switch_active ;
+ }
+ # return, after performing an integrity check
+ if(defined $indentationInformation){
+ if($indentationAbout eq "noAdditionalIndent" and $indentationInformation == 1){
+ $self->logger("Found! Using '' (see $indentationAbout)") if $is_t_switch_active;
+ return q();
+ } elsif($indentationAbout eq "indentRules" and $indentationInformation=~m/^\h*$/){
+ $self->logger("Found! Using '$indentationInformation' (see $indentationAbout)") if $is_t_switch_active;
+ return $indentationInformation ;
+ }
+ }
+ }
+ }
+
+ # gather information
+ $YamlName = ${$self}{modifyLineBreaksYamlName};
+
+ foreach my $indentationAbout ("noAdditionalIndent","indentRules"){
+ # global assignments in noAdditionalIndentGlobal and/or indentRulesGlobal
+ my $globalInformation = $indentationAbout."Global";
+ next if(!(defined ${$masterSettings{$globalInformation}}{$YamlName}));
+ if( ($globalInformation eq "noAdditionalIndentGlobal") and ${$masterSettings{$globalInformation}}{$YamlName}==1){
+ $self->logger("$globalInformation specified for $YamlName (see $globalInformation)") if $is_t_switch_active;
+ return q();
+ } elsif($globalInformation eq "indentRulesGlobal") {
+ if(${$masterSettings{$globalInformation}}{$YamlName}=~m/^\h*$/){
+ $self->logger("$globalInformation specified for $YamlName (see $globalInformation)") if $is_t_switch_active;
+ return ${$masterSettings{$globalInformation}}{$YamlName};
+ } else {
+ $self->logger("$globalInformation specified (${$masterSettings{$globalInformation}}{$YamlName}) for $YamlName, but it needs to only contain horizontal space -- I'm ignoring this one") if $is_t_switch_active;
+ }
+ }
+ }
+
+ # return defaultIndent, by default
+ $self->logger("Using defaultIndent for $name") if $is_t_switch_active;
+ return $masterSettings{defaultIndent};
+}
+
+sub get_object_attribute_for_indentation_settings{
+ # when looking for noAdditionalIndent or indentRules, we may need to determine
+ # which thing we're looking for, e.g
+ #
+ # chapter:
+ # body: 0
+ # optionalArguments: 1
+ # mandatoryArguments: 1
+ # afterHeading: 0
+ #
+ # this method returns 'body' by default, but the other objects (optionalArgument, mandatoryArgument, afterHeading)
+ # return their appropriate identifier.
+ return "body";
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/Heading.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/Heading.pm
new file mode 100644
index 00000000000..be7ea36661d
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/Heading.pm
@@ -0,0 +1,240 @@
+package LatexIndent::Heading;
+# 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::Switches qw/$is_m_switch_active $is_t_switch_active $is_tt_switch_active/;
+use LatexIndent::TrailingComments qw/$trailingCommentRegExp/;
+use LatexIndent::GetYamlSettings qw/%masterSettings/;
+use Data::Dumper;
+use Exporter qw/import/;
+our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321
+our @EXPORT_OK = qw/find_heading construct_headings_levels/;
+our $headingCounter;
+our @headingsRegexpArray;
+our $allHeadingsRegexp = q();
+
+sub construct_headings_levels{
+ my $self = shift;
+
+ # grab the heading levels
+ my %headingsLevels = %{$masterSettings{indentAfterHeadings}};
+
+ # delete the values that have indentAfterThisHeading set to 0
+ while( my ($headingName,$headingInfo)= each %headingsLevels){
+ if(!${$headingsLevels{$headingName}}{indentAfterThisHeading}){
+ $self->logger("Not indenting after $headingName (see indentAfterThisHeading)",'heading') if $is_t_switch_active;
+ delete $headingsLevels{$headingName};
+ } else {
+ # *all heading* regexp, remembering put starred headings at the front of the regexp
+ if($headingName =~ m/\*/){
+ $self->logger("Putting $headingName at the beginning of the allHeadings regexp, as it contains a *");
+ $allHeadingsRegexp = $headingName.($allHeadingsRegexp eq '' ?q():"|$allHeadingsRegexp");
+ } else {
+ $self->logger("Putting $headingName at the END of the allHeadings regexp, as it contains a *");
+ $allHeadingsRegexp .= ($allHeadingsRegexp eq '' ?q():"|").$headingName ;
+ }
+ }
+ }
+
+ # check for a * in the name
+ $allHeadingsRegexp =~ s/\*/\\\*/g;
+
+ # sort the file extensions by preference
+ my @sortedByLevels = sort { ${$headingsLevels{$a}}{level} <=> $headingsLevels{$b}{level} } keys(%headingsLevels);
+
+ # it could be that @sortedByLevels is empty;
+ return if !@sortedByLevels;
+
+ $self->logger("All headings regexp: $allHeadingsRegexp",'heading');
+ $self->logger("Now to construct headings regexp for each level:",'heading');
+
+ # loop through the levels, and create a regexp for each (min and max values are the first and last values respectively from sortedByLevels)
+ for(my $i = ${$headingsLevels{$sortedByLevels[0]}}{level}; $i <= ${$headingsLevels{$sortedByLevels[-1]}}{level}; $i++ ){
+ # level regexp
+ my @tmp = grep { ${$headingsLevels{$_}}{level} == $i } keys %headingsLevels;
+ if(@tmp){
+ my $headingsAtThisLevel = q();
+ foreach(@tmp){
+ # put starred headings at the front of the regexp
+ if($_ =~ m/\*/){
+ $self->logger("Putting $_ at the beginning of this regexp, as it contains a *");
+ $headingsAtThisLevel = $_.($headingsAtThisLevel eq '' ?q():"|$headingsAtThisLevel");
+ } else {
+ $self->logger("Putting $_ at the END of this regexp, as it contains a *");
+ $headingsAtThisLevel .= ($headingsAtThisLevel eq '' ?q():"|").$_ ;
+ }
+ }
+
+ # make the stars escaped correctly
+ $headingsAtThisLevel =~ s/\*/\\\*/g;
+ push(@headingsRegexpArray,$headingsAtThisLevel);
+ $self->logger("Heading level regexp for level $i will contain: $headingsAtThisLevel");
+ }
+ }
+ }
+
+sub find_heading{
+
+ # if there are no headings regexps, there's no point going any further
+ return if!@headingsRegexpArray;
+
+ my $self = shift;
+
+ # otherwise loop through the headings regexp
+ $self->logger("Searching for special begin/end (see specialBeginEnd)");
+
+ # loop through each headings match; note that we need to
+ # do it in *reverse* so as to ensure that the lower level headings get matched first of all
+ foreach(reverse(@headingsRegexpArray)){
+
+ # the regexp
+ my $headingRegExp = qr/
+ (
+ \\($_) # name stored into $2
+ ) # beginning bit into $1
+ (
+ .*?
+ ) # body into $3
+ (\R*)? # linebreaks at end of body into $4
+ ((?:\\(?:$allHeadingsRegexp))|$) # up to another heading, or else the end of the file
+ /sx;
+
+ while(${$self}{body} =~ m/$headingRegExp/){
+
+ # log file output
+ $self->logger("heading found: $2",'heading');
+
+ ${$self}{body} =~ s/
+ $headingRegExp
+ /
+ # create a new heading object
+ my $headingObject = LatexIndent::Heading->new(begin=>q(),
+ body=>$1.$3,
+ end=>q(),
+ afterbit=>($4?$4:q()).($5?$5:q()),
+ name=>$2.":heading",
+ parent=>$2,
+ nameForIndentationSettings=>$2,
+ linebreaksAtEnd=>{
+ begin=>0,
+ body=>0,
+ end=>0,
+ },
+ modifyLineBreaksYamlName=>"afterHeading",
+ endImmediatelyFollowedByComment=>0,
+ );
+
+ # the settings and storage of most objects has a lot in common
+ $self->get_settings_and_store_new_object($headingObject);
+ ${@{${$self}{children}}[-1]}{replacementText};
+ /xse;
+ }
+ }
+}
+
+sub get_replacement_text{
+ my $self = shift;
+
+ # the replacement text for a heading (chapter, section, etc) needs to put the trailing part back in
+ $self->logger("Custom replacement text routine for heading ${$self}{name}");
+ ${$self}{replacementText} = ${$self}{id}.${$self}{afterbit};
+ delete ${$self}{afterbit};
+}
+
+sub create_unique_id{
+ my $self = shift;
+
+ $headingCounter++;
+
+ ${$self}{id} = "$tokens{heading}$headingCounter";
+ return;
+}
+
+sub adjust_replacement_text_line_breaks_at_end{
+ return;
+}
+
+sub get_object_attribute_for_indentation_settings{
+ # when looking for noAdditionalIndent or indentRules, we may need to determine
+ # which thing we're looking for, e.g
+ #
+ # chapter:
+ # body: 0
+ # optionalArguments: 1
+ # mandatoryArguments: 1
+ # afterHeading: 0
+ #
+ # this method returns 'body' by default, but the other objects (optionalArgument, mandatoryArgument, afterHeading)
+ # return their appropriate identifier.
+ my $self = shift;
+
+ return ${$self}{modifyLineBreaksYamlName};
+}
+
+sub tasks_particular_to_each_object{
+ my $self = shift;
+
+ # search for commands, keys, named grouping braces
+ $self->find_commands_or_key_equals_values_braces;
+
+ # we need to transfer the details from the modifyLineBreaks of the command
+ # child object to the heading object.
+ #
+ # for example, if we have
+ #
+ # \chapter{some heading here}
+ #
+ # and we want to modify the linebreak before the \chapter command using, for example,
+ #
+ # commands:
+ # CommandStartsOnOwnLine: 1
+ #
+ # then we need to transfer this information to the heading object
+ if($is_m_switch_active){
+ $self->logger("Searching for linebreak preferences immediately infront of ${$self}{parent}",'heading');
+ foreach(@{${$self}{children}}){
+ if(${$_}{name} eq ${$self}{parent}){
+ $self->logger("Named child found: ${$_}{name}");
+ if(defined ${$_}{BeginStartsOnOwnLine}){
+ $self->logger("Transferring information from ${$_}{id} (${$_}{name}) to ${$self}{id} (${$self}{name}) for BeginStartsOnOwnLine");
+ ${$self}{BeginStartsOnOwnLine} = ${$_}{BeginStartsOnOwnLine};
+ } else {
+ $self->logger("No information found in ${$_}{name} for BeginStartsOnOwnLine");
+ }
+ last;
+ }
+ }
+ }
+
+ # search for special begin/end
+ $self->find_special;
+
+ return;
+}
+
+sub add_surrounding_indentation_to_begin_statement{
+ # almost all of the objects add surrounding indentation to the 'begin' statements,
+ # but some (e.g HEADING) have their own method
+ my $self = shift;
+
+ $self->logger("Adding surrounding indentation after (empty, by design!) begin statement of ${$self}{name} (${$self}{id})");
+ ${$self}{begin} .= ${$self}{surroundingIndentation}; # add indentation
+
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/HiddenChildren.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/HiddenChildren.pm
new file mode 100644
index 00000000000..594e7dbd14e
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/HiddenChildren.pm
@@ -0,0 +1,190 @@
+package LatexIndent::HiddenChildren;
+# 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::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use LatexIndent::Tokens qw/%tokens/;
+use Data::Dumper;
+use Exporter qw/import/;
+our @EXPORT_OK = qw/find_surrounding_indentation_for_children update_family_tree get_family_tree check_for_hidden_children %familyTree/;
+
+# hiddenChildren can be stored in a global array, it doesn't matter what level they're at
+our %familyTree;
+our %allChildren;
+
+
+#----------------------------------------------------------
+# Discussion surrounding hidden children
+#
+# Consider the following latex code
+#
+# \begin{one}
+# body of one
+# body of one
+# body of one
+# \begin{two}
+# body of two
+# body of two
+# body of two
+# body of two
+# \end{two}
+# \end{one}
+#
+# From the visual perspective, we might say that <one> and <two> are *nested* children;
+# from the persepective of latexindent.pl, however, they actually have *the same level*.
+#
+# Graphically, you might represent it as follows
+#
+# *
+# / \
+# / \
+# / \
+# O O
+#
+# where * represents the 'root' document object, and each 'O' is an environment object; the
+# first one, on the left, represents <two> and the second one, on the right, represents <one>.
+# (Remember that the environment regexp does not allow \begin within its body.)
+#
+# When processing the document, <one> will be processed *before* <two>. Furthermore, because
+# <one> and <two> are at the same level, they are not *natural* ancestors of each other; as such,
+# we say that <two> is a *hidden* child, and that its 'adopted' ancestor is <one>.
+#
+# We need to go to a lot of effort to make sure that <two> knows about its ancestors and its
+# surrounding indentation (<one> in this case). The subroutines in this file do that effort.
+#----------------------------------------------------------
+
+sub find_surrounding_indentation_for_children{
+ my $self = shift;
+
+ # output to logfile
+ $self->logger("FamilyTree before update:",'heading') if $is_t_switch_active;
+ $self->logger(Dumper(\%familyTree)) if($is_t_switch_active);
+
+ # update the family tree with ancestors
+ $self->update_family_tree;
+
+ # output information to the logfile
+ $self->logger("FamilyTree after update:",'heading') if $is_t_switch_active;
+ $self->logger(Dumper(\%familyTree)) if($is_t_switch_active);
+
+ while( my ($idToSearch,$ancestorToSearch) = each %familyTree){
+ $self->logger("Hidden child ID: ,$idToSearch, here are its ancestors:",'heading') if $is_t_switch_active;
+ foreach(@{${$ancestorToSearch}{ancestors}}){
+ $self->logger("ID: ${$_}{ancestorID}") if($is_t_switch_active);
+ my $tmpIndentation = ref(${$_}{ancestorIndentation}) eq 'SCALAR'?${${$_}{ancestorIndentation}}:${$_}{ancestorIndentation};
+ $tmpIndentation = $tmpIndentation ? $tmpIndentation : q();
+ $self->logger("indentation: '$tmpIndentation'") if($is_t_switch_active);
+ }
+ }
+
+ return;
+}
+
+sub update_family_tree{
+ my $self = shift;
+
+ # loop through the hash
+ $self->logger("Updating FamilyTree...",'heading') if $is_t_switch_active;
+ while( my ($idToSearch,$ancestorToSearch)= each %familyTree){
+ foreach(@{${$ancestorToSearch}{ancestors}}){
+ my $ancestorID = ${$_}{ancestorID};
+ $self->logger("current ID: $idToSearch, ancestor: $ancestorID") if($is_t_switch_active);
+ if($familyTree{$ancestorID}){
+ $self->logger("$ancestorID is a key within familyTree, grabbing its ancestors") if($is_t_switch_active);
+ my $naturalAncestors = q();
+ foreach(@{${$familyTree{$idToSearch}}{ancestors}}){
+ $naturalAncestors .= "---".${$_}{ancestorID} if(${$_}{type} eq "natural");
+ }
+ foreach(@{${$familyTree{$ancestorID}}{ancestors}}){
+ $self->logger("ancestor of *hidden* child: ${$_}{ancestorID}") if($is_t_switch_active);
+ my $newAncestorId = ${$_}{ancestorID};
+ my $type;
+ if($naturalAncestors =~ m/$ancestorID/){
+ $type = "natural";
+ } else {
+ $type = "adopted";
+ }
+ my $matched = grep { $_->{ancestorID} eq $newAncestorId } @{${$familyTree{$idToSearch}}{ancestors}};
+ push(@{${$familyTree{$idToSearch}}{ancestors}},{ancestorID=>${$_}{ancestorID},ancestorIndentation=>${$_}{ancestorIndentation},type=>$type}) unless($matched);
+ }
+ } else {
+ my $naturalAncestors = q();
+ foreach(@{${$familyTree{$idToSearch}}{ancestors}}){
+ $naturalAncestors .= "---".${$_}{ancestorID} if(${$_}{type} eq "natural");
+ }
+ $self->logger("natural ancestors of $ancestorID: $naturalAncestors") if($is_t_switch_active);
+ foreach(@{${$allChildren{$ancestorID}}{ancestors}}){
+ my $newAncestorId = ${$_}{ancestorID};
+ my $type;
+ if($naturalAncestors =~ m/$newAncestorId/){
+ $type = "natural";
+ } else {
+ $type = "adopted";
+ }
+ my $matched = grep { $_->{ancestorID} eq $newAncestorId } @{${$familyTree{$idToSearch}}{ancestors}};
+ unless($matched){
+ $self->logger("ancestor of UNHIDDEN child: ${$_}{ancestorID}") if($is_t_switch_active);
+ push(@{${$familyTree{$idToSearch}}{ancestors}},{ancestorID=>${$_}{ancestorID},ancestorIndentation=>${$_}{ancestorIndentation},type=>$type});
+ }
+ }
+ }
+ }
+ }
+
+}
+
+sub check_for_hidden_children{
+
+ my $self = shift;
+
+ # if there are no hidden children, then exit
+ return if ${$self}{body} !~ m/$tokens{beginOfToken}/;
+
+ # grab the matches
+ my @matched = (${$self}{body} =~ /((?:$tokens{ifelsefiSpecial})?$tokens{beginOfToken}.[-a-z0-9]+?$tokens{endOfToken})/ig);
+
+ # log file
+ $self->logger("Hidden children check") if $is_t_switch_active;
+ $self->logger(join("|",@matched)) if $is_t_switch_active;
+
+ my $naturalAncestors = ${$self}{naturalAncestors};
+
+ # loop through the hidden children
+ foreach my $match (@matched){
+ # update the family tree with ancestors of self
+ if(${$self}{ancestors}){
+ foreach(@{${$self}{ancestors}}){
+ my $newAncestorId = ${$_}{ancestorID};
+ unless (grep { $_->{ancestorID} eq $newAncestorId } @{${$familyTree{$match}}{ancestors}}){
+ my $type = ($naturalAncestors =~ m/${$_}{ancestorID}/ ) ? "natural" : "adopted";
+ $self->logger("Adding ${$_}{ancestorID} to the $type family tree of $match") if($is_t_switch_active);
+ push(@{$familyTree{$match}{ancestors}},{ancestorID=>${$_}{ancestorID},ancestorIndentation=>${$_}{ancestorIndentation},type=>$type});
+ }
+ }
+ }
+
+ # update the family tree with self
+ unless (grep { $_->{ancestorID} eq ${$self}{id}} @{${$familyTree{$match}}{ancestors}}){
+ my $type = ($naturalAncestors =~ m/${$self}{id}/ ) ? "natural" : "adopted";
+ $self->logger("Adding ${$self}{id} to the $type family tree of hiddenChild $match") if($is_t_switch_active);
+ push(@{$familyTree{$match}{ancestors}},{ancestorID=>${$self}{id},ancestorIndentation=>${$self}{indentation},type=>$type});
+ }
+ }
+
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/HorizontalWhiteSpace.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/HorizontalWhiteSpace.pm
new file mode 100644
index 00000000000..360a8077022
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/HorizontalWhiteSpace.pm
@@ -0,0 +1,62 @@
+package LatexIndent::HorizontalWhiteSpace;
+# 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::GetYamlSettings qw/%masterSettings/;
+use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use Exporter qw/import/;
+our @EXPORT_OK = qw/remove_trailing_whitespace remove_leading_space/;
+
+sub remove_trailing_whitespace{
+ my $self = shift;
+ my %input = @_;
+
+ # this method can be called before the indendation, and after, depending upon the input
+ if($input{when} eq "before"){
+ return unless(${$masterSettings{removeTrailingWhitespace}}{beforeProcessing});
+ $self->logger("Removing trailing white space *before* the document is processed (see removeTrailingWhitespace: beforeProcessing)",'heading') if $is_t_switch_active;
+ } elsif($input{when} eq "after"){
+ return unless(${$masterSettings{removeTrailingWhitespace}}{afterProcessing});
+ $self->logger("Removing trailing white space *after* the document is processed (see removeTrailingWhitespace: afterProcessing)",'heading') if $is_t_switch_active;
+ } else {
+ return;
+ }
+
+ ${$self}{body} =~ s/
+ \h+ # followed by possible horizontal space
+ $ # up to the end of a line
+ //xsmg;
+
+ $self->logger("Processed body, *$input{when}* indentation (${$self}{name}):") if($is_t_switch_active);
+ $self->logger(${$self}{body}) if($is_t_switch_active);
+
+}
+
+sub remove_leading_space{
+ my $self = shift;
+ $self->logger("Removing leading space from ${$self}{name} (verbatim/noindentblock already accounted for)",'heading') if $is_t_switch_active;
+ ${$self}{body} =~ s/
+ (
+ ^ # beginning of the line
+ \h* # with 0 or more horizontal spaces
+ )? # possibly
+ //mxg;
+ return;
+}
+
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/IfElseFi.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/IfElseFi.pm
new file mode 100755
index 00000000000..e1ac17b3613
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/IfElseFi.pm
@@ -0,0 +1,276 @@
+package LatexIndent::IfElseFi;
+# 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::TrailingComments qw/$trailingCommentRegExp/;
+use LatexIndent::Switches qw/$is_m_switch_active $is_t_switch_active $is_tt_switch_active/;
+use Data::Dumper;
+use Exporter qw/import/;
+our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321
+our @EXPORT_OK = qw/find_ifelsefi/;
+our %previouslyFoundSettings;
+our $ifElseFiCounter;
+
+# store the regular expresssion for matching and replacing the \if...\else...\fi statements
+# note: we search for \else separately in an attempt to keep this regexp a little more managable
+our $ifElseFiRegExp = qr/
+ (
+ \\
+ (@?if[a-zA-Z@]*?)
+ \h*
+ (\R*)
+ ) # begin statement, e.g \ifnum, \ifodd
+ (
+ \\(?!if)|\R|\h|\#|!-! # up until a \\, linebreak # or !-!, which is
+ ) # part of the tokens used for latexindent
+ (
+ (?:
+ (?!\\if).
+ )*? # body, which can't include another \if
+ )
+ (\R*) # linebreaks after body
+ (
+ \\fi(?![a-zA-Z]) # \fi statement
+ )
+ (\h*) # 0 or more horizontal spaces
+ (\R)? # linebreaks after \fi
+/sx;
+
+
+sub indent{
+ my $self = shift;
+
+ # determine the surrounding and current indentation
+ $self->determine_total_indentation;
+
+ # line break checks after \if statement, can get messy if we
+ # have, for example
+ # \ifnum
+ # something
+ # which might be changed into
+ # \ifnumsomething
+ # which is undeserible
+ if (defined ${$self}{BodyStartsOnOwnLine}
+ and ${$self}{BodyStartsOnOwnLine}==-1
+ and ${$self}{body} !~ m/^(\h|\\|(?:!-!))/s
+ ){
+ ${$self}{begin} .= " ";
+ }
+
+ # indent the body
+ $self->indent_body;
+
+ # calculate and grab the surrounding indentation for the \else statement adjustment
+ $self->get_surrounding_indentation;
+ my $surroundingIndentation = ${$self}{surroundingIndentation}?${$self}{surroundingIndentation}:q();
+
+ if(${$self}{elsePresent} and ${$self}{linebreaksAtEnd}{ifbody}){
+ $self->logger("Adding surrounding indentation to \\else statement ('$surroundingIndentation')")if $is_t_switch_active;
+ ${$self}{body} =~ s/\h*\\else/$surroundingIndentation\\else/;
+ $self->logger("Body (${$self}{name}) after \\else adjustment:\n${$self}{body}") if $is_t_switch_active;
+ }
+
+ # indent the end statement
+ $self->indent_end_statement;
+
+ # wrap-up statement
+ $self->wrap_up_statement;
+
+ # line break checks *after* \end{statement}
+ if (defined ${$self}{EndFinishesWithLineBreak}
+ and ${$self}{EndFinishesWithLineBreak}==-1
+ ) {
+ # add a single horizontal space after the child id, otherwise we can end up
+ # with things like
+ # before:
+ # \fi
+ # text
+ # after:
+ # \fitext
+ $self->logger("Adding a single space after \\fi statement (otherwise \\fi can be comined with next line of text in an unwanted way)",'heading') if $is_t_switch_active;
+ ${$self}{end} =${$self}{end}." ";
+ }
+
+ return $self;
+}
+
+sub find_ifelsefi{
+ my $self = shift;
+
+ while( ${$self}{body} =~ m/$ifElseFiRegExp\h*($trailingCommentRegExp)?/){
+
+ ${$self}{body} =~ s/
+ $ifElseFiRegExp(\h*)($trailingCommentRegExp)?
+ /
+ # log file output
+ $self->logger("IfElseFi found: $2",'heading')if $is_t_switch_active;
+
+ # create a new IfElseFi object
+ my $ifElseFi = LatexIndent::IfElseFi->new(begin=>$1.(($4 eq "\n" and !$3)?"\n":q()),
+ name=>$2,
+ # if $4 is a line break, don't count it twice (it will already be in 'begin')
+ body=>($4 eq "\n") ? $5.$6 : $4.$5.$6,
+ end=>$7,
+ linebreaksAtEnd=>{
+ begin=>(($4 eq "\n")||$3)?1:0,
+ body=>$6?1:0,
+ end=>$9?1:0,
+ },
+ aliases=>{
+ # begin statements
+ BeginStartsOnOwnLine=>"IfStartsOnOwnLine",
+ # end statements
+ EndStartsOnOwnLine=>"FiStartsOnOwnLine",
+ # after end statements
+ EndFinishesWithLineBreak=>"FiFinishesWithLineBreak",
+ },
+ elsePresent=>0,
+ modifyLineBreaksYamlName=>"ifElseFi",
+ additionalAssignments=>["ElseStartsOnOwnLine","ElseFinishesWithLineBreak"],
+ endImmediatelyFollowedByComment=>$9?0:($11?1:0),
+ horizontalTrailingSpace=>$8?$8:q(),
+ );
+ # the settings and storage of most objects has a lot in common
+ $self->get_settings_and_store_new_object($ifElseFi);
+ ${@{${$self}{children}}[-1]}{replacementText}.($10?$10:q()).($11?$11:q());
+ /xse;
+
+ }
+ return;
+}
+
+sub tasks_particular_to_each_object{
+ my $self = shift;
+
+ # check for existence of \else statement, and associated line break information
+ $self->check_for_else_statement;
+
+ # search for headings (important to do this before looking for commands!)
+ $self->find_heading;
+
+ # search for commands, keys, named grouping braces
+ $self->find_commands_or_key_equals_values_braces;
+
+ # search for arguments
+ $self->find_opt_mand_arguments;
+
+ # search for ifElseFi blocks
+ $self->find_ifelsefi;
+
+ # search for special begin/end
+ $self->find_special;
+
+}
+
+sub create_unique_id{
+ my $self = shift;
+
+ $ifElseFiCounter++;
+
+ ${$self}{id} = "$tokens{ifelsefi}$ifElseFiCounter";
+ return;
+}
+
+sub check_for_else_statement{
+ my $self = shift;
+ $self->logger("Looking for \\else statement (${$self}{name})",'heading') if $is_t_switch_active;
+ if(${$self}{body} =~ m/
+ (\R*) # possible line breaks before \else statement
+ \\else
+ \h* # possible horizontal space
+ (\R*) # possible line breaks after \else statement
+ /x){
+ $self->logger("found \\else statement, storing line break information:") if($is_t_switch_active);
+
+ # linebreaks *before* \else statement
+ ${$self}{linebreaksAtEnd}{ifbody} = $1?1:0;
+ $self->logger("linebreaksAtEnd of ifbody: ${$self}{linebreaksAtEnd}{ifbody}") if($is_t_switch_active);
+
+ # linebreaks *after* \else statement
+ ${$self}{linebreaksAtEnd}{else} = $2?1:0;
+ $self->logger("linebreaksAtEnd of else: ${$self}{linebreaksAtEnd}{else}") if($is_t_switch_active);
+ ${$self}{elsePresent}=1;
+
+ # check that \else isn't the first thing in body
+ if(${$self}{body} =~ m/^\\else/s and ${$self}{linebreaksAtEnd}{begin}){
+ ${$self}{linebreaksAtEnd}{ifbody} = 1;
+ $self->logger("\\else *begins* the ifbody, linebreaksAtEnd of ifbody: ${$self}{linebreaksAtEnd}{ifbody}") if($is_t_switch_active);
+ }
+
+ # check if -m switch is active
+ return unless $is_m_switch_active;
+
+ # possibly modify line break *before* \else statement
+ if(defined ${$self}{ElseStartsOnOwnLine}){
+ if(${$self}{ElseStartsOnOwnLine}>=1 and !${$self}{linebreaksAtEnd}{ifbody}){
+ # by default, assume that no trailing comment token is needed
+ my $trailingCommentToken = q();
+ if(${$self}{ElseStartsOnOwnLine}==2){
+ $self->logger("Adding a % immediately before else statement of ${$self}{name} (ElseStartsOnOwnLine==2)") if $is_t_switch_active;
+ $trailingCommentToken = "%".$self->add_comment_symbol;
+ }
+
+ # add a line break after ifbody, if appropriate
+ $self->logger("Adding a linebreak before the \\else statement (see ElseStartsOnOwnLine)");
+ ${$self}{body} =~ s/\\else/$trailingCommentToken\n\\else/s;
+ ${$self}{linebreaksAtEnd}{ifbody} = 1;
+ } elsif (${$self}{ElseStartsOnOwnLine}==-1 and ${$self}{linebreaksAtEnd}{ifbody}){
+ # remove line break *after* ifbody, if appropriate
+ $self->logger("Removing linebreak before \\else statement (see ElseStartsOnOwnLine)");
+ ${$self}{body} =~ s/\R*(\h*)\\else/$1\\else/sx;
+ ${$self}{linebreaksAtEnd}{ifbody} = 0;
+ }
+ }
+
+ # possibly modify line break *before* \else statement
+ if(defined ${$self}{ElseFinishesWithLineBreak}){
+ if(${$self}{ElseFinishesWithLineBreak}>=1 and !${$self}{linebreaksAtEnd}{else}){
+ # by default, assume that no trailing comment token is needed
+ my $trailingCommentToken = q();
+ if(${$self}{ElseFinishesWithLineBreak}==2){
+ return if(${$self}{body} =~ m/\\else\h*$trailingCommentRegExp/s);
+ $self->logger("Adding a % immediately after else statement of ${$self}{name} (ElseFinishesWithLineBreak==2)") if $is_t_switch_active;
+ $trailingCommentToken = "%".$self->add_comment_symbol;
+ }
+
+ # add a line break after else, if appropriate
+ $self->logger("Adding a linebreak after the \\else statement (see ElseFinishesWithLineBreak)")if $is_t_switch_active;
+ ${$self}{body} =~ s/\\else\h*/\\else$trailingCommentToken\n/s;
+ ${$self}{linebreaksAtEnd}{else} = 1;
+ } elsif (${$self}{ElseFinishesWithLineBreak}==-1 and ${$self}{linebreaksAtEnd}{else}){
+ # remove line break *after* else, if appropriate,
+ # note the space so that, for example,
+ # \else
+ # some text
+ # becomes
+ # \else some text
+ # and not
+ # \elsesome text
+ $self->logger("Removing linebreak after \\else statement (see ElseFinishesWithLineBreak)")if $is_t_switch_active;
+ ${$self}{body} =~ s/\\else\h*\R*/\\else /sx;
+ ${$self}{linebreaksAtEnd}{else} = 0;
+ }
+ }
+
+ return;
+ } else {
+ $self->logger("\\else statement not found") if($is_t_switch_active);
+ }
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/Indent.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/Indent.pm
new file mode 100644
index 00000000000..600aed5040f
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/Indent.pm
@@ -0,0 +1,371 @@
+package LatexIndent::Indent;
+# 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::Switches qw/$is_m_switch_active $is_t_switch_active $is_tt_switch_active/;
+use LatexIndent::HiddenChildren qw/%familyTree/;
+use Data::Dumper;
+use Exporter qw/import/;
+our @EXPORT_OK = qw/indent wrap_up_statement determine_total_indentation indent_begin indent_body indent_end_statement final_indentation_check push_family_tree_to_indent get_surrounding_indentation indent_children_recursively check_for_blank_lines_at_beginning put_blank_lines_back_in_at_beginning add_surrounding_indentation_to_begin_statement/;
+our %familyTree;
+
+sub indent{
+ my $self = shift;
+
+ # determine the surrounding and current indentation
+ $self->determine_total_indentation;
+
+ # indent the begin statement
+ $self->indent_begin;
+
+ # indent the body
+ $self->indent_body;
+
+ # indent the end statement
+ $self->indent_end_statement;
+
+ # output the completed object to the log file
+ $self->logger("Complete indented object (${$self}{name}) after indentation:\n${$self}{begin}${$self}{body}${$self}{end}") if $is_t_switch_active;
+
+ # wrap-up statement
+ $self->wrap_up_statement;
+ return $self;
+}
+
+sub wrap_up_statement{
+ my $self = shift;
+ $self->logger("Finished indenting ${$self}{name}",'heading') if $is_t_switch_active;
+ return $self;
+ }
+
+sub determine_total_indentation{
+ my $self = shift;
+
+ # calculate and grab the surrounding indentation
+ $self->get_surrounding_indentation;
+
+ # logfile information
+ my $surroundingIndentation = ${$self}{surroundingIndentation};
+ $self->logger("indenting object ${$self}{name}") if($is_t_switch_active);
+ (my $during = $surroundingIndentation) =~ s/\t/TAB/g;
+ $self->logger("indentation *surrounding* object: '$during'") if($is_t_switch_active);
+ ($during = ${$self}{indentation}) =~ s/\t/TAB/g;
+ $self->logger("indentation *of* object: '$during'") if($is_t_switch_active);
+ ($during = $surroundingIndentation.${$self}{indentation}) =~ s/\t/TAB/g;
+ $self->logger("*total* indentation to be added: '$during'") if($is_t_switch_active);
+
+ # form the total indentation of the object
+ ${$self}{indentation} = $surroundingIndentation.${$self}{indentation};
+
+}
+
+sub get_surrounding_indentation{
+ my $self = shift;
+
+ my $surroundingIndentation = q();
+
+ if($familyTree{${$self}{id}}){
+ $self->logger("Adopted ancestors found!") if($is_t_switch_active);
+ foreach(@{${$familyTree{${$self}{id}}}{ancestors}}){
+ if(${$_}{type} eq "adopted"){
+ my $newAncestorId = ${$_}{ancestorID};
+ $self->logger("ancestor ID: $newAncestorId, adding indentation of $newAncestorId to surroundingIndentation of ${$self}{id}") if($is_t_switch_active);
+ $surroundingIndentation .= ref(${$_}{ancestorIndentation}) eq 'SCALAR'
+ ?
+ (${${$_}{ancestorIndentation}}?${${$_}{ancestorIndentation}}:q())
+ :
+ (${$_}{ancestorIndentation}?${$_}{ancestorIndentation}:q());
+ }
+ }
+ }
+ ${$self}{surroundingIndentation} = $surroundingIndentation;
+
+}
+
+sub indent_begin{
+ # for most objects, the begin statement is just one line, but there are exceptions, e.g KeyEqualsValuesBraces
+ return;
+}
+
+sub indent_body{
+ my $self = shift;
+
+ # grab the indentation of the object
+ my $indentation = ${$self}{indentation};
+
+ # output to the logfile
+ $self->logger("Body (${$self}{name}) before indentation:\n${$self}{body}") if $is_t_switch_active;
+
+ # last minute check for modified bodyLineBreaks
+ $self->count_body_line_breaks if $is_m_switch_active;
+
+ # some objects need to check for blank line tokens at the beginning
+ $self->check_for_blank_lines_at_beginning if $is_m_switch_active;
+
+ # some objects can format their body to align at the & character
+ $self->align_at_ampersand if ${$self}{lookForAlignDelims};
+
+ # body indendation
+ if(${$self}{linebreaksAtEnd}{begin}==1){
+ if(${$self}{body} =~ m/^\h*$/s){
+ $self->logger("Body of ${$self}{name} is empty, not applying indentation") if $is_t_switch_active;
+ } else {
+ # put any existing horizontal space after the current indentation
+ $self->logger("Entire body of ${$self}{name} receives indendentation") if $is_t_switch_active;
+ ${$self}{body} =~ s/^(\h*)/$indentation$1/mg; # add indentation
+ }
+ } elsif(${$self}{linebreaksAtEnd}{begin}==0 and ${$self}{bodyLineBreaks}>0) {
+ if(${$self}{body} =~ m/
+ (.*?) # content of first line
+ \R # first line break
+ (.*$) # rest of body
+ /sx){
+ my $bodyFirstLine = $1;
+ my $remainingBody = $2;
+ $self->logger("first line of body: $bodyFirstLine",'heading') if $is_t_switch_active;
+ $self->logger("remaining body (before indentation):\n'$remainingBody'") if($is_t_switch_active);
+
+ # add the indentation to all the body except first line
+ $remainingBody =~ s/^/$indentation/mg unless($remainingBody eq ''); # add indentation
+ $self->logger("remaining body (after indentation):\n$remainingBody'") if($is_t_switch_active);
+
+ # put the body back together
+ ${$self}{body} = $bodyFirstLine."\n".$remainingBody;
+ }
+ }
+
+ # if the routine check_for_blank_lines_at_beginning has been called, then the following routine
+ # puts blank line tokens back in
+ $self->put_blank_lines_back_in_at_beginning if $is_m_switch_active;
+
+ # the final linebreak can be modified by a child object; see test-cases/commands/figureValign-mod5.tex, for example
+ if($is_m_switch_active and defined ${$self}{linebreaksAtEnd}{body} and ${$self}{linebreaksAtEnd}{body}==1 and ${$self}{body} !~ m/\R$/){
+ $self->logger("Updating body for ${$self}{name} to contain a linebreak at the end (linebreaksAtEnd is 1, but there isn't currently a linebreak)") if($is_t_switch_active);
+ ${$self}{body} .= "\n";
+ }
+
+ # output to the logfile
+ $self->logger("Body (${$self}{name}) after indentation:\n${$self}{body}") if $is_t_switch_active;
+ return $self;
+}
+
+sub check_for_blank_lines_at_beginning{
+ # some objects need this routine
+ return;
+}
+
+sub put_blank_lines_back_in_at_beginning{
+ # some objects need this routine
+ return;
+}
+
+sub indent_end_statement{
+ my $self = shift;
+ my $surroundingIndentation = (${$self}{surroundingIndentation} and $familyTree{${$self}{id}})
+ ?
+ (ref(${$self}{surroundingIndentation}) eq 'SCALAR'?${${$self}{surroundingIndentation}}:${$self}{surroundingIndentation})
+ :q();
+
+ # end{statement} indentation, e.g \end{environment}, \fi, }, etc
+ if(${$self}{linebreaksAtEnd}{body}){
+ ${$self}{end} =~ s/^\h*/$surroundingIndentation/mg; # add indentation
+ $self->logger("Adding surrounding indentation to ${$self}{end} (${$self}{name}: '$surroundingIndentation')") if($is_t_switch_active);
+ }
+ return $self;
+}
+
+sub final_indentation_check{
+ # problem:
+ # if a tab is appended to spaces, it will look different
+ # from spaces appended to tabs (see test-cases/items/spaces-and-tabs.tex)
+ # solution:
+ # move all of the tabs to the beginning of ${$self}{indentation}
+ # notes;
+ # this came to light when studying test-cases/items/items1.tex
+
+ my $self = shift;
+
+ my $indentation;
+ my $numberOfTABS;
+ my $after;
+ ${$self}{body} =~ s/
+ ^((\h*|\t*)((\h+)(\t+))+)
+ /
+ # fix the indentation
+ $indentation = $1;
+
+ # count the number of tabs
+ $numberOfTABS = () = $indentation=~ \/\t\/g;
+ $self->logger("Number of tabs: $numberOfTABS") if($is_t_switch_active);
+
+ # log the after
+ ($after = $indentation) =~ s|\t||g;
+ $after = "TAB"x$numberOfTABS.$after;
+ $self->logger("Indentation after: '$after'") if($is_t_switch_active);
+ ($indentation = $after) =~s|TAB|\t|g;
+ $indentation;
+ /xsmeg;
+
+}
+
+sub indent_children_recursively{
+ my $self = shift;
+
+ unless(defined ${$self}{children}) {
+ $self->logger("No child objects (${$self}{name})") if $is_t_switch_active;
+ return;
+ }
+
+ $self->logger('Pre-processed body:','heading') if $is_t_switch_active;
+ $self->logger(${$self}{body}) if($is_t_switch_active);
+
+ # send the children through this indentation routine recursively
+ if(defined ${$self}{children}){
+ foreach my $child (@{${$self}{children}}){
+ $self->logger("Indenting child objects on ${$child}{name}") if $is_t_switch_active;
+ $child->indent_children_recursively;
+ }
+ }
+
+ $self->logger("Replacing ids with begin, body, and end statements:",'heading') if $is_t_switch_active;
+
+ # loop through document children hash
+ while( scalar (@{${$self}{children}}) > 0 ){
+ my $index = 0;
+ # we work through the array *in order*
+ foreach my $child (@{${$self}{children}}){
+ $self->logger("Searching ${$self}{name} for ${$child}{id}...",'heading') if $is_t_switch_active;
+ if(${$self}{body} =~ m/${$child}{id}/s){
+ # we only care if id is first non-white space character
+ # and if followed by line break
+ # if m switch is active
+ my $IDFirstNonWhiteSpaceCharacter = 0;
+ my $IDFollowedImmediatelyByLineBreak = 0;
+
+ # update the above two, if necessary
+ if ($is_m_switch_active){
+ $IDFirstNonWhiteSpaceCharacter = (${$self}{body} =~ m/^${$child}{id}/m
+ or
+ ${$self}{body} =~ m/^\h\h*${$child}{id}/m
+ ) ?1:0;
+ $IDFollowedImmediatelyByLineBreak = (${$self}{body} =~ m/${$child}{id}\h*\R*/m) ?1:0;
+ }
+
+ # log file info
+ $self->logger("${$child}{id} found!") if($is_t_switch_active);
+ $self->logger("Indenting ${$child}{name} (id: ${$child}{id})",'heading') if $is_t_switch_active;
+ $self->logger("looking up indentation scheme for ${$child}{name}") if($is_t_switch_active);
+
+ # line break checks *after* <end statement>
+ if (defined ${$child}{EndFinishesWithLineBreak}
+ and ${$child}{EndFinishesWithLineBreak}==-1
+ and $IDFollowedImmediatelyByLineBreak) {
+ # remove line break *after* <end statement>, if appropriate
+ my $EndStringLogFile = ${$child}{aliases}{EndFinishesWithLineBreak}||"EndFinishesWithLineBreak";
+ $self->logger("Removing linebreak after ${$child}{end} (see $EndStringLogFile)") if $is_t_switch_active;
+ ${$self}{body} =~ s/${$child}{id}(\h*)?(\R|\h)*/${$child}{id}$1/s;
+ ${$child}{linebreaksAtEnd}{end} = 0;
+ }
+
+ # perform indentation
+ $child->indent;
+
+ # surrounding indentation is now up to date
+ my $surroundingIndentation = (${$child}{surroundingIndentation} and ${$child}{hiddenChildYesNo})
+ ?
+ (ref(${$child}{surroundingIndentation}) eq 'SCALAR'?${${$child}{surroundingIndentation}}:${$child}{surroundingIndentation})
+ :q();
+
+ # line break checks before <begin statement>
+ if(defined ${$child}{BeginStartsOnOwnLine}){
+ my $BeginStringLogFile = ${$child}{aliases}{BeginStartsOnOwnLine}||"BeginStartsOnOwnLine";
+ if(${$child}{BeginStartsOnOwnLine}>=1 and !$IDFirstNonWhiteSpaceCharacter){
+ # by default, assume that no trailing comment token is needed
+ my $trailingCommentToken = q();
+ if(${$child}{BeginStartsOnOwnLine}==2){
+ $self->logger("Removing space immediately before ${$child}{id}, in preparation for adding % ($BeginStringLogFile == 2)") if $is_t_switch_active;
+ ${$self}{body} =~ s/\h*${$child}{id}/${$child}{id}/s;
+ $self->logger("Adding a % at the end of the line that ${$child}{begin} is on, then a linebreak ($BeginStringLogFile == 2)") if $is_t_switch_active;
+ $trailingCommentToken = "%".$self->add_comment_symbol;
+ } else {
+ $self->logger("Adding a linebreak at the beginning of ${$child}{begin} (see $BeginStringLogFile)") if $is_t_switch_active;
+ }
+
+ # the trailing comment/linebreak magic
+ ${$child}{begin} = "$trailingCommentToken\n".${$child}{begin};
+ $child->add_surrounding_indentation_to_begin_statement;
+
+ # remove surrounding indentation ahead of %
+ ${$child}{begin} =~ s/^(\h*)%/%/ if(${$child}{BeginStartsOnOwnLine}==2);
+ } elsif (${$child}{BeginStartsOnOwnLine}==-1 and $IDFirstNonWhiteSpaceCharacter){
+ # important to check we don't move the begin statement next to a blank-line-token
+ my $blankLineToken = $tokens{blanklines};
+ if(${$self}{body} !~ m/$blankLineToken\R*\h*${$child}{id}/s){
+ $self->logger("Removing linebreak before ${$child}{begin} (see $BeginStringLogFile in ${$child}{modifyLineBreaksYamlName} YAML)") if $is_t_switch_active;
+ ${$self}{body} =~ s/(\h*)(?:\R*|\h*)+${$child}{id}/$1${$child}{id}/s;
+ } else {
+ $self->logger("Not removing linebreak ahead of ${$child}{begin}, as blank-line-token present (see preserveBlankLines)") if $is_t_switch_active;
+ }
+ }
+ }
+
+ $self->logger(Dumper(\%{$child}),'ttrace') if($is_tt_switch_active);
+
+ # replace ids with body
+ ${$self}{body} =~ s/${$child}{id}/${$child}{begin}${$child}{body}${$child}{end}/;
+
+ # log file info
+ $self->logger("Body (${$self}{name}) now looks like:",'heading') if $is_t_switch_active;
+ $self->logger(${$self}{body}) if($is_t_switch_active);
+
+ # remove element from array: http://stackoverflow.com/questions/174292/what-is-the-best-way-to-delete-a-value-from-an-array-in-perl
+ splice(@{${$self}{children}}, $index, 1);
+
+ # output to the log file
+ $self->logger("deleted child key ${$child}{name} (parent is: ${$self}{name})") if $is_t_switch_active;
+
+ # restart the loop, as the size of the array has changed
+ last;
+ } else {
+ $self->logger("${$child}{id} not found") if($is_t_switch_active);
+ }
+
+ # increment the loop counter
+ $index++;
+ }
+ }
+
+ # logfile info
+ $self->logger("${$self}{name} has this many children:",'heading') if $is_t_switch_active;
+ $self->logger(scalar @{${$self}{children}}) if $is_t_switch_active;
+ $self->logger("Post-processed body (${$self}{name}):") if($is_t_switch_active);
+ $self->logger(${$self}{body}) if($is_t_switch_active);
+
+}
+
+sub add_surrounding_indentation_to_begin_statement{
+ # almost all of the objects add surrounding indentation to the 'begin' statements,
+ # but some (e.g HEADING) have their own method
+ my $self = shift;
+
+ my $surroundingIndentation = ${$self}{surroundingIndentation};
+ ${$self}{begin} =~ s/^(\h*)?/$surroundingIndentation/mg; # add indentation
+
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/Item.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/Item.pm
new file mode 100644
index 00000000000..d85f4e24c31
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/Item.pm
@@ -0,0 +1,149 @@
+package LatexIndent::Item;
+# 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::TrailingComments qw/$trailingCommentRegExp/;
+use LatexIndent::GetYamlSettings qw/%masterSettings/;
+use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use Data::Dumper;
+use Exporter qw/import/;
+our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321
+our @EXPORT_OK = qw/find_items construct_list_of_items/;
+our $itemCounter;
+our $listOfItems = q();
+our $itemRegExp;
+
+sub construct_list_of_items{
+ my $self = shift;
+
+ # put together a list of the items
+ while( my ($item,$lookForThisItem)= each %{$masterSettings{itemNames}}){
+ $listOfItems .= ($listOfItems eq "")?"$item":"|$item" if($lookForThisItem);
+ }
+
+ # detail items in the log
+ $self->logger("List of items: $listOfItems (see itemNames)",'heading');
+
+ $itemRegExp = qr/
+ (
+ \\($listOfItems)
+ \h*
+ (\R*)?
+ )
+ (
+ (?: # cluster-only (), don't capture
+ (?!
+ (?:\\(?:$listOfItems)) # cluster-only (), don't capture
+ ). # any character, but not \\$item
+ )*
+ )
+ (\R)?
+ /sx;
+
+
+ return;
+}
+
+sub find_items{
+ # no point carrying on if the list of items is empty
+ return if($listOfItems eq "");
+
+ my $self = shift;
+
+ return unless ${$masterSettings{indentAfterItems}}{${$self}{name}};
+
+ # otherwise loop through the item names
+ $self->logger("Searching for items (see itemNames) in ${$self}{name} (see indentAfterItems)") if $is_t_switch_active;
+ $self->logger(Dumper(\%{$masterSettings{itemNames}})) if $is_t_switch_active;
+
+ while(${$self}{body} =~ m/$itemRegExp\h*($trailingCommentRegExp)?/){
+
+ # log file output
+ $self->logger("Item found: $2",'heading') if $is_t_switch_active;
+
+ ${$self}{body} =~ s/
+ $itemRegExp(\h*)($trailingCommentRegExp)?
+ /
+ # create a new Item object
+ my $itemObject = LatexIndent::Item->new(begin=>$1,
+ body=>$4,
+ end=>q(),
+ name=>$2,
+ linebreaksAtEnd=>{
+ begin=>$3?1:0,
+ body=>$5?1:0,
+ },
+ aliases=>{
+ # begin statements
+ BeginStartsOnOwnLine=>"ItemStartsOnOwnLine",
+ # body statements
+ BodyStartsOnOwnLine=>"ItemFinishesWithLineBreak",
+ },
+ modifyLineBreaksYamlName=>"items",
+ endImmediatelyFollowedByComment=>$5?0:($7?1:0),
+ );
+
+ # the settings and storage of most objects has a lot in common
+ $self->get_settings_and_store_new_object($itemObject);
+ ${@{${$self}{children}}[-1]}{replacementText}.($6?$6:q()).($7?$7:q());
+ /xseg;
+ }
+}
+
+
+sub create_unique_id{
+ my $self = shift;
+
+ $itemCounter++;
+
+ ${$self}{id} = "$tokens{item}$itemCounter";
+ return;
+}
+
+sub tasks_particular_to_each_object{
+ my $self = shift;
+
+ # the item body could hoover up line breaks; we do an additional check
+ ${${$self}{linebreaksAtEnd}}{body}=1 if(${$self}{body} =~ m/\R+$/s );
+
+ # search for ifElseFi blocks
+ $self->find_ifelsefi;
+
+ # search for headings (part, chapter, section, setc)
+ $self->find_heading;
+
+ # search for commands with arguments
+ $self->find_commands_or_key_equals_values_braces;
+
+ # search for special begin/end
+ $self->find_special;
+
+}
+
+sub remove_line_breaks_begin{
+ # the \item command can need a trailing white space if the line breaks have been removed after it and
+ # there is no white space
+ my $self = shift;
+ my $BodyStringLogFile = ${$self}{aliases}{BodyStartsOnOwnLine}||"BodyStartsOnOwnLine";
+ $self->logger("Removing linebreak at the end of begin (see $BodyStringLogFile)");
+ ${$self}{begin} =~ s/\R*$//sx;
+ ${$self}{begin} .= " " unless(${$self}{begin} =~ m/\h$/s or ${$self}{body} =~ m/^\h/s or ${$self}{body} =~ m/^\R/s );
+ ${$self}{linebreaksAtEnd}{begin} = 0;
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/KeyEqualsValuesBraces.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/KeyEqualsValuesBraces.pm
new file mode 100755
index 00000000000..41fcb981a15
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/KeyEqualsValuesBraces.pm
@@ -0,0 +1,122 @@
+package LatexIndent::KeyEqualsValuesBraces;
+# 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::TrailingComments qw/$trailingCommentRegExp/;
+use LatexIndent::Switches qw/$is_m_switch_active/;
+use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use Data::Dumper;
+use Exporter qw/import/;
+our @ISA = "LatexIndent::Command"; # class inheritance, Programming Perl, pg 321
+our @EXPORT_OK = qw/construct_key_equals_values_regexp $key_equals_values_bracesRegExp $key_equals_values_bracesRegExpTrailingComment/;
+our $key_equals_values_braces_Counter;
+our $key_equals_values_bracesRegExp;
+our $key_equals_values_bracesRegExpTrailingComment;
+
+sub construct_key_equals_values_regexp{
+ my $self = shift;
+
+ # grab the arguments regexp
+ my $optAndMandRegExp = $self->get_arguments_regexp;
+
+ # blank line token
+ my $blankLineToken = $tokens{blanklines};
+
+ # store the regular expresssion for matching and replacing
+ $key_equals_values_bracesRegExp = qr/
+ (
+ (?:
+ (?:(?<!\\)\{)
+ |
+ ,
+ |
+ (?:(?<!\\)\[)
+ )
+ (?:\h|\R|$blankLineToken|$trailingCommentRegExp)*
+ ) # $1 pre-key bit: could be { OR , OR [
+ (\\)? # $2 possible backslash
+ (
+ [a-zA-Z@\*0-9_\/.\h\{\}:\#-]+? # lowercase|uppercase letters, @, *, numbers, forward slash, dots
+ ) # $3 name
+ (
+ (?:\h|\R|$blankLineToken|$trailingCommentRegExp)*
+ =\h*
+ (?:\d*\:?)
+ ) # $4 = symbol
+ (\R*)? # $5 linebreak after =
+ ($optAndMandRegExp) # $6 opt|mand arguments
+ (\R)? # $9 linebreak at end
+ /sx;
+
+ $key_equals_values_bracesRegExpTrailingComment = qr/$key_equals_values_bracesRegExp(\h*)((?:$trailingCommentRegExp\h*)*)?/;
+}
+
+sub indent_begin{
+ my $self = shift;
+
+ # blank line token
+ my $blankLineToken = $tokens{blanklines};
+
+ if(${$self}{begin} =~ /\R=/s or ${$self}{begin} =~ /$blankLineToken\h*=/s ){
+ $self->logger("= found on own line in ${$self}{name}, adding indentation") if $is_t_switch_active;
+ ${$self}{begin} =~ s/=/${$self}{indentation}=/s;
+ }
+}
+
+sub check_linebreaks_before_equals{
+ # check if -m switch is active
+ return unless $is_m_switch_active;
+
+ my $self = shift;
+
+ # linebreaks *infront* of = symbol
+ if(${$self}{begin} =~ /\R\h*=/s){
+ if(defined ${$self}{EqualsStartsOnOwnLine} and ${$self}{EqualsStartsOnOwnLine}==-1){
+ $self->logger("Removing linebreak before = symbol in ${$self}{name} (see EqualsStartsOnOwnLine)") if $is_t_switch_active;
+ ${$self}{begin} =~ s/(\R|\h)*=/=/s;
+ }
+ } else {
+ if(defined ${$self}{EqualsStartsOnOwnLine} and ${$self}{EqualsStartsOnOwnLine}==1){
+ $self->logger("Adding a linebreak before = symbol for ${$self}{name} (see EqualsStartsOnOwnLine)") if $is_t_switch_active;
+ ${$self}{begin} =~ s/=/\n=/s;
+ } elsif(defined ${$self}{EqualsStartsOnOwnLine} and ${$self}{EqualsStartsOnOwnLine}==2){
+ $self->logger("Adding a % linebreak immediately before = symbol for ${$self}{name} (see EqualsStartsOnOwnLine)") if $is_t_switch_active;
+ ${$self}{begin} =~ s/\h*=/%\n=/s;
+ }
+ }
+ return;
+}
+
+sub create_unique_id{
+ my $self = shift;
+
+ $key_equals_values_braces_Counter++;
+ ${$self}{id} = "$tokens{key_equals_values_braces}$key_equals_values_braces_Counter";
+ return;
+}
+
+sub get_replacement_text{
+ my $self = shift;
+
+ # the replacement text for a key = {value} needes to accomodate the leading [ OR { OR % OR , OR any combination thereof
+ $self->logger("Custom replacement text routine for ${$self}{name}") if $is_t_switch_active;
+ ${$self}{replacementText} = ${$self}{beginningbit}.${$self}{id};
+ delete ${$self}{beginningbit};
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/LogFile.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/LogFile.pm
new file mode 100644
index 00000000000..89cffa58a81
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/LogFile.pm
@@ -0,0 +1,166 @@
+package LatexIndent::LogFile;
+# 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::GetYamlSettings qw/%masterSettings/;
+use LatexIndent::Switches qw/%switches/;
+use FindBin;
+use File::Basename; # to get the filename and directory path
+use Exporter qw/import/;
+our @EXPORT_OK = qw/logger output_logfile processSwitches/;
+our @logFileNotes;
+
+# log file methods
+# log file methods
+# log file methods
+# reference: http://stackoverflow.com/questions/6736998/help-calling-a-sub-routine-from-a-perl-module-and-printing-to-logfile
+
+sub logger{
+ shift;
+ my $line = shift;
+ my $infoLevel = shift;
+ push(@logFileNotes,{line=>$line,level=>$infoLevel?$infoLevel:'default'});
+ return
+}
+
+sub processSwitches{
+ my $self = shift;
+
+ # details of the script to log file
+ $self->logger("$FindBin::Script version 3.0, a script to indent .tex files",'heading');
+ $self->logger("$FindBin::Script lives here: $FindBin::RealBin/");
+
+ # time the script is used
+ my $time = localtime();
+ $self->logger("$time");
+
+ if(scalar(@ARGV) < 1 or $switches{showhelp}) {
+ print <<ENDQUOTE
+latexindent.pl version 3.0
+usage: latexindent.pl [options] [file][.tex|.sty|.cls|.bib|...]
+ -h, --help
+ help (see the documentation for detailed instructions and examples)
+ -o, --outputfile
+ output to another file; sample usage:
+ latexindent.pl -o outputfile.tex myfile.tex
+ latexindent.pl -o=outputfile.tex myfile.tex
+ -w, --overwrite
+ overwrite the current file; a backup will be made, but still be careful
+ -s, --silent
+ silent mode: no output will be given to the terminal
+ -t, --trace
+ tracing mode: verbose information given to the log file
+ -l, --local[=myyaml.yaml]
+ use localSettings.yaml (assuming it exists in the directory of your file);
+ alternatively, use myyaml.yaml, if it exists; sample usage:
+ latexindent.pl -l some.yaml myfile.tex
+ latexindent.pl -l=another.yaml myfile.tex
+ latexindent.pl -l=some.yaml,another.yaml myfile.tex
+ -d, --onlydefault
+ ONLY use defaultSettings.yaml, ignore ALL (yaml) user files
+ -g, --logfile
+ used to specify the name of logfile (default is indent.log)
+ -c, --cruft=<cruft directory>
+ used to specify the location of backup files and indent.log
+ -m, --modifylinebreaks
+ modify linebreaks before, during, and at the end of code blocks;
+ trailing comments can also be added using this feature
+ENDQUOTE
+ ;
+ exit(2);
+}
+
+ # log the switches from the user
+ $self->logger('Processing switches','heading');
+
+ # check on the trace mode switch (should be turned on if ttrace mode active)
+ $switches{trace} = $switches{ttrace} ? 1 : $switches{trace};
+
+ # output details of switches
+ $self->logger('-t|--trace: Trace mode active (you have used either -t or --trace)') if($switches{trace} and !$switches{ttrace});
+ $self->logger('-tt|--ttrace: TTrace mode active (you have used either -tt or --ttrace)') if($switches{tracingModeVeryDetailed});
+ $self->logger('-s|--silent: Silent mode active (you have used either -s or --silent)') if($switches{silentMode});
+ $self->logger('-d|--onlydefault: Only defaultSettings.yaml will be used (you have used either -d or --onlydefault)') if($switches{onlyDefault});
+ $self->logger("-w|--overwrite: Overwrite mode active, will make a back up of ${$self}{fileName} first") if($switches{overwrite});
+ $self->logger("-l|--localSettings: Read localSettings YAML file") if($switches{readLocalSettings});
+ $self->logger("-o|--outputfile: output to file") if($switches{outputToFile});
+ $self->logger("-m|--modifylinebreaks: modify line breaks") if($switches{modifyLineBreaks});
+ $self->logger("-g|--logfile: logfile name") if($switches{logFileName});
+ $self->logger("-c|--cruft: cruft directory") if($switches{cruftDirectory});
+
+ # check if overwrite and outputfile are active similtaneously
+ if($switches{overwrite} and $switches{outputToFile}){
+ $self->logger("Options check",'heading');
+ $self->logger("You have called latexindent.pl with both -o and -w");
+ $self->logger("-o (output to file) will take priority, and -w (over write) will be ignored");
+ $switches{overwrite}=0;
+ }
+
+ # cruft directory
+ ${$self}{cruftDirectory} = $switches{cruftDirectory}||(dirname ${$self}{fileName});
+ die "Could not find directory ${$self}{cruftDirectory}\nExiting, no indentation done." if(!(-d ${$self}{cruftDirectory}));
+ my $logfileName = $switches{logFileName}||"indent.log";
+ $self->logger("Directory for backup files and $logfileName: ${$self}{cruftDirectory}",'heading');
+
+ # output location of modules
+ if($FindBin::Script eq 'latexindent.pl' or ($FindBin::Script eq 'latexindent.exe' and $switches{trace} )) {
+ my @listOfModules = ('FindBin', 'YAML::Tiny', 'File::Copy', 'File::Basename', 'Getopt::Long','File::HomeDir');
+ $self->logger("Perl modules are being loaded from the following directories:",'heading');
+ foreach my $moduleName (@listOfModules) {
+ (my $file = $moduleName) =~ s|::|/|g;
+ require $file . '.pm';
+ $self->logger($INC{$file .'.pm'});
+ }
+ $self->logger("Latex Indent perl modules are being loaded from, for example:",'heading');
+ (my $file = 'LatexIndent::Document') =~ s|::|/|g;
+ require $file . '.pm';
+ $self->logger($INC{$file .'.pm'});
+ }
+ return;
+}
+
+sub output_logfile{
+ my $self = shift;
+ my $logfile;
+ my $logfileName = $switches{logFileName}||"indent.log";
+
+ open($logfile,">","${$self}{cruftDirectory}/$logfileName") or die "Can't open $logfileName";
+
+ # put the final line in the logfile
+ $self->logger("${$masterSettings{logFilePreferences}}{endLogFileWith}",'heading');
+
+ # github info line
+ $self->logger("Please direct all communication/issues to: ",'heading') if ${$masterSettings{logFilePreferences}}{showGitHubInfoFooter};
+ $self->logger("https://github.com/cmhughes/latexindent.pl") if ${$masterSettings{logFilePreferences}}{showGitHubInfoFooter};
+
+ # output the logfile
+ foreach my $line (@logFileNotes){
+ if(${$line}{level} eq 'heading'){
+ print $logfile ${$line}{line},"\n";
+ } elsif(${$line}{level} eq 'default') {
+ # add tabs to the beginning of lines
+ # for default logfile lines
+ ${$line}{line} =~ s/^/\t/mg;
+ print $logfile ${$line}{line},"\n";
+ }
+ }
+
+ close($logfile);
+}
+
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/MandatoryArgument.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/MandatoryArgument.pm
new file mode 100644
index 00000000000..172e002eda4
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/MandatoryArgument.pm
@@ -0,0 +1,130 @@
+package LatexIndent::MandatoryArgument;
+# 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::TrailingComments qw/$trailingCommentRegExp/;
+use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use Exporter qw/import/;
+our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321
+our @EXPORT_OK = qw/find_mandatory_arguments get_mand_arg_reg_exp/;
+our $mandatoryArgumentCounter;
+
+sub find_mandatory_arguments{
+ my $self = shift;
+
+ my $mandArgRegExp = $self->get_mand_arg_reg_exp;
+
+ # pick out the mandatory arguments
+ while(${$self}{body} =~ m/$mandArgRegExp\h*($trailingCommentRegExp)*(.*)/s){
+ # log file output
+ $self->logger("Mandatory argument found, body in ${$self}{name}",'heading') if $is_t_switch_active;
+ $self->logger("(last argument)") if($9 eq '' and $is_t_switch_active);
+
+ ${$self}{body} =~ s/
+ $mandArgRegExp(\h*)($trailingCommentRegExp)*(.*)
+ /
+ # create a new Mandatory Argument object
+ my $mandatoryArg = LatexIndent::MandatoryArgument->new(begin=>$1,
+ name=>${$self}{name}.":mandatoryArgument",
+ nameForIndentationSettings=>${$self}{parent},
+ parent=>${$self}{parent},
+ body=>$3.($4?$4:q()),
+ end=>$5,
+ linebreaksAtEnd=>{
+ begin=>$2?1:0,
+ body=>$4?1:0,
+ end=>$7?1:0,
+ },
+ aliases=>{
+ # begin statements
+ BeginStartsOnOwnLine=>"LCuBStartsOnOwnLine",
+ # body statements
+ BodyStartsOnOwnLine=>"MandArgBodyStartsOnOwnLine",
+ # end statements
+ EndStartsOnOwnLine=>"RCuBStartsOnOwnLine",
+ # after end statements
+ EndFinishesWithLineBreak=>"RCuBFinishesWithLineBreak",
+ },
+ horizontalTrailingSpace=>$6?$6:q(),
+ modifyLineBreaksYamlName=>"mandatoryArguments",
+ # the last argument (determined by $10 eq '') needs information from the argument container object
+ endImmediatelyFollowedByComment=>($10 eq '')?${$self}{endImmediatelyFollowedByComment}:($9?1:0),
+ );
+
+ # the settings and storage of most objects has a lot in common
+ $self->get_settings_and_store_new_object($mandatoryArg);
+ ${@{${$self}{children}}[-1]}{replacementText}.($8?$8:q()).($9?$9:q()).($10?$10:q());
+ /xseg;
+ $self->wrap_up_tasks;
+ }
+ }
+
+sub create_unique_id{
+ my $self = shift;
+
+ $mandatoryArgumentCounter++;
+ ${$self}{id} = "$tokens{mandatoryArgument}$mandatoryArgumentCounter";
+ return;
+}
+
+sub get_mand_arg_reg_exp{
+
+ my $mandArgRegExp = qr/
+ (?<!\\) # not immediately pre-ceeded by \
+ (
+ \{
+ \h*
+ (\R*) # linebreaks after { into $2
+ ) # { captured into $1
+ (
+ (?:
+ (?!
+ (?:(?<!\\)\{)
+ ).
+ )*? # not including {, but \{ ok
+ ) # body into $3
+ (\R*) # linebreaks after body into $4
+ (?<!\\) # not immediately pre-ceeded by \
+ (
+ \} # {mandatory arguments}
+ ) # } into $5
+ (\h*)
+ (\R)? # linebreaks after } into $6
+ /sx;
+
+ return $mandArgRegExp;
+}
+
+sub get_object_attribute_for_indentation_settings{
+ my $self = shift;
+
+ return ${$self}{modifyLineBreaksYamlName};
+}
+
+sub tasks_particular_to_each_object{
+ my $self = shift;
+
+ # search for ifElseFi blocks
+ $self->find_ifelsefi;
+
+ # search for special begin/end
+ $self->find_special;
+
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/ModifyLineBreaks.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/ModifyLineBreaks.pm
new file mode 100644
index 00000000000..bcfb2336264
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/ModifyLineBreaks.pm
@@ -0,0 +1,174 @@
+package LatexIndent::ModifyLineBreaks;
+# 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 Data::Dumper;
+use Exporter qw/import/;
+use LatexIndent::Tokens qw/%tokens/;
+use LatexIndent::TrailingComments qw/$trailingCommentRegExp/;
+use LatexIndent::Switches qw/$is_m_switch_active $is_t_switch_active $is_tt_switch_active/;
+our @EXPORT_OK = qw/modify_line_breaks_body modify_line_breaks_end adjust_line_breaks_end_parent remove_line_breaks_begin/;
+our @allObjects;
+
+sub modify_line_breaks_body{
+ my $self = shift;
+
+ # add a line break after \begin{statement} if appropriate
+ if(defined ${$self}{BodyStartsOnOwnLine}){
+ my $BodyStringLogFile = ${$self}{aliases}{BodyStartsOnOwnLine}||"BodyStartsOnOwnLine";
+ if(${$self}{BodyStartsOnOwnLine}>=1 and !${$self}{linebreaksAtEnd}{begin}){
+ if(${$self}{BodyStartsOnOwnLine}==1){
+ # modify the begin statement
+ $self->logger("Adding a linebreak at the end of begin, ${$self}{begin} (see $BodyStringLogFile)") if $is_t_switch_active;
+ ${$self}{begin} .= "\n";
+ ${$self}{linebreaksAtEnd}{begin} = 1;
+ $self->logger("Removing leading space from body of ${$self}{name} (see $BodyStringLogFile)") if $is_t_switch_active;
+ ${$self}{body} =~ s/^\h*//;
+ } elsif(${$self}{BodyStartsOnOwnLine}==2){
+ # by default, assume that no trailing comment token is needed
+ my $trailingCommentToken = q();
+ if(${$self}{body} !~ m/^\h*$trailingCommentRegExp/s){
+ # modify the begin statement
+ $self->logger("Adding a % at the end of begin, ${$self}{begin}, followed by a linebreak ($BodyStringLogFile == 2)") if $is_t_switch_active;
+ $trailingCommentToken = "%".$self->add_comment_symbol;
+ ${$self}{begin} =~ s/\h*$//;
+ ${$self}{begin} .= "$trailingCommentToken\n";
+ ${$self}{linebreaksAtEnd}{begin} = 1;
+ $self->logger("Removing leading space from body of ${$self}{name} (see $BodyStringLogFile)") if $is_t_switch_active;
+ ${$self}{body} =~ s/^\h*//;
+ } else {
+ $self->logger("Even though $BodyStringLogFile == 2, ${$self}{begin} already finishes with a %, so not adding another.") if $is_t_switch_active;
+ }
+ }
+ } elsif (${$self}{BodyStartsOnOwnLine}==-1 and ${$self}{linebreaksAtEnd}{begin}){
+ # remove line break *after* begin, if appropriate
+ $self->remove_line_breaks_begin;
+ }
+ }
+ }
+
+sub remove_line_breaks_begin{
+ my $self = shift;
+ my $BodyStringLogFile = ${$self}{aliases}{BodyStartsOnOwnLine}||"BodyStartsOnOwnLine";
+ $self->logger("Removing linebreak at the end of begin (see $BodyStringLogFile)") if $is_t_switch_active;
+ ${$self}{begin} =~ s/\R*$//sx;
+ ${$self}{linebreaksAtEnd}{begin} = 0;
+}
+
+sub modify_line_breaks_end{
+ my $self = shift;
+
+ # possibly modify line break *before* \end{statement}
+ if(defined ${$self}{EndStartsOnOwnLine}){
+ my $EndStringLogFile = ${$self}{aliases}{EndStartsOnOwnLine}||"EndStartsOnOwnLine";
+ if(${$self}{EndStartsOnOwnLine}>=1 and !${$self}{linebreaksAtEnd}{body}){
+ # add a line break after body, if appropriate
+ $self->logger("Adding a linebreak at the end of body (see $EndStringLogFile)") if $is_t_switch_active;
+
+ # by default, assume that no trailing comment token is needed
+ my $trailingCommentToken = q();
+ if(${$self}{EndStartsOnOwnLine}==2){
+ $self->logger("Adding a % immediately after body of ${$self}{name} ($EndStringLogFile==2)") if $is_t_switch_active;
+ $trailingCommentToken = "%".$self->add_comment_symbol;
+ ${$self}{body} =~ s/\h*$//s;
+ }
+
+ # modified end statement
+ if(${$self}{body} =~ m/^\h*$/s and ${$self}{BodyStartsOnOwnLine} >=1 ){
+ ${$self}{linebreaksAtEnd}{body} = 0;
+ } else {
+ ${$self}{body} .= "$trailingCommentToken\n";
+ ${$self}{linebreaksAtEnd}{body} = 1;
+ }
+ } elsif (${$self}{EndStartsOnOwnLine}==-1 and ${$self}{linebreaksAtEnd}{body}){
+ # remove line break *after* body, if appropriate
+
+ # check to see that body does *not* finish with blank-line-token,
+ # if so, then don't remove that final line break
+ if(${$self}{body} !~ m/$tokens{blanklines}$/s){
+ $self->logger("Removing linebreak at the end of body (see $EndStringLogFile)") if $is_t_switch_active;
+ ${$self}{body} =~ s/\R*$//sx;
+ ${$self}{linebreaksAtEnd}{body} = 0;
+ } else {
+ $self->logger("Blank line token found at end of body (${$self}{name}), see preserveBlankLines, not removing line break before ${$self}{end}") if $is_t_switch_active;
+ }
+ }
+ }
+
+ # possibly modify line break *after* \end{statement}
+ if(defined ${$self}{EndFinishesWithLineBreak}
+ and ${$self}{EndFinishesWithLineBreak}>=1
+ and !${$self}{linebreaksAtEnd}{end}){
+ my $EndStringLogFile = ${$self}{aliases}{EndFinishesWithLineBreak}||"EndFinishesWithLineBreak";
+ if(${$self}{EndFinishesWithLineBreak}==1){
+ $self->logger("Adding a linebreak at the end of ${$self}{end} (see $EndStringLogFile)") if $is_t_switch_active;
+ ${$self}{linebreaksAtEnd}{end} = 1;
+
+ # modified end statement
+ ${$self}{replacementText} .= "\n";
+ } elsif(${$self}{EndFinishesWithLineBreak}==2){
+ if(${$self}{endImmediatelyFollowedByComment}){
+ # no need to add a % if one already exists
+ $self->logger("Even though $EndStringLogFile == 2, ${$self}{end} is immediately followed by a %, so not adding another; not adding line break.") if $is_t_switch_active;
+ } else {
+ # otherwise, create a trailing comment, and tack it on
+ $self->logger("Adding a % immediately after, ${$self}{end} ($EndStringLogFile==2)") if $is_t_switch_active;
+ my $trailingCommentToken = "%".$self->add_comment_symbol;
+ ${$self}{end} =~ s/\h*$//s;
+ ${$self}{replacementText} .= "$trailingCommentToken\n";
+ ${$self}{linebreaksAtEnd}{end} = 1;
+ }
+ }
+ }
+
+}
+
+sub adjust_line_breaks_end_parent{
+ # when a parent object contains a child object, the line break
+ # at the end of the parent object can become messy
+ return unless $is_m_switch_active;
+
+ my $self = shift;
+
+ # most recent child object
+ my $child = @{${$self}{children}}[-1];
+
+ # adjust parent linebreaks information
+ if(${$child}{linebreaksAtEnd}{end} and ${$self}{body} =~ m/${$child}{replacementText}\h*\R*$/s and !${$self}{linebreaksAtEnd}{body}){
+ $self->logger("ID: ${$child}{id}") if($is_t_switch_active);
+ $self->logger("${$child}{begin}...${$child}{end} is found at the END of body of parent, ${$self}{name}, avoiding a double line break:") if($is_t_switch_active);
+ $self->logger("adjusting ${$self}{name} linebreaksAtEnd{body} to be 1") if($is_t_switch_active);
+ ${$self}{linebreaksAtEnd}{body}=1;
+ }
+
+ # the modify line switch can adjust line breaks, so we need another check,
+ # see for example, test-cases/environments/environments-remove-line-breaks-trailing-comments.tex
+ if(defined ${$child}{linebreaksAtEnd}{body}
+ and !${$child}{linebreaksAtEnd}{body}
+ and ${$child}{body} =~ m/\R(?:$trailingCommentRegExp\h*)?$/s ){
+ # log file information
+ $self->logger("Undisclosed line break at the end of body of ${$child}{name}: '${$child}{end}'") if($is_t_switch_active);
+ $self->logger("Adding a linebreak at the end of body for ${$child}{id}") if($is_t_switch_active);
+
+ # make the adjustments
+ ${$child}{body} .= "\n";
+ ${$child}{linebreaksAtEnd}{body}=1;
+ }
+
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/NamedGroupingBracesBrackets.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/NamedGroupingBracesBrackets.pm
new file mode 100755
index 00000000000..ea2c5323cf3
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/NamedGroupingBracesBrackets.pm
@@ -0,0 +1,71 @@
+package LatexIndent::NamedGroupingBracesBrackets;
+# 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::TrailingComments qw/$trailingCommentRegExp/;
+use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use Exporter qw/import/;
+our @ISA = "LatexIndent::Command"; # class inheritance, Programming Perl, pg 321
+our @EXPORT_OK = qw/construct_grouping_braces_brackets_regexp $grouping_braces_regexp $grouping_braces_regexpTrailingComment/;
+our $groupingBracesCounter;
+our $grouping_braces_regexp;
+our $grouping_braces_regexpTrailingComment;
+
+sub construct_grouping_braces_brackets_regexp{
+ my $self = shift;
+
+ # grab the arguments regexp
+ my $optAndMandRegExp = $self->get_arguments_regexp;
+
+ # store the regular expresssion for matching and replacing
+ $grouping_braces_regexp = qr/
+ (
+ \h|\R|\{|\[|\$|\)
+ )
+ (
+ [0-9a-zA-Z@\*><]+? # lowercase|uppercase letters, @, *, numbers, forward slash, dots
+ ) # $2 name
+ (\h*) # $3 h-space
+ (\R*) # $4 linebreaks
+ ($optAndMandRegExp) # $5 mand|opt arguments (at least one)
+ (\R)? # $8 linebreak
+ /sx;
+
+ # something {value} grouping braces with trailing comment
+ $grouping_braces_regexpTrailingComment = qr/$grouping_braces_regexp\h*((?:$trailingCommentRegExp\h*)*)?/;
+
+}
+
+sub create_unique_id{
+ my $self = shift;
+
+ $groupingBracesCounter++;
+ ${$self}{id} = "$tokens{groupingBraces}$groupingBracesCounter";
+ return;
+}
+
+sub get_replacement_text{
+ my $self = shift;
+
+ # the replacement text for a key = {value} needes to accomodate the leading [ OR { OR % OR , OR any combination thereof
+ $self->logger("Custom replacement text routine for ${$self}{name}") if $is_t_switch_active;
+ ${$self}{replacementText} = ${$self}{beginningbit}.${$self}{id};
+ delete ${$self}{beginningbit};
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/OptionalArgument.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/OptionalArgument.pm
new file mode 100644
index 00000000000..b949e9b7e92
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/OptionalArgument.pm
@@ -0,0 +1,115 @@
+package LatexIndent::OptionalArgument;
+# 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::TrailingComments qw/$trailingCommentRegExp/;
+use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use Exporter qw/import/;
+our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321
+our @EXPORT_OK = qw/find_optional_arguments/;
+our $optionalArgumentCounter;
+our $optArgRegExp = qr/
+ (?<!\\) # not immediately pre-ceeded by \
+ (
+ \[
+ \h*
+ (\R*)
+ )
+ (.*?)
+ (\R*)
+ (?<!\\) # not immediately pre-ceeded by \
+ (
+ \] # [optional arguments]
+ )
+ (\h*)
+ (\R)?
+ /sx;
+
+sub find_optional_arguments{
+ my $self = shift;
+
+ # pick out the optional arguments
+ while(${$self}{body} =~ m/$optArgRegExp\h*($trailingCommentRegExp)*(.*)/s){
+ # log file output
+ $self->logger("Optional argument found, body in ${$self}{name}",'heading') if $is_t_switch_active;
+ $self->logger("(last argument)") if($9 eq '' and $is_t_switch_active);
+
+ ${$self}{body} =~ s/
+ $optArgRegExp(\h*)($trailingCommentRegExp)*(.*)
+ /
+ # create a new Optional Argument object
+ my $optionalArg = LatexIndent::OptionalArgument->new(begin=>$1,
+ name=>${$self}{name}.":optionalArgument",
+ nameForIndentationSettings=>${$self}{parent},
+ parent=>${$self}{parent},
+ body=>$3.($4?$4:q()),
+ end=>$5,
+ linebreaksAtEnd=>{
+ begin=>$2?1:0,
+ body=>$4?1:0,
+ end=>$7?1:0,
+ },
+ aliases=>{
+ # begin statements
+ BeginStartsOnOwnLine=>"LSqBStartsOnOwnLine",
+ # body statements
+ BodyStartsOnOwnLine=>"OptArgBodyStartsOnOwnLine",
+ # end statements
+ EndStartsOnOwnLine=>"RSqBStartsOnOwnLine",
+ # after end statements
+ EndFinishesWithLineBreak=>"RSqBFinishesWithLineBreak",
+ },
+ modifyLineBreaksYamlName=>"optionalArguments",
+ # the last argument (determined by $10 eq '') needs information from the argument container object
+ endImmediatelyFollowedByComment=>($10 eq '')?${$self}{endImmediatelyFollowedByComment}:($9?1:0),
+ horizontalTrailingSpace=>$6?$6:q(),
+ );
+
+ # the settings and storage of most objects has a lot in common
+ $self->get_settings_and_store_new_object($optionalArg);
+ ${@{${$self}{children}}[-1]}{replacementText}.($8?$8:q()).($9?$9:q()).($10?$10:q());
+ /xseg;
+ }
+ }
+
+sub get_object_attribute_for_indentation_settings{
+ my $self = shift;
+
+ return ${$self}{modifyLineBreaksYamlName};
+}
+
+sub create_unique_id{
+ my $self = shift;
+
+ $optionalArgumentCounter++;
+ ${$self}{id} = "$tokens{optionalArgument}$optionalArgumentCounter";
+ return;
+}
+
+sub tasks_particular_to_each_object{
+ my $self = shift;
+
+ # search for ifElseFi blocks
+ $self->find_ifelsefi;
+
+ # search for special begin/end
+ $self->find_special;
+
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/Preamble.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/Preamble.pm
new file mode 100644
index 00000000000..a3a62dd3f56
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/Preamble.pm
@@ -0,0 +1,64 @@
+package LatexIndent::Preamble;
+# 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/%masterSettings/;
+our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321
+our $preambleCounter;
+
+sub create_unique_id{
+ my $self = shift;
+
+ $preambleCounter++;
+ ${$self}{id} = "$tokens{preamble}$preambleCounter$tokens{endOfToken}";
+ return;
+}
+
+sub get_replacement_text{
+ my $self = shift;
+
+ # the replacement text for preamble needs to put the \\begin{document} back in
+ $self->logger("Custom replacement text routine for preamble ${$self}{name}");
+ ${$self}{replacementText} = ${$self}{id}.${$self}{afterbit};
+ delete ${$self}{afterbit};
+}
+
+sub indent{
+ # preamble doesn't receive any additional indentation
+ return;
+}
+
+sub tasks_particular_to_each_object{
+ my $self = shift;
+
+ # search for environments
+ $self->find_environments;
+
+ # search for ifElseFi blocks
+ $self->find_ifelsefi;
+
+ # search for commands with arguments
+ $self->find_commands_or_key_equals_values_braces if(!$masterSettings{preambleCommandsBeforeEnvironments});
+
+ # search for special begin/end
+ $self->find_special;
+
+
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/RoundBrackets.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/RoundBrackets.pm
new file mode 100644
index 00000000000..f320d007fa2
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/RoundBrackets.pm
@@ -0,0 +1,100 @@
+package LatexIndent::RoundBrackets;
+# 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::TrailingComments qw/$trailingCommentRegExp/;
+use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use Exporter qw/import/;
+our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321
+our @EXPORT_OK = qw/find_round_brackets/;
+our $roundBracketCounter;
+our $roundBracketRegExp = qr/
+ (?<!\\)
+ (
+ \(
+ \h*
+ (\R*)
+ )
+ (.*?)
+ (\R*)
+ (?<!\\) # not immediately pre-ceeded by \
+ (
+ \) # [optional arguments]
+ )
+ (\h*)
+ (\R)?
+ /sx;
+
+sub indent{
+ return;
+}
+
+sub find_round_brackets{
+ my $self = shift;
+
+ # pick out the optional arguments
+ while(${$self}{body} =~ m/$roundBracketRegExp\h*($trailingCommentRegExp)*(.*)/s){
+ # log file output
+
+ ${$self}{body} =~ s/
+ $roundBracketRegExp(\h*)($trailingCommentRegExp)*(.*)
+ /
+ # create a new Optional Argument object
+ my $roundBracket = LatexIndent::RoundBrackets->new(begin=>$1,
+ name=>${$self}{name}.":roundBracket",
+ nameForIndentationSettings=>${$self}{parent},
+ parent=>${$self}{parent},
+ body=>$3.($4?$4:q()),
+ end=>$5,
+ linebreaksAtEnd=>{
+ begin=>$2?1:0,
+ body=>$4?1:0,
+ end=>$7?1:0,
+ },
+ modifyLineBreaksYamlName=>"roundBracket",
+ # the last argument (determined by $10 eq '') needs information from the argument container object
+ endImmediatelyFollowedByComment=>($10 eq '')?${$self}{endImmediatelyFollowedByComment}:($9?1:0),
+ horizontalTrailingSpace=>$6?$6:q(),
+ );
+
+ # the settings and storage of most objects has a lot in common
+ $self->get_settings_and_store_new_object($roundBracket);
+ ${@{${$self}{children}}[-1]}{replacementText}.($8?$8:q()).($9?$9:q()).($10?$10:q());
+ /xseg;
+ }
+ }
+
+sub get_object_attribute_for_indentation_settings{
+ my $self = shift;
+
+ return ${$self}{modifyLineBreaksYamlName};
+}
+
+sub create_unique_id{
+ my $self = shift;
+
+ $roundBracketCounter++;
+ ${$self}{id} = "$tokens{roundBracket}$roundBracketCounter";
+ return;
+}
+
+sub tasks_particular_to_each_object{
+ return;
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/Special.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/Special.pm
new file mode 100644
index 00000000000..ba7b905b492
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/Special.pm
@@ -0,0 +1,184 @@
+package LatexIndent::Special;
+# 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::TrailingComments qw/$trailingCommentRegExp/;
+use LatexIndent::GetYamlSettings qw/%masterSettings/;
+use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use Data::Dumper;
+use Exporter qw/import/;
+our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321
+our @EXPORT_OK = qw/find_special construct_special_begin/;
+our $specialCounter;
+our $specialBegins = q();
+our $specialAllMatchesRegExp = q();
+our %individualSpecialRegExps;
+
+sub construct_special_begin{
+ my $self = shift;
+
+ # put together a list of the begin terms in special
+ while( my ($specialName,$BeginEnd)= each %{$masterSettings{specialBeginEnd}}){
+ # only append the regexps if lookForThis is 1
+ $specialBegins .= ($specialBegins eq ""?q():"|").${$BeginEnd}{begin} if(${$BeginEnd}{lookForThis});
+ }
+
+ # put together a list of the begin terms in special
+ while( my ($specialName,$BeginEnd)= each %{$masterSettings{specialBeginEnd}}){
+
+ # only append the regexps if lookForThis is 1
+ if(${$BeginEnd}{lookForThis}){
+ # the beginning parts
+ $specialBegins .= ($specialBegins eq ""?q():"|").${$BeginEnd}{begin};
+
+ # the overall regexp
+ $specialAllMatchesRegExp .= ($specialAllMatchesRegExp eq ""?q():"|")
+ .qr/
+ ${$BeginEnd}{begin}
+ (?: # cluster-only (), don't capture
+ (?!
+ (?:$specialBegins) # cluster-only (), don't capture
+ ). # any character, but not anything in $specialBegins
+ )*?
+ ${$BeginEnd}{end}
+ /sx;
+
+ # store the individual special regexp
+ $individualSpecialRegExps{$specialName} = qr/
+ (
+ ${$BeginEnd}{begin}
+ \h*
+ (\R*)?
+ )
+ (
+ (?: # cluster-only (), don't capture
+ (?!
+ (?:$specialBegins) # cluster-only (), don't capture
+ ). # any character, but not anything in $specialBegins
+ )*?
+ (\R*)?
+ )
+ (
+ ${$BeginEnd}{end}
+ )
+ (\h*)
+ (\R)?
+ /sx
+
+ } else {
+ $self->logger("The special regexps won't include anything from $specialName (see lookForThis)",'heading') if $is_t_switch_active ;
+ }
+ }
+
+ # move $$ to the beginning
+ if($specialBegins =~ m/\|\\\$\\\$/){
+ $specialBegins =~ s/\|(\\\$\\\$)//;
+ $specialBegins = $1."|".$specialBegins;
+ }
+
+ # info to the log file
+ $self->logger("The special beginnings regexp is: $specialBegins (see specialBeginEnd)",'heading') if $is_t_switch_active;
+
+ # overall special regexp
+ $self->logger("The overall special regexp is: $specialAllMatchesRegExp(see specialBeginEnd)",'heading') if $is_t_switch_active;
+
+ }
+
+sub find_special{
+ my $self = shift;
+
+ # no point carrying on if the list of specials is empty
+ return if($specialBegins eq "");
+
+ # otherwise loop through the special begin/end
+ $self->logger("Searching for special begin/end (see specialBeginEnd)") if $is_t_switch_active ;
+ $self->logger(Dumper(\%{$masterSettings{specialBeginEnd}})) if $is_tt_switch_active;
+
+ # keep looping as long as there is a special match of some kind
+ while(${$self}{body} =~ m/$specialAllMatchesRegExp/sx){
+
+ # loop through each special match
+ while( my ($specialName,$BeginEnd)= each %{$masterSettings{specialBeginEnd}}){
+
+ # log file
+ if(${$BeginEnd}{lookForThis}){
+ $self->logger("Looking for $specialName",'heading') if $is_t_switch_active ;
+ } else {
+ $self->logger("Not looking for $specialName (see lookForThis)",'heading') if $is_t_switch_active ;
+ next;
+ }
+
+ # the regexp
+ my $specialRegExp = $individualSpecialRegExps{$specialName};
+
+ while(${$self}{body} =~ m/$specialRegExp(\h*)($trailingCommentRegExp)?/){
+
+ # global substitution
+ ${$self}{body} =~ s/
+ $specialRegExp(\h*)($trailingCommentRegExp)?
+ /
+ # log file output
+ $self->logger("special found: $specialName",'heading') if $is_t_switch_active;
+
+ # create a new special object
+ my $specialObject = LatexIndent::Special->new(begin=>$1,
+ body=>$3,
+ end=>$5,
+ name=>$specialName,
+ linebreaksAtEnd=>{
+ begin=>$2?1:0,
+ body=>$4?1:0,
+ end=>$7?1:0,
+ },
+ aliases=>{
+ # begin statements
+ BeginStartsOnOwnLine=>"SpecialBeginStartsOnOwnLine",
+ # body statements
+ BodyStartsOnOwnLine=>"SpecialBodyStartsOnOwnLine",
+ # end statements
+ EndStartsOnOwnLine=>"SpecialEndStartsOnOwnLine",
+ # after end statements
+ EndFinishesWithLineBreak=>"SpecialEndFinishesWithLineBreak",
+ },
+ modifyLineBreaksYamlName=>"specialBeginEnd",
+ endImmediatelyFollowedByComment=>$7?0:($9?1:0),
+ horizontalTrailingSpace=>$6?$6:q(),
+ );
+
+ # the settings and storage of most objects has a lot in common
+ $self->get_settings_and_store_new_object($specialObject);
+ ${@{${$self}{children}}[-1]}{replacementText}.($8?$8:q()).($9?$9:q());
+ /xseg;
+
+ $self->wrap_up_tasks;
+ }
+ }
+ }
+}
+
+
+sub create_unique_id{
+ my $self = shift;
+
+ $specialCounter++;
+
+ ${$self}{id} = "$tokens{special}$specialCounter";
+ return;
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/Switches.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/Switches.pm
new file mode 100644
index 00000000000..a5b07936a6d
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/Switches.pm
@@ -0,0 +1,37 @@
+package LatexIndent::Switches;
+# 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 Exporter qw/import/;
+our @EXPORT_OK = qw/%switches storeSwitches $is_m_switch_active $is_t_switch_active $is_tt_switch_active/;
+our %switches;
+our $is_m_switch_active;
+our $is_t_switch_active;
+our $is_tt_switch_active;
+
+sub storeSwitches{
+ my $self = shift;
+
+ # copy document switches into hash local to this module
+ %switches = %{${$self}{switches}};
+ $is_m_switch_active = defined $switches{modifyLineBreaks}?$switches{modifyLineBreaks}: 0;
+ $is_t_switch_active = defined $switches{trace}?$switches{trace}: 0;
+ $is_tt_switch_active = defined $switches{ttrace}?$switches{ttrace}: 0;
+ $is_t_switch_active = $is_tt_switch_active ? $is_tt_switch_active : $is_t_switch_active;
+ delete ${$self}{switches};
+ }
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/Tokens.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/Tokens.pm
new file mode 100644
index 00000000000..8a9a100dc26
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/Tokens.pm
@@ -0,0 +1,68 @@
+package LatexIndent::Tokens;
+# 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 Exporter qw/import/;
+use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+our @EXPORT_OK = qw/token_check %tokens/;
+
+# each of the tokens begins the same way -- this is exploited during the hidden Children routine
+my $beginningToken = "LTXIN-TK-";
+my $ifelsefiSpecial = "!-!";
+
+# the %tokens hash is passed around many modules
+our %tokens = (
+ environment=>$beginningToken."ENVIRONMENT",
+ ifelsefiSpecial=>$ifelsefiSpecial,
+ ifelsefi=>$ifelsefiSpecial.$beginningToken."IFELSEFI",
+ item=>$beginningToken."ITEMS",
+ trailingComment=>"latexindenttrailingcomment",
+ blanklines=>$beginningToken."blank-line",
+ arguments=>$beginningToken."ARGUMENTS",
+ optionalArgument=>$beginningToken."OPTIONAL-ARGUMENT",
+ mandatoryArgument=>$beginningToken."MANDATORY-ARGUMENT",
+ roundBracket=>$beginningToken."ROUND-BRACKET",
+ verbatim=>$beginningToken."VERBATIM",
+ command=>$beginningToken."COMMAND",
+ key_equals_values_braces=>$beginningToken."KEY-VALUE-BRACES",
+ groupingBraces=>$beginningToken."GROUPING-BRACES",
+ unNamedgroupingBraces=>$beginningToken."UN-NAMED-GROUPING-BRACES",
+ special=>$beginningToken."SPECIAL",
+ heading=>$beginningToken."HEADING",
+ filecontents=>$beginningToken."FILECONTENTS",
+ preamble=>$beginningToken."preamble",
+ beginOfToken=>$beginningToken,
+ doubleBackSlash=>$beginningToken."DOUBLEBACKSLASH",
+ alignmentBlock=>$beginningToken."ALIGNMENTBLOCK",
+ endOfToken=>"-END",
+ );
+
+sub token_check{
+ my $self = shift;
+
+ $self->logger("Token check",'heading') if $is_t_switch_active;
+ # we use tokens for trailing comments, environments, commands, etc, so check that they're not in the body
+ foreach( keys %tokens){
+ while(${$self}{body} =~ m/$tokens{$_}/si){
+ $self->logger("Found $tokens{$_} within body, updating replacement token to $tokens{$_}-LIN") if($is_t_switch_active);
+ $tokens{$_} .= "-LIN";
+ }
+ }
+}
+
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/TrailingComments.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/TrailingComments.pm
new file mode 100644
index 00000000000..f8987d271f1
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/TrailingComments.pm
@@ -0,0 +1,112 @@
+package LatexIndent::TrailingComments;
+# 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::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use Data::Dumper;
+use Exporter qw/import/;
+our @EXPORT_OK = qw/remove_trailing_comments put_trailing_comments_back_in $trailingCommentRegExp add_comment_symbol construct_trailing_comment_regexp/;
+our @trailingComments;
+our $commentCounter = 0;
+our $trailingCommentRegExp;
+
+sub construct_trailing_comment_regexp{
+ $trailingCommentRegExp = qr/(?<!\\)%$tokens{trailingComment}\d+$tokens{endOfToken}/;
+}
+
+sub add_comment_symbol{
+ # add a trailing comment token after, for example, a square brace [
+ # or a curly brace { when, for example, BeginStartsOnOwnLine == 2
+ my $self = shift;
+
+ # increment the comment counter
+ $commentCounter++;
+
+ # store the comment -- without this, it won't get processed correctly at the end
+ push(@trailingComments,{id=>$tokens{trailingComment}.$commentCounter.$tokens{endOfToken},value=>q()});
+
+ # log file info
+ $self->logger("Updating trailing comment array",'heading')if $is_t_switch_active;
+ $self->logger(Dumper(\@trailingComments),'ttrace') if($is_tt_switch_active);
+
+ # the returned value
+ return $tokens{trailingComment}.$commentCounter.$tokens{endOfToken};
+}
+
+sub remove_trailing_comments{
+ my $self = shift;
+ $self->logger("Storing trailing comments",'heading')if $is_t_switch_active;
+
+ # perform the substitution
+ ${$self}{body} =~ s/
+ (?<!\\) # not preceeded by a \
+ % # %
+ (
+ \h*? # followed by possible horizontal space
+ .*? # and anything else
+ )
+ $ # up to the end of a line
+ /
+ # increment comment counter and store comment
+ $commentCounter++;
+ push(@trailingComments,{id=>$tokens{trailingComment}.$commentCounter.$tokens{endOfToken},value=>$1});
+
+ # replace comment with dummy text
+ "%".$tokens{trailingComment}.$commentCounter.$tokens{endOfToken};
+ /xsmeg;
+ if(@trailingComments){
+ $self->logger("Trailing comments stored in:") if($is_t_switch_active);
+ $self->logger(Dumper(\@trailingComments)) if($is_t_switch_active);
+ } else {
+ $self->logger("No trailing comments found") if($is_t_switch_active);
+ }
+ return;
+}
+
+sub put_trailing_comments_back_in{
+ my $self = shift;
+ return unless( @trailingComments > 0 );
+
+ $self->logger("Returning trailing comments to body",'heading')if $is_t_switch_active;
+
+ # loop through trailing comments in reverse so that, for example,
+ # latexindenttrailingcomment1 doesn't match the first
+ # part of latexindenttrailingcomment18, which would result in an 8 left over (bad)
+ while( my $comment = pop @trailingComments){
+ my $trailingcommentID = ${$comment}{id};
+ my $trailingcommentValue = ${$comment}{value};
+ if(${$self}{body} =~ m/%$trailingcommentID
+ (
+ (?! # not immediately preceeded by
+ (?<!\\) # \
+ % # %
+ ).*?
+ ) # captured into $1
+ (\h*)?$
+ /mx and $1 ne ''){
+ $self->logger("Comment not at end of line $trailingcommentID, moving it to end of line")if $is_t_switch_active;
+ ${$self}{body} =~ s/%$trailingcommentID(.*)$/$1%$trailingcommentValue/m;
+ } else {
+ ${$self}{body} =~ s/%$trailingcommentID/%$trailingcommentValue/;
+ }
+ $self->logger("replace %$trailingcommentID with %$trailingcommentValue") if($is_t_switch_active);
+ }
+ return;
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/UnNamedGroupingBracesBrackets.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/UnNamedGroupingBracesBrackets.pm
new file mode 100644
index 00000000000..268f5b5adfa
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/UnNamedGroupingBracesBrackets.pm
@@ -0,0 +1,135 @@
+package LatexIndent::UnNamedGroupingBracesBrackets;
+# 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::TrailingComments qw/$trailingCommentRegExp/;
+use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use Exporter qw/import/;
+our @ISA = "LatexIndent::Command"; # class inheritance, Programming Perl, pg 321
+our @EXPORT_OK = qw/construct_unnamed_grouping_braces_brackets_regexp $un_named_grouping_braces_RegExp $un_named_grouping_braces_RegExp_trailing_comment/;
+our $unNamedGroupingBracesCounter;
+our $un_named_grouping_braces_RegExp;
+our $un_named_grouping_braces_RegExp_trailing_comment;
+
+sub construct_unnamed_grouping_braces_brackets_regexp{
+ my $self = shift;
+
+ # grab the arguments regexp
+ my $optAndMandRegExp = $self->get_arguments_regexp;
+
+ # blank line token
+ my $blankLineToken = $tokens{blanklines};
+
+ # for example #1 #2, etc
+ my $numberedArgRegExp = $self->get_numbered_arg_regexp;
+
+ # store the regular expresssion for matching and replacing
+ $un_named_grouping_braces_RegExp = qr/
+ # NOT
+ (?!
+ (?:
+ (?:(?<!\\)\])
+ |
+ (?:(?<!\\)\})
+ )
+ (?:\h|\R|$blankLineToken|$trailingCommentRegExp|$numberedArgRegExp)* # 0 or more h-space, blanklines, trailing comments
+ )
+ # END of NOT
+ (
+ (?:
+ \{|\[|,|&|\)|\(|\$ # starting with { OR [ OR , OR & OR ) OR ( OR $
+ )
+ \h*
+ ) # $1 into beginning bit
+ (\R*) # $2 linebreaksAtEnd of begin
+ ($optAndMandRegExp) # $3 mand|opt arguments (at least one) stored into body
+ (\R)? # $6 linebreak
+ /sx;
+
+ $un_named_grouping_braces_RegExp_trailing_comment = qr/$un_named_grouping_braces_RegExp(\h*)((?:$trailingCommentRegExp\h*)*)?/;
+}
+
+sub create_unique_id{
+ my $self = shift;
+
+ $unNamedGroupingBracesCounter++;
+ ${$self}{id} = "$tokens{unNamedgroupingBraces}$unNamedGroupingBracesCounter";
+ return;
+}
+
+sub get_replacement_text{
+ my $self = shift;
+
+ # the replacement text for a key = {value} needes to accomodate the leading [ OR { OR % OR , OR any combination thereof
+ $self->logger("Custom replacement text routine for ${$self}{name}") if $is_t_switch_active;
+
+ # the un-named object is a little special, as it doesn't have a name; as such, if there are blank lines before
+ # the braces/brackets, we have to insert them
+ #
+ # also, the argument reg-exp can pick up a leading comment (with line break), which needs to be put
+ # into the replacement text (see documentation/demonstrations/pstricks.tex and test-cases/unnamed-braces/unnamed.tex for example)
+ ${$self}{body} =~ s/(.*?)(\{|\[)/$2/s;
+ ${$self}{replacementText} = ${$self}{beginningbit}.($1 ne ''?$1:q()).${$self}{id};
+
+ # but now turn off the switch for linebreaksAtEnd{begin}, otherwise the first brace gets too much indentation
+ # (see, for example, test-cases/namedGroupingBracesBrackets/special-characters-minimal.tex)
+ ${${$self}{linebreaksAtEnd}}{begin} = 0;
+ $self->logger("Beginning bit is: ${$self}{beginningbit}") if($is_t_switch_active);
+ delete ${$self}{beginningbit};
+}
+
+sub check_for_blank_lines_at_beginning{
+ # some examples can have blank line tokens at the beginning of the body,
+ # which can confuse the routine below
+ # See, for example,
+ # test-cases/namedGroupingBracesBrackets/special-characters-minimal-blank-lines-m-switch.tex
+ # compared to
+ # test-cases/namedGroupingBracesBrackets/special-characters-minimal-blank-lines-default.tex
+ my $self = shift;
+
+ # blank line token
+ my $blankLineToken = $tokens{blanklines};
+
+ # if the body begins with 2 or more blank line tokens
+ if(${$self}{body} =~ m/^((?:$blankLineToken\R){2,})/s){
+
+ # remove them
+ ${$self}{body} =~ s/^((?:$blankLineToken\R)+)//s;
+
+ # store
+ my $blank_line_tokens_at_beginning_of_body = $1;
+
+ # and count them, for use after the indentation routine
+ ${$self}{blankLinesAtBeginning} = () = $blank_line_tokens_at_beginning_of_body =~ /$blankLineToken\R/sg
+ }
+ return;
+}
+
+sub put_blank_lines_back_in_at_beginning{
+ my $self = shift;
+
+ # some bodies have blank lines at the beginning
+ if(${$self}{blankLinesAtBeginning}){
+ for(my $i=0; $i<${$self}{blankLinesAtBeginning}; $i++){
+ ${$self}{body} = $tokens{blanklines}.${$self}{body};
+ }
+ }
+ return
+}
+
+1;
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/Verbatim.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/Verbatim.pm
new file mode 100644
index 00000000000..f3804aaf94d
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/Verbatim.pm
@@ -0,0 +1,296 @@
+package LatexIndent::Verbatim;
+# 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/%masterSettings/;
+use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+use Data::Dumper;
+use Exporter qw/import/;
+our @EXPORT_OK = qw/put_verbatim_back_in find_verbatim_environments find_noindent_block find_verbatim_commands put_verbatim_commands_back_in/;
+our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321
+our $verbatimCounter;
+
+sub find_noindent_block{
+ my $self = shift;
+
+ # noindent block
+ $self->logger('looking for NOINDENTBLOCk environments (see noIndentBlock)','heading') if $is_t_switch_active;
+ $self->logger(Dumper(\%{$masterSettings{noIndentBlock}})) if($is_t_switch_active);
+ while( my ($noIndentBlock,$yesno)= each %{$masterSettings{noIndentBlock}}){
+ if($yesno){
+ $self->logger("looking for $noIndentBlock:$yesno environments") if $is_t_switch_active;
+
+ my $noIndentRegExp = qr/
+ (
+ (?!<\\)
+ %
+ \h* # possible horizontal spaces
+ \\begin\{
+ $noIndentBlock # environment name captured into $2
+ \} # %* \begin{noindentblock} statement
+ )
+ (
+ .*?
+ ) # non-greedy match (body)
+ (
+ (?!<\\)
+ % # %
+ \h* # possible horizontal spaces
+ \\end\{$noIndentBlock\} # \end{noindentblock}
+ ) # %* \end{<something>} statement
+ /sx;
+
+ while( ${$self}{body} =~ m/$noIndentRegExp/sx){
+
+ # create a new Environment object
+ my $noIndentBlock = LatexIndent::Verbatim->new( begin=>$1,
+ body=>$2,
+ end=>$3,
+ name=>$noIndentBlock,
+ );
+
+ # give unique id
+ $noIndentBlock->create_unique_id;
+
+ # verbatim children go in special hash
+ ${$self}{verbatim}{${$noIndentBlock}{id}}=$noIndentBlock;
+
+ # log file output
+ $self->logger("NOINDENTBLOCK environment found: $noIndentBlock") if $is_t_switch_active;
+
+ # remove the environment block, and replace with unique ID
+ ${$self}{body} =~ s/$noIndentRegExp/${$noIndentBlock}{id}/sx;
+
+ $self->logger("replaced with ID: ${$noIndentBlock}{id}") if $is_t_switch_active;
+ }
+ } else {
+ $self->logger("*not* looking for $noIndentBlock as $noIndentBlock:$yesno") if $is_t_switch_active;
+ }
+ }
+ return;
+}
+
+sub find_verbatim_environments{
+ my $self = shift;
+
+ # verbatim environments
+ $self->logger('looking for VERBATIM environments (see verbatimEnvironments)','heading') if $is_t_switch_active;
+ $self->logger(Dumper(\%{$masterSettings{verbatimEnvironments}})) if($is_t_switch_active);
+ while( my ($verbEnv,$yesno)= each %{$masterSettings{verbatimEnvironments}}){
+ if($yesno){
+ $self->logger("looking for $verbEnv:$yesno environments") if $is_t_switch_active;
+
+ my $verbatimRegExp = qr/
+ (
+ \\begin\{
+ $verbEnv # environment name captured into $1
+ \} # \begin{<something>} statement
+ )
+ (
+ .*?
+ ) # any character, but not \\begin
+ (
+ \\end\{$verbEnv\}# \end{<something>} statement
+ )
+ /sx;
+
+ while( ${$self}{body} =~ m/$verbatimRegExp/sx){
+
+ # create a new Environment object
+ my $verbatimBlock = LatexIndent::Verbatim->new( begin=>$1,
+ body=>$2,
+ end=>$3,
+ name=>$verbEnv,
+ );
+ # give unique id
+ $verbatimBlock->create_unique_id;
+
+ # verbatim children go in special hash
+ ${$self}{verbatim}{${$verbatimBlock}{id}}=$verbatimBlock;
+
+ # log file output
+ $self->logger("VERBATIM environment found: $verbEnv") if $is_t_switch_active;
+
+ # remove the environment block, and replace with unique ID
+ ${$self}{body} =~ s/$verbatimRegExp/${$verbatimBlock}{id}/sx;
+
+ $self->logger("replaced with ID: ${$verbatimBlock}{id}") if $is_t_switch_active;
+ }
+ } else {
+ $self->logger("*not* looking for $verbEnv as $verbEnv:$yesno") if $is_t_switch_active;
+ }
+ }
+ return;
+}
+
+sub find_verbatim_commands{
+ my $self = shift;
+
+ # verbatim commands need to be treated separately to verbatim environments;
+ # note that, for example, we could quite reasonably have \lstinline!%!, which
+ # would need to be found *before* trailing comments have been removed. Similarly,
+ # verbatim commands need to be put back in *after* trailing comments have been put
+ # back in
+ $self->logger('looking for VERBATIM commands (see verbatimCommands)','heading');
+ $self->logger(Dumper(\%{$masterSettings{verbatimCommands}})) if($is_tt_switch_active);
+ while( my ($verbCommand,$yesno)= each %{$masterSettings{verbatimCommands}}){
+ if($yesno){
+ $self->logger("looking for $verbCommand:$yesno Commands") if $is_t_switch_active;
+
+ my $verbatimCommandRegExp = qr/
+ (
+ \\$verbCommand
+ \h*
+ ) # name of command into $1
+ (
+ \[
+ (?:
+ (?!
+ (?:(?<!\\)\[)
+ ).
+ )*? # not including [, but \[ ok
+ (?<!\\) # not immediately pre-ceeded by \
+ \] # [optional arguments]
+ \h*
+ )? # opt arg into $2
+ (
+ .
+ ) # delimiter into $3
+ (
+ .*?
+ ) # body into $4
+ \3
+ /mx;
+
+ while( ${$self}{body} =~ m/$verbatimCommandRegExp/){
+
+ # create a new Environment object
+ my $verbatimCommand = LatexIndent::Verbatim->new( begin=>$1.($2?$2:q()).$3,
+ body=>$4,
+ end=>$3,
+ name=>$verbCommand,
+ optArg=>$2?$2:q(),
+ );
+ # give unique id
+ $verbatimCommand->create_unique_id;
+
+ # output, if desired
+ $self->logger(Dumper($verbatimCommand),'ttrace') if($is_tt_switch_active);
+
+ # verbatim children go in special hash
+ ${$self}{verbatimCommands}{${$verbatimCommand}{id}}=$verbatimCommand;
+
+ # log file output
+ $self->logger("VERBATIM command found: $verbCommand") if $is_t_switch_active;
+
+ # remove the environment block, and replace with unique ID
+ ${$self}{body} =~ s/$verbatimCommandRegExp/${$verbatimCommand}{id}/sx;
+
+ $self->logger("replaced with ID: ${$verbatimCommand}{id}") if $is_t_switch_active;
+ }
+ } else {
+ $self->logger("*not* looking for $verbCommand as $verbCommand:$yesno") if $is_t_switch_active;
+ }
+ }
+ return;
+
+}
+
+sub put_verbatim_back_in {
+ my $self = shift;
+
+ # if there are no verbatim children, return
+ return unless(${$self}{verbatim});
+
+ # search for environments/commands
+ $self->logger('Putting verbatim back in, here is the pre-processed body:','heading') if $is_t_switch_active;
+ $self->logger(${$self}{body}) if($is_t_switch_active);
+
+ # loop through document children hash
+ while( (scalar keys %{${$self}{verbatim}})>0 ){
+ while( my ($key,$child)= each %{${$self}{verbatim}}){
+ if(${$self}{body} =~ m/${$child}{id}/mx){
+
+ # replace ids with body
+ ${$self}{body} =~ s/${$child}{id}/${$child}{begin}${$child}{body}${$child}{end}/;
+
+ # log file info
+ $self->logger('Body now looks like:','heading') if $is_tt_switch_active;
+ $self->logger(${$self}{body},'ttrace') if($is_tt_switch_active);
+
+ # delete the hash so it won't be operated upon again
+ delete ${$self}{verbatim}{${$child}{id}};
+ $self->logger("deleted key") if $is_t_switch_active;
+ }
+ }
+ }
+
+ # logfile info
+ $self->logger("Number of children:",'heading') if $is_t_switch_active;
+ $self->logger(scalar keys %{${$self}{verbatim}}) if $is_t_switch_active;
+ $self->logger('Post-processed body:','heading') if $is_t_switch_active;
+ $self->logger(${$self}{body}) if($is_t_switch_active);
+ return;
+}
+
+sub put_verbatim_commands_back_in {
+ my $self = shift;
+
+ # if there are no verbatim children, return
+ return unless(${$self}{verbatimCommands});
+
+ # search for environments/commands
+ $self->logger('Putting verbatim commands back in, here is the pre-processed body:','heading') if $is_t_switch_active;
+ $self->logger(${$self}{body}) if($is_t_switch_active);
+
+ # loop through document children hash
+ while( (scalar keys %{${$self}{verbatimCommands}})>0 ){
+ while( my ($key,$child)= each %{${$self}{verbatimCommands}}){
+ if(${$self}{body} =~ m/${$child}{id}/mx){
+
+ # replace ids with body
+ ${$self}{body} =~ s/${$child}{id}/${$child}{begin}${$child}{body}${$child}{end}/;
+
+ # log file info
+ $self->logger('Body now looks like:','heading') if $is_tt_switch_active;
+ $self->logger(${$self}{body},'ttrace') if($is_tt_switch_active);
+
+ # delete the hash so it won't be operated upon again
+ delete ${$self}{verbatimCommands}{${$child}{id}};
+ $self->logger("deleted key") if $is_t_switch_active;
+ }
+ }
+ }
+
+ # logfile info
+ $self->logger("Number of children:",'heading');
+ $self->logger(scalar keys %{${$self}{verbatimCommands}});
+ $self->logger('Post-processed body:','heading') if $is_t_switch_active;
+ $self->logger(${$self}{body}) if($is_t_switch_active);
+ return;
+}
+
+sub create_unique_id{
+ my $self = shift;
+
+ $verbatimCounter++;
+ ${$self}{id} = "$tokens{verbatim}$verbatimCounter$tokens{endOfToken}";
+ return;
+}
+
+1;