summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/latexindent
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2017-02-24 22:07:42 +0000
committerKarl Berry <karl@freefriends.org>2017-02-24 22:07:42 +0000
commit990e60c6ed4437e5b632b7c9acf7f2237bbda871 (patch)
tree4dc9ea04f5fa46cc8ac59faa8dd62e4f8bc6835b /Master/texmf-dist/scripts/latexindent
parent2e70942b44a972908c3180dd7a3c68be51afc466 (diff)
latexindent (23feb17)
git-svn-id: svn://tug.org/texlive/trunk@43326 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts/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
-rwxr-xr-xMaster/texmf-dist/scripts/latexindent/defaultSettings.yaml456
-rwxr-xr-xMaster/texmf-dist/scripts/latexindent/latexindent.pl2132
34 files changed, 5933 insertions, 2269 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;
diff --git a/Master/texmf-dist/scripts/latexindent/defaultSettings.yaml b/Master/texmf-dist/scripts/latexindent/defaultSettings.yaml
index ef620846f4f..20040817b12 100755
--- a/Master/texmf-dist/scripts/latexindent/defaultSettings.yaml
+++ b/Master/texmf-dist/scripts/latexindent/defaultSettings.yaml
@@ -1,5 +1,5 @@
#
-# defaultSettings.yaml for latexindent.pl,
+# defaultSettings.yaml for latexindent.pl, version 3.0
# a script that aims to
# beautify .tex, .sty, .cls files
#
@@ -13,8 +13,17 @@
#
# Please read the manual first to understand what each switch does :)
-# Default value of indentation
-defaultIndent: "\t"
+# latexindent can be called without a file extension,
+# e.g, simply
+# latexindent myfile
+# in which case the choice of file extension is chosen
+# according to the choices made in fileExtensionPreference
+# Other file extensions can be added.
+fileExtensionPreference:
+ .tex: 1
+ .sty: 2
+ .cls: 3
+ .bib: 4
# default file extension of backup file (if original is overwritten with -w switch)
# for example, if your .tex file is called
@@ -50,19 +59,62 @@ maxNumberOfBackUps: 0
# the back up will be written to myfile.bak4
cycleThroughBackUps: 0
+# preferences for information displayed in the log file
+logFilePreferences:
+ showEveryYamlRead: 1
+ showAmalgamatedSettings: 0
+ endLogFileWith: '--------------'
+ showGitHubInfoFooter: 1
+
+# verbatim environments- environments specified
+# in this hash table will not be changed at all!
+verbatimEnvironments:
+ verbatim: 1
+ lstlisting: 1
+
+# verbatim commands such as \verb! body !, \lstinline$something else$
+verbatimCommands:
+ verb: 1
+ lstinline: 1
+
+# no indent blocks (not necessarily verbatim
+# environments) which are marked as %\begin{noindent}
+# or anything else that the user puts in this hash
+# table
+noIndentBlock:
+ noindent: 1
+ cmhtest: 1
+
+# remove trailing whitespace from all lines
+removeTrailingWhitespace:
+ beforeProcessing: 0
+ afterProcessing: 1
+
+# \begin{document} and \end{document} are treated differently
+# by latexindent within filecontents environments
+fileContentsEnvironments:
+ filecontents: 1
+ filecontents*: 1
+
# indent preamble
indentPreamble: 0
-# always look for split { }, which means that the user doesn't
-# have to complete checkunmatched, checkunmatchedELSE
-alwaysLookforSplitBraces: 1
+# assume no preamble in cls, sty, by default
+lookForPreamble:
+ .tex: 1
+ .sty: 0
+ .cls: 0
+ .bib: 0
-# always look for split [ ], which means that the user doesn't
-# have to complete checkunmatchedbracket
-alwaysLookforSplitBrackets: 1
+# some preambles can contain \begin and \end statements
+# that are not in their 'standard environment block', for example,
+# consider the following key = values:
+# preheadhook={\begin{mdframed}[style=myframedstyle]},
+# postfoothook=\end{mdframed},
+preambleCommandsBeforeEnvironments: 0
-# remove trailing whitespace from all lines
-removeTrailingWhitespace: 0
+# Default value of indentation
+defaultIndent: "\t"
# environments that have tab delimiters, add more
# as needed
@@ -91,48 +143,43 @@ lookForAlignDelims:
dcases: 1
listabla: 1
-# if you have indent rules for particular environments
-# or commands, put them in here; for example, you might just want
-# to use a space " " or maybe a double tab "\t\t"
-indentRules:
- myenvironment: "\t\t"
- anotherenvironment: "\t\t\t\t"
- chapter: " "
- section: " "
- item: " "
-
-# verbatim environments- environments specified
-# in this hash table will not be changed at all!
-verbatimEnvironments:
- verbatim: 1
- lstlisting: 1
+# if you want the script to look for \item commands
+# and format it, as follows (for example),
+# \begin{itemize}
+# \item content here
+# next line is indented
+# next line is indented
+# \item another item
+# \end{itemize}
+# then populate indentAfterItems. See also itemNames
+indentAfterItems:
+ itemize: 1
+ enumerate: 1
+ list: 1
-# no indent blocks (not necessarily verbatim
-# environments) which are marked as %\begin{noindent}
-# or anything else that the user puts in this hash
-# table
-noIndentBlock:
- noindent: 1
- cmhtest: 1
+# if you want to use other names for your items (such as, for example, part)
+# then populate them here- note that you can trick latexindent.pl
+# into indenting all kinds of commands (within environments specified in
+# indentAfterItems) using this technique.
+itemNames:
+ item: 1
+ myitem: 1
-# if you don't want to have additional indentation
-# in an environment put it in this hash table; note that
-# environments in this hash table will inherit
-# the *current* level of indentation they just won't
-# get any *additional*.
-noAdditionalIndent:
- myexample: 1
- mydefinition: 1
- problem: 1
- exercises: 1
- mysolution: 1
- foreach: 0
- widepage: 1
- comment: 1
- \[: 0
- \]: 0
- document: 1
- frame: 0
+# specialBeginEnd is mainly mathmode focus, although
+# there's no restrictions
+specialBeginEnd:
+ displayMath:
+ begin: '\\\['
+ end: '\\\]'
+ lookForThis: 1
+ inlineMath:
+ begin: '(?<!\$)(?<!\\)\$(?!\$)'
+ end: '(?<!\\)\$(?!\$)'
+ lookForThis: 1
+ displayMathTeX:
+ begin: '\$\$'
+ end: '\$\$'
+ lookForThis: 1
# if you want to add indentation after
# a heading, such as \part, \chapter, etc
@@ -144,139 +191,220 @@ noAdditionalIndent:
# or add your own title command
indentAfterHeadings:
part:
- indent: 0
+ indentAfterThisHeading: 0
level: 1
chapter:
- indent: 0
+ indentAfterThisHeading: 0
level: 2
section:
- indent: 0
+ indentAfterThisHeading: 0
level: 3
subsection:
- indent: 0
+ indentAfterThisHeading: 0
level: 4
subsection*:
- indent: 0
+ indentAfterThisHeading: 0
level: 4
subsubsection:
- indent: 0
+ indentAfterThisHeading: 0
level: 5
paragraph:
- indent: 0
+ indentAfterThisHeading: 0
level: 6
subparagraph:
- indent: 0
+ indentAfterThisHeading: 0
level: 7
-# if you want the script to look for \item commands
-# and format it, as follows (for example),
-# \begin{itemize}
-# \item content here
-# next line is indented
-# next line is indented
-# \item another item
-# \end{itemize}
-# then populate indentAfterItems. See also itemNames
-indentAfterItems:
- itemize: 1
- enumerate: 1
- list: 1
-
-# if you want to use other names for your items (such as, for example, part)
-# then populate them here- note that you can trick latexindent.pl
-# into indenting all kinds of commands (within environments specified in
-# indentAfterItems) using this technique.
-itemNames:
- item: 1
- myitem: 1
+# if you don't want to have additional indentation
+# in an environment put it in this hash table; note that
+# environments in this hash table will inherit
+# the *current* level of indentation they just won't
+# get any *additional*.
+noAdditionalIndent:
+ myexample: 1
+ mydefinition: 1
+ problem: 1
+ exercises: 1
+ mysolution: 1
+ foreach: 0
+ widepage: 1
+ comment: 1
+ document: 1
+ frame: 0
-# if you want to indent if, else, fi constructs such as, for example,
-#
-# \ifnum#1=2
-# something
-# \else
-# something else
-# \fi
-#
-# then populate them in constructIfElseFi
-constructIfElseFi:
- ifnum: 1
- ifdim: 1
- ifodd: 1
- ifvmode: 1
- ifhmode: 1
- ifmmode: 1
- ifinner: 1
- if: 1
- ifcat: 1
- ifx: 1
- ifvoid: 1
- ifeof: 1
- iftrue: 1
- ifcase: 1
+# if you have indent rules for particular environments
+# or commands, put them in here; for example, you might just want
+# to use a space " " or maybe a double tab "\t\t"
+indentRules:
+ myenvironment: "\t\t"
+ anotherenvironment: "\t\t\t\t"
+ chapter: " "
+ section: " "
+ item: " "
+ myitem: " "
-# latexindent can be called without a file extension,
-# e.g, simply
-# latexindent myfile
-# in which case the choice of file extension is chosen
-# according to the choices made in fileExtensionPreference
-# Other file extensions can be added.
-fileExtensionPreference:
- .tex: 1
- .sty: 2
- .cls: 3
- .bib: 4
+# set noAdditionalIndent globally for codeblocks
+noAdditionalIndentGlobal:
+ environments: 0
+ commands: 1
+ optionalArguments: 0
+ mandatoryArguments: 0
+ ifElseFi: 0
+ items: 0
+ keyEqualsValuesBracesBrackets: 0
+ namedGroupingBracesBrackets: 0
+ UnNamedGroupingBracesBrackets: 0
+ specialBeginEnd: 0
+ afterHeading: 0
+ filecontents: 0
-# preferences for information displayed in the log file
-logFilePreferences:
- showEveryYamlRead: 1
- showAlmagamatedSettings: 0
- endLogFileWith: '--------------'
- traceModeIncreaseIndent: '>>'
- traceModeAddCurrentIndent: '||'
- traceModeDecreaseIndent: '<<'
- traceModeBetweenLines: "\n"
+# set indentRules globally for codeblocks; these need
+# to be horizontal spaces, if they are to be used
+indentRulesGlobal:
+ environments: 0
+ commands: 0
+ optionalArguments: 0
+ mandatoryArguments: 0
+ ifElseFi: 0
+ items: 0
+ keyEqualsValuesBracesBrackets: 0
+ namedGroupingBracesBrackets: 0
+ UnNamedGroupingBracesBrackets: 0
+ specialBeginEnd: 0
+ afterHeading: 0
+ filecontents: 0
-# \begin{document} and \end{document} are treated differently
-# by latexindent within filecontents environments
-fileContentsEnvironments:
- filecontents: 1
- filecontents*: 1
+# command code block details
+commandCodeBlocks:
+ roundParenthesesAllowed: 1
+ stringsAllowedBetweenArguments:
+ node: 1
+ at: 1
+ to: 1
+ decoration: 1
+ ++: 1
+ --: 1
-
-# *** NOTE ***
-# If you have specified alwaysLookforSplitBraces: 1
-# and alwaysLookforSplitBrackets: 1 then you don't need
-# to worry about completing
+# modifyLineBreaks will only be searched if the -m
+# switch is active
+# BeginStartsOnOwnLine: 1
+# if a begin statement is not already on its own line,
+# then, with -m active, it will be moved to its own line.
#
-# checkunmatched
-# checkunmatchedELSE
-# checkunmatchedbracket
+# when set to 1, e.g
+# some text some text \begin{myenvironment}
+# will be changed to
+# some text some text
+# \begin{myenvironment}
+# when set to -1, e.g
+# some text some text
+# \begin{myenvironment}
+# will be changed to
+# some text some text \begin{myenvironment}
+# when set to 0, the switch is ignored
#
-# in other words, you don't really need to edit anything
-# below this line- it used to be necessary for older
-# versions of the script, but not anymore :)
-#*** ***
-
-# commands that might split {} across lines
-# such as \parbox, \marginpar, etc
-checkunmatched:
- parbox: 1
- vbox: 1
-
-# very similar to %checkunmatched except these
-# commands might have an else construct
-checkunmatchedELSE:
- pgfkeysifdefined: 1
- DTLforeach: 1
- ifthenelse: 1
-
-# commands that might split [] across lines
-# such as \pgfplotstablecreatecol, etc
-checkunmatchedbracket:
- pgfplotstablecreatecol: 1
- pgfplotstablesave: 1
- pgfplotstabletypeset: 1
- mycommand: 1
- psSolid: 1
-
+# BodyStartsOnOwnLine: 1
+# if body does not already start on its own line, then with
+# -m active, it will be moved to its own line.
+#
+# when set to 1, e.g
+# \begin{myenv}body text body text
+# will be changed to
+# \begin{myenv}
+# body text body text
+# when set to -1, e.g
+# \begin{myenv}
+# body text body text
+# will be changed to
+# \begin{myenv}body text body text
+# when set to 0, the switch is ignored
+#
+# EndStartsOnOwnLine: 1
+# if an end statement is not already on its own line,
+# then, with -m active, it will be moved to its own line.
+#
+# when set to 1, e.g
+# some text some text \end{myenvironment}
+# will be changed to
+# some text some text
+# \end{myenvironment}
+# when set to -1, e.g
+# some text some text
+# \end{myenvironment}
+# will be changed to
+# some text some text \end{myenvironment}
+# when set to 0, the switch is ignored
+#
+# EndFinishesWithLineBreak: 1
+# this switch adjusts line breaks immmediately
+# after an end statement (with -m active)
+#
+# when set to 1, e.g
+# \end{myenvironment}some text some text
+# will be changed to
+# \end{myenvironment}
+# some text some text
+# when set to -1, e.g
+# \end{myenvironment}
+# some text some text
+# will be changed to
+# \end{myenvironment}some text some text
+# when set to 0, the switch is ignored
+#
+# Naturally, you can specify settings for individual environments,
+# commands, headings, etc, populate as you wish.
+modifyLineBreaks:
+ preserveBlankLines: 1
+ condenseMultipleBlankLinesInto: 1
+ environments:
+ BeginStartsOnOwnLine: 0
+ BodyStartsOnOwnLine: 0
+ EndStartsOnOwnLine: 0
+ EndFinishesWithLineBreak: 0
+ equation*:
+ BeginStartsOnOwnLine: 0
+ BodyStartsOnOwnLine: 0
+ EndStartsOnOwnLine: 0
+ EndFinishesWithLineBreak: 0
+ ifElseFi:
+ IfStartsOnOwnLine: 0
+ BodyStartsOnOwnLine: 0
+ ElseStartsOnOwnLine: 0
+ ElseFinishesWithLineBreak: 0
+ FiStartsOnOwnLine: 0
+ FiFinishesWithLineBreak: 0
+ ifnum:
+ IfStartsOnOwnLine: 0
+ BodyStartsOnOwnLine: 0
+ ElseStartsOnOwnLine: 0
+ ElseFinishesWithLineBreak: 0
+ FiStartsOnOwnLine: 0
+ FiFinishesWithLineBreak: 0
+ commands:
+ CommandStartsOnOwnLine: 0
+ CommandNameFinishesWithLineBreak: 0
+ optionalArguments:
+ LSqBStartsOnOwnLine: 0
+ OptArgBodyStartsOnOwnLine: 0
+ RSqBStartsOnOwnLine: 0
+ RSqBFinishesWithLineBreak: 0
+ mandatoryArguments:
+ LCuBStartsOnOwnLine: 0
+ MandArgBodyStartsOnOwnLine: 0
+ RCuBStartsOnOwnLine: 0
+ RCuBFinishesWithLineBreak: 0
+ keyEqualsValuesBracesBrackets:
+ KeyStartsOnOwnLine: 0
+ EqualsStartsOnOwnLine: 0
+ EqualsFinishesWithLineBreak: 0
+ items:
+ ItemStartsOnOwnLine: 0
+ ItemFinishesWithLineBreak: 0
+ namedGroupingBracesBrackets:
+ NameStartsOnOwnLine: 0
+ NameFinishesWithLineBreak: 0
+ specialBeginEnd:
+ SpecialBeginStartsOnOwnLine: 0
+ SpecialBodyStartsOnOwnLine: 0
+ SpecialEndStartsOnOwnLine: 0
+ SpecialEndFinishesWithLineBreak: 0
diff --git a/Master/texmf-dist/scripts/latexindent/latexindent.pl b/Master/texmf-dist/scripts/latexindent/latexindent.pl
index cdd979a8ccf..06adc58a1b8 100755
--- a/Master/texmf-dist/scripts/latexindent/latexindent.pl
+++ b/Master/texmf-dist/scripts/latexindent/latexindent.pl
@@ -11,57 +11,34 @@
#
# See http://www.gnu.org/licenses/.
#
-# For details of how to use this file, please see readme.txt
+# Chris Hughes, 2017
+#
+# For all communication, please visit: https://github.com/cmhughes/latexindent.pl
-# load packages/modules: assume strict and warnings are part of every perl distribution
use strict;
use warnings;
+use FindBin; # help find defaultSettings.yaml
+use Getopt::Long; # to get the switches/options/flags
-# list of modules
-my @listOfModules = ('FindBin','YAML::Tiny','File::Copy','File::Basename','Getopt::Long','File::HomeDir');
-
-# check the other modules are available
-foreach my $moduleName (@listOfModules) {
- # references:
- # http://stackoverflow.com/questions/251694/how-can-i-check-if-i-have-a-perl-module-before-using-it
- # http://stackoverflow.com/questions/1917261/how-can-i-dynamically-include-perl-modules-without-using-eval
- eval {
- (my $file = $moduleName) =~ s|::|/|g;
- require $file . '.pm';
- $moduleName->import();
- 1;
- } or die "$moduleName Perl Module not currently installed; please install the module, and then try running latexindent.pl again; exiting";
-}
-
-# now that we have confirmed the modules are available, load them
-use FindBin; # help find defaultSettings.yaml
-use YAML::Tiny; # interpret defaultSettings.yaml and other potential settings files
-use File::Copy; # to copy the original file to backup (if overwrite option set)
-use File::Basename; # to get the filename and directory path
-use Getopt::Long; # to get the switches/options/flags
-use File::HomeDir; # to get users home directory, regardless of OS
+# use lib to make sure that @INC contains the latexindent directory
+use lib $FindBin::RealBin;
+use LatexIndent::Document;
# get the options
-my $overwrite;
-my $outputToFile;
-my $silentMode;
-my $tracingMode;
-my $tracingModeVeryDetailed;
-my $readLocalSettings=0;
-my $onlyDefault;
-my $showhelp;
-my $cruftDirectory;
+my %switches = (readLocalSettings=>0);
GetOptions (
- "overwrite|w"=>\$overwrite,
-"outputfile|o"=>\$outputToFile,
-"silent|s"=>\$silentMode,
-"trace|t"=>\$tracingMode,
-"ttrace|tt"=>\$tracingModeVeryDetailed,
-"local|l:s"=>\$readLocalSettings,
-"onlydefault|d"=>\$onlyDefault,
-"help|h"=>\$showhelp,
-"cruft|c=s"=>\$cruftDirectory,
+ "silent|s"=>\$switches{silentMode},
+ "trace|t"=>\$switches{trace},
+ "ttrace|tt"=>\$switches{ttrace},
+ "local|l:s"=>\$switches{readLocalSettings},
+ "onlydefault|d"=>\$switches{onlyDefault},
+ "overwrite|w"=>\$switches{overwrite},
+ "outputfile|o=s"=>\$switches{outputToFile},
+ "modifylinebreaks|m"=>\$switches{modifyLineBreaks},
+ "logfile|g=s"=>\$switches{logFileName},
+ "help|h"=>\$switches{showhelp},
+ "cruft|c=s"=>\$switches{cruftDirectory},
);
# check local settings doesn't interfer with reading the file;
@@ -74,20 +51,9 @@ GetOptions (
#
# In such circumstances, we correct the mistake by assuming that
# the only argument is the file to be indented, and place it in @ARGV
-if($readLocalSettings and scalar(@ARGV) < 1) {
- push(@ARGV,$readLocalSettings);
- $readLocalSettings = '';
-}
-
-# this can also happen if the script is called as
-#
-# latexindent.pl -o -l myfile.tex outputfile.tex
-#
-# in which case, the GetOptions routine mistakes myfile.tex
-# as the optional parameter to the l flag.
-if($readLocalSettings and scalar(@ARGV) < 2 and $outputToFile) {
- unshift(@ARGV,$readLocalSettings);
- $readLocalSettings = '';
+if($switches{readLocalSettings} and scalar(@ARGV) < 1) {
+ push(@ARGV,$switches{readLocalSettings});
+ $switches{readLocalSettings} = '';
}
# default value of readLocalSettings
@@ -95,2054 +61,10 @@ if($readLocalSettings and scalar(@ARGV) < 2 and $outputToFile) {
# latexindent -l myfile.tex
#
# means that we wish to use localSettings.yaml
-if(defined($readLocalSettings) and ($readLocalSettings eq '')){
- $readLocalSettings = 'localSettings.yaml';
-}
-
-# detailed tracing mode also implies regular tracing mode
-$tracingMode = $tracingModeVeryDetailed ? 1 : $tracingMode;
-
-# version number
-my $versionNumber = "2.2";
-
-# Check the number of input arguments- if it is 0 then simply
-# display the list of options (like a manual)
-if(scalar(@ARGV) < 1 or $showhelp) {
- print <<ENDQUOTE
-latexindent.pl version $versionNumber
-usage: latexindent.pl [options] [file][.tex]
- -h, --help
- help (see the documentation for detailed instructions and examples)
- -o, --outputfile
- output to another file; sample usage
- latexindent.pl -o myfile.tex outputfile.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
- -d, --onlydefault
- ONLY use defaultSettings.yaml, ignore ALL user files
- -c, --cruft=<cruft directory>
- used to specify the location of backup files and indent.log
-ENDQUOTE
- ;
- exit(2);
-}
-
-# set up default for cruftDirectory using the one from the input file,
-# unless it has been specified using -c="/some/directory"
-$cruftDirectory=dirname $ARGV[0] unless(defined($cruftDirectory));
-
-die "Could not find directory $cruftDirectory\nExiting, no indentation done." if(!(-d $cruftDirectory));
-
-# we'll be outputting to the logfile and to standard output
-my $logfile;
-my $out = *STDOUT;
-
-# open the log file
-open($logfile,">","$cruftDirectory/indent.log") or die "Can't open indent.log";
-
-# output time to log file
-my $time = localtime();
-print $logfile $time;
-
-# output version to log file
-print $logfile <<ENDQUOTE
-
-$FindBin::Script version $versionNumber, a script to indent .tex files
-$FindBin::Script lives here: $FindBin::RealBin/
-
-ENDQUOTE
-;
-
-# latexindent.exe is a standalone executable, and caches
-# the required perl modules onto the users system; they will
-# only be displayed if the user specifies the trace option
-if($FindBin::Script eq 'latexindent.exe' and !$tracingMode ) {
-print $logfile <<ENDQUOTE
-$FindBin::Script is a standalone script and caches the required perl modules
-onto your system. If you'd like to see their location in your log file, indent.log,
-call the script with the tracing option, e.g latexindent.exe -t myfile.tex
-
-ENDQUOTE
-;
-}
-
-# output location of modules
-if($FindBin::Script eq 'latexindent.pl' or ($FindBin::Script eq 'latexindent.exe' and $tracingMode )) {
- print $logfile "Modules are being loaded from the following directories:\n ";
- foreach my $moduleName (@listOfModules) {
- (my $file = $moduleName) =~ s|::|/|g;
- require $file . '.pm';
- print $logfile "\t",$INC{$file .'.pm'},"\n";
- }
-}
-
-# a quick options check
-if($outputToFile and $overwrite) {
- print $logfile <<ENDQUOTE
-
-WARNING:
-\t You have called latexindent.pl with both -o and -w
-\t -o (output to file) will take priority, and -w (over write) will be ignored
-
-ENDQUOTE
-;
- $overwrite = 0;
-}
-
-# can't call the script with MORE THAN 2 files
-if(scalar(@ARGV)>2) {
- for my $fh ($out,$logfile) {print $fh <<ENDQUOTE
-
-ERROR:
-\t You're calling latexindent.pl with more than two file names
-\t The script can take at MOST two file names, but you
-\t need to call it with the -o switch; for example
-
-\t latexindent.pl -o originalfile.tex outputfile.tex
-
-No indentation done :(
-Exiting...
-ENDQUOTE
- };
- exit(2);
-}
-
-# don't call the script with 2 files unless the -o flag is active
-if(!$outputToFile and scalar(@ARGV)==2)
-{
-for my $fh ($out,$logfile) {
-print $fh <<ENDQUOTE
-
-ERROR:
-\t You're calling latexindent.pl with two file names, but not the -o flag.
-\t Did you mean to use the -o flag ?
-
-No indentation done :(
-Exiting...
-ENDQUOTE
-};
- exit(2);
-}
-
-# if the script is called with the -o switch, then check that
-# a second file is present in the call, e.g
-# latexindent.pl -o myfile.tex output.tex
-if($outputToFile and scalar(@ARGV)==1) {
- for my $fh ($out,$logfile) {print $fh <<ENDQUOTE
-ERROR: When using the -o flag you need to call latexindent.pl with 2 arguments
-
-latexindent.pl -o "$ARGV[0]" [needs another name here]
-
-No indentation done :(
-Exiting...
-ENDQUOTE
-};
- exit(2);
+if(defined($switches{readLocalSettings}) and ($switches{readLocalSettings} eq '')){
+ $switches{readLocalSettings} = 'localSettings.yaml';
}
-# yaml work
-print $logfile "YAML files:\n";
-
-# Read in defaultSettings.YAML file
-my $defaultSettings = YAML::Tiny->new;
-
-# Open defaultSettings.yaml
-$defaultSettings = YAML::Tiny->read( "$FindBin::RealBin/defaultSettings.yaml" );
-print $logfile "\tReading defaultSettings.yaml from $FindBin::RealBin/defaultSettings.yaml\n\n" if($defaultSettings);
-
-# 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");
- print $logfile "\tReading defaultSettings.yaml (2nd attempt, TeXLive, Windows) from $FindBin::RealBin/../../texmf-dist/scripts/latexindent/defaultSettings.yaml\n\n" if($defaultSettings);
-}
-
-# if both of the above attempts have failed, we need to exit
-if(!$defaultSettings) {
- for my $fh ($out,$logfile) {
- print $fh <<ENDQUOTE
- ERROR There seems to be a yaml formatting error in defaultSettings.yaml
- Please check it for mistakes- you can find a working version at https://github.com/cmhughes/latexindent.pl
- if you would like to overwrite your current version
-
- Exiting, no indendation done.
-ENDQUOTE
-};
- exit(2);
-}
-
-# the MASTER settings will initially be from defaultSettings.yaml
-# and we update them with USER settings (if any) below
-my %masterSettings = %{$defaultSettings->[0]};
-
-# empty array to store the paths
-my @absPaths;
-
-# scalar to read user settings
-my $userSettings;
-
-# get information about user settings- first check if indentconfig.yaml exists
-my $indentconfig = File::HomeDir->my_home . "/indentconfig.yaml";
-# if indentconfig.yaml doesn't exist, check for the hidden file, .indentconfig.yaml
-$indentconfig = File::HomeDir->my_home . "/.indentconfig.yaml" if(! -e $indentconfig);
-
-if ( -e $indentconfig and !$onlyDefault ) {
- print $logfile "\tReading path information from $indentconfig\n";
- # if both indentconfig.yaml and .indentconfig.yaml exist
- if ( -e File::HomeDir->my_home . "/indentconfig.yaml" and -e File::HomeDir->my_home . "/.indentconfig.yaml") {
- print $logfile File::HomeDir->my_home,"/.indentconfig.yaml has been found, but $indentconfig takes priority\n";
- } elsif ( -e File::HomeDir->my_home . "/indentconfig.yaml" ) {
- print $logfile "\tAlternatively, ",File::HomeDir->my_home,"/.indentconfig.yaml can be used\n";
-
- } elsif ( -e File::HomeDir->my_home . "/.indentconfig.yaml" ) {
- print $logfile "\tAlternatively, ",File::HomeDir->my_home,"/indentconfig.yaml can be used\n";
- }
-
- # read the absolute paths from indentconfig.yaml
- $userSettings = YAML::Tiny->read( "$indentconfig" );
-
- # integrity check
- if($userSettings) {
- print $logfile "\t",Dump \%{$userSettings->[0]};
- print $logfile "\n";
- @absPaths = @{$userSettings->[0]->{paths}};
- } else {
- print $logfile <<ENDQUOTE
-WARNING: $indentconfig
- contains some invalid .yaml formatting- unable to read from it.
- No user settings loaded.
-ENDQUOTE
-;
- }
-} else {
- if($onlyDefault) {
- print $logfile "\tOnly default settings requested, not reading USER settings from $indentconfig\n";
- print $logfile "\tIgnoring $readLocalSettings\n" if($readLocalSettings);
- $readLocalSettings = 0;
- } else {
- # give the user instructions on where to put indentconfig.yaml or .indentconfig.yaml
- print $logfile "\tHome directory is ",File::HomeDir->my_home,"\n";
- print $logfile "\tTo specify user settings you would put indentconfig.yaml here: \n\t",File::HomeDir->my_home,"/indentconfig.yaml\n\n";
- print $logfile "\tAlternatively, you can use the hidden file .indentconfig.yaml as: \n\t",File::HomeDir->my_home,"/.indentconfig.yaml\n\n";
- }
-}
-
-# get information about LOCAL settings, assuming that $readLocalSettings exists
-my $directoryName = dirname $ARGV[0];
-
-# add local settings to the paths, if appropriate
-if ( (-e "$directoryName/$readLocalSettings") and $readLocalSettings and !(-z "$directoryName/$readLocalSettings")) {
- print $logfile "\tAdding $directoryName/$readLocalSettings to paths\n\n";
- push(@absPaths,"$directoryName/$readLocalSettings");
-} elsif ( !(-e "$directoryName/$readLocalSettings") and $readLocalSettings) {
- print $logfile "\tWARNING yaml file not found: \n\t$directoryName/$readLocalSettings not found\n";
- print $logfile "\t\tcarrying on without it.\n";
-}
-
-# 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)) {
- print $logfile "\tReading USER settings from $settings\n";
- $userSettings = YAML::Tiny->read( "$settings" );
-
- # if we can read userSettings
- if($userSettings) {
- # update the MASTER setttings to include updates from the userSettings
- while(my($userKey, $userValue) = each %{$userSettings->[0]}) {
- # the update approach is slightly different for hashes vs scalars/arrays
- if (ref($userValue) eq "HASH") {
- while(my ($userKeyFromHash,$userValueFromHash) = each %{$userSettings->[0]{$userKey}}) {
- $masterSettings{$userKey}{$userKeyFromHash} = $userValueFromHash;
- }
- } else {
- $masterSettings{$userKey} = $userValue;
- }
- }
- # output settings to $logfile
- if($masterSettings{logFilePreferences}{showEveryYamlRead}){
- print $logfile Dump \%{$userSettings->[0]};
- print $logfile "\n";
- } else {
- print $logfile "\t\tNot showing settings in the log file, see showEveryYamlRead.\n";
- }
- } else {
- # otherwise print a warning that we can not read userSettings.yaml
- print $logfile "WARNING\n\t$settings \n\t contains invalid yaml format- not reading from it\n";
- }
- } else {
- # otherwise keep going, but put a warning in the log file
- print $logfile "\nWARNING\n\t",File::HomeDir->my_home,"/indentconfig.yaml\n";
- if (-z $settings) {
- print $logfile "\tspecifies $settings \n\tbut this file is EMPTY- not reading from it\n\n"
- } else {
- print $logfile "\tspecifies $settings \n\tbut this file does not exist- unable to read settings from this file\n\n"
- }
- }
-}
-
-# some people may wish to see showAlmagamatedSettings
-# which details the overall state of the settings modified
-# from the default in various user files
-if($masterSettings{logFilePreferences}{showAlmagamatedSettings}){
- print $logfile "Almagamated/overall settings to be used:\n";
- print $logfile Dump \%masterSettings ;
-}
-
-# scalar variables
-my $defaultIndent = $masterSettings{defaultIndent};
-my $alwaysLookforSplitBraces = $masterSettings{alwaysLookforSplitBraces};
-my $alwaysLookforSplitBrackets = $masterSettings{alwaysLookforSplitBrackets};
-my $backupExtension = $masterSettings{backupExtension};
-my $indentPreamble = $masterSettings{indentPreamble};
-my $onlyOneBackUp = $masterSettings{onlyOneBackUp};
-my $maxNumberOfBackUps = $masterSettings{maxNumberOfBackUps};
-my $removeTrailingWhitespace = $masterSettings{removeTrailingWhitespace};
-my $cycleThroughBackUps = $masterSettings{cycleThroughBackUps};
-
-# hash variables
-my %lookForAlignDelims= %{$masterSettings{lookForAlignDelims}};
-my %indentRules= %{$masterSettings{indentRules}};
-my %verbatimEnvironments= %{$masterSettings{verbatimEnvironments}};
-my %noIndentBlock= %{$masterSettings{noIndentBlock}};
-my %checkunmatched= %{$masterSettings{checkunmatched}};
-my %checkunmatchedELSE= %{$masterSettings{checkunmatchedELSE}};
-my %checkunmatchedbracket= %{$masterSettings{checkunmatchedbracket}};
-my %noAdditionalIndent= %{$masterSettings{noAdditionalIndent}};
-my %indentAfterHeadings= %{$masterSettings{indentAfterHeadings}};
-my %indentAfterItems= %{$masterSettings{indentAfterItems}};
-my %itemNames= %{$masterSettings{itemNames}};
-my %constructIfElseFi= %{$masterSettings{constructIfElseFi}};
-my %fileExtensionPreference= %{$masterSettings{fileExtensionPreference}};
-my %fileContentsEnvironments= %{$masterSettings{fileContentsEnvironments}};
-
-# original name of file
-my $fileName = $ARGV[0];
-
-# 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);
-
-# quick check to make sure given file type is supported
-if( -e $ARGV[0] and !$ext ){
-for my $fh ($out,$logfile) {print $fh <<ENDQUOTE
-The file $ARGV[0] exists , but the extension does not correspond to any given in fileExtensionPreference;
-consinder updating fileExtensionPreference.
-
-Exiting, no indentation done.
-ENDQUOTE
-};
-exit(2);
-}
-
-# if no extension, search according to fileExtensionPreference
-if (!$ext) {
- print $logfile "File extension work:\n";
- print $logfile "\tlatexindent called to act upon $fileName with an, as yet, unrecognised file extension;\n";
- print $logfile "\tsearching for file with an extension in the following order (see fileExtensionPreference):\n\t\t";
- print $logfile join("\n\t\t",@fileExtensions),"\n";
- my $fileFound = 0;
- # loop through the known file extensions (see @fileExtensions)
- foreach my $fileExt (@fileExtensions ){
- if ( -e $fileName.$fileExt ) {
- print $logfile "\t",$fileName,$fileExt," found!\n";
- $fileName .= $fileExt;
- print $logfile "\tUpdated $ARGV[0] to ",$fileName,"\n";
- $fileFound = 1;
- last;
- }
- }
- unless($fileFound){
- print $logfile "\tI couldn't find a match for $ARGV[0] in fileExtensionPreference (see defaultSettings.yaml)\n";
- foreach my $fileExt (@fileExtensions ){
- print $logfile "\t\tI searched for $ARGV[0]$fileExt\n";
- }
- print $logfile "\tbut couldn't find any of them.\n";
- print $logfile "\tConsider updating fileExtensionPreference. \nError: Exiting, no indendation done.";
- die "I couldn't find a match for $ARGV[0] in fileExtensionPreference.\nExiting, no indendation done.\n"
- }
- } else {
- # if the file has a recognised extension, check that the file exists
- unless( -e $ARGV[0] ){
- print $logfile "Error: I couldn't find $ARGV[0], are you sure it exists?. No indentation done. \nExiting.\n";
- die "Error: I couldn't find $ARGV[0], are you sure it exists?. Exiting.\n" ;
- }
- }
-
-# if we want to over write the current file create a backup first
-if ($overwrite) {
- print $logfile "\nBackup procedure:\n";
- # cruft directory
- print $logfile "\tDirectory for backup files and indent.log: $cruftDirectory\n\n";
-
- my $backupFile;
-
- # backup file name is the base name
- $backupFile = basename($fileName,@fileExtensions);
-
- # add the user's backup directory to the backup path
- $backupFile = "$cruftDirectory/$backupFile";
-
- # 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) {
- print $logfile "\t WARNING: onlyOneBackUp=$onlyOneBackUp and maxNumberOfBackUps: $maxNumberOfBackUps\n";
- print $logfile "\t\t setting onlyOneBackUp=0 which will allow you to reach $maxNumberOfBackUps back ups\n";
- $onlyOneBackUp = 0;
- }
-
- # if the user has specified that $maxNumberOfBackUps = 1 then
- # they only want one backup
- if($maxNumberOfBackUps==1) {
- $onlyOneBackUp=1 ;
- print $logfile "\t FYI: you set maxNumberOfBackUps=1, so I'm setting onlyOneBackUp: 1 \n";
- } 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;
- print $logfile "\tcopying $fileName to $backupFile\n";
- print $logfile "\t$backupFile was overwritten\n\n" 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) {
- print $logfile "\t maxNumberOfBackUps reached ($maxNumberOfBackUps)\n";
-
- # 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) {
- print $logfile "\t cycleThroughBackUps detected (see cycleThroughBackUps) \n";
- 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){
- print $logfile "\t\t copying $oldBackupFile to $newBackupFile \n";
- copy($oldBackupFile,$newBackupFile) or die "Could not write to backup file $backupFile. Please check permissions. Exiting.\n";
- }
- }
- }
-
- # rest maxNumberOfBackUps
- $maxNumberOfBackUps=1 ;
- last; # break out of the loop
- } elsif(!(-e $backupFile)) {
- $maxNumberOfBackUps=1 ;
- last; # break out of the loop
- }
- print $logfile "\t $backupFile already exists, incrementing by 1...\n";
- $backupCounter++;
- $backupFile =~ s/$backupExtension.*/$backupExtension$backupCounter/;
- }
- print $logfile "\n\t copying $fileName to $backupFile\n\n";
- }
-
- # output these lines to the log file
- print $logfile "\tBackup file: ",$backupFile,"\n";
- print $logfile "\tOverwriting file: ",$fileName,"\n\n";
- copy($fileName,$backupFile) or die "Could not write to backup file $backupFile. Please check permissions. Exiting.\n";
-}
-
-if(!($outputToFile or $overwrite)) {
- print $logfile "Just out put to the terminal :)\n\n" if !$silentMode ;
-}
-
-
-# scalar variables
-my $line; # $line: takes the $line of the file
-my $inpreamble=!$indentPreamble;
- # $inpreamble: switch to determine if in
- # preamble or not
-my $inverbatim=0; # $inverbatim: switch to determine if in
- # a verbatim environment or not
-my $delimiters=0; # $delimiters: switch that governs if
- # we need to check for & or not
-my $trailingcomments; # $trailingcomments stores the comments at the end of
- # a line
-my $lineCounter=0; # $lineCounter keeps track of the line number
-my $inIndentBlock=0; # $inindentblock: switch to determine if in
- # a inindentblock or not
-my $inFileContents=0; # $inFileContents: switch to determine if we're in a filecontents environment
-
-# array variables
-my @lines; # @lines: stores the newly indented lines
-my @mainfile; # @mainfile: stores input file; used to
- # grep for \documentclass
-
-# array of hashes, containing details of commands & environments
-my @masterIndentationArrayOfHashes;
-
-# check to see if the current file has \documentclass, if so, then
-# it's the main file, if not, then it doesn't have preamble
-open(MAINFILE, $fileName) or die "Could not open input file, $fileName";
- @mainfile=<MAINFILE>;
-close(MAINFILE);
-
-# if the MAINFILE doesn't have a \documentclass statement, then
-# it shouldn't have preamble
-if(scalar(@{[grep(m/^\s*\\documentclass/, @mainfile)]})==0) {
- $inpreamble=0;
-
- print $logfile "Trace:\tNo documentclass detected, assuming no preamble\n" if($tracingMode);
-} else {
- print $logfile "Trace:\t documentclass detected, assuming preamble\n" if($tracingMode);
-}
-
-# the previous OPEN command puts us at the END of the file
-open(MAINFILE, $fileName) or die "Could not open input file, $fileName";
-
-# loop through the lines in the INPUT file
-while(<MAINFILE>) {
- # increment the line counter
- $lineCounter++;
-
- # very detailed output to logfile
- if($tracingModeVeryDetailed){
- if( @masterIndentationArrayOfHashes){
- print $logfile "\nLine $lineCounter\t (detailed trace) indentation hash: \n" if($tracingMode);
- for my $href ( @masterIndentationArrayOfHashes) {
- print $logfile Dump \%{$href};
- }
- }
- }
-
- # tracing mode
- print $logfile $masterSettings{logFilePreferences}{traceModeBetweenLines} if($tracingMode and !($inpreamble or $inverbatim or $inIndentBlock));
-
- # check to see if we're still in the preamble
- # or in a verbatim environment or in IndentBlock
- if(!($inpreamble or $inverbatim or $inIndentBlock)) {
- # if not, remove all leading spaces and tabs
- # from the current line, assuming it isn't empty
- s/^\t*// if($_ !~ /^((\s*)|(\t*))*$/);
- s/^\s*// if($_ !~ /^((\s*)|(\t*))*$/);
-
- # tracing mode
- print $logfile "Line $lineCounter\t removing leading spaces\n" if($tracingMode);
- } else {
- # otherwise check to see if we've reached the main
- # part of the document
- if(m/^\s*\\begin\{document\}/ and !$inFileContents and !$inverbatim) {
- $inpreamble = 0;
-
- # tracing mode
- print $logfile "Line $lineCounter\t \\begin{document} found, switching indentation searches on. \n" if($tracingMode);
- } else {
- # tracing mode
- if($inpreamble) {
- print $logfile "Line $lineCounter\t still in PREAMBLE, leaving exisiting leading space (see indentPreamble)\n" if($tracingMode);
- } elsif($inverbatim) {
- print $logfile "Line $lineCounter\t in VERBATIM-LIKE environment, leaving exisiting leading space\n" if($tracingMode);
- } elsif($inIndentBlock) {
- print $logfile "Line $lineCounter\t in NO INDENT BLOCK, leaving exisiting leading space\n" if($tracingMode);
- }
- }
- }
-
- # \END{ENVIRONMENTS}, or CLOSING } or CLOSING ]
- # \END{ENVIRONMENTS}, or CLOSING } or CLOSING ]
- # \END{ENVIRONMENTS}, or CLOSING } or CLOSING ]
-
- # check to see if we're ending a filecontents environment
- if( $_ =~ m/^\s*\\end\{(.*?)\}/ and $fileContentsEnvironments{$1} and $inFileContents){
- print $logfile "Line $lineCounter\t Found END of filecontents environment (see fileContentsEnvironments)\n" if($tracingMode);
- $inFileContents = 0;
- }
-
- # set the delimiters switch
- $delimiters = @masterIndentationArrayOfHashes?$masterIndentationArrayOfHashes[-1]{alignmentDelimiters}:0;
-
- if($inverbatim){
- print $logfile "Line $lineCounter\t $masterSettings{logFilePreferences}{traceModeDecreaseIndent} PHASE 1: in VERBATIM-LIKE environment, looking for $masterIndentationArrayOfHashes[-1]{end} \n" if($tracingMode);
- } elsif($inIndentBlock) {
- print $logfile "Line $lineCounter\t in NO INDENT BLOCK, doing nothing\n" if($tracingMode);
- } elsif($delimiters) {
- print $logfile "Line $lineCounter\t $masterSettings{logFilePreferences}{traceModeDecreaseIndent} PHASE 1: in ALIGNMENT BLOCK environment, looking for $masterIndentationArrayOfHashes[-1]{end}\n" if($tracingMode);
- } elsif($inpreamble and !$inFileContents) {
- print $logfile "Line $lineCounter\t In preamble, looking for \\begin{document}\n" if($tracingMode);
- } elsif($inpreamble and $inFileContents) {
- print $logfile "Line $lineCounter\t In preamble, in filecontents environment\n" if($tracingMode);
- } else {
- print $logfile "Line $lineCounter\t $masterSettings{logFilePreferences}{traceModeDecreaseIndent} PHASE 1: looking for reasons to DECREASE indentation of CURRENT line \n" if($tracingMode);
- }
-
- # check to see if we have \end{something} or \]
- &at_end_of_env_or_eq() unless ($inpreamble or $inIndentBlock);
-
- # check to see if we have %* \end{something} for alignment blocks
- # outside of environments
- &end_command_with_alignment();
-
- # check to see if we're at the end of a noindent
- # block %\end{noindent}
- &at_end_noindent();
-
- # only check for unmatched braces if we're not in
- # a verbatim-like environment or in the preamble or in a
- # noIndentBlock or in a delimiter block
- if(!($inverbatim or $inpreamble or $inIndentBlock or $delimiters)) {
- # The check for closing } and ] relies on counting, so
- # we have to remove trailing comments so that any {, }, [, ]
- # that are found after % are not counted
- #
- # note that these lines are NOT in @lines, so we
- # have to store the $trailingcomments to put
- # back on after the counting
- #
- # note the use of (?<!\\)% so that we don't match \%
- if ( $_=~ m/(?<!\\)%.*/) {
- s/((?<!\\)%.*)//;
- $trailingcomments=$1;
-
- # tracing mode
- print $logfile "Line $lineCounter\t Removed trailing comments to count braces and brackets: $1\n" if($tracingMode);
- }
-
- # check to see if we're at the end of a \parbox, \marginpar
- # or other split-across-lines command and check that
- # we're not starting another command that has split braces (nesting)
- &end_command_or_key_unmatched_braces();
-
- # check to see if we're at the end of a command that splits
- # [ ] across lines
- &end_command_or_key_unmatched_brackets();
-
- # check for a heading such as \chapter, \section, etc
- &indent_heading();
-
- # check for \item
- &indent_item();
-
- # check for \else or \fi
- &indent_if_else_fi();
-
- # add the trailing comments back to the end of the line
- if(scalar($trailingcomments)) {
- # some line break magic, http://stackoverflow.com/questions/881779/neatest-way-to-remove-linebreaks-in-perl
- s/\R//;
- $_ = $_ . $trailingcomments."\n" ;
-
- # tracing mode
- print $logfile "Line $lineCounter\t counting braces/brackets complete\n" if($tracingMode);
- print $logfile "Line $lineCounter\t Adding trailing comments back on: $trailingcomments\n" if($tracingMode);
-
- # empty the trailingcomments
- $trailingcomments='';
-
- }
- # remove trailing whitespace
- if ($removeTrailingWhitespace) {
- print $logfile "Line $lineCounter\t removing trailing whitespace (see removeTrailingWhitespace)\n" if ($tracingMode);
- s/\s+$/\n/;
- }
- }
-
- # ADD CURRENT LEVEL OF INDENTATION
- # ADD CURRENT LEVEL OF INDENTATION
- # ADD CURRENT LEVEL OF INDENTATION
- # (unless we're in a delimiter-aligned block)
- if(!$delimiters) {
- # make sure we're not in a verbatim block or in the preamble
- if($inverbatim or $inpreamble or $inIndentBlock) {
- # just push the current line as is
- push(@lines,$_);
- } else {
- # add current value of indentation to the current line
- # and output it
- # unless this would only create trailing whitespace and the
- # corresponding option is set
- unless ($_ =~ m/^$/ and $removeTrailingWhitespace){
- $_ = &current_indentation().$_;
- }
- push(@lines,$_);
- # tracing mode
- print $logfile "Line $lineCounter\t $masterSettings{logFilePreferences}{traceModeAddCurrentIndent} PHASE 2: Adding current level of indentation: ",&current_indentation_names(),"\n" if($tracingMode);
- }
- } else {
- # output to @block (within masterIndentationArrayOfHashes) if we're in a delimiter block
- push(@{$masterIndentationArrayOfHashes[-1]{block}},$_);
-
- # tracing mode
- print $logfile "Line $lineCounter\t In delimeter block ($masterIndentationArrayOfHashes[-1]{name}), waiting for block formatting\n" if($tracingMode);
- }
-
- # \BEGIN{ENVIRONMENT} or OPEN { or OPEN [
- # \BEGIN{ENVIRONMENT} or OPEN { or OPEN [
- # \BEGIN{ENVIRONMENT} or OPEN { or OPEN [
-
- # check to see if we're beginning a filecontents environment
- if( ($_ =~ m/^\s*\\begin\{(.*?)\}/ and $fileContentsEnvironments{$1} and !$inverbatim)){
- print $logfile "Line $lineCounter\t Found filecontents environment (see fileContentsEnvironments)\n" if($tracingMode);
- $inFileContents = 1;
- }
-
- # only check for new environments or commands if we're
- # not in a verbatim-like environment or in the preamble
- # or in a noIndentBlock, or delimiter block
- if(!($inverbatim or $inpreamble or $inIndentBlock or $delimiters)) {
-
- print $logfile "Line $lineCounter\t $masterSettings{logFilePreferences}{traceModeIncreaseIndent} PHASE 3: looking for reasons to INCREASE indentation of SUBSEQUENT lines \n" if($tracingMode);
-
- # check if we are in a
- # % \begin{noindent}
- # block; this is similar to a verbatim block, the user
- # may not want some blocks of code to be touched
- #
- # IMPORTANT: this needs to go before the trailing comments
- # are removed!
- &at_beg_noindent();
-
- # check for
- # %* \begin{tabular}
- # which might be used to align blocks that contain delimeters that
- # are NOT contained in an alignment block in the usual way, e.g
- # \matrix{
- # %* \begin{tabular}
- # 1 & 2 \\
- # 3 & 4 \\
- # %* \end{tabular}
- # }
- &begin_command_with_alignment();
- if(@masterIndentationArrayOfHashes){
- $delimiters = $masterIndentationArrayOfHashes[-1]{alignmentDelimiters}||0;
- }
-
- # remove trailing comments so that any {, }, [, ]
- # that are found after % are not counted
- #
- # note that these lines are already in @lines, so we
- # can remove the trailing comments WITHOUT having
- # to put them back in
- #
- # Note that this won't match \%
- s/(?<!\\)%.*// if( $_=~ m/(?<!\\)%.*/);
-
- # tracing mode
- print $logfile "Line $lineCounter\t Removing trailing comments for brace count (line is already stored)\n" if($tracingMode);
-
- # check to see if we have \begin{something} or \[
- &at_beg_of_env_or_eq() if(!($inverbatim or $inpreamble or $inIndentBlock or $delimiters));
- if(@masterIndentationArrayOfHashes){
- $delimiters = $masterIndentationArrayOfHashes[-1]{alignmentDelimiters}||0;
- }
-
- # check to see if we have \parbox, \marginpar, or
- # something similar that might split braces {} across lines,
- # specified in %checkunmatched hash table
- &start_command_or_key_unmatched_braces() if(!($inverbatim or $inpreamble or $inIndentBlock or $delimiters));
-
- # check for an else statement (braces, not \else)
- &check_for_else() if(!($inverbatim or $inpreamble or $inIndentBlock or $delimiters));
-
- # check for a command that splits [] across lines
- &start_command_or_key_unmatched_brackets();
-
- # check for a heading
- &indent_after_heading() if(!($inverbatim or $inpreamble or $inIndentBlock or $delimiters));
-
- # check for \item
- &indent_after_item() if(!($inverbatim or $inpreamble or $inIndentBlock or $delimiters));
-
- # check for \if or \else command
- &indent_after_if_else_fi() if(!($inverbatim or $inpreamble or $inIndentBlock or $delimiters));
-
- # tracing mode
- if($tracingMode){
- if(scalar(@masterIndentationArrayOfHashes)){
- print $logfile "Line $lineCounter\t Indentation array: ",&current_indentation_names(),"\n";
- } else {
- print $logfile "Line $lineCounter\t Indentation array empty\n";
- }
- }
- }
-}
-
-# close the main file
-close(MAINFILE);
-
-# put line count information in the log file
-print $logfile "Line Count of $fileName: ",scalar(@mainfile),"\n";
-print $logfile "Line Count of indented $fileName: ",scalar(@lines);
-if(scalar(@mainfile) != scalar(@lines))
-{
- print $logfile <<ENDQUOTE
-WARNING: \t line count of original file and indented file does
-\t not match- consider reverting to a back up, see $backupExtension;
-ENDQUOTE
-;
-} else {
- print $logfile "\n\nLine counts of original file and indented file match.\n";
-}
-
-# output the formatted lines to the terminal
-print @lines if(!$silentMode);
-
-# if -w is active then output to $ARGV[0]
-if($overwrite) {
- open(OUTPUTFILE,">",$fileName);
- print OUTPUTFILE @lines;
- close(OUTPUTFILE);
-}
-
-# if -o is active then output to $ARGV[1]
-if($outputToFile) {
- open(OUTPUTFILE,">",$ARGV[1]);
- print OUTPUTFILE @lines;
- close(OUTPUTFILE);
- print $logfile "Output from indentation written to $ARGV[1].\n";
-}
-
-# final line of the logfil
-print $logfile "\n",$masterSettings{logFilePreferences}{endLogFileWith};
-
-# close the log file
-close($logfile);
-
+my $document = LatexIndent::Document->new(name=>"masterdocument",fileName=>$ARGV[0],switches=>\%switches);
+$document->latexindent;
exit(0);
-
-sub indent_if_else_fi{
- # PURPOSE: set indentation of line that contains \else, \fi command
- #
- #
-
- # @masterIndentationArrayOfHashes could be empty -- if so, exit
- return unless @masterIndentationArrayOfHashes;
- return unless $constructIfElseFi{$masterIndentationArrayOfHashes[-1]{name}};
-
- # look for \fi
- if( $_ =~ m/^\s*\\fi/) {
- # tracing mode
- print $logfile "Line $lineCounter\t \\fi command found, matching: \\",$masterIndentationArrayOfHashes[-1]{name}, "\n" if($tracingMode);
- &decrease_indent($masterIndentationArrayOfHashes[-1]{name});
- }
- # look for \else or \or
- elsif( $_ =~ m/^\s*\\else/ or $_ =~ m/^\s*\\or/ ) {
- # tracing mode
- print $logfile "Line $lineCounter\t \\else command found, matching: \\",$masterIndentationArrayOfHashes[-1]{name}, "\n" if($tracingMode);
- print $logfile "Line $lineCounter\t decreasing indent, still looking for \\fi to match \\",&current_indentation_names(), "\n" if($tracingMode);
-
- # finding an \else or \or command removes the *indentation*, but not the entry from the master hash
- $masterIndentationArrayOfHashes[-1]{indent}="";
- }
-}
-
-sub indent_after_if_else_fi{
- # PURPOSE: set indentation *after* \if construct such as
- #
- # \ifnum\x=2
- # <stuff>
- # <stuff>
- # \else
- # <stuff>
- # <stuff>
- # \fi
- #
- # How to read /^\s*\\(if.*?)(\s|\\|\#)
- #
- # ^\s* begins with multiple spaces (possibly none)
- # \\(if.*?)(\s|\\|\#) matches \if... up to either a
- # space, a \, or a #
- # Note: this won't match \if.*\fi
- if( $_ =~ m/^\s*\\(if.*?)(\s|\\|\#)/ and $_ !~ m/^\s*\\(if.*?\\fi)/ and $constructIfElseFi{$1}) {
- # tracing mode
- print $logfile "Line $lineCounter\t ifelsefi construct found: $1 \n" if($tracingMode);
- &increase_indent({name=>$1,type=>"ifelseif"});
- } elsif(@masterIndentationArrayOfHashes) {
- if( ($_ =~ m/^\s*\\else/ or $_ =~ m/^\s*\\or/ ) and $constructIfElseFi{$masterIndentationArrayOfHashes[-1]{name}}) {
- # tracing mode
- print $logfile "Line $lineCounter\t setting indent *after* \\else or \\or command found for $masterIndentationArrayOfHashes[-1]{name} \n" if($tracingMode);
-
- # recover the indentation to be implemented *after* the \else or \or
- $masterIndentationArrayOfHashes[-1]{indent}=$indentRules{$masterIndentationArrayOfHashes[-1]{name}}||$defaultIndent unless ($noAdditionalIndent{$masterIndentationArrayOfHashes[-1]{name}});
- }
- }
-}
-
-sub indent_item{
- # PURPOSE: when considering environments that can contain items, such
- # as enumerate, itemize, etc, this subroutine sets the indentation for the item *itself*
-
- return unless(scalar(@masterIndentationArrayOfHashes)>1);
- return unless $indentAfterItems{$masterIndentationArrayOfHashes[-2]{name}};
-
- if( $_ =~ m/^\s*\\(.*?)(\[|\s)/ and $itemNames{$1}){
- # tracing mode
- print $logfile "Line $lineCounter\t $1 found within ",$masterIndentationArrayOfHashes[-1]{name}," environment (see indentAfterItems and itemNames)\n" if($tracingMode);
- if($itemNames{$masterIndentationArrayOfHashes[-1]{name}}) {
- print $logfile "Line $lineCounter\t $1 found - neutralizing indentation from previous ",$masterIndentationArrayOfHashes[-1]{name},"\n" if($tracingMode);
- &decrease_indent($1);
- }
- }
-
-}
-
-sub indent_after_item{
- # PURPOSE: Set the indentation *after* the item
- # This matches a line that begins with
- #
- # \item
- # \item[
- # \myitem
- # \myitem[
- #
- # or anything else specified in itemNames
- #
- return unless @masterIndentationArrayOfHashes;
- return unless $indentAfterItems{$masterIndentationArrayOfHashes[-1]{name}};
-
- if( $_ =~ m/^\s*\\(.*?)(\[|\s)/
- and $itemNames{$1}) {
- # tracing mode
- print $logfile "Line $lineCounter\t $1 found within ",$masterIndentationArrayOfHashes[-1]{name}," environment (see indentAfterItems and itemNames)\n" if($tracingMode);
- &increase_indent({name=>$1,type=>"item"});
- }
-}
-
-sub begin_command_with_alignment{
- # PURPOSE: This matches
- # %* \begin{tabular}
- # with any number of spaces (possibly none) between
- # the * and \begin{noindent}.
- #
- # the comment symbol IS indended!
- #
- # This is to align blocks that contain delimeters that
- # are NOT contained in an alignment block in the usual way, e.g
- # \matrix{
- # %* \begin{tabular}
- # 1 & 2 \\
- # 3 & 4 \\
- # %* \end{tabular}
- # }
-
- if( $_ =~ m/^\s*%\*\s*\\begin\{(.*?)\}/ and $lookForAlignDelims{$1}) {
- # increase the indentation
- &increase_indent({name=>$1,
- alignmentDelimiters=>1,
- type=>"environment",
- begin=>"\\begin{$1}",
- end=>"\\end{$1}"});
- # tracing mode
- print $logfile "Line $lineCounter\t Delimiter environment started: $1 (see lookForAlignDelims)\n" if($tracingMode);
- }
-}
-
-sub end_command_with_alignment{
- # PURPOSE: This matches
- # %* \end{tabular}
- # with any number of spaces (possibly none) between
- # the * and \end{tabular} (or any other name used from
- # lookFroAlignDelims)
- #
- # Note: the comment symbol IS indended!
- #
- # This is to align blocks that contain delimeters that
- # are NOT contained in an alignment block in the usual way, e.g
- # \matrix{
- # %* \begin{tabular}
- # 1 & 2 \\
- # 3 & 4 \\
- # %* \end{tabular}
- # }
- return unless @masterIndentationArrayOfHashes;
- return unless $masterIndentationArrayOfHashes[-1]{alignmentDelimiters};
-
- if( $_ =~ m/^\s*%\*\s*\\end\{(.*?)\}/ and $lookForAlignDelims{$1}) {
- # same subroutine used at the end of regular tabular, align, etc
- # environments
- if($delimiters) {
- &print_aligned_block();
- &decrease_indent($1);
- } else {
- # tracing mode
- print $logfile "Line $lineCounter\t FYI: did you mean to start a delimiter block on a previous line? \n" if($tracingMode);
- print $logfile "Line $lineCounter\t perhaps using %* \\begin{$1}\n" if($tracingMode);
- }
- }
-}
-
-sub indent_heading{
- # PURPOSE: This matches
- # \part
- # \chapter
- # \section
- # \subsection
- # \subsubsection
- # \paragraph
- # \subparagraph
- #
- # and anything else listed in indentAfterHeadings
- #
- # This subroutine specifies the indentation for the
- # heading itself, i.e the line that has \chapter, \section etc
- if( $_ =~ m/^\s*\\(.*?)(\[|{)/ and $indentAfterHeadings{$1}){
- # tracing mode
- print $logfile "Line $lineCounter\t Heading found: $1 \n" if($tracingMode);
-
- # get the heading settings, it's a hash within a hash
- my %currentHeading = %{$indentAfterHeadings{$1}};
-
- # $previousHeadingLevel: scalar that stores which heading
- # we are under: \part, \chapter, etc
- my $previousHeadingLevel=0;
-
- # form an array of the headings available
- my @headingStore=();
- foreach my $env (@masterIndentationArrayOfHashes){
- if($env->{type} eq 'heading'){
- push(@headingStore,$env->{name});
- # update heading level
- $previousHeadingLevel= $env->{headinglevel};
- }
- }
-
- # if current heading level < old heading level,
- if($currentHeading{level}<$previousHeadingLevel) {
- # decrease indentation, but only if
- # specified in indentHeadings. Note that this check
- # needs to be done here- decrease_indent won't
- # check a nested hash
-
- if(scalar(@headingStore)) {
- while($currentHeading{level}<$previousHeadingLevel and scalar(@headingStore)) {
- my $higherHeadingName = pop(@headingStore);
- my %higherLevelHeading = %{$indentAfterHeadings{$higherHeadingName}};
-
- # tracing mode
- print $logfile "Line $lineCounter\t stepping UP heading level from $higherHeadingName \n" if($tracingMode);
-
- &decrease_indent($higherHeadingName) if($higherLevelHeading{indent});
- $previousHeadingLevel=$higherLevelHeading{level};
- }
- # put the heading name back in to storage
- push(@headingStore,$1);
- }
- } elsif($currentHeading{level}==$previousHeadingLevel) {
- if(scalar(@headingStore)) {
- my $higherHeadingName = pop(@headingStore);
- my %higherLevelHeading = %{$indentAfterHeadings{$higherHeadingName}};
- &decrease_indent($higherHeadingName) if($higherLevelHeading{indent});
- }
- }
- }
-}
-
-sub indent_after_heading{
- # PURPOSE: This matches
- # \part
- # \chapter
- # \section
- # \subsection
- # \subsubsection
- # \paragraph
- # \subparagraph
- #
- # and anything else listed in indentAfterHeadings
- #
- # This subroutine is specifies the indentation for
- # the text AFTER the heading, i.e the body of conent
- # in each \chapter, \section, etc
- if( $_ =~ m/^\s*\\(.*?)(\[|{)/ and $indentAfterHeadings{$1}) {
- # get the heading settings- it's a hash within a hash
- my %currentHeading = %{$indentAfterHeadings{$1}};
-
- &increase_indent({name=>$1,type=>"heading",headinglevel=>$currentHeading{level}}) if($currentHeading{indent});
- }
-}
-
-sub at_end_noindent{
- # PURPOSE: This matches
- # % \end{noindent}
- # with any number of spaces (possibly none) between
- # the comment and \end{noindent}.
- #
- # the comment symbol IS indended!
- #
- # This is for blocks of code that the user wants
- # to leave untouched- similar to verbatim blocks
-
- if( $_ =~ m/^%\s*\\end\{(.*?)\}/ and $noIndentBlock{$1}) {
- $inIndentBlock=0;
- # tracing mode
- print $logfile "Line $lineCounter\t % \\end{no indent block} found, switching inIndentBlock OFF \n" if($tracingMode);
- }
-}
-
-sub at_beg_noindent{
- # PURPOSE: This matches
- # % \begin{noindent}
- # with any number of spaces (possibly none) between
- # the comment and \begin{noindent}.
- #
- # the comment symbol IS indended!
- #
- # This is for blocks of code that the user wants
- # to leave untouched- similar to verbatim blocks
-
- if( $_ =~ m/^%\s*\\begin\{(.*?)\}/ and $noIndentBlock{$1}) {
- $inIndentBlock = 1;
- # tracing mode
- print $logfile "Line $lineCounter\t % \\begin{no indent block} found, switching inIndentBlock ON \n" if($tracingMode);
- }
-}
-
-sub start_command_or_key_unmatched_brackets{
- # PURPOSE: This matches
- # \pgfplotstablecreatecol[...
- #
- # or any other command/key that has brackets [ ]
- # split across lines specified in the
- # hash tables, %checkunmatchedbracket
- #
- # How to read: ^\s*(\\)?(.*?)(\[\s*)
- #
- # ^ line begins with
- # \s* any (or no)spaces
- # (\\)? matches a \ backslash but not necessarily
- # (.*?) non-greedy character match and store the result
- # ((?<!\\)\[\s*) match [ possibly leading with spaces
- # but it WON'T match \[
-
- if ($_ =~ m/^\s*(\\)?(.*?)(\s*(?<!\\)\[)/
- and ($checkunmatchedbracket{$2} or $alwaysLookforSplitBrackets)) {
- # store the command name, because $2
- # will not exist after the next match
- my $commandname = $2;
- my $matchedBRACKETS=0;
-
- # match [ but don't match \[
- $matchedBRACKETS++ while ($_ =~ /(?<!\\)\[/g);
- # match ] but don't match \]
- $matchedBRACKETS-- while ($_ =~ /(?<!\\)\]/g);
-
- # set the indentation
- if($matchedBRACKETS != 0 ) {
- # tracing mode
- print $logfile "Line $lineCounter\t Found opening BRACKET [ $commandname\n" if($tracingMode);
-
- &increase_indent({name=>$commandname,matchedBRACKETS=>$matchedBRACKETS,type=>'splitBrackets'});
- }
- }
-}
-
-sub end_command_or_key_unmatched_brackets{
- # PURPOSE: Check for the closing BRACKET of a command that
- # splits its BRACKETS across lines, such as
- #
- # \pgfplotstablecreatecol[ ...
- #
- # It works by checking if we have any entries
- # in the array @masterIndentationArrayOfHashes, and making
- # sure that we're not starting another command/key
- # that has split BRACKETS (nesting).
- #
- # It also checks that the line is not commented.
- #
- # We count the number of [ and ADD to the counter
- # ] and SUBTRACT to the counter
- return unless @masterIndentationArrayOfHashes;
- return unless ($masterIndentationArrayOfHashes[-1]{type} eq 'splitBrackets');
- print $logfile "Line $lineCounter\t Searching for closing BRACKET ] $masterIndentationArrayOfHashes[-1]{name}\n" if($tracingMode);
-
- if(!($_ =~ m/^\s*(\\)?(.*?)(\s*\[)/
- and ($checkunmatchedbracket{$2} or $alwaysLookforSplitBrackets))
- and $_ !~ m/^\s*%/) {
-
- # get the details of the most recent command name
- my $commandname = $masterIndentationArrayOfHashes[-1]{name};
- my $matchedBRACKETS = $masterIndentationArrayOfHashes[-1]{matchedBRACKETS};
-
- # match [ but don't match \[
- $matchedBRACKETS++ while ($_ =~ m/(?<!\\)\[/g);
-
- # match ] but don't match \]
- $matchedBRACKETS-- while ($_ =~ m/(?<!\\)\]/g);
-
- # if we've matched up the BRACKETS then
- # we can decrease the indent by 1 level
- if($matchedBRACKETS == 0){
- # tracing mode
- print $logfile "Line $lineCounter\t Found closing BRACKET ] $commandname\n" if($tracingMode);
-
- # decrease the indentation (if appropriate)
- &decrease_indent($commandname);
- } else {
- # otherwise we need to enter the new value
- # of $matchedBRACKETS and the value of $command
- # back into storage
- $masterIndentationArrayOfHashes[-1]{matchedBRACKETS} = $matchedBRACKETS;
-
- # tracing mode
- print $logfile "Line $lineCounter\t Searching for closing BRACKET ] $commandname\n" if($tracingMode);
- }
- }
-}
-
-sub start_command_or_key_unmatched_braces{
- # PURPOSE: This matches
- # \parbox{...
- # \parbox[..]..{
- # empty header/.style={
- # \foreach \something
- # etc
- #
- # or any other command/key that has BRACES
- # split across lines specified in the
- # hash tables, %checkunmatched, %checkunmatchedELSE
- #
- # How to read: ^\s*(\\)?(.*?)(\[|{|\s)
- #
- # ^ line begins with
- # \s* any (or no) spaces
- # (\\)? matches a \ backslash but not necessarily
- # (.*?) non-greedy character match and store the result
- # (\[|}|=|(\s*\\)) either [ or { or = or space \
-
- if ($_ =~ m/^\s*(\\)?(.*?)(\[|{|=|(\s*\\))/
- and ($checkunmatched{$2} or $checkunmatchedELSE{$2}
- or $alwaysLookforSplitBraces)
- ) {
- # store the command name, because $2
- # will not exist after the next match
- my $commandname = $2;
- my $matchedbraces=0;
-
- # by default, don't look for an else construct
- my $lookforelse=$checkunmatchedELSE{$2}||0;
-
- # match { but don't match \{
- $matchedbraces++ while ($_ =~ /(?<!\\){/g);
-
- # match } but don't match \}
- $matchedbraces-- while ($_ =~ /(?<!\\)}/g);
-
- # tracing mode
- print $logfile "Line $lineCounter\t matchedbraces = $matchedbraces\n" if($tracingMode);
-
- # set the indentation
- if($matchedbraces > 0 ) {
- # tracing mode
- print $logfile "Line $lineCounter\t Found opening BRACE { $commandname\n" if($tracingMode);
-
- &increase_indent({name=>$commandname,
- matchedbraces=>$matchedbraces,
- lookforelse=>$lookforelse,
- countzeros=>0,
- type=>"splitbraces"});
- } elsif($matchedbraces<0) {
- # if $matchedbraces < 0 then we must be matching
- # braces from a previous split-braces command
-
- # keep matching { OR }, and don't match \{ or \}
- while ($_ =~ m/(((?<!\\){)|((?<!\\)}))/g) {
-
- # store the match, either { or }
- my $braceType = $1;
-
- # exit the loop if @masterIndentationArrayOfHashes[-1] is empty
- last if(!@masterIndentationArrayOfHashes);
-
- # exit the loop if we're not looking for split braces
- last if($masterIndentationArrayOfHashes[-1]{type} ne 'splitbraces');
-
- # get the details of the most recent command name
- $commandname = $masterIndentationArrayOfHashes[-1]{name};
- $matchedbraces = $masterIndentationArrayOfHashes[-1]{'matchedbraces'};
- my $countzeros = $masterIndentationArrayOfHashes[-1]{'countzeros'};
- $lookforelse= $masterIndentationArrayOfHashes[-1]{'lookforelse'};
-
- $matchedbraces++ if($1 eq "{");
- $matchedbraces-- if($1 eq "}");
-
- # update the matched braces count
- $masterIndentationArrayOfHashes[-1]{matchedbraces} = $matchedbraces;
-
- # if we've matched up the braces then
- # we can decrease the indent by 1 level
- if($matchedbraces == 0) {
- $countzeros++ if $lookforelse;
-
- # tracing mode
- print $logfile "Line $lineCounter\t Found closing BRACE } $1\n" if($tracingMode);
-
- # decrease the indentation (if appropriate)
- &decrease_indent($commandname);
-
- if($countzeros==1) {
- $masterIndentationArrayOfHashes[-1]{'matchedbraces'} = $matchedbraces;
- $masterIndentationArrayOfHashes[-1]{'countzeros'} = $countzeros;
- $masterIndentationArrayOfHashes[-1]{'lookforelse'} = $lookforelse;
- }
- }
- }
- }
- }
-}
-
-sub end_command_or_key_unmatched_braces{
- # PURPOSE: Check for the closing BRACE of a command that
- # splits its BRACES across lines, such as
- #
- # \parbox{ ...
- #
- # or one of the tikz keys, such as
- #
- # empty header/.style={
- #
- # It works by checking if we have any entries
- # in the array @masterIndentationArrayOfHashes, and making
- # sure that we're not starting another command/key
- # that has split BRACES (nesting).
- #
- # It also checks that the line is not commented.
- #
- # We count the number of { and ADD to the counter
- # } and SUBTRACT to the counter
- return unless @masterIndentationArrayOfHashes;
- return unless ($masterIndentationArrayOfHashes[-1]{type} eq 'splitbraces');
- print $logfile "Line $lineCounter\t Searching for closing BRACE } $masterIndentationArrayOfHashes[-1]{name}\n" if($tracingMode);
-
- if(!($_ =~ m/^\s*(\\)?(.*?)(\[|{|=|(\s*\\))/
- and ($checkunmatched{$2} or $checkunmatchedELSE{$2} or $alwaysLookforSplitBraces))
- and $_ !~ m/^\s*%/
- ) {
- # keep matching { OR }, and don't match \{ or \}
- while ($_ =~ m/(((?<!\\){)|((?<!\\)}))/g) {
- # store the match, either { or }
- my $braceType = $1;
-
- # exit the loop if @masterIndentationArrayOfHashes[-1] is empty
- last if(!@masterIndentationArrayOfHashes);
-
- # exit the loop if we're not looking for split braces
- last if($masterIndentationArrayOfHashes[-1]{type} ne 'splitbraces');
-
- # get the details of the most recent command name
- my $commandname = $masterIndentationArrayOfHashes[-1]{name};
- my $matchedbraces = $masterIndentationArrayOfHashes[-1]{matchedbraces};
- my $countzeros = $masterIndentationArrayOfHashes[-1]{countzeros};
- my $lookforelse= $masterIndentationArrayOfHashes[-1]{lookforelse};
-
- $matchedbraces++ if($1 eq "{");
- $matchedbraces-- if($1 eq "}");
-
- # update the matched braces count
- $masterIndentationArrayOfHashes[-1]{matchedbraces} = $matchedbraces;
-
- # if we've matched up the braces then
- # we can decrease the indent by 1 level
- if($matchedbraces == 0) {
- $countzeros++ if $lookforelse;
-
- # tracing mode
- print $logfile "Line $lineCounter\t Found closing BRACE } $commandname\n" if($tracingMode);
-
- # decrease the indentation (if appropriate)
- &decrease_indent($commandname);
-
- if($countzeros==1){
- $masterIndentationArrayOfHashes[-1]{'matchedbraces'} = $matchedbraces;
- $masterIndentationArrayOfHashes[-1]{'countzeros'} = $countzeros;
- $masterIndentationArrayOfHashes[-1]{'lookforelse'} = $lookforelse;
- }
- }
-
- if(@masterIndentationArrayOfHashes){
- if($masterIndentationArrayOfHashes[-1]{'type'} eq 'splitbraces'){
- # tracing mode
- print $logfile "Line $lineCounter\t Searching for closing BRACE } $masterIndentationArrayOfHashes[-1]{name}\n" if($tracingMode);
- }
- }
- }
- }
-}
-
-sub check_for_else{
- # PURPOSE: Check for an else clause
- #
- # Some commands have the form
- #
- # \mycommand{
- # if this
- # }
- # {
- # else this
- # }
- #
- # so we need to look for the else bit, and set
- # the indentation appropriately.
- #
- # We only perform this check if there's something
- # in the array @masterIndentationArrayOfHashes, and if
- # the line itself is not a command, or comment,
- # and if it begins with {
-
- if(scalar(@masterIndentationArrayOfHashes)
- and !($_ =~ m/^\s*(\\)?(.*?)(\[|{|=)/
- and ($checkunmatched{$2} or $checkunmatchedELSE{$2}
- or $alwaysLookforSplitBraces))
- and $_ =~ m/^\s*{/
- and $_ !~ m/^\s*%/
- ) {
- # get the details of the most recent command name
- my $matchedbraces = $masterIndentationArrayOfHashes[-1]{'matchedbraces'};
- my $countzeros = $masterIndentationArrayOfHashes[-1]{'countzeros'};
- my $lookforelse= $masterIndentationArrayOfHashes[-1]{'lookforelse'};
-
- # increase indentation
- if($lookforelse and $countzeros==1) {
- #&increase_indent($commandname);
- }
-
- # put the array back together
- $masterIndentationArrayOfHashes[-1]{'matchedbraces'} = $matchedbraces;
- $masterIndentationArrayOfHashes[-1]{'countzeros'} = $countzeros;
- $masterIndentationArrayOfHashes[-1]{'lookforelse'} = $lookforelse;
- }
-}
-
-sub at_beg_of_env_or_eq{
- # PURPOSE: Check if we're at the BEGINning of an environment
- # or at the BEGINning of a displayed equation \[
- #
- # This subroutine checks for matches of the form
- #
- # \begin{environmentname}
- # or
- # \[
- #
- # It also checks to see if the current environment
- # should have alignment delimiters; if so, we need to turn
- # ON the $delimiter switch
-
- # How to read
- # m/^\s*(\$)?\\begin{(.*?)}/
- #
- # ^ beginning of a line
- # \s* any white spaces (possibly none)
- # (\$)? possibly a $ symbol, but not required
- # \\begin{(.*)?} \begin{environmentname}
- #
- # How to read
- # m/^\s*()(\\\[)/
- #
- # ^ beginning of a line
- # \s* any white spaces (possibly none)
- # () empty just so that $1 and $2 are defined
- # (\\\[) \[ there are lots of \ because both \ and [ need escaping
- # \\begin{\\?(.*?)} \begin{something} where something could start
- # with a backslash, e.g \my@env@ which can happen
- # in a style or class file, for example
-
- if( ( ( $_ =~ m/^\s*(\$)?\\begin\{\\?(.*?)\}/ and $_ !~ m/\\end\{$2\}/)
- or ($_=~ m/^\s*()(\\\[)/ and $_ !~ m/\\\]/) )
- and $_ !~ m/^\s*%/ ) {
- # tracing mode
- print $logfile "Line $lineCounter\t \\begin{environment} found: $2 \n" if($tracingMode);
-
- # increase the indentation
- &increase_indent({name=>$2,
- type=>"environment",
- begin=>"\\begin{$2}",
- end=>"\\end{$2}"});
-
- # check for verbatim-like environments
- if($verbatimEnvironments{$2}){
- $inverbatim = 1;
- # tracing mode
- print $logfile "Line $lineCounter\t \\begin{verbatim-like} found, $2, switching ON verbatim \n" if($tracingMode);
-
- # remove the key and value from %lookForAlignDelims hash
- # to avoid any further confusion
- if($lookForAlignDelims{$2}) {
- print $logfile "WARNING\n\t Line $lineCounter\t $2 is in *both* lookForAlignDelims and verbatimEnvironments\n";
- print $logfile "\t\t\t ignoring lookForAlignDelims and prioritizing verbatimEnvironments\n";
- print $logfile "\t\t\t Note that you only get this message once per environment\n";
- delete $lookForAlignDelims{$2};
- }
- }
- }
-}
-
-sub at_end_of_env_or_eq{
- # PURPOSE: Check if we're at the END of an environment
- # or at the END of a displayed equation \]
- #
- # This subroutine checks for matches of the form
- #
- # \end{environmentname}
- # or
- # \]
- #
- # Note: environmentname can begin with a backslash
- # which might happen in a sty or cls file.
- #
- # It also checks to see if the current environment
- # had alignment delimiters; if so, we need to turn
- # OFF the $delimiter switch
-
- return unless @masterIndentationArrayOfHashes;
- print $logfile "Line $lineCounter\t looking for \\end{$masterIndentationArrayOfHashes[-1]{name}} \n" if($tracingMode);
-
- if( ($_ =~ m/^\s*\\end\{\\?(.*?)\}/ or $_=~ m/^(\\\])/) and $_ !~ m/\s*^%/) {
- # check if we're at the end of a verbatim-like environment
- if($verbatimEnvironments{$1}) {
- $inverbatim = 0;
- # tracing mode
-
- print $logfile "Line $lineCounter\t \\end{verbatim-like} found: $1, switching off verbatim \n" if($tracingMode);
- print $logfile "Line $lineCounter\t removing leading spaces \n" if($tracingMode);
- #s/^\ *//;
- s/^\t+// if($_ ne "");
- s/^\s+// if($_ ne "");
- }
-
- # check if we're in an environment that is looking
- # to indent after each \item
- if(scalar(@masterIndentationArrayOfHashes) and $itemNames{$masterIndentationArrayOfHashes[-1]{name}}) {
- &decrease_indent($masterIndentationArrayOfHashes[-1]{name});
- }
-
- # if we're at the end of an environment that receives no additional indent, log it, and move on
- if($noAdditionalIndent{$1}){
- print $logfile "Line $lineCounter\t \\end{$1} finished a no-additional-indent environment (see noAdditionalIndent)\n" if($tracingMode);
- }
-
- # some commands contain \end{environmentname}, which
- # can cause a problem if \begin{environmentname} was not
- # started previously; if @masterIndentationArrayOfHashes is empty,
- # then we don't need to check for \end{environmentname}
- if(@masterIndentationArrayOfHashes) {
- # check to see if \end{environment} fits with most recent \begin{...}
- my %previousEnvironment = %{$masterIndentationArrayOfHashes[-1]};
-
- # check to see if we need to turn off alignment
- # delimiters and output the current block
- if($masterIndentationArrayOfHashes[-1]{alignmentDelimiters} and ($previousEnvironment{name} eq $1)) {
- &print_aligned_block();
- }
-
- # tracing mode
- print $logfile "Line $lineCounter\t \\end{environment} found: $1 \n" if($tracingMode and !$verbatimEnvironments{$1});
-
- # check to see if \end{environment} fits with most recent \begin{...}
- if($previousEnvironment{name} eq $1) {
- # decrease the indentation (if appropriate)
- print $logfile "Line $lineCounter\t removed $1 from Indentation array\n" if($tracingMode);
- &decrease_indent($1);
- } else {
- # otherwise put the environment name back on the stack
- print $logfile "Line $lineCounter\t WARNING: \\end{$1} found on its own line, not matched to \\begin{$previousEnvironment{name}}\n" unless ($delimiters or $inverbatim or $inIndentBlock or $1 eq "\\\]");
- }
-
- # need a special check for \[ and \]
- if($1 eq "\\\]") {
- &decrease_indent($1);
- }
- }
-
- # if we're at the end of the document, we remove all current
- # indentation- this is especially prominent in examples that
- # have headings, and the user has chosen to indentAfterHeadings
- if($1 eq "document" and !$inFileContents and !$inpreamble and !$delimiters and !$inverbatim and !$inIndentBlock and @masterIndentationArrayOfHashes) {
- @masterIndentationArrayOfHashes=();
-
- # tracing mode
- if($tracingMode) {
- print $logfile "Line $lineCounter\t \\end{$1} found, emptying indentation array \n" unless ($delimiters or $inverbatim or $inIndentBlock or $1 eq "\\\]");
- }
- }
- }
-}
-
-sub print_aligned_block{
- # PURPOSE: this subroutine does a few things related
- # to printing blocks of code that contain
- # delimiters, such as align, tabular, etc
- #
- # It does the following
- # - turns off delimiters switch
- # - processes the block
- # - deletes the block
- $delimiters=0;
-
- # tracing mode
- print $logfile "Line $lineCounter\t Delimiter body FINISHED: $masterIndentationArrayOfHashes[-1]{name}\n" if($tracingMode);
-
- # print the current FORMATTED block
- my @block = &format_block(@{$masterIndentationArrayOfHashes[-1]{block}});
- foreach $line (@block) {
- # add the indentation and add the
- # each line of the formatted block
- # to the output
- # unless this would only create trailing whitespace and the
- # corresponding option is set
- unless ($line =~ m/^$/ and $removeTrailingWhitespace) {
- $line =&current_indentation().$line;
- }
- push(@lines,$line);
- }
-}
-
-sub format_block{
- # PURPOSE: Format a delimited environment such as the
- # tabular or align environment that contains &
- #
- # INPUT: @block array containing unformatted block
- # from, for example, align, or tabular
- # OUTPUT: @formattedblock array containing FORMATTED block
-
- # @block is the input
- my @block=@_;
-
- # tracing mode
- print $logfile "\t\tFormatting alignment block: $masterIndentationArrayOfHashes[-1]{name}\n" if($tracingMode);
-
- # step the line counter back to the beginning of the block-
- # it will be increased back to the end of the block in the
- # loop later on: foreach $row (@tmpblock)
- $lineCounter -= scalar(@block);
-
- # local array variables
- my @formattedblock;
- my @tmprow=();
- my @tmpblock=();
- my @maxmstringsize=();
- my @ampersandCount=();
-
- # local scalar variables
- my $alignrowcounter=-1;
- my $aligncolcounter=-1;
- my $tmpstring;
- my $row;
- my $column;
- my $maxmcolstrlength;
- my $i;
- my $j;
- my $fmtstring;
- my $linebreak;
- my $maxNumberAmpersands = 0;
- my $currentNumberAmpersands;
- my $trailingcomments;
-
- # local hash table
- my %stringsize=();
-
- # loop through the block and count & per line- store the biggest
- # NOTE: this needs to be done in its own block so that
- # we can know what the maximum number of & in the block is
- foreach $row (@block) {
- # delete trailing comments
- $trailingcomments='';
- if($row =~ m/((?<!\\)%.*$)/) {
- $row =~ s/((?<!\\)%.*)/%TC/;
- $trailingcomments=$1;
- }
-
- # reset temporary counter
- $currentNumberAmpersands=0;
-
- # count & in current row (exclude \&)
- $currentNumberAmpersands++ while ($row =~ /(?<!\\)&/g);
-
- # store the ampersand count for future
- push(@ampersandCount,$currentNumberAmpersands);
-
- # overwrite maximum count if the temp count is higher
- $maxNumberAmpersands = $currentNumberAmpersands if($currentNumberAmpersands > $maxNumberAmpersands );
-
- # put trailing comments back on
- if($trailingcomments){
- $row =~ s/%TC/$trailingcomments/;
- }
- }
-
- # tracing mode
- print $logfile "\t\tmaximum number of & in any row: $maxNumberAmpersands\n" if($tracingMode);
-
- # loop through the lines in the @block
- foreach $row (@block){
- # get the ampersand count
- $currentNumberAmpersands = shift(@ampersandCount);
-
- # increment row counter
- $alignrowcounter++;
-
- # clear the $linebreak variable
- $linebreak='';
-
- # check for line break \\
- # and don't mess with a line that doesn't have the maximum
- # number of &
- if($row =~ m/\\\\/ and $currentNumberAmpersands==$maxNumberAmpersands ) {
- # remove \\ and all characters that follow
- # and put it back in later, once the measurement
- # has been done
- $row =~ s/(\\\\.*)//;
- $linebreak = $1;
- }
-
- if($currentNumberAmpersands==$maxNumberAmpersands) {
-
- # remove trailing comments
- $trailingcomments='';
- if($row =~ m/((?<!\\)%.*$)/) {
- $row =~ s/((?<!\\)%.*)/%TC/;
- $trailingcomments=$1;
- }
-
- # separate the row at each &, but not at \&
- @tmprow = split(/(?<!\\)&/,$row);
-
- # reset column counter
- $aligncolcounter=-1;
-
- # loop through each column element
- # removing leading and trailing space
- foreach $column (@tmprow) {
- # increment column counter
- $aligncolcounter++;
-
- # remove leading and trailing space from element
- $column =~ s/^\s+//;
- $column =~ s/\s+$//;
-
- # assign string size to the array
- $stringsize{$alignrowcounter.$aligncolcounter}=length($column);
- if(length($column)==0){
- $column=" ";
- }
-
- # put the row back together
- if ($aligncolcounter ==0){
- $tmpstring = $column;
- } else {
- $tmpstring .= "&".$column;
- }
- }
-
-
- # put $linebreak back on the string, now that
- # the measurement has been done
- $tmpstring .= $linebreak;
-
- # put trailing comments back on
- if($trailingcomments) {
- $tmpstring =~ s/%TC/$trailingcomments/;
- }
-
- push(@tmpblock,$tmpstring);
- } else {
- # if there are no & then use the
- # NOFORMATTING token
- # remove leading space
- s/^\s+//;
- push(@tmpblock,$row."NOFORMATTING");
- }
- }
-
- # calculate the maximum string size of each column
- for($j=0;$j<=$aligncolcounter;$j++) {
- $maxmcolstrlength=0;
- for($i=0; $i<=$alignrowcounter;$i++) {
- # make sure the stringsize is defined
- if(defined $stringsize{$i.$j}) {
- if ($stringsize{$i.$j}>$maxmcolstrlength) {
- $maxmcolstrlength = $stringsize{$i.$j};
- }
- }
- }
- push(@maxmstringsize,$maxmcolstrlength);
- }
-
- # 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 %-"
- $fmtstring = join("s & %-",@maxmstringsize);
-
- # add an s to the end, and a newline
- $fmtstring .= "s ";
-
- # add %- to the beginning
- $fmtstring = "%-".$fmtstring;
-
- # process the @tmpblock of aligned material
- foreach $row (@tmpblock) {
- $linebreak='';
- # check for line break \\
- if($row =~ m/\\\\/) {
- # remove \\ and all characters that follow
- # and put it back in later
- $row =~ s/(\\\\.*$)//;
- $linebreak = $1;
- }
-
- if($row =~ m/NOFORMATTING/) {
- $row =~ s/NOFORMATTING//;
- $tmpstring=$row;
-
- # tracing mode
- print $logfile "\t\tLine $lineCounter\t maximum number of & NOT found- not aligning delimiters \n" if($tracingMode);
- } else {
- # remove trailing comments
- $trailingcomments='';
- if($row =~ m/((?<!\\)%.*$)/) {
- $row =~ s/((?<!\\)%.*)/%TC/;
- $trailingcomments=$1;
- }
-
- $tmpstring = sprintf($fmtstring,split(/(?<!\\)&/,$row)).$linebreak."\n";
-
- # remove space before \\ if specified in alignDoubleBackSlash
- if($masterIndentationArrayOfHashes[-1]{alignDoubleBackSlash}==0){
- print $logfile "\t\tLine $lineCounter\t removing space before \\\\ (see $masterIndentationArrayOfHashes[-1]{name} alignDoubleBackSlash)\n" if($tracingMode);
- $tmpstring =~ s/\s*\\\\/\\\\/;
- # some users may like to put a number of spaces before \\
- if($masterIndentationArrayOfHashes[-1]{spacesBeforeDoubleBackSlash}){
- my $spaceString;
- for($j=1;$j<=$masterIndentationArrayOfHashes[-1]{spacesBeforeDoubleBackSlash};$j++) {
- $spaceString .= ' ';
- }
- print $logfile "\t\tLine $lineCounter\t adding $masterIndentationArrayOfHashes[-1]{spacesBeforeDoubleBackSlash} ",$masterIndentationArrayOfHashes[-1]{spacesBeforeDoubleBackSlash}>1?"spaces":"space"," before \\\\ (see $masterIndentationArrayOfHashes[-1]{name} spacesBeforeDoubleBackSlash)\n" if($tracingMode);
- $tmpstring =~ s/\\\\/$spaceString\\\\/;
- }
- }
-
- # put trailing comments back on
- if($trailingcomments) {
- $tmpstring =~ s/%TC/$trailingcomments/;
- }
-
- # tracing mode
- print $logfile "\t\tLine $lineCounter\t Found maximum number of & so aligning delimiters\n" if($tracingMode);
- }
-
- # remove trailing whitespace
- if ($removeTrailingWhitespace) {
- print $logfile "\t\tLine $lineCounter\t removing trailing whitespace from delimiter aligned line\n" if ($tracingMode);
- $tmpstring =~ s/\s+$/\n/;
- }
-
- push(@formattedblock,$tmpstring);
-
- # increase the line counter
- $lineCounter++;
- }
-
- # return the formatted block
- @formattedblock;
-}
-
-sub increase_indent{
- # PURPOSE: Adjust the indentation
- # of the current environment, command, etc;
- # check that it's not an environment
- # that doesn't want indentation.
-
- my %infoHash = %{pop(@_)};
- my $command = $infoHash{name};
-
- # check for conflicting hash keys
- &check_conflicting_keys($command);
-
- # quick check for verbatim Environment
- if($inverbatim){
- print $logfile "Line $lineCounter\t currently inverbatim environment, not increasing indentation\n" if($tracingMode);
- return;
- }
-
- if($indentRules{$command}) {
- # tracing mode
- print $logfile "Line $lineCounter\t increasing indent using rule for $command (see indentRules)\n" if($tracingMode);
- } else {
- # default indentation
- if(!($noAdditionalIndent{$command} or $verbatimEnvironments{$command})) {
- # tracing mode
- print $logfile "Line $lineCounter\t increasing indent using defaultIndent\n" if($tracingMode);
- } elsif($noAdditionalIndent{$command}) {
- # tracing mode
- print $logfile "Line $lineCounter\t no additional indent added for $command (see noAdditionalIndent)\n" if($tracingMode);
- }
- }
-
- # add to the master array of hashes
- push(@masterIndentationArrayOfHashes,\%infoHash);
-
- # handle the keys slightly different when dealing with environments or commands
- if($infoHash{type} eq 'environment'){
- # environments
- if(!$noAdditionalIndent{$command}){
- $masterIndentationArrayOfHashes[-1]{indent} = $indentRules{$command}||$defaultIndent;
- }
- # check to see if we need to look for alignment delimiters
- if($lookForAlignDelims{$command}){
- # there are two ways to complete the lookForAlignDelims field, either as a scalar
- # or as a hash, so that we can check for alignDoubleBackSlash.
- #
- # tabular:
- # delims: 1
- # alignDoubleBackSlash: 1
- #
- # or, simply,
- #
- # tabular: 1
- #
- # We need to perform a check to see which has been done.
- if(ref($lookForAlignDelims{$command}) eq 'HASH'){
- # tabular:
- # delims: 1
- # alignDoubleBackSlash: 1
- $masterIndentationArrayOfHashes[-1]{alignmentDelimiters}=defined $lookForAlignDelims{$command}{delims}?$lookForAlignDelims{$command}{delims}:1;
- $masterIndentationArrayOfHashes[-1]{alignDoubleBackSlash}=defined $lookForAlignDelims{$command}{alignDoubleBackSlash}?$lookForAlignDelims{$command}{alignDoubleBackSlash}:1;
- $masterIndentationArrayOfHashes[-1]{spacesBeforeDoubleBackSlash}=$lookForAlignDelims{$command}{spacesBeforeDoubleBackSlash}||0;
- } else {
- # tabular: 1
- $masterIndentationArrayOfHashes[-1]{alignmentDelimiters}=1;
- $masterIndentationArrayOfHashes[-1]{alignDoubleBackSlash}=1;
- }
- if($masterIndentationArrayOfHashes[-1]{alignmentDelimiters}==1){
- # tracing mode
- print $logfile "Line $lineCounter\t Delimiter environment started: $command (see lookForAlignDelims)\n" if($tracingMode);
- }
- }
- } else {
- # commands, headings, etc
- if(!$noAdditionalIndent{$command}){
- $masterIndentationArrayOfHashes[-1]{indent} = $indentRules{$command}||$defaultIndent;
- }
- }
-
- # details of noAdditionalIndent to the main hash
- if($noAdditionalIndent{$command}){
- $masterIndentationArrayOfHashes[-1]{noAdditionalIndent} = 'yes';
- }
-}
-
-sub decrease_indent{
- # PURPOSE: Adjust the indentation
- # of the current environment;
- # check that it's not an environment
- # that doesn't want indentation.
-
- # if there is no evidence of indentation, then return
- return unless(scalar(@masterIndentationArrayOfHashes));
-
- # otherwise get details of the most recent command, environment, item, if, heading, etc
- my $command = pop(@_);
-
- if(!$inverbatim) {
- print $logfile "Line $lineCounter\t removing ", $masterIndentationArrayOfHashes[-1]{name}, " from masterIndentationArrayOfHashes\n" if($tracingMode);
- pop(@masterIndentationArrayOfHashes);
- # tracing mode
- if($tracingMode) {
- if(@masterIndentationArrayOfHashes) {
- print $logfile "Line $lineCounter\t decreasing masterIndentationArrayOfHashes to: ",&current_indentation_names(),"\n";
- } else {
- print $logfile "Line $lineCounter\t masterIndentationArrayOfHashes now empty \n";
- }
- }
- }
-}
-
-sub current_indentation{
- # PURPOSE: loop through masterIndentationArrayOfHashes and
- # pull out the indentation, and join it together
-
- # if the masterIndentationArrayOfHashes is empty, return an empty string
- return "" unless(@masterIndentationArrayOfHashes);
-
- my $indent;
- foreach my $env (@masterIndentationArrayOfHashes){
- $indent .= defined($env->{indent})?$env->{indent}:'';
- }
- return $indent;
-}
-
-sub current_indentation_names{
- # PURPOSE: loop through masterIndentationArrayOfHashes and
- # pull out the list of environment/command names
- return "masterIndentationArrayOfHashes empty" unless(@masterIndentationArrayOfHashes);
-
- my $listOfNames;
- foreach my $env (@masterIndentationArrayOfHashes){
- $listOfNames .= $env->{name};
- $listOfNames .= "," unless $env == $masterIndentationArrayOfHashes[-1];
- }
- return $listOfNames;
-}
-
-sub check_conflicting_keys{
- # PURPOSE: users may sometimes put an environment in two
- # hash keys; for example, they might put lstlistings
- # in both indentRules and in noAdditionalIndent;
- # in which case, we need a hierachy.
- #
- # This subroutine implements such a hierachy,
- # and deletes the redundant key.
-
- # if the user has specified $indentRules{$command} and
- # $noAdditionalIndent{$command} then they are a bit confused-
- # we remove the $indentRules{$command} and assume that they
- # want $noAdditionalIndent{$command}
-
- my $command = pop(@_);
-
- if(scalar($indentRules{$command}) and $noAdditionalIndent{$command}) {
- print $logfile "WARNING\n\t Line $lineCounter\t $command is in *both* indentRules and noAdditionalIndent\n";
- print $logfile "\t\t\t ignoring indentRules and prioritizing noAdditionalIndent\n";
- print $logfile "\t\t\t Note that you only get this message once per command/environment\n";
-
- # remove the key and value from %indentRules hash
- # to avoid any further confusion
- delete $indentRules{$command};
- }
-
- # if the command is in verbatimEnvironments and in indentRules then
- # remove it from %indentRules hash
- # to avoid any further confusion
- if($indentRules{$command} and $verbatimEnvironments{$command}) {
- # remove the key and value from %indentRules hash
- # to avoid any further confusion
- print $logfile "WARNING\n\t Line $lineCounter\t $command is in *both* indentRules and verbatimEnvironments\n";
- print $logfile "\t\t\t ignoring indentRules and prioritizing verbatimEnvironments\n";
- print $logfile "\t\t\t Note that you only get this message once per environment\n";
- delete $indentRules{$command};
- }
-
-}