diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/latexindent |
Initial commit
Diffstat (limited to 'support/latexindent')
68 files changed, 14822 insertions, 0 deletions
diff --git a/support/latexindent/LatexIndent/AlignmentAtAmpersand.pm b/support/latexindent/LatexIndent/AlignmentAtAmpersand.pm new file mode 100644 index 0000000000..8b8e387d09 --- /dev/null +++ b/support/latexindent/LatexIndent/AlignmentAtAmpersand.pm @@ -0,0 +1,525 @@ +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 utf8; +use Unicode::GCString; +use Data::Dumper; +use Exporter qw/import/; +use List::Util qw(max); +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 LatexIndent::LogFile qw/$logger/; +our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321 +our @EXPORT_OK = qw/align_at_ampersand find_aligned_block double_back_slash_else/; +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} + $logger->trace('*Searching for ALIGNED blocks marked by comments')if($is_t_switch_active); + $logger->trace(Dumper(\%{$masterSettings{lookForAlignDelims}})) if($is_tt_switch_active); + while( my ($alignmentBlock,$yesno)= each %{$masterSettings{lookForAlignDelims}}){ + if(ref $yesno eq "HASH"){ + $yesno = (defined ${$yesno}{delims} ) ? ${$yesno}{delims} : 1; + } + if($yesno){ + $logger->trace("looking for %*\\begin\{$alignmentBlock\} environments"); + + my $alignmentRegExp = 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/$alignmentRegExp/sx){ + + ${$self}{body} =~ s/ + $alignmentRegExp + / + # create a new Environment object + my $alignmentBlockObj = LatexIndent::AlignmentAtAmpersand->new( begin=>$1, + body=>$2, + end=>$3, + name=>$alignmentBlock, + modifyLineBreaksYamlName=>"environments", + linebreaksAtEnd=>{ + begin=>1, + body=>1, + end=>0, + }, + ); + + # log file output + $logger->trace("*Alignment block found: %*\\begin\{$alignmentBlock\}") if $is_t_switch_active; + + # the settings and storage of most objects has a lot in common + $self->get_settings_and_store_new_object($alignmentBlockObj); + + ${@{${$self}{children}}[-1]}{replacementText}; + /xseg; + } + } else { + $logger->trace("*not* looking for $alignmentBlock as $alignmentBlock:$yesno"); + } + } + return; +} + +sub yaml_modify_line_breaks_settings{ + return; +} + +sub tasks_particular_to_each_object{ + return; +} + +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 @maximumColumnWidth = (0) x ($maximumNumberOfAmpersands+1); + my @maximumColumnWidthMC = (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; + } + + # remove any trailing comments + my $trailingComments; + if($_ =~ m/$trailingCommentRegExp/ ){ + $_ =~ s/($trailingCommentRegExp)//; + $trailingComments = $1; + } + + # count the number of ampersands in the current row + my $numberOfAmpersands = () = $_ =~ /(?<!\\)&/g; + + # switch for multiColumGrouping + my $multiColumnGrouping = ($_ =~ m/\\multicolumn/ and ${$self}{multiColumnGrouping}); + my $alignRowsWithoutMaxDelims = ${$self}{alignRowsWithoutMaxDelims}; + + # by default, the stripped row is simply the current row + my $strippedRow = $_; + + # loop through the columns + my $columnCount = 0; + + # format switch off by default + my $formatRow = 0; + + # store the column sizes for measuring and comparison purposes + my @columnSizes = (); + + # we will store the columns in each row + my @columns; + + # need to have at least one ampersand, or contain a \multicolumn command + if( ($_ =~ m/(?<!\\)&/ and ( ($numberOfAmpersands == $maximumNumberOfAmpersands)||$multiColumnGrouping||$alignRowsWithoutMaxDelims ) ) + or + ($multiColumnGrouping and $alignRowsWithoutMaxDelims) ){ + # remove space at the beginning of a row, surrounding &, and at the end of the row + $_ =~ s/(?<!\\)\h*(?<!\\)&\h*/&/g; + $_ =~ s/^\h*//g; + $_ =~ s/\h*$//g; + + # if the line finishes with an &, then add an empty space, + # otherwise the column count is off + $_ .= ($_ =~ m/(?<!\\)&$/ ? " ":q()); + + # store the columns, which are either split by & + # or otherwise simply the current line, if for example, the current line simply + # contains \multicolumn{8}... \\ (see test-cases/texexchange/366841-zarko.tex, for example) + @columns = ($_ =~ m/(?<!\\)&/ ? split(/(?<!\\)&/,$_) : $_); + + # empty the white-space-stripped row + $strippedRow = ''; + foreach my $column (@columns){ + # 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 + # reference: http://www.perl.com/pub/2012/05/perlunicook-unicode-column-width-for-printing.html + my $gcs = Unicode::GCString->new($column); + my $columnWidth = $gcs->columns(); + + # multicolumn cells need a bit of special care + if($multiColumnGrouping and $column =~ m/\\multicolumn\{(\d+)\}/ and $1>1){ + $maximumColumnWidthMC[$columnCount] = $columnWidth if( defined $maximumColumnWidthMC[$columnCount] and ($columnWidth > $maximumColumnWidthMC[$columnCount]) ); + $columnWidth = 1 if($multiColumnGrouping and ($column =~ m/\\multicolumn\{(\d+)\}/)); + } + + # store the maximum column width + $maximumColumnWidth[$columnCount] = $columnWidth if( defined $maximumColumnWidth[$columnCount] and ($columnWidth > $maximumColumnWidth[$columnCount]) ); + + # put the row back together, using " " if the column is empty + $strippedRow .= ($columnCount>0 ? "&" : q() ).($columnWidth > 0 ? $column: " "); + + # store the column width + $columnSizes[$columnCount] = $columnWidth; + + # move on to the next column + if($multiColumnGrouping and ($column =~ m/\\multicolumn\{(\d+)\}/)){ + # columns that are within the multiCol statement receive a width of -1 + for my $i (($columnCount+1)..($columnCount+$1)){ + $columnSizes[$i] = -1; + } + # update the columnCount to account for the multiColSpan + $columnCount += $1; + } else { + $columnCount++; + } + } + + # toggle the formatting switch + $formatRow = 1; + } elsif($endPiece and ${$self}{alignDoubleBackSlash}){ + # otherwise a row could contain no ampersands, but would still desire + # the \\ to be aligned, see test-cases/alignment/multicol-no-ampersands.tex + @columns = $_; + $formatRow = 1; + } + + # store the information + push(@formattedBody,{ + row=>$strippedRow, + format=>$formatRow, + multiColumnGrouping=>$multiColumnGrouping, + columnSizes=>\@columnSizes, + columns=>\@columns, + endPiece=>($endPiece ? $endPiece :q() ), + trailingComment=>($trailingComments ? $trailingComments :q() )}); + } + + # output some of the info so far to the log file + $logger->trace("*Alignment at ampersand routine for ${$self}{name} (see lookForAlignDelims)") if $is_t_switch_active; + $logger->trace("Maximum column sizes of horizontally stripped formatted block (${$self}{name}): @maximumColumnWidth") if $is_t_switch_active; + $logger->trace("align at ampersand: ${$self}{lookForAlignDelims}") if $is_t_switch_active; + $logger->trace("align at \\\\: ${$self}{alignDoubleBackSlash}") if $is_t_switch_active; + $logger->trace("spaces before \\\\: ${$self}{spacesBeforeDoubleBackSlash}") if $is_t_switch_active; + $logger->trace("multi column grouping: ${$self}{multiColumnGrouping}") if $is_t_switch_active; + $logger->trace("align rows without maximum delimeters: ${$self}{alignRowsWithoutMaxDelims}") if $is_t_switch_active; + $logger->trace("spaces before ampersand: ${$self}{spacesBeforeAmpersand}") if $is_t_switch_active; + $logger->trace("spaces after ampersand: ${$self}{spacesAfterAmpersand}") if $is_t_switch_active; + $logger->trace("justification: ${$self}{justification}") if $is_t_switch_active; + + # acount for multicolumn grouping, if the appropriate switch is set + if(${$self}{multiColumnGrouping}){ + foreach(@formattedBody){ + if(${$_}{format} and ${$_}{row} !~ m/^\h*$/){ + + # set a columnCount, which will vary depending on multiColumnGrouping settings or not + my $columnCount=0; + + # loop through the columns + foreach my $column (@{${$_}{columns}}){ + # calculate the width of the current column + my $gcs = Unicode::GCString->new($column); + my $columnWidth = $gcs->columns(); + + # check for multiColumnGrouping + if(${$_}{multiColumnGrouping} and $column =~ m/\\multicolumn\{(\d+)\}/ and $1>1){ + my $multiColSpan = $1; + + # for example, \multicolumn{3}{c}{<stuff>} spans 3 columns, so + # the maximum column needs to account for this (subtract 1 because of 0 index in perl arrays) + my $columnMax = $columnCount+$multiColSpan-1; + + # groupingWidth contains the total width of column sizes grouped + # underneath the \multicolumn{} statement + my $groupingWidth = 0; + my $maxGroupingWidth = 0; + foreach (@formattedBody){ + $groupingWidth = 0; + + # loop through the columns covered by the multicolumn statement + foreach my $j ($columnCount..$columnMax){ + if( defined @{${$_}{columnSizes}}[$j] + and + @{${$_}{columnSizes}}[$j] >= 0 + and + ${$_}{format} + ){ + $groupingWidth += (defined $maximumColumnWidth[$j] ? $maximumColumnWidth[$j] : 0); + } else { + $groupingWidth = 0; + } + } + + # update the maximum grouping width + $maxGroupingWidth = $groupingWidth if($groupingWidth > $maxGroupingWidth); + + # the cells that receive multicolumn grouping need extra padding; in particular + # if the justification is *left*: + # the *last* cell of the multicol group receives the padding + # if the justification is *right*: + # the *first* cell of the multicol group receives the padding + # + # this motivates the introduction of $columnOffset, which is + # 0 if justification is left + # $multiColSpan if justification is right + my $columnOffset = (${$self}{justification} eq "left") ? $columnMax : $columnCount; + if(defined @{${$_}{columnSizes}}[$columnMax] and ($columnWidth > ($groupingWidth+(${$self}{spacesBeforeAmpersand}+1+${$self}{spacesAfterAmpersand})*($multiColSpan-1)) ) and @{${$_}{columnSizes}}[$columnMax] >= 0){ + my $multiColPadding = $columnWidth-$groupingWidth-(${$self}{spacesBeforeAmpersand}+1+${$self}{spacesAfterAmpersand})*($multiColSpan-1); + + # it's possible that multiColPadding might already be assigned; in which case, + # we need to check that the current value of $multiColPadding is greater than the existing one + if(defined @{${$_}{multiColPadding}}[$columnOffset]){ + @{${$_}{multiColPadding}}[$columnOffset] = max($multiColPadding,@{${$_}{multiColPadding}}[$columnOffset]); + } else { + @{${$_}{multiColPadding}}[$columnOffset] = $multiColPadding; + } + + # also need to account for maximum column width *including* other multicolumn statements + if($maximumColumnWidthMC[$columnCount]>$columnWidth and $column !~ m/\\multicolumn\{(\d+)\}/){ + @{${$_}{multiColPadding}}[$columnOffset] += ($maximumColumnWidthMC[$columnCount]-$columnWidth); + } + } + } + # update it to account for the ampersands and the spacing either side of ampersands + $maxGroupingWidth += ($multiColSpan-1)*(${$self}{spacesBeforeAmpersand}+1+${$self}{spacesAfterAmpersand}); + + # store the maxGroupingWidth for use in the next loop + @{${$_}{maxGroupingWidth}}[$columnCount] = $maxGroupingWidth; + + # update the columnCount to account for the multiColSpan + $columnCount += $multiColSpan - 1; + } + + # increase the column count + $columnCount++; + } + } + } + } + + # the maximum row width will be used in aligning (or not) the \\ + my $maximumRowWidth = 0; + + # now that the multicolumn widths have been accounted for, loop through the body + foreach(@formattedBody){ + if(${$_}{format} and ${$_}{row} !~ m/^\h*$/){ + + # set a columnCount, which will vary depending on multiColumnGrouping settings or not + my $columnCount=0; + my $tmpRow = q(); + + # loop through the columns + foreach my $column (@{${$_}{columns}}){ + # calculate the width of the current column + my $gcs = Unicode::GCString->new($column); + my $columnWidth = $gcs->columns(); + + # reset the column padding + my $padding = q(); + + # check for multiColumnGrouping + if(${$_}{multiColumnGrouping} and $column =~ m/\\multicolumn\{(\d+)\}/ and $1>1){ + my $multiColSpan = $1; + + # groupingWidth contains the total width of column sizes grouped + # underneath the \multicolumn{} statement + my $maxGroupingWidth = ${${$_}{maxGroupingWidth}}[$columnCount]; + + # it's possible to have situations such as + # + # \multicolumn{3}{l}{one} & \multicolumn{3}{l}{two} & \\ + # \multicolumn{6}{l}{one} & \\ + # + # in which case we need to loop through the @maximumColumnWidthMC + my $groupingWidthMC = 0; + my $multicolsEncountered =0; + for ($columnCount..($columnCount + ($multiColSpan-1))){ + if(defined $maximumColumnWidthMC[$_]){ + $groupingWidthMC += $maximumColumnWidthMC[$_]; + $multicolsEncountered++ if $maximumColumnWidthMC[$_]>0; + } + } + + # need to account for (spacesBeforeAmpersands) + length of ampersands (which is 1) + (spacesAfterAmpersands) + $groupingWidthMC += ($multicolsEncountered-1)*(${$self}{spacesBeforeAmpersand}+1+${$self}{spacesAfterAmpersand}); + + # set the padding; we need + # maximum( $maxGroupingWidth, $maximumColumnWidthMC[$columnCount] ) + my $maxValueToUse = 0; + if(defined $maximumColumnWidthMC[$columnCount]){ + $maxValueToUse = max($maxGroupingWidth,$maximumColumnWidthMC[$columnCount],$groupingWidthMC); + } else { + $maxValueToUse = $maxGroupingWidth; + } + + # calculate the padding + $padding = " " x ( $maxValueToUse >= $columnWidth ? $maxValueToUse - $columnWidth : 0 ); + + # to the log file + if($is_tt_switch_active){ + $logger->trace("*---------column-------------"); + $logger->trace($column); + $logger->trace("multiColSpan: $multiColSpan"); + $logger->trace("groupingWidthMC: $groupingWidthMC"); + $logger->trace("padding length: ",$maxValueToUse - $columnWidth); + $logger->trace("multicolsEncountered: $multicolsEncountered"); + $logger->trace("maxValueToUse: $maxValueToUse"); + $logger->trace("maximumColumnWidth: ",join(",",@maximumColumnWidth)); + $logger->trace("maximumColumnWidthMC: ",join(",",@maximumColumnWidthMC)); + } + + # update the columnCount to account for the multiColSpan + $columnCount += $multiColSpan - 1; + } else { + # compare the *current* column width with the *maximum* column width + $padding = " " x (defined $maximumColumnWidth[$columnCount] and $maximumColumnWidth[$columnCount] >= $columnWidth ? $maximumColumnWidth[$columnCount] - $columnWidth : 0); + } + + # either way, the row is formed of "COLUMN + PADDING" + if(${$self}{justification} eq "left"){ + $tmpRow .= $column.$padding.(defined @{${$_}{multiColPadding}}[$columnCount] ? " " x @{${$_}{multiColPadding}}[$columnCount]: q()).(" " x ${$self}{spacesBeforeAmpersand})."&".(" " x ${$self}{spacesAfterAmpersand}); + } else { + $tmpRow .= $padding.(defined @{${$_}{multiColPadding}}[$columnCount] ? " " x @{${$_}{multiColPadding}}[$columnCount]: q()).$column.(" " x ${$self}{spacesBeforeAmpersand})."&".(" " x ${$self}{spacesAfterAmpersand}); + } + $columnCount++; + } + + # remove the final & + $tmpRow =~ s/\h*&\h*$/ /; + my $finalSpacing = q(); + $finalSpacing = " " x (${$self}{spacesBeforeDoubleBackSlash}) if ${$self}{spacesBeforeDoubleBackSlash}>=1; + $tmpRow =~ s/\h*$/$finalSpacing/; + + # replace the row with the formatted row + ${$_}{row} = $tmpRow; + + # update the maximum row width + my $gcs = Unicode::GCString->new($tmpRow); + ${$_}{rowWidth} = $gcs->columns(); + $maximumRowWidth = ${$_}{rowWidth} if(${$_}{rowWidth} > $maximumRowWidth); + } + } + + # final loop through to get \\ aligned + foreach (@formattedBody){ + # reset the padding + my $padding = q(); + + # possibly adjust the padding + if(${$_}{format} and ${$_}{row} !~ m/^\h*$/){ + # remove trailing horizontal space if ${$self}{alignDoubleBackSlash} is set to 0 + ${$_}{row} =~ s/\h*$// if (!${$self}{alignDoubleBackSlash}); + + # format spacing infront of \\ + if(defined ${$self}{spacesBeforeDoubleBackSlash} and ${$self}{spacesBeforeDoubleBackSlash}<0 and !${$self}{alignDoubleBackSlash}){ + # zero spaces (possibly resulting in un-aligned \\) + $padding = q(); + } elsif(defined ${$self}{spacesBeforeDoubleBackSlash} and ${$self}{spacesBeforeDoubleBackSlash}>=0 and !${$self}{alignDoubleBackSlash}){ + # specified number of spaces (possibly resulting in un-aligned \\) + $padding = " " x (${$self}{spacesBeforeDoubleBackSlash}); + } else { + # aligned \\ + $padding = " " x ($maximumRowWidth - ${$_}{rowWidth}); + } + } + + # format the row, and put the trailing \\ and trailing comments back into the row + ${$_}{row} .= $padding.(${$_}{endPiece} ? ${$_}{endPiece} :q() ).(${$_}{trailingComment}? ${$_}{trailingComment} : q() ); + } + + # to the log file + if($is_tt_switch_active){ + $logger->trace(${$_}{row}) 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}; +} + +sub double_back_slash_else{ + my $self = shift; + + # check for existence of \\ statement, and associated line break information + $self->check_for_else_statement( + # else name regexp + elseNameRegExp=>qr/${${$masterSettings{fineTuning}}{modifyLineBreaks}}{doubleBackSlash}/, + # else statements name: note that DBS stands for 'Double Back Slash' + ElseStartsOnOwnLine=>"DBSStartsOnOwnLine", + # end statements + ElseFinishesWithLineBreak=>"DBSFinishesWithLineBreak", + # for the YAML settings storage + storageNameAppend=>"DBS", + # logfile information + logName=>"double-back-slash-block (for align at ampersand, see lookForAlignDelims)", + ); +} +1; diff --git a/support/latexindent/LatexIndent/Arguments.pm b/support/latexindent/LatexIndent/Arguments.pm new file mode 100644 index 0000000000..96450fdaaf --- /dev/null +++ b/support/latexindent/LatexIndent/Arguments.pm @@ -0,0 +1,409 @@ +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_m_switch_active $is_t_switch_active $is_tt_switch_active/; +use LatexIndent::GetYamlSettings qw/%masterSettings/; +use LatexIndent::LogFile qw/$logger/; +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 construct_arguments_regexp $optAndMandRegExp comma_else/; +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}{body} =~ s/\R$//s if ($is_m_switch_active and ${$self}{IDFollowedImmediatelyByLineBreak}); + $logger->trace("*Arguments object doesn't receive any direct indentation, but its children will...") if $is_t_switch_active; + return; +} + +sub find_opt_mand_arguments{ + my $self = shift; + + $logger->trace("*Searching ${$self}{name} for optional and mandatory arguments") 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)?/){ + $logger->trace("Optional/Mandatory arguments".(${$masterSettings{commandCodeBlocks}}{roundParenthesesAllowed}?" (possibly round Parentheses)":q())." found in ${$self}{name}: $1") 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; + + # text wrapping can make the ID split across lines + ${$arguments}{idRegExp} = ${$arguments}{id}; + + if($is_m_switch_active){ + my $IDwithLineBreaks = join("\\R?\\h*",split(//,${$arguments}{id})); + ${$arguments}{idRegExp} = qr/$IDwithLineBreaks/s; + } + + # determine which comes first, optional or mandatory + if(${$arguments}{body} =~ m/.*?((?<!\\)\{|\[)/s){ + + if($1 eq "\["){ + $logger->trace("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 { + $logger->trace("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; + } + + # it's possible not to have any mandatory or optional arguments, see + # https://github.com/cmhughes/latexindent.pl/issues/123 + if( !(defined ${$arguments}{children}) ){ + $logger->trace("No optional or mandatory arguments found; searching for matching round parenthesis") if $is_t_switch_active; + $arguments->find_round_brackets; + } + } else { + $logger->trace("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"; + $logger->trace("$BodyStringLogFile = 1 (in ${$self}{name}), but first argument should not begin on its own line (see $BeginStringLogFile)") if $is_t_switch_active; + $logger->trace("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)/){ + $logger->trace("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"; + $logger->trace("$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 or a blank line, depending on if BeginStartsOnOwnLine == 2 or 3 respectively + # at the end of the begin statement + my $trailingCharacterToken = q(); + if(${${${$arguments}{children}}[0]}{BeginStartsOnOwnLine}==1){ + $logger->trace("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){ + $logger->trace("Adding a % at the end of begin, ${$self}{begin} followed by a linebreak ($BeginStringLogFile == 2)") if $is_t_switch_active; + $trailingCharacterToken = "%".$self->add_comment_symbol; + $logger->trace("Removing trailing space on ${$self}{begin}") if $is_t_switch_active; + ${$self}{begin} =~ s/\h*$//s; + } elsif (${${${$arguments}{children}}[0]}{BeginStartsOnOwnLine}==3) { + $logger->trace("Adding a blank line immediately ${$self}{begin} ($BeginStringLogFile==3)") if $is_t_switch_active; + $trailingCharacterToken = "\n".(${$masterSettings{modifyLineBreaks}}{preserveBlankLines}?$tokens{blanklines}:q()); + } + + # modification + ${$self}{begin} .= "$trailingCharacterToken\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}}){ + $logger->trace("Updating argument child 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) ){ + $logger->trace("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}; + + $logger->trace(Dumper(\%{$arguments})) if($is_tt_switch_active); + $logger->trace("replaced with ID: ${$arguments}{id}") if $is_tt_switch_active; + } else { + $logger->trace("... 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_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(); + + # arguments Before, by default, includes beamer special and numbered arguments, for example #1 #2, etc + my $argumentsBefore = qr/${${$masterSettings{fineTuning}}{arguments}}{before}/; + my $argumentsBetween = qr/${${$masterSettings{fineTuning}}{arguments}}{between}/; + + # 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 + and ref(${$masterSettings{commandCodeBlocks}}{stringsAllowedBetweenArguments}) eq "ARRAY"){ + # grab the strings allowed between arguments + my @stringsAllowedBetweenArguments = @{${$masterSettings{commandCodeBlocks}}{stringsAllowedBetweenArguments}}; + + $logger->trace("*Looping through array for commandCodeBlocks->stringsAllowedBetweenArguments") if $is_t_switch_active ; + + # note that the zero'th element in this array contains the amalgamate switch, which we don't want! + foreach (@stringsAllowedBetweenArguments[1 .. $#stringsAllowedBetweenArguments]) { + $logger->trace("$_") if $is_t_switch_active ; + $stringsBetweenArguments .= ($stringsBetweenArguments eq ""?q():"|").$_; + } + + $stringsBetweenArguments = qr/$stringsBetweenArguments/; + # report to log file + $logger->trace("*Strings allowed between arguments: $stringsBetweenArguments (see stringsAllowedBetweenArguments)") if $is_t_switch_active; + } + + if(defined ${input}{roundBrackets} and ${input}{roundBrackets}==1){ + # arguments regexp + return qr/ + ( # capture into $1 + (?: + (?:\h|\R|$blankLineToken|$trailingCommentRegExp|$argumentsBefore|$argumentsBetween|$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|$argumentsBefore|$stringsBetweenArguments)* # 0 or more h-space, blanklines, trailing comments + (?: + (?:(?<!\\)\[) + | + (?:(?<!\\)\{) + | + (?:(?<!\\)\() + ) + ) + ) + \h* + ($lineBreaksAtEnd) + ) + /sx; + } else { + return qr/ + ( # capture into $1 + (?: + (?:\h|\R|$blankLineToken|$trailingCommentRegExp|$argumentsBefore|$argumentsBetween|$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|$argumentsBefore)* # 0 or more h-space, blanklines, trailing comments + (?: + (?:(?<!\\)\[) + | + (?:(?<!\\)\{) + ) + ) + ) + \h* + ($lineBreaksAtEnd) + ) + /sx; + } +} + +sub comma_else{ + my $self = shift; + + # check for existence of \\ statement, and associated line break information + $self->check_for_else_statement( + # else name regexp + elseNameRegExp=>qr/${${$masterSettings{fineTuning}}{modifyLineBreaks}}{comma}/, + # else statements name + ElseStartsOnOwnLine=>"CommaStartsOnOwnLine", + # end statements + ElseFinishesWithLineBreak=>"CommaFinishesWithLineBreak", + # for the YAML settings storage + storageNameAppend=>"comma", + # logfile information + logName=>"comma block, see CommaStartsOnOwnLine and CommaFinishesWithLineBreak", + ); +} +1; diff --git a/support/latexindent/LatexIndent/BackUpFileProcedure.pm b/support/latexindent/LatexIndent/BackUpFileProcedure.pm new file mode 100644 index 0000000000..a0374552b4 --- /dev/null +++ b/support/latexindent/LatexIndent/BackUpFileProcedure.pm @@ -0,0 +1,136 @@ +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 LatexIndent::LogFile qw/$logger/; +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 + $logger->info("*Backup procedure (-w flag active):"); + + 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) { + $logger->warn("*onlyOneBackUp=$onlyOneBackUp and maxNumberOfBackUps: $maxNumberOfBackUps"); + $logger->warn("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 ; + $logger->info("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; + $logger->info("copying $fileName to $backupFile"); + $logger->info("$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) { + $logger->info("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) { + $logger->info("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){ + $logger->info(" 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 + } + $logger->info("$backupFile already exists, incrementing by 1... (see maxNumberOfBackUps and onlyOneBackUp)"); + $backupCounter++; + $backupFile =~ s/$backupExtension.*/$backupExtension$backupCounter/; + } + $logger->info("copying $fileName to $backupFile"); + } + + # output these lines to the log file + $logger->info("Backup file: $backupFile"); + $logger->info("$fileName will be overwritten after indentation"); + copy($fileName,$backupFile) or die "Could not write to backup file $backupFile. Please check permissions. Exiting."; +} +1; diff --git a/support/latexindent/LatexIndent/BlankLines.pm b/support/latexindent/LatexIndent/BlankLines.pm new file mode 100644 index 0000000000..abd1ed73a3 --- /dev/null +++ b/support/latexindent/LatexIndent/BlankLines.pm @@ -0,0 +1,126 @@ +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 LatexIndent::LogFile qw/$logger/; +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; + + # the text wrap routine, under some circumstances, can + # cause new blank lines that haven't been awarded a token; see, for example, + # test-cases/maxLineChars/multi-object-all.tex -l=multi-object2.yaml -m + # we can circumvent this by forcing blank lines to be protected if + # the text wrap option is active, which allows us to remove blank + # lines from wrapped text + if(ref ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns} eq "HASH" + or ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}>0 ){ + ${$masterSettings{modifyLineBreaks}}{preserveBlankLines} = 1; + } + + + unless(${$masterSettings{modifyLineBreaks}}{preserveBlankLines}){ + $logger->trace("*Blank lines will not be protected (preserveBlankLines=0)") if $is_t_switch_active; + return + } + + $logger->trace("*Protecting blank lines (see preserveBlankLines)") 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; + + $logger->trace("*condense blank lines routine") if $is_t_switch_active; + # 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 + $logger->trace("Updating body to include blank line token, this requires preserveBlankLines = 1") if($is_tt_switch_active); + $logger->trace("(any blanklines that could have been removed, would have done so by this point)") if($is_tt_switch_active); + + # make the call + $self->protect_blank_lines ; + $logger->trace("body now looks like:\n${$self}{body}") 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! + $logger->trace("Condensing multiple blank lines into $condenseMultipleBlankLinesInto (see condenseMultipleBlankLinesInto)") if $is_t_switch_active; + my $replacementToken = $blankLineToken; + for (my $i=1; $i<$condenseMultipleBlankLinesInto; $i++ ){ + $replacementToken .= "\n$blankLineToken"; + } + + $logger->trace("blank line replacement token: $replacementToken") if($is_tt_switch_active); + ${$self}{body} =~ s/($blankLineToken\h*\R*\h*){1,}$blankLineToken/$replacementToken/mgs; + $logger->trace("body now looks like:\n${$self}{body}") if($is_tt_switch_active); + return; +} + +sub unprotect_blank_lines{ + return unless $is_m_switch_active; + + return unless ${$masterSettings{modifyLineBreaks}}{preserveBlankLines}; + my $self = shift; + + # remove any empty lines that might have been added by the text_wrap routine; see, for example, + # test-cases/maxLineChars/multi-object-all.tex -l=multi-object2.yaml -m + ${$self}{body} =~ s/^\h*\R//mg; + + $logger->trace("Unprotecting blank lines (see preserveBlankLines)") if $is_t_switch_active; + my $blankLineToken = join("(?:\\h|\\R)*",split(//,$tokens{blanklines})); + + # loop through the body, looking for the blank line token + while(${$self}{body} =~ m/$blankLineToken/s){ + # when the blank line token occupies the whole line + ${$self}{body} =~ s/^\h*$blankLineToken$//mg; + + # when there's stuff *after* the blank line token + ${$self}{body} =~ s/(^\h*)$blankLineToken/"\n".$1/meg; + + # when there is stuff before and after the blank line token + ${$self}{body} =~ s/^(.*?)$blankLineToken\h*(.*?)\h*$/$1."\n".($2?"\n".$2:$2)/meg; + + # when there is only stuff *after* the blank line token + ${$self}{body} =~ s/^$blankLineToken\h*(.*?)$/$1."\n"/emg; + } + $logger->trace("Finished unprotecting lines (see preserveBlankLines)") if $is_t_switch_active; + $logger->trace("body now looks like:") if($is_tt_switch_active); + $logger->trace("${$self}{body}") if($is_tt_switch_active); +} + +1; diff --git a/support/latexindent/LatexIndent/Braces.pm b/support/latexindent/LatexIndent/Braces.pm new file mode 100644 index 0000000000..4861dd1f6a --- /dev/null +++ b/support/latexindent/LatexIndent/Braces.pm @@ -0,0 +1,189 @@ +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 LatexIndent::LogFile qw/$logger/; +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 $braceBracketRegExpBasic/; +our $commandCounter; +our $braceBracketRegExpBasic = qr/\{|\[/; + +sub find_commands_or_key_equals_values_braces{ + + my $self = shift; + + $logger->trace("*Searching for commands with optional and/or mandatory arguments AND key = {value}") 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 + / + # 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, + ); + + # log file output + $logger->trace("*command found: $2") if $is_t_switch_active ; + + # 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 + / + # 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"], + ); + + # log file output + $logger->trace("*key_equals_values_braces found: $3") if $is_t_switch_active ; + + # 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 + / + # 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", + }, + ); + # log file output + $logger->trace("*named grouping braces found: $2") if $is_t_switch_active ; + + # 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 + / + # 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, + ); + + # log file output + $logger->trace("*UNnamed grouping braces found: (no name, by definition!)") if $is_t_switch_active ; + + # 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/support/latexindent/LatexIndent/Command.pm b/support/latexindent/LatexIndent/Command.pm new file mode 100644 index 0000000000..a877a6295c --- /dev/null +++ b/support/latexindent/LatexIndent/Command.pm @@ -0,0 +1,215 @@ +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 LatexIndent::LogFile qw/$logger/; +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); + + # put together a list of the special command names (this was mostly motivated by the \@ifnextchar[ issue) + my $commandNameSpecialRegExp = q(); + if(ref(${$masterSettings{commandCodeBlocks}}{commandNameSpecial}) eq "ARRAY"){ + + my @commandNameSpecial = @{${$masterSettings{commandCodeBlocks}}{commandNameSpecial}}; + $logger->trace("*Looping through array for commandCodeBlocks->commandNameSpecial") if $is_t_switch_active ; + + # note that the zero'th element in this array contains the amalgamate switch, which we don't want! + foreach (@commandNameSpecial[1 .. $#commandNameSpecial]) { + $logger->trace("$_") if $is_t_switch_active ; + $commandNameSpecialRegExp .= ($commandNameSpecialRegExp eq ""?q():"|").$_; + } + + # turn the above into a regexp + $commandNameSpecialRegExp = qr/$commandNameSpecialRegExp/; + } + + # details to log file + $logger->trace("*The special command names regexp is: $commandNameSpecialRegExp (see commandNameSpecial)") if $is_t_switch_active; + + # read from fine tuning + my $commandNameRegExp = qr/${${$masterSettings{fineTuning}}{commands}}{name}/; + + # construct the command regexp + $commandRegExp = qr/ + (\\|\\@|@) + ( + $commandNameRegExp|$commandNameSpecialRegExp # 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}){ + $logger->trace("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 + ){ + $logger->trace("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 + $logger->trace("Adjusting linebreaksAtEnd in command ${$self}{name}") if $is_t_switch_active; + ${${$self}{linebreaksAtEnd}}{end} = ${${${${${$self}{children}}[0]}{children}[-1]}{linebreaksAtEnd}}{end}; + ${$self}{replacementText} .= "\n"; + + # if the last argument has EndFinishesWithLineBreak == 3 + if (${${${${$self}{children}}[0]}{children}[-1]}{EndFinishesWithLineBreak} == 3 ){ + my $EndStringLogFile = ${${${${$self}{children}}[0]}{children}[-1]}{aliases}{EndFinishesWithLineBreak}||"EndFinishesWithLineBreak"; + $logger->trace("Adding another blank line to replacement text for ${$self}{name} as last argument has $EndStringLogFile == 3 ") if $is_t_switch_active; + ${$self}{replacementText} .= (${$masterSettings{modifyLineBreaks}}{preserveBlankLines}?$tokens{blanklines}:"\n")."\n"; + } + + # update the argument object + $logger->trace("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 + $logger->trace("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 + $logger->trace(Dumper(${${${$self}{children}}[0]}{children}[-1])) if $is_tt_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 + $logger->trace("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 + $logger->trace("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$/){ + $logger->trace("${$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{commands}$commandCounter"; + return; +} + +sub align_at_ampersand{ + # need an empty routine here for commands; see + # test-cases/matrix1.tex for example + return; +} + +sub double_back_slash_else{ + # need an empty routine here for commands; see + # test-cases/alignment/command-align.tex for example + return; +} + +1; diff --git a/support/latexindent/LatexIndent/Document.pm b/support/latexindent/LatexIndent/Document.pm new file mode 100644 index 0000000000..7f6694d049 --- /dev/null +++ b/support/latexindent/LatexIndent/Document.pm @@ -0,0 +1,406 @@ +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; +use utf8; +use open ':std', ':encoding(UTF-8)'; + +# 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 $is_r_switch_active $is_rr_switch_active $is_rv_switch_active/; +use LatexIndent::LogFile qw/processSwitches $logger/; +use LatexIndent::Replacement qw/make_replacements/; +use LatexIndent::GetYamlSettings qw/yaml_read_settings yaml_modify_line_breaks_settings yaml_get_indentation_settings_for_this_object yaml_poly_switch_get_every_or_custom_value yaml_get_indentation_information yaml_get_object_attribute_for_indentation_settings yaml_alignment_at_ampersand_settings yaml_get_textwrap_removeparagraphline_breaks %masterSettings yaml_get_columns/; +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 text_wrap remove_paragraph_line_breaks construct_paragraph_reg_exp text_wrap_remove_paragraph_line_breaks verbatim_modify_line_breaks/; +use LatexIndent::Sentence qw/one_sentence_per_line/; +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 post_indentation_check/; +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 double_back_slash_else/; +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 find_verbatim_special verbatim_common_tasks/; +use LatexIndent::Environment qw/find_environments $environmentBasicRegExp construct_environments_regexp/; +use LatexIndent::IfElseFi qw/find_ifelsefi construct_ifelsefi_regexp $ifElseFiBasicRegExp/; +use LatexIndent::Else qw/check_for_else_statement/; +use LatexIndent::Arguments qw/get_arguments_regexp find_opt_mand_arguments construct_arguments_regexp comma_else/; +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 $braceBracketRegExpBasic/; +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 $specialBeginAndBracesBracketsBasicRegExp $specialBeginBasicRegExp/; +use LatexIndent::Heading qw/find_heading construct_headings_levels $allHeadingsRegexp/; +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 = {@_}; + $logger->trace(${$masterSettings{logFilePreferences}}{showDecorationStartCodeBlockTrace}) if ${$masterSettings{logFilePreferences}}{showDecorationStartCodeBlockTrace}; + bless ($self,$class); + return $self; +} + +sub latexindent{ + my $self = shift; + $self->storeSwitches; + $self->processSwitches; + $self->file_extension_check; + $self->operate_on_file; +} + +sub operate_on_file{ + my $self = shift; + + $self->create_back_up_file; + $self->token_check; + $self->make_replacements(when=>"before") if ($is_r_switch_active and !$is_rv_switch_active); + unless ($is_rr_switch_active){ + $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->find_verbatim_special; + $self->verbatim_modify_line_breaks if $is_m_switch_active; + $self->make_replacements(when=>"before") if $is_rv_switch_active; + $self->text_wrap if ($is_m_switch_active and !${$masterSettings{modifyLineBreaks}{textWrapOptions}}{perCodeBlockBasis} and ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}>1); + $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->make_replacements(when=>"after") if $is_rv_switch_active; + $self->put_verbatim_back_in (match=>"everything-except-commands"); + $self->put_trailing_comments_back_in; + $self->put_verbatim_back_in (match=>"just-commands"); + $self->make_replacements(when=>"after") if ($is_r_switch_active and !$is_rv_switch_active); + } + $self->output_indented_text; + return +} + +sub construct_regular_expressions{ + my $self = shift; + $self->construct_trailing_comment_regexp; + $self->construct_environments_regexp; + $self->construct_ifelsefi_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; + $self->construct_paragraph_reg_exp if $is_m_switch_active; +} + +sub output_indented_text{ + my $self = shift; + + $logger->info("*Output routine:"); + + # if -overwrite is active then output to original fileName + if($switches{overwrite}) { + $logger->info("Overwriting file ${$self}{fileName}"); + open(OUTPUTFILE,">",${$self}{fileName}); + print OUTPUTFILE ${$self}{body}; + close(OUTPUTFILE); + } elsif($switches{outputToFile}) { + $logger->info("Outputting to file $switches{outputToFile}"); + open(OUTPUTFILE,">",$switches{outputToFile}); + print OUTPUTFILE ${$self}{body}; + close(OUTPUTFILE); + } else { + $logger->info("Not outputting to file; see -w and -o switches for more options."); + } + + # put the final line in the logfile + $logger->info("${$masterSettings{logFilePreferences}}{endLogFileWith}") if ${$masterSettings{logFilePreferences}}{endLogFileWith}; + + # github info line + $logger->info("*Please direct all communication/issues to:\nhttps://github.com/cmhughes/latexindent.pl") if ${$masterSettings{logFilePreferences}}{showGitHubInfoFooter}; + + # output to screen, unless silent mode + print ${$self}{body} unless $switches{silentMode}; + + return; +} + +sub process_body_of_text{ + my $self = shift; + + # find objects recursively + $logger->info('*Phase 1: searching for objects'); + $self->find_objects; + + # find all hidden child + $logger->info('*Phase 2: finding surrounding indentation'); + $self->find_surrounding_indentation_for_children; + + # indentation recursively + $logger->info('*Phase 3: indenting objects'); + $self->indent_children_recursively; + + # final indentation check + $logger->info('*Phase 4: final indentation check'); + $self->final_indentation_check; + + return; +} + +sub find_objects{ + my $self = shift; + + # one sentence per line: sentences are objects, as of V3.5.1 + $self->one_sentence_per_line if ($is_m_switch_active and ${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{manipulateSentences}); + + # search for environments + $logger->trace('looking for ENVIRONMENTS') if $is_t_switch_active; + $self->find_environments if ${$self}{body} =~ m/$environmentBasicRegExp/s; + + # search for ifElseFi blocks + $logger->trace('looking for IFELSEFI') if $is_t_switch_active; + $self->find_ifelsefi if ${$self}{body} =~ m/$ifElseFiBasicRegExp/s; + + # search for headings (part, chapter, section, setc) + $logger->trace('looking for HEADINGS (chapter, section, part, etc)') if $is_t_switch_active; + $self->find_heading if ${$self}{body} =~ m/$allHeadingsRegexp/s; + + # the ordering of finding commands and special code blocks can change + $self->find_commands_or_key_equals_values_braces_and_special if ${$self}{body} =~ m/$specialBeginAndBracesBracketsBasicRegExp/s; + + # documents without preamble need a manual call to the paragraph_one_line routine + if ($is_m_switch_active and !${$self}{preamblePresent}){ + $self->yaml_get_textwrap_removeparagraphline_breaks; + + # call the remove_paragraph_line_breaks and text_wrap routines + if(${$masterSettings{modifyLineBreaks}{removeParagraphLineBreaks}}{beforeTextWrap}){ + $self->remove_paragraph_line_breaks if ${$self}{removeParagraphLineBreaks}; + $self->text_wrap if (${$self}{textWrapOptions} and ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{perCodeBlockBasis}); + } else { + $self->text_wrap if (${$self}{textWrapOptions} and ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{perCodeBlockBasis}); + $self->remove_paragraph_line_breaks if ${$self}{removeParagraphLineBreaks}; + } + } + + # if there are no children, return + if(${$self}{children}){ + $logger->trace("*Objects have been found.") if $is_t_switch_active; + } else { + $logger->trace("No objects found."); + return; + } + + # logfile information + $logger->trace(Dumper(\%{$self})) if($is_tt_switch_active); + + return; +} + +sub find_commands_or_key_equals_values_braces_and_special{ + my $self = shift; + + # the order in which we search for specialBeginEnd and commands/key/braces + # can change depending upon specialBeforeCommand + if(${$masterSettings{specialBeginEnd}}{specialBeforeCommand}){ + # search for special begin/end + $logger->trace('looking for SPECIAL begin/end *before* looking for commands (see specialBeforeCommand)') if $is_t_switch_active; + $self->find_special if ${$self}{body} =~ m/$specialBeginBasicRegExp/s; + + # search for commands with arguments + $logger->trace('looking for COMMANDS and key = {value}') if $is_t_switch_active; + $self->find_commands_or_key_equals_values_braces if ${$self}{body} =~ m/$braceBracketRegExpBasic/s; + } else { + # search for commands with arguments + $logger->trace('looking for COMMANDS and key = {value}') if $is_t_switch_active; + $self->find_commands_or_key_equals_values_braces if ${$self}{body} =~ m/$braceBracketRegExpBasic/s; + + # search for special begin/end + $logger->trace('looking for SPECIAL begin/end') if $is_t_switch_active; + $self->find_special if ${$self}{body} =~ m/$specialBeginBasicRegExp/s; + } + return; +} + +sub tasks_particular_to_each_object{ + my $self = shift; + $logger->trace("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; + + # removeParagraphLineBreaks and textWrapping fun! + $latexIndentObject->text_wrap_remove_paragraph_line_breaks if($is_m_switch_active); + + # store children in special hash + push(@{${$self}{children}},$latexIndentObject); + + # possible decoration in log file + $logger->trace(${$masterSettings{logFilePreferences}}{showDecorationFinishCodeBlockTrace}) if ${$masterSettings{logFilePreferences}}{showDecorationFinishCodeBlockTrace}; +} + +sub tasks_common_to_each_object{ + my $self = shift; + + # grab the parent information + my %parent = @_; + + # update/create the ancestor information + if($parent{ancestors}){ + $logger->trace("Ancestors *have* been found for ${$self}{name}") if($is_t_switch_active); + push(@{${$self}{ancestors}},@{$parent{ancestors}}); + } else { + $logger->trace("No ancestors found for ${$self}{name}") if($is_t_switch_active); + if(defined $parent{id} and $parent{id} ne ''){ + $logger->trace("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->yaml_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}; + + # text wrapping can make the ID split across lines + ${$self}{idRegExp} = ${$self}{id}; + + if($is_m_switch_active){ + my $IDwithLineBreaks = join("\\R?\\h*",split(//,${$self}{id})); + ${$self}{idRegExp} = qr/$IDwithLineBreaks/s; + } + + # the replacement text can be just the ID, but the ID might have a line break at the end of it + $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 if ($is_m_switch_active and defined ${$self}{BodyStartsOnOwnLine}); + + # modify line breaks end statements + $self->modify_line_breaks_end if $is_m_switch_active; + + # check the body for current children + $self->check_for_hidden_children if ${$self}{body} =~ m/$tokens{beginOfToken}/; + + # double back slash poly-switch check + $self->double_back_slash_else if ($is_m_switch_active and ${$self}{lookForAlignDelims}); + 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 + $logger->trace("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; + $logger->trace("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 if $is_m_switch_active; + + $logger->trace(Dumper(\%{$child})) if($is_tt_switch_active); + $logger->trace("replaced with ID: ${$child}{id}") if $is_t_switch_active; + +} + +1; diff --git a/support/latexindent/LatexIndent/DoubleBackSlash.pm b/support/latexindent/LatexIndent/DoubleBackSlash.pm new file mode 100644 index 0000000000..8b8be3e25e --- /dev/null +++ b/support/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/support/latexindent/LatexIndent/Else.pm b/support/latexindent/LatexIndent/Else.pm new file mode 100644 index 0000000000..5a367c784f --- /dev/null +++ b/support/latexindent/LatexIndent/Else.pm @@ -0,0 +1,134 @@ +package LatexIndent::Else; +# 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 LatexIndent::LogFile qw/$logger/; +use LatexIndent::Braces qw/$braceBracketRegExpBasic/; +use LatexIndent::Special qw/$specialBeginAndBracesBracketsBasicRegExp/; +use LatexIndent::Heading qw/$allHeadingsRegexp/; +use Data::Dumper; +use Exporter qw/import/; +our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321 +our @EXPORT_OK = qw/check_for_else_statement/; +our $elseCounter; + +sub check_for_else_statement{ + my $self = shift; + + # we call the else routine from different places; see IfElseFi.pm and Special.pm + my %input = @_; + + # store the regular expresssion for matching and replacing the \else statements + my $elseRegExp = qr/ + ( + $input{elseNameRegExp} + \h* # possible horizontal space + (\R*) # possible line breaks after \else statement + ) + ( + (?: + (?!$input{elseNameRegExp}). + )*? # body, which can't include another \else + ) + $ + /sx; + + $logger->trace("*Looking for $input{elseNameRegExp} statement (${$self}{name})") if $is_t_switch_active; + + while(${$self}{body} =~ m/$elseRegExp(\h*)($trailingCommentRegExp)?/){ + ${$self}{body} =~ s/$elseRegExp(\h*)($trailingCommentRegExp)? + / + # create a new Else object + my $else = LatexIndent::Else->new(begin=>$1, + name=>${$self}{name}, + storageNameAppend=>$input{storageNameAppend}, + body=>$3, + end=>q(), + linebreaksAtEnd=>{ + begin=>$2?1:0, + body=>0, + end=>0, + }, + aliases=>{ + # begin statements + BeginStartsOnOwnLine=>$input{ElseStartsOnOwnLine}, + # end statements + BodyStartsOnOwnLine=>$input{ElseFinishesWithLineBreak}, + }, + modifyLineBreaksYamlName=>${$self}{modifyLineBreaksYamlName}, + endImmediatelyFollowedByComment=>0, + horizontalTrailingSpace=>q(), + # mandatory and optional arguments have a parent, which we need + # to detail for double back slash poly-switches + # (see test-cases alignment command-align.tex, for example) + parent=>(${$self}{parent}?${$self}{parent}:"none"), + ); + + # log file output + $logger->trace("*$input{logName} found: ${$self}{name}")if $is_t_switch_active; + + # the settings and storage of most objects has a lot in common + $self->get_settings_and_store_new_object($else); + ${@{${$self}{children}}[-1]}{replacementText}; + /xse; + } + return; +} + +sub remove_line_breaks_begin{ + # the \else 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"; + $logger->trace("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; +} + +sub tasks_particular_to_each_object{ + my $self = shift; + + # search for headings (important to do this before looking for commands!) + $self->find_heading if ${$self}{body} =~ m/$allHeadingsRegexp/s; + + # search for commands and special code blocks + $self->find_commands_or_key_equals_values_braces_and_special if ${$self}{body} =~ m/$specialBeginAndBracesBracketsBasicRegExp/s; + + # search for arguments + $self->find_opt_mand_arguments if ${$self}{body} =~ m/$braceBracketRegExpBasic/s; + + return; +} + +sub yaml_get_indentation_information{ + return q(); +} + +sub create_unique_id{ + my $self = shift; + + $elseCounter++; + + ${$self}{id} = "$tokens{else}$elseCounter"; + return; +} + +1; diff --git a/support/latexindent/LatexIndent/Environment.pm b/support/latexindent/LatexIndent/Environment.pm new file mode 100644 index 0000000000..401cbecbee --- /dev/null +++ b/support/latexindent/LatexIndent/Environment.pm @@ -0,0 +1,139 @@ +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::GetYamlSettings qw/%masterSettings/; +use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active $is_m_switch_active/; +use LatexIndent::LogFile qw/$logger/; +use LatexIndent::Braces qw/$braceBracketRegExpBasic/; +use LatexIndent::IfElseFi qw/$ifElseFiBasicRegExp/; +use LatexIndent::Heading qw/$allHeadingsRegexp/; +use LatexIndent::Special qw/$specialBeginAndBracesBracketsBasicRegExp/; +use Data::Dumper; +use Exporter qw/import/; +our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321 +our @EXPORT_OK = qw/find_environments $environmentBasicRegExp construct_environments_regexp/; +our $environmentCounter; +our $environmentBasicRegExp = qr/\\begin\{/; +our $environmentRegExp; + +# store the regular expresssion for matching and replacing the \begin{}...\end{} statements +sub construct_environments_regexp{ + + # read from fine tuning + my $environmentNameRegExp = qr/${${$masterSettings{fineTuning}}{environments}}{name}/; + $environmentRegExp = qr/ + ( + \\begin\{ + ( + $environmentNameRegExp # 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)? + / + # 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(), + ); + + # log file output + $logger->trace("*environment found: $2") if $is_t_switch_active; + + # 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 if $is_m_switch_active; + } + 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}){ + $logger->trace("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 if ${$masterSettings{indentAfterItems}}{${$self}{name}}; + + # search for headings (important to do this before looking for commands!) + $self->find_heading if ${$self}{body} =~ m/$allHeadingsRegexp/s; + + # search for commands and special code blocks + $self->find_commands_or_key_equals_values_braces_and_special if ${$self}{body} =~ m/$specialBeginAndBracesBracketsBasicRegExp/s; + + # search for arguments + $self->find_opt_mand_arguments if ${$self}{body} =~ m/$braceBracketRegExpBasic/s; + + # search for ifElseFi blocks + $self->find_ifelsefi if ${$self}{body} =~ m/$ifElseFiBasicRegExp/s; + +} + +sub create_unique_id{ + my $self = shift; + + $environmentCounter++; + ${$self}{id} = "$tokens{environments}$environmentCounter"; + return; +} + + +1; diff --git a/support/latexindent/LatexIndent/FileContents.pm b/support/latexindent/LatexIndent/FileContents.pm new file mode 100644 index 0000000000..cbb20e5148 --- /dev/null +++ b/support/latexindent/LatexIndent/FileContents.pm @@ -0,0 +1,265 @@ +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 $is_m_switch_active/; +use LatexIndent::LogFile qw/$logger/; +use LatexIndent::Environment qw/$environmentBasicRegExp/; +use LatexIndent::IfElseFi qw/$ifElseFiBasicRegExp/; +use LatexIndent::Special qw/$specialBeginAndBracesBracketsBasicRegExp/; +use LatexIndent::Heading qw/$allHeadingsRegexp/; +use Data::Dumper; +use Exporter qw/import/; +our @EXPORT_OK = qw/find_file_contents_environments_and_preamble/; +our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321 +our $fileContentsCounter; + +sub find_file_contents_environments_and_preamble{ + my $self = shift; + + # store the file contents blocks in an array which, depending on the value + # of indentPreamble, will be put into the verbatim hash, or otherwise + # stored as children to be operated upon + my @fileContentsStorageArray; + + # fileContents environments + $logger->trace('*Searching for FILE CONTENTS environments (see fileContentsEnvironments)') if $is_t_switch_active; + $logger->trace(Dumper(\%{$masterSettings{fileContentsEnvironments}})) if($is_tt_switch_active); + while( my ($fileContentsEnv,$yesno)= each %{$masterSettings{fileContentsEnvironments}}){ + if($yesno){ + $logger->trace("looking for $fileContentsEnv environments") if $is_t_switch_active; + + # the trailing * needs some care + if($fileContentsEnv =~ m/\*$/){ + $fileContentsEnv =~ s/\*$//; + $fileContentsEnv .= '\*'; + } + + my $fileContentsRegExp = qr/ + ( + \\begin\{ + $fileContentsEnv + \} + ) + ( + .*? + ) + ( + \\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; + + # text wrapping can make the ID split across lines + ${$fileContentsBlock}{idRegExp} = ${$fileContentsBlock}{id}; + + if($is_m_switch_active){ + my $IDwithLineBreaks = join("\\R?\\h*",split(//,${$fileContentsBlock}{id})); + ${$fileContentsBlock}{idRegExp} = qr/$IDwithLineBreaks/s; + } + + # the replacement text can be just the ID, but the ID might have a line break at the end of it + $fileContentsBlock->get_replacement_text; + + # count body line breaks + $fileContentsBlock->count_body_line_breaks; + + # the above regexp, when used below, will remove the trailing linebreak in ${$self}{linebreaksAtEnd}{end} + # so we compensate for it here + $fileContentsBlock->adjust_replacement_text_line_breaks_at_end; + + # store the fileContentsBlock, and determine location afterwards + push(@fileContentsStorageArray,$fileContentsBlock); + + # log file output + $logger->trace("FILECONTENTS environment found: $fileContentsEnv"); + + # remove the environment block, and replace with unique ID + ${$self}{body} =~ s/$fileContentsRegExp/${$fileContentsBlock}{replacementText}/sx; + + $logger->trace("replaced with ID: ${$fileContentsBlock}{id}") if $is_tt_switch_active; + } + } else { + $logger->trace("*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*\h*)? # 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}}){ + + $logger->trace("\\begin{document} found in body (after searching for filecontents)-- assuming that a preamble exists") if $is_t_switch_active ; + + # create a preamble object + $preamble = LatexIndent::Preamble->new( begin=>q(), + body=>$1, + end=>q(), + name=>"preamble", + linebreaksAtEnd=>{ + begin=>0, + body=>$2?1:0, + end=>0, + }, + afterbit=>($2?$2:q())."\\begin{document}", + modifyLineBreaksYamlName=>"preamble", + ); + + # give unique id + $preamble->create_unique_id; + + # text wrapping can make the ID split across lines + ${$preamble}{idRegExp} = ${$preamble}{id}; + + if($is_m_switch_active){ + my $IDwithLineBreaks = join("\\R?\\h*",split(//,${$preamble}{id})); + ${$preamble}{idRegExp} = qr/$IDwithLineBreaks/s; + } + + # get the replacement_text + $preamble->get_replacement_text; + + # log file output + $logger->trace("preamble found: preamble") if $is_t_switch_active; + + # remove the environment block, and replace with unique ID + ${$self}{body} =~ s/$preambleRegExp/${$preamble}{replacementText}/sx; + + $logger->trace("replaced with ID: ${$preamble}{replacementText}") if $is_tt_switch_active; + # indentPreamble set to 1 + if($masterSettings{indentPreamble}){ + $logger->trace("storing ${$preamble}{id} for indentation (see indentPreamble)"); + $needToStorePreamble = 1; + } else { + # indentPreamble set to 0 + $logger->trace("NOT storing ${$preamble}{id} for indentation -- will store as VERBATIM object (because indentPreamble:0)") if $is_t_switch_active; + $preamble->unprotect_blank_lines; + ${$self}{verbatim}{${$preamble}{id}} = $preamble; + } + } else { + ${$self}{preamblePresent} = 0; + } + + # loop through the fileContents array, check if it's in the preamble + foreach(@fileContentsStorageArray){ + my $indentThisChild = 0; + # verbatim children go in special hash + if($preamble ne '' and ${$preamble}{body} =~ m/${$_}{id}/){ + $logger->trace("filecontents (${$_}{id}) is within preamble"); + # indentPreamble set to 1 + if($masterSettings{indentPreamble}){ + $logger->trace("storing ${$_}{id} for indentation (indentPreamble is 1)"); + $indentThisChild = 1; + } else { + # indentPreamble set to 0 + $logger->trace("Storing ${$_}{id} as a VERBATIM object (indentPreamble is 0)"); + ${$self}{verbatim}{${$_}{id}}=$_; + } + } else { + $logger->trace("storing ${$_}{id} for indentation (${$_}{name} found outside of preamble)"); + $indentThisChild = 1; + } + # store the child, if necessary + if($indentThisChild){ + $_->remove_leading_space; + $_->yaml_get_indentation_settings_for_this_object; + $_->tasks_particular_to_each_object; + push(@{${$self}{children}},$_); + + # possible decoration in log file + $logger->trace(${$masterSettings{logFilePreferences}}{showDecorationFinishCodeBlockTrace}) if ${$masterSettings{logFilePreferences}}{showDecorationFinishCodeBlockTrace}; + } + } + + 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 if ${$self}{body} =~ m/$environmentBasicRegExp/s; + + # search for ifElseFi blocks + $self->find_ifelsefi if ${$self}{body} =~ m/$ifElseFiBasicRegExp/s; + + # search for headings (part, chapter, section, setc) + $self->find_heading if ${$self}{body} =~ m/$allHeadingsRegexp/s; + + # search for commands and special code blocks + $self->find_commands_or_key_equals_values_braces_and_special if ${$self}{body} =~ m/$specialBeginAndBracesBracketsBasicRegExp/s; + + # text wrapping, remove paragraph line breaks + if ($is_m_switch_active){ + $self->yaml_get_textwrap_removeparagraphline_breaks; + + # call the remove_paragraph_line_breaks and text_wrap routines + if(${$masterSettings{modifyLineBreaks}{removeParagraphLineBreaks}}{beforeTextWrap}){ + $self->remove_paragraph_line_breaks if ${$self}{removeParagraphLineBreaks}; + $self->text_wrap if (${$self}{textWrapOptions} and ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{perCodeBlockBasis}); + } else { + $self->text_wrap if (${$self}{textWrapOptions} and ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{perCodeBlockBasis}); + $self->remove_paragraph_line_breaks if ${$self}{removeParagraphLineBreaks}; + } + } +} + +1; diff --git a/support/latexindent/LatexIndent/FileExtension.pm b/support/latexindent/LatexIndent/FileExtension.pm new file mode 100644 index 0000000000..716598de77 --- /dev/null +++ b/support/latexindent/LatexIndent/FileExtension.pm @@ -0,0 +1,154 @@ +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 utf8; +use PerlIO::encoding; +use open ':std', ':encoding(UTF-8)'; +use File::Basename; # to get the filename and directory path +use Exporter qw/import/; +use Log::Log4perl qw(get_logger :levels); +use LatexIndent::GetYamlSettings qw/%masterSettings/; +use LatexIndent::Switches qw/%switches/; +our @EXPORT_OK = qw/file_extension_check/; + +sub file_extension_check{ + my $self = shift; + + # grab the logger object + my $logger = get_logger("Document"); + + # grab the filename + my $fileName = ${$self}{fileName}; + + # see if an extension exists for the fileName + my ($name,$dir,$ext) = fileparse($fileName,qr/\..[^.]*$/); + + # grab the file extension preferences + my %fileExtensionPreference= %{$masterSettings{fileExtensionPreference}}; + + # sort the file extensions by preference + my @fileExtensions = sort { $fileExtensionPreference{$a} <=> $fileExtensionPreference{$b} } keys(%fileExtensionPreference); + + # store the base name + ${$self}{baseName} = $name; + + # if no extension, search according to fileExtensionPreference + if ($fileName ne "-"){ + if (!$ext) { + $logger->info("*File extension work:"); + $logger->info("latexindent called to act upon $fileName without a file extension;\nsearching for file with an extension in the following order (see fileExtensionPreference):"); + $logger->info(join("\n",@fileExtensions)); + + my $fileFound = 0; + # loop through the known file extensions (see @fileExtensions) + foreach (@fileExtensions ){ + if ( -e $fileName.$_ ) { + $logger->info("$fileName$_ found!"); + $fileName .= $_; + $logger->info("Updated fileName to $fileName"); + ${$self}{fileName} = $fileName ; + $fileFound = 1; + $ext = $_; + last; + } + } + unless($fileFound){ + $logger->fatal("*I couldn't find a match for $fileName in fileExtensionPreference (see defaultSettings.yaml)"); + foreach (@fileExtensions ){ + $logger->fatal("I searched for $fileName$_"); + } + $logger->fatal("but couldn't find any of them.\nConsider updating fileExtensionPreference.\nExiting, no indendation done."); + 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 = "I couldn't find $fileName, are you sure it exists?.\nNo indentation done.\nExiting."; + $logger->fatal("*$message"); + die $message; + } + } + } + + # store the file extension + ${$self}{fileExtension} = $ext; + + # check to see if -o switch is active + if($switches{outputToFile}){ + + $logger->info("*-o switch active: output file check"); + + if ($fileName eq "-" and $switches{outputToFile} =~ m/^\+/){ + $logger->info("STDIN input mode active, -o switch is removing all + symbols"); + $switches{outputToFile} =~ s/\+//g; + } + # the -o file name might begin with a + symbol + if($switches{outputToFile} =~ m/^\+(.*)/ and $1 ne "+"){ + $logger->info("-o switch called with + symbol at the beginning: $switches{outputToFile}"); + $switches{outputToFile} = ${$self}{baseName}.$1; + $logger->info("output file is now: $switches{outputToFile}"); + } + + my $strippedFileExtension = ${$self}{fileExtension}; + $strippedFileExtension =~ s/\.//; + $strippedFileExtension = "tex" if ($strippedFileExtension eq ""); + + # grab the name, directory, and extension of the output file + my ($name, $dir, $ext) = fileparse($switches{outputToFile}, $strippedFileExtension); + + # if there is no extension, then add the extension from the file to be operated upon + if(!$ext){ + $logger->info("-o switch called with file name without extension: $switches{outputToFile}"); + $switches{outputToFile} = $name.($name=~m/\.\z/ ? q() : ".").$strippedFileExtension; + $logger->info("Updated to $switches{outputToFile} as the file extension of the input file is $strippedFileExtension"); + } + + # the -o file name might end with ++ in which case we wish to search for existence, + # and then increment accordingly + $name =~ s/\.$//; + if($name =~ m/\+\+$/){ + $logger->info("-o switch called with file name ending with ++: $switches{outputToFile}"); + $name =~ s/\+\+$//; + $name = ${$self}{baseName} if ($name eq ""); + my $outputFileCounter = 0; + my $fileName = $name.$outputFileCounter.".".$strippedFileExtension; + $logger->info("will search for existence and increment counter, starting with $fileName"); + while( -e $fileName ){ + $logger->info("$fileName exists, incrementing counter"); + $outputFileCounter++; + $fileName = $name.$outputFileCounter.".".$strippedFileExtension; + } + $logger->info("$fileName does not exist, and will be the output file"); + $switches{outputToFile} = $fileName; + } + } + + # read the file into the Document body + my @lines; + if($fileName ne "-"){ + open(MAINFILE, $fileName) or die "Could not open input file, $fileName"; + push(@lines,$_) while(<MAINFILE>); + close(MAINFILE); + } else { + push(@lines,$_) while (<>) + } + + # the all-important step: update the body + ${$self}{body} = join("",@lines); +} +1; diff --git a/support/latexindent/LatexIndent/GetYamlSettings.pm b/support/latexindent/LatexIndent/GetYamlSettings.pm new file mode 100644 index 0000000000..3029043f23 --- /dev/null +++ b/support/latexindent/LatexIndent/GetYamlSettings.pm @@ -0,0 +1,929 @@ +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 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 Log::Log4perl qw(get_logger :levels); +use Exporter qw/import/; +our @EXPORT_OK = qw/yaml_read_settings yaml_modify_line_breaks_settings yaml_get_indentation_settings_for_this_object yaml_poly_switch_get_every_or_custom_value yaml_get_indentation_information yaml_get_object_attribute_for_indentation_settings yaml_alignment_at_ampersand_settings yaml_get_textwrap_removeparagraphline_breaks %masterSettings yaml_get_columns/; + +# 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; + +# default values for align at ampersand routine +our @alignAtAmpersandInformation = ( {name=>"lookForAlignDelims",yamlname=>"delims",default=>1}, + {name=>"alignDoubleBackSlash",default=>1}, + {name=>"spacesBeforeDoubleBackSlash",default=>1}, + {name=>"multiColumnGrouping",default=>0}, + {name=>"alignRowsWithoutMaxDelims",default=>1}, + {name=>"spacesBeforeAmpersand",default=>1}, + {name=>"spacesAfterAmpersand",default=>1}, + {name=>"justification",default=>"left"}, + ); + +sub yaml_read_settings{ + my $self = shift; + + # read the default settings + $defaultSettings = YAML::Tiny->read( "$FindBin::RealBin/defaultSettings.yaml" ) if ( -e "$FindBin::RealBin/defaultSettings.yaml" ); + + # grab the logger object + my $logger = get_logger("Document"); + $logger->info("*YAML settings read: defaultSettings.yaml\nReading 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"); + $logger->info("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; + $logger->info("*YAML settings read: indentconfig.yaml or .indentconfig.yaml") unless $switches{onlyDefault}; + + # 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}) { + # read the absolute paths from indentconfig.yaml + $userSettings = YAML::Tiny->read( "$indentconfig" ); + + # update the absolute paths + if($userSettings and (ref($userSettings->[0]) eq 'HASH') and $userSettings->[0]->{paths}){ + $logger->info("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") { + $logger->info("$homeDir/.indentconfig.yaml has been found, but $indentconfig takes priority"); + } elsif ( -e File::HomeDir->my_home . "/indentconfig.yaml" ) { + $logger->info("(Alternatively $homeDir/.indentconfig.yaml can be used)"); + } elsif ( -e File::HomeDir->my_home . "/.indentconfig.yaml" ) { + $logger->info("(Alternatively $homeDir/indentconfig.yaml can be used)"); + } + + # output the contents of indentconfig to the log file + $logger->info(Dump \%{$userSettings->[0]}); + + @absPaths = @{$userSettings->[0]->{paths}}; + } else { + $logger->warn("*The paths field cannot be read from $indentconfig; this means it is either empty or contains invalid YAML"); + $logger->warn("See https://latexindentpl.readthedocs.io/en/latest/sec-indent-config-and-settings.html for an example"); + } + } else { + if($switches{onlyDefault}) { + $logger->info("*-d switch active: only default settings requested"); + $logger->info("not reading USER settings from $indentconfig") if (-e $indentconfig); + $logger->info("Ignoring the -l switch: $switches{readLocalSettings} (you used the -d switch)") if($switches{readLocalSettings}); + $logger->info("Ignoring the -y switch: $switches{yaml} (you used the -d switch)") if($switches{yaml}); + $switches{readLocalSettings}=0; + $switches{yaml}=0; + } else { + # give the user instructions on where to put indentconfig.yaml or .indentconfig.yaml + $logger->info("Home directory is $homeDir (didn't find either indentconfig.yaml or .indentconfig.yaml)\nTo specify user settings you would put indentconfig.yaml here: $homeDir/indentconfig.yaml\nAlternatively, you can use the hidden file .indentconfig.yaml as: $homeDir/.indentconfig.yaml"); + } + } + + + # local settings can be called with a + symbol, for example + # -l=+myfile.yaml + # -l "+ myfile.yaml" + # -l=myfile.yaml+ + # which translates to, respectively + # -l=localSettings.yaml,myfile.yaml + # -l=myfile.yaml,localSettings.yaml + # Note: the following is *not allowed*: + # -l+myfile.yaml + # and + # -l + myfile.yaml + # will *only* load localSettings.yaml, and myfile.yaml will be ignored + my @localSettings; + + $logger->info("*YAML settings read: -l switch") if $switches{readLocalSettings}; + + # remove leading, trailing, and intermediate space + $switches{readLocalSettings} =~ s/^\h*//g; + $switches{readLocalSettings} =~ s/\h*$//g; + $switches{readLocalSettings} =~ s/\h*,\h*/,/g; + if($switches{readLocalSettings} =~ m/\+/){ + $logger->info("+ found in call for -l switch: will add localSettings.yaml"); + + # + can be either at the beginning or the end, which determines if where the comma should go + my $commaAtBeginning = ($switches{readLocalSettings} =~ m/^\h*\+/ ? q() : ","); + my $commaAtEnd = ($switches{readLocalSettings} =~ m/^\h*\+/ ? "," : q()); + $switches{readLocalSettings} =~ s/\h*\+\h*/$commaAtBeginning."localSettings.yaml".$commaAtEnd/e; + $logger->info("New value of -l switch: $switches{readLocalSettings}"); + } + + # local settings can be separated by , + # e.g + # -l = myyaml1.yaml,myyaml2.yaml + # and in which case, we need to read them all + if($switches{readLocalSettings} =~ m/,/){ + $logger->info("Multiple localSettings found, separated by commas:"); + @localSettings = split(/,/,$switches{readLocalSettings}); + $logger->info(join(', ',@localSettings)); + } else { + push(@localSettings,$switches{readLocalSettings}) if($switches{readLocalSettings}); + } + + # add local settings to the paths, if appropriate + foreach (@localSettings) { + # check for an extension (.yaml) + my ($name, $dir , $ext) = fileparse($_, "yaml"); + + # if no extension is found, append the current localSetting with .yaml + $_ = $_.($_=~m/\.\z/ ? q() : ".")."yaml" if(!$ext); + + # if the -l switch is called on its own, or else with + + # and latexindent.pl is called from a different directory, then + # we need to account for this + if($_ eq "localSettings.yaml"){ + $_ = dirname (${$self}{fileName})."/".$_; + } + + # check for existence and non-emptiness + if ( (-e $_) and !(-z $_)) { + $logger->info("Adding $_ to YAML read paths"); + push(@absPaths,"$_"); + } elsif ( !(-e $_) ) { + $logger->warn("*yaml file not found: $_ not found. Proceeding without it."); + } + } + + # heading for the log file + $logger->info("*YAML settings, reading from the following files:") if @absPaths; + + # 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)) { + $logger->info("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"){ + $logger->trace("*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"){ + $logger->trace("*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 { + # settings such as commandCodeBlocks can have arrays, which may wish + # to be amalgamated, rather than overwritten + if(ref($secondLevelValue) eq "ARRAY" + and + ${${$masterSettings{$firstLevelKey}{$secondLevelKey}}[0]}{amalgamate} + and + !(ref(${$secondLevelValue}[0]) eq "HASH" and defined ${$secondLevelValue}[0]{amalgamate} and !${$secondLevelValue}[0]{amalgamate}) + ){ + $logger->trace("*$firstLevelKey -> $secondLevelKey, amalgamate: 1") if($is_t_switch_active); + foreach (@{$secondLevelValue}){ + $logger->trace("$_") if($is_t_switch_active); + push (@{$masterSettings{$firstLevelKey}{$secondLevelKey}},$_) unless(ref($_) eq "HASH"); + } + + # remove duplicated entries, https://stackoverflow.com/questions/7651/how-do-i-remove-duplicate-items-from-an-array-in-perl + my %seen = (); + my @unique = grep { ! $seen{ $_ }++ } @{$masterSettings{$firstLevelKey}{$secondLevelKey}}; + @{$masterSettings{$firstLevelKey}{$secondLevelKey}} = @unique; + + $logger->trace("*master settings for $firstLevelKey -> $secondLevelKey now look like:") if $is_t_switch_active; + foreach (@{$masterSettings{$firstLevelKey}{$secondLevelKey}}){ + $logger->trace("$_") if($is_t_switch_active); + } + } else { + $masterSettings{$firstLevelKey}{$secondLevelKey} = $secondLevelValue; + } + } + } + } elsif (ref($firstLevelValue) eq "ARRAY") { + # update amalgamate in master settings + if(ref(${$firstLevelValue}[0]) eq "HASH" and defined ${$firstLevelValue}[0]{amalgamate}){ + ${$masterSettings{$firstLevelKey}[0]}{amalgamate} = ${$firstLevelValue}[0]{amalgamate}; + shift @{$firstLevelValue} if ${$masterSettings{$firstLevelKey}[0]}{amalgamate}; + } + + # if amalgamate is set to 1, then append + if(${$masterSettings{$firstLevelKey}[0]}{amalgamate}){ + # loop through the other settings + foreach (@{$firstLevelValue}){ + push (@{$masterSettings{$firstLevelKey}},$_); + } + } else { + # otherwise overwrite + $masterSettings{$firstLevelKey} = $firstLevelValue; + } + } else { + $masterSettings{$firstLevelKey} = $firstLevelValue; + } + } + + # output settings to $logfile + if($masterSettings{logFilePreferences}{showEveryYamlRead}){ + $logger->info(Dump \%{$userSettings->[0]}); + } else { + $logger->info("Not showing settings in the log file (see showEveryYamlRead and showAmalgamatedSettings)."); + } + } else { + # otherwise print a warning that we can not read userSettings.yaml + $logger->warn("*$settings contains invalid yaml format- not reading from it"); + } + } else { + # otherwise keep going, but put a warning in the log file + $logger->warn("*$homeDir/indentconfig.yaml"); + if (-z $settings) { + $logger->info("specifies $settings but this file is EMPTY -- not reading from it"); + } else { + $logger->info("specifies $settings but this file does not exist - unable to read settings from this file"); + } + } + } + + # read settings from -y|--yaml switch + if($switches{yaml}){ + # report to log file + $logger->info("*YAML settings read: -y switch"); + + # remove any horizontal space before or after , OR : OR ; or at the beginning or end of the switch value + $switches{yaml} =~ s/\h*(,|(?<!\\):|;)\h*/$1/g; + $switches{yaml} =~ s/^\h*//g; + + # store settings, possibly multiple ones split by commas + my @yamlSettings; + if($switches{yaml} =~ m/(?<!\\),/){ + @yamlSettings = split(/(?<!\\),/,$switches{yaml}); + } else { + push(@yamlSettings,$switches{yaml}); + } + + # it is possible to specify, for example, + # + # -y=indentAfterHeadings:paragraph:indentAfterThisHeading:1;level:1 + # -y=specialBeginEnd:displayMath:begin:'\\\[';end: '\\\]';lookForThis: 1 + # + # which should be translated into + # + # indentAfterHeadings: + # paragraph: + # indentAfterThisHeading:1 + # level:1 + # + # so we need to loop through the comma separated list and search + # for semi-colons + my $settingsCounter=0; + my @originalYamlSettings = @yamlSettings; + foreach(@originalYamlSettings){ + # increment the counter + $settingsCounter++; + + # check for a match of the ; + if($_ =~ m/(?<!\\);/){ + my (@subfield) = split(/(?<!\\);/,$_); + + # the content up to the first ; is called the 'root' + my $root = shift @subfield; + + # split the root at : + my (@keysValues) = split(/:/,$root); + + # get rid of the last *two* elements, which will be + # key: value + # for example, in + # -y=indentAfterHeadings:paragraph:indentAfterThisHeading:1;level:1 + # then @keysValues holds + # indentAfterHeadings:paragraph:indentAfterThisHeading:1 + # so we need to get rid of both + # 1 + # indentAfterThisHeading + # so that we are in a position to concatenate + # indentAfterHeadings:paragraph + # with + # level:1 + # to form + # indentAfterHeadings:paragraph:level:1 + pop(@keysValues); + pop(@keysValues); + + # update the appropriate piece of the -y switch, for example: + # -y=indentAfterHeadings:paragraph:indentAfterThisHeading:1;level:1 + # needs to be changed to + # -y=indentAfterHeadings:paragraph:indentAfterThisHeading:1 + # the + # indentAfterHeadings:paragraph:level:1 + # will be added in the next part + $yamlSettings[$settingsCounter-1] = $root; + + # reform the root + $root = join(":",@keysValues); + $logger->trace("*Sub-field detected (; present) and the root is: $root") if $is_t_switch_active; + + # now we need to attach the $root back together with any subfields + foreach(@subfield){ + # splice the new field into @yamlSettings (reference: https://perlmaven.com/splice-to-slice-and-dice-arrays-in-perl) + splice @yamlSettings,$settingsCounter,0,$root.":".$_; + + # increment the counter + $settingsCounter++; + } + $logger->info("-y switch value interpreted as: ".join(',',@yamlSettings)); + } + } + + # loop through each of the settings specified in the -y switch + foreach(@yamlSettings){ + # split each value at semi-colon + my (@keysValues) = split(/(?<!(?:\\|\[)):(?!\])/,$_); + + # $value will always be the last element + my $value = $keysValues[-1]; + + # it's possible that the 'value' will contain an escaped + # semi-colon, so we replace it with just a semi-colon + $value =~ s/\\:/:/; + + # horizontal space needs special treatment + if($value =~ m/^(?:"|')(\h*)(?:"|')$/){ + # pure horizontal space + $value = $1; + } elsif($value =~ m/^(?:"|')((?:\\t)*)(?:"|')$/){ + # tabs + $value =~ s/^(?:"|')//; + $value =~ s/(?:"|')$//; + $value =~ s/\\t/\t/g; + } + + if(scalar(@keysValues) == 2){ + # for example, -y="defaultIndent: ' '" + my $key = $keysValues[0]; + $logger->info("Updating masterSettings with $key: $value"); + $masterSettings{$key} = $value; + } elsif(scalar(@keysValues) == 3){ + # for example, -y="indentRules: one: '\t\t\t\t'" + my $parent = $keysValues[0]; + my $child = $keysValues[1]; + $logger->info("Updating masterSettings with $parent: $child: $value"); + $masterSettings{$parent}{$child} = $value; + } elsif(scalar(@keysValues) == 4){ + # for example, -y='modifyLineBreaks : environments: EndStartsOnOwnLine:3' -m + my $parent = $keysValues[0]; + my $child = $keysValues[1]; + my $grandchild = $keysValues[2]; + $logger->info("Updating masterSettings with $parent: $child: $grandchild: $value"); + $masterSettings{$parent}{$child}{$grandchild} = $value; + } elsif(scalar(@keysValues) == 5){ + # for example, -y='modifyLineBreaks : environments: one: EndStartsOnOwnLine:3' -m + my $parent = $keysValues[0]; + my $child = $keysValues[1]; + my $grandchild = $keysValues[2]; + my $greatgrandchild = $keysValues[3]; + $logger->info("Updating masterSettings with $parent: $child: $grandchild: $greatgrandchild: $value"); + $masterSettings{$parent}{$child}{$grandchild}{$greatgrandchild} = $value; + } + } + } + + # 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}){ + $logger->info("Amalgamated/overall settings to be used:"); + $logger->info(Dump \%masterSettings); + } + + return; +} + +sub yaml_get_indentation_settings_for_this_object{ + my $self = shift; + + # create a name for previously found settings + my $storageName = ${$self}{name}.${$self}{modifyLineBreaksYamlName}.(defined ${$self}{storageNameAppend}?${$self}{storageNameAppend}:q()); + + # grab the logging object + my $logger = get_logger("Document"); + + # check for storage of repeated objects + if ($previouslyFoundSettings{$storageName}){ + $logger->trace("*Using stored settings for $storageName") if($is_t_switch_active); + } else { + my $name = ${$self}{name}; + $logger->trace("Storing settings for $storageName") if($is_t_switch_active); + + # check for noAdditionalIndent and indentRules + # otherwise use defaultIndent + my $indentation = $self->yaml_get_indentation_information; + + # check for alignment at ampersand settings + $self->yaml_alignment_at_ampersand_settings; + + # check for line break settings + $self->yaml_modify_line_breaks_settings if $is_m_switch_active; + + # store the settings + %{${previouslyFoundSettings}{$storageName}} = ( + indentation=>$indentation, + BeginStartsOnOwnLine=>${$self}{BeginStartsOnOwnLine}, + BodyStartsOnOwnLine=>${$self}{BodyStartsOnOwnLine}, + EndStartsOnOwnLine=>${$self}{EndStartsOnOwnLine}, + EndFinishesWithLineBreak=>${$self}{EndFinishesWithLineBreak}, + removeParagraphLineBreaks=>${$self}{removeParagraphLineBreaks}, + textWrapOptions=>${$self}{textWrapOptions}, + columns=>${$self}{columns}, + ); + + # don't forget alignment settings! + foreach (@alignAtAmpersandInformation){ + ${${previouslyFoundSettings}{$storageName}}{${$_}{name}} = ${$self}{${$_}{name}} if(defined ${$self}{${$_}{name}}); + } + + # 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}{$_}; + } + + # log file information + $logger->trace("Settings for $name (stored for future use):") if $is_tt_switch_active; + $logger->trace(Dump \%{${previouslyFoundSettings}{$storageName}}) if $is_tt_switch_active; + + } + + # append indentation settings to the current object + while( my ($key,$value)= each %{${previouslyFoundSettings}{$storageName}}){ + ${$self}{$key} = $value; + } + + return; +} + +sub yaml_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}; + + if(ref ${$masterSettings{lookForAlignDelims}}{$name} eq "HASH"){ + # specified as a hash, e.g + # + # lookForAlignDelims: + # tabular: + # delims: 1 + # alignDoubleBackSlash: 1 + # spacesBeforeDoubleBackSlash: 2 + foreach (@alignAtAmpersandInformation){ + my $yamlname = (defined ${$_}{yamlname} ? ${$_}{yamlname}: ${$_}{name}); + ${$self}{${$_}{name}} = (defined ${${$masterSettings{lookForAlignDelims}}{$name}}{$yamlname} ) ? ${${$masterSettings{lookForAlignDelims}}{$name}}{$yamlname} : ${$_}{default}; + } + } else { + # specified as a scalar, e.g + # + # lookForAlignDelims: + # tabular: 1 + foreach (@alignAtAmpersandInformation){ + ${$self}{${$_}{name}} = ${$_}{default}; + } + } + return; +} + +sub yaml_modify_line_breaks_settings{ + my $self = shift; + + # grab the logging object + my $logger = get_logger("Document"); + + # details to the log file + $logger->trace("*-m modifylinebreaks switch active") if $is_t_switch_active; + $logger->trace("looking for polyswitch, textWrapOptions, removeParagraphLineBreaks, oneSentencePerLine settings for ${$self}{name} ") 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 efficiently loop through the following + foreach (@toBeAssignedTo){ + $self->yaml_poly_switch_get_every_or_custom_value( + toBeAssignedTo=>$_, + toBeAssignedToAlias=> ${$self}{aliases}{$_} ? ${$self}{aliases}{$_} : $_, + ); + }; + + $self->yaml_get_textwrap_removeparagraphline_breaks; + return; +} + +sub yaml_get_textwrap_removeparagraphline_breaks{ + my $self = shift; + + # grab the logging object + my $logger = get_logger("Document"); + + # textWrap and removeParagraphLineBreaks settings + foreach ("textWrapOptions","removeParagraphLineBreaks"){ + + # first check for either + # + # textWrapOptions: + # all: 0 + # + # or + # + # removeParagraphLineBreaks: + # all: 0 + # + # *IMPORTANT* + # even if all is set to 1, then it can still be disabled on either a + # + # per-object: + # + # for example + # + # textWrapOptions: + # all: + # except: + # - environments + # + # will disable textWrapOptions for *all* environments + # + # per-name + # + # for example + # + # textWrapOptions: + # all: + # except: + # - itemize + # + # will disable textWrapOptions for itemize + + # if 'all' is set as a hash, then the default value is 1, to be turned off (possibly) later + ${$self}{$_} = ( ref ${$masterSettings{modifyLineBreaks}{$_}}{all} eq "HASH" ? 1 : ${$masterSettings{modifyLineBreaks}{$_}}{all}); + + # get the columns + if($_ eq "textWrapOptions" and ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{perCodeBlockBasis}){ + $self->yaml_get_columns; + } + + # 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}; + + # move to the next <thing> if + # + # textWrapOptions/removeParagraphLineBreaks:: + # all: 1 + # + if(${$self}{$_} + and + ref ${$masterSettings{modifyLineBreaks}{$_}}{all} ne "HASH" + and + ${$masterSettings{modifyLineBreaks}{$_}}{all}){ + $logger->trace("$_ for $name is ${$self}{$_}") if $is_t_switch_active; + next; + }; + + # otherwise, look for exceptions, either through + # + # textWrapOptions/removeParagraphLineBreaks: + # all: + # except: + # - <*type* of thing or *name* of thing> + # + # so, for example, the following (per code-block) is acceptable + # which makes an exception for all *environments* + # + # all: + # except: + # - 'environments' + # + # the following (per-name) is acceptable + # which only makes an exception for things called itemize + # + # all: + # except: + # - 'itemize' + # + if(${$self}{$_} + and + defined ${${$masterSettings{modifyLineBreaks}{$_}}{all}}{except} + and + ref ${${$masterSettings{modifyLineBreaks}{$_}}{all}}{except} eq "ARRAY" + ){ + my %except = map { $_ => 1 } @{${${$masterSettings{modifyLineBreaks}}{$_}}{all}{except}}; + if( $except{$name} or $except{$YamlName}){ + ${$self}{$_} = 0; + my $detail = ($except{$name} ? "per-name" : "per-code-block-type"); + $logger->trace("$_ for $name is ${$self}{$_} (found as exception $detail, see $_:all:except)") if $is_t_switch_active; + next; + } + } else { + # or otherwise through, for example + # + # all: 0 + # ifElseFi: 1 + # + # the textWrapOptions/removeParagraphLineBreaks can contain fields that are hashes or scalar + # + if(ref ${$masterSettings{modifyLineBreaks}{$_}}{$YamlName} eq "HASH"){ + # textWrapOptions/removeParagraphLineBreaks: + # all: 0 + # environments: + # quotation: 0 + $logger->trace("*$YamlName specified with fields in $_, looking for $name") if $is_t_switch_active; + ${$self}{$_} = ${${$masterSettings{modifyLineBreaks}{$_}}{$YamlName}}{$name} if (defined ${${$masterSettings{modifyLineBreaks}{$_}}{$YamlName}}{$name}); + } elsif(defined ${$masterSettings{modifyLineBreaks}{$_}}{$YamlName}){ + # textWrapOptions/removeParagraphLineBreaks: + # all: 0 + # environments: 0 + $logger->trace("*$YamlName specified with just a number in $_ ${$masterSettings{modifyLineBreaks}{$_}}{$YamlName}") if $is_t_switch_active; + ${$self}{$_} = ${$masterSettings{modifyLineBreaks}{$_}}{$YamlName} if (defined ${$masterSettings{modifyLineBreaks}{$_}}{$YamlName}); + } + } + + # summary to log file + $logger->trace("$_ for $name is ${$self}{$_}") if $is_t_switch_active; + } + + return; +} + +sub yaml_get_columns{ + my $self = shift; + + my $YamlName = ${$self}{modifyLineBreaksYamlName}; + + # the columns settings can have a variety of different ways of being specified + if(ref ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns} eq "HASH"){ + # assign default value of $columns + my $columns; + if(defined ${${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}}{default}){ + $columns = ${${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}}{default}; + } else { + $columns = 80; + } + + # possibly specify object wrapping on a per-name basis + if(ref ${${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}}{$YamlName} eq "HASH"){ + # for example: + # modifyLineBreaks: + # textWrapOptions: + # columns: + # default: 80 + # environments: + # default: 80 + # something: 10 + # another: 20 + if(defined ${${${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}}{$YamlName}}{${$self}{name}}){ + $columns = ${${${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}}{$YamlName}}{${$self}{name}}; + } elsif (${${${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}}{$YamlName}}{default}){ + $columns = ${${${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}}{$YamlName}}{default}; + } + } else { + # for example: + # modifyLineBreaks: + # textWrapOptions: + # columns: + # default: 80 + # environments: 10 + $columns = ${${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}}{$YamlName}; + } + ${$self}{columns} = $columns; + } else { + ${$self}{columns} = ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}; + } + return; +} + +sub yaml_poly_switch_get_every_or_custom_value{ + my $self = shift; + my %input = @_; + + my $toBeAssignedTo = $input{toBeAssignedTo}; + my $toBeAssignedToAlias = $input{toBeAssignedToAlias}; + + # grab the logging object + my $logger = get_logger("Document"); + + # alias + if(${$self}{aliases}{$toBeAssignedTo}){ + $logger->trace("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){ + $logger->trace("$name: $toBeAssignedToAlias=$customValue, (*per-name* 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){ + $logger->trace("$name: $toBeAssignedToAlias=$everyValue, (*global* value) adjusting $toBeAssignedTo") if($is_t_switch_active); + ${$self}{$toBeAssignedTo} = $everyValue; + } + } + return; +} + +sub yaml_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->yaml_get_object_attribute_for_indentation_settings; + + # grab the logging object + my $logger = get_logger("Document"); + + 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"){ + $logger->trace("$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}; + $logger->trace("$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){ + $logger->trace("Found! Using '' (see $indentationAbout)") if $is_t_switch_active; + return q(); + } elsif($indentationAbout eq "indentRules" and $indentationInformation=~m/^\h*$/){ + $logger->trace("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){ + $logger->trace("$globalInformation specified for $YamlName (see $globalInformation)") if $is_t_switch_active; + return q(); + } elsif($globalInformation eq "indentRulesGlobal") { + if(${$masterSettings{$globalInformation}}{$YamlName}=~m/^\h*$/){ + $logger->trace("$globalInformation specified for $YamlName (see $globalInformation)") if $is_t_switch_active; + return ${$masterSettings{$globalInformation}}{$YamlName}; + } else { + $logger->trace("$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 + $logger->trace("Using defaultIndent for $name") if $is_t_switch_active; + return $masterSettings{defaultIndent}; +} + +sub yaml_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/support/latexindent/LatexIndent/Heading.pm b/support/latexindent/LatexIndent/Heading.pm new file mode 100644 index 0000000000..208dfa9715 --- /dev/null +++ b/support/latexindent/LatexIndent/Heading.pm @@ -0,0 +1,245 @@ +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 LatexIndent::LogFile qw/$logger/; +use LatexIndent::Special qw/$specialBeginBasicRegExp/; +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 $allHeadingsRegexp/; +our $headingCounter; +our @headingsRegexpArray; +our $allHeadingsRegexp = q(); + +sub construct_headings_levels{ + my $self = shift; + + # grab the heading levels + my %headingsLevels = %{$masterSettings{indentAfterHeadings}}; + + # output to log file + $logger->trace("*Constructing headings reg exp for example, chapter, section, etc (see indentAfterThisHeading)") if $is_t_switch_active ; + + # delete the values that have indentAfterThisHeading set to 0 + while( my ($headingName,$headingInfo)= each %headingsLevels){ + if(!${$headingsLevels{$headingName}}{indentAfterThisHeading}){ + $logger->trace("Not indenting after $headingName (see indentAfterThisHeading)") if $is_t_switch_active; + delete $headingsLevels{$headingName}; + } else { + # *all heading* regexp, remembering to put starred headings at the front of the regexp + if($headingName =~ m/\*/){ + $logger->trace("Putting $headingName at the beginning of the allHeadings regexp, as it contains a *") if $is_t_switch_active ; + $allHeadingsRegexp = $headingName.($allHeadingsRegexp eq '' ?q():"|$allHeadingsRegexp"); + } else { + $logger->trace("Putting $headingName at the END of the allHeadings regexp") if $is_t_switch_active ; + $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; + + $logger->trace("*All headings regexp: $allHeadingsRegexp") if $is_tt_switch_active; + $logger->trace("*Now to construct headings regexp for each level:") if $is_t_switch_active; + + # 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/\*/){ + $logger->trace("Putting $_ at the beginning of this regexp (level $i), as it contains a *") if $is_t_switch_active; + $headingsAtThisLevel = $_.($headingsAtThisLevel eq '' ?q():"|$headingsAtThisLevel"); + } else { + $logger->trace("Putting $_ at the END of this regexp (level $i)") if $is_t_switch_active; + $headingsAtThisLevel .= ($headingsAtThisLevel eq '' ?q():"|").$_ ; + } + } + + # make the stars escaped correctly + $headingsAtThisLevel =~ s/\*/\\\*/g; + push(@headingsRegexpArray,$headingsAtThisLevel); + $logger->trace("Heading level regexp for level $i will contain: $headingsAtThisLevel") if $is_t_switch_active; + } + } + } + +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 + $logger->trace("*Searching ${$self}{name} for headings ") if $is_t_switch_active; + + # 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 + $logger->trace("heading found: $2") if $is_t_switch_active; + + ${$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 + $logger->trace("Custom replacement text routine for ${$self}{name}") if $is_t_switch_active; + ${$self}{replacementText} = ${$self}{id}.${$self}{afterbit}; + delete ${$self}{afterbit}; +} + +sub create_unique_id{ + my $self = shift; + + $headingCounter++; + + ${$self}{id} = "$tokens{afterHeading}$headingCounter"; + return; +} + +sub adjust_replacement_text_line_breaks_at_end{ + return; +} + +sub yaml_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){ + $logger->trace("Searching for linebreak preferences immediately infront of ${$self}{parent}") if $is_t_switch_active; + foreach(@{${$self}{children}}){ + if(${$_}{name} eq ${$self}{parent}){ + $logger->trace("Named child found: ${$_}{name}") if $is_t_switch_active; + if(defined ${$_}{BeginStartsOnOwnLine}){ + $logger->trace("Transferring information from ${$_}{id} (${$_}{name}) to ${$self}{id} (${$self}{name}) for BeginStartsOnOwnLine") if $is_t_switch_active; + ${$self}{BeginStartsOnOwnLine} = ${$_}{BeginStartsOnOwnLine}; + } else { + $logger->trace("No information found in ${$_}{name} for BeginStartsOnOwnLine") if $is_t_switch_active; + } + last; + } + } + } + + # search for special begin/end + $self->find_special if ${$self}{body} =~ m/$specialBeginBasicRegExp/s; + + 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; + + $logger->trace("Adding surrounding indentation after (empty, by design!) begin statement of ${$self}{name} (${$self}{id})") if $is_t_switch_active; + ${$self}{begin} .= ${$self}{surroundingIndentation}; # add indentation + +} + +1; diff --git a/support/latexindent/LatexIndent/HiddenChildren.pm b/support/latexindent/LatexIndent/HiddenChildren.pm new file mode 100644 index 0000000000..878939fd41 --- /dev/null +++ b/support/latexindent/LatexIndent/HiddenChildren.pm @@ -0,0 +1,205 @@ +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 $is_m_switch_active /; +use LatexIndent::Tokens qw/%tokens/; +use LatexIndent::LogFile qw/$logger/; +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 + $logger->trace("*FamilyTree before update:") if $is_tt_switch_active; + $logger->trace(Dumper(\%familyTree)) if($is_tt_switch_active); + + # update the family tree with ancestors + $self->update_family_tree; + + # output information to the logfile + $logger->trace("*FamilyTree after update:") if $is_tt_switch_active; + $logger->trace(Dumper(\%familyTree)) if($is_tt_switch_active); + + while( my ($idToSearch,$ancestorToSearch) = each %familyTree){ + $logger->trace("*Hidden child ID: ,$idToSearch, here are its ancestors:") if $is_t_switch_active; + foreach(@{${$ancestorToSearch}{ancestors}}){ + $logger->trace("ID: ${$_}{ancestorID}") if($is_t_switch_active); + my $tmpIndentation = ref(${$_}{ancestorIndentation}) eq 'SCALAR'?${${$_}{ancestorIndentation}}:${$_}{ancestorIndentation}; + $tmpIndentation = $tmpIndentation ? $tmpIndentation : q(); + $logger->trace("indentation: '$tmpIndentation'") if($is_t_switch_active); + } + } + + return; +} + +sub update_family_tree{ + my $self = shift; + + # loop through the hash + $logger->trace("*Updating FamilyTree...") if $is_t_switch_active; + while( my ($idToSearch,$ancestorToSearch)= each %familyTree){ + $logger->trace("*current ID: $idToSearch") if($is_t_switch_active); + foreach(@{${$ancestorToSearch}{ancestors}}){ + my $ancestorID = ${$_}{ancestorID}; + + # construct the natural ancestors + my $naturalAncestors = q(); + foreach(@{${$familyTree{$idToSearch}}{ancestors}}){ + $naturalAncestors .= "---".${$_}{ancestorID} if(${$_}{type} eq "natural"); + } + + # we only need to update the family tree if the $ancestorID is *not* a natural + # ancestor, otherwise everything will be taken care of by the natural ancestor + if($naturalAncestors !~ m/$ancestorID/){ + $logger->trace("ancestor: $ancestorID") if($is_t_switch_active); + if($familyTree{$ancestorID}){ + $logger->trace("$ancestorID is a key within familyTree, grabbing its ancestors") if($is_t_switch_active); + foreach(@{${$familyTree{$ancestorID}}{ancestors}}){ + $logger->trace("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 { + $logger->trace("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){ + $logger->trace("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; + + my @matched; + # grab the matches + if($is_m_switch_active){ + # if modifyLineBreaks is active, then the IDS can be split across lines + my $ifElseFiSpecialBegin = join("\\R?\\h*",split(//,$tokens{ifelsefiSpecial})); + my $BeginwithLineBreaks = join("\\R?\\h*",split(//,$tokens{beginOfToken})); + my $EndwithLineBreaks = join("\\R?\\h*",split(//,$tokens{endOfToken})); + my $BlankLinesWithLineBreaks = join("\\R?\\h*",split(//,$tokens{blanklines})); + @matched = (${$self}{body} =~ /(?!$BlankLinesWithLineBreaks)((?:$ifElseFiSpecialBegin)?$BeginwithLineBreaks[-a-z0-9\n]+?$EndwithLineBreaks)/ig); + + # remove line breaks and horizontal space from matches + $_ =~ s/\R|\h//gs foreach (@matched); + } else { + @matched = (${$self}{body} =~ /((?:$tokens{ifelsefiSpecial})?$tokens{beginOfToken}.[-a-z0-9]+?$tokens{endOfToken})/igs); + } + + # log file + $logger->trace("*Hidden children check") if $is_t_switch_active; + $logger->trace(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"; + $logger->trace("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"; + $logger->trace("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/support/latexindent/LatexIndent/HorizontalWhiteSpace.pm b/support/latexindent/LatexIndent/HorizontalWhiteSpace.pm new file mode 100644 index 0000000000..9c5a04e2fb --- /dev/null +++ b/support/latexindent/LatexIndent/HorizontalWhiteSpace.pm @@ -0,0 +1,79 @@ +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 LatexIndent::LogFile qw/$logger/; +use Exporter qw/import/; +our @EXPORT_OK = qw/remove_trailing_whitespace remove_leading_space/; + +sub remove_trailing_whitespace{ + my $self = shift; + my %input = @_; + + $logger->trace("*Horizontal space removal routine") if $is_t_switch_active; + + # removeTrailingWhitespace can be either a hash or a scalar, but if + # it's a scalar, we need to fix it + if(ref($masterSettings{removeTrailingWhitespace}) ne 'HASH'){ + $logger->trace("removeTrailingWhitespace specified as scalar, will update it to be a hash") if $is_t_switch_active; + # grab the value + my $removeTWS = $masterSettings{removeTrailingWhitespace}; + + # delete the scalar + delete $masterSettings{removeTrailingWhitespace}; + + # redefine it as a hash + ${$masterSettings{removeTrailingWhitespace}}{beforeProcessing} = $removeTWS; + ${$masterSettings{removeTrailingWhitespace}}{afterProcessing} = $removeTWS; + $logger->trace("removeTrailingWhitespace: beforeProcessing is now $removeTWS") if $is_t_switch_active; + $logger->trace("removeTrailingWhitespace: afterProcessing is now $removeTWS") if $is_t_switch_active; + } + + # this method can be called before the indendation, and after, depending upon the input + if($input{when} eq "before"){ + return unless(${$masterSettings{removeTrailingWhitespace}}{beforeProcessing}); + $logger->trace("Removing trailing white space *before* the document is processed (see removeTrailingWhitespace: beforeProcessing)") if $is_t_switch_active; + } elsif($input{when} eq "after"){ + return unless(${$masterSettings{removeTrailingWhitespace}}{afterProcessing}); + $logger->trace("Removing trailing white space *after* the document is processed (see removeTrailingWhitespace: afterProcessing)") if $is_t_switch_active; + } else { + return; + } + + ${$self}{body} =~ s/ + \h+ # followed by possible horizontal space + $ # up to the end of a line + //xsmg; + +} + +sub remove_leading_space{ + my $self = shift; + $logger->trace("*Removing leading space from ${$self}{name} (verbatim/noindentblock already accounted for)") 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/support/latexindent/LatexIndent/IfElseFi.pm b/support/latexindent/LatexIndent/IfElseFi.pm new file mode 100644 index 0000000000..056b36d8c3 --- /dev/null +++ b/support/latexindent/LatexIndent/IfElseFi.pm @@ -0,0 +1,211 @@ +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::GetYamlSettings qw/%masterSettings/; +use LatexIndent::TrailingComments qw/$trailingCommentRegExp/; +use LatexIndent::Switches qw/$is_m_switch_active $is_t_switch_active $is_tt_switch_active/; +use LatexIndent::LogFile qw/$logger/; +use LatexIndent::Heading qw/$allHeadingsRegexp/; +use Data::Dumper; +use Exporter qw/import/; +our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321 +our @EXPORT_OK = qw/find_ifelsefi construct_ifelsefi_regexp $ifElseFiBasicRegExp/; +our $ifElseFiCounter; +our $ifElseFiRegExp; +our $ifElseFiBasicRegExp = qr/\\if/; + +# 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 + +sub construct_ifelsefi_regexp{ + my $ifElseFiNameRegExp = qr/${${$masterSettings{fineTuning}}{ifElseFi}}{name}/; + $ifElseFiRegExp = qr/ + ( + \\ + ($ifElseFiNameRegExp) + \h* + (\R*) + ) # begin statement, e.g \ifnum, \ifodd + ( + \\(?!if)|[0-9]|\R|\h|\#|!-!|$trailingCommentRegExp # 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 find_ifelsefi{ + my $self = shift; + + while( ${$self}{body} =~ m/$ifElseFiRegExp\h*($trailingCommentRegExp)?/){ + + ${$self}{body} =~ s/ + $ifElseFiRegExp(\h*)($trailingCommentRegExp)? + / + # 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", + }, + modifyLineBreaksYamlName=>"ifElseFi", + endImmediatelyFollowedByComment=>$9?0:($11?1:0), + horizontalTrailingSpace=>$8?$8:q(), + ); + # log file output + $logger->trace("*IfElseFi found: $2")if $is_t_switch_active; + + # 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 post_indentation_check{ + # needed to remove leading horizontal space before \else + my $self = shift; + + # loop through \else and \or + foreach ({regExp=>qr/\\else/},{regExp=>qr/\\or/}){ + my %else = %{$_}; + if(${$self}{body} =~ m/^\h*$else{regExp}/sm + and + !(${$self}{body} =~ m/^\h*$else{regExp}/s and ${$self}{linebreaksAtEnd}{begin}==0) + ){ + $logger->trace("*Adding surrounding indentation to $else{regExp} statement(s) ('${$self}{surroundingIndentation}')") if $is_t_switch_active; + ${$self}{body} =~ s/^\h*($else{regExp})/${$self}{surroundingIndentation}$1/smg; + } + } + 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( + # else name regexp + elseNameRegExp=>qr|\\else|, + # else statements name + ElseStartsOnOwnLine=>"ElseStartsOnOwnLine", + # end statements + ElseFinishesWithLineBreak=>"ElseFinishesWithLineBreak", + # for the YAML settings storage + storageNameAppend=>"else", + # logfile information + logName=>"else", + ); + + # check for existence of \or statement, and associated line break information + $self->check_for_else_statement( + # else name regexp + elseNameRegExp=>qr|\\or|, + # else statements name + ElseStartsOnOwnLine=>"OrStartsOnOwnLine", + # end statements + ElseFinishesWithLineBreak=>"OrFinishesWithLineBreak", + # for the YAML settings storage + storageNameAppend=>"or", + # logfile information + logName=>"or", + ); + + # search for headings (important to do this before looking for commands!) + $self->find_heading if ${$self}{body} =~ m/$allHeadingsRegexp/s; + + # search for commands and special code blocks + $self->find_commands_or_key_equals_values_braces_and_special; + +} + +sub indent_begin{ + my $self = shift; + # 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} .= " "; + } +} + +sub wrap_up_statement{ + my $self = shift; + + # 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 + $logger->trace("*Adding a single space after \\fi statement (otherwise \\fi can be comined with next line of text in an unwanted way)") if $is_t_switch_active; + ${$self}{end} =${$self}{end}." "; + } + $logger->trace("*Finished indenting ${$self}{name}") if $is_t_switch_active; + return $self; +} + + +sub create_unique_id{ + my $self = shift; + + $ifElseFiCounter++; + + ${$self}{id} = "$tokens{ifElseFi}$ifElseFiCounter"; + return; +} + + +1; diff --git a/support/latexindent/LatexIndent/Indent.pm b/support/latexindent/LatexIndent/Indent.pm new file mode 100644 index 0000000000..eb36db2108 --- /dev/null +++ b/support/latexindent/LatexIndent/Indent.pm @@ -0,0 +1,425 @@ +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 LatexIndent::GetYamlSettings qw/%masterSettings/; +use LatexIndent::LogFile qw/$logger/; +use Text::Tabs; +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 post_indentation_check/; +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 + $logger->trace("Complete indented object (${$self}{name}) after indentation:\n${$self}{begin}${$self}{body}${$self}{end}") if $is_tt_switch_active; + + # wrap-up statement + $self->wrap_up_statement; + return $self; +} + +sub wrap_up_statement{ + my $self = shift; + $logger->trace("*Finished indenting ${$self}{name}") 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}; + $logger->trace("indenting object ${$self}{name}") if($is_t_switch_active); + (my $during = $surroundingIndentation) =~ s/\t/TAB/g; + $logger->trace("indentation *surrounding* object: '$during'") if($is_t_switch_active); + ($during = ${$self}{indentation}) =~ s/\t/TAB/g; + $logger->trace("indentation *of* object: '$during'") if($is_t_switch_active); + ($during = $surroundingIndentation.${$self}{indentation}) =~ s/\t/TAB/g; + $logger->trace("*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}}){ + $logger->trace("Adopted ancestors found!") if($is_t_switch_active); + foreach(@{${$familyTree{${$self}{id}}}{ancestors}}){ + if(${$_}{type} eq "adopted"){ + my $newAncestorId = ${$_}{ancestorID}; + $logger->trace("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 + $logger->trace("Body (${$self}{name}) before indentation:\n${$self}{body}") if $is_tt_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}; + + # possibly remove paragraph line breaks + $self->remove_paragraph_line_breaks if ($is_m_switch_active and ${$self}{removeParagraphLineBreaks} and !${$masterSettings{modifyLineBreaks}{removeParagraphLineBreaks}}{beforeTextWrap}); + + # body indendation + if(${$self}{linebreaksAtEnd}{begin}==1){ + if(${$self}{body} =~ m/^\h*$/s){ + $logger->trace("Body of ${$self}{name} is empty, not applying indentation") if $is_t_switch_active; + } else { + # put any existing horizontal space after the current indentation + $logger->trace("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; + $logger->trace("first line of body: $bodyFirstLine") if $is_tt_switch_active; + $logger->trace("remaining body (before indentation):\n'$remainingBody'") if($is_tt_switch_active); + + # add the indentation to all the body except first line + $remainingBody =~ s/^/$indentation/mg unless($remainingBody eq ''); # add indentation + $logger->trace("remaining body (after indentation):\n$remainingBody'") if($is_tt_switch_active); + + # put the body back together + ${$self}{body} = $bodyFirstLine."\n".$remainingBody; + } + } + + # some objects need a post-indentation check, e.g ifElseFi + $self->post_indentation_check; + # 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$/ and ${$self}{body} ne ''){ + $logger->trace("Adding a linebreak at end of 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 + $logger->trace("Body (${$self}{name}) after indentation:\n${$self}{body}") if $is_tt_switch_active; + return $self; +} + +sub post_indentation_check{ + return; +} + +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 + $logger->trace("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; + $logger->trace("Number of tabs: $numberOfTABS") if($is_t_switch_active); + + # log the after + ($after = $indentation) =~ s|\t||g; + $after = "TAB"x$numberOfTABS.$after; + $logger->trace("Indentation after: '$after'") if($is_t_switch_active); + ($indentation = $after) =~s|TAB|\t|g; + + $indentation; + /xsmeg; + + return unless($masterSettings{maximumIndentation} =~ m/^\h+$/); + + # maximum indentation check + $logger->trace("*Maximum indentation check") if($is_t_switch_active); + + # replace any leading tabs with spaces, and update the body + my @expanded_lines = expand(${$self}{body}); + ${$self}{body} = join("",@expanded_lines); + + # grab the maximum indentation + my $maximumIndentation = $masterSettings{maximumIndentation}; + my $maximumIndentationLength = length($maximumIndentation)+1; + + # replace any leading space that is greater than the + # specified maximum indentation with the maximum indentation + ${$self}{body} =~ s/^\h{$maximumIndentationLength,}/$maximumIndentation/smg; +} + +sub indent_children_recursively{ + my $self = shift; + + unless(defined ${$self}{children}) { + $logger->trace("No child objects (${$self}{name})") if $is_t_switch_active; + return; + } + + $logger->trace('Pre-processed body:') if $is_tt_switch_active; + $logger->trace(${$self}{body}) if($is_tt_switch_active); + + # send the children through this indentation routine recursively + if(defined ${$self}{children}){ + foreach my $child (@{${$self}{children}}){ + $logger->trace("Indenting child objects on ${$child}{name}") if $is_t_switch_active; + $child->indent_children_recursively; + } + } + + $logger->trace("*Replacing ids with begin, body, and end statements:") 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}}){ + $logger->trace("Searching ${$self}{name} for ${$child}{id}...") if $is_t_switch_active; + if(${$self}{body} =~ m/${$child}{idRegExp}/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}{idRegExp}/m + or + ${$self}{body} =~ m/^\h\h*${$child}{idRegExp}/m + ) ?1:0; + $IDFollowedImmediatelyByLineBreak = (${$self}{body} =~ m/${$child}{idRegExp}\h*\R+/m) ?1:0; + ${$child}{IDFollowedImmediatelyByLineBreak} = $IDFollowedImmediatelyByLineBreak; + } + + # log file info + $logger->trace("${$child}{id} found!") if($is_t_switch_active); + $logger->trace("*Indenting ${$child}{name} (id: ${$child}{id})") if $is_t_switch_active; + $logger->trace("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"; + $logger->trace("Removing linebreak after ${$child}{end} (see $EndStringLogFile)") if $is_t_switch_active; + ${$self}{body} =~ s/${$child}{idRegExp}(\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"; + + # + # Blank line poly-switch notes (==4) + # + # when BeginStartsOnOwnLine=4 we adopt the following approach: + # temporarily change BeginStartsOnOwnLine to -1, make adjustments + # temporarily change BeginStartsOnOwnLine to 3, make adjustments + # + # we use an array, @polySwitchValues to facilitate this + my @polySwitchValues = (${$child}{BeginStartsOnOwnLine}==4)?(-1,3):(${$child}{BeginStartsOnOwnLine}); + + foreach(@polySwitchValues){ + # if BeginStartsOnOwnLine is 4, then we hack + # $IDFirstNonWhiteSpaceCharacter + # to be 0 on the second time through (poly-switch set to 3) + $IDFirstNonWhiteSpaceCharacter = 0 if (${$child}{BeginStartsOnOwnLine}==4 and $_==3); + + # if the child ID is not the first character and BeginStartsOnOwnLine>=1 + # then we will need to add a line break (==1), a comment (==2) or another blank line (==3) + if($_>=1 and !$IDFirstNonWhiteSpaceCharacter){ + # by default, assume that no trailing comment token is needed + my $trailingCharacterToken = q(); + if($_==2){ + $logger->trace("Removing space immediately before ${$child}{id}, in preparation for adding % ($BeginStringLogFile == 2)") if $is_t_switch_active; + ${$self}{body} =~ s/\h*${$child}{idRegExp}/${$child}{id}/s; + $logger->trace("Adding a % at the end of the line that ${$child}{begin} is on, then a linebreak ($BeginStringLogFile == 2)") if $is_t_switch_active; + $trailingCharacterToken = "%".$self->add_comment_symbol; + } elsif ($_==3){ + $logger->trace("Adding a blank line at the end of the line that ${$child}{begin} is on, then a linebreak ($BeginStringLogFile == 3)") if $is_t_switch_active; + $trailingCharacterToken = "\n".(${$masterSettings{modifyLineBreaks}}{preserveBlankLines}?$tokens{blanklines}:q()); + } else { + $logger->trace("Adding a linebreak at the beginning of ${$child}{begin} (see $BeginStringLogFile)") if $is_t_switch_active; + } + + # the trailing comment/linebreak magic + ${$child}{begin} = "$trailingCharacterToken\n".${$child}{begin}; + $child->add_surrounding_indentation_to_begin_statement; + + # remove surrounding indentation ahead of % + ${$child}{begin} =~ s/^(\h*)%/%/ if($_==2); + } elsif ($_==-1 and $IDFirstNonWhiteSpaceCharacter){ + # finally, if BeginStartsOnOwnLine == -1 then we might need to *remove* a blank line(s) + # 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}{idRegExp}/s){ + $logger->trace("Removing linebreak before ${$child}{begin} (see $BeginStringLogFile in ${$child}{modifyLineBreaksYamlName} YAML)") if $is_t_switch_active; + ${$self}{body} =~ s/(\h*)(?:\R*|\h*)+${$child}{idRegExp}/$1${$child}{id}/s; + } else { + $logger->trace("Not removing linebreak ahead of ${$child}{begin}, as blank-line-token present (see preserveBlankLines)") if $is_t_switch_active; + } + } + } + } + + $logger->trace(Dumper(\%{$child})) if($is_tt_switch_active); + + # replace ids with body + ${$self}{body} =~ s/${$child}{idRegExp}/${$child}{begin}${$child}{body}${$child}{end}/; + + # log file info + $logger->trace("Body (${$self}{name}) now looks like:") if $is_tt_switch_active; + $logger->trace(${$self}{body}) if($is_tt_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 + $logger->trace("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 { + $logger->trace("${$child}{id} not found") if($is_t_switch_active); + } + + # increment the loop counter + $index++; + } + } + + # logfile info + $logger->trace("${$self}{name} has this many children:") if $is_tt_switch_active; + $logger->trace(scalar @{${$self}{children}}) if $is_tt_switch_active; + $logger->trace("Post-processed body (${$self}{name}):") if($is_tt_switch_active); + $logger->trace(${$self}{body}) if($is_tt_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/support/latexindent/LatexIndent/Item.pm b/support/latexindent/LatexIndent/Item.pm new file mode 100644 index 0000000000..f9fbf8dd4b --- /dev/null +++ b/support/latexindent/LatexIndent/Item.pm @@ -0,0 +1,148 @@ +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 LatexIndent::LogFile qw/$logger/; +use LatexIndent::IfElseFi qw/$ifElseFiBasicRegExp/; +use LatexIndent::Special qw/$specialBeginAndBracesBracketsBasicRegExp/; +use LatexIndent::Heading qw/$allHeadingsRegexp/; +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 $listOfItems/; +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 + $logger->trace("*List of items: $listOfItems (see itemNames)") if $is_t_switch_active; + + $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; + + # otherwise loop through the item names + $logger->trace("Searching for items (see itemNames) in ${$self}{name} (see indentAfterItems)") if $is_t_switch_active; + $logger->trace(Dumper(\%{$masterSettings{itemNames}})) if $is_tt_switch_active; + + while(${$self}{body} =~ m/$itemRegExp\h*($trailingCommentRegExp)?/){ + + # log file output + $logger->trace("*Item found: $2") 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{items}$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 if ${$self}{body} =~ m/$ifElseFiBasicRegExp/s; + + # search for headings (part, chapter, section, setc) + $self->find_heading if ${$self}{body} =~ m/$allHeadingsRegexp/s; + + # search for commands and special code blocks + $self->find_commands_or_key_equals_values_braces_and_special if ${$self}{body} =~ m/$specialBeginAndBracesBracketsBasicRegExp/s; + +} + +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"; + $logger->trace("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/support/latexindent/LatexIndent/KeyEqualsValuesBraces.pm b/support/latexindent/LatexIndent/KeyEqualsValuesBraces.pm new file mode 100644 index 0000000000..590d9acb8c --- /dev/null +++ b/support/latexindent/LatexIndent/KeyEqualsValuesBraces.pm @@ -0,0 +1,121 @@ +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::GetYamlSettings qw/%masterSettings/; +use LatexIndent::TrailingComments qw/$trailingCommentRegExp/; +use LatexIndent::Switches qw/$is_m_switch_active $is_t_switch_active $is_tt_switch_active/; +use LatexIndent::LogFile qw/$logger/; +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}; + + # read from fine tuning + my $keyEqualsValuesBracesBrackets = qr/${${$masterSettings{fineTuning}}{keyEqualsValuesBracesBrackets}}{name}/; + my $keyEqualsValuesBracesBracketsFollow = qr/${${$masterSettings{fineTuning}}{keyEqualsValuesBracesBrackets}}{follow}/; + + # store the regular expresssion for matching and replacing + $key_equals_values_bracesRegExp = qr/ + ( + (?:$keyEqualsValuesBracesBracketsFollow) + (?:\h|\R|$blankLineToken|$trailingCommentRegExp)* + ) # $1 pre-key bit: could be { OR , OR [ + (\\)? # $2 possible backslash + ( + $keyEqualsValuesBracesBrackets? # 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 ){ + $logger->trace("= 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){ + $logger->trace("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){ + $logger->trace("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){ + $logger->trace("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{keyEqualsValuesBracesBrackets}$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 + $logger->trace("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/support/latexindent/LatexIndent/LogFile.pm b/support/latexindent/LatexIndent/LogFile.pm new file mode 100644 index 0000000000..75ebe0b2ea --- /dev/null +++ b/support/latexindent/LatexIndent/LogFile.pm @@ -0,0 +1,251 @@ +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 FindBin; +use File::Basename; # to get the filename and directory path +use Exporter qw/import/; +use Log::Log4perl qw(get_logger :levels); +use Log::Log4perl::Appender::Screen; +use Log::Dispatch::File; +use LatexIndent::GetYamlSettings qw/%masterSettings/; +use LatexIndent::Switches qw/%switches/; +use LatexIndent::Version qw/$versionNumber $versionDate/; +our @EXPORT_OK = qw/processSwitches $logger/; +our @logFileNotes; +our $logger = get_logger("Document"); + +# log file methods, using log4perl; references +# pattern layout: http://search.cpan.org/~mschilli/Log-Log4perl-1.32/lib/Log/Log4perl/Layout/PatternLayout.pm +# multiple appenders: https://stackoverflow.com/questions/8620347/perl-log4perl-printing-and-logging-at-the-same-time-in-a-line?rq=1 +# getting started: https://www.perl.com/pub/2002/09/11/log4perl.html + +sub processSwitches{ + # -v switch is just to show the version number + if($switches{version}) { + print $versionNumber,", ",$versionDate,"\n"; + exit(2); + } + + if(scalar(@ARGV) < 1 or $switches{showhelp}) { + print <<ENDQUOTE +latexindent.pl version $versionNumber, $versionDate +usage: latexindent.pl [options] [file][.tex|.sty|.cls|.bib|...] + -v, --version + displays the version number and date of release + -h, --help + help (see the documentation for detailed instructions and examples) + -sl, --screenlog + log file will also be output to the screen + -o, --outputfile=<name-of-output-file> + 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 + -y, --yaml=<yaml settings> + specify YAML settings; sample usage: + latexindent.pl -y="defaultIndent:' '" myfile.tex + latexindent.pl -y="defaultIndent:' ',maximumIndentation:' '" myfile.tex + -d, --onlydefault + ONLY use defaultSettings.yaml, ignore ALL (yaml) user files + -g, --logfile=<name of log file> + 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 and blank lines can also be added using this feature + -r,--replacement + replacement mode, allows you to replace strings and regular expressions + verbatim blocks not respected + -rv,--replacementrespectverb + replacement mode, allows you to replace strings and regular expressions + while respecting verbatim code blocks + -rr,--onlyreplacement + *only* replacement mode, no indentation + verbatim blocks not respected +ENDQUOTE + ; + exit(2); +} + + # if we've made it this far, the processing of switches and logging begins + my $self = shift; + + ($switches{trace}||$switches{ttrace}) ? $logger->level($TRACE): $logger->level($INFO); + + # 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{cruftDirectory} ? ${$self}{cruftDirectory} : '').($switches{logFileName}||"indent.log"); + + # layout of the logfile information, for example + # + # 2017/09/16 11:59:09 INFO: message + # 2017/09/16 12:23:53 INFO: LogFile.pm:156 LatexIndent::LogFile::processSwitches - message + # reference: https://stackoverflow.com/questions/46262844/log4perl-grouping-messages/46309392#46309392 + Log::Log4perl::Layout::PatternLayout::add_global_cspec( + 'A', sub { + if($_[1] =~ /^\*/){ + $_[1]=~s/^\*//; + if($_[1]=~m/\R/s){ + my $indentation = ' '.(' ' x length $_[3]); + $_[1] =~ s/\R/\n$indentation/gs; + } + return "$_[3]: ".$_[1]; + } else { + my $indentation = ' '.(' ' x length $_[3]); + $_[1] =~ s/\R/\n$indentation/gs if($_[1]=~m/\R/s); + return $indentation.$_[1]; + } + }); + + my $layout = Log::Log4perl::Layout::PatternLayout->new("%A%n"); + + # details for the Log4perl logging + my $appender = Log::Log4perl::Appender->new( + "Log::Dispatch::File", + filename => $logfileName, + mode => "write", + utf8 => 1, + ); + + # add the layout + $appender->layout($layout); + + # adjust the logger object + $logger->add_appender($appender); + + # appender object for output to screen + my $appender_screen = q(); + + # output to screen, if appropriate + if($switches{screenlog}){ + $appender_screen = Log::Log4perl::Appender->new( + "Log::Log4perl::Appender::Screen", + stderr => 1, + utf8 => 1, + ); + + $appender_screen->layout($layout); + $logger->add_appender($appender_screen); + } + + # details of the script to log file + $logger->info("*$FindBin::Script version $versionNumber, $versionDate, a script to indent .tex files"); + $logger->info("$FindBin::Script lives here: $FindBin::RealBin/"); + + my $time = localtime(); + $logger->info($time); + + if (${$self}{fileName} ne "-"){ + $logger->info("Filename: ${$self}{fileName}"); + } else { + $logger->info("Reading input from STDIN"); + my $buttonText = ($FindBin::Script eq 'latexindent.exe') ? 'CTRL+Z followed by ENTER':'CTRL+D'; + print STDERR "Please enter text to be indented: (press $buttonText when finished)\n"; + } + + # log the switches from the user + $logger->info("*Processing switches:"); + + # 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 + $logger->info("-sl|--screenlog: log file will also be output to the screen") if($switches{screenlog}); + $logger->info("-t|--trace: Trace mode active (you have used either -t or --trace)") if($switches{trace} and !$switches{ttrace}); + $logger->info("-tt|--ttrace: TTrace mode active (you have used either -tt or --ttrace)") if($switches{ttrace}); + $logger->info("-s|--silent: Silent mode active (you have used either -s or --silent)") if($switches{silentMode}); + $logger->info("-d|--onlydefault: Only defaultSettings.yaml will be used (you have used either -d or --onlydefault)") if($switches{onlyDefault}); + $logger->info("-w|--overwrite: Overwrite mode active, will make a back up of ${$self}{fileName} first") if($switches{overwrite}); + $logger->info("-l|--localSettings: Read localSettings YAML file") if($switches{readLocalSettings}); + $logger->info("-y|--yaml: YAML settings specified via command line") if($switches{yaml}); + $logger->info("-o|--outputfile: output to file") if($switches{outputToFile}); + $logger->info("-m|--modifylinebreaks: modify line breaks") if($switches{modifyLineBreaks}); + $logger->info("-g|--logfile: logfile name") if($switches{logFileName}); + $logger->info("-c|--cruft: cruft directory") if($switches{cruftDirectory}); + $logger->info("-r|--replacement: replacement mode") if($switches{replacement}); + $logger->info("-rr|--onlyreplacement: *only* replacement mode, no indentation") if($switches{onlyreplacement}); + + # check if overwrite and outputfile are active similtaneously + if($switches{overwrite} and $switches{outputToFile}){ + $logger->info("Options check, -w and -o specified\nYou have called latexindent.pl with both -o and -w\noutput to file) will take priority, and -w (over write) will be ignored"); + $switches{overwrite}=0; + } + + $logger->info("*Directory for backup files and $logfileName: ${$self}{cruftDirectory}"); + + # 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','Unicode::GCString','Log::Log4perl'); + $logger->info("*Perl modules are being loaded from the following directories:"); + foreach my $moduleName (@listOfModules) { + (my $file = $moduleName) =~ s|::|/|g; + $logger->info($INC{$file .'.pm'}); + } + $logger->info("*Latex Indent perl modules are being loaded from, for example:"); + (my $file = 'LatexIndent::Document') =~ s|::|/|g; + $logger->info($INC{$file .'.pm'}); + } + + # read the YAML settings + $self->yaml_read_settings; + + # the user may have specified their own settings for the rest of the log file, + # for example: + # + # logFilePreferences: + # PatternLayout: + # default: "%p: %m{indent}%n" + # trace: "%p: %m{indent}%n" + # ttrace: "%d %p: %F{1}:%L %M - %m{indent}%n" + # + # e.g, default mode: + # 2017/09/16 11:59:09 INFO: message + # or trace mode: + # 2017/09/16 11:59:09 INFO: message + # or in trace mode: + # 2017/09/16 12:23:53 INFO: LogFile.pm:156 LatexIndent::LogFile::processSwitches - message + my $pattern = q(); + if($switches{ttrace}){ + $pattern = ${${$masterSettings{logFilePreferences}}{PatternLayout}}{ttrace}; + } elsif($switches{trace}){ + $pattern = ${${$masterSettings{logFilePreferences}}{PatternLayout}}{trace}; + } else { + $pattern = ${${$masterSettings{logFilePreferences}}{PatternLayout}}{default}; + } + $layout = Log::Log4perl::Layout::PatternLayout->new($pattern); + $appender->layout($layout); + + $appender_screen->layout($layout) if $switches{screenlog}; + return; +} + +1; diff --git a/support/latexindent/LatexIndent/MandatoryArgument.pm b/support/latexindent/LatexIndent/MandatoryArgument.pm new file mode 100644 index 0000000000..b5b3ac8de6 --- /dev/null +++ b/support/latexindent/LatexIndent/MandatoryArgument.pm @@ -0,0 +1,135 @@ +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 $is_m_switch_active /; +use LatexIndent::LogFile qw/$logger/; +use LatexIndent::IfElseFi qw/$ifElseFiBasicRegExp/; +use LatexIndent::Special qw/$specialBeginBasicRegExp/; +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 + $logger->trace("*Mandatory argument found, body in ${$self}{name}") if $is_t_switch_active; + $logger->trace("(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{mandatoryArguments}$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 yaml_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 if ${$self}{body} =~ m/$ifElseFiBasicRegExp/s; + + # search for special begin/end + $self->find_special if ${$self}{body} =~ m/$specialBeginBasicRegExp/s; + + # comma poly-switch check + $self->comma_else if $is_m_switch_active; +} + +1; diff --git a/support/latexindent/LatexIndent/ModifyLineBreaks.pm b/support/latexindent/LatexIndent/ModifyLineBreaks.pm new file mode 100644 index 0000000000..0f33f88b83 --- /dev/null +++ b/support/latexindent/LatexIndent/ModifyLineBreaks.pm @@ -0,0 +1,394 @@ +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 Text::Wrap; +use LatexIndent::GetYamlSettings qw/%masterSettings/; +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 LatexIndent::Item qw/$listOfItems/; +use LatexIndent::LogFile qw/$logger/; +our @EXPORT_OK = qw/modify_line_breaks_body modify_line_breaks_end adjust_line_breaks_end_parent remove_line_breaks_begin text_wrap remove_paragraph_line_breaks construct_paragraph_reg_exp text_wrap_remove_paragraph_line_breaks verbatim_modify_line_breaks/; +our $paragraphRegExp = q(); + +sub modify_line_breaks_body{ + my $self = shift; + # + # Blank line poly-switch notes (==4) + # + # when BodyStartsOnOwnLine=4 we adopt the following approach: + # temporarily change BodyStartsOnOwnLine to -1, make adjustments + # temporarily change BodyStartsOnOwnLine to 3, make adjustments + # switch BodyStartsOnOwnLine back to 4 + + # add a line break after \begin{statement} if appropriate + my @polySwitchValues = (${$self}{BodyStartsOnOwnLine}==4)?(-1,3):(${$self}{BodyStartsOnOwnLine}); + foreach(@polySwitchValues){ + my $BodyStringLogFile = ${$self}{aliases}{BodyStartsOnOwnLine}||"BodyStartsOnOwnLine"; + if($_>=1 and !${$self}{linebreaksAtEnd}{begin}){ + # if the <begin> statement doesn't finish with a line break, + # then we have different actions based upon the value of BodyStartsOnOwnLine: + # BodyStartsOnOwnLine == 1 just add a new line + # BodyStartsOnOwnLine == 2 add a comment, and then new line + # BodyStartsOnOwnLine == 3 add a blank line, and then new line + if($_==1){ + # modify the begin statement + $logger->trace("Adding a linebreak at the end of begin statement ${$self}{begin} (see $BodyStringLogFile)") if $is_t_switch_active; + ${$self}{begin} .= "\n"; + ${$self}{linebreaksAtEnd}{begin} = 1; + $logger->trace("Removing leading space from body of ${$self}{name} (see $BodyStringLogFile)") if $is_t_switch_active; + ${$self}{body} =~ s/^\h*//; + } elsif($_==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 + $logger->trace("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; + $logger->trace("Removing leading space from body of ${$self}{name} (see $BodyStringLogFile)") if $is_t_switch_active; + ${$self}{body} =~ s/^\h*//; + } else { + $logger->trace("Even though $BodyStringLogFile == 2, ${$self}{begin} already finishes with a %, so not adding another.") if $is_t_switch_active; + } + } elsif ($_==3){ + my $trailingCharacterToken = q(); + $logger->trace("Adding a blank line at the end of begin ${$self}{begin} followed by a linebreak ($BodyStringLogFile == 3)") if $is_t_switch_active; + ${$self}{begin} =~ s/\h*$//; + ${$self}{begin} .= (${$masterSettings{modifyLineBreaks}}{preserveBlankLines}?$tokens{blanklines}:"\n")."\n"; + ${$self}{linebreaksAtEnd}{begin} = 1; + $logger->trace("Removing leading space from body of ${$self}{name} (see $BodyStringLogFile)") if $is_t_switch_active; + ${$self}{body} =~ s/^\h*//; + } + } elsif ($_==-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"; + $logger->trace("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; + + # + # Blank line poly-switch notes (==4) + # + # when EndStartsOnOwnLine=4 we adopt the following approach: + # temporarily change EndStartsOnOwnLine to -1, make adjustments + # temporarily change EndStartsOnOwnLine to 3, make adjustments + # switch EndStartsOnOwnLine back to 4 + # + + my @polySwitchValues =(defined ${$self}{EndStartsOnOwnLine} and ${$self}{EndStartsOnOwnLine}==4) ?(-1,3):(${$self}{EndStartsOnOwnLine}); + foreach(@polySwitchValues){ + # possibly modify line break *before* \end{statement} + if(defined ${$self}{EndStartsOnOwnLine}){ + my $EndStringLogFile = ${$self}{aliases}{EndStartsOnOwnLine}||"EndStartsOnOwnLine"; + if($_>=1 and !${$self}{linebreaksAtEnd}{body}){ + # if the <body> statement doesn't finish with a line break, + # then we have different actions based upon the value of EndStartsOnOwnLine: + # EndStartsOnOwnLine == 1 just add a new line + # EndStartsOnOwnLine == 2 add a comment, and then new line + # EndStartsOnOwnLine == 3 add a blank line, and then new line + $logger->trace("Adding a linebreak at the end of body (see $EndStringLogFile)") if $is_t_switch_active; + + # by default, assume that no trailing character token is needed + my $trailingCharacterToken = q(); + if($_==2){ + $logger->trace("Adding a % immediately after body of ${$self}{name} ($EndStringLogFile==2)") if $is_t_switch_active; + $trailingCharacterToken = "%".$self->add_comment_symbol; + ${$self}{body} =~ s/\h*$//s; + } elsif ($_==3) { + $logger->trace("Adding a blank line immediately after body of ${$self}{name} ($EndStringLogFile==3)") if $is_t_switch_active; + $trailingCharacterToken = "\n".(${$masterSettings{modifyLineBreaks}}{preserveBlankLines}?$tokens{blanklines}:q()); + ${$self}{body} =~ s/\h*$//s; + } + + # modified end statement + if(${$self}{body} =~ m/^\h*$/s and (defined ${$self}{BodyStartsOnOwnLine}) and ${$self}{BodyStartsOnOwnLine} >=1 ){ + ${$self}{linebreaksAtEnd}{body} = 0; + } else { + ${$self}{body} .= "$trailingCharacterToken\n"; + ${$self}{linebreaksAtEnd}{body} = 1; + } + } elsif ($_==-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){ + $logger->trace("Removing linebreak at the end of body (see $EndStringLogFile)") if $is_t_switch_active; + ${$self}{body} =~ s/\R*$//sx; + ${$self}{linebreaksAtEnd}{body} = 0; + } else { + $logger->trace("Blank line token found at end of body (${$self}{name}), see preserveBlankLines, not removing line break before ${$self}{end}") if $is_t_switch_active; + } + } + } + } + + # + # Blank line poly-switch notes (==4) + # + # when EndFinishesWithLineBreak=4 we adopt the following approach: + # temporarily change EndFinishesWithLineBreak to -1, make adjustments + # temporarily change EndFinishesWithLineBreak to 3, make adjustments + # switch EndFinishesWithLineBreak back to 4 + # + @polySwitchValues =(defined ${$self}{EndFinishesWithLineBreak} and ${$self}{EndFinishesWithLineBreak}==4) ?(-1,3):(${$self}{EndFinishesWithLineBreak}); + foreach(@polySwitchValues){ + last if !(defined $_); + ${$self}{linebreaksAtEnd}{end} = 0 if($_==3 and (defined ${$self}{EndFinishesWithLineBreak} and ${$self}{EndFinishesWithLineBreak}==4)); + + # possibly modify line break *after* \end{statement} + if(defined $_ and $_>=1 + and !${$self}{linebreaksAtEnd}{end}){ + # if the <end> statement doesn't finish with a line break, + # then we have different actions based upon the value of EndFinishesWithLineBreak: + # EndFinishesWithLineBreak == 1 just add a new line + # EndFinishesWithLineBreak == 2 add a comment, and then new line + # EndFinishesWithLineBreak == 3 add a blank line, and then new line + my $EndStringLogFile = ${$self}{aliases}{EndFinishesWithLineBreak}||"EndFinishesWithLineBreak"; + if($_==1){ + $logger->trace("Adding a linebreak at the end of ${$self}{end} ($EndStringLogFile==1)") if $is_t_switch_active; + ${$self}{linebreaksAtEnd}{end} = 1; + + # modified end statement + ${$self}{replacementText} .= "\n"; + } elsif($_==2){ + if(${$self}{endImmediatelyFollowedByComment}){ + # no need to add a % if one already exists + $logger->trace("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 + $logger->trace("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; + } + } elsif($_==3){ + $logger->trace("Adding a blank line at the end of ${$self}{end} ($EndStringLogFile==3)") if $is_t_switch_active; + ${$self}{linebreaksAtEnd}{end} = 1; + + # modified end statement + ${$self}{replacementText} .= (${$masterSettings{modifyLineBreaks}}{preserveBlankLines}?$tokens{blanklines}:"\n")."\n"; + } + } + } + +} + +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 + + 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}){ + $logger->trace("ID: ${$child}{id}") if($is_t_switch_active); + $logger->trace("${$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); + $logger->trace("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 + $logger->trace("Undisclosed line break at the end of body of ${$child}{name}: '${$child}{end}'") if($is_t_switch_active); + $logger->trace("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; + } + +} + +sub verbatim_modify_line_breaks{ + # verbatim modify line breaks are a bit special, as they happen before + # any of the main processes have been done + my $self = shift; + while ( my ($key,$child)= each %{${$self}{verbatim}}){ + if(defined ${$child}{BeginStartsOnOwnLine}){ + my $BeginStringLogFile = ${$child}{aliases}{BeginStartsOnOwnLine}; + $logger->trace("*$BeginStringLogFile is ${$child}{BeginStartsOnOwnLine} for ${$child}{name}") if $is_t_switch_active ; + if (${$child}{BeginStartsOnOwnLine}==-1){ + # VerbatimStartsOnOwnLine = -1 + if(${$self}{body}=~m/^\h*${$child}{id}/m){ + $logger->trace("${$child}{name} begins on its own line, removing leading line break") if $is_t_switch_active ; + ${$self}{body} =~ s/(\R|\h)*${$child}{id}/${$child}{id}/s; + } + } elsif (${$child}{BeginStartsOnOwnLine}>=1 and ${$self}{body}!~m/^\h*${$child}{id}/m){ + # VerbatimStartsOnOwnLine = 1, 2 or 3 + my $trailingCharacterToken = q(); + if(${$child}{BeginStartsOnOwnLine}==1){ + $logger->trace("Adding a linebreak at the beginning of ${$child}{begin} (see $BeginStringLogFile)") if $is_t_switch_active; + } elsif (${$child}{BeginStartsOnOwnLine}==2){ + $logger->trace("Adding a % at the end of the line that ${$child}{begin} is on, then a linebreak ($BeginStringLogFile == 2)") if $is_t_switch_active; + $trailingCharacterToken = "%".$self->add_comment_symbol; + } elsif (${$child}{BeginStartsOnOwnLine}==3){ + $logger->trace("Adding a blank line at the end of the line that ${$child}{begin} is on, then a linebreak ($BeginStringLogFile == 3)") if $is_t_switch_active; + $trailingCharacterToken = "\n"; + } + ${$self}{body} =~ s/\h*${$child}{id}/$trailingCharacterToken\n${$child}{id}/s; + } + } + } +} +sub text_wrap_remove_paragraph_line_breaks{ + my $self = shift; + + if(${$masterSettings{modifyLineBreaks}{removeParagraphLineBreaks}}{beforeTextWrap}){ + $self->remove_paragraph_line_breaks if ${$self}{removeParagraphLineBreaks}; + $self->text_wrap if (${$self}{textWrapOptions} and ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{perCodeBlockBasis}); + } else { + $self->text_wrap if (${$self}{textWrapOptions} and ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{perCodeBlockBasis}); + } + +} + +sub text_wrap{ + + my $self = shift; + + # alignment at ampersand can take priority + return if(${$self}{lookForAlignDelims} and ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{alignAtAmpersandTakesPriority}); + + # call the text wrapping routine + my $columns; + + # columns might have been defined by the user + if(defined ${$self}{columns}){ + $columns = ${$self}{columns}; + } elsif(ref ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns} eq "HASH"){ + if(defined ${${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}}{default}){ + $columns = ${${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}}{default}; + } else { + $columns = 80; + } + } elsif (defined ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}){ + $columns = ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns} ; + } else { + $columns = 80; + } + + $Text::Wrap::columns=$columns; + if(${$masterSettings{modifyLineBreaks}{textWrapOptions}}{separator} ne ''){ + $Text::Wrap::separator=${$masterSettings{modifyLineBreaks}{textWrapOptions}}{separator}; + } + ${$self}{body} = wrap('','',${$self}{body}); +} + +sub construct_paragraph_reg_exp{ + my $self = shift; + + $logger->trace("*Constructing the paragraph-stop regexp (see paragraphsStopAt)") if $is_t_switch_active ; + my $stopAtRegExp = q(); + while( my ($paragraphStopAt,$yesNo)= each %{${$masterSettings{modifyLineBreaks}{removeParagraphLineBreaks}}{paragraphsStopAt}}){ + if($yesNo){ + # the headings (chapter, section, etc) need a slightly special treatment + $paragraphStopAt = "afterHeading" if($paragraphStopAt eq "heading"); + + # the comment need a slightly special treatment + $paragraphStopAt = "trailingComment" if($paragraphStopAt eq "comments"); + + # output to log file + $logger->trace("The paragraph-stop regexp WILL include $tokens{$paragraphStopAt} (see paragraphsStopAt)") if $is_t_switch_active ; + + # update the regexp + if($paragraphStopAt eq "items"){ + $stopAtRegExp .= "|(?:\\\\(?:".$listOfItems."))"; + } else { + $stopAtRegExp .= "|(?:".($paragraphStopAt eq "trailingComment" ? "%" : q() ).$tokens{$paragraphStopAt}."\\d+)"; + } + } else { + $logger->trace("The paragraph-stop regexp won't include $tokens{$paragraphStopAt} (see paragraphsStopAt)") if ($tokens{$paragraphStopAt} and $is_t_switch_active); + } + } + + $paragraphRegExp = qr/ + ^ + (?!$tokens{beginOfToken}) + (\w + (?: + (?! + (?:$tokens{blanklines}|\\par) + ). + )*? + ) + ( + (?: + ^(?:(\h*\R)|\\par|$tokens{blanklines}$stopAtRegExp) + ) + | + \z # end of string + )/sxm; + + $logger->trace("The paragraph-stop-regexp is:") if $is_tt_switch_active ; + $logger->trace($paragraphRegExp) if $is_tt_switch_active ; +} + +sub remove_paragraph_line_breaks{ + my $self = shift; + return unless ${$self}{removeParagraphLineBreaks}; + + # alignment at ampersand can take priority + return if(${$self}{lookForAlignDelims} and ${$masterSettings{modifyLineBreaks}{removeParagraphLineBreaks}}{alignAtAmpersandTakesPriority}); + + $logger->trace("Checking ${$self}{name} for paragraphs (see removeParagraphLineBreaks)") if $is_t_switch_active; + + my $paragraphCounter; + my @paragraphStorage; + + ${$self}{body} =~ s/$paragraphRegExp/ + $paragraphCounter++; + push(@paragraphStorage,{id=>$tokens{paragraph}.$paragraphCounter.$tokens{endOfToken},value=>$1}); + + # replace comment with dummy text + $tokens{paragraph}.$paragraphCounter.$tokens{endOfToken}.$2; + /xsmeg; + + while( my $paragraph = pop @paragraphStorage){ + # remove all line breaks from paragraph, except for any at the very end + ${$paragraph}{value} =~ s/\R(?!\z)/ /sg; + ${$self}{body} =~ s/${$paragraph}{id}/${$paragraph}{value}/; + } +} + + +1; diff --git a/support/latexindent/LatexIndent/NamedGroupingBracesBrackets.pm b/support/latexindent/LatexIndent/NamedGroupingBracesBrackets.pm new file mode 100644 index 0000000000..f20e23ca54 --- /dev/null +++ b/support/latexindent/LatexIndent/NamedGroupingBracesBrackets.pm @@ -0,0 +1,77 @@ +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::GetYamlSettings qw/%masterSettings/; +use LatexIndent::TrailingComments qw/$trailingCommentRegExp/; +use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/; +use LatexIndent::LogFile qw/$logger/; +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; + + # read from fine tuning + my $NamedGroupingBracesBracketsRegExp = qr/${${$masterSettings{fineTuning}}{NamedGroupingBracesBrackets}}{name}/; + my $NamedGroupingFollowRegExp = qr/${${$masterSettings{fineTuning}}{NamedGroupingBracesBrackets}}{follow}/; + + # store the regular expresssion for matching and replacing + $grouping_braces_regexp = qr/ + ( + $NamedGroupingFollowRegExp + ) + ( + $NamedGroupingBracesBracketsRegExp # 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{namedGroupingBracesBrackets}$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 + $logger->trace("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/support/latexindent/LatexIndent/OptionalArgument.pm b/support/latexindent/LatexIndent/OptionalArgument.pm new file mode 100644 index 0000000000..fdaa549a7a --- /dev/null +++ b/support/latexindent/LatexIndent/OptionalArgument.pm @@ -0,0 +1,120 @@ +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 $is_m_switch_active /; +use LatexIndent::LogFile qw/$logger/; +use LatexIndent::IfElseFi qw/$ifElseFiBasicRegExp/; +use LatexIndent::Special qw/$specialBeginBasicRegExp/; +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 + $logger->trace("*Optional argument found, body in ${$self}{name}") if $is_t_switch_active; + $logger->trace("(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 yaml_get_object_attribute_for_indentation_settings{ + my $self = shift; + + return ${$self}{modifyLineBreaksYamlName}; +} + +sub create_unique_id{ + my $self = shift; + + $optionalArgumentCounter++; + ${$self}{id} = "$tokens{optionalArguments}$optionalArgumentCounter"; + return; +} + +sub tasks_particular_to_each_object{ + my $self = shift; + + # search for ifElseFi blocks + $self->find_ifelsefi if ${$self}{body} =~ m/$ifElseFiBasicRegExp/s; + + # search for special begin/end + $self->find_special if ${$self}{body} =~ m/$specialBeginBasicRegExp/s; + + # comma poly-switch check + $self->comma_else if $is_m_switch_active; +} + +1; diff --git a/support/latexindent/LatexIndent/Preamble.pm b/support/latexindent/LatexIndent/Preamble.pm new file mode 100644 index 0000000000..a31efd1a49 --- /dev/null +++ b/support/latexindent/LatexIndent/Preamble.pm @@ -0,0 +1,90 @@ +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::Switches qw/$is_t_switch_active $is_m_switch_active/; +use LatexIndent::GetYamlSettings qw/%masterSettings/; +use LatexIndent::LogFile qw/$logger/; +use LatexIndent::Environment qw/$environmentBasicRegExp/; +use LatexIndent::IfElseFi qw/$ifElseFiBasicRegExp/; +use LatexIndent::Special qw/$specialBeginBasicRegExp/; +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 + $logger->trace("Custom replacement text routine for preamble ${$self}{name}") if $is_t_switch_active; + ${$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 if ${$self}{body} =~ m/$environmentBasicRegExp/; + + # search for ifElseFi blocks + $self->find_ifelsefi if ${$self}{body} =~ m/$ifElseFiBasicRegExp/s; + + if(${$masterSettings{specialBeginEnd}}{specialBeforeCommand}){ + # search for special begin/end + $self->find_special if ${$self}{body} =~ m/$specialBeginBasicRegExp/s; + + # search for commands with arguments + $self->find_commands_or_key_equals_values_braces if(!$masterSettings{preambleCommandsBeforeEnvironments}); + } else { + # search for commands with arguments + $self->find_commands_or_key_equals_values_braces if(!$masterSettings{preambleCommandsBeforeEnvironments}); + + # search for special begin/end + $self->find_special if ${$self}{body} =~ m/$specialBeginBasicRegExp/s; + } + + # text wrapping, remove paragraph line breaks + if ($is_m_switch_active){ + $self->yaml_get_textwrap_removeparagraphline_breaks; + + # call the remove_paragraph_line_breaks and text_wrap routines + if(${$masterSettings{modifyLineBreaks}{removeParagraphLineBreaks}}{beforeTextWrap}){ + $self->remove_paragraph_line_breaks if ${$self}{removeParagraphLineBreaks}; + $self->text_wrap if (${$self}{textWrapOptions} and ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{perCodeBlockBasis}); + } else { + $self->text_wrap if (${$self}{textWrapOptions} and ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{perCodeBlockBasis}); + $self->remove_paragraph_line_breaks if ${$self}{removeParagraphLineBreaks}; + } + } + +} + +1; diff --git a/support/latexindent/LatexIndent/Replacement.pm b/support/latexindent/LatexIndent/Replacement.pm new file mode 100644 index 0000000000..1bc51caed2 --- /dev/null +++ b/support/latexindent/LatexIndent/Replacement.pm @@ -0,0 +1,89 @@ +package LatexIndent::Replacement; +# 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 $is_rr_switch_active/; +use LatexIndent::LogFile qw/$logger/; +use Data::Dumper; +use Exporter qw/import/; +our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321 +our @EXPORT_OK = qw/make_replacements/; + +sub make_replacements{ + my $self = shift; + my %input = @_; + if ($is_t_switch_active and !$is_rr_switch_active){ + $logger->trace("*Replacement mode *$input{when}* indentation: -r") ; + } elsif ($is_t_switch_active and $is_rr_switch_active) { + $logger->trace("*Replacement mode, -rr switch is active") if $is_t_switch_active ; + } + + my @replacements = @{$masterSettings{replacements}}; + + foreach ( @replacements ){ + next if !(${$_}{this} or ${$_}{substitution}); + + # default value of "lookForThis" is 1 + ${$_}{lookForThis} = 1 if( !(defined ${$_}{lookForThis})); + + # move on if this one shouldn't be looked for + next if(!${$_}{lookForThis}); + + # default value of "when" is before + ${$_}{when} = "before" if( !(defined ${$_}{when}) or $is_rr_switch_active); + + # update to the logging file + if($is_t_switch_active and (${$_}{when} eq $input{when})){ + $logger->trace("-"); + $logger->trace("this: ${$_}{this}") if(${$_}{this}); + $logger->trace("that: ${$_}{that}") if(${$_}{that}); + $logger->trace("substitution: ${$_}{substitution}") if(${$_}{substitution}); + $logger->trace("when: ${$_}{when}"); + } + + # perform the substitutions + if(${$_}{when} eq $input{when}){ + $logger->warn("*You have specified both 'this' and 'substitution'; the 'substitution' field will be ignored") if(${$_}{this} and ${$_}{substitution}); + if(${$_}{this}){ + # *string* replacement + # *string* replacement + # *string* replacement + my $this = qq{${$_}{this}}; + my $that = (defined ${$_}{that}) ? qq{${$_}{that}} : q(); + my $index_match = index(${$self}{body}, $this); + while ( $index_match != -1 ) { + substr (${$self}{body}, $index_match, length($this), $that ); + $index_match = index(${$self}{body}, $this); + } + } else { + # *regex* replacement + # *regex* replacement + # *regex* replacement + + # https://stackoverflow.com/questions/12423337/how-to-pass-a-replacing-regex-as-a-command-line-argument-to-a-perl-script + my $body = ${$self}{body}; + eval("\$body =~ ${$_}{substitution}"); + ${$self}{body} = $body ; + } + } + } +} + +1; diff --git a/support/latexindent/LatexIndent/RoundBrackets.pm b/support/latexindent/LatexIndent/RoundBrackets.pm new file mode 100644 index 0000000000..fff5efedd0 --- /dev/null +++ b/support/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 yaml_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/support/latexindent/LatexIndent/Sentence.pm b/support/latexindent/LatexIndent/Sentence.pm new file mode 100644 index 0000000000..cc11997e36 --- /dev/null +++ b/support/latexindent/LatexIndent/Sentence.pm @@ -0,0 +1,362 @@ +package LatexIndent::Sentence; +# 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 $is_m_switch_active/; +use LatexIndent::LogFile qw/$logger/; +use LatexIndent::Environment qw/$environmentBasicRegExp/; +use LatexIndent::IfElseFi qw/$ifElseFiBasicRegExp/; +use LatexIndent::Heading qw/$allHeadingsRegexp/; +use LatexIndent::Special qw/$specialBeginAndBracesBracketsBasicRegExp/; +use Data::Dumper; +use Exporter qw/import/; +our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321 +our @EXPORT_OK = qw/one_sentence_per_line/; +our $sentenceCounter; + +sub one_sentence_per_line{ + my $self = shift; + + $logger->trace("*One sentence per line regular expression construction: (see oneSentencePerLine: manipulateSentences)") if $is_t_switch_active; + + # sentences FOLLOW + # sentences FOLLOW + # sentences FOLLOW + my $sentencesFollow = q(); + + while( my ($sentencesFollowEachPart,$yesNo)= each %{${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{sentencesFollow}}){ + if($yesNo){ + if($sentencesFollowEachPart eq "par"){ + $sentencesFollowEachPart = qr/\R?\\par/s; + } elsif ($sentencesFollowEachPart eq "blankLine"){ + $sentencesFollowEachPart = qr/ + (?:\A(?:$tokens{blanklines}\R)+) # the order of each of these + | # is important, as (like always) the first + (?:\G(?:$tokens{blanklines}\R)+) # thing to be matched will + | # be accepted + (?:(?:$tokens{blanklines}\h*\R)+) + | + \R{2,} + | + \G + /sx; + } elsif ($sentencesFollowEachPart eq "fullStop"){ + $sentencesFollowEachPart = qr/\./s; + } elsif ($sentencesFollowEachPart eq "exclamationMark"){ + $sentencesFollowEachPart = qr/\!/s; + } elsif ($sentencesFollowEachPart eq "questionMark"){ + $sentencesFollowEachPart = qr/\?/s; + } elsif ($sentencesFollowEachPart eq "rightBrace"){ + $sentencesFollowEachPart = qr/\}/s; + } elsif ($sentencesFollowEachPart eq "commentOnPreviousLine"){ + $sentencesFollowEachPart = qr/$trailingCommentRegExp\h*\R/s; + } elsif ($sentencesFollowEachPart eq "other"){ + $sentencesFollowEachPart = qr/$yesNo/; + } + $sentencesFollow .= ($sentencesFollow eq '' ? q() : "|").qr/$sentencesFollowEachPart/sx; + } + } + # if blankLine is not active from sentencesFollow then we need to set up the + # beginning of the string, but make sure that it is *not* followed by a + # blank line token, or a blank line + if(!${${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{sentencesFollow}}{blankLine}){ + $sentencesFollow .= ($sentencesFollow eq '' ? q() : "|"). + qr/ + \G + (?!$tokens{blanklines}) + /sx; + } + + if(${${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{sentencesFollow}}{blankLine}){ + $sentencesFollow = ($sentencesFollow eq '' ? q() : qr/(?:$sentencesFollow)(?:\h|\R)*/sx ); + } else { + $sentencesFollow = ($sentencesFollow eq '' ? q() : qr/(?:$sentencesFollow)(?:\h*\R?)/sx ); + } + + + $logger->trace("Sentences follow regexp:") if $is_tt_switch_active; + $logger->trace($sentencesFollow) if $is_tt_switch_active; + + # sentences BEGIN with + # sentences BEGIN with + # sentences BEGIN with + my $sentencesBeginWith = q(); + + while( my ($sentencesBeginWithEachPart,$yesNo)= each %{${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{sentencesBeginWith}}){ + if($yesNo){ + if($sentencesBeginWithEachPart eq "A-Z"){ + $logger->trace("sentence BEGINS with capital letters (see oneSentencePerLine:sentencesBeginWith:A-Z)") if $is_t_switch_active; + $sentencesBeginWithEachPart = qr/(?!(?:$tokens{blanklines}|$tokens{verbatim}|$tokens{preamble}))[A-Z]/; + } elsif ($sentencesBeginWithEachPart eq "a-z"){ + $logger->trace("sentence BEGINS with lower-case letters (see oneSentencePerLine:sentencesBeginWith:a-z)") if $is_t_switch_active; + $sentencesBeginWithEachPart = qr/[a-z]/; + } elsif ($sentencesBeginWithEachPart eq "other"){ + $logger->trace("sentence BEGINS with other $yesNo (reg exp) (see oneSentencePerLine:sentencesBeginWith:other)") if $is_t_switch_active; + $sentencesBeginWithEachPart = qr/$yesNo/; + } + $sentencesBeginWith .= ($sentencesBeginWith eq "" ? q(): "|" ).$sentencesBeginWithEachPart; + } + } + $sentencesBeginWith = qr/$sentencesBeginWith/; + + # sentences END with + # sentences END with + # sentences END with + ${${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{sentencesEndWith}}{basicFullStop} = 0 if ${${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{sentencesEndWith}}{betterFullStop}; + my $sentencesEndWith = q(); + + while( my ($sentencesEndWithEachPart,$yesNo)= each %{${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{sentencesEndWith}}){ + if($yesNo){ + if($sentencesEndWithEachPart eq "basicFullStop"){ + $logger->trace("sentence ENDS with full stop (see oneSentencePerLine:sentencesEndWith:basicFullStop") if $is_t_switch_active; + $sentencesEndWithEachPart = qr/\./; + } elsif($sentencesEndWithEachPart eq "betterFullStop"){ + $logger->trace("sentence ENDS with *better* full stop (see oneSentencePerLine:sentencesEndWith:betterFullStop") if $is_t_switch_active; + $sentencesEndWithEachPart = qr/${${$masterSettings{fineTuning}}{modifyLineBreaks}}{betterFullStop}/; + } elsif ($sentencesEndWithEachPart eq "exclamationMark"){ + $logger->trace("sentence ENDS with exclamation mark (see oneSentencePerLine:sentencesEndWith:exclamationMark)") if $is_t_switch_active; + $sentencesEndWithEachPart = qr/!/; + } elsif ($sentencesEndWithEachPart eq "questionMark"){ + $logger->trace("sentence ENDS with question mark (see oneSentencePerLine:sentencesEndWith:questionMark)") if $is_t_switch_active; + $sentencesEndWithEachPart = qr/\?/; + } elsif ($sentencesEndWithEachPart eq "other"){ + $logger->trace("sentence ENDS with other $yesNo (reg exp) (see oneSentencePerLine:sentencesEndWith:other)") if $is_t_switch_active; + $sentencesEndWithEachPart = qr/$yesNo/; + } + $sentencesEndWith .= ($sentencesEndWith eq "" ? q(): "|" ).$sentencesEndWithEachPart; + } + } + $sentencesEndWith = qr/$sentencesEndWith/; + + # the OVERALL sentence regexp + # the OVERALL sentence regexp + # the OVERALL sentence regexp + $logger->trace("Overall sentences end with regexp:") if $is_tt_switch_active; + $logger->trace($sentencesEndWith) if $is_tt_switch_active; + + $logger->trace("Finding sentences...") if $is_t_switch_active; + + my $notWithinSentence = qr/$trailingCommentRegExp/s; + + # if + # + # modifyLineBreaks + # oneSentencePerLine + # sentencesFollow + # blankLine + # + # is set to 0 then we need to *exclude* the $tokens{blanklines} from the sentence routine, + # otherwise we could begin a sentence with $tokens{blanklines}. + if(!${${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{sentencesFollow}}{blankLine}){ + $notWithinSentence .= "|".qr/(?:\h*\R?$tokens{blanklines})/s; + } + + # similarly for \par + if(${${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{sentencesFollow}}{par}){ + $notWithinSentence .= "|".qr/(?:\R?\\par)/s; + } + + # initiate the sentence counter + my @sentenceStorage; + + # make the sentence manipulation + ${$self}{body} =~ s/((?:$sentencesFollow)) + (\h*) + (?!$notWithinSentence) + ((?:$sentencesBeginWith).*?) + ($sentencesEndWith) + (\h*)? # possibly followed by horizontal space + (\R)? # possibly followed by a line break + ($trailingCommentRegExp)? # possibly followed by trailing comments + / + my $beginning = $1; + my $h_space = ($2?$2:q()); + my $middle = $3; + my $end = $4; + my $trailing = ($5?$5:q()).($6?$6:q()).($7?$7:q()); + my $lineBreaksAtEnd = ($6? 1 : ($7?1:0) ); + my $trailingComments = q(); + # remove trailing comments from within the body of the sentence + if (${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{removeSentenceLineBreaks}){ + while($middle =~ m|$trailingCommentRegExp|){ + $middle =~ s|\h*($trailingCommentRegExp)||s; + $trailingComments .= $1; + } + } + # remove line breaks from within a sentence + $middle =~ s| + (?!\A) # not at the *beginning* of a match + (\h*)\R # possible horizontal space, then line break + |$1?$1:" ";|esgx if ${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{removeSentenceLineBreaks}; + $middle =~ s|$tokens{blanklines}\h*\R?|$tokens{blanklines}\n|sg; + $logger->trace("follows: $beginning") if $is_tt_switch_active; + $logger->trace("sentence: $middle") if $is_tt_switch_active; + $logger->trace("ends with: $end") if $is_tt_switch_active; + # if indentation is specified for sentences, then we treat + # them as objects; otherwise we don't + my $replacementText = q(); + if(${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{sentenceIndent} =~ m|\h+|){ + my $sentenceObj = LatexIndent::Sentence->new( + name=>"sentence", + begin=>q(), + body=>$middle.$end, + end=>q(), + indentation=>${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{sentenceIndent}, + modifyLineBreaksYamlName=>"sentence", + BeginStartsOnOwnLine=>1, + ); + # log file output + $logger->trace("*sentence found: $middle.$end") if $is_t_switch_active; + + # the settings and storage of most objects has a lot in common + $self->get_settings_and_store_new_object($sentenceObj); + ${@{${$self}{children}}[-1]}{replacementText} = $beginning.$h_space.$tokens{sentence}.$sentenceCounter.$tokens{endOfToken}.$trailingComments.$trailing.($lineBreaksAtEnd ? q() : "\n"); + $replacementText = ${@{${$self}{children}}[-1]}{replacementText}; + } else { + $sentenceCounter++; + push(@sentenceStorage,{id=>$tokens{sentence}.$sentenceCounter.$tokens{endOfToken},value=>$middle.$end}); + $replacementText = $beginning.$h_space.$tokens{sentence}.$sentenceCounter.$tokens{endOfToken}.$trailingComments.$trailing.($lineBreaksAtEnd ? q() : "\n"); + }; + $replacementText; + /xsge; + + if(${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{sentenceIndent} !~ m/\h+/){ + # loop back through the sentenceStorage and replace with the sentence, adjusting line breaks + # before and after appropriately + while( my $sentence = pop @sentenceStorage){ + my $sentenceStorageID = ${$sentence}{id}; + my $sentenceStorageValue = ${$sentence}{value}; + + # option to text wrap (and option to indent) sentences + if(${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{textWrapSentences}){ + my $sentenceObj = LatexIndent::Document->new(body=>$sentenceStorageValue, + name=>"sentence", + modifyLineBreaksYamlName=>"sentence", + ); + + # text wrapping + $sentenceObj->yaml_get_columns; + $sentenceObj->text_wrap; + + # indentation of sentences + if(${$sentenceObj}{body} =~ m/ + (.*?) # content of first line + \R # first line break + (.*$) # rest of body + /sx){ + my $bodyFirstLine = $1; + my $remainingBody = $2; + my $indentation = ${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{sentenceIndent}; + $logger->trace("first line of sencent: $bodyFirstLine") if $is_tt_switch_active; + $logger->trace("remaining body (before indentation):\n'$remainingBody'") if($is_tt_switch_active); + + # add the indentation to all the body except first line + $remainingBody =~ s/^/$indentation/mg unless($remainingBody eq ''); # add indentation + $logger->trace("remaining body (after indentation):\n$remainingBody'") if($is_tt_switch_active); + + # put the body back together + ${$sentenceObj}{body} = $bodyFirstLine."\n".$remainingBody; + } + $sentenceStorageValue = ${$sentenceObj}{body}; + }; + # sentence at the very END + ${$self}{body} =~ s/\h*$sentenceStorageID\h*$/$sentenceStorageValue/s; + # sentence at the very BEGINNING + ${$self}{body} =~ s/^$sentenceStorageID\h*\R?/$sentenceStorageValue\n/s; + # all other sentences + ${$self}{body} =~ s/\R?\h*$sentenceStorageID\h*\R?/\n$sentenceStorageValue\n/s; + } + } +} + +sub create_unique_id{ + my $self = shift; + + $sentenceCounter++; + ${$self}{id} = "$tokens{sentence}$sentenceCounter"; + return; +} + +sub tasks_particular_to_each_object{ + my $self = shift; + + # option to text wrap (and option to indent) sentences + if(${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{textWrapSentences}){ + $self->yaml_get_columns; + $self->text_wrap; + } + + # search for environments + $logger->trace('looking for ENVIRONMENTS') if $is_t_switch_active; + $self->find_environments if ${$self}{body} =~ m/$environmentBasicRegExp/s; + + # search for ifElseFi blocks + $logger->trace('looking for IFELSEFI') if $is_t_switch_active; + $self->find_ifelsefi if ${$self}{body} =~ m/$ifElseFiBasicRegExp/s; + + # search for headings (part, chapter, section, setc) + $logger->trace('looking for HEADINGS (chapter, section, part, etc)') if $is_t_switch_active; + $self->find_heading if ${$self}{body} =~ m/$allHeadingsRegexp/s; + + # the ordering of finding commands and special code blocks can change + $self->find_commands_or_key_equals_values_braces_and_special if ${$self}{body} =~ m/$specialBeginAndBracesBracketsBasicRegExp/s; +} + +sub indent_body{ + return unless ${$masterSettings{modifyLineBreaks}{oneSentencePerLine}}{sentenceIndent} =~ m/\h+/; + + my $self = shift; + + # indentation of sentences + if(${$self}{body} =~ m/ + (.*?) # content of first line + \R # first line break + (.*$) # rest of body + /sx){ + my $bodyFirstLine = $1; + my $remainingBody = $2; + my $indentation = ${$self}{indentation}; + $logger->trace("first line of sentence $bodyFirstLine") if $is_tt_switch_active; + $logger->trace("remaining body (before indentation):\n'$remainingBody'") if($is_tt_switch_active); + + # add the indentation to all the body except first line + $remainingBody =~ s/^/$indentation/mg unless($remainingBody eq ''); # add indentation + $logger->trace("remaining body (after indentation):\n$remainingBody'") if($is_tt_switch_active); + + # put the body back together + ${$self}{body} = $bodyFirstLine."\n".$remainingBody; + } +} + +sub yaml_get_indentation_settings_for_this_object{ + return; +} + +sub add_surrounding_indentation_to_begin_statement{ + # specific method for sentences + my $self = shift; + + my $surroundingIndentation = ${$self}{surroundingIndentation}; + ${$self}{body} =~ s/^(\h*)?/$surroundingIndentation/s; # add indentation + +} + + +1; diff --git a/support/latexindent/LatexIndent/Special.pm b/support/latexindent/LatexIndent/Special.pm new file mode 100644 index 0000000000..8898697d0f --- /dev/null +++ b/support/latexindent/LatexIndent/Special.pm @@ -0,0 +1,272 @@ +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 LatexIndent::LogFile qw/$logger/; +use LatexIndent::IfElseFi qw/$ifElseFiBasicRegExp/; +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 $specialBeginAndBracesBracketsBasicRegExp $specialBeginBasicRegExp/; +our $specialCounter; +our $specialBegins = q(); +our $specialAllMatchesRegExp = q(); +our %individualSpecialRegExps; +our $specialBeginAndBracesBracketsBasicRegExp; +our $specialBeginBasicRegExp; + +sub construct_special_begin{ + my $self = shift; + + # put together a list of the begin terms in special + while( my ($specialName,$BeginEnd)= each %{$masterSettings{specialBeginEnd}}){ + if(ref($BeginEnd) eq "HASH"){ + # only append the regexps if lookForThis is 1 + $specialBegins .= ($specialBegins eq ""?q():"|").${$BeginEnd}{begin} if(${$BeginEnd}{lookForThis}=~m/\d/s and ${$BeginEnd}{lookForThis} == 1); + } + } + + # 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( (ref($BeginEnd) eq "HASH") and ${$BeginEnd}{lookForThis}=~m/\d/s and ${$BeginEnd}{lookForThis} == 1){ + + # 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 { + $logger->trace("*The special regexps won't include anything from $specialName (see lookForThis)") if $is_t_switch_active ; + } + } + + # move $$ to the beginning + if($specialBegins =~ m/\|\\\$\\\$/){ + $specialBegins =~ s/\|(\\\$\\\$)//; + $specialBegins = $1."|".$specialBegins; + } + + # info to the log file + $logger->trace("*The special beginnings regexp is: (see specialBeginEnd)") if $is_tt_switch_active; + $logger->trace($specialBegins) if $is_tt_switch_active; + + # overall special regexp + $logger->trace("*The overall special regexp is: (see specialBeginEnd)") if $is_tt_switch_active; + $logger->trace($specialAllMatchesRegExp) if $is_tt_switch_active; + + # basic special begin regexp + $specialBeginBasicRegExp = qr/$specialBegins/; + $specialBeginAndBracesBracketsBasicRegExp = $specialBegins."|\\{|\\["; + $specialBeginAndBracesBracketsBasicRegExp = qr/$specialBeginAndBracesBracketsBasicRegExp/; + } + +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 + $logger->trace("*Searching ${$self}{name} for special begin/end (see specialBeginEnd)") if $is_t_switch_active ; + $logger->trace(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((ref($BeginEnd) eq "HASH") and ${$BeginEnd}{lookForThis}=~m/\d/s and ${$BeginEnd}{lookForThis} == 1){ + $logger->trace("Looking for $specialName") if $is_t_switch_active ; + } else { + $logger->trace("Not looking for $specialName (see lookForThis)") if ($is_t_switch_active and (ref($BeginEnd) eq "HASH")); + next; + } + + # the regexp + my $specialRegExp = $individualSpecialRegExps{$specialName}; + $logger->trace("$specialName regexp: \n$specialRegExp") if $is_tt_switch_active ; + + while(${$self}{body} =~ m/$specialRegExp(\h*)($trailingCommentRegExp)?/){ + + # global substitution + ${$self}{body} =~ s/ + $specialRegExp(\h*)($trailingCommentRegExp)? + / + # 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(), + ); + + # log file output + $logger->trace("*Special found: $specialName") if $is_t_switch_active; + + # 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 tasks_particular_to_each_object{ + my $self = shift; + + if( defined ${${$masterSettings{specialBeginEnd}}{${$self}{name}}}{middle}){ + $logger->trace("middle specified for ${$self}{name} (see specialBeginEnd -> ${$self}{name} -> middle)") if $is_t_switch_active ; + + # initiate the middle regexp + my $specialMiddle = q(); + + # we can specify middle as either an array or a hash + if(ref(${${$masterSettings{specialBeginEnd}}{${$self}{name}}}{middle}) eq "ARRAY"){ + $logger->trace("looping through middle array for ${$self}{name}") if $is_t_switch_active ; + foreach(@{${${$masterSettings{specialBeginEnd}}{${$self}{name}}}{middle}}){ + $specialMiddle .= ($specialMiddle eq ""?q():"|").$_; + } + $specialMiddle = qr/$specialMiddle/; + } else { + $specialMiddle = qr/${${$masterSettings{specialBeginEnd}}{${$self}{name}}}{middle}/; + } + + $logger->trace("overall middle regexp for ${$self}{name}: $specialMiddle") if $is_t_switch_active ; + + # store the middle regexp for later + ${$self}{middleRegExp} = $specialMiddle; + + # check for existence of a 'middle' statement, and associated line break information + $self->check_for_else_statement( + # else name regexp + elseNameRegExp=>$specialMiddle, + # else statements name + ElseStartsOnOwnLine=>"SpecialMiddleStartsOnOwnLine", + # end statements + ElseFinishesWithLineBreak=>"SpecialMiddleFinishesWithLineBreak", + # for the YAML settings storage + storageNameAppend=>"middle", + # logfile information + logName=>"special middle", + ); + + + } + + return unless(${$masterSettings{specialBeginEnd}}{specialBeforeCommand}); + + # search for commands with arguments + $self->find_commands_or_key_equals_values_braces; + + # search for arguments + $self->find_opt_mand_arguments; + + # search for ifElseFi blocks + $self->find_ifelsefi; + +} + +sub post_indentation_check{ + # needed to remove leading horizontal space before \else + my $self = shift; + + return unless ( defined ${${$masterSettings{specialBeginEnd}}{${$self}{name}}}{middle}); + + $logger->trace("post indentation check for ${$self}{name} to account for middle") if $is_t_switch_active ; + + # loop through \else and \or + foreach ({regExp=>${$self}{middleRegExp}}){ + my %else = %{$_}; + if(${$self}{body} =~ m/^\h*$else{regExp}/sm + and + !(${$self}{body} =~ m/^\h*$else{regExp}/s and ${$self}{linebreaksAtEnd}{begin}==0) + ){ + $logger->trace("*Adding surrounding indentation to $else{regExp} statement(s) ('${$self}{surroundingIndentation}')") if $is_t_switch_active; + ${$self}{body} =~ s/^\h*($else{regExp})/${$self}{surroundingIndentation}$1/smg; + } + } + return; +} + + +sub create_unique_id{ + my $self = shift; + + $specialCounter++; + + ${$self}{id} = "$tokens{specialBeginEnd}$specialCounter"; + return; +} + +1; diff --git a/support/latexindent/LatexIndent/Switches.pm b/support/latexindent/LatexIndent/Switches.pm new file mode 100644 index 0000000000..d738a4b36b --- /dev/null +++ b/support/latexindent/LatexIndent/Switches.pm @@ -0,0 +1,44 @@ +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 $is_r_switch_active $is_rr_switch_active $is_rv_switch_active/; +our %switches; +our $is_m_switch_active; +our $is_t_switch_active; +our $is_tt_switch_active; +our $is_r_switch_active; +our $is_rr_switch_active; +our $is_rv_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; + $is_r_switch_active = defined $switches{replacement}?$switches{replacement}: 0; + $is_rr_switch_active = defined $switches{onlyreplacement}?$switches{onlyreplacement}: 0; + $is_rv_switch_active = defined $switches{replacementRespectVerb}?$switches{replacementRespectVerb}: 0; + $is_r_switch_active = ($is_rr_switch_active|$is_rv_switch_active) ? ($is_rr_switch_active|$is_rv_switch_active) : $is_r_switch_active; + delete ${$self}{switches}; + } +1; diff --git a/support/latexindent/LatexIndent/Tokens.pm b/support/latexindent/LatexIndent/Tokens.pm new file mode 100644 index 0000000000..2e358e0b62 --- /dev/null +++ b/support/latexindent/LatexIndent/Tokens.pm @@ -0,0 +1,74 @@ +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/; +use LatexIndent::LogFile qw/$logger/; +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 = ( + # user-facing naming convention + environments=>$beginningToken."ENVIRONMENT", + commands=>$beginningToken."COMMAND", + optionalArguments=>$beginningToken."OPTIONAL-ARGUMENT", + mandatoryArguments=>$beginningToken."MANDATORY-ARGUMENT", + ifElseFi=>$ifelsefiSpecial.$beginningToken."IFELSEFI", + else=>$beginningToken."ELSE", + items=>$beginningToken."ITEMS", + keyEqualsValuesBracesBrackets=>$beginningToken."KEY-VALUE-BRACES", + namedGroupingBracesBrackets=>$beginningToken."GROUPING-BRACES", + UnNamedGroupingBracesBrackets=>$beginningToken."UN-NAMED-GROUPING-BRACES", + specialBeginEnd=>$beginningToken."SPECIAL", + afterHeading=>$beginningToken."HEADING", + filecontents=>$beginningToken."FILECONTENTS", + # internal-facing naming convention + trailingComment=>"latexindenttrailingcomment", + ifelsefiSpecial=>$ifelsefiSpecial, + blanklines=>$beginningToken."blank-line", + arguments=>$beginningToken."ARGUMENTS", + roundBracket=>$beginningToken."ROUND-BRACKET", + verbatim=>$beginningToken."VERBATIM", + preamble=>$beginningToken."preamble", + beginOfToken=>$beginningToken, + doubleBackSlash=>$beginningToken."DOUBLEBACKSLASH", + alignmentBlock=>$beginningToken."ALIGNMENTBLOCK", + paragraph=>$beginningToken."PARA", + sentence=>$beginningToken."SENTENCE", + endOfToken=>"-END", + ); + +sub token_check{ + my $self = shift; + + $logger->trace("*Token check") 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){ + $logger->trace("Found $tokens{$_} within body, updating replacement token to $tokens{$_}-LIN") if($is_t_switch_active); + $tokens{$_} .= "-LIN"; + } + } +} + + +1; diff --git a/support/latexindent/LatexIndent/TrailingComments.pm b/support/latexindent/LatexIndent/TrailingComments.pm new file mode 100644 index 0000000000..6ec052a0f3 --- /dev/null +++ b/support/latexindent/LatexIndent/TrailingComments.pm @@ -0,0 +1,132 @@ +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 $is_m_switch_active/; +use LatexIndent::GetYamlSettings qw/%masterSettings/; +use LatexIndent::LogFile qw/$logger/; +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 + $logger->trace("*Updating trailing comment array")if $is_t_switch_active; + $logger->trace(Dumper(\@trailingComments),'ttrace') if($is_tt_switch_active); + + # the returned value + return $tokens{trailingComment}.$commentCounter.$tokens{endOfToken}; +} + +sub remove_trailing_comments{ + my $self = shift; + $logger->trace("*Storing trailing comments")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){ + $logger->trace("Trailing comments stored in:") if($is_tt_switch_active); + $logger->trace(Dumper(\@trailingComments)) if($is_tt_switch_active); + } else { + $logger->trace("No trailing comments found") if($is_t_switch_active); + } + return; +} + +sub put_trailing_comments_back_in{ + my $self = shift; + return unless( @trailingComments > 0 ); + + $logger->trace("*Returning trailing comments to body")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}; + + # the -m switch can modify max characters per line, and trailing comment IDs can + # be split across lines + if($is_m_switch_active and ${$self}{body} !~ m/%$trailingcommentID/m){ + $logger->trace("$trailingcommentID not found in body using /m matching, assuming it has been split across line (see modifyLineBreaks: textWrapOptions)") if($is_t_switch_active); + my $trailingcommentIDwithLineBreaks; + + # construct a reg exp that contains possible line breaks in between each character + if(${$masterSettings{modifyLineBreaks}{textWrapOptions}}{separator} ne ''){ + $trailingcommentIDwithLineBreaks = join("\\".${$masterSettings{modifyLineBreaks}{textWrapOptions}}{separator}."?",split(//,$trailingcommentID)); + } else { + $trailingcommentIDwithLineBreaks = join("(?:\\h|\\R)*",split(//,$trailingcommentID)); + } + my $trailingcommentIDwithLineBreaksRegExp = qr/$trailingcommentIDwithLineBreaks/s; + + # replace the line-broken trailing comment ID with a non-broken trailing comment ID + ${$self}{body} =~ s/%\R?$trailingcommentIDwithLineBreaksRegExp/%$trailingcommentID/s; + } + if(${$self}{body} =~ m/%$trailingcommentID + ( + (?! # not immediately preceeded by + (?<!\\) # \ + % # % + ).*? + ) # captured into $1 + (\h*)?$ + /mx and $1 ne ''){ + $logger->trace("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/; + } + $logger->trace("replace %$trailingcommentID with %$trailingcommentValue") if($is_tt_switch_active); + } + return; +} + +1; diff --git a/support/latexindent/LatexIndent/UnNamedGroupingBracesBrackets.pm b/support/latexindent/LatexIndent/UnNamedGroupingBracesBrackets.pm new file mode 100644 index 0000000000..eea6ee6747 --- /dev/null +++ b/support/latexindent/LatexIndent/UnNamedGroupingBracesBrackets.pm @@ -0,0 +1,138 @@ +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::GetYamlSettings qw/%masterSettings/; +use LatexIndent::TrailingComments qw/$trailingCommentRegExp/; +use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/; +use LatexIndent::LogFile qw/$logger/; +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}; + + # arguments Before, by default, includes beamer special and numbered arguments, for example #1 #2, etc + my $argumentsBefore = qr/${${$masterSettings{fineTuning}}{arguments}}{before}/; + my $UnNamedGroupingFollowRegExp = qr/${${$masterSettings{fineTuning}}{UnNamedGroupingBracesBrackets}}{follow}/; + + # store the regular expresssion for matching and replacing + $un_named_grouping_braces_RegExp = qr/ + # NOT + (?! + (?: + (?:(?<!\\)\]) + | + (?:(?<!\\)\}) + ) + (?:\h|\R|$blankLineToken|$trailingCommentRegExp|$argumentsBefore)* # 0 or more h-space, blanklines, trailing comments + ) + # END of NOT + ( + (?: + $UnNamedGroupingFollowRegExp # 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{UnNamedGroupingBracesBrackets}$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 + $logger->trace("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; + $logger->trace("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/support/latexindent/LatexIndent/Verbatim.pm b/support/latexindent/LatexIndent/Verbatim.pm new file mode 100644 index 0000000000..d115bf078f --- /dev/null +++ b/support/latexindent/LatexIndent/Verbatim.pm @@ -0,0 +1,435 @@ +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 Data::Dumper; +use Exporter qw/import/; +use LatexIndent::Tokens qw/%tokens/; +use LatexIndent::GetYamlSettings qw/%masterSettings/; +use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active $is_m_switch_active/; +use LatexIndent::LogFile qw/$logger/; +our @EXPORT_OK = qw/put_verbatim_back_in find_verbatim_environments find_noindent_block find_verbatim_commands find_verbatim_special verbatim_common_tasks/; +our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321 +our $verbatimCounter; + +sub find_noindent_block{ + my $self = shift; + + # noindent block + $logger->trace('*Searching for NOINDENTBLOCk environments (see noIndentBlock)') if $is_t_switch_active; + $logger->trace(Dumper(\%{$masterSettings{noIndentBlock}})) if($is_tt_switch_active); + while( my ($noIndentBlock,$yesno)= each %{$masterSettings{noIndentBlock}}){ + if($yesno){ + $logger->trace("looking for $noIndentBlock:$yesno environments") if $is_t_switch_active; + + (my $noIndentBlockSpec = $noIndentBlock) =~ s/\*/\\*/sg; + my $noIndentRegExp = qr/ + ( + (?!<\\) + % + \h* # possible horizontal spaces + \\begin\{ + $noIndentBlockSpec # environment name captured into $2 + \} # % \begin{noindentblock} statement + ) + ( + .*? + ) # non-greedy match (body) + ( + (?!<\\) + % # % + \h* # possible horizontal spaces + \\end\{$noIndentBlockSpec\} # \end{noindentblock} + ) # % \end{<something>} statement + /sx; + + while( ${$self}{body} =~ m/$noIndentRegExp/sx){ + + # create a new Verbatim object + my $noIndentBlockObj = LatexIndent::Verbatim->new( begin=>$1, + body=>$2, + end=>$3, + name=>$noIndentBlock, + type=>"noindentblock", + modifyLineBreaksYamlName=>"verbatim", + ); + + # give unique id + $noIndentBlockObj->create_unique_id; + + # verbatim children go in special hash + ${$self}{verbatim}{${$noIndentBlockObj}{id}}=$noIndentBlockObj; + + # log file output + $logger->trace("*NOINDENTBLOCK environment found: $noIndentBlock") if $is_t_switch_active; + + # remove the environment block, and replace with unique ID + ${$self}{body} =~ s/$noIndentRegExp/${$noIndentBlockObj}{id}/sx; + + $logger->trace("replaced with ID: ${$noIndentBlockObj}{id}") if $is_t_switch_active; + + # possible decoration in log file + $logger->trace(${$masterSettings{logFilePreferences}}{showDecorationFinishCodeBlockTrace}) if ${$masterSettings{logFilePreferences}}{showDecorationFinishCodeBlockTrace}; + } + } else { + $logger->trace("*not* looking for $noIndentBlock as $noIndentBlock:$yesno") if $is_t_switch_active; + } + } + return; +} + +sub find_verbatim_environments{ + my $self = shift; + + # verbatim environments + $logger->trace('*Searching for VERBATIM environments (see verbatimEnvironments)') if $is_t_switch_active; + $logger->trace(Dumper(\%{$masterSettings{verbatimEnvironments}})) if($is_tt_switch_active); + while( my ($verbEnv,$yesno)= each %{$masterSettings{verbatimEnvironments}}){ + if($yesno){ + $logger->trace("looking for $verbEnv:$yesno environments") if $is_t_switch_active; + + (my $verbEnvSpec = $verbEnv) =~ s/\*/\\*/sg; + my $verbatimRegExp = qr/ + ( + \\begin\{ + $verbEnvSpec # environment name captured into $1 + \} # \begin{<something>} statement + ) + ( + .*? + ) # any character, but not \\begin + ( + \\end\{$verbEnvSpec\} # \end{<something>} statement + ) + (\h*)? # possibly followed by horizontal space + (\R)? # possibly followed by a line break + /sx; + + while( ${$self}{body} =~ m/$verbatimRegExp/sx){ + + # create a new Verbatim object + my $verbatimBlock = LatexIndent::Verbatim->new( begin=>$1, + body=>$2, + end=>$3, + name=>$verbEnv, + type=>"environment", + modifyLineBreaksYamlName=>"verbatim", + linebreaksAtEnd=>{ + end=>$5?1:0, + }, + horizontalTrailingSpace=>$4?$4:q(), + aliases=>{ + # begin statements + BeginStartsOnOwnLine=>"VerbatimBeginStartsOnOwnLine", + # after end statements + EndFinishesWithLineBreak=>"VerbatimEndFinishesWithLineBreak", + }, + ); + + # there are common tasks for each of the verbatim objects + $verbatimBlock->verbatim_common_tasks; + + # verbatim children go in special hash + ${$self}{verbatim}{${$verbatimBlock}{id}}=$verbatimBlock; + + # log file output + $logger->trace("*VERBATIM environment found: $verbEnv") if $is_t_switch_active; + + # remove the environment block, and replace with unique ID + ${$self}{body} =~ s/$verbatimRegExp/${$verbatimBlock}{replacementText}/sx; + + $logger->trace("replaced with ID: ${$verbatimBlock}{id}") if $is_t_switch_active; + + # possible decoration in log file + $logger->trace(${$masterSettings{logFilePreferences}}{showDecorationFinishCodeBlockTrace}) if ${$masterSettings{logFilePreferences}}{showDecorationFinishCodeBlockTrace}; + } + } else { + $logger->trace("*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 + $logger->trace('*Searching for VERBATIM commands (see verbatimCommands)') if $is_t_switch_active; + $logger->trace(Dumper(\%{$masterSettings{verbatimCommands}})) if($is_tt_switch_active); + while( my ($verbCommand,$yesno)= each %{$masterSettings{verbatimCommands}}){ + if($yesno){ + $logger->trace("looking for $verbCommand:$yesno Commands") if $is_t_switch_active; + + my $verbatimRegExp = 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 + (\h*)? # possibly followed by horizontal space + (\R)? # possibly followed by a line break + /mx; + + while( ${$self}{body} =~ m/$verbatimRegExp/){ + + # create a new Verbatim object + my $verbatimCommand = LatexIndent::Verbatim->new( begin=>$1.($2?$2:q()).$3, + body=>$4, + end=>$3, + name=>$verbCommand, + type=>"command", + modifyLineBreaksYamlName=>"verbatim", + linebreaksAtEnd=>{ + end=>$6?1:0, + }, + horizontalTrailingSpace=>$5?$5:q(), + aliases=>{ + # begin statements + BeginStartsOnOwnLine=>"VerbatimBeginStartsOnOwnLine", + # after end statements + EndFinishesWithLineBreak=>"VerbatimEndFinishesWithLineBreak", + }, + optArg=>$2?$2:q(), + ); + # there are common tasks for each of the verbatim objects + $verbatimCommand->verbatim_common_tasks; + + # output, if desired + $logger->trace(Dumper($verbatimCommand),'ttrace') if($is_tt_switch_active); + + # verbatim children go in special hash + ${$self}{verbatim}{${$verbatimCommand}{id}}=$verbatimCommand; + + # log file output + $logger->trace("*VERBATIM command found: $verbCommand") if $is_t_switch_active; + + # remove the environment block, and replace with unique ID + ${$self}{body} =~ s/$verbatimRegExp/${$verbatimCommand}{replacementText}/sx; + + $logger->trace("replaced with ID: ${$verbatimCommand}{id}") if $is_t_switch_active; + + # possible decoration in log file + $logger->trace(${$masterSettings{logFilePreferences}}{showDecorationFinishCodeBlockTrace}) if ${$masterSettings{logFilePreferences}}{showDecorationFinishCodeBlockTrace}; + } + } else { + $logger->trace("*not* looking for $verbCommand as $verbCommand:$yesno") if $is_t_switch_active; + } + } + return; + +} + +sub find_verbatim_special{ + my $self = shift; + + # loop through specialBeginEnd + while( my ($specialName,$BeginEnd)= each %{$masterSettings{specialBeginEnd}}){ + + # only classify special Verbatim if lookForThis is 'verbatim' + if( (ref($BeginEnd) eq "HASH") and ${$BeginEnd}{lookForThis}=~m/v/s and ${$BeginEnd}{lookForThis} eq 'verbatim'){ + $logger->trace('*Searching for VERBATIM special (see specialBeginEnd)') if $is_t_switch_active; + + my $verbatimRegExp = qr/ + ( + ${$BeginEnd}{begin} + ) + ( + .*? + ) + ( + ${$BeginEnd}{end} + ) + (\h*)? # possibly followed by horizontal space + (\R)? # possibly followed by a line break + /sx; + + while( ${$self}{body} =~ m/$verbatimRegExp/sx){ + + # create a new Verbatim object + my $verbatimBlock = LatexIndent::Verbatim->new( begin=>$1, + body=>$2, + end=>$3, + name=>$specialName, + modifyLineBreaksYamlName=>"specialBeginEnd", + linebreaksAtEnd=>{ + end=>$5?1:0, + }, + horizontalTrailingSpace=>$4?$4:q(), + type=>"special", + aliases=>{ + # begin statements + BeginStartsOnOwnLine=>"SpecialBeginStartsOnOwnLine", + # after end statements + EndFinishesWithLineBreak=>"SpecialEndFinishesWithLineBreak", + }, + ); + # there are common tasks for each of the verbatim objects + $verbatimBlock->verbatim_common_tasks; + + # verbatim children go in special hash + ${$self}{verbatim}{${$verbatimBlock}{id}}=$verbatimBlock; + + # log file output + $logger->trace("*VERBATIM special found: $specialName") if $is_t_switch_active; + + # remove the special block, and replace with unique ID + ${$self}{body} =~ s/$verbatimRegExp/${$verbatimBlock}{replacementText}/sx; + + $logger->trace("replaced with ID: ${$verbatimBlock}{id}") if $is_t_switch_active; + + # possible decoration in log file + $logger->trace(${$masterSettings{logFilePreferences}}{showDecorationFinishCodeBlockTrace}) if ${$masterSettings{logFilePreferences}}{showDecorationFinishCodeBlockTrace}; + } + } + } +} + +sub put_verbatim_back_in { + my $self = shift; + my %input = @_; + + # if there are no verbatim children, return + return unless(${$self}{verbatim}); + + my $verbatimCount=0; + my $toMatch = q(); + if($input{match} eq "everything-except-commands"){ + $toMatch = "noindentblockenvironmentspecial"; + + # count the number of non-command verbatim objects + while( my ($key,$child)= each %{${$self}{verbatim}}){ + ${$child}{type} = "environment" if !(defined ${$child}{type}); + $verbatimCount++ if($toMatch =~ m/${$child}{type}/); + } + return unless($verbatimCount>0); + } else { + $toMatch = "command"; + # count the number of command verbatim objects + while( my ($key,$child)= each %{${$self}{verbatim}}){ + ${$child}{type} = "environment" if !(defined ${$child}{type}); + $verbatimCount++ if($toMatch =~ m/${$child}{type}/); + } + return unless($verbatimCount>0); + } + + # search for environments/commands + $logger->trace('*Putting verbatim back in') if $is_t_switch_active; + $logger->trace('pre-processed body:') if $is_tt_switch_active; + $logger->trace(${$self}{body}) if($is_tt_switch_active); + + # loop through document children hash + my $verbatimFound=0; + while($verbatimFound < $verbatimCount){ + while( my ($key,$child)= each %{${$self}{verbatim}}){ + if($toMatch =~ m/${$child}{type}/){ + if(${$self}{body} =~ m/${$child}{id}/mx){ + # possibly remove trailing line break + if(defined ${$child}{EndFinishesWithLineBreak} + and ${$child}{EndFinishesWithLineBreak}==-1 + and ${$self}{body} =~ m/${$child}{id}\h*\R/s){ + $logger->trace("m-switch active, removing trailing line breaks from ${$child}{name}") if $is_t_switch_active; + ${$self}{body} =~ s/${$child}{id}(\h*)?(\R|\h)*/${$child}{id} /s; + } + # replace ids with body + ${$self}{body} =~ s/${$child}{id}/${$child}{begin}${$child}{body}${$child}{end}/s; + + # log file info + $logger->trace('Body now looks like:') if $is_tt_switch_active; + $logger->trace(${$self}{body},'ttrace') if($is_tt_switch_active); + + # delete the child so it won't be operated upon again + delete ${$self}{verbatim}{${$child}{id}}; + $verbatimFound++; + } elsif ($is_m_switch_active and ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}>1 and ${$self}{body} !~ m/${$child}{id}/){ + $logger->trace("${$child}{id} not found in body using /m matching, it may have been split across line (see modifyLineBreaks: textWrapOptions)") if($is_t_switch_active); + + # search for a version of the verbatim ID that may have line breaks + my $verbatimIDwithLineBreaks = join("\\R?",split(//,${$child}{id})); + my $verbatimIDwithLineBreaksRegExp = qr/$verbatimIDwithLineBreaks/s; + + # replace the line-broken verbatim ID with a non-broken verbatim ID + ${$self}{body} =~ s/$verbatimIDwithLineBreaksRegExp/${$child}{id}/s; + + # note: we do *not* label this as found, as we need to go back through + # and search for the newly modified ID + } + } + + # logfile info + $logger->trace('*Post-processed body:') if $is_tt_switch_active; + $logger->trace(${$self}{body}) if($is_tt_switch_active); + } + } + return; +} + +sub verbatim_common_tasks{ + + my $self = shift; + + # get yaml settings + $self->yaml_modify_line_breaks_settings if $is_m_switch_active; + + # give unique id + $self->create_unique_id; + + # 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 end statements + $self->modify_line_breaks_end if $is_m_switch_active; +} + +sub create_unique_id{ + my $self = shift; + + $verbatimCounter++; + ${$self}{id} = "$tokens{verbatim}$verbatimCounter$tokens{endOfToken}"; + return; +} + +sub yaml_get_textwrap_removeparagraphline_breaks{ + my $self = shift; + $logger->trace("No text wrap or remove paragraph line breaks for verbatim code blocks, ${$self}{name}") if ${$masterSettings{logFilePreferences}}{showDecorationFinishCodeBlockTrace}; +} + +1; diff --git a/support/latexindent/LatexIndent/Version.pm b/support/latexindent/LatexIndent/Version.pm new file mode 100644 index 0000000000..c0ecb8bb06 --- /dev/null +++ b/support/latexindent/LatexIndent/Version.pm @@ -0,0 +1,24 @@ +package LatexIndent::Version; +# 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/$versionNumber $versionDate/; + +our $versionNumber = '3.7'; +our $versionDate = '2019-07-13'; +1 diff --git a/support/latexindent/README b/support/latexindent/README new file mode 100644 index 0000000000..86781a6af1 --- /dev/null +++ b/support/latexindent/README @@ -0,0 +1,57 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + latexindent.pl, version 3.7, 2019-07-13 + + PERL script to indent code within environments, and align delimited + environments in .tex files. + + 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/ + + C. M. Hughes + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+ + +FOR COMPLETE DETAILS, PLEASE SEE documentation/latexindent.pdf + +Note: latexindent.exe was created using + + perl ppp.pl -u -o latexindent.exe latexindent.pl + + using the Par::Packer perl module. + +*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+ + +USAGE + You'll need + + latexindent.pl + LatexIndent/*.pm + defaultSettings.yaml + + in the same directory. Windows users might prefer to grab latexindent.exe + +* IMPORTANT * + +This script may not work for your style of formatting; I highly +recommend comparing the outputfile.tex to make sure that +nothing has been changed (or removed) in a way that will damage +your file. + +I recommend both using the following: + - a visual check, at the very least, make sure that + each file has the same number of lines + - a check using latexdiff inputfile.tex outputfile.tex + - git status myfile.tex + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/support/latexindent/defaultSettings.yaml b/support/latexindent/defaultSettings.yaml new file mode 100644 index 0000000000..a706131365 --- /dev/null +++ b/support/latexindent/defaultSettings.yaml @@ -0,0 +1,603 @@ +# defaultSettings.yaml for latexindent.pl, version 3.7, 2019-07-13 +# a script that aims to +# beautify .tex, .sty, .cls files +# +# (or latexindent.exe if you're on Windows) +# +# 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 + + +# You should feel encouraged to change anything you like in these settings, but +# it would probably be better to have your own user settings +# files somewhere else - remember that this file may be overwritten +# when you update your tex distribution. Please see the manual linked from: +# +# https://github.com/cmhughes/latexindent.pl +# +# for details of how to create and configure your own settings files. +# +# Please read the manual (linked from above) first to understand what each switch does. + +# latexindent can be called to act on a file without using the file's 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 -w switch is active) +# for example, if your .tex file is called +# myfile.tex +# and you specify the backupExtension as BACKUP.bak then your +# backup file will be +# myfileBACKUP.bak +backupExtension: .bak + +# only one backup per file: +# - if onlyOneBackUp is 0 then, as a safety measure, +# the number on the extension increments by 1 each time: +# +# myfile.bak0, myfile.bak1, myfile.bak2 +# - if you set onlyOnebackUp to 1, then the backup file will +# be overwritten each time (not recommended until you trust the script) +onlyOneBackUp: 0 + +# some users may want a finite number of backup files, +# say at most 3; in which case, they can change this maxNumberOfBackUps. +# +# If maxNumberOfBackUps is set to 0 (or less) then infinitely +# many backups are possible, unless onlyOneBackUp is switched on +maxNumberOfBackUps: 0 + +# some users may wish to cycle through back up files. +# +# for example, with maxNumberOfBackUps: 4, they may +# wish to delete the oldest back up file, and keep only the most recent. +# +# copy myfile.bak1 to myfile.bak0 +# copy myfile.bak2 to myfile.bak1 +# copy myfile.bak3 to myfile.bak2 +# copy myfile.bak4 to myfile.bak3 +# +# the back up will be written to myfile.bak4 +cycleThroughBackUps: 0 + +# preferences for information displayed in the log file +logFilePreferences: + showEveryYamlRead: 1 + showAmalgamatedSettings: 0 + showDecorationStartCodeBlockTrace: 0 + showDecorationFinishCodeBlockTrace: 0 + endLogFileWith: '--------------' + showGitHubInfoFooter: 1 + PatternLayout: + default: "%A%n" + trace: "%A%n" + ttrace: "%A%n" + +# verbatim environments specified +# in this field will not be changed at all! +verbatimEnvironments: + verbatim: 1 + lstlisting: 1 + minted: 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 you detail in the following +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 + +# assume no preamble in cls, sty, by default +lookForPreamble: + .tex: 1 + .sty: 0 + .cls: 0 + .bib: 0 + +# 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 + +# default value of indentation +defaultIndent: "\t" + +# name of code blocks that should have their body aligned at ampersand delimiters +lookForAlignDelims: + tabular: + delims: 1 + alignDoubleBackSlash: 1 + spacesBeforeDoubleBackSlash: 1 + multiColumnGrouping: 0 + alignRowsWithoutMaxDelims: 1 + spacesBeforeAmpersand: 1 + spacesAfterAmpersand: 1 + justification: "left" + tabularx: + delims: 1 + longtable: 1 + tabu: 1 + array: 1 + matrix: 1 + bmatrix: 1 + Bmatrix: 1 + pmatrix: 1 + vmatrix: 1 + Vmatrix: 1 + align: 1 + align*: 1 + alignat: 1 + alignat*: 1 + aligned: 1 + cases: 1 + dcases: 1 + listabla: 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 + description: 1 + list: 1 + +# if you want to use other names for your items (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 + +# specialBeginEnd is, by default, mathmode focus, although +# there's no restrictions +specialBeginEnd: + displayMath: + begin: '\\\[' + end: '\\\]' + lookForThis: 1 + inlineMath: + begin: '(?<!\$)(?<!\\)\$(?!\$)' + end: '(?<!\\)\$(?!\$)' + lookForThis: 1 + displayMathTeX: + begin: '\$\$' + end: '\$\$' + lookForThis: 1 + specialBeforeCommand: 0 + +# if you want to add indentation after +# a heading, such as \part, \chapter, etc +# then populate it in here - you can add +# an indent rule to indentRules if you would +# like something other than defaultIndent +# +# you can also change the level if you like, +# or add your own title command +indentAfterHeadings: + part: + indentAfterThisHeading: 0 + level: 1 + chapter: + indentAfterThisHeading: 0 + level: 2 + section: + indentAfterThisHeading: 0 + level: 3 + subsection: + indentAfterThisHeading: 0 + level: 4 + subsection*: + indentAfterThisHeading: 0 + level: 4 + subsubsection: + indentAfterThisHeading: 0 + level: 5 + paragraph: + indentAfterThisHeading: 0 + level: 6 + subparagraph: + indentAfterThisHeading: 0 + level: 7 + +# maximum indentation, off by default +maximumIndentation: -1 + +# if you don't want to have additional indentation +# in a code block, then add it to noAdditionalIndent; note that +# code blocks in this field will inherit +# the *current* level of indentation they just won't +# get any *additional* indentation +noAdditionalIndent: + myexample: 1 + mydefinition: 1 + problem: 1 + exercises: 1 + mysolution: 1 + foreach: 0 + widepage: 1 + comment: 1 + document: 1 + frame: 0 + +# if you have indent rules for particular code blocks +# then you can populate them in indentRules; 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: " " + +# 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 + +# 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 + +# command code block details +commandCodeBlocks: + roundParenthesesAllowed: 1 + stringsAllowedBetweenArguments: + - + amalgamate: 1 + - 'node' + - 'at' + - 'to' + - 'decoration' + - '\+\+' + - '\-\-' + commandNameSpecial: + - + amalgamate: 1 + - '@ifnextchar\[' + +# modifyLineBreaks will only be searched if the -m +# switch is active +# +# poly-switch examples: +# +# BeginStartsOnOwnLine: +# modify line breaks before a begin statement +# +# 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 +# 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 2, e.g +# some text some text \begin{myenvironment} +# will be changed to +# some text some text% +# \begin{myenvironment} +# when set to 3, e.g +# some text some text \begin{myenvironment} +# will be changed to +# some text some text +# +# \begin{myenvironment} +# +# BodyStartsOnOwnLine: +# modify line breaks before the beginning of the body +# +# 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 +# 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 2, e.g +# \begin{myenv}body text body text +# will be changed to +# \begin{myenv}% +# body text body text +# when set to 3, e.g +# \begin{myenv}body text body text +# will be changed to +# \begin{myenv} +# +# body text body text +# +# EndStartsOnOwnLine: +# modify line breaks before the end statement +# +# 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 +# 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 2, e.g +# some text some text \end{myenvironment} +# will be changed to +# some text some text% +# \end{myenvironment} +# when set to 3, e.g +# some text some text \end{myenvironment} +# will be changed to +# some text some text +# +# \end{myenvironment} +# +# EndFinishesWithLineBreak: +# modify line breaks after the end statement +# +# 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 +# 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 2, e.g +# \end{myenvironment}some text some text +# will be changed to +# \end{myenvironment}% +# some text some text +# when set to 3, e.g +# \end{myenvironment}some text some text +# will be changed to +# \end{myenvironment} +# +# some text some text +# +# you can specify settings on a per-name basis +modifyLineBreaks: + preserveBlankLines: 1 + condenseMultipleBlankLinesInto: 1 + oneSentencePerLine: + manipulateSentences: 0 + removeSentenceLineBreaks: 1 + textWrapSentences: 0 + sentenceIndent: "" + sentencesFollow: + par: 1 + blankLine: 1 + fullStop: 1 + exclamationMark: 1 + questionMark: 1 + rightBrace: 1 + commentOnPreviousLine: 1 + other: 0 + sentencesBeginWith: + A-Z: 1 + a-z: 0 + other: 0 + sentencesEndWith: + basicFullStop: 0 + betterFullStop: 1 + exclamationMark: 1 + questionMark: 1 + other: 0 + textWrapOptions: + columns: 0 + separator: "" + perCodeBlockBasis: 0 + all: 0 + alignAtAmpersandTakesPriority: 1 + environments: + quotation: 0 + ifElseFi: 0 + optionalArguments: 0 + mandatoryArguments: 0 + items: 0 + specialBeginEnd: 0 + afterHeading: 0 + preamble: 0 + filecontents: 0 + masterDocument: 0 + removeParagraphLineBreaks: + all: 0 + beforeTextWrap: 0 + alignAtAmpersandTakesPriority: 1 + environments: + quotation: 0 + ifElseFi: 0 + optionalArguments: 0 + mandatoryArguments: 0 + items: 0 + specialBeginEnd: 0 + afterHeading: 0 + preamble: 0 + filecontents: 0 + masterDocument: 0 + paragraphsStopAt: + environments: 1 + verbatim: 1 + commands: 0 + ifElseFi: 0 + items: 0 + specialBeginEnd: 0 + heading: 0 + filecontents: 0 + comments: 0 + environments: + BeginStartsOnOwnLine: 0 + BodyStartsOnOwnLine: 0 + EndStartsOnOwnLine: 0 + EndFinishesWithLineBreak: 0 + equation*: + BeginStartsOnOwnLine: 0 + BodyStartsOnOwnLine: 0 + EndStartsOnOwnLine: 0 + EndFinishesWithLineBreak: 0 + ifElseFi: + IfStartsOnOwnLine: 0 + BodyStartsOnOwnLine: 0 + OrStartsOnOwnLine: 0 + OrFinishesWithLineBreak: 0 + ElseStartsOnOwnLine: 0 + ElseFinishesWithLineBreak: 0 + FiStartsOnOwnLine: 0 + FiFinishesWithLineBreak: 0 + ifnum: + IfStartsOnOwnLine: 0 + BodyStartsOnOwnLine: 0 + OrStartsOnOwnLine: 0 + OrFinishesWithLineBreak: 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 + verbatim: + VerbatimBeginStartsOnOwnLine: 0 + VerbatimEndFinishesWithLineBreak: 0 + +# replacements, only active when either -r or -rr switches are active +replacements: + - + amalgamate: 1 + - + this: 'latexindent.pl' + that: 'pl.latexindent' + lookForThis: 1 + when: before + +# fineTuning allows you to tweak the internal pattern matching that +# is central to latexindent.pl +fineTuning: + environments: + name: '[a-zA-Z@\*0-9_\\]+' + ifElseFi: + name: '@?if[a-zA-Z@]*?' + commands: + name: '[+a-zA-Z@\*0-9_\:]+?' + keyEqualsValuesBracesBrackets: + name: '[a-zA-Z@\*0-9_\/.\h\{\}:\#-]+?' + follow: '(?:(?<!\\)\{)|,|(?:(?<!\\)\[)' + NamedGroupingBracesBrackets: + name: '[0-9\.a-zA-Z@\*><]+?' + follow: '\h|\R|\{|\[|\$|\)|\(' + UnNamedGroupingBracesBrackets: + follow: '\{|\[|,|&|\)|\(|\$' + arguments: + before: '(?:#\d\h*;?,?\/?)+|\<.*?\>' + between: '_|\^|\*' + modifyLineBreaks: + betterFullStop: '(?:\.\)(?!\h*[a-z]))|(?:(?<!(?:(?:e\.g)|(?:i\.e)|(?:etc))))\.(?!(?:[a-z]|[A-Z]|\-|~|\,|[0-9]))' + doubleBackSlash: '\\\\(?:\h*\[\h*\d+\h*[a-zA-Z]+\h*\])?' + comma: ',' diff --git a/support/latexindent/documentation/appendices.tex b/support/latexindent/documentation/appendices.tex new file mode 100644 index 0000000000..59037a5b24 --- /dev/null +++ b/support/latexindent/documentation/appendices.tex @@ -0,0 +1,247 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\appendix + \section{Required Perl modules}\label{sec:requiredmodules} + If you intend to use \texttt{latexindent.pl} and \emph{not} one of the + supplied standalone executable files, then you will need a few standard Perl modules -- + if you can run the minimum code in \cref{lst:helloworld} (\texttt{perl helloworld.pl}) then you will be able to run + \texttt{latexindent.pl}, otherwise you may need to install the missing modules -- see + \cref{sec:module-installer,sec:manual-module-instal}. + + \begin{cmhlistings}[style=tcblatex,language=Perl]{\texttt{helloworld.pl}}{lst:helloworld} +#!/usr/bin/perl + +use strict; +use warnings; +use utf8; +use PerlIO::encoding; +use Unicode::GCString; +use open ':std', ':encoding(UTF-8)'; +use Text::Wrap; +use Text::Tabs; +use FindBin; +use YAML::Tiny; +use File::Copy; +use File::Basename; +use File::HomeDir; +use Getopt::Long; +use Data::Dumper; +use List::Util qw(max); +use Log::Log4perl qw(get_logger :levels); + +print "hello world"; +exit; +\end{cmhlistings} + + \subsection{Module installer script}\label{sec:module-installer} + \announce{2018-01-13}{perl module helper script} \texttt{latexindent.pl} ships with a helper script that will + install any missing \texttt{perl} modules on your system; if you run + \begin{commandshell} +perl latexindent-module-installer.pl +\end{commandshell} + or + \begin{dosprompt} +perl latexindent-module-installer.pl + \end{dosprompt} + then, once you have answered \texttt{Y}, the appropriate modules will be + installed onto your distribution. + + \subsection{Manually installed modules}\label{sec:manual-module-instal} + Manually installing the modules given in \cref{lst:helloworld} will vary depending on + your operating system and \texttt{Perl} distribution. For example, Ubuntu users + might visit the software center, or else run + \begin{commandshell} +sudo perl -MCPAN -e 'install "File::HomeDir"' +\end{commandshell} + + Linux users may be interested in exploring Perlbrew \cite{perlbrew}; possible + installation and setup options follow for Ubuntu (other distributions will need slightly + different commands). + \begin{commandshell} +sudo apt-get install perlbrew +perlbrew install perl-5.22.1 +perlbrew switch perl-5.22.1 +sudo apt-get install curl +curl -L http://cpanmin.us | perl - App::cpanminus +cpanm YAML::Tiny +cpanm File::HomeDir +cpanm Unicode::GCString +cpanm Log::Log4perl +cpanm Log::Dispatch +\end{commandshell} + + Users of the Macintosh operating system might like to explore the following commands, for + example: + \begin{commandshell} +brew install perl +brew install cpanm + +cpanm YAML::Tiny +cpanm File::HomeDir +cpanm Unicode::GCString +cpanm Log::Log4perl +cpanm Log::Dispatch +\end{commandshell} + + Strawberry Perl users on Windows might use \texttt{CPAN client}. All of the modules are + readily available on CPAN \cite{cpan}. + + \texttt{indent.log} will contain details of the location + of the Perl modules on your system. \texttt{latexindent.exe} is a standalone executable + for Windows (and therefore does not require a Perl distribution) and caches copies of the + Perl modules onto your system; if you wish to see where they are cached, use the + \texttt{trace} option, e.g + \begin{dosprompt} +latexindent.exe -t myfile.tex + \end{dosprompt} + + \section{Updating the path variable}\label{sec:updating-path} + \texttt{latexindent.pl} has a few scripts (available at \cite{latexindent-home}) that can + update the \texttt{path} variables. Thank you to \cite{jasjuang} for this + feature. If you're on a Linux or Mac machine, then you'll want \texttt{CMakeLists.txt} + from \cite{latexindent-home}. + \subsection{Add to path for Linux} + To add \texttt{latexindent.pl} to the path for Linux, follow these steps: + \begin{enumerate} + \item download \texttt{latexindent.pl} and its associated modules, \texttt{defaultSettings.yaml}, to + your chosen directory from \cite{latexindent-home} ; + \item within your directory, create a directory called \texttt{path-helper-files} and download + \texttt{CMakeLists.txt} and \lstinline!cmake_uninstall.cmake.in! from + \cite{latexindent-home}/path-helper-files to this directory; + \item run + \begin{commandshell} +ls /usr/local/bin +\end{commandshell} + to see what is \emph{currently} in there; + \item run the following commands + \begin{commandshell} +sudo apt-get install cmake +sudo apt-get update && sudo apt-get install build-essential +mkdir build && cd build +cmake ../path-helper-files +sudo make install +\end{commandshell} + \item run + \begin{commandshell} +ls /usr/local/bin +\end{commandshell} + again to check that \texttt{latexindent.pl}, its modules and \texttt{defaultSettings.yaml} have + been added. + \end{enumerate} + To \emph{remove} the files, run + \begin{commandshell} +sudo make uninstall +\end{commandshell} + \subsection{Add to path for Windows} + To add \texttt{latexindent.exe} to the path for Windows, follow these steps: + \begin{enumerate} + \item download \texttt{latexindent.exe}, \texttt{defaultSettings.yaml}, \texttt{add-to-path.bat} from + \cite{latexindent-home} to your chosen directory; + \item open a command prompt and run the following command to see what is \emph{currently} + in your \lstinline!%path%! variable; + \begin{dosprompt} +echo %path% + \end{dosprompt} + \item right click on \texttt{add-to-path.bat} and \emph{Run as administrator}; + \item log out, and log back in; + \item open a command prompt and run + \begin{dosprompt} +echo %path% + \end{dosprompt} + to check that the appropriate directory has been added to your \lstinline!%path%!. + \end{enumerate} + To \emph{remove} the directory from your \lstinline!%path%!, run + \texttt{remove-from-path.bat} as administrator. + + \section{logFilePreferences}\label{app:logfile-demo} + \Vref{lst:logFilePreferences} describes the options for customising the information given + to the log file, and we provide a few demonstrations here. Let's say that we start with + the code given in \cref{lst:simple}, and the settings specified in + \cref{lst:logfile-prefs1-yaml}. + + \begin{minipage}{.35\linewidth} + \cmhlistingsfromfile{demonstrations/simple.tex}{\texttt{simple.tex}}{lst:simple} + \end{minipage} + \hfill + \begin{minipage}{.6\linewidth} + \cmhlistingsfromfile{demonstrations/logfile-prefs1.yaml}[yaml-TCB]{\texttt{logfile-prefs1.yaml}}{lst:logfile-prefs1-yaml} + \end{minipage} + + If we run the following command (noting that \texttt{-t} is active) + \begin{commandshell} +latexindent.pl -t -l=logfile-prefs1.yaml simple.tex +\end{commandshell} + then on inspection of \texttt{indent.log} we will find the snippet given in + \cref{lst:indentlog}. + \begin{cmhlistings}[style=tcblatex,morekeywords={TRACE}]{\texttt{indent.log}}{lst:indentlog} + +++++ +TRACE: environment found: myenv + No ancestors found for myenv + Storing settings for myenvenvironments + indentRulesGlobal specified (0) for environments, ... + Using defaultIndent for myenv + Putting linebreak after replacementText for myenv + looking for COMMANDS and key = {value} +TRACE: Searching for commands with optional and/or mandatory arguments AND key = {value} + looking for SPECIAL begin/end +TRACE: Searching myenv for special begin/end (see specialBeginEnd) +TRACE: Searching myenv for optional and mandatory arguments + ... no arguments found + ----- + \end{cmhlistings} + Notice that the information given about \texttt{myenv} is `framed' using + \texttt{+++++} and \lstinline!-----! respectively. + + \section{Differences from Version 2.2 to 3.0}\label{app:differences} + There are a few (small) changes to the interface when comparing Version 2.2 to Version + 3.0. Explicitly, in previous versions you might have run, for example, + \begin{commandshell} +latexindent.pl -o myfile.tex outputfile.tex +\end{commandshell} + whereas in Version 3.0 you would run any of the following, for example, + \begin{commandshell} +latexindent.pl -o=outputfile.tex myfile.tex +latexindent.pl -o outputfile.tex myfile.tex +latexindent.pl myfile.tex -o outputfile.tex +latexindent.pl myfile.tex -o=outputfile.tex +latexindent.pl myfile.tex -outputfile=outputfile.tex +latexindent.pl myfile.tex -outputfile outputfile.tex +\end{commandshell} + noting that the \emph{output} file is given \emph{next to} the + \texttt{-o} switch. + + The fields given in \cref{lst:obsoleteYaml} are \emph{obsolete} from Version 3.0 + onwards. \cmhlistingsfromfile{demonstrations/obsolete.yaml}[yaml-obsolete]{Obsolete YAML fields from Version 3.0}{lst:obsoleteYaml} + + There is a slight difference when specifying indentation after headings; specifically, we + now write \texttt{indentAfterThisHeading} instead of \texttt{indent}. See + \cref{lst:indentAfterThisHeadingOld,lst:indentAfterThisHeadingNew} + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/indentAfterThisHeadingOld.yaml}[yaml-TCB]{\texttt{indentAfterThisHeading} in Version 2.2}{lst:indentAfterThisHeadingOld} + \end{minipage}% + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/indentAfterThisHeadingNew.yaml}[yaml-TCB]{\texttt{indentAfterThisHeading} in Version 3.0}{lst:indentAfterThisHeadingNew} + \end{minipage}% + + To specify \texttt{noAdditionalIndent} for display-math environments in Version 2.2, you would + write YAML as in \cref{lst:noAdditionalIndentOld}; as of Version 3.0, you would write YAML as in + \cref{lst:indentAfterThisHeadingNew1} or, if you're using \texttt{-m} switch, + \cref{lst:indentAfterThisHeadingNew2}. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/noAddtionalIndentOld.yaml}[yaml-TCB]{\texttt{noAdditionalIndent} in Version 2.2}{lst:noAdditionalIndentOld} + \end{minipage}% + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/noAddtionalIndentNew.yaml}[yaml-TCB]{\texttt{noAdditionalIndent} for \texttt{displayMath} in Version 3.0}{lst:indentAfterThisHeadingNew1} + + \cmhlistingsfromfile{demonstrations/noAddtionalIndentNew1.yaml}[yaml-TCB]{\texttt{noAdditionalIndent} for \texttt{displayMath} in Version 3.0}{lst:indentAfterThisHeadingNew2} + \end{minipage}% + + \mbox{}\hfill \begin{minipage}{.25\textwidth} + \hrule + + \hfill\itshape End\\\mbox{}\hfill\mbox{}\rlap{\hfill\includegraphics{logo}} + + \end{minipage} diff --git a/support/latexindent/documentation/cmhlistings.tex b/support/latexindent/documentation/cmhlistings.tex new file mode 100644 index 0000000000..1f058616d6 --- /dev/null +++ b/support/latexindent/documentation/cmhlistings.tex @@ -0,0 +1,28 @@ +\newcommand{\cmhlistingsfromfile}[4]{ +.. literalinclude:: #1^^J +^^I:class: .#4^^J +^^I:caption: #2^^J +^^I:name: #3^^J +} +\newcommand{\cmhlistingsfromfilefour}[6]{ +.. literalinclude:: #3^^J +^^I:class: .#6^^J +^^I:caption: #4^^J +^^I:name: #5^^J +^^I:lines: #1-#2^^J +^^I:linenos:^^J +^^I:lineno-start: #1^^J +} +\newcommand{\cmhlabel}[1]{ +.. _#1:^^J +} +\newcommand{\warning}[1]{ +.. warning::^^J +#1^^J +.. endwarning::^^J +} +\newcommand{\example}[1]{ +.. proof:example::^^J +#1^^J +.. endproof:example::^^J +} diff --git a/support/latexindent/documentation/figure-schematic.tex b/support/latexindent/documentation/figure-schematic.tex new file mode 100644 index 0000000000..911f85f6ad --- /dev/null +++ b/support/latexindent/documentation/figure-schematic.tex @@ -0,0 +1,33 @@ +\documentclass{standalone} +\usepackage{tikz} +\usetikzlibrary{positioning} +\begin{document} +\begin{tikzpicture}[ + needed/.style={very thick, draw=blue,fill=blue!20, + text centered, minimum height=2.5em,rounded corners=1ex}, + optional/.style={draw=black, very thick,scale=0.8, + text centered, minimum height=2.5em,rounded corners=1ex}, + optionalfill/.style={fill=black!10}, + connections/.style={draw=black!30,dotted,line width=3pt,text=red}, + ] + % Draw diagram elements + \node (latexindent) [needed,circle] {\texttt{latexindent.pl}}; + \node (default) [needed,above right=.5cm of latexindent] {\texttt{defaultSettings.yaml}}; + \node (indentconfig) [optional,right=of latexindent] {\texttt{indentconfig.yaml}}; + \node (any) [optional,optionalfill,above right=of indentconfig] {\texttt{any.yaml}}; + \node (name) [optional,optionalfill,right=of indentconfig] {\texttt{name.yaml}}; + \node (you) [optional,optionalfill,below right=of indentconfig] {\texttt{you.yaml}}; + \node (want) [optional,optionalfill,below=of indentconfig] {\texttt{want.yaml}}; + \node (local) [optional,below=of latexindent] {\texttt{localSettings.yaml}}; + \node (yamlswitch) [optional,left=of latexindent] {\texttt{-y switch}}; + % Draw arrows between elements + \draw[connections,solid] (latexindent) to[in=-90]node[pos=0.5,anchor=north]{1} (default.south) ; + \draw[connections,optional] (latexindent) -- node[pos=0.5,anchor=north]{2} (indentconfig) ; + \draw[connections,optional] (indentconfig) to[in=-90] (any.south) ; + \draw[connections,optional] (indentconfig) -- (name) ; + \draw[connections,optional] (indentconfig) to[out=-45,in=90] (you) ; + \draw[connections,optional] (indentconfig) -- (want) ; + \draw[connections,optional] (latexindent) -- node[pos=0.5,anchor=west]{3} (local) ; + \draw[connections,optional] (latexindent) -- node[pos=0.5,anchor=north]{4} (yamlswitch) ; +\end{tikzpicture} +\end{document} diff --git a/support/latexindent/documentation/latexindent.pdf b/support/latexindent/documentation/latexindent.pdf Binary files differnew file mode 100644 index 0000000000..12a226e99d --- /dev/null +++ b/support/latexindent/documentation/latexindent.pdf diff --git a/support/latexindent/documentation/latexindent.tex b/support/latexindent/documentation/latexindent.tex new file mode 100644 index 0000000000..775bd6a7d9 --- /dev/null +++ b/support/latexindent/documentation/latexindent.tex @@ -0,0 +1,777 @@ +% arara: pdflatex: {shell: yes} +% arara: bibtex +% arara: pdflatex: {shell: yes} +% arara: pdflatex: {shell: yes} +% arara: pdflatex: {shell: yes} +\documentclass[10pt]{article} +% 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/>. +\usepackage[left=4.5cm,right=2.5cm,showframe=false, + top=2cm,bottom=1.5cm,marginparsep=2cm]{geometry} % page setup +\usepackage{lmodern} +\usepackage{parskip} % paragraph skips +\usepackage{booktabs} % beautiful tables +\usepackage{listings} % nice verbatim environments +\usepackage{titlesec} % customize headings +\usepackage{titletoc} % customize headings +\usepackage{multicol} +\usepackage{changepage} % adjust width of page +\usepackage{fancyhdr} % headers & footers +\usepackage{wrapfig} +\usepackage{fontawesome} +\usepackage[sc,format=hang,font=small]{caption} % captions +\usepackage[backend=bibtex]{biblatex} % bibliography +\usepackage{tcolorbox} % framed environments +\usepackage{tikz} +\usepackage{xparse} +\usepackage[charter]{mathdesign} % changes font +\usepackage[expansion=false,kerning=true]{microtype} % better kerning +\usepackage{enumitem} % custom lists +\usepackage{longtable} +\usepackage{array} +\usepackage{totalcount} +\usepackage{standalone} +% setup gitinfo2, as in the manual, details just above begin{document} +\usepackage[mark,grumpy]{gitinfo2} +% tikz, tcolorbox libraries +\usetikzlibrary{positioning} +\usetikzlibrary{decorations.pathmorphing} +\usetikzlibrary{decorations,shapes} +\usepackage{varioref} % the documentation library from tcolorbox loads hyperref +\tcbuselibrary{breakable,minted,xparse,documentation,hooks,raster} +\hypersetup{ + pdfauthor={Chris Hughes}, + pdftitle={latexindent.pl package}, + pdfkeywords={perl;beautify;indentation}, + bookmarksnumbered, + bookmarksopen, + linktocpage, + colorlinks=true, + linkcolor=blue, + citecolor=black, +} +\usepackage{cleveref} + +% create my own star style, for re-use at various points +\tikzset{cmhstar/.style={star,star point ratio=2.25,fill=cmhgold,draw=orange,}} + +% shortcut command for displaying star in documentation +\newcommand{\stardemo}[1][]{\begin{tikzpicture} + \node at (10:.1cm)[very thin,cmhstar,scale=0.25,rotate=20,#1]{}; + \node at (120:.1cm)[very thin,cmhstar,scale=0.15,rotate=-10,#1]{}; + \node at (235:.1cm)[very thin,cmhstar,scale=0.2,rotate=-20,#1]{}; + \end{tikzpicture}} + +% totalcount +\DeclareTotalCounter{lstlisting} + +% customise the \tcbdocnew command +\tcbset{doclang/new={{\bfseries\color{green!50!black}N\normalfont\color{black}}}} +\tcbset{doclang/updated={{\bfseries\color{green!50!black}U\normalfont\color{black}}}} +\tcbset{doc marginnote={width=1.6cm}} +\NewDocumentCommand{\announce}{ s m s m }{% + \IfBooleanTF{#1}% + {% \announce* + \tcbdocmarginnote[overlay={\node at ([yshift=0mm,xshift=1mm]frame.north east) {\stardemo}; }]{% + \IfBooleanTF{#3}% + {% \announce*{date}*[text] means *updated* as of <date> + \tcbdocupdated{#2}% + }% + {% \announce*{date}[text] means *new* as of <date> + \tcbdocnew{#2}% + }% + }% + \IfBooleanTF{#3}% + {% + \addcontentsline{new}{cmhtitle}{#4 (U)}% + }% + {% + \addcontentsline{new}{cmhtitle}{#4 (N)}% + }% + }% + {% \announce + \tcbdocmarginnote{% + \IfBooleanTF{#3}% + {% \announce{date}*[text] means *updated* as of <date> + \tcbdocupdated{#2}% + }% + {% \announce{date}[text] means *new* as of <date> + \tcbdocnew{#2}% + }% + }% + }} + +\reversemarginpar +% bibliographies +\addbibresource{latex-indent} +\addbibresource{contributors} + +% http://tex.stackexchange.com/questions/122135/how-to-add-a-png-icon-on-the-right-side-of-a-tcolorbox-title +\newtcolorbox{warning}{parbox=false,breakable,enhanced,arc=0mm,colback=red!5,colframe=red,leftrule=12mm,% + overlay={\node[anchor=north west,outer sep=2pt] at (frame.north west) {\includegraphics[width=8mm]{warning}}; } +} + +\definecolor{harvestgold}{cmyk}{0.00, 0.05, 0.51, 0.07} %EDE275 +\definecolor{cmhgold}{cmyk}{0,0.178,0.909,0.008} %FDD017 +\colorlet{cmhgray}{gray!30!white} + +\makeatletter +\tcbset{ + addtolol/.style={list entry={\kvtcb@title},add to list={lol}{lstlisting}}, + addtololstar/.style={list entry={\kvtcb@title},add to list={lol}{lstlistingstar}}, + cmhlistings/.style={ + % width=\linewidth, + %breakable, + colback=blue!5!white, + colframe=white!25!black,colback=white, + top=0cm, + bottom=0cm, + left=0mm, + listing only, + center title, + listing engine=minted, + minted style=colorful, + minted options={obeytabs=true,showtabs=true,tabsize=4,showspaces=true}, + boxrule=0pt, + toprule=1pt,bottomrule=1pt, + titlerule=1pt, + colframe=white!40!black, + colback=white, + sharp corners, + colbacktitle=white!75!black + }, + yaml-TCB/.style={ + listing only, + listing engine=listings, + left=0cm, + boxrule=0pt, + sharp corners, + center title, + colbacktitle=white!75!blue, + colframe=white!25!blue, + colback=white!90!blue, + toprule=2pt, + titlerule=2pt, + }, + MLB-TCB/.style={ + yaml-TCB, + center title, + colframe=cmhgold, + colbacktitle=harvestgold, + colback=white!60!cmhgold, + width=0.9\linewidth, + before=\centering, + enhanced, + overlay={\node[anchor=north east,outer sep=2pt,draw=cmhgold,very thick,double,fill=harvestgold,font =\small] at ([yshift=-3mm]frame.north east) {\texttt{-m}}; } + }, + replace-TCB/.style={ + yaml-TCB, + center title, + colbacktitle=white!75!green, + colframe=white!25!green, + colback=white!90!green, + width=0.9\linewidth, + before=\centering, + enhanced, + overlay={\node[anchor=north east,outer sep=2pt,draw=white!25!green,very thick,circle,fill=white!75!green,font =\small] at ([yshift=-3mm]frame.north east) {\texttt{-r}}; } + }, + yaml-obsolete/.style={ + listing only, + listing engine=listings, + left=0cm, + boxrule=0pt, + sharp corners, + center title, + toprule=2pt, + titlerule=2pt, + colframe=white!25!red, + colbacktitle=white!75!red, + colback=white!90!red, + }, + new-to-this-version/.style={ + enhanced, + overlay app={\node at ([yshift=0mm,xshift=0mm]frame.north east) {\stardemo[scale=1.2]}; }, + addtololstar, + }, +} + +\DeclareTCBListing[use counter=lstlisting]{cmhlistings}{s O{} m m}{% + cmhlistings, + IfBooleanTF={#1}{new-to-this-version}{addtolol}, + center title, + title={\color{black}{\scshape Listing \thetcbcounter}: ~#3},label={#4}, + listing engine=listings, + listing options={#2}, +} + +\DeclareDocumentEnvironment{cmhtcbraster}{O{}}{\begin{tcbraster}[ + new-to-this-version/.style={ + enhanced, + overlay app={\node[anchor=south east] at ([yshift=-3mm,xshift=3mm]frame.north east) {\stardemo[scale=1.2]}; }, + addtololstar, + }, + raster valign=top,raster columns=2,#1]}{\end{tcbraster}} + +% \cmhlistingsfromfile +% * no star: not new, star: new +% [ listing/minted options ] +% * no star: uses minted library, star: uses listings library star: +% {<name of listing file>} +% [<options for tcolorbox>] +% {<title>} +% {<label>} +% +% for example, +% \cmhlistingsfromfile*[listing options]... uses listings library and is NEW +% \cmhlistingsfromfile[listing options]... uses listings library +% \cmhlistingsfromfile[listing options]*... uses minted library +% \cmhlistingsfromfile*[listing options]*... uses minted library and is NEW +\DeclareTCBInputListing[use counter=lstlisting]{\cmhlistingsfromfile}{s O{} s m O{} m m}{% + cmhlistings, + listing file={#4}, + IfBooleanTF={#3}{listing options={style=tcblatex,showspaces=false,#2}}{minted options={obeytabs=true,showtabs=false,tabsize=4,showspaces=false,#2}}, + title={\color{black}{\scshape Listing \thetcbcounter}: ~#6},label={#7}, + #5, + IfBooleanTF={#1}{new-to-this-version}{addtolol}, +} + +\makeatletter +\@tempswafalse +\def\@tempa{draft} +\@for\next:=\@classoptionslist\do +{\ifx\next\@tempa\@tempswatrue\fi} +\if@tempswa % draft option is active + \RenewDocumentCommand{\cmhlistingsfromfile}{s O{} s m O{} m m}{\captionof{lstlisting}{#6}\label{#7}} + \renewcommand{\stardemo}[1][]{$\star$} +\fi + +% command shell +\newtcblisting{commandshell}{colback=black,colupper=white,colframe=yellow!75!black, + listing only,listing options={style=tcblatex,language=sh, + morekeywords={latexindent,pl}, + keywordstyle=\color{blue!35!white}\bfseries, + }, + listing engine=listings, + left=0cm, + every listing line={\textcolor{red}{\small\ttfamily\fontseries{b}\selectfont cmh:$\sim$\$ }}} + +% dosprompt +\newtcblisting{dosprompt}{ + colback=black, + colupper=white, + colframe=yellow!75!black, + listing only, + listing options={ + language=command.com, + morekeywords={latexindent,pl}, + keywordstyle=\color{blue!35!white}\bfseries, + basicstyle=\small\color{white}\ttfamily + }, + listing engine=listings, + left=0cm, + every listing line={\textcolor{white}{\small\ttfamily\fontseries{b}\selectfont C:\textbackslash Users\textbackslash cmh$>$}}} + +\lstset{% + basicstyle=\small\ttfamily,language={[LaTeX]TeX}, + numberstyle=\ttfamily%\small, + breaklines=true, + keywordstyle=\color{blue}, % keywords + commentstyle=\color{purple}, % comments + tabsize=3, +}% +\DeclareTCBListing[use counter=lstlisting]{yaml}{O{} m O{} m}{ + yaml-TCB, + listing options={ + style=tcblatex, + numbers=none, + numberstyle=\color{red}, + #1, + }, + title={\color{black}{\scshape Listing \thetcbcounter}: ~#2},label={#4}, + #3, +} + +\lstdefinestyle{yaml-LST}{ + style=tcblatex, + numbers=none, + numberstyle=\color{red}, +} + +\lstdefinestyle{demo}{ + numbers=none, + linewidth=1.25\textwidth, + columns=fullflexible, +} + +\lstdefinestyle{fileExtensionPreference}{ + style=yaml-LST, + firstnumber=41,linerange={41-45}, + numbers=left, +} + +\lstdefinestyle{logFilePreferences}{ + style=yaml-LST, + firstnumber=85,linerange={85-95}, + numbers=left, +} + +\lstdefinestyle{verbatimEnvironments}{ + style=yaml-LST, + firstnumber=99,linerange={99-102}, + numbers=left, +} + +\lstdefinestyle{verbatimCommands}{ + style=yaml-LST, + firstnumber=105,linerange={105-107}, + numbers=left, +} + +\lstdefinestyle{noIndentBlock}{ + style=yaml-LST, + firstnumber=112,linerange={112-114}, + numbers=left, +} + +\lstdefinestyle{removeTrailingWhitespace}{ + style=yaml-LST, + firstnumber=117,linerange={117-119}, + numbers=left, +} + +\lstdefinestyle{fileContentsEnvironments}{ + style=yaml-LST, + firstnumber=123,linerange={123-125}, + numbers=left, +} + +\lstdefinestyle{lookForPreamble}{ + style=yaml-LST, + firstnumber=131,linerange={131-135}, + numbers=left, +} + +\lstdefinestyle{indentAfterItems}{ + style=yaml-LST, + firstnumber=187,linerange={187-191}, + numbers=left, +} + +\lstdefinestyle{itemNames}{ + style=yaml-LST, + firstnumber=197,linerange={197-199}, + numbers=left, +} + +\lstdefinestyle{specialBeginEnd}{ + style=yaml-LST, + firstnumber=203,linerange={203-216}, + numbers=left, +} + +\lstdefinestyle{indentAfterHeadings}{ + style=yaml-LST, + firstnumber=226,linerange={226-235}, + numbers=left, +} + +\lstdefinestyle{noAdditionalIndentGlobalEnv}{ + style=yaml-LST, + firstnumber=284,linerange={284-285}, + numbers=left, +} + +\lstdefinestyle{noAdditionalIndentGlobal}{ + style=yaml-LST, + firstnumber=284,linerange={284-296}, + numbers=left, +} + +\lstdefinestyle{indentRulesGlobalEnv}{ + style=yaml-LST, + firstnumber=300,linerange={300-301}, + numbers=left, +} + +\lstdefinestyle{indentRulesGlobal}{ + style=yaml-LST, + firstnumber=300,linerange={300-312}, + numbers=left, +} + +\lstdefinestyle{commandCodeBlocks}{ + style=yaml-LST, + firstnumber=315,linerange={315-329}, + numbers=left, +} + +\lstdefinestyle{modifylinebreaks}{ + style=yaml-LST, + firstnumber=441,linerange={441-443}, + numbers=left, +} + +\lstdefinestyle{textWrapOptions}{ + style=yaml-LST, + firstnumber=468,linerange={468-469}, + numbers=left, +} + +\lstdefinestyle{textWrapOptionsAll}{ + style=yaml-LST, + firstnumber=468,linerange={468-484}, + numbers=left, +} + +\lstdefinestyle{removeParagraphLineBreaks}{ + style=yaml-LST, + firstnumber=485,linerange={485-499}, + numbers=left, +} + +\lstdefinestyle{paragraphsStopAt}{ + style=yaml-LST, + firstnumber=500,linerange={500-509}, + numbers=left, +} + +\lstdefinestyle{oneSentencePerLine}{ + style=yaml-LST, + firstnumber=444,linerange={444-467}, + numbers=left, +} + +\lstdefinestyle{sentencesFollow}{ + style=yaml-LST, + firstnumber=449,linerange={449-457}, + numbers=left, +} + +\lstdefinestyle{sentencesBeginWith}{ + style=yaml-LST, + firstnumber=458,linerange={458-461}, + numbers=left, +} + +\lstdefinestyle{sentencesEndWith}{ + style=yaml-LST, + firstnumber=462,linerange={462-467}, + numbers=left, +} + +\lstdefinestyle{modifylinebreaksEnv}{ + style=yaml-LST, + firstnumber=510,linerange={510-519}, + numbers=left, +} + +\lstdefinestyle{replacements}{ + style=yaml-LST, + firstnumber=571,linerange={571-579}, + numbers=left, +} + +\lstdefinestyle{fineTuning}{ + style=yaml-LST, + firstnumber=582,linerange={582-603}, + numbers=left, +} + +% stars around contributors +\pgfdeclaredecoration{stars}{initial}{ + \state{initial}[width=15pt] + { + \pgfmathparse{round(rnd*100)} + \pgfsetfillcolor{yellow!\pgfmathresult!orange} + \pgfsetstrokecolor{yellow!\pgfmathresult!red} + \pgfnode{star}{center}{}{}{\pgfusepath{stroke,fill}} + } + \state{final} + { + \pgfpathmoveto{\pgfpointdecoratedpathlast} + } +} + +\newtcolorbox{stars}{% + enhanced jigsaw, + breakable, % allow page breaks + left=0cm, + top=0cm, + before skip=0.2cm, + boxsep=0cm, + frame style={draw=none,fill=none}, % hide the default frame + colback=white, + overlay={ + \draw[inner sep=0,minimum size=rnd*15pt+2pt] + decorate[decoration={stars,segment length=2cm}] { + decorate[decoration={zigzag,segment length=2cm,amplitude=0.3cm}] { + ([xshift=-.5cm,yshift=0.1cm]frame.south west) -- ([xshift=-.5cm,yshift=0.4cm]frame.north west) + }}; + \draw[inner sep=0,minimum size=rnd*15pt+2pt] + decorate[decoration={stars,segment length=2cm}] { + decorate[decoration={zigzag,segment length=2cm,amplitude=0.3cm}] { + ([xshift=.75cm,yshift=0.1cm]frame.south east) -- ([xshift=.75cm,yshift=0.6cm]frame.north east) + }}; + \node[anchor=north west,outer sep=2pt,opacity=0.25] at ([xshift=-4.25cm]frame.north west) {\resizebox{3cm}{!}{\faGithub}}; + }, + % paragraph skips obeyed within tcolorbox + parbox=false, +} + +\newtcolorbox[auto counter]{example}{breakable, + enhanced jigsaw, + before skip=6pt,after skip=6pt, + frame hidden, + title={\llap{Example~\thetcbcounter\hspace{3mm}}}, + fonttitle=\bfseries, + coltitle=black, + attach title to upper, + grow to left by=5mm, + top=3mm, + overlay unbroken={% + \draw[thick,cmhgray] ([xshift=3mm]interior.north east)--([xshift=3mm]interior.south east); + \path [fill=cmhgray] ([xshift=3mm]interior.north east) circle (.5mm); + \path [fill=cmhgray] ([xshift=3mm]interior.south east) circle (1mm); + }, + overlay first={% + \draw[thick,cmhgray] ([xshift=3mm]interior.north east)--([xshift=3mm]interior.south east); + \path [fill=cmhgray] ([xshift=3mm]interior.north east) circle (.5mm); + %\path [draw=cmhgray,fill=white] ([xshift=3mm]interior.south east) circle (.5mm); + }, + overlay last={% + \draw[thick,cmhgray] ([xshift=3mm]interior.north east)--([xshift=3mm]interior.south east); + %\path [fill=white,draw=cmhgray] ([xshift=3mm]interior.north east) circle (.5mm); + \path [fill=cmhgray] ([xshift=3mm]interior.south east) circle (1mm); + }, + parbox=false, +} + +% copied from /usr/local/texlive/2013/texmf-dist/tex/latex/biblatex/bbx/numeric.bbx +% the only modification is the \stars and \endstars +\defbibenvironment{specialbib} +{\stars\list + {\printtext[labelnumberwidth]{% + \printfield{prefixnumber}% + \printfield{labelnumber}}} + {\setlength{\labelwidth}{\labelnumberwidth}% + \setlength{\leftmargin}{\labelwidth}% + \setlength{\labelsep}{\biblabelsep}% + \addtolength{\leftmargin}{\labelsep}% + \setlength{\itemsep}{\bibitemsep}% + \setlength{\parsep}{\bibparsep}}% + \renewcommand*{\makelabel}[1]{\hss##1}} +{\endlist\endstars} +{\item} + +\newtcbox{yamltitlebox}[1][]{colframe=black!50!white,boxrule=.5pt,sharp corners,#1} + +\newcommand{\flagbox}[1]{% + \par + \makebox[30pt][l]{% + \hspace{-1cm}% + \ttfamily\fontseries{b}\selectfont #1 + }% +} + +\NewDocumentCommand{\yamltitle}{O{} m s m}{% + \par + \makebox[30pt][l]{% + \hspace{-2cm}% + \yamltitlebox[#1]{% + {\ttfamily\fontseries{b}\selectfont{\color{blue!80!white}#2}}: % + \IfBooleanTF{#3} + {$\langle$\itshape #4$\rangle$} + {{\bfseries #4}} + }} + \par\nobreak% +} + +\newcommand{\fixthis}[1] +{% + \marginpar{\huge \color{red} \framebox{FIX}}% + \typeout{FIXTHIS: p\thepage : #1^^J}% +} +% custom section +\titleformat{\section} +{\normalfont\Large\bfseries} +{\llap{\thesection\hskip.5cm}} +{0pt} +{} + +\newcommand*\ruleline[1]{% + \par\noindent\raisebox{.8ex}{\makebox[\linewidth]{\hrulefill\hspace{1ex}\raisebox{-1.8ex}{#1}\hspace{1ex}\hrulefill}}} + +\newcommand{\imagetouse}{logo-bw} + +\titleformat% Formatting the header +{\section} % command +[block] % shape - Only managed to get it working with block +{\normalfont\bfseries\huge} % format - Change here as needed +{\centering {\scshape Section \thesection}\\} % the section +{0pt} % sep +{\centering \ruleline{\includegraphics{\imagetouse}}\\ % The horizontal rule + \centering} % And the actual title + +% custom subsection +\titleformat{\subsection} +{\normalfont\bfseries} +{\llap{\thesubsection\hskip.5cm}} +{0pt} +{} +% custom subsubsection +\titleformat{\subsubsection} +{\normalfont\bfseries} +{\llap{\thesubsubsection\hskip.5cm}} +{0pt} +{} + +\titlespacing\section{0pt}{12pt plus 4pt minus 2pt}{34pt plus 2pt minus 2pt} +\titlespacing\subsection{0pt}{12pt plus 4pt minus 2pt}{4pt plus 2pt minus 2pt} +\titlespacing\subsubsection{0pt}{12pt plus 4pt minus 2pt}{4pt plus 2pt minus 2pt} + +% list of listings +\contentsuse{lstlisting}{lol} +\titleclass{\lstlistingstar}{straight}[\section] +\titleformat{\lstlistingstar}{}{}{}{} +\titlecontents{lstlistingstar}[2em] + {\addvspace{0.25pc}} + {} + {\llap{\stardemo}\thecontentslabel} + {\titlerule*[0.5em]{$\cdot$}\contentspage} + [] +\titlecontents{lstlisting}[2em] + {\addvspace{0.25pc}} + {\thecontentslabel} + {\thecontentslabel} + {\titlerule*[0.5em]{$\cdot$}\contentspage} + [] +\AtBeginDocument{\addtocontents{lol}{\protect\begin{widepage}\protect\begin{multicols}{2}}} +\AtEndDocument{\addtocontents{lol}{\protect\end{multicols}\protect\end{widepage}}} + +% cleveref settings +\crefname{table}{Table}{Tables} +\Crefname{table}{Table}{Tables} +\crefname{figure}{Figure}{Figures} +\Crefname{figure}{Figure}{Figures} +\crefname{section}{Section}{Sections} +\Crefname{section}{Section}{Sections} +\crefname{listing}{Listing}{Listings} +\Crefname{listing}{Listing}{Listings} + +% headers and footers +\fancyhf{} % delete current header and footer +\fancyhead[R]{\bfseries\thepage% + \tikz[remember picture,overlay] { + \node at (1,0){\includegraphics{logo-bw}}; } +} +\fancyhead[L]{\rightmark} +\fancyheadoffset[L]{3cm} +\pagestyle{fancy} + +% renew plain style +\fancypagestyle{plain}{% + \fancyhf{} % clear all header and footer fields + \renewcommand{\headrulewidth}{0pt} + \renewcommand{\footrulewidth}{0pt}} + +% widepage environment +\newenvironment{widepage}{\begin{adjustwidth}{-3cm}{0cm}}{\end{adjustwidth}} + +% symbols for the m switch +\newcommand{\BeginStartsOnOwnLine}{\color{red}\spadesuit} +\newcommand{\BodyStartsOnOwnLine}{\color{red}\heartsuit} +\newcommand{\EndStartsOnOwnLine}{\color{red}\diamondsuit} +\newcommand{\EndFinishesWithLineBreak}{\color{red}\clubsuit} +\newcommand{\ElseStartsOnOwnLine}{\color{red}\bigstar} +\newcommand{\ElseFinishesWithLineBreak}{\color{red}\square} +\newcommand{\OrStartsOnOwnLine}{\color{red}\blacktriangle} +\newcommand{\OrFinishesWithLineBreak}{\color{red}\blacktriangledown} +\newcommand{\EqualsStartsOnOwnLine}{\color{red}\bullet} + +% table rules +\setlength\heavyrulewidth{0.25ex} +% gitinfo2 settings +\renewcommand{\gitMark}{\gitBranch\,@\,\gitAbbrevHash{}\,\textbullet{}\,\gitAuthorDate\,\textbullet{}\faGithub\,\textbullet{}\gitRel} + +% setting up gitinfo2: +% copy the file post-xxx-sample.txt from http://mirror.ctan.org/macros/latex/contrib/gitinfo2 +% and put it in .git/hooks/post-checkout +% then +% cd .git/hooks +% chmod g+x post-checkout +% chmod +x post-checkout +% cp post-checkout post-commit +% cp post-checkout post-merge +% cd ../.. +% git checkout master +% git checkout develop +% ls .git +% and you should see gitHeadInfo.gin +% +% Make sure that the 'V' is included in the below! +% +% RELTAG=$(git describe --tags --long --always --dirty='-*' --match 'V[0-9]*.*' 2>/dev/null) + +% http://tex.stackexchange.com/questions/233843/textasteriskcentered-invisible-with-garamondmathdesign +% remove the definition of \textasteriskcentered for TS1 encoding +\UndeclareTextCommand{\textasteriskcentered}{TS1} +% reinstate a default encoding +\DeclareTextSymbolDefault{\textasteriskcentered}{OT1} +% suitably define the command +\DeclareTextCommand{\textasteriskcentered}{OT1}{\raisebox{-.7ex}[1ex][0pt]{*}} + +% new features list +\newcommand{\listOfNewFeatures}{\setcounter{tocdepth}{4}\@starttoc{new}} +\contentsuse{cmhtitle}{new} +% toc settings +\titleclass{\cmhtitle}{straight}[\subsection] +\titleformat{\cmhtitle}{}{}{}{} +\titlecontents{cmhtitle}% <paragaph> + [2cm]% <left> + {\small\itshape}% <above-code> + {}% <numbered-entry-format>; you could also use {\thecontentslabel. } to show the numbers + {}% <numberless-entry-format> + {\titlerule*[0.5em]{$\cdot$}\contentspage} + +\setcounter{secnumdepth}{5} +\begin{document} +\renewcommand*{\thefootnote}{\arabic{footnote}} +\input{title.tex} +\tableofcontents +{\small\lstlistoflistings} +% start sections on new page +\newcommand{\sectionbreak}{\clearpage\thispagestyle{plain}} + \input{sec-introduction} + \input{sec-demonstration} + \input{sec-how-to-use} + \input{sec-indent-config-and-settings.tex} + \input{sec-default-user-local} + \input{subsec-noAdditionalIndent-indentRules} + \input{subsubsec-environments-and-their-arguments} + \input{subsubsec-environments-with-items} + \input{subsubsec-commands-with-arguments} + \input{subsubsec-ifelsefi} + \input{subsubsec-special} + \input{subsubsec-headings} + \input{subsubsec-no-add-remaining-code-blocks} + \stopcontents[noAdditionalIndent] + \input{subsec-commands-and-their-options} + \input{sec-the-m-switch} + \stopcontents[the-m-switch] + \input{subsec-partnering-poly-switches} + \input{subsec-conflicting-poly-switches} + \input{sec-replacements} + \input{sec-fine-tuning} + \input{sec-conclusions-know-limitations} + \input{references} + \input{appendices} +\end{document} diff --git a/support/latexindent/documentation/logo.tex b/support/latexindent/documentation/logo.tex new file mode 100644 index 0000000000..5bc31e0a4f --- /dev/null +++ b/support/latexindent/documentation/logo.tex @@ -0,0 +1,33 @@ +% arara: pdflatex +% +% to create the black and white logo: +% convert -density 1000 -colorspace GRAY logo.pdf logo-bw.pdf +\documentclass[border=1mm]{standalone} + +\usepackage{tikz} +\usetikzlibrary{matrix} +\definecolor{harvestgold}{cmyk}{0.00, 0.05, 0.51, 0.07} %EDE275 +\definecolor{cmhgold}{cmyk}{0,0.178,0.909,0.008} %FDD017 +\definecolor{bakeoffblue}{cmyk}{0.24, 0.00, 0.02, 0.18} %9fd2cd +\definecolor{bakeoffgreen}{cmyk}{0.30, 0.00, 0.20, 0.29} %80b692 +\definecolor{burntorange}{cmyk}{0.00, 0.41, 1.00, 0.04} +\begin{document} +\begin{tikzpicture}[logo/.style={draw=blue,circle,fill=white}] + \matrix{ + \node[logo,dash pattern=on .5pt off 1.0pt,thick,draw=purple!75!white]{}; & \node[logo,draw=burntorange]{}; \\ + \node[logo,fill,draw=bakeoffgreen,fill=bakeoffblue]{}; & \node[logo,double,draw=cmhgold,fill=harvestgold]{};\\}; +\end{tikzpicture} +\end{document} + +% for reference, the following puts the GitHub logo in the background; also needs +% \usetikzlibrary{backgrounds} +\pagecolor{white} +\begin{tikzpicture}[logo/.style={draw=blue,circle,fill=none,fill opacity=0.75}] + \matrix{% + \node[logo,dash pattern=on .5pt off 1.0pt,thick,draw=purple!75!white]{}; & \node[logo,draw=burntorange]{}; \\ + \node[logo,fill,draw=bakeoffgreen,fill=bakeoffblue]{}; & \node[logo,double,draw=cmhgold,fill=harvestgold]{};\\}; + \begin{scope}[on background layer] + %\node [yshift=0.01em,xshift=0.07em] {\resizebox{1cm}{!}{\color{gray!50!white}\faGithub}}; + \node {\resizebox{1cm}{!}{\color{gray!25!white}\faGithub}}; + \end{scope} +\end{tikzpicture} diff --git a/support/latexindent/documentation/references.tex b/support/latexindent/documentation/references.tex new file mode 100644 index 0000000000..548b394578 --- /dev/null +++ b/support/latexindent/documentation/references.tex @@ -0,0 +1,4 @@ +\nocite{*} +\section{References} + \printbibliography[heading=subbibnumbered,title={External links},notkeyword=contributor] + \printbibliography[env=specialbib,heading=subbibnumbered,title={Contributors\label{sec:contributors}},keyword=contributor] diff --git a/support/latexindent/documentation/sec-conclusions-know-limitations.tex b/support/latexindent/documentation/sec-conclusions-know-limitations.tex new file mode 100644 index 0000000000..1e865c9c84 --- /dev/null +++ b/support/latexindent/documentation/sec-conclusions-know-limitations.tex @@ -0,0 +1,38 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\section{Conclusions and known limitations}\label{sec:knownlimitations} + There are a number of known limitations of the script, and almost certainly quite a few + that are \emph{unknown}! + + The main limitation is to do with the alignment routine discussed on + \cpageref{yaml:lookforaligndelims}; for example, consider the file given in \cref{lst:matrix2}. + + \cmhlistingsfromfile{demonstrations/matrix2.tex}{\texttt{matrix2.tex}}{lst:matrix2} + + The default output is given in \cref{lst:matrix2-default}, and it is clear that the alignment + routine has not worked as hoped, but it is \emph{expected}. \cmhlistingsfromfile{demonstrations/matrix2-default.tex}{\texttt{matrix2.tex} default output}{lst:matrix2-default} + + The reason for the problem is that when \texttt{latexindent.pl} stores its code blocks + (see \vref{tab:code-blocks}) it uses replacement tokens. The alignment routine is using + the \emph{length of the replacement token} in its measuring -- I hope to be able to address this in the + future. + + There are other limitations to do with the multicolumn alignment routine (see + \vref{lst:tabular2-mod2}); in particular, when working with code blocks in which + multicolumn commands overlap, the algorithm can fail. + + Another limitation is to do with efficiency, particularly when the \texttt{-m} + switch is active, as this adds many checks and processes. The current implementation + relies upon finding and storing \emph{every} code block (see the discussion on + \cpageref{page:phases}); it is hoped that, in a future version, only + \emph{nested} code blocks will need to be stored in the `packing' phase, and + that this will improve the efficiency of the script. + + You can run \texttt{latexindent} on any file;% + \announce*{2019-07-13}*{ability to call latexindent on any file} if you don't specify an extension, then the extensions that you + specify in \lstinline[breaklines=true]!fileExtensionPreference! (see \vref{lst:fileExtensionPreference}) will be consulted. If you + find a case in which the script struggles, please feel free to report it at + \cite{latexindent-home}, and in the meantime, consider using a \texttt{noIndentBlock} (see \cpageref{lst:noIndentBlockdemo}). + + I hope that this script is useful to some; if you find an example where the script does + not behave as you think it should, the best way to contact me is to report an issue on + \cite{latexindent-home}; otherwise, feel free to find me on the \url{http://tex.stackexchange.com/users/6621/cmhughes}. diff --git a/support/latexindent/documentation/sec-default-user-local.tex b/support/latexindent/documentation/sec-default-user-local.tex new file mode 100644 index 0000000000..2a657fae8a --- /dev/null +++ b/support/latexindent/documentation/sec-default-user-local.tex @@ -0,0 +1,737 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\section{defaultSettings.yaml}\label{sec:defuseloc} + \texttt{latexindent.pl} loads its settings from \texttt{defaultSettings.yaml}. The idea is to + separate the behaviour of the script from the internal working -- this is very similar to + the way that we separate content from form when writing our documents in \LaTeX. + + If you look in \texttt{defaultSettings.yaml} you'll find the switches that govern the behaviour + of \texttt{latexindent.pl}. If you're not sure where \texttt{defaultSettings.yaml} resides on + your computer, don't worry as \texttt{indent.log} will tell you where to find it. + \texttt{defaultSettings.yaml} is commented, but here is a description of what each switch is + designed to do. The default value is given in each case; whenever you see + \emph{integer} in \emph{this} section, assume that it must be + greater than or equal to \texttt{0} unless otherwise stated. + +\yamltitle{fileExtensionPreference}*{fields} + \texttt{latexindent.pl} can be called to + act on a file without specifying the file extension. For example we can call + \begin{commandshell} +latexindent.pl myfile +\end{commandshell} + \begin{wrapfigure}[8]{r}[0pt]{6cm} + \cmhlistingsfromfile[style=fileExtensionPreference]*{../defaultSettings.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{fileExtensionPreference}}{lst:fileExtensionPreference} + \end{wrapfigure} + + in which case the script will look for \texttt{myfile} with the extensions + specified in \texttt{fileExtensionPreference} in their numeric order. If no match is found, the + script will exit. As with all of the fields, you should change and/or add to this as + necessary. + + Calling \texttt{latexindent.pl myfile} with the (default) settings specified in + \cref{lst:fileExtensionPreference} means that the script will first look for + \texttt{myfile.tex}, then \texttt{myfile.sty}, \texttt{myfile.cls}, and + finally \texttt{myfile.bib} in order\footnote{Throughout this manual, listings shown with line numbers represent code + taken directly from \texttt{defaultSettings.yaml}.}. + +\yamltitle{backupExtension}*{extension name} + + If you call \texttt{latexindent.pl} with the \texttt{-w} switch (to overwrite + \texttt{myfile.tex}) then it will create a backup file before doing any indentation; + the default extension is \texttt{.bak}, so, for example, + \texttt{myfile.bak0} would be created when calling \texttt{latexindent.pl myfile.tex} for the + first time. + + By default, every time you subsequently call \texttt{latexindent.pl} with the + \texttt{-w} to act upon \texttt{myfile.tex}, it will create successive + back up files: \texttt{myfile.bak1}, \texttt{myfile.bak2}, etc. + +\yamltitle{onlyOneBackUp}*{integer} + \label{page:onlyonebackup} + If you don't want a backup for every time that you call \texttt{latexindent.pl} (so + you don't want \texttt{myfile.bak1}, \texttt{myfile.bak2}, etc) and you + simply want \texttt{myfile.bak} (or whatever you chose \texttt{backupExtension} to be) then change \texttt{onlyOneBackUp} to + \texttt{1}; the default value of \texttt{onlyOneBackUp} is + \texttt{0}. + +\yamltitle{maxNumberOfBackUps}*{integer} + Some users may only want a finite number of backup files, say at most + $3$, in which case, they can change this switch. The smallest value of + \texttt{maxNumberOfBackUps} is $0$ which will \emph{not} + prevent backup files being made; in this case, the behaviour will be dictated entirely by + \texttt{onlyOneBackUp}. The default value of \texttt{maxNumberOfBackUps} is + \texttt{0}. + +\yamltitle{cycleThroughBackUps}*{integer} + Some users may wish to cycle through backup files, by deleting the oldest backup file and + keeping only the most recent; for example, with \texttt{maxNumberOfBackUps: 4}, and + \texttt{cycleThroughBackUps} set to \texttt{1} then the \texttt{copy} + procedure given below would be obeyed. + + \begin{commandshell} +copy myfile.bak1 to myfile.bak0 +copy myfile.bak2 to myfile.bak1 +copy myfile.bak3 to myfile.bak2 +copy myfile.bak4 to myfile.bak3 +\end{commandshell} + The default value of \texttt{cycleThroughBackUps} is \texttt{0}. + +\yamltitle{logFilePreferences}*{fields} + \texttt{latexindent.pl} writes information to \texttt{indent.log}, some + of which can be customized by changing \texttt{logFilePreferences}; see + \cref{lst:logFilePreferences}. If you load your own user settings (see \vref{sec:indentconfig}) + then \texttt{latexindent.pl} will detail them in \texttt{indent.log}; you can choose + not to have the details logged by switching \texttt{showEveryYamlRead} to + \texttt{0}. Once all of your settings have been loaded, you can see the + amalgamated settings in the log file by switching \texttt{showAmalgamatedSettings} to + \texttt{1}, if you wish. + + \cmhlistingsfromfile[style=logFilePreferences,]*{../defaultSettings.yaml}[width=.9\linewidth,before=\centering,yaml-TCB]{\texttt{logFilePreferences}}{lst:logFilePreferences} + + When% + \announce{2018-01-13}{showDecorationStartCodeBlockTrace feature for log file} either of the + \texttt{trace} modes (see \cpageref{page:traceswitch}) are active, you will receive + detailed information in \texttt{indent.log}. You can specify character strings to + appear before and after the notification of a found code block using, respectively, + \texttt{showDecorationStartCodeBlockTrace} and \texttt{showDecorationFinishCodeBlockTrace}. A demonstration is given in + \vref{app:logfile-demo}. + + The log file will end with the characters given in \texttt{endLogFileWith}, and will + report the \texttt{GitHub} address of \texttt{latexindent.pl} to the log file if + \texttt{showGitHubInfoFooter} is set to \texttt{1}. + + \texttt{latexindent.pl}% + \announce{2018-01-13}{log file pattern layout for log file} uses the \texttt{log4perl} module \cite{log4perl} + to handle the creation of the logfile. You can specify the layout of the information + given in the logfile using any of the \texttt{Log Layouts} detailed at + \cite{log4perl}. + +\yamltitle{verbatimEnvironments}*{fields} + + A field that contains a list of environments that you would like left completely alone -- + no indentation will be performed on environments that you have specified in this field, + see \cref{lst:verbatimEnvironments}. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile[style=verbatimEnvironments]*{../defaultSettings.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{verbatimEnvironments}}{lst:verbatimEnvironments} + \cmhlistingsfromfile[style=verbatimCommands]*{../defaultSettings.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{verbatimCommands}}{lst:verbatimCommands} + \end{cmhtcbraster} + + Note that if you put an environment in \texttt{verbatimEnvironments} and in other fields such + as \texttt{lookForAlignDelims} or \texttt{noAdditionalIndent} then \texttt{latexindent.pl} will + \emph{always} prioritize \texttt{verbatimEnvironments}. + +\yamltitle{verbatimCommands}*{fields} + A field that contains a list of commands that are verbatim commands, for example + \lstinline|\lstinline|; any commands populated in this field are protected from line + breaking routines (only relevant if the \texttt{-m} is active, see + \vref{sec:modifylinebreaks}). + +\yamltitle{noIndentBlock}*{fields} + + \begin{wrapfigure}[8]{r}[0pt]{6cm} + \cmhlistingsfromfile[style=noIndentBlock]*{../defaultSettings.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{noIndentBlock}}{lst:noIndentBlock} + \end{wrapfigure} + If you have a block of code that you don't want \texttt{latexindent.pl} to touch (even if + it is \emph{not} a verbatim-like environment) then you can wrap it in an + environment from \texttt{noIndentBlock}; you can use any name you like for this, + provided you populate it as demonstrate in \cref{lst:noIndentBlock}. + + Of course, you don't want to have to specify these as null environments in your code, so + you use them with a comment symbol, \lstinline!%!, followed by as many spaces + (possibly none) as you like; see \cref{lst:noIndentBlockdemo} for example. + + \begin{cmhlistings}[style=demo,escapeinside={(*@}{@*)}]{\texttt{noIndentBlock} demonstration}{lst:noIndentBlockdemo} +%(*@@*) \begin{noindent} + this code + won't + be touched + by + latexindent.pl! +%(*@@*)\end{noindent} + \end{cmhlistings} + +\yamltitle{removeTrailingWhitespace}*{fields}\label{yaml:removeTrailingWhitespace} + + \begin{wrapfigure}[10]{r}[0pt]{7cm} + \cmhlistingsfromfile[style=removeTrailingWhitespace]*{../defaultSettings.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{removeTrailingWhitespace}{lst:removeTrailingWhitespace} + + \vspace{.1cm} + \begin{yaml}[numbers=none]{removeTrailingWhitespace (alt)}[width=.8\linewidth,before=\centering]{lst:removeTrailingWhitespace-alt} +removeTrailingWhitespace: 1 +\end{yaml} + \end{wrapfigure} + Trailing white space can be removed both \emph{before} and + \emph{after} processing the document, as detailed in \cref{lst:removeTrailingWhitespace}; + each of the fields can take the values \texttt{0} or + \texttt{1}. See \vref{lst:removeTWS-before,lst:env-mlb5-modAll,lst:env-mlb5-modAll-remove-WS} for before and after results. Thanks + to \cite{vosskuhle} for providing this feature. + + You can specify \texttt{removeTrailingWhitespace} simply as \texttt{0} or + \texttt{1}, if you wish; in this case,% + \announce{2017-06-28}{removeTrailingWhitespace} \texttt{latexindent.pl} will set both \texttt{beforeProcessing} and + \texttt{afterProcessing} to the value you specify; see \cref{lst:removeTrailingWhitespace-alt}. +\yamltitle{fileContentsEnvironments}*{field} + + \begin{wrapfigure}[6]{r}[0pt]{6cm} + \cmhlistingsfromfile[style=fileContentsEnvironments]*{../defaultSettings.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{fileContentsEnvironments}}{lst:fileContentsEnvironments} + \end{wrapfigure} + Before \texttt{latexindent.pl} determines the difference between preamble (if any) and + the main document, it first searches for any of the environments specified in + \texttt{fileContentsEnvironments}, see \cref{lst:fileContentsEnvironments}. The behaviour of + \texttt{latexindent.pl} on these environments is determined by their location (preamble + or not), and the value \texttt{indentPreamble}, discussed next. + +\yamltitle{indentPreamble}{0|1} + + The preamble of a document can sometimes contain some trickier code for + \texttt{latexindent.pl} to operate upon. By default, \texttt{latexindent.pl} won't try to + operate on the preamble (as \texttt{indentPreamble} is set to \texttt{0}, by + default), but if you'd like \texttt{latexindent.pl} to try then change + \texttt{indentPreamble} to \texttt{1}. + +\yamltitle{lookForPreamble}*{fields} + + \begin{wrapfigure}[8]{r}[0pt]{5cm} + \cmhlistingsfromfile[style=lookForPreamble]*{../defaultSettings.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{lookForPreamble}{lst:lookForPreamble} + \end{wrapfigure} + Not all files contain preamble; for example, \texttt{sty}, + \texttt{cls} and \texttt{bib} files typically do + \emph{not}. Referencing \cref{lst:lookForPreamble}, if you set, for example, + \texttt{.tex} to \texttt{0}, then regardless of the setting of the + value of \texttt{indentPreamble}, preamble will not be assumed when operating upon + \texttt{.tex} files. +\yamltitle{preambleCommandsBeforeEnvironments}{0|1} + Assuming that \texttt{latexindent.pl} is asked to operate upon the preamble of a + document, when this switch is set to \texttt{0} then environment code blocks + will be sought first, and then command code blocks. When this switch is set to + \texttt{1}, commands will be sought first. The example that first motivated + this switch contained the code given in \cref{lst:motivatepreambleCommandsBeforeEnvironments}. + + \begin{cmhlistings}{Motivating \texttt{preambleCommandsBeforeEnvironments}}{lst:motivatepreambleCommandsBeforeEnvironments} +... +preheadhook={\begin{mdframed}[style=myframedstyle]}, +postfoothook=\end{mdframed}, +... +\end{cmhlistings} + +\yamltitle{defaultIndent}*{horizontal space} + This is the default indentation (\lstinline!\t! means a tab, and is the default + value) used in the absence of other details for the command or environment we are working + with; see \texttt{indentRules} in \vref{sec:noadd-indent-rules} for more details. + + If you're interested in experimenting with \texttt{latexindent.pl} then you can + \emph{remove} all indentation by setting \texttt{defaultIndent: ""}. + +\yamltitle{lookForAlignDelims}*{fields}\label{yaml:lookforaligndelims} + \begin{wrapfigure}[12]{r}[0pt]{5cm} + \begin{yaml}[numbers=none]{\texttt{lookForAlignDelims} (basic)}[width=.8\linewidth,before=\centering]{lst:aligndelims:basic} +lookForAlignDelims: + tabular: 1 + tabularx: 1 + longtable: 1 + array: 1 + matrix: 1 + ... + \end{yaml} + \end{wrapfigure} + This contains a list of environments and/or commands that are operated upon in a special + way by \texttt{latexindent.pl} (see \cref{lst:aligndelims:basic}). In fact, the fields in \texttt{lookForAlignDelims} can + actually take two different forms: the \emph{basic} version is shown in + \cref{lst:aligndelims:basic} and the \emph{advanced} version in + \cref{lst:aligndelims:advanced}; we will discuss each in turn. + + The environments specified in this field will be operated on in a special way by + \texttt{latexindent.pl}. In particular, it will try and align each column by its + alignment tabs. It does have some limitations (discussed further in + \cref{sec:knownlimitations}), but in many cases it will produce results such as those in + \cref{lst:tabularbefore:basic,lst:tabularafter:basic}. + + If you find that \texttt{latexindent.pl} does not perform satisfactorily on such + environments then you can set the relevant key to \texttt{0}, for example + \texttt{tabular: 0}; alternatively, if you just want to ignore + \emph{specific} instances of the environment, you could wrap them in something + from \texttt{noIndentBlock} (see \vref{lst:noIndentBlock}). + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/tabular1.tex}{\texttt{tabular1.tex}}{lst:tabularbefore:basic} + \cmhlistingsfromfile{demonstrations/tabular1-default.tex}{\texttt{tabular1.tex} default output}{lst:tabularafter:basic} + \end{cmhtcbraster} + + If, for example, you wish to remove the alignment of the \lstinline!\\! within a + delimiter-aligned block, then the advanced form of \texttt{lookForAlignDelims} shown in + \cref{lst:aligndelims:advanced} is for you. + + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/tabular.yaml}[yaml-TCB]{\texttt{tabular.yaml}}{lst:aligndelims:advanced} + + Note that you can use a mixture of the basic and advanced form: in + \cref{lst:aligndelims:advanced} \texttt{tabular} and \texttt{tabularx} are advanced + and \texttt{longtable} is basic. When using the advanced form, each field should + receive at least 1 sub-field, and \emph{can} + (but does not have to) receive any of the following + fields: + \begin{itemize} + \item \texttt{delims}: binary switch (0 or 1) equivalent to simply specifying, for + example, \texttt{tabular: 1} in the basic version shown in \cref{lst:aligndelims:basic}. If + \texttt{delims} is set to \texttt{0} then the align at ampersand + routine will not be called for this code block (default: 1); + \item \texttt{alignDoubleBackSlash}: binary switch (0 or 1) to determine if \lstinline!\\! + should be aligned (default: 1); + \item \texttt{spacesBeforeDoubleBackSlash}: optionally,% + \announce{2018-01-13}*{update to spacesBeforeDoubleBackSlash in ampersand alignment} specifies the number (integer $\geq$ 0) of spaces + to be inserted before \lstinline!\\! (default: 1). \footnote{Previously this only activated if \texttt{alignDoubleBackSlash} was set to \texttt{0}.} + \item \announce{2017-06-19}{multiColumnGrouping} \texttt{multiColumnGrouping}: binary switch (0 or 1) that details if + \texttt{latexindent.pl} should group columns + above and below a \lstinline!\multicolumn! command (default: 0); + \item \announce{2017-06-19}{alignRowsWithoutMaxDelims} \texttt{alignRowsWithoutMaxDelims}: binary switch (0 or 1) that details if + rows that do not contain the maximum number of delimeters should be formatted so as to + have the ampersands aligned (default: 1); + \item \announce{2018-01-13}{spacesBeforeAmpersand in ampersand alignment}\texttt{spacesBeforeAmpersand}: optionally specifies the number (integer + $\geq$ 0) of + spaces to be placed \emph{before} ampersands (default: 1); + \item \announce{2018-01-13}{spacesAfterAmpersand in ampersand alignment}\texttt{spacesAfterAmpersand}: optionally specifies the number (integer + $\geq$ 0) of + spaces to be placed \emph{After} ampersands (default: 1); + \item \announce{2018-01-13}{justification of cells in ampersand alignment}\texttt{justification}: optionally specifies the justification of + each cell as either \emph{left} or \emph{right} (default: left). + \end{itemize} + + We will explore each of these features using the file \texttt{tabular2.tex} in + \cref{lst:tabular2} (which contains a \lstinline!\multicolumn! command), and the YAML files in \crefrange{lst:tabular2YAML}{lst:tabular8YAML}. + + \cmhlistingsfromfile{demonstrations/tabular2.tex}{\texttt{tabular2.tex}}{lst:tabular2} + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/tabular2.yaml}[yaml-TCB]{\texttt{tabular2.yaml}}{lst:tabular2YAML} + \end{minipage}% + \hfill + \begin{minipage}{.48\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/tabular3.yaml}[yaml-TCB]{\texttt{tabular3.yaml}}{lst:tabular3YAML} + \end{minipage}% + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/tabular4.yaml}[yaml-TCB]{\texttt{tabular4.yaml}}{lst:tabular4YAML} + \end{minipage}% + \hfill + \begin{minipage}{.48\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/tabular5.yaml}[yaml-TCB]{\texttt{tabular5.yaml}}{lst:tabular5YAML} + \end{minipage}% + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/tabular6.yaml}[yaml-TCB]{\texttt{tabular6.yaml}}{lst:tabular6YAML} + \end{minipage}% + \hfill + \begin{minipage}{.48\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/tabular7.yaml}[yaml-TCB]{\texttt{tabular7.yaml}}{lst:tabular7YAML} + \end{minipage}% + + \begin{minipage}{.48\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/tabular8.yaml}[yaml-TCB]{\texttt{tabular8.yaml}}{lst:tabular8YAML} + \end{minipage}% + + On running the commands + \begin{commandshell} +latexindent.pl tabular2.tex +latexindent.pl tabular2.tex -l tabular2.yaml +latexindent.pl tabular2.tex -l tabular3.yaml +latexindent.pl tabular2.tex -l tabular2.yaml,tabular4.yaml +latexindent.pl tabular2.tex -l tabular2.yaml,tabular5.yaml +latexindent.pl tabular2.tex -l tabular2.yaml,tabular6.yaml +latexindent.pl tabular2.tex -l tabular2.yaml,tabular7.yaml +latexindent.pl tabular2.tex -l tabular2.yaml,tabular8.yaml +\end{commandshell} + we obtain the respective outputs given in \crefrange{lst:tabular2-default}{lst:tabular2-mod8}. + + \begin{widepage} + \cmhlistingsfromfile{demonstrations/tabular2-default.tex}{\texttt{tabular2.tex} default output}{lst:tabular2-default} + \cmhlistingsfromfile{demonstrations/tabular2-mod2.tex}{\texttt{tabular2.tex} using \cref{lst:tabular2YAML}}{lst:tabular2-mod2} + \cmhlistingsfromfile{demonstrations/tabular2-mod3.tex}{\texttt{tabular2.tex} using \cref{lst:tabular3YAML}}{lst:tabular2-mod3} + \cmhlistingsfromfile{demonstrations/tabular2-mod4.tex}{\texttt{tabular2.tex} using \cref{lst:tabular2YAML,lst:tabular4YAML}}{lst:tabular2-mod4} + \cmhlistingsfromfile{demonstrations/tabular2-mod5.tex}{\texttt{tabular2.tex} using \cref{lst:tabular2YAML,lst:tabular5YAML}}{lst:tabular2-mod5} + \cmhlistingsfromfile{demonstrations/tabular2-mod6.tex}{\texttt{tabular2.tex} using \cref{lst:tabular2YAML,lst:tabular6YAML}}{lst:tabular2-mod6} + \cmhlistingsfromfile{demonstrations/tabular2-mod7.tex}{\texttt{tabular2.tex} using \cref{lst:tabular2YAML,lst:tabular7YAML}}{lst:tabular2-mod7} + \cmhlistingsfromfile{demonstrations/tabular2-mod8.tex}{\texttt{tabular2.tex} using \cref{lst:tabular2YAML,lst:tabular8YAML}}{lst:tabular2-mod8} + \end{widepage} + + Notice in particular: + \begin{itemize} + \item in both \cref{lst:tabular2-default,lst:tabular2-mod2} all rows have been aligned at the ampersand, even those + that do not contain the maximum number of ampersands (3 ampersands, in this case); + \item in \cref{lst:tabular2-default} the columns have been aligned at the ampersand; + \item in \cref{lst:tabular2-mod2} the \lstinline!\multicolumn! command has grouped the + $2$ columns beneath \emph{and} above it, because + \texttt{multiColumnGrouping} is set to $1$ in \cref{lst:tabular2YAML}; + \item in \cref{lst:tabular2-mod3} rows~3 and~6 have \emph{not} been aligned at the + ampersand, because \texttt{alignRowsWithoutMaxDelims} has been to set to $0$ in + \cref{lst:tabular3YAML}; however, the \lstinline!\\! \emph{have} still + been aligned; + \item in \cref{lst:tabular2-mod4} the columns beneath and above the \lstinline!\multicolumn! + commands have been grouped (because \texttt{multiColumnGrouping} is set to + $1$), and there are at least $4$ spaces + \emph{before} each aligned ampersand because \texttt{spacesBeforeAmpersand} is set to + $4$; + \item in \cref{lst:tabular2-mod5} the columns beneath and above the \lstinline!\multicolumn! + commands have been grouped (because \texttt{multiColumnGrouping} is set to + $1$), and there are at least $4$ spaces + \emph{after} each aligned ampersand because \texttt{spacesAfterAmpersand} is set to + $4$; + \item in \cref{lst:tabular2-mod6} the \lstinline!\\! have \emph{not} been + aligned, because \texttt{alignDoubleBackSlash} is set to \texttt{0}, otherwise the + output is the same as \cref{lst:tabular2-mod2}; + \item in \cref{lst:tabular2-mod7} the \lstinline!\\! \emph{have} been + aligned, and because \texttt{spacesBeforeDoubleBackSlash} is set to \texttt{0}, there are + no spaces ahead of them; the output is otherwise the same as \cref{lst:tabular2-mod2}. + \item in \cref{lst:tabular2-mod8} the cells have been \emph{right}-justified; note + that cells above and below the \lstinline!\multicol! statements have still been group + correctly, because of the settings in \cref{lst:tabular2YAML}. + \end{itemize} + + As of Version 3.0, the alignment routine works on mandatory and optional arguments within + commands, and also within `special' code blocks (see \texttt{specialBeginEnd} on + \cpageref{yaml:specialBeginEnd}); for example, assuming that you have a command called + \lstinline!\matrix! and that it is populated within \texttt{lookForAlignDelims} (which it is, by default), and that + you run the command + \begin{commandshell} +latexindent.pl matrix1.tex +\end{commandshell} + then the before-and-after results shown in \cref{lst:matrixbefore,lst:matrixafter} are achievable by + default. + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/matrix1.tex}{\texttt{matrix1.tex}}{lst:matrixbefore} + \cmhlistingsfromfile{demonstrations/matrix1-default.tex}{\texttt{matrix1.tex} default output}{lst:matrixafter} + \end{cmhtcbraster} + + If you have blocks of code that you wish to align at the \& character that are + \emph{not} wrapped in, for example, \lstinline!\begin{tabular}! \ldots + \lstinline!\end{tabular}!, then you can use the mark up illustrated in + \cref{lst:alignmentmarkup}; the default output is shown in \cref{lst:alignmentmarkup-default}. Note + that the \lstinline!%*! must be next to each other, but that there can be any + number of spaces (possibly none) between the + \lstinline!*! and \lstinline!\begin{tabular}!; note also that you may use any + environment name that you have specified in \texttt{lookForAlignDelims}. + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/align-block.tex}{\texttt{align-block.tex}}{lst:alignmentmarkup} + \cmhlistingsfromfile{demonstrations/align-block-default.tex}{\texttt{align-block.tex} default output}{lst:alignmentmarkup-default} + \end{cmhtcbraster} + + With reference to \vref{tab:code-blocks} and the, yet undiscussed, fields of + \texttt{noAdditionalIndent} and \texttt{indentRules} + (see \vref{sec:noadd-indent-rules}), these comment-marked blocks are + considered \texttt{environments}. + +\yamltitle{indentAfterItems}*{fields} + The environment names specified in \texttt{indentAfterItems} tell \texttt{latexindent.pl} + to look for \lstinline!\item! commands; if these switches are set to + \texttt{1} then indentation will be performed so as indent the code after + each \texttt{item}. A demonstration is given in \cref{lst:itemsbefore,lst:itemsafter} + + \begin{cmhtcbraster}[raster columns=3, + raster left skip=-3.5cm, + raster right skip=-2cm, + raster column skip=.03\linewidth] + \cmhlistingsfromfile[style=indentAfterItems]*{../defaultSettings.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{indentAfterItems}}{lst:indentafteritems} + \cmhlistingsfromfile{demonstrations/items1.tex}{\texttt{items1.tex}}{lst:itemsbefore} + \cmhlistingsfromfile{demonstrations/items1-default.tex}{\texttt{items1.tex} default output}{lst:itemsafter} + \end{cmhtcbraster} + +\yamltitle{itemNames}*{fields} + \begin{wrapfigure}[5]{r}[0pt]{5cm} + \cmhlistingsfromfile[style=itemNames]*{../defaultSettings.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{itemNames}}{lst:itemNames} + \end{wrapfigure} + If you have your own \texttt{item} commands (perhaps you prefer to use + \texttt{myitem}, for example) then you can put populate them in + \texttt{itemNames}. For example, users of the \texttt{exam} document class + might like to add \texttt{parts} to \texttt{indentAfterItems} and + \texttt{part} to \texttt{itemNames} to their user settings (see + \vref{sec:indentconfig} for details of how to configure user settings, and + \vref{lst:mysettings} \\ in particular \label{page:examsettings}.) + +\yamltitle{specialBeginEnd}*{fields}\label{yaml:specialBeginEnd} + The fields specified% + \announce{2017-08-21}*{specialBeginEnd} in + \texttt{specialBeginEnd} are, in their default state, focused on math mode begin and end + statements, but there is no requirement for this to be the case; \cref{lst:specialBeginEnd} + shows the default settings of \texttt{specialBeginEnd}. + + \cmhlistingsfromfile[style=specialBeginEnd]*{../defaultSettings.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{specialBeginEnd}}{lst:specialBeginEnd} + + The field \texttt{displayMath} represents \lstinline!\[...\]!, + \texttt{inlineMath} represents + \lstinline!$...$! and \texttt{displayMathTex} represents \lstinline!$$...$$!. + You can, of course, rename these in your own YAML files (see \vref{sec:localsettings}); + indeed, you might like to set up your own special begin and end statements. + + A demonstration of the before-and-after results are shown in \cref{lst:specialbefore,lst:specialafter}. + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/special1.tex}{\texttt{special1.tex} before}{lst:specialbefore} + \cmhlistingsfromfile{demonstrations/special1-default.tex}{\texttt{special1.tex} default output}{lst:specialafter} + \end{cmhtcbraster} + + For each field, \texttt{lookForThis} is set to \texttt{1} by default, + which means that \texttt{latexindent.pl} will look for this pattern; you can tell + \texttt{latexindent.pl} not to look for the pattern, by setting \texttt{lookForThis} + to \texttt{0}. + + There are% + \announce{2017-08-21}{specialBeforeCommand} examples in which it + is advantageous to search for \texttt{specialBeginEnd} fields \emph{before} + searching for commands, and the \texttt{specialBeforeCommand} switch controls this behaviour. + For example, consider the file shown in \cref{lst:specialLRbefore}. + + \cmhlistingsfromfile{demonstrations/specialLR.tex}{\texttt{specialLR.tex}}{lst:specialLRbefore} + + Now consider the YAML files shown in \cref{lst:specialsLeftRight-yaml,lst:specialBeforeCommand-yaml} + + \begin{cmhtcbraster} + \cmhlistingsfromfile[]*{demonstrations/specialsLeftRight.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{specialsLeftRight.yaml}}{lst:specialsLeftRight-yaml} + \cmhlistingsfromfile[]*{demonstrations/specialBeforeCommand.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{specialBeforeCommand.yaml}}{lst:specialBeforeCommand-yaml} + \end{cmhtcbraster} + + Upon running the following commands + \begin{widepage} + \begin{commandshell} +latexindent.pl specialLR.tex -l=specialsLeftRight.yaml +latexindent.pl specialLR.tex -l=specialsLeftRight.yaml,specialBeforeCommand.yaml +\end{commandshell} + \end{widepage} + we receive the respective outputs in \cref{lst:specialLR-comm-first-tex,lst:specialLR-special-first-tex}. + + \begin{minipage}{.49\linewidth} + \cmhlistingsfromfile{demonstrations/specialLR-comm-first.tex}{\texttt{specialLR.tex} using \cref{lst:specialsLeftRight-yaml}}{lst:specialLR-comm-first-tex} + \end{minipage} + \hfill + \begin{minipage}{.49\linewidth} + \cmhlistingsfromfile{demonstrations/specialLR-special-first.tex}{\texttt{specialLR.tex} using \cref{lst:specialsLeftRight-yaml,lst:specialBeforeCommand-yaml}}{lst:specialLR-special-first-tex} + \end{minipage} + + Notice that in: + \begin{itemize} + \item \cref{lst:specialLR-comm-first-tex} the \lstinline!\left! has been treated as a + \emph{command}, with one optional argument; + \item \cref{lst:specialLR-special-first-tex} the \texttt{specialBeginEnd} pattern in \cref{lst:specialsLeftRight-yaml} + has been obeyed because \cref{lst:specialBeforeCommand-yaml} specifies that the + \texttt{specialBeginEnd} should be sought \emph{before} commands. + \end{itemize} + + You can,optionally, specify% + \announce{2018-04-27}{update to specialBeginEnd} the + \texttt{middle} field for anything that you specify in \texttt{specialBeginEnd}. + For example, let's consider the \texttt{.tex} file in \cref{lst:special2}. + + \cmhlistingsfromfile{demonstrations/special2.tex}{\texttt{special2.tex}}{lst:special2} + + Upon saving the YAML settings in \cref{lst:middle-yaml,lst:middle1-yaml} and running the commands + \begin{commandshell} +latexindent.pl special2.tex -l=middle +latexindent.pl special2.tex -l=middle1 +\end{commandshell} + then we obtain the output given in \cref{lst:special2-mod1,lst:special2-mod2}. + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/middle.yaml}[yaml-TCB]{\texttt{middle.yaml}}{lst:middle-yaml} + \cmhlistingsfromfile{demonstrations/special2-mod1.tex}{\texttt{special2.tex} using \cref{lst:middle-yaml}}{lst:special2-mod1} + \end{cmhtcbraster} + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/middle1.yaml}[yaml-TCB]{\texttt{middle1.yaml}}{lst:middle1-yaml} + \cmhlistingsfromfile{demonstrations/special2-mod2.tex}{\texttt{special2.tex} using \cref{lst:middle1-yaml}}{lst:special2-mod2} + \end{cmhtcbraster} + + We note that: + \begin{itemize} + \item in \cref{lst:special2-mod1} the bodies of each of the \texttt{Elsif} statements + have been indented appropriately; + \item the \texttt{Else} statement has \emph{not} been indented + appropriately in \cref{lst:special2-mod1} -- read on! + \item we have specified multiple settings for the \texttt{middle} field using the + syntax demonstrated in \cref{lst:middle1-yaml} so that the body of the + \texttt{Else} statement has been indented appropriately in + \cref{lst:special2-mod2}. + \end{itemize} + + You may% + \announce{2018-08-13}{specialBeginEnd verbatim} specify fields in + \texttt{specialBeginEnd} to be treated as verbatim code blocks by changing + \texttt{lookForThis} to be \texttt{verbatim}. + + For example, beginning with the code in \cref{lst:special3-mod1} and the YAML in + \cref{lst:special-verb1-yaml}, and running + \begin{commandshell} +latexindent.pl special3.tex -l=special-verb1 +\end{commandshell} + then the output in \cref{lst:special3-mod1} is unchanged. + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/special-verb1.yaml}[yaml-TCB]{\texttt{special-verb1.yaml}}{lst:special-verb1-yaml} + \cmhlistingsfromfile{demonstrations/special3-mod1.tex}{\texttt{special3.tex} and output using \cref{lst:special-verb1-yaml}}{lst:special3-mod1} + \end{cmhtcbraster} + +\yamltitle{indentAfterHeadings}*{fields} + \begin{wrapfigure}[17]{r}[0pt]{8cm} + \cmhlistingsfromfile[style=indentAfterHeadings]*{../defaultSettings.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{indentAfterHeadings}}{lst:indentAfterHeadings} + \end{wrapfigure} + This field enables the user to specify indentation rules that take effect after heading + commands such as \lstinline!\part!, \lstinline!\chapter!, + \lstinline!\section!, \lstinline!\subsection*!, or indeed any user-specified command + written in this field.\footnote{There is a slight + difference in interface for this field when comparing Version 2.2 to Version 3.0; see \vref{app:differences} for details.} + + The default settings do \emph{not} place indentation after a heading, but + you can easily switch them on by changing \\ \texttt{indentAfterThisHeading: 0} to \\ + \texttt{indentAfterThisHeading: 1}. The \texttt{level} field tells \texttt{latexindent.pl} + the hierarchy of the heading structure in your document. You might, for example, like to + have both \texttt{section} and \texttt{subsection} set with + \texttt{level: 3} because you do not want the indentation to go too deep. + + You can add any of your own custom heading commands to this field, specifying the + \texttt{level} as appropriate. You can also specify your own indentation in + \texttt{indentRules} (see \vref{sec:noadd-indent-rules}); you will find the default \texttt{indentRules} contains + \lstinline!chapter: " "! which tells \texttt{latexindent.pl} simply to use a space + character after \texttt{\chapter} headings (once \texttt{indent} is set to + \texttt{1} for \texttt{chapter}). + + For example, assuming that you have the code in \cref{lst:headings1yaml} saved into + \texttt{headings1.yaml}, and that you have the text from \cref{lst:headings1} saved + into \texttt{headings1.tex}. + + \begin{cmhtcbraster} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/headings1.yaml}[yaml-TCB]{\texttt{headings1.yaml}}{lst:headings1yaml} + \cmhlistingsfromfile{demonstrations/headings1.tex}{\texttt{headings1.tex}}{lst:headings1} + \end{cmhtcbraster} + + If you run the command + \begin{commandshell} +latexindent.pl headings1.tex -l=headings1.yaml +\end{commandshell} + then you should receive the output given in \cref{lst:headings1-mod1}. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[showtabs=true]{demonstrations/headings1-mod1.tex}{\texttt{headings1.tex} using \cref{lst:headings1yaml}}{lst:headings1-mod1} + \end{minipage}% + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[showtabs=true]{demonstrations/headings1-mod2.tex}{\texttt{headings1.tex} second modification}{lst:headings1-mod2} + \end{minipage} + + Now say that you modify the \texttt{YAML} from \cref{lst:headings1yaml} so that + the \texttt{paragraph} \texttt{level} is \texttt{1}; after + running + \begin{commandshell} +latexindent.pl headings1.tex -l=headings1.yaml +\end{commandshell} + you should receive the code given in \cref{lst:headings1-mod2}; notice that the + \texttt{paragraph} and \texttt{subsection} are at the same indentation level. + +\yamltitle{maximumIndentation}*{horizontal space} + You can control the maximum indentation given to your file + by% + \announce{2017-08-21}{maximumIndentation} specifying the + \texttt{maximumIndentation} field as horizontal space (but \emph{not} including + tabs). This feature uses the \texttt{Text::Tabs} module \cite{texttabs}, and + is \emph{off} by default. + + For example, consider the example shown in \cref{lst:mult-nested} together with the + default output shown in \cref{lst:mult-nested-default}. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile{demonstrations/mult-nested.tex}{\texttt{mult-nested.tex}}{lst:mult-nested} + \cmhlistingsfromfile[showtabs=true]{demonstrations/mult-nested-default.tex}{\texttt{mult-nested.tex} default output}{lst:mult-nested-default} + \end{cmhtcbraster} + + Now say that, for example, you have the \texttt{max-indentation1.yaml} from + \cref{lst:max-indentation1yaml} and that you run the following command: + \begin{commandshell} +latexindent.pl mult-nested.tex -l=max-indentation1 +\end{commandshell} + You should receive the output shown in \cref{lst:mult-nested-max-ind1}. + + \begin{cmhtcbraster} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/max-indentation1.yaml}[yaml-TCB]{\texttt{max-indentation1.yaml}}{lst:max-indentation1yaml} + \cmhlistingsfromfile[showspaces=true]{demonstrations/mult-nested-max-ind1.tex}{\texttt{mult-nested.tex} using \cref{lst:max-indentation1yaml}}{lst:mult-nested-max-ind1} + \end{cmhtcbraster} + + Comparing the output in \cref{lst:mult-nested-default,lst:mult-nested-max-ind1} we notice that the (default) tabs of + indentation have been replaced by a single space. + + In general, when using the \texttt{maximumIndentation} feature, any leading tabs will be + replaced by equivalent spaces except, of course, those found in \texttt{verbatimEnvironments} (see \vref{lst:verbatimEnvironments}) + or \texttt{noIndentBlock} (see \vref{lst:noIndentBlock}). + +\subsection{The code blocks known latexindent.pl}\label{subsubsec:code-blocks} + As of Version 3.0, \texttt{latexindent.pl} processes documents using code blocks; each of + these are shown in \cref{tab:code-blocks}. + + \begin{table}[!htp] + \begin{widepage} + \centering + \caption{Code blocks known to \texttt{latexindent.pl}}\label{tab:code-blocks} + \begin{tabular}{m{.3\linewidth}@{\hspace{.25cm}}m{.4\linewidth}@{\hspace{.25cm}}m{.2\linewidth}} + \toprule + Code block & characters allowed in name & example \\ + \midrule + environments & \lstinline!a-zA-Z@\*0-9_\\! & + \begin{lstlisting}[,nolol=true,] +\begin{myenv} +body of myenv +\end{myenv} + \end{lstlisting} + \\\cmidrule{2-3} + optionalArguments & \emph{inherits} name from parent (e.g environment name) & + \begin{lstlisting}[,nolol=true,] +[ +opt arg text +] + \end{lstlisting} + \\\cmidrule{2-3} + mandatoryArguments & \emph{inherits} name from parent (e.g environment name) & + \begin{lstlisting}[,nolol=true,] +{ +mand arg text +} + \end{lstlisting} + \\\cmidrule{2-3} + commands & \lstinline!+a-zA-Z@\*0-9_\:! & \lstinline!\mycommand!$\langle$\itshape{arguments}$\rangle$ \\\cmidrule{2-3} + keyEqualsValuesBracesBrackets & \lstinline!a-zA-Z@\*0-9_\/.\h\{\}:\#-! & \lstinline!my key/.style=!$\langle$\itshape{arguments}$\rangle$ \\\cmidrule{2-3} + namedGroupingBracesBrackets & \lstinline!0-9\.a-zA-Z@\*><! & \lstinline!in!$\langle$\itshape{arguments}$\rangle$ \\\cmidrule{2-3} + UnNamedGroupingBracesBrackets & \centering\emph{No name!} & \lstinline!{! or \lstinline![! or \lstinline!,! or \lstinline!&! or \lstinline!)! or \lstinline!(! or \lstinline!$! followed by $\langle$\itshape{arguments}$\rangle$ \\\cmidrule{2-3} + ifElseFi & \lstinline!@a-zA-Z! but must begin with either \newline \lstinline!\if! of \lstinline!\@if! & + \begin{lstlisting}[,nolol=true,] +\ifnum... +... +\else +... +\fi + \end{lstlisting} \\\cmidrule{2-3} + items & User specified, see \vref{lst:indentafteritems,lst:itemNames} & + \begin{lstlisting}[,nolol=true,] +\begin{enumerate} + \item ... +\end{enumerate} + \end{lstlisting} \\\cmidrule{2-3} + specialBeginEnd & User specified, see \vref{lst:specialBeginEnd} & + \begin{lstlisting}[,nolol=true,] +\[ + ... +\] + \end{lstlisting} \\\cmidrule{2-3} + afterHeading & User specified, see \vref{lst:indentAfterHeadings} & + \begin{lstlisting}[,morekeywords={chapter},nolol=true,] +\chapter{title} + ... +\section{title} + \end{lstlisting} \\\cmidrule{2-3} + filecontents & User specified, see \vref{lst:fileContentsEnvironments} & + \begin{lstlisting}[,nolol=true,] +\begin{filecontents} +... +\end{filecontents} + \end{lstlisting} \\ + \bottomrule + \end{tabular} + \end{widepage} + \end{table} + + We will refer to these code blocks in what follows.% + \announce*{2019-07-13}{fine tuning of code blocks} Note that the fine tuning of the definition of the code blocks + detailed in \cref{tab:code-blocks} is discussed in \vref{sec:finetuning}. diff --git a/support/latexindent/documentation/sec-demonstration.tex b/support/latexindent/documentation/sec-demonstration.tex new file mode 100644 index 0000000000..18459edcda --- /dev/null +++ b/support/latexindent/documentation/sec-demonstration.tex @@ -0,0 +1,40 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\section{Demonstration: before and after} + Let's give a demonstration of some before and after code -- after all, you probably won't + want to try the script if you don't much like the results. You might also like to watch + the video demonstration I made on youtube \cite{cmh:videodemo} + + As you look at \crefrange{lst:filecontentsbefore}{lst:pstricksafter}, remember that \texttt{latexindent.pl} is just + following its rules, and there is nothing particular about these code snippets. All of + the rules can be modified so that you can personalise your indentation scheme. + + In each of the samples given in \crefrange{lst:filecontentsbefore}{lst:pstricksafter} the `before' case is a `worst case + scenario' with no effort to make indentation. The `after' result would be the same, + regardless of the leading white space at the beginning of each line which is stripped by + \texttt{latexindent.pl} (unless a \texttt{verbatim}-like + environment or \texttt{noIndentBlock} is specified -- more on this in \cref{sec:defuseloc}). + + \begin{widepage} + \centering + \begin{minipage}{.45\linewidth} + \cmhlistingsfromfile{demonstrations/filecontents1.tex}{\texttt{filecontents1.tex}}{lst:filecontentsbefore} + \end{minipage}\hfill + \begin{minipage}{.45\linewidth} + \cmhlistingsfromfile{demonstrations/filecontents1-default.tex}{\texttt{filecontents1.tex} default output}{lst:filecontentsafter} + \end{minipage}% + + \begin{minipage}{.45\linewidth} + \cmhlistingsfromfile{demonstrations/tikzset.tex}{\texttt{tikzset.tex}}{lst:tikzsetbefore} + \end{minipage}\hfill + \begin{minipage}{.45\linewidth} + \cmhlistingsfromfile{demonstrations/tikzset-default.tex}{\texttt{tikzset.tex} default output}{lst:tikzsetafter} + \end{minipage}% + + \begin{minipage}{.45\linewidth} + \cmhlistingsfromfile{demonstrations/pstricks.tex}{\texttt{pstricks.tex}}{lst:pstricksbefore} + \end{minipage}\hfill + \begin{minipage}{.45\linewidth} + \cmhlistingsfromfile{demonstrations/pstricks-default.tex}{\texttt{pstricks.tex} default output}{lst:pstricksafter} + \end{minipage}% + \end{widepage} + diff --git a/support/latexindent/documentation/sec-fine-tuning.tex b/support/latexindent/documentation/sec-fine-tuning.tex new file mode 100644 index 0000000000..9a2cbbb5c8 --- /dev/null +++ b/support/latexindent/documentation/sec-fine-tuning.tex @@ -0,0 +1,109 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\section{Fine tuning}\label{sec:finetuning} + \texttt{latexindent.pl} operates by looking for the code blocks detailed in + \vref{tab:code-blocks}. + \announce*{2019-07-13}{details of fine tuning of code blocks} The fine tuning of the details of such code blocks + is controlled by the \texttt{fineTuning} field, detailed in \cref{lst:fineTuning}. + + This field is for those that would like to peek under the bonnet/hood and make some fine + tuning to \texttt{latexindent.pl}'s operating. + + \begin{warning} + Making changes to the fine tuning may have significant consequences for your indentation scheme, + proceed with caution! + \end{warning} + + \begin{widepage} + \cmhlistingsfromfile*[style=fineTuning]*{../defaultSettings.yaml}[width=0.95\linewidth,before=\centering,yaml-TCB]{\texttt{fineTuning}}{lst:fineTuning} + \end{widepage} + + The fields given in \cref{lst:fineTuning} are all \emph{regular expressions}. This manual is + not intended to be a tutorial on regular expressions; you might like to read, for + example, \cite{masteringregexp} for a detailed covering of the topic. + + We make the following comments with reference to \cref{lst:fineTuning}: + \begin{enumerate} + \item the \texttt{environments:name} field details that the \emph{name} of an + environment can contain: + \begin{enumerate} + \item \texttt{a-z} lower case letters + \item \texttt{A-Z} upper case letters + \item \texttt{@} the \texttt{@} 'letter' + \item \lstinline!\*! stars + \item \texttt{0-9} numbers + \item \lstinline!_! underscores + \item \lstinline!\! backslashes + \end{enumerate} + The \texttt{+} at the end means \emph{at least one} of the above + characters. + \item the \texttt{ifElseFi:name} field: + \begin{enumerate} + \item \lstinline^@?^ means that it \emph{can possibly} begin with + \lstinline^@^ + \item followed by \texttt{if} + \item followed by 0 or more characters from \texttt{a-z}, \texttt{A-Z} and + \texttt{@} + \item the \texttt{?} the end means \emph{non-greedy}, which means `stop the + match as soon as possible' + \end{enumerate} + \item the \texttt{keyEqualsValuesBracesBrackets} contains some interesting syntax: + \begin{enumerate} + \item \lstinline!|! means `or' + \item \lstinline^(?:(?<!\\)\{)^ the \lstinline^(?:...)^ uses a \emph{non-capturing} group -- + you don't necessarily need to worry about what this means, but just know that for the + \texttt{fineTuning} feature you should only ever use \emph{non}-capturing + groups, and \emph{not} capturing groups, which are simply + \lstinline!(...)! + \item \lstinline^(?<!\\)\{)^ means a \lstinline^{^ but it can \emph{not} + be immediately preceded by a \lstinline!\! + \end{enumerate} + \item in the \texttt{arguments:before} field + \begin{enumerate} + \item \lstinline^\d\h*^ means a digit (i.e. a number), followed by 0 or more horizontal + spaces + \item \lstinline^;?,?^ means \emph{possibly} a semi-colon, and possibly a comma + \item \lstinline^\<.*?\>^ is designed for 'beamer'-type commands; the + \lstinline^.*?^ means anything in between \lstinline^<...>^ + \end{enumerate} + \item the \texttt{modifyLineBreaks} field refers to fine tuning settings detailed in + \vref{sec:modifylinebreaks}. In particular: + \begin{enumerate} + \item \texttt{betterFullStop} is in relation to the one sentence per line routine, detailed in + \vref{sec:onesentenceperline} + \item \texttt{doubleBackSlash} is in relation to the \texttt{DBSStartsOnOwnLine} and + \texttt{DBSFinishesWithLineBreak} polyswitches surrounding double back slashes, see + \vref{subsec:dbs} + \item \texttt{comma} is in relation to the \texttt{CommaStartsOnOwnLine} and + \texttt{CommaFinishesWithLineBreak} polyswitches surrounding commas in optional and mandatory + arguments; see \vref{tab:poly-switch-mapping} + \end{enumerate} + \end{enumerate} + + It is not obvious from \cref{lst:fineTuning}, but each of the \texttt{follow}, + \texttt{before} and \texttt{between} fields allow trailing comments, line + breaks, and horizontal spaces between each character. + + As a demonstration, consider the file given in \cref{lst:finetuning1}, together with its + default output using the command + \begin{commandshell} +latexindent.pl finetuning1.tex +\end{commandshell} + is given in \cref{lst:finetuning1-default}. + + \begin{cmhtcbraster}[raster column skip=.01\linewidth] + \cmhlistingsfromfile*{demonstrations/finetuning1.tex}{\texttt{finetuning1.tex}}{lst:finetuning1} + \cmhlistingsfromfile*{demonstrations/finetuning1-default.tex}{\texttt{finetuning1.tex} default}{lst:finetuning1-default} + \end{cmhtcbraster} + + It's clear from \cref{lst:finetuning1-default} that the indentation scheme has not worked as + expected. We can \emph{fine tune} the indentation scheme by employing the settings + given in \cref{lst:fine-tuning1} and running the command + \begin{commandshell} +latexindent.pl finetuning1.tex -l=fine-tuning1.yaml +\end{commandshell} + and the associated (desired) output is given in \cref{lst:finetuning1-mod1}. + + \begin{cmhtcbraster}[raster column skip=.01\linewidth] + \cmhlistingsfromfile*{demonstrations/finetuning1-mod1.tex}{\texttt{finetuning1.tex} using \cref{lst:fine-tuning1}}{lst:finetuning1-mod1} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/fine-tuning1.yaml}[yaml-TCB]{\texttt{finetuning1.yaml}}{lst:fine-tuning1} + \end{cmhtcbraster} diff --git a/support/latexindent/documentation/sec-how-to-use.tex b/support/latexindent/documentation/sec-how-to-use.tex new file mode 100644 index 0000000000..bdae2b428e --- /dev/null +++ b/support/latexindent/documentation/sec-how-to-use.tex @@ -0,0 +1,391 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\section{How to use the script} + \texttt{latexindent.pl} ships as part of the \TeX Live distribution for + Linux and Mac users; \texttt{latexindent.exe} ships as part of the \TeX Live and + MiK\TeX{} distributions for Windows users. These files are also available + from github \cite{latexindent-home} should you wish to use them without a + \TeX{} distribution; in this case, you may like to read + \vref{sec:updating-path} which details how the \texttt{path} variable can be + updated. + + In what follows, we will always refer to \texttt{latexindent.pl}, but depending on your + operating system and preference, you might substitute \texttt{latexindent.exe} or simply + \texttt{latexindent}. + + There are two ways to use \texttt{latexindent.pl}: from the command line, and using + \texttt{arara}; we discuss these in \cref{sec:commandline} and + \cref{sec:arara} respectively. We will discuss how to change the settings and + behaviour of the script in \vref{sec:defuseloc}. + + \texttt{latexindent.pl} ships with \texttt{latexindent.exe} for Windows + users, so that you can use the script with or without a Perl distribution. If you plan to + use \texttt{latexindent.pl} (i.e, the original Perl script) then you will need a few standard Perl modules -- see + \vref{sec:requiredmodules} for details;% + \announce{2018-01-13}{perl module installer helper script} in particular, note that a module installer helper script is + shipped with \texttt{latexindent.pl}. + +\subsection{From the command line}\label{sec:commandline} + \texttt{latexindent.pl} has a number of different switches/flags/options, which + can be combined in any way that you like, either in short or long form as detailed below. + \texttt{latexindent.pl} produces a \texttt{.log} file, \texttt{indent.log}, + every time it is run; the name of the log file can be customised, but we will refer to + the log file as \texttt{indent.log} throughout this document. There is a base of + information that is written to \texttt{indent.log}, but other additional information + will be written depending on which of the following options are used. + +\flagbox{-v, --version} + \announce{2017-06-25}{version} + \begin{commandshell} +latexindent.pl -v +\end{commandshell} + This will output only the version number to the terminal. + +\flagbox{-h, --help} + + \begin{commandshell} +latexindent.pl -h +\end{commandshell} + + As above this will output a welcome message to the terminal, including the version number + and available options. + \begin{commandshell} +latexindent.pl myfile.tex +\end{commandshell} + + This will operate on \texttt{myfile.tex}, but will simply output to your terminal; + \texttt{myfile.tex} will not be changed by \texttt{latexindent.pl} in any way using + this command. + +\flagbox{-w, --overwrite} + \begin{commandshell} +latexindent.pl -w myfile.tex +latexindent.pl --overwrite myfile.tex +latexindent.pl myfile.tex --overwrite +\end{commandshell} + + This \emph{will} overwrite \texttt{myfile.tex}, but it will make a copy of + \texttt{myfile.tex} first. You can control the name of the extension (default is + \texttt{.bak}), and how many different backups are made -- more on this in + \cref{sec:defuseloc}, and in particular see \texttt{backupExtension} and + \texttt{onlyOneBackUp}. + + Note that if \texttt{latexindent.pl} can not create the backup, then it will exit without + touching your original file; an error message will be given asking you to check the + permissions of the backup file. + +\flagbox{-o=output.tex,--outputfile=output.tex} + \begin{commandshell} +latexindent.pl -o=output.tex myfile.tex +latexindent.pl myfile.tex -o=output.tex +latexindent.pl --outputfile=output.tex myfile.tex +latexindent.pl --outputfile output.tex myfile.tex +\end{commandshell} + + This will indent \texttt{myfile.tex} and output it to \texttt{output.tex}, + overwriting it (\texttt{output.tex}) if it already exists\footnote{Users of version 2.* should + note the subtle change in syntax}. Note + that if \texttt{latexindent.pl} is called with both the \texttt{-w} and + \texttt{-o} switches, then \texttt{-w} will be ignored and + \texttt{-o} will take priority (this seems safer than the other way round). + + Note that using \texttt{-o} as above is equivalent to using + \begin{commandshell} +latexindent.pl myfile.tex > output.tex +\end{commandshell} + + You can call the \texttt{-o} switch with the name of the output file + \emph{without} an extension; in% + \announce{2017-06-25}{upgrade to -o switch} this case, \texttt{latexindent.pl} will use the extension from the + original file. For example, the following two calls to \texttt{latexindent.pl} are + equivalent: + \begin{commandshell} +latexindent.pl myfile.tex -o=output +latexindent.pl myfile.tex -o=output.tex +\end{commandshell} + + You can call the \texttt{-o} switch using a \texttt{+} symbol at + the beginning; this will% + \announce{2017-06-25}{+ sign in o switch} + concatenate the name of the input file and the text given to the \texttt{-o} + switch. For example, the following two calls to \texttt{latexindent.pl} are equivalent: + \begin{commandshell} +latexindent.pl myfile.tex -o=+new +latexindent.pl myfile.tex -o=myfilenew.tex +\end{commandshell} + + You can call the \texttt{-o} switch using a \texttt{++} symbol at + the end of the name% + \announce{2017-06-25}{++ in o switch} of your output + file; this tells \texttt{latexindent.pl} to search successively for the name of your + output file concatenated with $0, 1, \ldots$ while the name of the output file + exists. For example, + \begin{commandshell} +latexindent.pl myfile.tex -o=output++ +\end{commandshell} + tells \texttt{latexindent.pl} to output to \texttt{output0.tex}, but if it exists then + output to \texttt{output1.tex}, and so on. + + Calling \texttt{latexindent.pl} with simply + \begin{commandshell} +latexindent.pl myfile.tex -o=++ +\end{commandshell} + tells it to output to \texttt{myfile0.tex}, but if it exists then output to + \texttt{myfile1.tex} and so on. + + The \texttt{+} and \texttt{++} feature of the + \texttt{-o} switch can be combined; for example, calling + \begin{commandshell} +latexindent.pl myfile.tex -o=+out++ +\end{commandshell} + tells \texttt{latexindent.pl} to output to \texttt{myfileout0.tex}, but if it exists, then + try \texttt{myfileout1.tex}, and so on. + + There is no need to specify a file extension when using the \texttt{++} + feature, but if you wish to, then you should include it \emph{after} the + \texttt{++} symbols, for example + \begin{commandshell} +latexindent.pl myfile.tex -o=+out++.tex +\end{commandshell} + + See \vref{app:differences} for details of how the interface has changed from Version 2.2 + to Version 3.0 for this flag. +\flagbox{-s, --silent} + \begin{commandshell} +latexindent.pl -s myfile.tex +latexindent.pl myfile.tex -s +\end{commandshell} + + Silent mode: no output will be given to the terminal. + +\flagbox{-t, --trace} + \begin{commandshell} +latexindent.pl -t myfile.tex +latexindent.pl myfile.tex -t +\end{commandshell} + + \label{page:traceswitch} + Tracing mode: verbose output will be given to \texttt{indent.log}. This is useful if + \texttt{latexindent.pl} has made a mistake and you're trying to find out where and why. + You might also be interested in learning about \texttt{latexindent.pl}'s thought process + -- if so, this switch is for you, although it should be noted that, especially for large + files, this does affect performance of the script. + +\flagbox{-tt, --ttrace} + \begin{commandshell} +latexindent.pl -tt myfile.tex +latexindent.pl myfile.tex -tt +\end{commandshell} + + \emph{More detailed} tracing mode: this option gives more details to + \texttt{indent.log} + than the standard \texttt{trace} option (note that, even more so than with + \texttt{-t}, especially for large files, performance of the script will be + affected). + +\flagbox{-l, --local[=myyaml.yaml,other.yaml,...]} + \begin{commandshell} +latexindent.pl -l myfile.tex +latexindent.pl -l=myyaml.yaml myfile.tex +latexindent.pl -l myyaml.yaml myfile.tex +latexindent.pl -l first.yaml,second.yaml,third.yaml myfile.tex +latexindent.pl -l=first.yaml,second.yaml,third.yaml myfile.tex +latexindent.pl myfile.tex -l=first.yaml,second.yaml,third.yaml +\end{commandshell} + + \label{page:localswitch} + \texttt{latexindent.pl} will always load \texttt{defaultSettings.yaml} (rhymes with camel) + and if it is called with the \texttt{-l} switch and it finds + \texttt{localSettings.yaml} in the same directory as \texttt{myfile.tex} then these + settings will be added to the indentation scheme. Information will be given in + \texttt{indent.log} on the success or failure of loading \texttt{localSettings.yaml}. + + The \texttt{-l} flag can take an \emph{optional} parameter which + details the name (or names separated by commas) of a YAML file(s) that resides in the + same directory as \texttt{myfile.tex}; you can use this option if you would like to + load a settings file in the current working directory that is \emph{not} + called \texttt{localSettings.yaml}.% + \announce{2017-08-21}*{-l switch absolute paths} + In fact, you can specify both \emph{relative} and \emph{absolute paths} for + your YAML files; for example + \begin{commandshell} +latexindent.pl -l=../../myyaml.yaml myfile.tex +latexindent.pl -l=/home/cmhughes/Desktop/myyaml.yaml myfile.tex +latexindent.pl -l=C:\Users\cmhughes\Desktop\myyaml.yaml myfile.tex +\end{commandshell} + You will find a lot of other explicit demonstrations of how to use the + \texttt{-l} switch throughout this documentation, + + You can call the \texttt{-l} switch with a `+' symbol either before or after + \announce{2017-06-25}{+ sign with -l switch} another YAML file; for example: + \begin{commandshell} +latexindent.pl -l=+myyaml.yaml myfile.tex +latexindent.pl -l "+ myyaml.yaml" myfile.tex +latexindent.pl -l=myyaml.yaml+ myfile.tex +\end{commandshell} + which translate, respectively, to + \begin{commandshell} +latexindent.pl -l=localSettings.yaml,myyaml.yaml myfile.tex +latexindent.pl -l=localSettings.yaml,myyaml.yaml myfile.tex +latexindent.pl -l=myyaml.yaml,localSettings.yaml myfile.tex +\end{commandshell} + Note that the following is \emph{not} allowed: + \begin{commandshell} +latexindent.pl -l+myyaml.yaml myfile.tex +\end{commandshell} + and + \begin{commandshell} +latexindent.pl -l + myyaml.yaml myfile.tex +\end{commandshell} + will \emph{only} load \texttt{localSettings.yaml}, and \texttt{myyaml.yaml} + will be ignored. If you wish to use spaces between any of the YAML settings, then you + must wrap the entire list of YAML files in quotes, as demonstrated above. + + You may also choose to omit the \texttt{yaml} extension, such as + \announce{2017-06-25}{no extension for -l switch} + \begin{commandshell} +latexindent.pl -l=localSettings,myyaml myfile.tex +\end{commandshell} +\flagbox{-y, --yaml=yaml settings} + \begin{commandshell} +latexindent.pl myfile.tex -y="defaultIndent: ' '" +latexindent.pl myfile.tex -y="defaultIndent: ' ',maximumIndentation:' '" +latexindent.pl myfile.tex -y="indentRules: one: '\t\t\t\t'" +latexindent.pl myfile.tex -y='modifyLineBreaks:environments:EndStartsOnOwnLine:3' -m +latexindent.pl myfile.tex -y='modifyLineBreaks:environments:one:EndStartsOnOwnLine:3' -m +\end{commandshell} + \label{page:yamlswitch}You% + \announce{2017-08-21}{the -y switch} can specify YAML settings from the command line using the + \texttt{-y} or \texttt{--yaml} switch; + sample demonstrations are given above. Note, in particular, that multiple settings can + be specified by separating them via commas. There is a further option to use a + \texttt{;} to separate fields, which is demonstrated in + \vref{sec:yamlswitch}. + + Any settings specified via this switch will be loaded \emph{after} any + specified using the \texttt{-l} switch. This is discussed further in + \vref{sec:loadorder}. +\flagbox{-d, --onlydefault} + \begin{commandshell} +latexindent.pl -d myfile.tex +\end{commandshell} + + Only \texttt{defaultSettings.yaml}: you might like to read \cref{sec:defuseloc} before using + this switch. By default, \texttt{latexindent.pl} will always search for + \texttt{indentconfig.yaml} or \texttt{.indentconfig.yaml} in your home directory. If you would + prefer it not to do so then (instead of deleting or renaming \texttt{indentconfig.yaml} or + \texttt{.indentconfig.yaml}) you can simply call the script with the \texttt{-d} + switch; note that this will also tell the script to ignore \texttt{localSettings.yaml} even + if it has been called with the \texttt{-l} switch; \texttt{latexindent.pl}% + \announce{2017-08-21}*{updated -d switch} will also ignore any settings specified from the + \texttt{-y} switch. + +\flagbox{-c, --cruft=<directory>} + \begin{commandshell} +latexindent.pl -c=/path/to/directory/ myfile.tex +\end{commandshell} + + If you wish to have backup files and \texttt{indent.log} written to a directory other + than the current working directory, then you can send these `cruft' files to another + directory. % this switch was made as a result of http://tex.stackexchange.com/questions/142652/output-latexindent-auxiliary-files-to-a-different-directory + +\flagbox{-g, --logfile=<name of log file>} + \begin{commandshell} +latexindent.pl -g=other.log myfile.tex +latexindent.pl -g other.log myfile.tex +latexindent.pl --logfile other.log myfile.tex +latexindent.pl myfile.tex -g other.log +\end{commandshell} + + By default, \texttt{latexindent.pl} reports information to \texttt{indent.log}, but + if you wish to change the name of this file, simply call the script with your chosen name + after the \texttt{-g} switch as demonstrated above. + +\flagbox{-sl, --screenlog} + \begin{commandshell} +latexindent.pl -sl myfile.tex +latexindent.pl -screenlog myfile.tex +\end{commandshell} + Using this% + \announce{2018-01-13}{screenlog switch created} option tells + \texttt{latexindent.pl} to output the log file to the screen, as well as to your chosen + log file. + +\flagbox{-m, --modifylinebreaks} + \begin{commandshell} +latexindent.pl -m myfile.tex +latexindent.pl -modifylinebreaks myfile.tex +\end{commandshell} + + One of the most exciting developments in Version~3.0 is the ability to modify line + breaks; for full details see \vref{sec:modifylinebreaks} + + \texttt{latexindent.pl} can also be called on a file without the file extension, for + example + \begin{commandshell} +latexindent.pl myfile +\end{commandshell} + and in which case, you can specify the order in which extensions are searched for; see + \vref{lst:fileExtensionPreference} for full details. +\flagbox{STDIN} + \begin{commandshell} +cat myfile.tex | latexindent.pl +\end{commandshell} + \texttt{latexindent.pl} will% + \announce{2018-01-13}{STDIN allowed} allow input from STDIN, which means that you can pipe output from + other commands directly into the script. For example assuming that you have content in + \texttt{myfile.tex}, then the above command will output the results of operating upon + \texttt{myfile.tex} + + Similarly, if you% + \announce{2018-01-13}*{no options/filename updated} simply type + \texttt{latexindent.pl} at the command line, then it will expect (STDIN) input from the + command line. + \begin{commandshell} +latexindent.pl +\end{commandshell} + + Once you have finished typing your input, you can press + \begin{itemize} + \item \texttt{CTRL+D} on Linux + \item \texttt{CTRL+Z} followed by \texttt{ENTER} on Windows + \end{itemize} + to signify that your input has finished. Thanks to \cite{xu-cheng} for an update + to this feature. +\flagbox{-r, --replacement} + \begin{commandshell} +latexindent.pl -r myfile.tex +latexindent.pl -replacement myfile.tex +\end{commandshell} + You can% + \announce*{2019-07-13}{replacement mode switch} call + \texttt{latexindent.pl} with the \texttt{-r} switch to instruct it to perform + replacements/substitutions on your file; full details and examples are given in + \vref{sec:replacements}. + +\flagbox{-rv, --replacementrespectverb} + \begin{commandshell} +latexindent.pl -rv myfile.tex +latexindent.pl -replacementrespectverb myfile.tex +\end{commandshell} + You can% + \announce*{2019-07-13}{replacement mode switch, respecting verbatim} instruct + \texttt{latexindent.pl} to perform replacements/substitutions by using the + \texttt{-rv} switch, but will \emph{respect verbatim code blocks}; full details and + examples are given in \vref{sec:replacements}. + +\flagbox{-rr, --onlyreplacement} + \begin{commandshell} +latexindent.pl -rr myfile.tex +latexindent.pl -onlyreplacement myfile.tex +\end{commandshell} + You can% + \announce*{2019-07-13}{replacement (only) mode switch} instruct + \texttt{latexindent.pl} to skip all of its other indentation operations and + \emph{only} perform replacements/substitutions by using the + \texttt{-rr} switch; full details and examples are given in + \vref{sec:replacements}. + +\subsection{From arara}\label{sec:arara} + Using \texttt{latexindent.pl} from the command line is fine for some folks, but others + may find it easier to use from \texttt{arara}; you can find the arara rule for + \texttt{latexindent.pl} and its associated documentation at \cite{paulo}. diff --git a/support/latexindent/documentation/sec-indent-config-and-settings.tex b/support/latexindent/documentation/sec-indent-config-and-settings.tex new file mode 100644 index 0000000000..cb551194c0 --- /dev/null +++ b/support/latexindent/documentation/sec-indent-config-and-settings.tex @@ -0,0 +1,179 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\section{indentconfig.yaml, local settings and the -y switch }\label{sec:indentconfig} + The behaviour of \texttt{latexindent.pl} is controlled from the settings specified in any + of the YAML files that you tell it to load. By default, \texttt{latexindent.pl} will only + load \texttt{defaultSettings.yaml}, but there are a few ways that you can tell it to load your + own settings files. + +\subsection{indentconfig.yaml and .indentconfig.yaml} + \texttt{latexindent.pl} will always check your home directory for \texttt{indentconfig.yaml} + and \texttt{.indentconfig.yaml} (unless + it is called with the \texttt{-d} switch), which is a plain text file you can create that contains the + \emph{absolute} paths for any settings files that you wish \texttt{latexindent.pl} + to load. There is no difference between \texttt{indentconfig.yaml} and + \texttt{.indentconfig.yaml}, other than the fact that \texttt{.indentconfig.yaml} is a `hidden' + file; thank you to \cite{jacobo-diaz-hidden-config} for providing this feature. In what follows, we + will use \texttt{indentconfig.yaml}, but it is understood that this could equally represent + \texttt{.indentconfig.yaml}. If you have both files in existence then \texttt{indentconfig.yaml} + takes priority. + + For Mac and Linux users, their home directory is \texttt{~/username} while Windows + (Vista onwards) is \lstinline!C:\Users\username!\footnote{If you're not sure + where to put \texttt{indentconfig.yaml}, don't + worry \texttt{latexindent.pl} will tell you in the log file exactly where to + put it assuming it doesn't exist already.} \Cref{lst:indentconfig} + shows a sample \texttt{indentconfig.yaml} file. + + \begin{yaml}{\texttt{indentconfig.yaml} (sample)}{lst:indentconfig} +# Paths to user settings for latexindent.pl +# +# Note that the settings will be read in the order you +# specify here- each successive settings file will overwrite +# the variables that you specify + +paths: +- /home/cmhughes/Documents/yamlfiles/mysettings.yaml +- /home/cmhughes/folder/othersettings.yaml +- /some/other/folder/anynameyouwant.yaml +- C:\Users\chughes\Documents\mysettings.yaml +- C:\Users\chughes\Desktop\test spaces\more spaces.yaml +\end{yaml} + + Note that the \texttt{.yaml} files you specify in \texttt{indentconfig.yaml} will be + loaded in the order in which you write them. Each file doesn't have to have every switch + from \texttt{defaultSettings.yaml}; in fact, I recommend that you only keep the switches that + you want to \emph{change} in these settings files. + + To get started with your own settings file, you might like to save a copy of + \texttt{defaultSettings.yaml} in another directory and call it, for example, + \texttt{mysettings.yaml}. Once you have added the path to \texttt{indentconfig.yaml} you can + change the switches and add more code-block names to it as you see fit -- have a look at + \cref{lst:mysettings} for an example that uses four tabs for the default indent, adds + the \texttt{tabbing} environment/command to the list of environments that contains + alignment delimiters; you might also like to refer to the many YAML files detailed + throughout the rest of this documentation. + + \begin{yaml}{\texttt{mysettings.yaml} (example)}{lst:mysettings} +# Default value of indentation +defaultIndent: "\t\t\t\t" + +# environments that have tab delimiters, add more +# as needed +lookForAlignDelims: + tabbing: 1 +\end{yaml} + + You can make sure that your settings are loaded by checking \texttt{indent.log} for + details -- if you have specified a path that \texttt{latexindent.pl} doesn't recognise + then you'll get a warning, otherwise you'll get confirmation that \texttt{latexindent.pl} + has read your settings file \footnote{Windows users + may find that they have to end \texttt{.yaml} files with a blank line}. + + \begin{warning} + When editing \texttt{.yaml} files it is \emph{extremely} important + to remember how sensitive they are to spaces. I highly recommend copying + and pasting from \texttt{defaultSettings.yaml} when you create your + first \texttt{whatevernameyoulike.yaml} file. + + If \texttt{latexindent.pl} can not read your \texttt{.yaml} file it + will tell you so in \texttt{indent.log}. + \end{warning} + +\subsection{localSettings.yaml}\label{sec:localsettings} + The \texttt{-l} switch tells \texttt{latexindent.pl} to look for + \texttt{localSettings.yaml} in the \emph{same directory} as \texttt{myfile.tex}. For + example, if you use the following command + \begin{commandshell} +latexindent.pl -l myfile.tex +\end{commandshell} + then \texttt{latexindent.pl} will (assuming it exists) load \texttt{localSettings.yaml} from + the same directory as \texttt{myfile.tex}. + + If you'd prefer to name your \texttt{localSettings.yaml} file something different, (say, + \texttt{mysettings.yaml} as in \cref{lst:mysettings}) then you can call + \texttt{latexindent.pl} using, for example, + \begin{commandshell} +latexindent.pl -l=mysettings.yaml myfile.tex +\end{commandshell} + + Any settings file(s) specified using the \texttt{-l} switch will be read + \emph{after} \texttt{defaultSettings.yaml} and, assuming they exist, any user setting + files specified in \texttt{indentconfig.yaml}. + + Your settings file can contain any switches that you'd like to change; a sample is shown + in \cref{lst:localSettings}, and you'll find plenty of further examples throughout this + manual. + + \begin{yaml}{\texttt{localSettings.yaml} (example)}{lst:localSettings} +# verbatim environments - environments specified +# here will not be changed at all! +verbatimEnvironments: + cmhenvironment: 0 + myenv: 1 +\end{yaml} + + You can make sure that your settings file has been loaded by checking + \texttt{indent.log} for details; if it can not be read then you receive a warning, + otherwise you'll get confirmation that \texttt{latexindent.pl} has read your settings + file. + +\subsection{The -y|yaml switch}\label{sec:yamlswitch} + You% + \announce{2017-08-21}{demonstration of the -y switch} may use the + \texttt{-y} switch to load your settings; for example, if you wished to + specify the settings from \cref{lst:localSettings} using the \texttt{-y} switch, + then you could use the following command: + \begin{commandshell} +latexindent.pl -y="verbatimEnvironments:cmhenvironment:0;myenv:1" myfile.tex +\end{commandshell} + Note the use of \texttt{;} to specify another field within + \texttt{verbatimEnvironments}. This is shorthand, and equivalent, to using the following + command: + \begin{commandshell} +latexindent.pl -y="verbatimEnvironments:cmhenvironment:0,verbatimEnvironments:myenv:1" myfile.tex +\end{commandshell} + You may, of course, specify settings using the \texttt{-y} switch as well as, + for example, settings loaded using the \texttt{-l} switch; for example, + \begin{commandshell} +latexindent.pl -l=mysettings.yaml -y="verbatimEnvironments:cmhenvironment:0;myenv:1" myfile.tex +\end{commandshell} + Any settings specified using the \texttt{-y} switch will be loaded + \emph{after} any specified using \texttt{indentconfig.yaml} and the + \texttt{-l} switch. + + If you wish to specify any regex-based settings using the \texttt{-y} switch, + it is important not to use quotes surrounding the regex; for example, with reference to + the `one sentence per line' feature (\vref{sec:onesentenceperline}) and the listings within + \vref{lst:sentencesEndWith}, the following settings give the option to have sentences end + with a semicolon + \begin{commandshell} +latexindent.pl -m --yaml='modifyLineBreaks:oneSentencePerLine:sentencesEndWith:other:\;' +\end{commandshell} + +\subsection{Settings load order}\label{sec:loadorder} + \texttt{latexindent.pl} loads the settings files in the following order: + \begin{enumerate} + \item \texttt{defaultSettings.yaml} is always loaded, and can not be renamed; + \item \texttt{anyUserSettings.yaml} and any other arbitrarily-named files specified in + \texttt{indentconfig.yaml}; + \item \texttt{localSettings.yaml} but only if found in the same directory as \texttt{myfile.tex} + and called with \texttt{-l} switch; this file can be renamed, provided that + the call to \texttt{latexindent.pl} is adjusted accordingly (see \cref{sec:localsettings}). + You may specify both relative and absolute% + \announce{2017-08-21}*{-l absolute paths} paths to other YAML files using the \texttt{-l} switch, + separating multiple files using commas; + \item any settings% + \announce{2017-08-21}{-y switch load order} specified in the + \texttt{-y} switch. + \end{enumerate} + A visual representation of this is given in \cref{fig:loadorder}. + + \begin{figure}[!htb] + \centering + \input{figure-schematic} + \caption{Schematic of the load order described in \cref{sec:loadorder}; solid lines represent + mandatory files, dotted lines represent optional files. \texttt{indentconfig.yaml} can + contain as many files as you like. The files will be loaded in order; if you specify + settings for the same field in more than one file, the most recent takes priority. } + \label{fig:loadorder} + \end{figure} diff --git a/support/latexindent/documentation/sec-introduction.tex b/support/latexindent/documentation/sec-introduction.tex new file mode 100644 index 0000000000..c6fafd9269 --- /dev/null +++ b/support/latexindent/documentation/sec-introduction.tex @@ -0,0 +1,130 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\section{Introduction} +\subsection{Thanks} + I first created \texttt{latexindent.pl} to help me format chapter files in a big project. + After I blogged about it on the \TeX{} stack exchange + \cite{cmhblog} I received some positive feedback and follow-up feature requests. + A big thank you to Harish Kumar \cite{harish} who helped to develop and test the + initial versions of the script. + + The \texttt{YAML}-based interface of \texttt{latexindent.pl} was inspired by the + wonderful \texttt{arara} tool; any similarities are deliberate, and I hope that + it is perceived as the compliment that it is. Thank you to Paulo Cereda and the team for + releasing this awesome tool; I initially worried that I was going to have to make a GUI + for \texttt{latexindent.pl}, but the release of \texttt{arara} has meant there is + no need. + + There have been several contributors to the project so far (and hopefully more in the + future!); thank you very much to the people detailed in \vref{sec:contributors} for their + valued contributions, and thank you to those who report bugs and request features at + \cite{latexindent-home}. + +\subsection{License} + \texttt{latexindent.pl} is free and open source, and it always will be; it + is released under the GNU General Public License v3.0. + + Before you start using it on any important files, bear in mind that + \texttt{latexindent.pl} has the option to overwrite your \texttt{.tex} files. It + will always make at least one backup (you can choose how many it makes, see + \cpageref{page:onlyonebackup}) but you should still be careful when using it. The script has + been tested on many files, but there are some known limitations (see + \cref{sec:knownlimitations}). You, the user, are responsible for ensuring that you maintain + backups of your files before running \texttt{latexindent.pl} on them. I think it is + important at this stage to restate an important part of the license here: + \begin{quote}\itshape + 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. + \end{quote} + There is certainly no malicious intent in releasing this script, and I do hope that it + works as you expect it to; if it does not, please first of all make sure that you have + the correct settings, and then feel free to let me know at \cite{latexindent-home} with a + complete minimum working example as I would like to improve the code as much as possible. + \begin{warning} + Before you try the script on anything important (like your thesis), test it + out on the sample files in the \texttt{test-case} directory \cite{latexindent-home}. + \end{warning} + + \emph{If you have used any version 2.* of \texttt{latexindent.pl}, there + are a few changes to the interface; see \vref{app:differences} and the comments + throughout this document for details}. + +\subsection{About this documentation} + As you read through this documentation, you will see many listings; in this version of + the documentation, there are a total of \totallstlistings. This may seem a lot, but I + deem it necessary in presenting the various different options of \texttt{latexindent.pl} + and the associated output that they are capable of producing. + + The different listings are presented using different styles: + + \begin{minipage}{.4\textwidth} + \cmhlistingsfromfile{demonstrations/demo-tex.tex}{\texttt{demo-tex.tex}}{lst:demo-tex} + \end{minipage}% + \hfill + \begin{minipage}{.4\textwidth} + This type of listing is a \texttt{.tex} file. + \end{minipage}% + + \begin{minipage}{.4\textwidth} + \cmhlistingsfromfile[style=fileExtensionPreference]*{../defaultSettings.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{fileExtensionPreference}}{lst:fileExtensionPreference-demo} + \end{minipage}% + \hfill + \begin{minipage}{.4\textwidth} + This type of listing is a \texttt{.yaml} file; when you see line numbers given (as here) + it means that the snippet is taken directly from \texttt{defaultSettings.yaml}, discussed in + detail in \vref{sec:defuseloc}. + \end{minipage}% + + \begin{minipage}{.55\textwidth} + \cmhlistingsfromfile[style=modifylinebreaks]*{../defaultSettings.yaml}[MLB-TCB,width=.85\linewidth,before=\centering]{\texttt{modifyLineBreaks}}{lst:modifylinebreaks-demo} + \end{minipage}% + \hfill + \begin{minipage}{.4\textwidth} + This type of listing is a \texttt{.yaml} file, but it will only + be relevant when the \texttt{-m} switch is active; see \vref{sec:modifylinebreaks} + for more details. + \end{minipage}% + + \begin{minipage}{.55\textwidth} + \cmhlistingsfromfile*[style=replacements]*{../defaultSettings.yaml}[replace-TCB,width=.85\linewidth,before=\centering]{\texttt{replacements}}{lst:replacements-demo} + \end{minipage}% + \hfill + \begin{minipage}{.4\textwidth} + This type of listing is a \texttt{.yaml} file, but it will only + be relevant when the \texttt{-r} switch is active; see \vref{sec:replacements} + for more details. + \end{minipage}% + + % \begin{latexonly} + You will occasionally see dates shown in the margin (for example, next to this + paragraph!)% + \announce{2017-06-25}{announce} which detail the date + of the version in which the feature was implemented; the `N' stands for `new as of the + date shown' and `U' stands for `updated as of the date shown'. If you see \stardemo, it + means that the feature is either new (N) or updated (U) as of the release of the current + version; if you see \stardemo\, attached to a listing, then it means that listing is new + (N) or updated (U) as of the current version. If you have not read this document before + (and even if you have!), then you can ignore every occurrence of the \stardemo; they are + simply there to highlight new and updated features. The new and updated features in this + documentation (\gitRel) are on the following pages: \listOfNewFeatures + % \end{latexonly} + +\subsection{Quick start}\label{sec:quickstart} + If you'd like to get started with \texttt{latexindent.pl} then simply type + \begin{commandshell} +latexindent.pl myfile.tex +\end{commandshell} + from the command line. If you receive an error message such as that given in + \cref{lst:poss-errors}, then you need to install the missing perl modules. + \begin{cmhlistings}[style=tcblatex,language=Perl]{Possible error messages}{lst:poss-errors} +Can't locate File/HomeDir.pm in @INC (@INC contains: /Library/Perl/5.12/darwin-thread-multi-2level /Library/Perl/5.12 /Network/Library/Perl/5.12/darwin-thread-multi-2level /Network/Library/Perl/5.12 /Library/Perl/Updates/5.12.4/darwin-thread-multi-2level /Library/Perl/Updates/5.12.4 /System/Library/Perl/5.12/darwin-thread-multi-2level /System/Library/Perl/5.12 /System/Library/Perl/Extras/5.12/darwin-thread-multi-2level /System/Library/Perl/Extras/5.12 .) at helloworld.pl line 10. +BEGIN failed--compilation aborted at helloworld.pl line 10. +\end{cmhlistings} + \texttt{latexindent.pl} ships with a script to help with this process; if you run the + following script, you should be prompted to install the appropriate modules. + \begin{commandshell} +perl latexindent-module-installer.pl +\end{commandshell} + You might also like to see \href{https://stackoverflow.com/questions/19590042/error-cant-locate-file-homedir-pm-in-inc}{https://stackoverflow.com/questions/19590042/error-cant-locate-file-homedir-pm-in-inc}, for example, as well as + \vref{sec:requiredmodules}. diff --git a/support/latexindent/documentation/sec-replacements.tex b/support/latexindent/documentation/sec-replacements.tex new file mode 100644 index 0000000000..6bc7223d4d --- /dev/null +++ b/support/latexindent/documentation/sec-replacements.tex @@ -0,0 +1,372 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\section{The -r, -rv and -rr switches}\label{sec:replacements} + \fancyhead[R]{\bfseries\thepage% + \tikz[remember picture,overlay] { + \node at (1,0){\includegraphics{logo-bw}}; } + } + + You can instruct \texttt{latexindent.pl} to perform replacements/substitutions on your + \announce*{2019-07-13}{replacement mode switches} + file by using any of the \texttt{-r}, \texttt{-rv} or + \texttt{-rr} switches: + \begin{itemize} + \item the \texttt{-r} switch will perform indentation and replacements, not + respecting verbatim code blocks; + \item the \texttt{-rv} switch will perform indentation and replacements, and + \emph{will} respect verbatim code blocks; + \item the \texttt{-rr} switch will \emph{not} perform indentation, and + will perform replacements not respecting verbatim code blocks. + \end{itemize} + + We will demonstrate each of the \texttt{-r}, \texttt{-rv} and + \texttt{-rr} switches, but a summary is given in \cref{tab:replacementswitches}. + + \begin{table}[!htb] + \centering + \caption{The replacement mode switches}\label{tab:replacementswitches} + \begin{tabular}{rcc} + \toprule + switch & indentation? & respect verbatim? \\ + \midrule + \texttt{-r} & \faCheck & \faClose \\ + \texttt{-rv} & \faCheck & \faCheck \\ + \texttt{-rr} & \faClose & \faClose \\ + \bottomrule + \end{tabular} + \end{table} + + The default value of the \texttt{replacements} field is shown in + \cref{lst:replacements}; as with all of the other fields, you are encouraged to customise + and change this as you see fit. The options in this field will \emph{only} be + considered if the \texttt{-r}, \texttt{-rv} or + \texttt{-rr} switches are active; when discussing YAML settings related to the + replacement-mode switches, we will use the style given in \cref{lst:replacements}. + + \cmhlistingsfromfile*[style=replacements]*{../defaultSettings.yaml}[width=0.95\linewidth,before=\centering,replace-TCB]{\texttt{replacements}}{lst:replacements} + + The first entry within the \texttt{replacements} field is \texttt{amalgamate}, and + is \emph{optional}; by default it is set to 1, so that replacements will be + amalgamated from each settings file that you specify. As you'll see in the demonstrations + that follow, there is no need to specify this field. + + You'll notice that, by default, there is only \emph{one} entry in the + \texttt{replacements} field, but it can take as many entries as you would like; each + one needs to begin with a \texttt{-} on its own line. + +\subsection{Introduction to replacements} + Let's explore the action of the default settings, and then we'll demonstrate the feature + with further examples. With reference to \cref{lst:replacements}, the default action will + replace every instance of the text \texttt{latexindent.pl} with \texttt{pl.latexindent}. + + Beginning with the code in \cref{lst:replace1} and running the command + \begin{commandshell} +latexindent.pl -r replace1.tex +\end{commandshell} + gives the output given in \cref{lst:replace1-r1}. + + \begin{cmhtcbraster}[raster column skip=.01\linewidth] + \cmhlistingsfromfile*{demonstrations/replace1.tex}{\texttt{replace1.tex}}{lst:replace1} + \cmhlistingsfromfile*{demonstrations/replace1-r1.tex}{\texttt{replace1.tex} default}{lst:replace1-r1} + \end{cmhtcbraster} + + If we don't wish to perform this replacement, then we can tweak the default settings of + \vref{lst:replacements} by changing \texttt{lookForThis} to 0; we perform this action + in \cref{lst:replace1-yaml}, and run the command + \begin{commandshell} +latexindent.pl -r replace1.tex -l=replace1.yaml +\end{commandshell} + which gives the output in \cref{lst:replace1-mod1}. + + \begin{cmhtcbraster}[raster column skip=.01\linewidth] + \cmhlistingsfromfile*{demonstrations/replace1-mod1.tex}{\texttt{replace1.tex} using \cref{lst:replace1-yaml}}{lst:replace1-mod1} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/replace1.yaml}[replace-TCB]{\texttt{replace1.yaml}}{lst:replace1-yaml} + \end{cmhtcbraster} + + Note that in \cref{lst:replace1-yaml} we have specified \texttt{amalgamate} as 0 so + that the default replacements are overwritten. + + We haven't yet discussed the \texttt{when} field; don't worry, we'll get to it + as part of the discussion in what follows. + +\subsection{The two types of replacements} + There are two types of replacements: + \begin{enumerate} + \item \emph{string}-based replacements, which replace the string in + \emph{this} with the string in \emph{that}. + If you specify \texttt{this} and you do not specify \texttt{that}, then + the \texttt{that} field will be assumed to be empty. + \item \emph{regex}-based replacements, which use the \texttt{substitution} field. + \end{enumerate} + We will demonstrate both in the examples that follow. + + \texttt{latexindent.pl} chooses which type of replacement to make based on which fields + have been specified; if the \texttt{this} field is specified, then it will make + \emph{string}-based replacements, regardless of if \texttt{substitution} is + present or not. + +\subsection{Examples of replacements} + \begin{example} + We begin with code given in \cref{lst:colsep} + + \cmhlistingsfromfile*{demonstrations/colsep.tex}{\texttt{colsep.tex}}{lst:colsep} + + Let's assume that our goal is to remove both of the \texttt{arraycolsep} statements; we can achieve this in + a few different ways. + + Using the YAML in \cref{lst:colsep-yaml}, and running the command + \begin{commandshell} +latexindent.pl -r colsep.tex -l=colsep.yaml +\end{commandshell} + then we achieve the output in \cref{lst:colsep-mod0}. + \begin{cmhtcbraster}[raster column skip=.01\linewidth] + \cmhlistingsfromfile*{demonstrations/colsep-mod0.tex}{\texttt{colsep.tex} using \cref{lst:colsep}}{lst:colsep-mod0} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/colsep.yaml}[replace-TCB]{\texttt{colsep.yaml}}{lst:colsep-yaml} + \end{cmhtcbraster} + Note that in \cref{lst:colsep-yaml}, we have specified \emph{two} separate fields, each with their own `\emph{this}' field; + furthermore, for both of the separate fields, we have not specified `\texttt{that}', so the \texttt{that} field + is assumed to be blank by \texttt{latexindent.pl}; + + We can make the YAML in \cref{lst:colsep-yaml} more concise by exploring the \texttt{substitution} field. Using + the settings in \cref{lst:colsep1} and running the command + \begin{commandshell} +latexindent.pl -r colsep.tex -l=colsep1.yaml +\end{commandshell} + then we achieve the output in \cref{lst:colsep-mod1}. + \begin{cmhtcbraster}[raster column skip=.01\linewidth, + raster force size=false, + raster column 1/.style={add to width=-.1\textwidth}] + \cmhlistingsfromfile*{demonstrations/colsep-mod1.tex}{\texttt{colsep.tex} using \cref{lst:colsep1}}{lst:colsep-mod1} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/colsep1.yaml}[replace-TCB,width=0.6\textwidth]{\texttt{colsep1.yaml}}{lst:colsep1} + \end{cmhtcbraster} + + The code given in \cref{lst:colsep1} is an example of a \emph{regular expression}, which we may abbreviate to \emph{regex} + in what follows. This manual is not intended to be + a tutorial on regular expressions; you might like to read, for example, \cite{masteringregexp} for a detailed + covering of the topic. With reference to \cref{lst:colsep1}, we do note the following: + \begin{itemize} + \item the general form of the \texttt{substitution} field is \lstinline!s/regex/replacement/modifiers!. You can + place any regular expression you like within this; + \item we have `escaped' the backslash by using \lstinline!\\! + \item we have used \lstinline!\d+! to represent \emph{at least} one digit + \item the \texttt{s} \emph{modifier} (in the \texttt{sg} at the end of the line) instructs \texttt{latexindent.pl} to + treat your file as one single line; + \item the \texttt{g} \emph{modifier} (in the \texttt{sg} at the end of the line) instructs \texttt{latexindent.pl} to + make the substitution \emph{globally} throughout your file; you might try removing + the \texttt{g} modifier from \cref{lst:colsep1} and observing the + difference in output. + \end{itemize} + You might like to see \href{https://perldoc.perl.org/perlre.html#Modifiers}{https://perldoc.perl.org/perlre.html\#Modifiers} + for details of modifiers; in general, I recommend starting with the \texttt{sg} modifiers for this feature. + \end{example} + + \begin{example} + We'll keep working with the file in \vref{lst:colsep} for this example. + + Using the YAML in \cref{lst:multi-line}, and running the command + \begin{commandshell} +latexindent.pl -r colsep.tex -l=multi-line.yaml +\end{commandshell} + then we achieve the output in \cref{lst:colsep-mod2}. + \begin{cmhtcbraster}[raster column skip=.01\linewidth] + \cmhlistingsfromfile*{demonstrations/colsep-mod2.tex}{\texttt{colsep.tex} using \cref{lst:multi-line}}{lst:colsep-mod2} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/multi-line.yaml}[replace-TCB]{\texttt{multi-line.yaml}}{lst:multi-line} + \end{cmhtcbraster} + With reference to \cref{lst:multi-line}, we have specified a \emph{multi-line} version of \texttt{this} by employing the \emph{literal} + YAML style \lstinline!|-!. See, for example, \href{https://stackoverflow.com/questions/3790454/in-yaml-how-do-i-break-a-string-over-multiple-lines}{https://stackoverflow.com/questions/3790454/in-yaml-how-do-i-break-a-string-over-multiple-lines} + for further options, all of which can be used in your YAML file. + + This is a natural point to explore the \texttt{when} field, specified in \vref{lst:replacements}. This field can take two values: \emph{before} + and \emph{after}, which respectively instruct \texttt{latexindent.pl} to perform the replacements \emph{before} indentation or \emph{after} it. + The default value is \texttt{before}. + + Using the YAML in \cref{lst:multi-line1}, and running the command + \begin{commandshell} +latexindent.pl -r colsep.tex -l=multi-line1.yaml +\end{commandshell} + then we achieve the output in \cref{lst:colsep-mod3}. + \begin{cmhtcbraster}[raster column skip=.01\linewidth] + \cmhlistingsfromfile*{demonstrations/colsep-mod3.tex}{\texttt{colsep.tex} using \cref{lst:multi-line1}}{lst:colsep-mod3} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/multi-line1.yaml}[replace-TCB]{\texttt{multi-line1.yaml}}{lst:multi-line1} + \end{cmhtcbraster} + We note that, because we have specified \texttt{when: after}, that \texttt{latexindent.pl} has not found the string specified + in \cref{lst:multi-line1} within the file in \vref{lst:colsep}. As it has looked for the string within \cref{lst:multi-line1} \emph{after} the indentation has been performed. After + indentation, the string as written in \cref{lst:multi-line1} is no longer part of the file, and has therefore not been replaced. + + As a final note on this example, if you use the \texttt{-rr} switch, as follows, + \begin{commandshell} +latexindent.pl -rr colsep.tex -l=multi-line1.yaml +\end{commandshell} + then the \texttt{when} field is ignored, no indentation is done, and the output is as in \cref{lst:colsep-mod2}. + \end{example} + + \begin{example} + An important part of the substitution routine is in \emph{capture groups}. + + Assuming that we start with + the code in \cref{lst:displaymath}, let's assume that our goal is to replace each occurrence of \lstinline!$$...$$! + with \lstinline!\begin{equation*}...\end{equation*}!. This example is partly motivated by \href{https://tex.stackexchange.com/questions/242150/good-looking-latex-code}{tex stackexchange question 242150}. + + \cmhlistingsfromfile*{demonstrations/displaymath.tex}{\texttt{displaymath.tex}}{lst:displaymath} + + We use the settings in \cref{lst:displaymath1} and run the command + \begin{commandshell} +latexindent.pl -r displaymath.tex -l=displaymath1.yaml +\end{commandshell} + to receive the output given in \cref{lst:displaymath-mod1}. + + \begin{cmhtcbraster}[raster left skip=-3.75cm, + raster right skip=-2cm,] + \cmhlistingsfromfile*{demonstrations/displaymath-mod1.tex}{\texttt{displaymath.tex} using \cref{lst:displaymath1}}{lst:displaymath-mod1} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/displaymath1.yaml}[replace-TCB]{\texttt{displaymath1.yaml}}{lst:displaymath1} + \end{cmhtcbraster} + + A few notes about \cref{lst:displaymath1}: + \begin{enumerate} + \item we have used the \texttt{x} modifier, which allows us to have white space + within the regex; + \item we have used a capture group, \lstinline!(.*?)! which captures the content between + the \lstinline!$$...$$! into the special variable, \lstinline!$1!; + \item we have used the content of the capture group, \lstinline!$1!, in the + replacement text. + \end{enumerate} + See \href{https://perldoc.perl.org/perlre.html#Capture-groups}{https://perldoc.perl.org/perlre.html\#Capture-groups} for a discussion + of capture groups. + + The features of the replacement switches can, of course, be combined with others from the toolkit of \texttt{latexindent.pl}. For example, + we can combine the poly-switches of \vref{sec:poly-switches}, which we do in \cref{lst:equation}; upon running the command + \begin{commandshell} +latexindent.pl -r -m displaymath.tex -l=displaymath1.yaml,equation.yaml +\end{commandshell} + then we receive the output in \cref{lst:displaymath-mod2}. + + \begin{cmhtcbraster}[ + raster force size=false, + raster column 1/.style={add to width=-.1\textwidth}, + raster column skip=.06\linewidth] + \cmhlistingsfromfile*{demonstrations/displaymath-mod2.tex}{\texttt{displaymath.tex} using \cref{lst:displaymath1,lst:equation}}{lst:displaymath-mod2} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/equation.yaml}[MLB-TCB,width=0.55\textwidth]{\texttt{equation.yaml}}{lst:equation} + \end{cmhtcbraster} + \end{example} + + \begin{example} + This example is motivated by \href{https://tex.stackexchange.com/questions/490086/bring-several-lines-together-to-fill-blank-spaces-in-texmaker}{tex stackexchange question 490086}. + We begin with the code in \cref{lst:phrase}. + + \cmhlistingsfromfile*{demonstrations/phrase.tex}{\texttt{phrase.tex}}{lst:phrase} + + Our goal is to make the spacing uniform between the phrases. To achieve this, we employ the settings in \cref{lst:hspace}, + and run the command + \begin{commandshell} +latexindent.pl -r phrase.tex -l=hspace.yaml +\end{commandshell} + which gives the output in \cref{lst:phrase-mod1}. + + \begin{cmhtcbraster} + \cmhlistingsfromfile*{demonstrations/phrase-mod1.tex}{\texttt{phrase.tex} using \cref{lst:hspace}}{lst:phrase-mod1} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/hspace.yaml}[replace-TCB]{\texttt{hspace.yaml}}{lst:hspace} + \end{cmhtcbraster} + + The \lstinline!\h+! setting in \cref{lst:hspace} say to replace \emph{at least one horizontal space} with a single space. + \end{example} + + \begin{example} + We begin with the code in \cref{lst:references}. + + \cmhlistingsfromfile*{demonstrations/references.tex}{\texttt{references.tex}}{lst:references} + + Our goal is to change each reference so that both the text and the reference are contained within one hyperlink. We + achieve this by employing \cref{lst:reference} and running the command + \begin{commandshell} +latexindent.pl -r references.tex -l=reference.yaml +\end{commandshell} + which gives the output in \cref{lst:references-mod1}. + + \cmhlistingsfromfile*{demonstrations/references-mod1.tex}{\texttt{references.tex} using \cref{lst:reference}}{lst:references-mod1} + + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/reference.yaml}[replace-TCB]{\texttt{reference.yaml}}{lst:reference} + + Referencing \cref{lst:reference}, the \lstinline!|! means \emph{or}, we have used \emph{capture groups}, together with an example + of an \emph{optional} pattern, \lstinline!(?:eq)?!. + \end{example} + + \begin{example} + Let's explore the three replacement mode switches (see \vref{tab:replacementswitches}) in the context of + an example that contains a verbatim code block, \cref{lst:verb1}; we will use the settings in \cref{lst:verbatim1-yaml}. + + \begin{cmhtcbraster} + \cmhlistingsfromfile*{demonstrations/verb1.tex}{\texttt{verb1.tex}}{lst:verb1} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/verbatim1.yaml}[replace-TCB]{\texttt{verbatim1.yaml}}{lst:verbatim1-yaml} + \end{cmhtcbraster} + + Upon running the following commands, + \begin{commandshell} +latexindent.pl -r verb1.tex -l=verbatim1.yaml -o=+mod1 +latexindent.pl -rv verb1.tex -l=verbatim1.yaml -o=+-rv-mod1 +latexindent.pl -rr verb1.tex -l=verbatim1.yaml -o=+-rr-mod1 +\end{commandshell} + we receive the respective output in \crefrange{lst:verb1-mod1}{lst:verb1-rr-mod1} + + \begin{cmhtcbraster}[raster columns=3, + raster left skip=-3.75cm, + raster right skip=-2cm,] + \cmhlistingsfromfile*{demonstrations/verb1-mod1.tex}{\texttt{verb1-mod1.tex}}{lst:verb1-mod1} + \cmhlistingsfromfile*{demonstrations/verb1-rv-mod1.tex}{\texttt{verb1-rv-mod1.tex}}{lst:verb1-rv-mod1} + \cmhlistingsfromfile*{demonstrations/verb1-rr-mod1.tex}{\texttt{verb1-rr-mod1.tex}}{lst:verb1-rr-mod1} + \end{cmhtcbraster} + \end{example} + + We note that: + \begin{enumerate} + \item in \cref{lst:verb1-mod1} indentation has been performed, and that the replacements + specified in \cref{lst:verbatim1-yaml} have been performed, even within the verbatim code + block; + \item in \cref{lst:verb1-rv-mod1} indentation has been performed, but that the replacements have + \emph{not} been performed within the verbatim environment, because the + \texttt{rv} switch is active; + \item in \cref{lst:verb1-rr-mod1} indentation has \emph{not} been performed, but + that replacements have been performed, not respecting the verbatim code block. + \end{enumerate} + See the summary within \vref{tab:replacementswitches}. + + \begin{example} + Let's explore the \texttt{amalgamate} field from \vref{lst:replacements} in the context of the file specified + in \cref{lst:amalg1}. + + \cmhlistingsfromfile*{demonstrations/amalg1.tex}{\texttt{amalg1.tex}}{lst:amalg1} + + Let's consider the YAML files given in \crefrange{lst:amalg1-yaml}{lst:amalg3-yaml}. + + \begin{cmhtcbraster}[raster columns=3, + raster left skip=-3.75cm, + raster right skip=-2cm,] + \cmhlistingsfromfile*[style=yaml-LST]{demonstrations/amalg1-yaml.yaml}[replace-TCB]{\texttt{amalg1-yaml.yaml}}{lst:amalg1-yaml} + \cmhlistingsfromfile*[style=yaml-LST]{demonstrations/amalg2-yaml.yaml}[replace-TCB]{\texttt{amalg2-yaml.yaml}}{lst:amalg2-yaml} + \cmhlistingsfromfile*[style=yaml-LST]{demonstrations/amalg3-yaml.yaml}[replace-TCB]{\texttt{amalg3-yaml.yaml}}{lst:amalg3-yaml} + \end{cmhtcbraster} + + Upon running the following commands, + \begin{commandshell} +latexindent.pl -r amalg1.tex -l=amalg1-yaml +latexindent.pl -r amalg1.tex -l=amalg1-yaml,amalg2-yaml +latexindent.pl -r amalg1.tex -l=amalg1-yaml,amalg2-yaml,amalg3-yaml +\end{commandshell} + we receive the respective output in \crefrange{lst:amalg1-mod1}{lst:amalg1-mod123}. + + \begin{cmhtcbraster}[raster columns=3, + raster left skip=-3.75cm, + raster right skip=-2cm,] + \cmhlistingsfromfile*{demonstrations/amalg1-mod1.tex}{\texttt{amalg1.tex} using \cref{lst:amalg1-yaml}}{lst:amalg1-mod1} + \cmhlistingsfromfile*{demonstrations/amalg1-mod12.tex}{\texttt{amalg1.tex} using \cref{lst:amalg1-yaml,lst:amalg2-yaml}}{lst:amalg1-mod12} + \cmhlistingsfromfile*{demonstrations/amalg1-mod123.tex}{\texttt{amalg1.tex} using \cref{lst:amalg1-yaml,lst:amalg2-yaml,lst:amalg3-yaml}}{lst:amalg1-mod123} + \end{cmhtcbraster} + We note that: + \begin{enumerate} + \item in \cref{lst:amalg1-mod1} the replacements from \cref{lst:amalg1-yaml} have been used; + \item in \cref{lst:amalg1-mod12} the replacements from \cref{lst:amalg1-yaml,lst:amalg2-yaml} have + \emph{both} been used, because the default value of \texttt{amalgamate} + is 1; + \item in \cref{lst:amalg1-mod123} \emph{only} the replacements from + \cref{lst:amalg3-yaml} have been used, because the value of \texttt{amalgamate} has + been set to 0. + \end{enumerate} + \end{example} diff --git a/support/latexindent/documentation/sec-the-m-switch.tex b/support/latexindent/documentation/sec-the-m-switch.tex new file mode 100644 index 0000000000..ec3a86871d --- /dev/null +++ b/support/latexindent/documentation/sec-the-m-switch.tex @@ -0,0 +1,1746 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\renewcommand{\imagetouse}{logo} +\section{The -m (modifylinebreaks) switch}\label{sec:modifylinebreaks} + \fancyhead[R]{\bfseries\thepage% + \tikz[remember picture,overlay] { + \node at (1,0){\includegraphics{logo}}; + }} + All features described in this section will only be relevant if the + \texttt{-m} switch is used. + + \startcontents[the-m-switch] + \printcontents[the-m-switch]{}{0}{} + +\yamltitle{modifylinebreaks}*{fields} + \begin{wrapfigure}[7]{r}[0pt]{8cm} + \cmhlistingsfromfile[style=modifylinebreaks]*{../defaultSettings.yaml}[MLB-TCB,width=.85\linewidth,before=\centering]{\texttt{modifyLineBreaks}}{lst:modifylinebreaks} + \end{wrapfigure} + \makebox[0pt][r]{% + \raisebox{-\totalheight}[0pt][0pt]{% + \tikz\node[opacity=1] at (0,0) {\includegraphics[width=4cm]{logo}};}}% + As of Version 3.0, \texttt{latexindent.pl} has the \texttt{-m} + switch, which permits \texttt{latexindent.pl} to modify line breaks, according to the + specifications in the \texttt{modifyLineBreaks} field. \emph{The settings + in this field will only be considered if the \texttt{-m} switch has been used}. A + snippet of the default settings of this field is shown in \cref{lst:modifylinebreaks}. + + Having read the previous paragraph, it should sound reasonable that, if you call + \texttt{latexindent.pl} using the \texttt{-m} switch, then you give + it permission to modify line breaks in your file, but let's be clear: + + \begin{warning} + If you call \texttt{latexindent.pl} with the \texttt{-m} switch, then you + are giving it permission to modify line breaks. By default, the only + thing that will happen is that multiple blank lines will be condensed into + one blank line; many other settings are possible, discussed next. + \end{warning} + +\yamltitle{preserveBlankLines}{0|1} + This field is directly related to \emph{poly-switches}, discussed below. By + default, it is set to \texttt{1}, which means that blank lines will be + protected from removal; however, regardless of this setting, multiple blank lines can be + condensed if \texttt{condenseMultipleBlankLinesInto} is greater than \texttt{0}, + discussed next. + +\yamltitle{condenseMultipleBlankLinesInto}*{positive integer} + Assuming that this switch takes an integer value greater than + \texttt{0}, \texttt{latexindent.pl} will condense multiple blank + lines into the number of blank lines illustrated by this switch. As an example, + \cref{lst:mlb-bl} shows a sample file with blank lines; upon running + \begin{commandshell} +latexindent.pl myfile.tex -m +\end{commandshell} + the output is shown in \cref{lst:mlb-bl-out}; note that the multiple blank lines + have been condensed into one blank line, and note also that we have used the + \texttt{-m} switch! + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/mlb1.tex}{\texttt{mlb1.tex}}{lst:mlb-bl} + \end{minipage}% + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/mlb1-out.tex}{\texttt{mlb1.tex} out output}{lst:mlb-bl-out} + \end{minipage} + +\subsection{textWrapOptions: modifying line breaks by text wrapping}\label{subsec:textwrapping} + When the \texttt{-m} switch is active \texttt{latexindent.pl} has the + ability to wrap text using the options% + \announce{2017-05-27}{textWrapOptions} specified in the \texttt{textWrapOptions} field, see + \cref{lst:textWrapOptions}. The value of \texttt{columns} specifies the + column at which the text should be wrapped. By default, the value of + \texttt{columns} is \texttt{0}, so + \texttt{latexindent.pl} will \emph{not} wrap text; if you change it + to a value of \texttt{2} or more, then text will be wrapped after the + character in the specified column. + + \cmhlistingsfromfile[style=textWrapOptions]*{../defaultSettings.yaml}[MLB-TCB,width=.85\linewidth,before=\centering]{\texttt{textWrapOptions}}{lst:textWrapOptions} + + For example, consider the file give in \cref{lst:textwrap1}. + + \begin{widepage} + \cmhlistingsfromfile{demonstrations/textwrap1.tex}{\texttt{textwrap1.tex}}{lst:textwrap1} + \end{widepage} + + Using the file \texttt{textwrap1.yaml} in \cref{lst:textwrap1-yaml}, and running + the command + \begin{commandshell} +latexindent.pl -m textwrap1.tex -o textwrap1-mod1.tex -l textwrap1.yaml +\end{commandshell} + we obtain the output in \cref{lst:textwrap1-mod1}. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile{demonstrations/textwrap1-mod1.tex}{\texttt{textwrap1-mod1.tex}}{lst:textwrap1-mod1} + \cmhlistingsfromfile{demonstrations/textwrap1.yaml}[MLB-TCB]{\texttt{textwrap1.yaml}}{lst:textwrap1-yaml} + \end{cmhtcbraster} + + The text wrapping routine is performed \emph{after} verbatim environments + have been stored, so verbatim environments and verbatim commands are exempt from the + routine. For example, using the file in \cref{lst:textwrap2}, + \begin{widepage} + \cmhlistingsfromfile{demonstrations/textwrap2.tex}{\texttt{textwrap2.tex}}{lst:textwrap2} + \end{widepage} + and running the following command and continuing to use \texttt{textwrap1.yaml} from + \cref{lst:textwrap1-yaml}, + \begin{commandshell} +latexindent.pl -m textwrap2.tex -o textwrap2-mod1.tex -l textwrap1.yaml +\end{commandshell} + then the output is as in \cref{lst:textwrap2-mod1}. + \begin{widepage} + \cmhlistingsfromfile{demonstrations/textwrap2-mod1.tex}{\texttt{textwrap2-mod1.tex}}{lst:textwrap2-mod1} + \end{widepage} + Furthermore, the text wrapping routine is performed after the trailing comments have been + stored, and they are also exempt from text wrapping. For example, using the file in + \cref{lst:textwrap3} + \begin{widepage} + \cmhlistingsfromfile{demonstrations/textwrap3.tex}{\texttt{textwrap3.tex}}{lst:textwrap3} + \end{widepage} + and running the following command and continuing to use \texttt{textwrap1.yaml} from + \cref{lst:textwrap1-yaml}, + \begin{commandshell} +latexindent.pl -m textwrap3.tex -o textwrap3-mod1.tex -l textwrap1.yaml +\end{commandshell} + then the output is as in \cref{lst:textwrap3-mod1}. + + \cmhlistingsfromfile{demonstrations/textwrap3-mod1.tex}{\texttt{textwrap3-mod1.tex}}{lst:textwrap3-mod1} + + The text wrapping routine of \texttt{latexindent.pl} is performed by the + \texttt{Text::Wrap} module, which provides a \texttt{separator} feature + to separate lines with characters other than a new line (see + \cite{textwrap}). By default, the separator is empty which means that a new + line token will be used, but you can change it as you see fit. + + For example starting with the file in \cref{lst:textwrap4} + + \cmhlistingsfromfile{demonstrations/textwrap4.tex}{\texttt{textwrap4.tex}}{lst:textwrap4} + and using \texttt{textwrap2.yaml} from \cref{lst:textwrap2-yaml} with the following + command + \begin{commandshell} +latexindent.pl -m textwrap4.tex -o textwrap4-mod2.tex -l textwrap2.yaml +\end{commandshell} + then we obtain the output in \cref{lst:textwrap4-mod2}. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile{demonstrations/textwrap4-mod2.tex}{\texttt{textwrap4-mod2.tex}}{lst:textwrap4-mod2} + \cmhlistingsfromfile{demonstrations/textwrap2.yaml}[MLB-TCB]{\texttt{textwrap2.yaml}}{lst:textwrap2-yaml} + \end{cmhtcbraster} + +\subsubsection{text wrapping on a per-code-block basis} + By default, if the value of \texttt{columns} is greater than 0 and the + \texttt{-m} switch is active, then% + \announce{2018-08-13}*{updates to textWrapOptions} the text wrapping routine will operate before the code blocks + have been searched for. This behaviour is customisable; in particular, you can instead + instruct \texttt{latexindent.pl} to apply \texttt{textWrap} on a + per-code-block basis. Thanks to \cite{zoehneto} for their help in testing and + shaping this feature. + + The full details of \texttt{textWrapOptions} are shown in \cref{lst:textWrapOptionsAll}. + In particular, note the field \texttt{perCodeBlockBasis: 0}. + + \cmhlistingsfromfile[style=textWrapOptionsAll]*{../defaultSettings.yaml}[MLB-TCB,width=.85\linewidth,before=\centering]{\texttt{textWrapOptions}}{lst:textWrapOptionsAll} + + The code blocks detailed in \cref{lst:textWrapOptionsAll} are with direct reference to + those detailed in \vref{tab:code-blocks}. The only special case is the + \texttt{masterDocument} field; this is designed for `chapter'-type files that may + contain paragraphs that are not within any other code-blocks. The same notation is used + between this feature and the \texttt{removeParagraphLineBreaks} described in + \vref{lst:removeParagraphLineBreaks}; in fact, the two features can even be combined (this is + detailed in \vref{subsec:removeparagraphlinebreaks:and:textwrap}). + + Let's explore these switches with reference to the code given in + \cref{lst:textwrap5}; the text outside of the environment is considered part of + the \texttt{masterDocument}. + + \begin{widepage} + \cmhlistingsfromfile{demonstrations/textwrap5.tex}{\texttt{textwrap5.tex}}{lst:textwrap5} + \end{widepage} + + With reference to this code block, the settings given in \cref{lst:textwrap3-yaml,lst:textwrap4-yaml,lst:textwrap5-yaml} each + give the same output. + + \begin{cmhtcbraster}[raster columns=3, + raster left skip=-3.5cm, + raster right skip=-2cm, + raster column skip=.03\linewidth] + \cmhlistingsfromfile{demonstrations/textwrap3.yaml}[MLB-TCB]{\texttt{textwrap3.yaml}}{lst:textwrap3-yaml} + \cmhlistingsfromfile{demonstrations/textwrap4.yaml}[MLB-TCB]{\texttt{textwrap4.yaml}}{lst:textwrap4-yaml} + \cmhlistingsfromfile{demonstrations/textwrap5.yaml}[MLB-TCB]{\texttt{textwrap5.yaml}}{lst:textwrap5-yaml} + \end{cmhtcbraster} + + Let's explore the similarities and differences in the equivalent (with respect to + \cref{lst:textwrap5}) syntax specified in \cref{lst:textwrap3-yaml,lst:textwrap4-yaml,lst:textwrap5-yaml}: + \begin{itemize} + \item in each of \cref{lst:textwrap3-yaml,lst:textwrap4-yaml,lst:textwrap5-yaml} notice that \texttt{columns: 30}; + \item in each of \cref{lst:textwrap3-yaml,lst:textwrap4-yaml,lst:textwrap5-yaml} notice that \texttt{perCodeBlockBasis: 1}; + \item in \cref{lst:textwrap3-yaml} we have specified \texttt{all: 1} so that the + text wrapping will operate upon \emph{all} code blocks; + \item in \cref{lst:textwrap4-yaml} we have \emph{not} specified + \texttt{all}, and instead, have specified that text wrapping should be + applied to each of \texttt{environments} and \texttt{masterDocument}; + \item in \cref{lst:textwrap5-yaml} we have specified text wrapping for + \texttt{masterDocument} and on a \emph{per-name} basis for + \texttt{environments} code blocks. + \end{itemize} + + Upon running the following commands + \begin{commandshell} +latexindent.pl -s textwrap5.tex -l=textwrap3.yaml -m +latexindent.pl -s textwrap5.tex -l=textwrap4.yaml -m +latexindent.pl -s textwrap5.tex -l=textwrap5.yaml -m +\end{commandshell} + we obtain the output shown in \cref{lst:textwrap5-mod3}. + + \cmhlistingsfromfile{demonstrations/textwrap5-mod3.tex}{\texttt{textwrap5-mod3.tex}}{lst:textwrap5-mod3} + + We can explore the idea of per-name text wrapping given in \cref{lst:textwrap5-yaml} by + using \cref{lst:textwrap6}. + + \begin{widepage} + \cmhlistingsfromfile{demonstrations/textwrap6.tex}{\texttt{textwrap6.tex}}{lst:textwrap6} + \end{widepage} + + In particular, upon running + \begin{commandshell} +latexindent.pl -s textwrap6.tex -l=textwrap5.yaml -m +\end{commandshell} + we obtain the output given in \cref{lst:textwrap6-mod5}. + + \begin{widepage} + \cmhlistingsfromfile{demonstrations/textwrap6-mod5.tex}{\texttt{textwrap6.tex} using \cref{lst:textwrap5-yaml}}{lst:textwrap6-mod5} + \end{widepage} + + Notice that, because \texttt{environments} has been specified only for + \texttt{myenv} (in \cref{lst:textwrap5-yaml}) that the environment named \texttt{another} has + \emph{not} had text wrapping applied to it. + + The {all} field can be specified with exceptions which can either + be done on a per-code-block or per-name basis; we explore this in relation to + \cref{lst:textwrap6} in the settings given in \crefrange{lst:textwrap6-yaml}{lst:textwrap8-yaml}. + + \begin{adjustwidth}{-3.5cm}{-2.5cm} + \begin{minipage}{.33\linewidth} + \cmhlistingsfromfile{demonstrations/textwrap6.yaml}[MLB-TCB]{\texttt{textwrap6.yaml}}{lst:textwrap6-yaml} + \end{minipage}% + \begin{minipage}{.33\linewidth} + \cmhlistingsfromfile{demonstrations/textwrap7.yaml}[MLB-TCB]{\texttt{textwrap7.yaml}}{lst:textwrap7-yaml} + \end{minipage}% + \begin{minipage}{.33\linewidth} + \cmhlistingsfromfile{demonstrations/textwrap8.yaml}[MLB-TCB]{\texttt{textwrap8.yaml}}{lst:textwrap8-yaml} + \end{minipage} + \end{adjustwidth} + + Upon running the commands + \begin{commandshell} +latexindent.pl -s textwrap6.tex -l=textwrap6.yaml -m +latexindent.pl -s textwrap6.tex -l=textwrap7.yaml -m +latexindent.pl -s textwrap6.tex -l=textwrap8.yaml -m +\end{commandshell} + we receive the respective output given in \crefrange{lst:textwrap6-mod6}{lst:textwrap6-mod8}. + \begin{widepage} + \cmhlistingsfromfile{demonstrations/textwrap6-mod6.tex}{\texttt{textwrap6.tex} using \cref{lst:textwrap6-yaml}}{lst:textwrap6-mod6} + + \cmhlistingsfromfile{demonstrations/textwrap6-mod7.tex}{\texttt{textwrap6.tex} using \cref{lst:textwrap7-yaml}}{lst:textwrap6-mod7} + + \cmhlistingsfromfile{demonstrations/textwrap6-mod8.tex}{\texttt{textwrap6.tex} using \cref{lst:textwrap8-yaml}}{lst:textwrap6-mod8} + \end{widepage} + + Notice that: + \begin{itemize} + \item in \cref{lst:textwrap6-mod6} the text wrapping routine has not been applied to any + \texttt{environments} because it has been switched off (per-code-block) in + \cref{lst:textwrap6-yaml}; + \item in \cref{lst:textwrap6-mod7} the text wrapping routine has not been applied to + \texttt{myenv} because it has been switched off (per-name) in + \cref{lst:textwrap7-yaml}; + \item in \cref{lst:textwrap6-mod8} the text wrapping routine has not been applied to + \texttt{masterDocument} because of the settings in \cref{lst:textwrap8-yaml}. + \end{itemize} + + The \texttt{columns} field has a variety of different ways that it can be + specified; we've seen two basic ways already: the default (set to + \texttt{0}) and a positive integer (see \vref{lst:textwrap6}, for + example). We explore further options in \crefrange{lst:textwrap9-yaml}{lst:textwrap11-yaml}. + + \begin{cmhtcbraster}[raster columns=3, + raster left skip=-3.5cm, + raster right skip=-2cm, + raster column skip=.03\linewidth] + \cmhlistingsfromfile{demonstrations/textwrap9.yaml}[MLB-TCB]{\texttt{textwrap9.yaml}}{lst:textwrap9-yaml} + \cmhlistingsfromfile{demonstrations/textwrap10.yaml}[MLB-TCB]{\texttt{textwrap10.yaml}}{lst:textwrap10-yaml} + \cmhlistingsfromfile{demonstrations/textwrap11.yaml}[MLB-TCB]{\texttt{textwrap11.yaml}}{lst:textwrap11-yaml} + \end{cmhtcbraster} + + \Cref{lst:textwrap9-yaml} and \cref{lst:textwrap10-yaml} are equivalent. Upon running + the commands + \begin{commandshell} +latexindent.pl -s textwrap6.tex -l=textwrap9.yaml -m +latexindent.pl -s textwrap6.tex -l=textwrap11.yaml -m +\end{commandshell} + we receive the respective output given in \cref{lst:textwrap6-mod9,lst:textwrap6-mod11}. + + \begin{widepage} + \cmhlistingsfromfile{demonstrations/textwrap6-mod9.tex}{\texttt{textwrap6.tex} using \cref{lst:textwrap9-yaml}}{lst:textwrap6-mod9} + + \cmhlistingsfromfile{demonstrations/textwrap6-mod11.tex}{\texttt{textwrap6.tex} using \cref{lst:textwrap11-yaml}}{lst:textwrap6-mod11} + \end{widepage} + + Notice that: + \begin{itemize} + \item in \cref{lst:textwrap6-mod9} the text for the \texttt{masterDocument} has been + wrapped using \texttt{30} columns, while \texttt{environments} has + been wrapped using \texttt{50} columns; + \item in \cref{lst:textwrap6-mod11} the text for \texttt{myenv} has been wrapped + using \texttt{50} columns, the text for \texttt{another} has + been wrapped using \texttt{15} columns, and \texttt{masterDocument} + has been wrapped using \texttt{30} columns. + \end{itemize} + If you don't specify a \texttt{default} value on per-code-block basis, then + the \texttt{default} value from \texttt{columns} will be inherited; + if you don't specify a default value for \texttt{columns} then + \texttt{80} will be used. + + \texttt{alignAtAmpersandTakesPriority} is set to \texttt{1} by default; assuming + that text wrapping is occurring on a per-code-block basis, and the current + environment/code block is specified within \vref{lst:aligndelims:basic} then text wrapping + will be disabled for this code block. + + If you wish to specify \texttt{afterHeading} commands (see + \vref{lst:indentAfterHeadings}) on a per-name basis, then you need to append the name with + \texttt{:heading}, for example, you might use \texttt{section:heading}. + +\subsubsection{Summary of text wrapping} + It is important to note the following: + \begin{itemize} + \item Verbatim environments (\vref{lst:verbatimEnvironments}) and verbatim commands + (\vref{lst:verbatimCommands}) will \emph{not} be affected by the text + wrapping routine (see \vref{lst:textwrap2-mod1}); + \item comments will \emph{not} be affected by the text wrapping routine (see + \vref{lst:textwrap3-mod1}); + \item it is possible to wrap text on a per-code-block and a per-name basis; + \announce{2018-08-13}*{updates to textWrapOptions} + \item the text wrapping routine sets \texttt{preserveBlankLines} as + \texttt{1}; + \item indentation is performed \emph{after} the text wrapping routine; as such, + indented code will likely exceed any maximum value set in the \texttt{columns} + field. + \end{itemize} + +\subsection{oneSentencePerLine: modifying line breaks for sentences}\label{sec:onesentenceperline} + You can instruct \texttt{latexindent.pl} to + format% + \announce{2018-01-13}{one sentence per line} your file so that + it puts one sentence per line. Thank you to \cite{mlep} for helping to + shape and test this feature. The behaviour of this part of the script is controlled by + the switches detailed in \cref{lst:oneSentencePerLine}, all of which we discuss next. + + \cmhlistingsfromfile[style=oneSentencePerLine]*{../defaultSettings.yaml}[MLB-TCB,width=.85\linewidth,before=\centering]{\texttt{oneSentencePerLine}}{lst:oneSentencePerLine} + +\yamltitle{manipulateSentences}{0|1} + This is a binary switch that details if \texttt{latexindent.pl} should perform the + sentence manipulation routine; it is \emph{off} (set to \texttt{0}) by default, and you will + need to turn it on (by setting it to \texttt{1}) if you want the script + to modify line breaks surrounding and within sentences. + +\yamltitle{removeSentenceLineBreaks}{0|1} + When operating upon sentences \texttt{latexindent.pl} will, by default, remove + internal line breaks as \texttt{removeSentenceLineBreaks} is set to + \texttt{1}. Setting this switch to \texttt{0} instructs + \texttt{latexindent.pl} not to do so. + + For example, consider \texttt{multiple-sentences.tex} shown in \cref{lst:multiple-sentences}. + + \cmhlistingsfromfile{demonstrations/multiple-sentences.tex}{\texttt{multiple-sentences.tex}}{lst:multiple-sentences} + + If we use the YAML files in \cref{lst:manipulate-sentences-yaml,lst:keep-sen-line-breaks-yaml}, and run the commands + \begin{widepage} + \begin{commandshell} +latexindent.pl multiple-sentences -m -l=manipulate-sentences.yaml +latexindent.pl multiple-sentences -m -l=keep-sen-line-breaks.yaml +\end{commandshell} + \end{widepage} + then we obtain the respective output given in \cref{lst:multiple-sentences-mod1,lst:multiple-sentences-mod2}. + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/multiple-sentences-mod1.tex}{\texttt{multiple-sentences.tex} using \cref{lst:manipulate-sentences-yaml}}{lst:multiple-sentences-mod1} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/manipulate-sentences.yaml}[MLB-TCB]{\texttt{manipulate-sentences.yaml}}{lst:manipulate-sentences-yaml} + \end{cmhtcbraster} + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/multiple-sentences-mod2.tex}{\texttt{multiple-sentences.tex} using \cref{lst:keep-sen-line-breaks-yaml}}{lst:multiple-sentences-mod2} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/keep-sen-line-breaks.yaml}[MLB-TCB]{\texttt{keep-sen-line-breaks.yaml}}{lst:keep-sen-line-breaks-yaml} + \end{cmhtcbraster} + + Notice, in particular, that the `internal' sentence line breaks in + \cref{lst:multiple-sentences} have been removed in \cref{lst:multiple-sentences-mod1}, but have + not been removed in \cref{lst:multiple-sentences-mod2}. + + The remainder of the settings displayed in \vref{lst:oneSentencePerLine} instruct + \texttt{latexindent.pl} on how to define a sentence. From the perspective of + \texttt{latexindent.pl} a sentence must: + \begin{itemize} + \item \emph{follow} a certain character or set of characters (see + \cref{lst:sentencesFollow}); by default, this is either \lstinline!\par!, a + blank line, a full stop/period (.), exclamation mark (!), question mark (?) right brace + (\}) or a comment on the previous line; + \item \emph{begin} with a character type (see \cref{lst:sentencesBeginWith}); by + default, this is only capital letters; + \item \emph{end} with a character (see \cref{lst:sentencesEndWith}); by + default, these are full stop/period (.), exclamation mark (!) and question mark (?). + \end{itemize} + In each case, you can specify the \texttt{other} field to include any + pattern that you would like; you can specify anything in this field using the language of + regular expressions. + + \begin{cmhtcbraster}[raster columns=3, + raster left skip=-3.5cm, + raster right skip=-2cm, + raster column skip=.06\linewidth] + \cmhlistingsfromfile[style=sentencesFollow]*{../defaultSettings.yaml}[MLB-TCB,width=.9\linewidth,before=\centering]{\texttt{sentencesFollow}}{lst:sentencesFollow} + \cmhlistingsfromfile[style=sentencesBeginWith]*{../defaultSettings.yaml}[MLB-TCB,width=.9\linewidth,before=\centering]{\texttt{sentencesBeginWith}}{lst:sentencesBeginWith} + \cmhlistingsfromfile[style=sentencesEndWith]*{../defaultSettings.yaml}[MLB-TCB,width=.9\linewidth,before=\centering]{\texttt{sentencesEndWith}}{lst:sentencesEndWith} + \end{cmhtcbraster} + +\subsubsection{sentencesFollow} + Let's explore a few of the switches in \texttt{sentencesFollow}; let's start with + \vref{lst:multiple-sentences}, and use the YAML settings given in + \cref{lst:sentences-follow1-yaml}. Using the command + \begin{commandshell} +latexindent.pl multiple-sentences -m -l=sentences-follow1.yaml +\end{commandshell} + we obtain the output given in \cref{lst:multiple-sentences-mod3}. + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/multiple-sentences-mod3.tex}{\texttt{multiple-sentences.tex} using \cref{lst:sentences-follow1-yaml}}{lst:multiple-sentences-mod3} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/sentences-follow1.yaml}[MLB-TCB]{\texttt{sentences-follow1.yaml}}{lst:sentences-follow1-yaml} + \end{cmhtcbraster} + + Notice that, because \texttt{blankLine} is set to \texttt{0}, + \texttt{latexindent.pl} will not seek sentences following a blank line, and so the + fourth sentence has not been accounted for. + + We can explore the \texttt{other} field in \cref{lst:sentencesFollow} with + the \texttt{.tex} file detailed in \cref{lst:multiple-sentences1}. + + \cmhlistingsfromfile{demonstrations/multiple-sentences1.tex}{\texttt{multiple-sentences1.tex}}{lst:multiple-sentences1} + + Upon running the following commands + \begin{widepage} + \begin{commandshell} +latexindent.pl multiple-sentences1 -m -l=manipulate-sentences.yaml +latexindent.pl multiple-sentences1 -m -l=manipulate-sentences.yaml,sentences-follow2.yaml +\end{commandshell} + \end{widepage} + then we obtain the respective output given in \cref{lst:multiple-sentences1-mod1,lst:multiple-sentences1-mod2}. + \cmhlistingsfromfile{demonstrations/multiple-sentences1-mod1.tex}{\texttt{multiple-sentences1.tex} using \vref{lst:manipulate-sentences-yaml}}{lst:multiple-sentences1-mod1} + + \begin{cmhtcbraster}[ + raster force size=false, + raster column 1/.style={add to width=1cm}, + ] + \cmhlistingsfromfile{demonstrations/multiple-sentences1-mod2.tex}{\texttt{multiple-sentences1.tex} using \cref{lst:sentences-follow2-yaml}}{lst:multiple-sentences1-mod2} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/sentences-follow2.yaml}[MLB-TCB,width=.45\textwidth]{\texttt{sentences-follow2.yaml}}{lst:sentences-follow2-yaml} + \end{cmhtcbraster} + + Notice that in \cref{lst:multiple-sentences1-mod1} the first sentence after the + \texttt{)} has not been accounted for, but that following the inclusion + of \cref{lst:sentences-follow2-yaml}, the output given in \cref{lst:multiple-sentences1-mod2} + demonstrates that the sentence \emph{has} been accounted for correctly. + +\subsubsection{sentencesBeginWith} + By default, \texttt{latexindent.pl} will only assume that sentences begin with the + upper case letters \texttt{A-Z}; you can instruct the script to define + sentences to begin with lower case letters (see \cref{lst:sentencesBeginWith}), and we can + use the \texttt{other} field to define sentences to begin with other + characters. + + \cmhlistingsfromfile{demonstrations/multiple-sentences2.tex}{\texttt{multiple-sentences2.tex}}{lst:multiple-sentences2} + + Upon running the following commands + \begin{widepage} + \begin{commandshell} +latexindent.pl multiple-sentences2 -m -l=manipulate-sentences.yaml +latexindent.pl multiple-sentences2 -m -l=manipulate-sentences.yaml,sentences-begin1.yaml +\end{commandshell} + \end{widepage} + then we obtain the respective output given in \cref{lst:multiple-sentences2-mod1,lst:multiple-sentences2-mod2}. + \cmhlistingsfromfile{demonstrations/multiple-sentences2-mod1.tex}{\texttt{multiple-sentences2.tex} using \vref{lst:manipulate-sentences-yaml}}{lst:multiple-sentences2-mod1} + + \begin{cmhtcbraster}[ + raster force size=false, + raster column 1/.style={add to width=1cm}, + ] + \cmhlistingsfromfile{demonstrations/multiple-sentences2-mod2.tex}{\texttt{multiple-sentences2.tex} using \cref{lst:sentences-begin1-yaml}}{lst:multiple-sentences2-mod2} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/sentences-begin1.yaml}[MLB-TCB,width=.45\textwidth]{\texttt{sentences-begin1.yaml}}{lst:sentences-begin1-yaml} + \end{cmhtcbraster} + Notice that in \cref{lst:multiple-sentences2-mod1}, the first sentence has been accounted for but + that the subsequent sentences have not. In \cref{lst:multiple-sentences2-mod2}, all of the + sentences have been accounted for, because the \texttt{other} field in + \cref{lst:sentences-begin1-yaml} has defined sentences to begin with either + \lstinline!$! or any numeric digit, \texttt{0} to + \texttt{9}. + +\subsubsection{sentencesEndWith} + Let's return to \vref{lst:multiple-sentences}; we have already seen the default way in + which \texttt{latexindent.pl} will operate on the sentences in this file in + \vref{lst:multiple-sentences-mod1}. We can populate the \texttt{other} field with + any character that we wish; for example, using the YAML specified in + \cref{lst:sentences-end1-yaml} and the command + \begin{commandshell} +latexindent.pl multiple-sentences -m -l=sentences-end1.yaml +latexindent.pl multiple-sentences -m -l=sentences-end2.yaml +\end{commandshell} + then we obtain the output in \cref{lst:multiple-sentences-mod4}. + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/multiple-sentences-mod4.tex}{\texttt{multiple-sentences.tex} using \cref{lst:sentences-end1-yaml}}{lst:multiple-sentences-mod4} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/sentences-end1.yaml}[MLB-TCB]{\texttt{sentences-end1.yaml}}{lst:sentences-end1-yaml} + \end{cmhtcbraster} + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/multiple-sentences-mod5.tex}{\texttt{multiple-sentences.tex} using \cref{lst:sentences-end2-yaml}}{lst:multiple-sentences-mod5} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/sentences-end2.yaml}[MLB-TCB]{\texttt{sentences-end2.yaml}}{lst:sentences-end2-yaml} + \end{cmhtcbraster} + + There is a subtle difference between the output in \cref{lst:multiple-sentences-mod4,lst:multiple-sentences-mod5}; in + particular, in \cref{lst:multiple-sentences-mod4} the word \texttt{sentence} has not + been defined as a sentence, because we have not instructed \texttt{latexindent.pl} to + begin sentences with lower case letters. We have changed this by using the settings in + \cref{lst:sentences-end2-yaml}, and the associated output in \cref{lst:multiple-sentences-mod5} + reflects this. + + Referencing \vref{lst:sentencesEndWith}, you'll notice that there is a field called + \texttt{basicFullStop}, which is set to \texttt{0}, and that the + \texttt{betterFullStop} is set to \texttt{1} by default. + + Let's consider the file shown in \cref{lst:url}. + + \cmhlistingsfromfile{demonstrations/url.tex}{\texttt{url.tex}}{lst:url} + + Upon running the following commands + \begin{commandshell} +latexindent.pl url -m -l=manipulate-sentences.yaml +\end{commandshell} + we obtain the output given in \cref{lst:url-mod1}. + + \cmhlistingsfromfile{demonstrations/url-mod1.tex}{\texttt{url.tex} using \vref{lst:manipulate-sentences-yaml}}{lst:url-mod1} + + Notice that the full stop within the url has been interpreted correctly. This is because, + within the \texttt{betterFullStop}, full stops at the end of sentences have the + following properties: + \begin{itemize} + \item they are ignored within \texttt{e.g.} and \texttt{i.e.}; + \item they can not be immediately followed by a lower case or upper case letter; + \item they can not be immediately followed by a hyphen, comma, or number. + \end{itemize} + If you find that the \texttt{betterFullStop} does not work for your purposes, then + you can switch it off by setting it to \texttt{0}, and you can + experiment with the \texttt{other} + field.% + \announce*{2019-07-13}{fine tuning the betterFullStop} You can also seek + to customise the \texttt{betterFullStop} routine by using the + \emph{fine tuning}, detailed in \vref{lst:fineTuning}. + + The \texttt{basicFullStop} routine should probably be avoided in most situations, as + it does not accommodate the specifications above. For example, using the following + command + \begin{commandshell} +latexindent.pl url -m -l=alt-full-stop1.yaml +\end{commandshell} + and the YAML in \cref{lst:alt-full-stop1-yaml} gives the output in + \cref{lst:url-mod2}. + + \begin{cmhtcbraster}[ raster left skip=-3.5cm, + raster right skip=-2cm, + raster force size=false, + raster column 1/.style={add to width=.1\textwidth}, + raster column skip=.06\linewidth] + \cmhlistingsfromfile{demonstrations/url-mod2.tex}{\texttt{url.tex} using \cref{lst:alt-full-stop1-yaml}}{lst:url-mod2} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/alt-full-stop1.yaml}[MLB-TCB,width=.5\textwidth]{\texttt{alt-full-stop1.yaml}}{lst:alt-full-stop1-yaml} + \end{cmhtcbraster} + + Notice that the full stop within the URL has not been accommodated correctly because of + the non-default settings in \cref{lst:alt-full-stop1-yaml}. + +\subsubsection{Features of the oneSentencePerLine routine} + The sentence manipulation routine takes place \emph{after} verbatim + environments, preamble and trailing comments have been accounted for; this means that any + characters within these types of code blocks will not be part of the sentence + manipulation routine. + + For example, if we begin with the \texttt{.tex} file in + \cref{lst:multiple-sentences3}, and run the command + \begin{commandshell} +latexindent.pl multiple-sentences3 -m -l=manipulate-sentences.yaml +\end{commandshell} + then we obtain the output in \cref{lst:multiple-sentences3-mod1}. \cmhlistingsfromfile{demonstrations/multiple-sentences3.tex}{\texttt{multiple-sentences3.tex}}{lst:multiple-sentences3} + \cmhlistingsfromfile{demonstrations/multiple-sentences3-mod1.tex}{\texttt{multiple-sentences3.tex} using \vref{lst:manipulate-sentences-yaml}}{lst:multiple-sentences3-mod1} + + Furthermore, if sentences run across environments then, by default, the line breaks + internal to the sentence will be removed. For example, if we use the + \texttt{.tex} file in \cref{lst:multiple-sentences4} and run the commands + \begin{commandshell} +latexindent.pl multiple-sentences4 -m -l=manipulate-sentences.yaml +latexindent.pl multiple-sentences4 -m -l=keep-sen-line-breaks.yaml +\end{commandshell} + then we obtain the output in \cref{lst:multiple-sentences4-mod1,lst:multiple-sentences4-mod2}. \cmhlistingsfromfile{demonstrations/multiple-sentences4.tex}{\texttt{multiple-sentences4.tex}}{lst:multiple-sentences4} + \begin{widepage} + \cmhlistingsfromfile{demonstrations/multiple-sentences4-mod1.tex}{\texttt{multiple-sentences4.tex} using \vref{lst:manipulate-sentences-yaml}}{lst:multiple-sentences4-mod1} + \end{widepage} + \cmhlistingsfromfile{demonstrations/multiple-sentences4-mod2.tex}{\texttt{multiple-sentences4.tex} using \vref{lst:keep-sen-line-breaks-yaml}}{lst:multiple-sentences4-mod2} + Once you've read \cref{sec:poly-switches}, you will know that you can accommodate the + removal of internal sentence line breaks by using the YAML in \cref{lst:item-rules2-yaml} + and the command + \begin{commandshell} +latexindent.pl multiple-sentences4 -m -l=item-rules2.yaml +\end{commandshell} + the output of which is shown in \cref{lst:multiple-sentences4-mod3}. + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/multiple-sentences4-mod3.tex}{\texttt{multiple-sentences4.tex} using \cref{lst:item-rules2-yaml}}{lst:multiple-sentences4-mod3} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/item-rules2.yaml}[MLB-TCB]{\texttt{item-rules2.yaml}}{lst:item-rules2-yaml} + \end{cmhtcbraster} + +\subsubsection{text wrapping and indenting sentences} + The \texttt{oneSentencePerLine}% + \announce{2018-08-13}{oneSentencePerline text wrap and indent} can be instructed to perform + text wrapping and indentation upon sentences. + + Let's use the code in \cref{lst:multiple-sentences5}. + + \cmhlistingsfromfile{demonstrations/multiple-sentences5.tex}{\texttt{multiple-sentences5.tex}}{lst:multiple-sentences5} + + Referencing \cref{lst:sentence-wrap1-yaml}, and running the following command + \begin{commandshell} +latexindent.pl multiple-sentences5 -m -l=sentence-wrap1.yaml +\end{commandshell} + we receive the output given in \cref{lst:multiple-sentences5-mod1}. + + \begin{cmhtcbraster}[ raster left skip=-3.5cm, + raster right skip=-2cm, + raster force size=false, + raster column 1/.style={add to width=.1\textwidth}, + raster column skip=.06\linewidth] + \cmhlistingsfromfile{demonstrations/multiple-sentences5-mod1.tex}{\texttt{multiple-sentences5.tex} using \cref{lst:sentence-wrap1-yaml}}{lst:multiple-sentences5-mod1} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/sentence-wrap1.yaml}[MLB-TCB,width=0.5\textwidth]{\texttt{sentence-wrap1.yaml}}{lst:sentence-wrap1-yaml} + \end{cmhtcbraster} + + If you wish to specify the \texttt{columns} field on a per-code-block basis + for sentences, then you would use \texttt{sentence}; explicitly, starting with + \vref{lst:textwrap9-yaml}, for example, you would replace/append + \texttt{environments} with, for example, \texttt{sentence: 50}. + + The indentation of sentences requires that sentences are stored as code blocks. This + means that you may need to tweak \vref{lst:sentencesEndWith}. Let's explore this in + relation to \cref{lst:multiple-sentences6}. + + \cmhlistingsfromfile{demonstrations/multiple-sentences6.tex}{\texttt{multiple-sentences6.tex}}{lst:multiple-sentences6} + + By default, \texttt{latexindent.pl} will find the full-stop within the first + \texttt{item}, which means that, upon running the following commands + \begin{commandshell} +latexindent.pl multiple-sentences6 -m -l=sentence-wrap1.yaml +latexindent.pl multiple-sentences6 -m -l=sentence-wrap1.yaml -y="modifyLineBreaks:oneSentencePerLine:sentenceIndent:''" +\end{commandshell} + we receive the respective output in \cref{lst:multiple-sentences6-mod1} and + \cref{lst:multiple-sentences6-mod2}. + + \cmhlistingsfromfile{demonstrations/multiple-sentences6-mod1.tex}{\texttt{multiple-sentences6-mod1.tex} using \cref{lst:sentence-wrap1-yaml}}{lst:multiple-sentences6-mod1} + + \cmhlistingsfromfile{demonstrations/multiple-sentences6-mod2.tex}{\texttt{multiple-sentences6-mod2.tex} using \cref{lst:sentence-wrap1-yaml} and no sentence indentation}{lst:multiple-sentences6-mod2} + + We note that \cref{lst:multiple-sentences6-mod1} the \texttt{itemize} code block has + \emph{not} been indented appropriately. This is because the + oneSentencePerLine has been instructed to store sentences (because + \cref{lst:sentence-wrap1-yaml}); each sentence is then searched for code blocks. + + We can tweak the settings in \vref{lst:sentencesEndWith} to ensure that full stops are + not followed by \texttt{item} commands, and that the end of sentences + contains \lstinline!\end{itemize}! as in \cref{lst:itemize-yaml} (if you intend to use this, ensure that you + remove the line breaks from the \texttt{other} field). + + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/itemized.yaml}[MLB-TCB]{\texttt{itemize.yaml}}{lst:itemize-yaml} + + Upon running + \begin{commandshell} +latexindent.pl multiple-sentences6 -m -l=sentence-wrap1.yaml,itemize.yaml +\end{commandshell} + we receive the output in \cref{lst:multiple-sentences6-mod3}. + + \cmhlistingsfromfile{demonstrations/multiple-sentences6-mod3.tex}{\texttt{multiple-sentences6-mod3.tex} using \cref{lst:sentence-wrap1-yaml} and \cref{lst:itemize-yaml}}{lst:multiple-sentences6-mod3} + + Notice that the sentence has received indentation, and that the + \texttt{itemize} code block has been found and indented correctly. + +\subsection{removeParagraphLineBreaks: modifying line breaks for paragraphs}\label{subsec:removeparagraphlinebreaks} + When the \texttt{-m} switch is active \texttt{latexindent.pl} has the + ability to remove line breaks% + \announce{2017-05-27}{removeParagraphLineBreaks} from within paragraphs; the behaviour is controlled by the + \texttt{removeParagraphLineBreaks} field, detailed in \cref{lst:removeParagraphLineBreaks}. Thank you to + \cite{jowens} for shaping and assisting with the testing of this feature. +\yamltitle{removeParagraphLineBreaks}*{fields} + This feature is considered complimentary to the \texttt{oneSentencePerLine} feature + described in \vref{sec:onesentenceperline}. + + \cmhlistingsfromfile[style=removeParagraphLineBreaks]*{../defaultSettings.yaml}[MLB-TCB,width=.85\linewidth,before=\centering]{\texttt{removeParagraphLineBreaks}}{lst:removeParagraphLineBreaks} + + This routine can be turned on \emph{globally} for \emph{every} + code block type known to \texttt{latexindent.pl} + (see \vref{tab:code-blocks}) by using the + \texttt{all} switch; by default, this switch is + \emph{off}. Assuming that the \texttt{all} switch is off, + then the routine can be controlled on a per-code-block-type basis, and within that, on a + per-name basis. We will consider examples of each of these in turn, but before we do, + let's specify what \texttt{latexindent.pl} considers as a paragraph: + \begin{itemize} + \item it must begin on its own line with either an alphabetic or numeric character, and not + with any of the code-block types detailed in \vref{tab:code-blocks}; + \item it can include line breaks, but finishes when it meets either a blank line, a + \lstinline!\par! command, or any of the user-specified settings in the + \texttt{paragraphsStopAt} field, detailed in \vref{lst:paragraphsStopAt}. + \end{itemize} + + Let's start with the \texttt{.tex} file in \cref{lst:shortlines}, + together with the YAML settings in \cref{lst:remove-para1-yaml}. + + \begin{cmhtcbraster} + \cmhlistingsfromfile[showspaces=true]{demonstrations/shortlines.tex}{\texttt{shortlines.tex}}{lst:shortlines} + \cmhlistingsfromfile{demonstrations/remove-para1.yaml}[MLB-TCB]{\texttt{remove-para1.yaml}}{lst:remove-para1-yaml} + \end{cmhtcbraster} + + Upon running the command + \begin{commandshell} +latexindent.pl -m shortlines.tex -o shortlines1.tex -l remove-para1.yaml +\end{commandshell} + then we obtain the output given in \cref{lst:shortlines1}. + + \cmhlistingsfromfile[showspaces=true]{demonstrations/shortlines1.tex}{\texttt{shortlines1.tex}}{lst:shortlines1} + + Keen readers may notice that some trailing white space must be present in the file in + \cref{lst:shortlines} which has crept in to the output in + \cref{lst:shortlines1}. This can be fixed using the YAML file in + \vref{lst:removeTWS-before} and running, for example, + \begin{commandshell} +latexindent.pl -m shortlines.tex -o shortlines1-tws.tex -l remove-para1.yaml,removeTWS-before.yaml +\end{commandshell} + in which case the output is as in \cref{lst:shortlines1-tws}; notice that the double + spaces present in \cref{lst:shortlines1} have been addressed. + + \cmhlistingsfromfile[showspaces=true]{demonstrations/shortlines1-tws.tex}{\texttt{shortlines1-tws.tex}}{lst:shortlines1-tws} + + Keeping with the settings in \cref{lst:remove-para1-yaml}, we note that the + \texttt{all} switch applies to \emph{all} code block + types. So, for example, let's consider the files in \cref{lst:shortlines-mand,lst:shortlines-opt} + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/shortlines-mand.tex}{\texttt{shortlines-mand.tex}}{lst:shortlines-mand} + \cmhlistingsfromfile{demonstrations/shortlines-opt.tex}{\texttt{shortlines-opt.tex}}{lst:shortlines-opt} + \end{cmhtcbraster} + + Upon running the commands + \begin{widepage} + \begin{commandshell} +latexindent.pl -m shortlines-mand.tex -o shortlines-mand1.tex -l remove-para1.yaml +latexindent.pl -m shortlines-opt.tex -o shortlines-opt1.tex -l remove-para1.yaml +\end{commandshell} + \end{widepage} + + then we obtain the respective output given in \cref{lst:shortlines-mand1,lst:shortlines-opt1}. + + \cmhlistingsfromfile{demonstrations/shortlines-mand1.tex}{\texttt{shortlines-mand1.tex}}{lst:shortlines-mand1} + \cmhlistingsfromfile{demonstrations/shortlines-opt1.tex}{\texttt{shortlines-opt1.tex}}{lst:shortlines-opt1} + + Assuming that we turn \emph{off} the \texttt{all} switch + (by setting it to \texttt{0}), then we can control the behaviour of + \texttt{removeParagraphLineBreaks} either on a per-code-block-type basis, or on a per-name + basis. + + For example, let's use the code in \cref{lst:shortlines-envs}, and consider the settings + in \cref{lst:remove-para2-yaml,lst:remove-para3-yaml}; note that in \cref{lst:remove-para2-yaml} we specify that + \emph{every} environment should receive treatment from the routine, while + in \cref{lst:remove-para3-yaml} we specify that \emph{only} the + \texttt{one} environment should receive the treatment. + + \begin{minipage}{.45\linewidth} + \cmhlistingsfromfile{demonstrations/shortlines-envs.tex}{\texttt{shortlines-envs.tex}}{lst:shortlines-envs} + \end{minipage} + \hfill + \begin{minipage}{.49\linewidth} + \cmhlistingsfromfile{demonstrations/remove-para2.yaml}[MLB-TCB]{\texttt{remove-para2.yaml}}{lst:remove-para2-yaml} + \cmhlistingsfromfile{demonstrations/remove-para3.yaml}[MLB-TCB]{\texttt{remove-para3.yaml}}{lst:remove-para3-yaml} + \end{minipage} + + Upon running the commands + \begin{widepage} + \begin{commandshell} +latexindent.pl -m shortlines-envs.tex -o shortlines-envs2.tex -l remove-para2.yaml +latexindent.pl -m shortlines-envs.tex -o shortlines-envs3.tex -l remove-para3.yaml +\end{commandshell} + \end{widepage} + then we obtain the respective output given in \cref{lst:shortlines-envs2,lst:shortlines-envs3}. + + \cmhlistingsfromfile{demonstrations/shortlines-envs2.tex}{\texttt{shortlines-envs2.tex}}{lst:shortlines-envs2} + \cmhlistingsfromfile{demonstrations/shortlines-envs3.tex}{\texttt{shortlines-envs3.tex}}{lst:shortlines-envs3} + + The remaining code-block types can be customised in analogous ways, although note that + \texttt{commands}, \texttt{keyEqualsValuesBracesBrackets}, \texttt{namedGroupingBracesBrackets}, + \texttt{UnNamedGroupingBracesBrackets} are controlled by the \texttt{optionalArguments} and the + \texttt{mandatoryArguments}. + + The only special case is the \texttt{masterDocument} field; this is designed for + `chapter'-type files that may contain paragraphs that are not within any other + code-blocks. For example, consider the file in \cref{lst:shortlines-md}, with the YAML + settings in \cref{lst:remove-para4-yaml}. + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/shortlines-md.tex}{\texttt{shortlines-md.tex}}{lst:shortlines-md} + \cmhlistingsfromfile{demonstrations/remove-para4.yaml}[MLB-TCB]{\texttt{remove-para4.yaml}}{lst:remove-para4-yaml} + \end{cmhtcbraster} + + Upon running the following command + \begin{widepage} + \begin{commandshell} +latexindent.pl -m shortlines-md.tex -o shortlines-md4.tex -l remove-para4.yaml +\end{commandshell} + \end{widepage} + then we obtain the output in \cref{lst:shortlines-md4}. \cmhlistingsfromfile{demonstrations/shortlines-md4.tex}{\texttt{shortlines-md4.tex}}{lst:shortlines-md4} + + Note% + \announce{2018-08-13}*{updates to all in removeParagraphLineBreaks} that the + \texttt{all} field can take the same exceptions detailed in + \cref{lst:textwrap6-yaml}{lst:textwrap8-yaml}. + +\yamltitle{paragraphsStopAt}*{fields} + The paragraph line break routine considers blank lines and the + \lstinline|\par| command to be the end of a paragraph; + \announce{2017-05-27}{paragraphsStopAt} you can fine tune the behaviour of the routine further by + using the \texttt{paragraphsStopAt} fields, shown in \cref{lst:paragraphsStopAt}. + + \cmhlistingsfromfile[style=paragraphsStopAt]*{../defaultSettings.yaml}[MLB-TCB,width=.85\linewidth,before=\centering]{\texttt{paragraphsStopAt}}{lst:paragraphsStopAt} + + The fields specified in \texttt{paragraphsStopAt} tell \texttt{latexindent.pl} to + stop the current paragraph when it reaches a line that \emph{begins} with + any of the code-block types specified as \texttt{1} in + \cref{lst:paragraphsStopAt}. By default, you'll see that the paragraph line break routine + will stop when it reaches an environment or verbatim code block at the beginning of a + line. It is \emph{not} possible to specify these fields on a per-name + basis. + + Let's use the \texttt{.tex} file in \cref{lst:sl-stop}; we will, + in turn, consider the settings in \cref{lst:stop-command-yaml,lst:stop-comment-yaml}. + + \begin{minipage}{.45\linewidth} + \cmhlistingsfromfile{demonstrations/sl-stop.tex}{\texttt{sl-stop.tex}}{lst:sl-stop} + \end{minipage} + \hfill + \begin{minipage}{.49\linewidth} + \cmhlistingsfromfile{demonstrations/stop-command.yaml}[MLB-TCB]{\texttt{stop-command.yaml}}{lst:stop-command-yaml} + + \cmhlistingsfromfile{demonstrations/stop-comment.yaml}[MLB-TCB]{\texttt{stop-comment.yaml}}{lst:stop-comment-yaml} + \end{minipage} + + Upon using the settings from \vref{lst:remove-para4-yaml} and running the commands + \begin{widepage} + \begin{commandshell} +latexindent.pl -m sl-stop.tex -o sl-stop4.tex -l remove-para4.yaml +latexindent.pl -m sl-stop.tex -o sl-stop4-command.tex -l=remove-para4.yaml,stop-command.yaml +latexindent.pl -m sl-stop.tex -o sl-stop4-comment.tex -l=remove-para4.yaml,stop-comment.yaml +\end{commandshell} + \end{widepage} + we obtain the respective outputs in \crefrange{lst:sl-stop4}{lst:sl-stop4-comment}; notice in particular + that: + \begin{itemize} + \item in \cref{lst:sl-stop4} the paragraph line break routine has included commands and + comments; + \item in \cref{lst:sl-stop4-command} the paragraph line break routine has + \emph{stopped} at the \texttt{emph} command, because in + \cref{lst:stop-command-yaml} we have specified \texttt{commands} to be + \texttt{1}, and \texttt{emph} is at the beginning of a + line; + \item in \cref{lst:sl-stop4-comment} the paragraph line break routine has + \emph{stopped} at the comments, because in \cref{lst:stop-comment-yaml} we + have specified \texttt{comments} to be \texttt{1}, and the + comment is at the beginning of a line. + \end{itemize} + In all outputs in \crefrange{lst:sl-stop4}{lst:sl-stop4-comment} we notice that the paragraph line break + routine has stopped at \lstinline!\begin{myenv}! because, by default, + \texttt{environments} is set to \texttt{1} in + \vref{lst:paragraphsStopAt}. + + \cmhlistingsfromfile{demonstrations/sl-stop4.tex}{\texttt{sl-stop4.tex}}{lst:sl-stop4} + \cmhlistingsfromfile{demonstrations/sl-stop4-command.tex}{\texttt{sl-stop4-command.tex}}{lst:sl-stop4-command} + \cmhlistingsfromfile{demonstrations/sl-stop4-comment.tex}{\texttt{sl-stop4-comment.tex}}{lst:sl-stop4-comment} + +\subsection{Combining removeParagraphLineBreaks and textWrapOptions}\label{subsec:removeparagraphlinebreaks:and:textwrap} + + The% + \announce{2018-08-13}{combine text wrap and remove paragraph line breaks} text wrapping + routine (\vref{subsec:textwrapping}) and remove paragraph line breaks routine + (\vref{subsec:removeparagraphlinebreaks}) can be combined. + + We motivate this feature with the code given in \cref{lst:textwrap7}. + + \cmhlistingsfromfile{demonstrations/textwrap7.tex}{\texttt{textwrap7.tex}}{lst:textwrap7} + + Applying the text wrap routine from \vref{subsec:textwrapping} with, for example, + \vref{lst:textwrap3-yaml} gives the output in \cref{lst:textwrap7-mod3}. + + \cmhlistingsfromfile{demonstrations/textwrap7-mod3.tex}{\texttt{textwrap7.tex} using \cref{lst:textwrap3-yaml}}{lst:textwrap7-mod3} + + The text wrapping routine has behaved as expected, but it may be desired to remove + paragraph line breaks \emph{before} performing the text wrapping routine. + The desired behaviour can be achieved by employing the \texttt{beforeTextWrap} + switch. + + Explicitly, using the settings in \cref{lst:textwrap12-yaml} and running the command + \begin{commandshell} +latexindent.pl -m textwrap7.tex -l=textwrap12.yaml -o=+-mod12 +\end{commandshell} + we obtain the output in \cref{lst:textwrap7-mod12}. + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/textwrap7-mod12.tex}{\texttt{textwrap7-mod12.tex}}{lst:textwrap7-mod12} + \cmhlistingsfromfile{demonstrations/textwrap12.yaml}[MLB-TCB]{\texttt{textwrap12.yaml}}{lst:textwrap12-yaml} + \end{cmhtcbraster} + + In \cref{lst:textwrap7-mod12} the paragraph line breaks have first been removed from + \cref{lst:textwrap7}, and then the text wrapping routine has been applied. It is + envisaged that variants of \cref{lst:textwrap12-yaml} will be among the most useful + settings for these two features. + +\subsection{Poly-switches}\label{sec:poly-switches} + Every other field in the \texttt{modifyLineBreaks} field uses poly-switches, and can + take one of \emph{five}% + \announce{2017-08-21}*{blank line poly-switch} integer values: + \begin{itemize}[font=\bfseries] + \item[$-1$] \emph{remove mode}: line breaks before or after the + \emph{<part of thing>} can be removed (assuming that \texttt{preserveBlankLines} is + set to \texttt{0}); + \item[0] \emph{off mode}: line breaks will not be modified for the + \emph{<part of thing>} under consideration; + \item[1] \emph{add mode}: a line break will be added before or after the + \emph{<part of thing>} under consideration, assuming that + there is not already a line break before or after the \emph{<part of thing>}; + \item[2] \emph{comment then add mode}: a comment symbol will be added, followed by a line break + before or after the \emph{<part of thing>} under consideration, assuming that there + is not already a comment and line break before or after the \emph{<part of thing>}; + \item[3] \emph{add then blank line mode}% + \announce{2017-08-21}{blank line poly-switch}: a line break will be added before or after the + \emph{<part of thing>} under consideration, assuming that + there is not already a line break before or after the \emph{<part of thing>}, + followed by a blank line; + \item[4] \emph{add blank line mode}% + \announce*{2019-07-13}{blank line poly-switch}; a blank line will + be added before or after the \emph{<part of thing>} under consideration, even if the + \emph{<part of thing>} is already on its own line. + \end{itemize} + In the above, \emph{<part of thing>} refers to either the + \emph{begin statement}, \emph{body} or \emph{end statement} + of the code blocks detailed in \vref{tab:code-blocks}. All poly-switches are + \emph{off} by default; \texttt{latexindent.pl} searches first of all + for per-name settings, and then followed by global per-thing settings. + +\subsection{modifyLineBreaks for environments}\label{sec:modifylinebreaks-environments} + We start by viewing a snippet of \texttt{defaultSettings.yaml} in + \cref{lst:environments-mlb}; note that it contains \emph{global} settings + (immediately after the \texttt{environments} field) and that + \emph{per-name} settings are also allowed -- in the case of + \cref{lst:environments-mlb}, settings for \texttt{equation*} have been specified + for demonstration. Note that all poly-switches are \emph{off} (set to 0) by + default. + + \cmhlistingsfromfile[style=modifylinebreaksEnv]*{../defaultSettings.yaml}[width=.8\linewidth,before=\centering,MLB-TCB]{\texttt{environments}}{lst:environments-mlb} + + Let's begin with the simple example given in \cref{lst:env-mlb1-tex}; note that we + have annotated key parts of the file using $\BeginStartsOnOwnLine$, + $\BodyStartsOnOwnLine$, $\EndStartsOnOwnLine$ and $\EndFinishesWithLineBreak$, + these will be related to fields specified in \cref{lst:environments-mlb}. + + \begin{cmhlistings}[style=tcblatex,escapeinside={(*@}{@*)}]{\texttt{env-mlb1.tex}}{lst:env-mlb1-tex} +before words(*@$\BeginStartsOnOwnLine$@*) \begin{myenv}(*@$\BodyStartsOnOwnLine$@*)body of myenv(*@$\EndStartsOnOwnLine$@*)\end{myenv}(*@$\EndFinishesWithLineBreak$@*) after words +\end{cmhlistings} + +\subsubsection{Adding line breaks: BeginStartsOnOwnLine and BodyStartsOnOwnLine} + Let's explore \texttt{BeginStartsOnOwnLine} and \texttt{BodyStartsOnOwnLine} in + \cref{lst:env-mlb1,lst:env-mlb2}, and in particular, let's allow each of them in turn to take + a value of $1$. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/env-mlb1.yaml}[MLB-TCB]{\texttt{env-mlb1.yaml}}{lst:env-mlb1} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/env-mlb2.yaml}[MLB-TCB]{\texttt{env-mlb2.yaml}}{lst:env-mlb2} + \end{minipage} + + After running the following commands, + \begin{commandshell} +latexindent.pl -m env-mlb.tex -l env-mlb1.yaml +latexindent.pl -m env-mlb.tex -l env-mlb2.yaml +\end{commandshell} + the output is as in \cref{lst:env-mlb-mod1,lst:env-mlb-mod2} respectively. + + \begin{widepage} + \begin{minipage}{.56\linewidth} + \cmhlistingsfromfile{demonstrations/env-mlb-mod1.tex}{\texttt{env-mlb.tex} using \cref{lst:env-mlb1}}{lst:env-mlb-mod1} + \end{minipage} + \hfill + \begin{minipage}{.43\linewidth} + \cmhlistingsfromfile{demonstrations/env-mlb-mod2.tex}{\texttt{env-mlb.tex} using \cref{lst:env-mlb2}}{lst:env-mlb-mod2} + \end{minipage} + \end{widepage} + + There are a couple of points to note: + \begin{itemize} + \item in \cref{lst:env-mlb-mod1} a line break has been added at the point denoted by + $\BeginStartsOnOwnLine$ in \cref{lst:env-mlb1-tex}; no other line breaks have been + changed; + \item in \cref{lst:env-mlb-mod2} a line break has been added at the point denoted by + $\BodyStartsOnOwnLine$ in \cref{lst:env-mlb1-tex}; furthermore, note that the + \emph{body} of \texttt{myenv} has received the appropriate + (default) indentation. + \end{itemize} + + Let's now change each of the \texttt{1} values in + \cref{lst:env-mlb1,lst:env-mlb2} so that they are $2$ and save them + into \texttt{env-mlb3.yaml} and \texttt{env-mlb4.yaml} respectively (see + \cref{lst:env-mlb3,lst:env-mlb4}). + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/env-mlb3.yaml}[MLB-TCB]{\texttt{env-mlb3.yaml}}{lst:env-mlb3} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/env-mlb4.yaml}[MLB-TCB]{\texttt{env-mlb4.yaml}}{lst:env-mlb4} + \end{minipage} + + Upon running commands analogous to the above, we obtain \cref{lst:env-mlb-mod3,lst:env-mlb-mod4}. + + \begin{widepage} + \begin{minipage}{.56\linewidth} + \cmhlistingsfromfile{demonstrations/env-mlb-mod3.tex}{\texttt{env-mlb.tex} using \cref{lst:env-mlb3}}{lst:env-mlb-mod3} + \end{minipage} + \hfill + \begin{minipage}{.43\linewidth} + \cmhlistingsfromfile{demonstrations/env-mlb-mod4.tex}{\texttt{env-mlb.tex} using \cref{lst:env-mlb4}}{lst:env-mlb-mod4} + \end{minipage} + \end{widepage} + + Note that line breaks have been added as in \cref{lst:env-mlb-mod1,lst:env-mlb-mod2}, but this time a + comment symbol has been added before adding the line break; in both cases, trailing + horizontal space has been stripped before doing so. + + Let's% + \announce{2017-08-21}{demonstration of blank line poly-switch (3)} now change each of + the \texttt{1} values in \cref{lst:env-mlb1,lst:env-mlb2} so that they are + $3$ and save them into \texttt{env-mlb5.yaml} and + \texttt{env-mlb6.yaml} respectively (see \cref{lst:env-mlb5,lst:env-mlb6}). + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/env-mlb5.yaml}[MLB-TCB]{\texttt{env-mlb5.yaml}}{lst:env-mlb5} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/env-mlb6.yaml}[MLB-TCB]{\texttt{env-mlb6.yaml}}{lst:env-mlb6} + \end{minipage} + + Upon running commands analogous to the above, we obtain \cref{lst:env-mlb-mod5,lst:env-mlb-mod6}. + + \begin{widepage} + \begin{minipage}{.56\linewidth} + \cmhlistingsfromfile{demonstrations/env-mlb-mod5.tex}{\texttt{env-mlb.tex} using \cref{lst:env-mlb5}}{lst:env-mlb-mod5} + \end{minipage} + \hfill + \begin{minipage}{.43\linewidth} + \cmhlistingsfromfile{demonstrations/env-mlb-mod6.tex}{\texttt{env-mlb.tex} using \cref{lst:env-mlb6}}{lst:env-mlb-mod6} + \end{minipage} + \end{widepage} + + Note that line breaks have been added as in \cref{lst:env-mlb-mod1,lst:env-mlb-mod2}, but this time a + \emph{blank line} has been added after adding the line break. + + Let's now change% + \announce*{2019-07-13}{demonstration of new blank line poly-switch} each of + the \texttt{1} values in \cref{lst:env-mlb5,lst:env-mlb6} so that they are + $4$ and save them into \texttt{env-beg4.yaml} and + \texttt{env-body4.yaml} respectively (see \cref{lst:env-beg4,lst:env-body4}). + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/env-beg4.yaml}[MLB-TCB]{\texttt{env-beg4.yaml}}{lst:env-beg4} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/env-body4.yaml}[MLB-TCB]{\texttt{env-body4.yaml}}{lst:env-body4} + \end{minipage} + + We will demonstrate this poly-switch value using the code in \cref{lst:env-mlb1-text}. + + \cmhlistingsfromfile*{demonstrations/env-mlb1.tex}{\texttt{env-mlb1.tex}}{lst:env-mlb1-text} + + Upon running the commands + \begin{commandshell} +latexindent.pl -m env-mlb1.tex -l env-beg4.yaml +latexindent.pl -m env-mlb.1tex -l env-body4.yaml +\end{commandshell} + + then we receive the respective outputs in \cref{lst:env-mlb1-beg4,lst:env-mlb1-body4}. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile*{demonstrations/env-mlb1-beg4.tex}{\texttt{env-mlb1.tex} using \cref{lst:env-beg4}}{lst:env-mlb1-beg4} + \cmhlistingsfromfile*{demonstrations/env-mlb1-body4.tex}{\texttt{env-mlb1.tex} using \cref{lst:env-body4}}{lst:env-mlb1-body4} + \end{cmhtcbraster} + + We note in particular that, by design, for this value of the poly-switches: + \begin{enumerate} + \item in \cref{lst:env-mlb1-beg4} a blank line has been inserted before the + \lstinline!\begin! statement, even though the \lstinline!\begin! + statement was already on its own line; + \item in \cref{lst:env-mlb1-body4} a blank line has been inserted before the beginning of the + \emph{body}, even though it already began on its own line. + \end{enumerate} + +\subsubsection{Adding line breaks using EndStartsOnOwnLine and EndFinishesWithLineBreak} + Let's explore \texttt{EndStartsOnOwnLine} and \texttt{EndFinishesWithLineBreak} in + \cref{lst:env-mlb7,lst:env-mlb8}, and in particular, let's allow each of them in turn to take + a value of $1$. + + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/env-mlb7.yaml}[MLB-TCB]{\texttt{env-mlb7.yaml}}{lst:env-mlb7} + \end{minipage} + \hfill + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/env-mlb8.yaml}[MLB-TCB]{\texttt{env-mlb8.yaml}}{lst:env-mlb8} + \end{minipage} + + After running the following commands, + \begin{commandshell} +latexindent.pl -m env-mlb.tex -l env-mlb7.yaml +latexindent.pl -m env-mlb.tex -l env-mlb8.yaml +\end{commandshell} + the output is as in \cref{lst:env-mlb-mod7,lst:env-mlb-mod8}. + + \begin{widepage} + \begin{minipage}{.42\linewidth} + \cmhlistingsfromfile{demonstrations/env-mlb-mod7.tex}{\texttt{env-mlb.tex} using \cref{lst:env-mlb7}}{lst:env-mlb-mod7} + \end{minipage} + \hfill + \begin{minipage}{.57\linewidth} + \cmhlistingsfromfile{demonstrations/env-mlb-mod8.tex}{\texttt{env-mlb.tex} using \cref{lst:env-mlb8}}{lst:env-mlb-mod8} + \end{minipage} + \end{widepage} + + There are a couple of points to note: + \begin{itemize} + \item in \cref{lst:env-mlb-mod7} a line break has been added at the point denoted by + $\EndStartsOnOwnLine$ in \vref{lst:env-mlb1-tex}; no other line breaks have been + changed and the \lstinline!\end{myenv}! statement has \emph{not} + received indentation (as intended); + \item in \cref{lst:env-mlb-mod8} a line break has been added at the point denoted by + $\EndFinishesWithLineBreak$ in \vref{lst:env-mlb1-tex}. + \end{itemize} + + Let's now change each of the \texttt{1} values in + \cref{lst:env-mlb7,lst:env-mlb8} so that they are $2$ and save them + into \texttt{env-mlb9.yaml} and \texttt{env-mlb10.yaml} respectively (see + \cref{lst:env-mlb9,lst:env-mlb10}). + + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/env-mlb9.yaml}[MLB-TCB]{\texttt{env-mlb9.yaml}}{lst:env-mlb9} + \end{minipage} + \hfill + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/env-mlb10.yaml}[MLB-TCB]{\texttt{env-mlb10.yaml}}{lst:env-mlb10} + \end{minipage} + + Upon running commands analogous to the above, we obtain \cref{lst:env-mlb-mod9,lst:env-mlb-mod10}. + + \begin{widepage} + \begin{minipage}{.43\linewidth} + \cmhlistingsfromfile{demonstrations/env-mlb-mod9.tex}{\texttt{env-mlb.tex} using \cref{lst:env-mlb9}}{lst:env-mlb-mod9} + \end{minipage} + \hfill + \begin{minipage}{.56\linewidth} + \cmhlistingsfromfile{demonstrations/env-mlb-mod10.tex}{\texttt{env-mlb.tex} using \cref{lst:env-mlb10}}{lst:env-mlb-mod10} + \end{minipage} + \end{widepage} + + Note that line breaks have been added as in \cref{lst:env-mlb-mod7,lst:env-mlb-mod8}, but this time a + comment symbol has been added before adding the line break; in both cases, trailing + horizontal space has been stripped before doing so. + + Let's% + \announce{2017-08-21}{demonstration of blank line poly-switch (3)} now change each of + the \texttt{1} values in \cref{lst:env-mlb7,lst:env-mlb8} so that they are + $3$ and save them into \texttt{env-mlb11.yaml} and + \texttt{env-mlb12.yaml} respectively (see \cref{lst:env-mlb11,lst:env-mlb12}). + + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/env-mlb11.yaml}[MLB-TCB]{\texttt{env-mlb11.yaml}}{lst:env-mlb11} + \end{minipage} + \hfill + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/env-mlb12.yaml}[MLB-TCB]{\texttt{env-mlb12.yaml}}{lst:env-mlb12} + \end{minipage} + + Upon running commands analogous to the above, we obtain \cref{lst:env-mlb-mod11,lst:env-mlb-mod12}. + + \begin{widepage} + \begin{minipage}{.42\linewidth} + \cmhlistingsfromfile{demonstrations/env-mlb-mod11.tex}{\texttt{env-mlb.tex} using \cref{lst:env-mlb11}}{lst:env-mlb-mod11} + \end{minipage} + \hfill + \begin{minipage}{.57\linewidth} + \cmhlistingsfromfile{demonstrations/env-mlb-mod12.tex}{\texttt{env-mlb.tex} using \cref{lst:env-mlb12}}{lst:env-mlb-mod12} + \end{minipage} + \end{widepage} + + Note that line breaks have been added as in \cref{lst:env-mlb-mod7,lst:env-mlb-mod8}, and that a + \emph{blank line} has been added after the line break. + + Let's now change% + \announce*{2019-07-13}{demonstration of new blank line poly-switch} each of + the \texttt{1} values in \cref{lst:env-mlb11,lst:env-mlb12} so that they are + $4$ and save them into \texttt{env-end4.yaml} and + \texttt{env-end-f4.yaml} respectively (see \cref{lst:env-end4,lst:env-end-f4}). + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/env-end4.yaml}[MLB-TCB]{\texttt{env-end4.yaml}}{lst:env-end4} + \end{minipage} + \hfill + \begin{minipage}{.5\textwidth} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/env-end-f4.yaml}[MLB-TCB]{\texttt{env-end-f4.yaml}}{lst:env-end-f4} + \end{minipage} + + We will demonstrate this poly-switch value using the code from + \vref{lst:env-mlb1-text}. + + Upon running the commands + \begin{commandshell} +latexindent.pl -m env-mlb1.tex -l env-end4.yaml +latexindent.pl -m env-mlb.1tex -l env-end-f4.yaml +\end{commandshell} + + then we receive the respective outputs in \cref{lst:env-mlb1-end4,lst:env-mlb1-end-f4}. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile*{demonstrations/env-mlb1-end4.tex}{\texttt{env-mlb1.tex} using \cref{lst:env-end4}}{lst:env-mlb1-end4} + \cmhlistingsfromfile*{demonstrations/env-mlb1-end-f4.tex}{\texttt{env-mlb1.tex} using \cref{lst:env-end-f4}}{lst:env-mlb1-end-f4} + \end{cmhtcbraster} + + We note in particular that, by design, for this value of the poly-switches: + \begin{enumerate} + \item in \cref{lst:env-mlb1-end4} a blank line has been inserted before the + \lstinline!\end! statement, even though the \lstinline!\end! + statement was already on its own line; + \item in \cref{lst:env-mlb1-end-f4} a blank line has been inserted after the + \lstinline!\end! statement, even though it already began on its own line. + \end{enumerate} + +\subsubsection{poly-switches 1, 2, and 3 only add line breaks when necessary} + If you ask \texttt{latexindent.pl} to add a line break (possibly with a comment) + using a poly-switch value of $1$ (or $2$ + or $3$), it will only do so if necessary. For example, if you + process the file in \vref{lst:mlb2} using poly-switch values of 1, 2, or 3, + it will be left unchanged. + + \begin{minipage}{.45\linewidth} + \cmhlistingsfromfile{demonstrations/env-mlb2.tex}{\texttt{env-mlb2.tex}}{lst:mlb2} + \end{minipage} + \hfill + \begin{minipage}{.45\linewidth} + \cmhlistingsfromfile{demonstrations/env-mlb3.tex}{\texttt{env-mlb3.tex}}{lst:mlb3} + \end{minipage} + + Setting the poly-switches to a value of $4$ instructs + \texttt{latexindent.pl} to add a line break even if the \emph{<part of thing>} + is already on its own line; see \cref{lst:env-mlb1-beg4,lst:env-mlb1-body4} and + \cref{lst:env-mlb1-end4,lst:env-mlb1-end-f4}. + + In contrast, the output from processing the file in \cref{lst:mlb3} will + vary depending on the poly-switches used; in \cref{lst:env-mlb3-mod2} you'll see that + the comment symbol after the \lstinline!\begin{myenv}! has been moved to the next line, + as \texttt{BodyStartsOnOwnLine} is set to \texttt{1}. In + \cref{lst:env-mlb3-mod4} you'll see that the comment has been accounted for correctly + because \texttt{BodyStartsOnOwnLine} has been set to \texttt{2}, and + the comment symbol has \emph{not} been moved to its own line. You're + encouraged to experiment with \cref{lst:mlb3} and by setting the other + poly-switches considered so far to \texttt{2} in turn. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile{demonstrations/env-mlb3-mod2.tex}{\texttt{env-mlb3.tex} using \vref{lst:env-mlb2}}{lst:env-mlb3-mod2} + \cmhlistingsfromfile{demonstrations/env-mlb3-mod4.tex}{\texttt{env-mlb3.tex} using \vref{lst:env-mlb4}}{lst:env-mlb3-mod4} + \end{cmhtcbraster} + + The details of the discussion in this section have concerned \emph{global} + poly-switches in the \texttt{environments} field; each switch can also be + specified on a \emph{per-name} basis, which would take priority over the + global values; with reference to \vref{lst:environments-mlb}, an example is shown for + the \texttt{equation*} environment. + +\subsubsection{Removing line breaks (poly-switches set to $-1$)} + Setting poly-switches to $-1$ tells \texttt{latexindent.pl} + to remove line breaks of the \emph{<part of the thing>}, if necessary. We will consider + the example code given in \cref{lst:mlb4}, noting in particular the + positions of the line break highlighters, $\BeginStartsOnOwnLine$, + $\BodyStartsOnOwnLine$, $\EndStartsOnOwnLine$ and $\EndFinishesWithLineBreak$, + together with the associated YAML files in \crefrange{lst:env-mlb13}{lst:env-mlb16}. + + \begin{minipage}{.45\linewidth} + \begin{cmhlistings}[style=tcblatex,escapeinside={(*@}{@*)}]{\texttt{env-mlb4.tex}}{lst:mlb4} +before words(*@$\BeginStartsOnOwnLine$@*) +\begin{myenv}(*@$\BodyStartsOnOwnLine$@*) +body of myenv(*@$\EndStartsOnOwnLine$@*) +\end{myenv}(*@$\EndFinishesWithLineBreak$@*) +after words +\end{cmhlistings} + \end{minipage}% + \hfill + \begin{minipage}{.51\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/env-mlb13.yaml}[MLB-TCB]{\texttt{env-mlb13.yaml}}{lst:env-mlb13} + + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/env-mlb14.yaml}[MLB-TCB]{\texttt{env-mlb14.yaml}}{lst:env-mlb14} + + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/env-mlb15.yaml}[MLB-TCB]{\texttt{env-mlb15.yaml}}{lst:env-mlb15} + + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/env-mlb16.yaml}[MLB-TCB]{\texttt{env-mlb16.yaml}}{lst:env-mlb16} + \end{minipage} + + After running the commands + \begin{commandshell} +latexindent.pl -m env-mlb4.tex -l env-mlb13.yaml +latexindent.pl -m env-mlb4.tex -l env-mlb14.yaml +latexindent.pl -m env-mlb4.tex -l env-mlb15.yaml +latexindent.pl -m env-mlb4.tex -l env-mlb16.yaml +\end{commandshell} + + we obtain the respective output in \crefrange{lst:env-mlb4-mod13}{lst:env-mlb4-mod16}. + + \begin{minipage}{.45\linewidth} + \cmhlistingsfromfile{demonstrations/env-mlb4-mod13.tex}{\texttt{env-mlb4.tex} using \cref{lst:env-mlb13}}{lst:env-mlb4-mod13} + \end{minipage} + \hfill + \begin{minipage}{.45\linewidth} + \cmhlistingsfromfile{demonstrations/env-mlb4-mod14.tex}{\texttt{env-mlb4.tex} using \cref{lst:env-mlb14}}{lst:env-mlb4-mod14} + \end{minipage} + + \begin{minipage}{.45\linewidth} + \cmhlistingsfromfile{demonstrations/env-mlb4-mod15.tex}{\texttt{env-mlb4.tex} using \cref{lst:env-mlb15}}{lst:env-mlb4-mod15} + \end{minipage} + \hfill + \begin{minipage}{.45\linewidth} + \cmhlistingsfromfile{demonstrations/env-mlb4-mod16.tex}{\texttt{env-mlb4.tex} using \cref{lst:env-mlb16}}{lst:env-mlb4-mod16} + \end{minipage} + + Notice that in: + \begin{itemize} + \item \cref{lst:env-mlb4-mod13} the line break denoted by $\BeginStartsOnOwnLine$ in + \cref{lst:mlb4} has been removed; + \item \cref{lst:env-mlb4-mod14} the line break denoted by $\BodyStartsOnOwnLine$ in + \cref{lst:mlb4} has been removed; + \item \cref{lst:env-mlb4-mod15} the line break denoted by $\EndStartsOnOwnLine$ in + \cref{lst:mlb4} has been removed; + \item \cref{lst:env-mlb4-mod16} the line break denoted by $\EndFinishesWithLineBreak$ in + \cref{lst:mlb4} has been removed. + \end{itemize} + We examined each of these cases separately for clarity of explanation, but you can + combine all of the YAML settings in \crefrange{lst:env-mlb13}{lst:env-mlb16} into one file; + alternatively, you could tell \texttt{latexindent.pl} to load them all by using the + following command, for example + \begin{widepage} + \begin{commandshell} +latexindent.pl -m env-mlb4.tex -l env-mlb13.yaml,env-mlb14.yaml,env-mlb15.yaml,env-mlb16.yaml +\end{commandshell} + \end{widepage} + which gives the output in \vref{lst:env-mlb1-tex}. + +\subsubsection{About trailing horizontal space} + Recall that on \cpageref{yaml:removeTrailingWhitespace} we discussed the YAML field + \texttt{removeTrailingWhitespace}, and that it has two (binary) switches to determine if + horizontal space should be removed \texttt{beforeProcessing} and + \texttt{afterProcessing}. The \texttt{beforeProcessing} is particularly relevant + when considering the \texttt{-m} switch; let's consider the file shown + in \cref{lst:mlb5}, which highlights trailing spaces. + + \begin{cmhtcbraster} + \begin{cmhlistings}[style=tcblatex,showspaces=true,escapeinside={(*@}{@*)}]{\texttt{env-mlb5.tex}}{lst:mlb5} +before words (*@$\BeginStartsOnOwnLine$@*) +\begin{myenv} (*@$\BodyStartsOnOwnLine$@*) +body of myenv (*@$\EndStartsOnOwnLine$@*) +\end{myenv} (*@$\EndFinishesWithLineBreak$@*) +after words +\end{cmhlistings} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/removeTWS-before.yaml}[yaml-TCB]{\texttt{removeTWS-before.yaml}}{lst:removeTWS-before} + \end{cmhtcbraster} + + The output from the following commands + \begin{widepage} + \begin{commandshell} +latexindent.pl -m env-mlb5.tex -l env-mlb13.yaml,env-mlb14.yaml,env-mlb15.yaml,env-mlb16.yaml +latexindent.pl -m env-mlb5.tex -l env-mlb13.yaml,env-mlb14.yaml,env-mlb15.yaml,env-mlb16.yaml,removeTWS-before.yaml +\end{commandshell} + \end{widepage} + is shown, respectively, in \cref{lst:env-mlb5-modAll,lst:env-mlb5-modAll-remove-WS}; note that the trailing + horizontal white space has been preserved (by default) in \cref{lst:env-mlb5-modAll}, + while in \cref{lst:env-mlb5-modAll-remove-WS}, it has been removed using the switch specified in + \cref{lst:removeTWS-before}. + + \begin{widepage} + \cmhlistingsfromfile[showspaces=true]{demonstrations/env-mlb5-modAll.tex}{\texttt{env-mlb5.tex} using \crefrange{lst:env-mlb4-mod13}{lst:env-mlb4-mod16}}{lst:env-mlb5-modAll} + + \cmhlistingsfromfile[showspaces=true]{demonstrations/env-mlb5-modAll-remove-WS.tex}{\texttt{env-mlb5.tex} using \crefrange{lst:env-mlb4-mod13}{lst:env-mlb4-mod16} \emph{and} \cref{lst:removeTWS-before}}{lst:env-mlb5-modAll-remove-WS} + \end{widepage} + +\subsubsection{poly-switch line break removal and blank lines} + Now let's consider the file in \cref{lst:mlb6}, which contains blank lines. + + \begin{cmhtcbraster} + \begin{cmhlistings}[style=tcblatex,escapeinside={(*@}{@*)}]{\texttt{env-mlb6.tex}}{lst:mlb6} +before words(*@$\BeginStartsOnOwnLine$@*) + + +\begin{myenv}(*@$\BodyStartsOnOwnLine$@*) + + +body of myenv(*@$\EndStartsOnOwnLine$@*) + + +\end{myenv}(*@$\EndFinishesWithLineBreak$@*) + +after words +\end{cmhlistings} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/UnpreserveBlankLines.yaml}[MLB-TCB]{\texttt{UnpreserveBlankLines.yaml}}{lst:UnpreserveBlankLines} + \end{cmhtcbraster} + + Upon running the following commands + \begin{widepage} + \begin{commandshell} +latexindent.pl -m env-mlb6.tex -l env-mlb13.yaml,env-mlb14.yaml,env-mlb15.yaml,env-mlb16.yaml +latexindent.pl -m env-mlb6.tex -l env-mlb13.yaml,env-mlb14.yaml,env-mlb15.yaml,env-mlb16.yaml,UnpreserveBlankLines.yaml +\end{commandshell} + \end{widepage} + we receive the respective outputs in \cref{lst:env-mlb6-modAll,lst:env-mlb6-modAll-un-Preserve-Blank-Lines}. In + \cref{lst:env-mlb6-modAll} we see that the multiple blank lines have each been + condensed into one blank line, but that blank lines have \emph{not} + been removed by the poly-switches -- this is because, by default, + \texttt{preserveBlankLines} is set to \texttt{1}. By contrast, in + \cref{lst:env-mlb6-modAll-un-Preserve-Blank-Lines}, we have allowed the poly-switches to remove blank lines + because, in \cref{lst:UnpreserveBlankLines}, we have set \texttt{preserveBlankLines} to + \texttt{0}. + + \begin{cmhtcbraster}[ raster left skip=-3.5cm, + raster right skip=-2cm, + raster force size=false, + raster column 1/.style={add to width=-.2\textwidth}, + raster column 2/.style={add to width=.2\textwidth}, + raster column skip=.06\linewidth] + \cmhlistingsfromfile{demonstrations/env-mlb6-modAll.tex}{\texttt{env-mlb6.tex} using \crefrange{lst:env-mlb4-mod13}{lst:env-mlb4-mod16}}{lst:env-mlb6-modAll} + \cmhlistingsfromfile{demonstrations/env-mlb6-modAll-un-Preserve-Blank-Lines.tex}{\texttt{env-mlb6.tex} using \crefrange{lst:env-mlb4-mod13}{lst:env-mlb4-mod16} \emph{and} \cref{lst:UnpreserveBlankLines}}{lst:env-mlb6-modAll-un-Preserve-Blank-Lines} + \end{cmhtcbraster} + + We can explore this further using the blank-line poly-switch value of + $3$; let's use the file given in \cref{lst:env-mlb7-tex}. + + \cmhlistingsfromfile{demonstrations/env-mlb7.tex}{\texttt{env-mlb7.tex}}{lst:env-mlb7-tex} + + Upon running the following commands + \begin{commandshell} +latexindent.pl -m env-mlb7.tex -l env-mlb12.yaml,env-mlb13.yaml +latexindent.pl -m env-mlb7.tex -l env-mlb13.yaml,env-mlb14.yaml,UnpreserveBlankLines.yaml +\end{commandshell} + we receive the outputs given in \cref{lst:env-mlb7-preserve,lst:env-mlb7-no-preserve}. + + \cmhlistingsfromfile{demonstrations/env-mlb7-preserve.tex}{\texttt{env-mlb7-preserve.tex}}{lst:env-mlb7-preserve} + \cmhlistingsfromfile{demonstrations/env-mlb7-no-preserve.tex}{\texttt{env-mlb7-no-preserve.tex}}{lst:env-mlb7-no-preserve} + + Notice that in: + \begin{itemize} + \item \cref{lst:env-mlb7-preserve} that \lstinline!\end{one}! has added a blank line, + because of the value of \texttt{EndFinishesWithLineBreak} in \vref{lst:env-mlb12}, and + even though the line break ahead of \lstinline!\begin{two}! should have been removed + (because of \texttt{BeginStartsOnOwnLine} in \vref{lst:env-mlb13}), the blank line + has been preserved by default; + \item \cref{lst:env-mlb7-no-preserve}, by contrast, has had the additional line-break removed, + because of the settings in \cref{lst:UnpreserveBlankLines}. + \end{itemize} + +\subsection{Poly-switches for double back slash}\label{subsec:dbs} + With reference to \texttt{lookForAlignDelims} (see \vref{lst:aligndelims:basic})% + \announce*{2019-07-13}{poly-switch for double back slash} you can + specify poly-switches to dictate the line-break behaviour of double back slashes in + environments (\vref{lst:tabularafter:basic}), commands (\vref{lst:matrixafter}), or + special code blocks (\vref{lst:specialafter}). Note that for these poly-switches to + take effect, the name of the code block must necessarily be specified within + \texttt{lookForAlignDelims} (\vref{lst:aligndelims:basic}); we will demonstrate this in what follows. + + Consider the code given in \cref{lst:dbs-demo}. + \begin{cmhlistings}*[style=tcblatex,escapeinside={(*@}{@*)}]{\texttt{tabular3.tex}}{lst:dbs-demo} +\begin{tabular}{cc} + 1 & 2 (*@$\ElseStartsOnOwnLine$@*)\\(*@$\ElseFinishesWithLineBreak$@*) 3 & 4 (*@$\ElseStartsOnOwnLine$@*)\\(*@$\ElseFinishesWithLineBreak$@*) +\end{tabular} +\end{cmhlistings} + Referencing \cref{lst:dbs-demo}: + \begin{itemize} + \item \texttt{DBS} stands for \emph{double back slash}; + \item line breaks ahead of the double back slash are annotated by $\ElseStartsOnOwnLine$, + and are controlled by \texttt{DBSStartsOnOwnLine}; + \item line breaks after the double back slash are annotated by $\ElseFinishesWithLineBreak$, and + are controlled by \texttt{DBSFinishesWithLineBreak}. + \end{itemize} + + Let's explore each of these in turn. + +\subsubsection{Double back slash starts on own line} + We explore \texttt{DBSStartsOnOwnLine} ($\ElseStartsOnOwnLine$ in \cref{lst:dbs-demo}); starting with the code in + \cref{lst:dbs-demo}, together with the YAML files given in + \cref{lst:DBS1} and \cref{lst:DBS2} and running the following + commands + \begin{commandshell} +latexindent.pl -m tabular3.tex -l DBS1.yaml +latexindent.pl -m tabular3.tex -l DBS2.yaml +\end{commandshell} + then we receive the respective output given in \cref{lst:tabular3-DBS1} and + \cref{lst:tabular3-DBS2}. + + \begin{cmhtcbraster}[raster column skip=.01\linewidth] + \cmhlistingsfromfile*{demonstrations/tabular3-mod1.tex}{\texttt{tabular3.tex} using \cref{lst:DBS1}}{lst:tabular3-DBS1} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/DBS1.yaml}[MLB-TCB]{\texttt{DBS1.yaml}}{lst:DBS1} + \end{cmhtcbraster} + + \begin{cmhtcbraster}[raster column skip=.01\linewidth] + \cmhlistingsfromfile*{demonstrations/tabular3-mod2.tex}{\texttt{tabular3.tex} using \cref{lst:DBS2}}{lst:tabular3-DBS2} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/DBS2.yaml}[MLB-TCB]{\texttt{DBS2.yaml}}{lst:DBS2} + \end{cmhtcbraster} + + We note that + \begin{itemize} + \item \cref{lst:DBS1} specifies \texttt{DBSStartsOnOwnLine} for + \emph{every} environment (that is within \texttt{lookForAlignDelims}, + \vref{yaml:lookforaligndelims}); + the double back slashes from \cref{lst:dbs-demo} have been moved to their own + line in \cref{lst:tabular3-DBS1}; + \item \cref{lst:DBS2} specifies \texttt{DBSStartsOnOwnLine} on a + \emph{per-name} basis for \texttt{tabular} (that is within \texttt{lookForAlignDelims}, \vref{yaml:lookforaligndelims}); + the double back slashes from \cref{lst:dbs-demo} have been moved to their own + line in \cref{lst:tabular3-DBS2}, having added comment symbols before moving them. + \end{itemize} + +\subsubsection{Double back slash finishes with line break} + Let's now explore \texttt{DBSFinishesWithLineBreak} ($\ElseFinishesWithLineBreak$ in \cref{lst:dbs-demo}); starting with the code in + \cref{lst:dbs-demo}, together with the YAML files given in + \cref{lst:DBS3} and \cref{lst:DBS4} and running the following + commands + \begin{commandshell} +latexindent.pl -m tabular3.tex -l DBS3.yaml +latexindent.pl -m tabular3.tex -l DBS4.yaml +\end{commandshell} + then we receive the respective output given in \cref{lst:tabular3-DBS3} and + \cref{lst:tabular3-DBS4}. + + \begin{cmhtcbraster}[raster column skip=.01\linewidth] + \cmhlistingsfromfile*{demonstrations/tabular3-mod3.tex}{\texttt{tabular3.tex} using \cref{lst:DBS3}}{lst:tabular3-DBS3} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/DBS3.yaml}[MLB-TCB]{\texttt{DBS3.yaml}}{lst:DBS3} + \end{cmhtcbraster} + + \begin{cmhtcbraster}[raster column skip=.01\linewidth] + \cmhlistingsfromfile*{demonstrations/tabular3-mod4.tex}{\texttt{tabular3.tex} using \cref{lst:DBS4}}{lst:tabular3-DBS4} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/DBS4.yaml}[MLB-TCB]{\texttt{DBS4.yaml}}{lst:DBS4} + \end{cmhtcbraster} + + We note that + \begin{itemize} + \item \cref{lst:DBS3} specifies \texttt{DBSFinishesWithLineBreak} for + \emph{every} environment (that is within \texttt{lookForAlignDelims}, + \vref{yaml:lookforaligndelims}); + the code following the double back slashes from \cref{lst:dbs-demo} has been + moved to their own line in \cref{lst:tabular3-DBS3}; + \item \cref{lst:DBS4} specifies \texttt{DBSFinishesWithLineBreak} on a + \emph{per-name} basis for \texttt{tabular} (that is within \texttt{lookForAlignDelims}, \vref{yaml:lookforaligndelims}); + the first double back slashes from \cref{lst:dbs-demo} have moved code following + them to their own line in \cref{lst:tabular3-DBS4}, having added comment symbols + before moving them; the final double back slashes have \emph{not} added + a line break as they are at the end of the body within the code block. + \end{itemize} + +\subsubsection{Double back slash poly switches for specialBeginEnd} + Let's explore the double back slash poly-switches for code blocks within + \texttt{specialBeginEnd} code blocks (\vref{lst:specialBeginEnd}); we begin with + the code within \cref{lst:special4}. + + \cmhlistingsfromfile*{demonstrations/special4.tex}{\texttt{special4.tex}}{lst:special4} + + Upon using the YAML settings in \cref{lst:DBS5}, and running the command + \begin{commandshell} +latexindent.pl -m special4.tex -l DBS5.yaml +\end{commandshell} + then we receive the output given in \cref{lst:special4-DBS5}. + + \begin{cmhtcbraster}[ + raster force size=false, + raster column 1/.style={add to width=-.1\textwidth}, + raster column skip=.06\linewidth] + \cmhlistingsfromfile*{demonstrations/special4-mod5.tex}{\texttt{special4.tex} using \cref{lst:DBS5}}{lst:special4-DBS5} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/DBS5.yaml}[MLB-TCB,width=0.6\textwidth]{\texttt{DBS5.yaml}}{lst:DBS5} + \end{cmhtcbraster} + + There are a few things to note: + \begin{itemize} + \item in \cref{lst:DBS5} we have specified \texttt{cmhMath} within + \texttt{lookForAlignDelims}; without this, the double back slash poly-switches would be + ignored for this code block; + \item the \texttt{DBSFinishesWithLineBreak} poly-switch has controlled the line breaks following the + double back slashes; + \item the \texttt{SpecialEndStartsOnOwnLine} poly-switch has controlled the addition of a comment + symbol, followed by a line break, as it is set to a value of 2. + \end{itemize} + +\subsubsection{Double back slash poly switches for optional and mandatory arguments} + For clarity, we provide a demonstration of controlling the double back slash + poly-switches for optional and mandatory arguments. We begin with the code in + \cref{lst:mycommand2}. + + \cmhlistingsfromfile*{demonstrations/mycommand2.tex}{\texttt{mycommand2.tex}}{lst:mycommand2} + + Upon using the YAML settings in \cref{lst:DBS6,lst:DBS7}, and running the command + \begin{commandshell} +latexindent.pl -m mycommand2.tex -l DBS6.yaml +latexindent.pl -m mycommand2.tex -l DBS7.yaml +\end{commandshell} + then we receive the output given in \cref{lst:mycommand2-DBS6,lst:mycommand2-DBS7}. + + \begin{cmhtcbraster}[ + raster force size=false, + raster column 1/.style={add to width=-.1\textwidth}, + raster column skip=.03\linewidth] + \cmhlistingsfromfile*{demonstrations/mycommand2-mod6.tex}{\texttt{mycommand2.tex} using \cref{lst:DBS6}}{lst:mycommand2-DBS6} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/DBS6.yaml}[MLB-TCB,width=0.6\textwidth]{\texttt{DBS6.yaml}}{lst:DBS6} + \end{cmhtcbraster} + + \begin{cmhtcbraster}[ + raster force size=false, + raster column 1/.style={add to width=-.1\textwidth}, + raster column skip=.03\linewidth] + \cmhlistingsfromfile*{demonstrations/mycommand2-mod7.tex}{\texttt{mycommand2.tex} using \cref{lst:DBS7}}{lst:mycommand2-DBS7} + \cmhlistingsfromfile*[style=yaml-LST]*{demonstrations/DBS7.yaml}[MLB-TCB,width=0.6\textwidth]{\texttt{DBS7.yaml}}{lst:DBS7} + \end{cmhtcbraster} + +\subsubsection{Double back slash optional square brackets} + The pattern matching for the double back slash will also, optionally, allow trailing + square brackets that contain a measurement of vertical spacing, for example + \lstinline!\\[3pt]!. + + For example, beginning with the code in \cref{lst:pmatrix3} + + \cmhlistingsfromfile*{demonstrations/pmatrix3.tex}{\texttt{pmatrix3.tex}}{lst:pmatrix3} + + and running the following command, using \cref{lst:DBS3}, + \begin{commandshell} +latexindent.pl -m pmatrix3.tex -l DBS3.yaml +\end{commandshell} + then we receive the output given in \cref{lst:pmatrix3-DBS3}. + + \cmhlistingsfromfile*{demonstrations/pmatrix3-mod3.tex}{\texttt{pmatrix3.tex} using \cref{lst:DBS3}}{lst:pmatrix3-DBS3} + + You can customise the pattern for the double back slash by exploring the + \emph{fine tuning} field detailed in \vref{lst:fineTuning}. + +\subsection{Poly-switches for other code blocks} + Rather than repeat the examples shown for the environment code blocks (in + \vref{sec:modifylinebreaks-environments}), we choose to detail the poly-switches for all other code + blocks in \cref{tab:poly-switch-mapping}; note that each and every one of these + poly-switches is \emph{off by default}, i.e, set to \texttt{0}. + + Note also that, by design, line breaks involving, \texttt{filecontents} and + `comment-marked' code blocks (\vref{lst:alignmentmarkup}) can + \emph{not} be modified using + \texttt{latexindent.pl}.% + \announce{2019-05-05}*{verbatim poly-switch} However, there are two poly-switches available for + \texttt{verbatim} code blocks: environments (\vref{lst:verbatimEnvironments}), + commands (\vref{lst:verbatimCommands}) and \texttt{specialBeginEnd} (\vref{lst:special-verb1-yaml}). + + \clearpage + \begin{longtable}{llll} + \caption{Poly-switch mappings for all code-block types}\label{tab:poly-switch-mapping} \\ + \toprule + Code block & Sample & \multicolumn{2}{c}{Poly-switch mapping} \\ + \midrule + environment & \verb!before words!$\BeginStartsOnOwnLine$ & $\BeginStartsOnOwnLine$ & BeginStartsOnOwnLine \\ + & \verb!\begin{myenv}!$\BodyStartsOnOwnLine$ & $\BodyStartsOnOwnLine$ & BodyStartsOnOwnLine \\ + & \verb!body of myenv!$\EndStartsOnOwnLine$ & $\EndStartsOnOwnLine$ & EndStartsOnOwnLine \\ + & \verb!\end{myenv}!$\EndFinishesWithLineBreak$ & $\EndFinishesWithLineBreak$ & EndFinishesWithLineBreak \\ + & \verb!after words! & & \\ + \cmidrule{2-4} + ifelsefi & \verb!before words!$\BeginStartsOnOwnLine$ & $\BeginStartsOnOwnLine$ & IfStartsOnOwnLine \\ + & \verb!\if...!$\BodyStartsOnOwnLine$ & $\BodyStartsOnOwnLine$ & BodyStartsOnOwnLine \\ + & \verb!body of if/or statement!$\OrStartsOnOwnLine$ & $\OrStartsOnOwnLine$ & OrStartsOnOwnLine % + \announce{2018-04-27}{new ifElseFi code block poly-switches} \\ + & \verb!\or!$\OrFinishesWithLineBreak$ & $\OrFinishesWithLineBreak$ & OrFinishesWithLineBreak \\ + & \verb!body of if/or statement!$\ElseStartsOnOwnLine$ & $\ElseStartsOnOwnLine$ & ElseStartsOnOwnLine \\ + & \verb!\else!$\ElseFinishesWithLineBreak$ & $\ElseFinishesWithLineBreak$ & ElseFinishesWithLineBreak \\ + & \verb!body of else statement!$\EndStartsOnOwnLine$ & $\EndStartsOnOwnLine$ & FiStartsOnOwnLine \\ + & \verb!\fi!$\EndFinishesWithLineBreak$ & $\EndFinishesWithLineBreak$ & FiFinishesWithLineBreak \\ + & \verb!after words! & & \\ + \cmidrule{2-4} + optionalArguments & \verb!...!$\BeginStartsOnOwnLine$ & $\BeginStartsOnOwnLine$ & LSqBStartsOnOwnLine\footnote{LSqB stands for Left Square Bracket} \\ + & \verb![!$\BodyStartsOnOwnLine$ & $\BodyStartsOnOwnLine$ & OptArgBodyStartsOnOwnLine \\ + \announce*{2019-07-13}{new comma-related poly-switches} & \verb!value before comma!$\ElseStartsOnOwnLine$, & $\ElseStartsOnOwnLine$ & CommaStartsOnOwnLine \\ + & $\ElseFinishesWithLineBreak$ & $\ElseFinishesWithLineBreak$ & CommaFinishesWithLineBreak \\ + & \verb!end of body of opt arg!$\EndStartsOnOwnLine$ & $\EndStartsOnOwnLine$ & RSqBStartsOnOwnLine \\ + & \verb!]!$\EndFinishesWithLineBreak$ & $\EndFinishesWithLineBreak$ & RSqBFinishesWithLineBreak \\ + & \verb!...! & & \\ + \cmidrule{2-4} + mandatoryArguments & \verb!...!$\BeginStartsOnOwnLine$ & $\BeginStartsOnOwnLine$ & LCuBStartsOnOwnLine\footnote{LCuB stands for Left Curly Brace} \\ + & \verb!{!$\BodyStartsOnOwnLine$ & $\BodyStartsOnOwnLine$ & MandArgBodyStartsOnOwnLine \\ + \announce*{2019-07-13}{new comma-related poly-switches} & \verb!value before comma!$\ElseStartsOnOwnLine$, & $\ElseStartsOnOwnLine$ & CommaStartsOnOwnLine \\ + & $\ElseFinishesWithLineBreak$ & $\ElseFinishesWithLineBreak$ & CommaFinishesWithLineBreak \\ + & \verb!end of body of mand arg!$\EndStartsOnOwnLine$ & $\EndStartsOnOwnLine$ & RCuBStartsOnOwnLine \\ + & \verb!}!$\EndFinishesWithLineBreak$ & $\EndFinishesWithLineBreak$ & RCuBFinishesWithLineBreak \\ + & \verb!...! & & \\ + \cmidrule{2-4} + commands & \verb!before words!$\BeginStartsOnOwnLine$ & $\BeginStartsOnOwnLine$ & CommandStartsOnOwnLine \\ + & \verb!\mycommand!$\BodyStartsOnOwnLine$ & $\BodyStartsOnOwnLine$ & CommandNameFinishesWithLineBreak \\ + & $\langle$\itshape{arguments}$\rangle$ & & \\ + \cmidrule{2-4} + namedGroupingBraces Brackets & before words$\BeginStartsOnOwnLine$ & $\BeginStartsOnOwnLine$ & NameStartsOnOwnLine \\ + & myname$\BodyStartsOnOwnLine$ & $\BodyStartsOnOwnLine$ & NameFinishesWithLineBreak \\ + & $\langle$\itshape{braces/brackets}$\rangle$ & & \\ + \cmidrule{2-4} + keyEqualsValuesBraces\newline Brackets & before words$\BeginStartsOnOwnLine$ & $\BeginStartsOnOwnLine$ & KeyStartsOnOwnLine \\ + & key$\EqualsStartsOnOwnLine$=$\BodyStartsOnOwnLine$ & $\EqualsStartsOnOwnLine$ & EqualsStartsOnOwnLine \\ + & $\langle$\itshape{braces/brackets}$\rangle$ & $\BodyStartsOnOwnLine$ & EqualsFinishesWithLineBreak \\ + \cmidrule{2-4} + items & before words$\BeginStartsOnOwnLine$ & $\BeginStartsOnOwnLine$ & ItemStartsOnOwnLine \\ + & \verb!\item!$\BodyStartsOnOwnLine$ & $\BodyStartsOnOwnLine$ & ItemFinishesWithLineBreak \\ + & \verb!...! & & \\ + \cmidrule{2-4} + specialBeginEnd & before words$\BeginStartsOnOwnLine$ & $\BeginStartsOnOwnLine$ & SpecialBeginStartsOnOwnLine \\ + & \verb!\[!$\BodyStartsOnOwnLine$ & $\BodyStartsOnOwnLine$ & SpecialBodyStartsOnOwnLine \\ + & \verb!body of special/middle!$\ElseStartsOnOwnLine$ & $\ElseStartsOnOwnLine$ & SpecialMiddleStartsOnOwnLine % + \announce{2018-04-27}{new special code block poly-switches} \\ + & \verb!\middle!$\ElseFinishesWithLineBreak$ & $\ElseFinishesWithLineBreak$ & SpecialMiddleFinishesWithLineBreak \\ + & body of special/middle $\EndStartsOnOwnLine$ & $\EndStartsOnOwnLine$ & SpecialEndStartsOnOwnLine \\ + & \verb!\]!$\EndFinishesWithLineBreak$ & $\EndFinishesWithLineBreak$ & SpecialEndFinishesWithLineBreak \\ + & after words & & \\ + \cmidrule{2-4} + verbatim & before words$\BeginStartsOnOwnLine$\verb!\begin{verbatim}! & $\BeginStartsOnOwnLine$ & VerbatimBeginStartsOnOwnLine \\ + \announce{2019-05-05}{verbatim poly-switches} & body of verbatim \verb!\end{verbatim}!$\EndFinishesWithLineBreak$ & $\EndFinishesWithLineBreak$ & VerbatimEndFinishesWithLineBreak \\ + & after words & & \\ + \bottomrule + \end{longtable} diff --git a/support/latexindent/documentation/subsec-commands-and-their-options.tex b/support/latexindent/documentation/subsec-commands-and-their-options.tex new file mode 100644 index 0000000000..a1184bf3a5 --- /dev/null +++ b/support/latexindent/documentation/subsec-commands-and-their-options.tex @@ -0,0 +1,245 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\subsection{Commands and the strings between their arguments}\label{subsec:commands-string-between} + The \texttt{command} code blocks will always look for optional (square bracketed) + and mandatory (curly braced) arguments which can contain comments, line breaks and + `beamer' commands \lstinline!<.*?>! between them. There are switches that can allow + them to contain other strings, which we discuss next. + +\yamltitle{commandCodeBlocks}*{fields} + + The \texttt{commandCodeBlocks} field% + \announce{2018-04-27}*{commandCodeBlocks} + contains a few switches detailed in \cref{lst:commandCodeBlocks}. + + \cmhlistingsfromfile[style=commandCodeBlocks]*{../defaultSettings.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{commandCodeBlocks}}{lst:commandCodeBlocks} + +\yamltitle{roundParenthesesAllowed}{0|1} + + The need for this field was mostly motivated by commands found in code used to generate + images in \texttt{PSTricks} and \texttt{tikz}; for example, let's consider + the code given in \cref{lst:pstricks1}. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/pstricks1.tex}{\texttt{pstricks1.tex}}{lst:pstricks1} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/pstricks1-default.tex}{\texttt{pstricks1} default output}{lst:pstricks1-default} + \end{minipage} + + Notice that the \lstinline!\defFunction! command has an optional argument, followed by a + mandatory argument, followed by a round-parenthesis argument, $(u,v)$. + + By default, because \texttt{roundParenthesesAllowed} is set to $1$ in + \cref{lst:commandCodeBlocks}, then \texttt{latexindent.pl} will allow round parenthesis between + optional and mandatory arguments. In the case of the code in \cref{lst:pstricks1}, + \texttt{latexindent.pl} finds \emph{all} the arguments of + \lstinline!defFunction!, both before and after \lstinline!(u,v)!. + + The default output from running \texttt{latexindent.pl} on \cref{lst:pstricks1} actually + leaves it unchanged (see \cref{lst:pstricks1-default}); note in particular, this is because of + \texttt{noAdditionalIndentGlobal} as discussed on \cpageref{page:command:noAddGlobal}. + + Upon using the YAML settings in \cref{lst:noRoundParentheses}, and running the command + \begin{commandshell} +latexindent.pl pstricks1.tex -l noRoundParentheses.yaml +\end{commandshell} + we obtain the output given in \cref{lst:pstricks1-nrp}. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile{demonstrations/pstricks1-nrp.tex}{\texttt{pstricks1.tex} using \cref{lst:noRoundParentheses}}{lst:pstricks1-nrp} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/noRoundParentheses.yaml}[yaml-TCB]{\texttt{noRoundParentheses.yaml}}{lst:noRoundParentheses} + \end{cmhtcbraster} + + Notice the difference between \cref{lst:pstricks1-default} and \cref{lst:pstricks1-nrp}; in + particular, in \cref{lst:pstricks1-nrp}, because round parentheses are + \emph{not} allowed, \texttt{latexindent.pl} finds that the + \lstinline!\defFunction! command finishes at the first opening round parenthesis. As such, + the remaining braced, mandatory, arguments are found to be \texttt{UnNamedGroupingBracesBrackets} (see \vref{tab:code-blocks}) which, + by default, assume indentation for their body, and hence the tabbed indentation in + \cref{lst:pstricks1-nrp}. + + Let's explore this using the YAML given in \cref{lst:defFunction} and run the command + \begin{commandshell} +latexindent.pl pstricks1.tex -l defFunction.yaml +\end{commandshell} + then the output is as in \cref{lst:pstricks1-indent-rules}. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile[showspaces=true]{demonstrations/pstricks1-indent-rules.tex}{\texttt{pstricks1.tex} using \cref{lst:defFunction}}{lst:pstricks1-indent-rules} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/defFunction.yaml}[yaml-TCB]{\texttt{defFunction.yaml}}{lst:defFunction} + \end{cmhtcbraster} + + Notice in \cref{lst:pstricks1-indent-rules} that the \emph{body} of the + \lstinline!defFunction! command i.e, the subsequent lines containing arguments after the + command name, have received the single space of indentation specified by + \cref{lst:defFunction}. + +\yamltitle{stringsAllowedBetweenArguments}*{fields} + \texttt{tikz} users may well specify code such as that given in + \cref{lst:tikz-node1}; processing this code using + \texttt{latexindent.pl} gives the default output in \cref{lst:tikz-node1-default}. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[columns=fixed]*{demonstrations/tikz-node1.tex}{\texttt{tikz-node1.tex}}{lst:tikz-node1} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[columns=fixed]*{demonstrations/tikz-node1-default.tex}{\texttt{tikz-node1} default output}{lst:tikz-node1-default} + \end{minipage} + + With reference to \vref{lst:commandCodeBlocks}, we see that the strings + \begin{quote} + to, node, ++ + \end{quote} + are all allowed to appear between arguments; importantly, you are encouraged to add + further names to this field as necessary. This means that when \texttt{latexindent.pl} + processes \cref{lst:tikz-node1}, it consumes: + \begin{itemize} + \item the optional argument \lstinline![thin]! + \item the round-bracketed argument \lstinline!(c)! because \texttt{roundParenthesesAllowed} is + $1$ by default + \item the string \lstinline!to! (specified in \texttt{stringsAllowedBetweenArguments}) + \item the optional argument \lstinline![in=110,out=-90]! + \item the string \lstinline!++! (specified in \texttt{stringsAllowedBetweenArguments}) + \item the round-bracketed argument \lstinline!(0,-0.5cm)! because \texttt{roundParenthesesAllowed} is + $1$ by default + \item the string \lstinline!node! (specified in \texttt{stringsAllowedBetweenArguments}) + \item the optional argument \lstinline![below,align=left,scale=0.5]! + \end{itemize} + + We can explore this further, for example using \cref{lst:draw} and running the + command + \begin{commandshell} +latexindent.pl tikz-node1.tex -l draw.yaml +\end{commandshell} + we receive the output given in \cref{lst:tikz-node1-draw}. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile[showspaces=true]{demonstrations/tikz-node1-draw.tex}{\texttt{tikz-node1.tex} using \cref{lst:draw}}{lst:tikz-node1-draw} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/draw.yaml}[yaml-TCB]{\texttt{draw.yaml}}{lst:draw} + \end{cmhtcbraster} + + Notice that each line after the \lstinline!\draw! command (its `body') in + \cref{lst:tikz-node1-draw} has been given the appropriate two-spaces worth of indentation + specified in \cref{lst:draw}. + + Let's compare this with the output from using the YAML settings in + \cref{lst:no-strings}, and running the command + \begin{commandshell} +latexindent.pl tikz-node1.tex -l no-strings.yaml +\end{commandshell} + given in \cref{lst:tikz-node1-no-strings}. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile{demonstrations/tikz-node1-no-strings.tex}{\texttt{tikz-node1.tex} using \cref{lst:no-strings}}{lst:tikz-node1-no-strings} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/no-strings.yaml}[yaml-TCB]{\texttt{no-strings.yaml}}{lst:no-strings} + \end{cmhtcbraster} + + In this case, \texttt{latexindent.pl} sees that: + \begin{itemize} + \item the \lstinline!\draw! command finishes after the \lstinline!(c)!, as + \texttt{stringsAllowedBetweenArguments} has been set to $0$ so there are no strings + allowed between arguments; + \item it finds a \texttt{namedGroupingBracesBrackets} called \texttt{to} (see \vref{tab:code-blocks}) \emph{with} + argument \lstinline![in=110,out=-90]! + \item it finds another \texttt{namedGroupingBracesBrackets} but this time called \texttt{node} with + argument \lstinline![below,align=left,scale=0.5]! + \end{itemize} + + Referencing \vref{lst:commandCodeBlocks},% + \announce{2018-04-27}*{amalgamate feature in commandCodeBlocks}, we see that the first field in the \texttt{stringsAllowedBetweenArguments} is + \texttt{amalgamate} and is set to \texttt{1} by default. This is for + users who wish to specify their settings in multiple YAML files. For example, by using + the settings in either \cref{lst:amalgamate-demo} or\cref{lst:amalgamate-demo1} is equivalent to + using the settings in \cref{lst:amalgamate-demo2}. + + \begin{cmhtcbraster}[raster columns=3, + raster left skip=-3.5cm, + raster right skip=-2cm, + raster column skip=.03\linewidth] + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/amalgamate-demo.yaml}[yaml-TCB]{\texttt{amalgamate-demo.yaml}}{lst:amalgamate-demo} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/amalgamate-demo1.yaml}[yaml-TCB]{\texttt{amalgamate-demo1.yaml}}{lst:amalgamate-demo1} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/amalgamate-demo2.yaml}[yaml-TCB]{\texttt{amalgamate-demo2.yaml}}{lst:amalgamate-demo2} + \end{cmhtcbraster} + + We specify \texttt{amalgamate} to be set to \texttt{0} and in which case + any settings loaded prior to those specified, including the default, will be overwritten. + For example, using the settings in \cref{lst:amalgamate-demo3} means that only the strings + specified in that field will be used. + + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/amalgamate-demo3.yaml}[yaml-TCB]{\texttt{amalgamate-demo3.yaml}}{lst:amalgamate-demo3} + + It is important to note that the \texttt{amalgamate} field, if used, must be in the + first field, and specified using the syntax given in \cref{lst:amalgamate-demo1,lst:amalgamate-demo2,lst:amalgamate-demo3}. + + We may explore this feature further with the code in \cref{lst:for-each}, whose + default output is given in \cref{lst:for-each-default}. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile{demonstrations/for-each.tex}{\texttt{for-each.tex}}{lst:for-each} + \cmhlistingsfromfile{demonstrations/for-each-default.tex}{\texttt{for-each} default output}{lst:for-each-default} + \end{cmhtcbraster} + + Let's compare this with the output from using the YAML settings in + \cref{lst:foreach}, and running the command + \begin{commandshell} +latexindent.pl for-each.tex -l foreach.yaml +\end{commandshell} + given in \cref{lst:for-each-mod1}. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile{demonstrations/for-each-mod1.tex}{\texttt{for-each.tex} using \cref{lst:foreach}}{lst:for-each-mod1} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/foreach.yaml}[yaml-TCB]{\texttt{foreach.yaml}}{lst:foreach} + \end{cmhtcbraster} + + You might like to compare the output given in \cref{lst:for-each-default} and + \cref{lst:for-each-mod1}. Note,in particular, in \cref{lst:for-each-default} that the + \texttt{foreach} command has not included any of the subsequent strings, and that + the braces have been treated as a \texttt{namedGroupingBracesBrackets}. In \cref{lst:for-each-mod1} the + \texttt{foreach} command has been allowed to have \lstinline!\x/\y! and + \texttt{in} between arguments because of the settings given in + \cref{lst:foreach}. + +\yamltitle{commandNameSpecial}*{fields} + There are some special command names% + \announce{2018-04-27}*{commandNameSpecial} that do not fit within the names recognised by + \texttt{latexindent.pl}, the first one of which is \lstinline!\@ifnextchar[!. From the + perspective of \texttt{latexindent.pl}, the whole of the text \lstinline!\@ifnextchar[! is + a command, because it is immediately followed by sets of mandatory arguments. However, + without the \texttt{commandNameSpecial} field, \texttt{latexindent.pl} would not be able to + label it as such, because the \lstinline![! is, necessarily, not matched by a + closing \lstinline!]!. + + For example, consider the sample file in \cref{lst:ifnextchar}, which has default + output in \cref{lst:ifnextchar-default}. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile{demonstrations/ifnextchar.tex}{\texttt{ifnextchar.tex}}{lst:ifnextchar} + \cmhlistingsfromfile{demonstrations/ifnextchar-default.tex}{\texttt{ifnextchar.tex} default output}{lst:ifnextchar-default} + \end{cmhtcbraster} + + Notice that in \cref{lst:ifnextchar-default} the \texttt{parbox} command has been able + to indent its body, because \texttt{latexindent.pl} has successfully found the command + \lstinline!\@ifnextchar! first; the pattern-matching of \texttt{latexindent.pl} starts + from \emph{the inner most <thing> and works outwards}, discussed in more detail on \cpageref{page:phases}. + + For demonstration, we can compare this output with that given in \cref{lst:ifnextchar-off} + in which the settings from \cref{lst:no-ifnextchar} have dictated that no special command + names, including the \lstinline!\@ifnextchar[! command, should not be searched for + specially; as such, the \texttt{parbox} command has been \emph{unable} + to indent its body successfully, because the \lstinline!\@ifnextchar[! command has not been + found. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile{demonstrations/ifnextchar-off.tex}{\texttt{ifnextchar.tex} using \cref{lst:no-ifnextchar}}{lst:ifnextchar-off} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/no-ifnextchar.yaml}[yaml-TCB]{\texttt{no-ifnextchar.yaml}}{lst:no-ifnextchar} + \end{cmhtcbraster} + + The \texttt{amalgamate} field can be used for \texttt{commandNameSpecial}, just as for + \texttt{stringsAllowedBetweenArguments}. The same condition holds as stated previously, which we state + again here: + \begin{warning} + It is important to note that the \texttt{amalgamate} field, if used, in either \texttt{commandNameSpecial} or \texttt{stringsAllowedBetweenArguments} must be in the first field, + and specified using the syntax given in \cref{lst:amalgamate-demo1,lst:amalgamate-demo2,lst:amalgamate-demo3}. + \end{warning} diff --git a/support/latexindent/documentation/subsec-conflicting-poly-switches.tex b/support/latexindent/documentation/subsec-conflicting-poly-switches.tex new file mode 100644 index 0000000000..25ebe3efaa --- /dev/null +++ b/support/latexindent/documentation/subsec-conflicting-poly-switches.tex @@ -0,0 +1,160 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\subsection{Conflicting poly-switches: sequential code blocks} + It is very easy to have conflicting poly-switches; if we use the example from + \vref{lst:mycommand1}, and consider the YAML settings given in \cref{lst:mycom-mlb4}. + The output from running + \begin{commandshell} +latexindent.pl -m -l=mycom-mlb4.yaml mycommand1.tex +\end{commandshell} + is given in \cref{lst:mycom-mlb4}. + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/mycommand1-mlb4.tex}{\texttt{mycommand1.tex} using \cref{lst:mycom-mlb4}}{lst:mycommand1-mlb4} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/mycom-mlb4.yaml}[MLB-TCB,width=\linewidth]{\texttt{mycom-mlb4.yaml}}{lst:mycom-mlb4} + \end{cmhtcbraster} + + Studying \cref{lst:mycom-mlb4}, we see that the two poly-switches are at opposition with + one another: + \begin{itemize} + \item on the one hand, \texttt{LCuBStartsOnOwnLine} should \emph{not} start on its own line + (as poly-switch is set to $-1$); + \item on the other hand, \texttt{RCuBFinishesWithLineBreak} \emph{should} finish with a line break. + \end{itemize} + So, which should win the conflict? As demonstrated in \cref{lst:mycommand1-mlb4}, it is clear + that \texttt{LCuBStartsOnOwnLine} won this conflict, and the reason is that + \emph{the second argument was processed after the first} -- in general, the most recently-processed code block and + associated poly-switch takes priority. + + We can explore this further by considering the YAML settings in \cref{lst:mycom-mlb5}; + upon running the command + \begin{commandshell} +latexindent.pl -m -l=mycom-mlb5.yaml mycommand1.tex +\end{commandshell} + we obtain the output given in \cref{lst:mycommand1-mlb5}. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile{demonstrations/mycommand1-mlb5.tex}{\texttt{mycommand1.tex} using \cref{lst:mycom-mlb5}}{lst:mycommand1-mlb5} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/mycom-mlb5.yaml}[MLB-TCB,width=\linewidth]{\texttt{mycom-mlb5.yaml}}{lst:mycom-mlb5} + \end{cmhtcbraster} + + As previously, the most-recently-processed code block takes priority -- as before, the + second (i.e, \emph{last}) argument. Exploring this further, we consider the + YAML settings in \cref{lst:mycom-mlb6}, which give associated output in + \cref{lst:mycommand1-mlb6}. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile{demonstrations/mycommand1-mlb6.tex}{\texttt{mycommand1.tex} using \cref{lst:mycom-mlb6}}{lst:mycommand1-mlb6} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/mycom-mlb6.yaml}[MLB-TCB,width=\linewidth]{\texttt{mycom-mlb6.yaml}}{lst:mycom-mlb6} + \end{cmhtcbraster} + + Note that a \lstinline!%! \emph{has} been added to the trailing + first \lstinline!}!; this is because: + \begin{itemize} + \item while processing the \emph{first} argument, the trailing line break has been + removed (\texttt{RCuBFinishesWithLineBreak} set to $-1$); + \item while processing the \emph{second} argument, \texttt{latexindent.pl} finds that + it does \emph{not} begin on its own line, and so because + \texttt{LCuBStartsOnOwnLine} is set to $2$, it adds a comment, followed by a + line break. + \end{itemize} + +\subsection{Conflicting poly-switches: nested code blocks} + Now let's consider an example when nested code blocks have conflicting poly-switches; + we'll use the code in \cref{lst:nested-env}, noting that it contains nested + environments. + + \cmhlistingsfromfile{demonstrations/nested-env.tex}{\texttt{nested-env.tex}}{lst:nested-env} + + Let's use the YAML settings given in \cref{lst:nested-env-mlb1-yaml}, which upon running the + command + \begin{commandshell} +latexindent.pl -m -l=nested-env-mlb1.yaml nested-env.tex +\end{commandshell} + gives the output in \cref{lst:nested-env-mlb1}. + + \begin{cmhtcbraster}[raster column skip=.05\linewidth] + \cmhlistingsfromfile{demonstrations/nested-env-mlb1.tex}{\texttt{nested-env.tex} using \cref{lst:nested-env-mlb1-yaml}}{lst:nested-env-mlb1} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/nested-env-mlb1.yaml}[MLB-TCB,width=\linewidth]{\texttt{nested-env-mlb1.yaml}}{lst:nested-env-mlb1-yaml} + \end{cmhtcbraster} + + In \cref{lst:nested-env-mlb1}, let's first of all note that both environments have received + the appropriate (default) indentation; secondly, note that the poly-switch + \texttt{EndStartsOnOwnLine} appears to have won the conflict, as \lstinline!\end{one}! has + had its leading line break removed. + + To understand it, let's talk about the three basic phases \label{page:phases}of + \texttt{latexindent.pl}: + \begin{enumerate} + \item Phase 1: packing, in which code blocks are replaced with unique ids, working from + \emph{the inside to the outside}, and then sequentially -- for example, in \cref{lst:nested-env}, + the \texttt{two} environment is found \emph{before} the + \texttt{one} environment; if the -m switch is active, then during this phase: + \begin{itemize} + \item line breaks at the beginning of the \texttt{body} can be added (if + \texttt{BodyStartsOnOwnLine} is $1$ or $2$) or removed (if + \texttt{BodyStartsOnOwnLine} is $-1$); + \item line breaks at the end of the body can be added (if \texttt{EndStartsOnOwnLine} is + $1$ or $2$) or removed (if \texttt{EndStartsOnOwnLine} is + $-1$); + \item line breaks after the end statement can be added (if \texttt{EndFinishesWithLineBreak} is + $1$ or $2$). + \end{itemize} + \item Phase 2: indentation, in which white space is added to the begin, body, and end + statements; + \item Phase 3: unpacking, in which unique ids are replaced by their \emph{indented} code + blocks; if the -m switch is active, then during this phase, + \begin{itemize} + \item line breaks before \texttt{begin} statements can be added or removed (depending + upon \texttt{BeginStartsOnOwnLine}); + \item line breaks after \emph{end} statements can be removed but + \emph{NOT} added (see \texttt{EndFinishesWithLineBreak}). + \end{itemize} + \end{enumerate} + + With reference to \cref{lst:nested-env-mlb1}, this means that during Phase 1: + \begin{itemize} + \item the \texttt{two} environment is found first, and the line break ahead of the + \lstinline!\end{two}! statement is removed because \texttt{EndStartsOnOwnLine} is set to + $-1$. Importantly, because, \emph{at this stage}, + \lstinline!\end{two}! \emph{does} finish with a line break, + \texttt{EndFinishesWithLineBreak} causes no action. + \item next, the \texttt{one} environment is found; the line break ahead of + \lstinline!\end{one}! is removed because \texttt{EndStartsOnOwnLine} is set to + $-1$. + \end{itemize} + The indentation is done in Phase 2; in Phase 3 \emph{there is no option to add a line break after the \lstinline!end! statements}. We can justify + this by remembering that during Phase 3, the \texttt{one} environment will be + found and processed first, followed by the \texttt{two} environment. If the + \texttt{two} environment were to add a line break after the + \lstinline!\end{two}! statement, then \texttt{latexindent.pl} would have no way of + knowing how much indentation to add to the subsequent text (in this case, + \lstinline!\end{one}!). + + We can explore this further using the poly-switches in \cref{lst:nested-env-mlb2}; upon + running the command + \begin{commandshell} +latexindent.pl -m -l=nested-env-mlb2.yaml nested-env.tex +\end{commandshell} + we obtain the output given in \cref{lst:nested-env-mlb2-output}. + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/nested-env-mlb2.tex}{\texttt{nested-env.tex} using \cref{lst:nested-env-mlb2}}{lst:nested-env-mlb2-output} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/nested-env-mlb2.yaml}[MLB-TCB,width=\linewidth]{\texttt{nested-env-mlb2.yaml}}{lst:nested-env-mlb2} + \end{cmhtcbraster} + + During Phase 1: + \begin{itemize} + \item the \texttt{two} environment is found first, and the line break ahead of the + \lstinline!\end{two}! statement is not changed because \texttt{EndStartsOnOwnLine} is set to + $1$. Importantly, because, \emph{at this stage}, + \lstinline!\end{two}! \emph{does} finish with a line break, + \texttt{EndFinishesWithLineBreak} causes no action. + \item next, the \texttt{one} environment is found; the line break ahead of + \lstinline!\end{one}! is already present, and no action is needed. + \end{itemize} + The indentation is done in Phase 2, and then in Phase 3, the \texttt{one} + environment is found and processed first, followed by the \texttt{two} + environment. \emph{At this stage}, the \texttt{two} environment finds + \texttt{EndFinishesWithLineBreak} is $-1$, so it removes the trailing line break; + remember, at this point, \texttt{latexindent.pl} has completely finished with the + \texttt{one} environment. diff --git a/support/latexindent/documentation/subsec-noAdditionalIndent-indentRules.tex b/support/latexindent/documentation/subsec-noAdditionalIndent-indentRules.tex new file mode 100644 index 0000000000..53fd835392 --- /dev/null +++ b/support/latexindent/documentation/subsec-noAdditionalIndent-indentRules.tex @@ -0,0 +1,27 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +% arara: pdflatex: {shell: yes, files: [latexindent]} +\subsection{noAdditionalIndent and indentRules}\label{sec:noadd-indent-rules} + \texttt{latexindent.pl} operates on files by looking for code blocks, as detailed in + \vref{subsubsec:code-blocks}; + for each type of code block in \vref{tab:code-blocks} (which we will call a \emph{$\langle$thing$\rangle$} in what follows) it searches YAML fields for + information in the following order: + \begin{enumerate} + \item \texttt{noAdditionalIndent} for the \emph{name} of the current \emph{$\langle$thing$\rangle$}; + \item \texttt{indentRules} for the \emph{name} of the current \emph{$\langle$thing$\rangle$}; + \item \texttt{noAdditionalIndentGlobal} for the \emph{type} of the current \emph{$\langle$thing$\rangle$}; + \item \texttt{indentRulesGlobal} for the \emph{type} of the current + \emph{$\langle$thing$\rangle$}. + \end{enumerate} + + Using the above list, the first piece of information to be found will be used; failing + that, the value of \texttt{defaultIndent} is used. If information is found in multiple + fields, the first one according to the list above will be used; for example, if + information is present in both \texttt{indentRules} and in \texttt{noAdditionalIndentGlobal}, then + the information from \texttt{indentRules} takes priority. + + We now present details for the different type of code blocks known to + \texttt{latexindent.pl}, as detailed in \vref{tab:code-blocks}; for reference, there + follows a list of the code blocks covered. + + \startcontents[noAdditionalIndent] + \printcontents[noAdditionalIndent]{}{0}{} diff --git a/support/latexindent/documentation/subsec-partnering-poly-switches.tex b/support/latexindent/documentation/subsec-partnering-poly-switches.tex new file mode 100644 index 0000000000..03337669ef --- /dev/null +++ b/support/latexindent/documentation/subsec-partnering-poly-switches.tex @@ -0,0 +1,51 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\subsection{Partnering BodyStartsOnOwnLine with argument-based poly-switches} + Some poly-switches need to be partnered together; in particular, when line breaks + involving the \emph{first} argument of a code block need to be accounted for + using both \texttt{BodyStartsOnOwnLine} (or its equivalent, see \vref{tab:poly-switch-mapping}) and \texttt{LCuBStartsOnOwnLine} for mandatory arguments, and + \texttt{LSqBStartsOnOwnLine} for optional arguments. + + Let's begin with the code in \cref{lst:mycommand1} and the YAML settings in + \cref{lst:mycom-mlb1}; with reference to \vref{tab:poly-switch-mapping}, the key + \texttt{CommandNameFinishesWithLineBreak} is an alias for \texttt{BodyStartsOnOwnLine}. + + \cmhlistingsfromfile{demonstrations/mycommand1.tex}{\texttt{mycommand1.tex}}{lst:mycommand1} + + Upon running the command + \begin{commandshell} +latexindent.pl -m -l=mycom-mlb1.yaml mycommand1.tex +\end{commandshell} + we obtain \cref{lst:mycommand1-mlb1}; note that the \emph{second} mandatory argument + beginning brace \lstinline!{! has had its leading line break removed, but that + the \emph{first} brace has not. + + \begin{cmhtcbraster}[ + raster force size=false, + raster column 1/.style={add to width=-1cm}, + ] + \cmhlistingsfromfile{demonstrations/mycommand1-mlb1.tex}{\texttt{mycommand1.tex} using \cref{lst:mycom-mlb1}}{lst:mycommand1-mlb1} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/mycom-mlb1.yaml}[MLB-TCB,width=.6\textwidth]{\texttt{mycom-mlb1.yaml}}{lst:mycom-mlb1} + \end{cmhtcbraster} + + Now let's change the YAML file so that it is as in \cref{lst:mycom-mlb2}; upon running + the analogous command to that given above, we obtain \cref{lst:mycommand1-mlb2}; both + beginning braces \lstinline!{! have had their leading line breaks removed. + + \begin{cmhtcbraster}[ + raster force size=false, + raster column 1/.style={add to width=-1cm}, + ] + \cmhlistingsfromfile{demonstrations/mycommand1-mlb2.tex}{\texttt{mycommand1.tex} using \cref{lst:mycom-mlb2}}{lst:mycommand1-mlb2} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/mycom-mlb2.yaml}[MLB-TCB,width=.6\textwidth]{\texttt{mycom-mlb2.yaml}}{lst:mycom-mlb2} + \end{cmhtcbraster} + + Now let's change the YAML file so that it is as in \cref{lst:mycom-mlb3}; upon running + the analogous command to that given above, we obtain \cref{lst:mycommand1-mlb3}. + + \begin{cmhtcbraster}[ + raster force size=false, + raster column 1/.style={add to width=-1cm}, + ] + \cmhlistingsfromfile{demonstrations/mycommand1-mlb3.tex}{\texttt{mycommand1.tex} using \cref{lst:mycom-mlb3}}{lst:mycommand1-mlb3} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/mycom-mlb3.yaml}[MLB-TCB,width=.6\textwidth]{\texttt{mycom-mlb3.yaml}}{lst:mycom-mlb3} + \end{cmhtcbraster} diff --git a/support/latexindent/documentation/subsubsec-commands-with-arguments.tex b/support/latexindent/documentation/subsubsec-commands-with-arguments.tex new file mode 100644 index 0000000000..84bff810a1 --- /dev/null +++ b/support/latexindent/documentation/subsubsec-commands-with-arguments.tex @@ -0,0 +1,104 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\subsubsection{Commands with arguments}\label{subsubsec:commands-arguments} + Let's begin with the simple example in \cref{lst:mycommand}; when + \texttt{latexindent.pl} operates on this file, the default output is shown in + \cref{lst:mycommand-default}. \footnote{The command code blocks + have quite a few subtleties, described in \vref{subsec:commands-string-between}.} + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile{demonstrations/mycommand.tex}{\texttt{mycommand.tex}}{lst:mycommand} + \cmhlistingsfromfile{demonstrations/mycommand-default.tex}{\texttt{mycommand.tex} default output}{lst:mycommand-default} + \end{cmhtcbraster} + + As in the environment-based case (see \vref{lst:myenv-noAdd1,lst:myenv-noAdd2}) we may specify + \texttt{noAdditionalIndent} either in `scalar' form, or in `field' form, as shown in + \cref{lst:mycommand-noAdd1,lst:mycommand-noAdd2} + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/mycommand-noAdd1.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{mycommand-noAdd1.yaml}}{lst:mycommand-noAdd1} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/mycommand-noAdd2.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{mycommand-noAdd2.yaml}}{lst:mycommand-noAdd2} + \end{minipage} + + After running the following commands, + \begin{commandshell} +latexindent.pl mycommand.tex -l mycommand-noAdd1.yaml +latexindent.pl mycommand.tex -l mycommand-noAdd2.yaml +\end{commandshell} + we receive the respective output given in \cref{lst:mycommand-output-noAdd1,lst:mycommand-output-noAdd2} + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/mycommand-noAdd1.tex}{\texttt{mycommand.tex} using \cref{lst:mycommand-noAdd1}}{lst:mycommand-output-noAdd1} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/mycommand-noAdd2.tex}{\texttt{mycommand.tex} using \cref{lst:mycommand-noAdd2}}{lst:mycommand-output-noAdd2} + \end{minipage} + + Note that in \cref{lst:mycommand-output-noAdd1} that the `body', optional argument + \emph{and} mandatory argument have \emph{all} received no + additional indentation, while in \cref{lst:mycommand-output-noAdd2}, only the `body' has not received + any additional indentation. We define the `body' of a command as any lines following the + command name that include its optional or mandatory arguments. + + We may further customise \texttt{noAdditionalIndent} for \texttt{mycommand} as we did in + \vref{lst:myenv-noAdd5,lst:myenv-noAdd6}; explicit examples are given in \cref{lst:mycommand-noAdd3,lst:mycommand-noAdd4}. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/mycommand-noAdd3.yaml}[width=.9\linewidth,before=\centering,yaml-TCB]{\texttt{mycommand-noAdd3.yaml}}{lst:mycommand-noAdd3} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/mycommand-noAdd4.yaml}[width=.9\linewidth,before=\centering,yaml-TCB]{\texttt{mycommand-noAdd4.yaml}}{lst:mycommand-noAdd4} + \end{minipage} + + After running the following commands, + \begin{commandshell} +latexindent.pl mycommand.tex -l mycommand-noAdd3.yaml +latexindent.pl mycommand.tex -l mycommand-noAdd4.yaml +\end{commandshell} + we receive the respective output given in \cref{lst:mycommand-output-noAdd3,lst:mycommand-output-noAdd4}. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/mycommand-noAdd3.tex}{\texttt{mycommand.tex} using \cref{lst:mycommand-noAdd3}}{lst:mycommand-output-noAdd3} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/mycommand-noAdd4.tex}{\texttt{mycommand.tex} using \cref{lst:mycommand-noAdd4}}{lst:mycommand-output-noAdd4} + \end{minipage} + + Attentive readers will note that the body of \texttt{mycommand} in both + \cref{lst:mycommand-output-noAdd3,lst:mycommand-output-noAdd4} has received no additional indent, even though + \texttt{body} is explicitly set to \texttt{0} in both + \cref{lst:mycommand-noAdd3,lst:mycommand-noAdd4}. This is because, by default, \texttt{noAdditionalIndentGlobal} for + \texttt{commands} is set to \texttt{1} by default; this can be easily + fixed as in \cref{lst:mycommand-noAdd5,lst:mycommand-noAdd6}.\label{page:command:noAddGlobal} + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/mycommand-noAdd5.yaml}[width=.9\linewidth,before=\centering,yaml-TCB]{\texttt{mycommand-noAdd5.yaml}}{lst:mycommand-noAdd5} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/mycommand-noAdd6.yaml}[width=.9\linewidth,before=\centering,yaml-TCB]{\texttt{mycommand-noAdd6.yaml}}{lst:mycommand-noAdd6} + \end{minipage} + + After running the following commands, + \begin{commandshell} +latexindent.pl mycommand.tex -l mycommand-noAdd5.yaml +latexindent.pl mycommand.tex -l mycommand-noAdd6.yaml +\end{commandshell} + we receive the respective output given in \cref{lst:mycommand-output-noAdd5,lst:mycommand-output-noAdd6}. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/mycommand-noAdd5.tex}{\texttt{mycommand.tex} using \cref{lst:mycommand-noAdd5}}{lst:mycommand-output-noAdd5} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/mycommand-noAdd6.tex}{\texttt{mycommand.tex} using \cref{lst:mycommand-noAdd6}}{lst:mycommand-output-noAdd6} + \end{minipage} + + Both \texttt{indentRules} and \texttt{indentRulesGlobal} can be adjusted as they were for + \emph{environment} code blocks, as in \vref{lst:myenv-rules3,lst:myenv-rules4} and + \vref{lst:indentRulesGlobal:environments,lst:opt-args-indent-rules-glob,lst:mand-args-indent-rules-glob}. diff --git a/support/latexindent/documentation/subsubsec-environments-and-their-arguments.tex b/support/latexindent/documentation/subsubsec-environments-and-their-arguments.tex new file mode 100644 index 0000000000..a37e3eb0ed --- /dev/null +++ b/support/latexindent/documentation/subsubsec-environments-and-their-arguments.tex @@ -0,0 +1,283 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\subsubsection{Environments and their arguments}\label{subsubsec:env-and-their-args} + There are a few different YAML switches governing the indentation of environments; let's + start with the code shown in \cref{lst:myenvtex}. + + \cmhlistingsfromfile{demonstrations/myenvironment-simple.tex}{\texttt{myenv.tex}}{lst:myenvtex} + +\yamltitle{noAdditionalIndent}*{fields} + If we do not wish \texttt{myenv} to receive any additional indentation, we have a + few choices available to us, as demonstrated in \cref{lst:myenv-noAdd1,lst:myenv-noAdd2}. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/myenv-noAdd1.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{myenv-noAdd1.yaml}}{lst:myenv-noAdd1} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/myenv-noAdd2.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{myenv-noAdd2.yaml}}{lst:myenv-noAdd2} + \end{minipage} + + On applying either of the following commands, + \begin{commandshell} +latexindent.pl myenv.tex -l myenv-noAdd1.yaml +latexindent.pl myenv.tex -l myenv-noAdd2.yaml +\end{commandshell} + we obtain the output given in \cref{lst:myenv-output}; note in particular that the + environment \texttt{myenv} has not received any \emph{additional} + indentation, but that the \texttt{outer} environment \emph{has} still + received indentation. + + \cmhlistingsfromfile{demonstrations/myenvironment-simple-noAdd-body1.tex}{\texttt{myenv.tex} output (using either \cref{lst:myenv-noAdd1} or \cref{lst:myenv-noAdd2})}{lst:myenv-output} + + Upon changing the YAML files to those shown in \cref{lst:myenv-noAdd3,lst:myenv-noAdd4}, and running either + \begin{commandshell} +latexindent.pl myenv.tex -l myenv-noAdd3.yaml +latexindent.pl myenv.tex -l myenv-noAdd4.yaml +\end{commandshell} + we obtain the output given in \cref{lst:myenv-output-4}. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/myenv-noAdd3.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{myenv-noAdd3.yaml}}{lst:myenv-noAdd3} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/myenv-noAdd4.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{myenv-noAdd4.yaml}}{lst:myenv-noAdd4} + \end{minipage} + + \cmhlistingsfromfile{demonstrations/myenvironment-simple-noAdd-body4.tex}{\texttt{myenv.tex output} (using either \cref{lst:myenv-noAdd3} or \cref{lst:myenv-noAdd4})}{lst:myenv-output-4} + + Let's now allow \texttt{myenv} to have some optional and mandatory arguments, as + in \cref{lst:myenv-args}. + + \cmhlistingsfromfile{demonstrations/myenvironment-args.tex}{\texttt{myenv-args.tex}}{lst:myenv-args} + + Upon running + \begin{commandshell} +latexindent.pl -l=myenv-noAdd1.yaml myenv-args.tex +\end{commandshell} + we obtain the output shown in \cref{lst:myenv-args-noAdd1}; note that the optional argument, + mandatory argument and body \emph{all} have received no additional indent. + This is because, when \texttt{noAdditionalIndent} is specified in `scalar' form (as in + \cref{lst:myenv-noAdd1}), then \emph{all} parts of the environment (body, + optional and mandatory arguments) are assumed to want no additional indent. + \cmhlistingsfromfile{demonstrations/myenvironment-args-noAdd-body1.tex}{\texttt{myenv-args.tex} using \cref{lst:myenv-noAdd1}}{lst:myenv-args-noAdd1} + + We may customise \texttt{noAdditionalIndent} for optional and mandatory arguments of the + \texttt{myenv} environment, as shown in, for example, \cref{lst:myenv-noAdd5,lst:myenv-noAdd6}. + + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/myenv-noAdd5.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{myenv-noAdd5.yaml}}{lst:myenv-noAdd5} + \end{minipage} + \hfill + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/myenv-noAdd6.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{myenv-noAdd6.yaml}}{lst:myenv-noAdd6} + \end{minipage} + + Upon running + \begin{commandshell} +latexindent.pl myenv.tex -l myenv-noAdd5.yaml +latexindent.pl myenv.tex -l myenv-noAdd6.yaml +\end{commandshell} + we obtain the respective outputs given in \cref{lst:myenv-args-noAdd5,lst:myenv-args-noAdd6}. Note that in + \cref{lst:myenv-args-noAdd5} the text for the \emph{optional} argument has not + received any additional indentation, and that in \cref{lst:myenv-args-noAdd6} the + \emph{mandatory} argument has not received any additional indentation; in both + cases, the \emph{body} has not received any additional indentation. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/myenvironment-args-noAdd5.tex}{\texttt{myenv-args.tex} using \cref{lst:myenv-noAdd5}}{lst:myenv-args-noAdd5} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/myenvironment-args-noAdd6.tex}{\texttt{myenv-args.tex} using \cref{lst:myenv-noAdd6}}{lst:myenv-args-noAdd6} + \end{minipage} + +\yamltitle{indentRules}*{fields} + We may also specify indentation rules for environment code blocks using the + \texttt{indentRules} field; see, for example, \cref{lst:myenv-rules1,lst:myenv-rules2}. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/myenv-rules1.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{myenv-rules1.yaml}}{lst:myenv-rules1} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/myenv-rules2.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{myenv-rules2.yaml}}{lst:myenv-rules2} + \end{cmhtcbraster} + + On applying either of the following commands, + \begin{commandshell} +latexindent.pl myenv.tex -l myenv-rules1.yaml +latexindent.pl myenv.tex -l myenv-rules2.yaml +\end{commandshell} + we obtain the output given in \cref{lst:myenv-rules-output}; note in particular that the + environment \texttt{myenv} has received one tab (from the + \texttt{outer} environment) plus three spaces from \cref{lst:myenv-rules1} or + \ref{lst:myenv-rules2}. + + \cmhlistingsfromfile[showtabs=true,showspaces=true]{demonstrations/myenv-rules1.tex}{\texttt{myenv.tex} output (using either \cref{lst:myenv-rules1} or \cref{lst:myenv-rules2})}{lst:myenv-rules-output} + + If you specify a field in \texttt{indentRules} using anything other than horizontal + space, it will be ignored. + + Returning to the example in \cref{lst:myenv-args} that contains optional and mandatory + arguments. Upon using \cref{lst:myenv-rules1} as in + \begin{commandshell} +latexindent.pl myenv-args.tex -l=myenv-rules1.yaml +\end{commandshell} + we obtain the output in \cref{lst:myenv-args-rules1}; note that the body, optional argument + and mandatory argument of \texttt{myenv} have \emph{all} received + the same customised indentation. \cmhlistingsfromfile[showtabs=true,showspaces=true]{demonstrations/myenvironment-args-rules1.tex}{\texttt{myenv-args.tex} using \cref{lst:myenv-rules1}}{lst:myenv-args-rules1} + + You can specify different indentation rules for the different features using, for + example, \cref{lst:myenv-rules3,lst:myenv-rules4} + + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/myenv-rules3.yaml}[width=.9\linewidth,before=\centering,yaml-TCB]{\texttt{myenv-rules3.yaml}}{lst:myenv-rules3} + \end{minipage} + \hfill + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/myenv-rules4.yaml}[width=.9\linewidth,before=\centering,yaml-TCB]{\texttt{myenv-rules4.yaml}}{lst:myenv-rules4} + \end{minipage} + + After running + \begin{commandshell} +latexindent.pl myenv-args.tex -l myenv-rules3.yaml +latexindent.pl myenv-args.tex -l myenv-rules4.yaml +\end{commandshell} + then we obtain the respective outputs given in \cref{lst:myenv-args-rules3,lst:myenv-args-rules4}. + + \begin{widepage} + \begin{minipage}{.5\textwidth} + \cmhlistingsfromfile[showtabs=true,showspaces=true]{demonstrations/myenvironment-args-rules3.tex}{\texttt{myenv-args.tex} using \cref{lst:myenv-rules3}}{lst:myenv-args-rules3} + \end{minipage}% + \hfill + \begin{minipage}{.5\textwidth} + \cmhlistingsfromfile[showtabs=true,showspaces=true]{demonstrations/myenvironment-args-rules4.tex}{\texttt{myenv-args.tex} using \cref{lst:myenv-rules4}}{lst:myenv-args-rules4} + \end{minipage} + \end{widepage} + + Note that in \cref{lst:myenv-args-rules3}, the optional argument has only received a single + space of indentation, while the mandatory argument has received the default (tab) + indentation; the environment body has received three spaces of indentation. + + In \cref{lst:myenv-args-rules4}, the optional argument has received the default (tab) + indentation, the mandatory argument has received two tabs of indentation, and the body + has received three spaces of indentation. + +\yamltitle{noAdditionalIndentGlobal}*{fields} + \begin{wrapfigure}[6]{r}[0pt]{7cm} + \cmhlistingsfromfile[style=noAdditionalIndentGlobalEnv]*{../defaultSettings.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{noAdditionalIndentGlobal}}{lst:noAdditionalIndentGlobal:environments} + \end{wrapfigure} + Assuming that your environment name is not found within neither \texttt{noAdditionalIndent} + nor \texttt{indentRules}, the next place that \texttt{latexindent.pl} will look is + \texttt{noAdditionalIndentGlobal}, and in particular \emph{for the environments} key (see + \cref{lst:noAdditionalIndentGlobal:environments}). Let's say that you change the value of \texttt{environments} + to \texttt{1} in \cref{lst:noAdditionalIndentGlobal:environments}, and that you run + + \begin{widepage} + \begin{commandshell} +latexindent.pl myenv-args.tex -l env-noAdditionalGlobal.yaml +latexindent.pl myenv-args.tex -l myenv-rules1.yaml,env-noAdditionalGlobal.yaml +\end{commandshell} + \end{widepage} + + The respective output from these two commands are in \cref{lst:myenv-args-no-add-global1,lst:myenv-args-no-add-global2}; in + \cref{lst:myenv-args-no-add-global1} notice that \emph{both} environments receive no + additional indentation but that the arguments of \texttt{myenv} still + \emph{do} receive indentation. In \cref{lst:myenv-args-no-add-global2} notice that the + \emph{outer} environment does not receive additional indentation, but because + of the settings from \texttt{myenv-rules1.yaml} (in \vref{lst:myenv-rules1}), the \texttt{myenv} environment + still \emph{does} receive indentation. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/myenvironment-args-rules1-noAddGlobal1.tex}{\texttt{myenv-args.tex} using \cref{lst:noAdditionalIndentGlobal:environments}}{lst:myenv-args-no-add-global1} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/myenvironment-args-rules1-noAddGlobal2.tex}{\texttt{myenv-args.tex} using \cref{lst:noAdditionalIndentGlobal:environments,lst:myenv-rules1}}{lst:myenv-args-no-add-global2} + \end{minipage} + + In fact, \texttt{noAdditionalIndentGlobal} also contains keys that control the indentation of + optional and mandatory arguments; on referencing \cref{lst:opt-args-no-add-glob,lst:mand-args-no-add-glob} + + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/opt-args-no-add-glob.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{opt-args-no-add-glob.yaml}}{lst:opt-args-no-add-glob} + \end{minipage} + \hfill + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/mand-args-no-add-glob.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{mand-args-no-add-glob.yaml}}{lst:mand-args-no-add-glob} + \end{minipage} + + we may run the commands + \begin{commandshell} +latexindent.pl myenv-args.tex -local opt-args-no-add-glob.yaml +latexindent.pl myenv-args.tex -local mand-args-no-add-glob.yaml +\end{commandshell} + which produces the respective outputs given in \cref{lst:myenv-args-no-add-opt,lst:myenv-args-no-add-mand}. Notice that in + \cref{lst:myenv-args-no-add-opt} the \emph{optional} argument has not received any + additional indentation, and in \cref{lst:myenv-args-no-add-mand} the \emph{mandatory} + argument has not received any additional indentation. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/myenvironment-args-rules1-noAddGlobal3.tex}{\texttt{myenv-args.tex} using \cref{lst:opt-args-no-add-glob}}{lst:myenv-args-no-add-opt} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/myenvironment-args-rules1-noAddGlobal4.tex}{\texttt{myenv-args.tex} using \cref{lst:mand-args-no-add-glob}}{lst:myenv-args-no-add-mand} + \end{minipage} + +\yamltitle{indentRulesGlobal}*{fields} + \begin{wrapfigure}[4]{r}[0pt]{7cm} + \cmhlistingsfromfile[style=indentRulesGlobalEnv]*{../defaultSettings.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{indentRulesGlobal}}{lst:indentRulesGlobal:environments} + \end{wrapfigure} + The final check that \texttt{latexindent.pl} will make is to look for + \texttt{indentRulesGlobal} as detailed in \cref{lst:indentRulesGlobal:environments}; if you change the + \texttt{environments} field to anything involving horizontal space, say + \lstinline!" "!, and then run the following commands + + \begin{commandshell} +latexindent.pl myenv-args.tex -l env-indentRules.yaml +latexindent.pl myenv-args.tex -l myenv-rules1.yaml,env-indentRules.yaml +\end{commandshell} + then the respective output is shown in \cref{lst:myenv-args-indent-rules-global1,lst:myenv-args-indent-rules-global2}. Note that in + \cref{lst:myenv-args-indent-rules-global1}, both the environment blocks have received a single-space + indentation, whereas in \cref{lst:myenv-args-indent-rules-global2} the \texttt{outer} environment + has received single-space indentation (specified by \texttt{indentRulesGlobal}), but + \texttt{myenv} has received \lstinline!" "!, as specified by the + particular \texttt{indentRules} for \texttt{myenv} \vref{lst:myenv-rules1}. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[showspaces=true]{demonstrations/myenvironment-args-global-rules1.tex}{\texttt{myenv-args.tex} using \cref{lst:indentRulesGlobal:environments}}{lst:myenv-args-indent-rules-global1} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[showspaces=true]{demonstrations/myenvironment-args-global-rules2.tex}{\texttt{myenv-args.tex} using \cref{lst:myenv-rules1,lst:indentRulesGlobal:environments}}{lst:myenv-args-indent-rules-global2} + \end{minipage} + + You can specify \texttt{indentRulesGlobal} for both optional and mandatory arguments, as + detailed in \cref{lst:opt-args-indent-rules-glob,lst:mand-args-indent-rules-glob} + + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/opt-args-indent-rules-glob.yaml}[width=.9\linewidth,before=\centering,yaml-TCB]{\texttt{opt-args-indent-rules-glob.yaml}}{lst:opt-args-indent-rules-glob} + \end{minipage} + \hfill + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/mand-args-indent-rules-glob.yaml}[width=.9\linewidth,before=\centering,yaml-TCB]{\texttt{mand-args-indent-rules-glob.yaml}}{lst:mand-args-indent-rules-glob} + \end{minipage} + + Upon running the following commands + \begin{commandshell} +latexindent.pl myenv-args.tex -local opt-args-indent-rules-glob.yaml +latexindent.pl myenv-args.tex -local mand-args-indent-rules-glob.yaml +\end{commandshell} + we obtain the respective outputs in \cref{lst:myenv-args-indent-rules-global3,lst:myenv-args-indent-rules-global4}. Note that the + \emph{optional} argument in \cref{lst:myenv-args-indent-rules-global3} has received two tabs worth + of indentation, while the \emph{mandatory} argument has done so in + \cref{lst:myenv-args-indent-rules-global4}. + + \begin{widepage} + \begin{minipage}{.55\textwidth} + \cmhlistingsfromfile[showtabs=true]{demonstrations/myenvironment-args-global-rules3.tex}{\texttt{myenv-args.tex} using \cref{lst:opt-args-indent-rules-glob}}{lst:myenv-args-indent-rules-global3} + \end{minipage} + \hfill + \begin{minipage}{.55\textwidth} + \cmhlistingsfromfile[showtabs=true]{demonstrations/myenvironment-args-global-rules4.tex}{\texttt{myenv-args.tex} using \cref{lst:mand-args-indent-rules-glob}}{lst:myenv-args-indent-rules-global4} + \end{minipage} + \end{widepage} diff --git a/support/latexindent/documentation/subsubsec-environments-with-items.tex b/support/latexindent/documentation/subsubsec-environments-with-items.tex new file mode 100644 index 0000000000..4cc53e654f --- /dev/null +++ b/support/latexindent/documentation/subsubsec-environments-with-items.tex @@ -0,0 +1,61 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\subsubsection{Environments with items} + With reference to \vref{lst:indentafteritems,lst:itemNames}, some commands may contain + \texttt{item} commands; for the purposes of this discussion, we will use the + code from \vref{lst:itemsbefore}. + + Assuming that you've populated \texttt{itemNames} with the name of your + \texttt{item}, you can put the item name into \texttt{noAdditionalIndent} as in + \cref{lst:item-noAdd1}, although a more efficient approach may be to change the relevant + field in \texttt{itemNames} to \texttt{0}. Similarly, you can customise + the indentation that your \texttt{item} receives using \texttt{indentRules}, + as in \cref{lst:item-rules1} + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/item-noAdd1.yaml}[yaml-TCB]{\texttt{item-noAdd1.yaml}}{lst:item-noAdd1} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/item-rules1.yaml}[yaml-TCB]{\texttt{item-rules1.yaml}}{lst:item-rules1} + \end{cmhtcbraster} + + Upon running the following commands + \begin{commandshell} +latexindent.pl items1.tex -local item-noAdd1.yaml +latexindent.pl items1.tex -local item-rules1.yaml +\end{commandshell} + the respective outputs are given in \cref{lst:items1-noAdd1,lst:items1-rules1}; note that in + \cref{lst:items1-noAdd1} that the text after each \texttt{item} has not received + any additional indentation, and in \cref{lst:items1-rules1}, the text after each + \texttt{item} has received a single space of indentation, specified by + \cref{lst:item-rules1}. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/items1-noAdd1.tex}{\texttt{items1.tex} using \cref{lst:item-noAdd1}}{lst:items1-noAdd1} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[showtabs=true,showspaces=true]{demonstrations/items1-rules1.tex}{\texttt{items1.tex} using \cref{lst:item-rules1}}{lst:items1-rules1} + \end{minipage} + + Alternatively, you might like to populate \texttt{noAdditionalIndentGlobal} or + \texttt{indentRulesGlobal} using the \texttt{items} key, as demonstrated in + \cref{lst:items-noAdditionalGlobal,lst:items-indentRulesGlobal}. Note that there is a need to `reset/remove' the + \texttt{item} field from \texttt{indentRules} in both cases (see the hierarchy + description given on \cpageref{sec:noadd-indent-rules}) as the \texttt{item} command is a + member of \texttt{indentRules} by default. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/items-noAdditionalGlobal.yaml}[yaml-TCB]{\texttt{items-noAdditionalGlobal.yaml}}{lst:items-noAdditionalGlobal} + \end{minipage}% + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/items-indentRulesGlobal.yaml}[yaml-TCB]{\texttt{items-indentRulesGlobal.yaml}}{lst:items-indentRulesGlobal} + \end{minipage} + + Upon running the following commands, + \begin{commandshell} +latexindent.pl items1.tex -local items-noAdditionalGlobal.yaml +latexindent.pl items1.tex -local items-indentRulesGlobal.yaml +\end{commandshell} + the respective outputs from \cref{lst:items1-noAdd1,lst:items1-rules1} are obtained; note, however, that + \emph{all} such \texttt{item} commands without their own individual + \texttt{noAdditionalIndent} or \texttt{indentRules} settings would behave as in these + listings. diff --git a/support/latexindent/documentation/subsubsec-headings.tex b/support/latexindent/documentation/subsubsec-headings.tex new file mode 100644 index 0000000000..088ece973d --- /dev/null +++ b/support/latexindent/documentation/subsubsec-headings.tex @@ -0,0 +1,85 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\subsubsection{afterHeading code blocks}\label{subsubsec-headings-no-add-indent-rules} + Let's use the example \cref{lst:headings2} for demonstration throughout this + \namecref{subsubsec-headings-no-add-indent-rules}. As discussed on \cpageref{lst:headings1}, by default + \texttt{latexindent.pl} will not add indentation after headings. + + \cmhlistingsfromfile{demonstrations/headings2.tex}{\texttt{headings2.tex}}{lst:headings2} + + On using the YAML file in \cref{lst:headings3yaml} by running the command + \begin{commandshell} +latexindent.pl headings2.tex -l headings3.yaml +\end{commandshell} + we obtain the output in \cref{lst:headings2-mod3}. Note that the argument of + \texttt{paragraph} has received (default) indentation, and that the body after the + heading statement has received (default) indentation. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile{demonstrations/headings2-mod3.tex}{\texttt{headings2.tex} using \cref{lst:headings3yaml}}{lst:headings2-mod3} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/headings3.yaml}[yaml-TCB]{\texttt{headings3.yaml}}{lst:headings3yaml} + \end{cmhtcbraster} + + If we specify \texttt{noAdditionalIndent} as in \cref{lst:headings4yaml} and run the command + \begin{commandshell} +latexindent.pl headings2.tex -l headings4.yaml +\end{commandshell} + then we receive the output in \cref{lst:headings2-mod4}. Note that the arguments + \emph{and} the body after the heading of \texttt{paragraph} has received + no additional indentation, because we have specified \texttt{noAdditionalIndent} in scalar + form. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile{demonstrations/headings2-mod4.tex}{\texttt{headings2.tex} using \cref{lst:headings4yaml}}{lst:headings2-mod4} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/headings4.yaml}[yaml-TCB]{\texttt{headings4.yaml}}{lst:headings4yaml} + \end{cmhtcbraster} + + Similarly, if we specify \texttt{indentRules} as in \cref{lst:headings5yaml} and run + analogous commands to those above, we receive the output in \cref{lst:headings2-mod5}; note + that the \emph{body}, \emph{mandatory argument} and content + \emph{after the heading} of \texttt{paragraph} have \emph{all} received + three tabs worth of indentation. + + \begin{cmhtcbraster}[raster force size=false, + raster column 1/.style={add to width=1cm}, + raster column 2/.style={add to width=-1cm}, + ] + \cmhlistingsfromfile[showtabs=true]{demonstrations/headings2-mod5.tex}{\texttt{headings2.tex} using \cref{lst:headings5yaml}}{lst:headings2-mod5} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/headings5.yaml}[yaml-TCB]{\texttt{headings5.yaml}}{lst:headings5yaml} + \end{cmhtcbraster} + + We may, instead, specify \texttt{noAdditionalIndent} in `field' form, as in + \cref{lst:headings6yaml} which gives the output in \cref{lst:headings2-mod6}. + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/headings2-mod6.tex}{\texttt{headings2.tex} using \cref{lst:headings6yaml}}{lst:headings2-mod6} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/headings6.yaml}[yaml-TCB]{\texttt{headings6.yaml}}{lst:headings6yaml} + \end{cmhtcbraster} + + Analogously, we may specify \texttt{indentRules} as in \cref{lst:headings7yaml} which + gives the output in \cref{lst:headings2-mod7}; note that mandatory argument text has only + received a single space of indentation, while the body after the heading has received + three tabs worth of indentation. + + \begin{cmhtcbraster} + \cmhlistingsfromfile[showtabs=true]{demonstrations/headings2-mod7.tex}{\texttt{headings2.tex} using \cref{lst:headings7yaml}}{lst:headings2-mod7} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/headings7.yaml}[yaml-TCB]{\texttt{headings7.yaml}}{lst:headings7yaml} + \end{cmhtcbraster} + + Finally, let's consider \texttt{noAdditionalIndentGlobal} and \texttt{indentRulesGlobal} shown in + \cref{lst:headings8yaml,lst:headings9yaml} respectively, with respective output in \cref{lst:headings2-mod8,lst:headings2-mod9}. + Note that in \cref{lst:headings8yaml} the \emph{mandatory argument} of \texttt{paragraph} + has received a (default) tab's worth of indentation, while the body after the heading has + received \emph{no additional indentation}. Similarly, in \cref{lst:headings2-mod9}, the + \emph{argument} has received both a (default) tab plus two spaces of indentation + (from the global rule specified in \cref{lst:headings9yaml}), and the remaining body after + \texttt{paragraph} has received just two spaces of indentation. + + \begin{cmhtcbraster} + \cmhlistingsfromfile{demonstrations/headings2-mod8.tex}{\texttt{headings2.tex} using \cref{lst:headings8yaml}}{lst:headings2-mod8} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/headings8.yaml}[yaml-TCB]{\texttt{headings8.yaml}}{lst:headings8yaml} + \end{cmhtcbraster} + + \begin{cmhtcbraster} + \cmhlistingsfromfile[showspaces=true,showtabs=true]{demonstrations/headings2-mod9.tex}{\texttt{headings2.tex} using \cref{lst:headings9yaml}}{lst:headings2-mod9} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/headings9.yaml}[yaml-TCB]{\texttt{headings9.yaml}}{lst:headings9yaml} + \end{cmhtcbraster} diff --git a/support/latexindent/documentation/subsubsec-ifelsefi.tex b/support/latexindent/documentation/subsubsec-ifelsefi.tex new file mode 100644 index 0000000000..c7171bc752 --- /dev/null +++ b/support/latexindent/documentation/subsubsec-ifelsefi.tex @@ -0,0 +1,84 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\subsubsection{ifelsefi code blocks} + Let's use the simple example shown in \cref{lst:ifelsefi1}; when \texttt{latexindent.pl} + operates on this file, the output as in \cref{lst:ifelsefi1-default}; note that the body of + each of the \lstinline!\if! statements have been indented, and that the + \lstinline!\else! statement has been accounted for correctly. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/ifelsefi1.tex}{\texttt{ifelsefi1.tex}}{lst:ifelsefi1} + \end{minipage}% + \hfill + \begin{minipage}{.54\textwidth} + \cmhlistingsfromfile{demonstrations/ifelsefi1-default.tex}{\texttt{ifelsefi1.tex} default output}{lst:ifelsefi1-default} + \end{minipage} + + It is recommended to specify \texttt{noAdditionalIndent} and \texttt{indentRules} in the + `scalar' form only for these type of code blocks, although the `field' form would work, + assuming that \texttt{body} was specified. Examples are shown in + \cref{lst:ifnum-noAdd,lst:ifnum-indent-rules}. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/ifnum-noAdd.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{ifnum-noAdd.yaml}}{lst:ifnum-noAdd} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/ifnum-indent-rules.yaml}[width=.8\linewidth,before=\centering,yaml-TCB]{\texttt{ifnum-indent-rules.yaml}}{lst:ifnum-indent-rules} + \end{minipage} + + After running the following commands, + \begin{commandshell} +latexindent.pl ifelsefi1.tex -local ifnum-noAdd.yaml +latexindent.pl ifelsefi1.tex -l ifnum-indent-rules.yaml +\end{commandshell} + we receive the respective output given in \cref{lst:ifelsefi1-output-noAdd,lst:ifelsefi1-output-indent-rules}; note that in + \cref{lst:ifelsefi1-output-noAdd}, the \texttt{ifnum} code block has \emph{not} + received any additional indentation, while in \cref{lst:ifelsefi1-output-indent-rules}, the + \texttt{ifnum} code block has received one tab and two spaces of indentation. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/ifelsefi1-noAdd.tex}{\texttt{ifelsefi1.tex} using \cref{lst:ifnum-noAdd}}{lst:ifelsefi1-output-noAdd} + \end{minipage} + \hfill + \begin{minipage}{.5\textwidth} + \cmhlistingsfromfile[showspaces=true,showtabs=true]{demonstrations/ifelsefi1-indent-rules.tex}{\texttt{ifelsefi1.tex} using \cref{lst:ifnum-indent-rules}}{lst:ifelsefi1-output-indent-rules} + \end{minipage} + + We may specify \texttt{noAdditionalIndentGlobal} and \texttt{indentRulesGlobal} as in + \cref{lst:ifelsefi-noAdd-glob,lst:ifelsefi-indent-rules-global}. + + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/ifelsefi-noAdd-glob.yaml}[width=.9\linewidth,before=\centering,yaml-TCB]{\texttt{ifelsefi-noAdd-glob.yaml}}{lst:ifelsefi-noAdd-glob} + \end{minipage} + \hfill + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/ifelsefi-indent-rules-global.yaml}[width=.9\linewidth,before=\centering,yaml-TCB]{\texttt{ifelsefi-indent-rules-global.yaml}}{lst:ifelsefi-indent-rules-global} + \end{minipage} + + Upon running the following commands + \begin{commandshell} +latexindent.pl ifelsefi1.tex -local ifelsefi-noAdd-glob.yaml +latexindent.pl ifelsefi1.tex -l ifelsefi-indent-rules-global.yaml +\end{commandshell} + we receive the outputs in \cref{lst:ifelsefi1-output-noAdd-glob,lst:ifelsefi1-output-indent-rules-global}; notice that in \cref{lst:ifelsefi1-output-noAdd-glob} + neither of the \texttt{ifelsefi} code blocks have received indentation, while in + \cref{lst:ifelsefi1-output-indent-rules-global} both code blocks have received a single space of indentation. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/ifelsefi1-noAdd-glob.tex}{\texttt{ifelsefi1.tex} using \cref{lst:ifelsefi-noAdd-glob}}{lst:ifelsefi1-output-noAdd-glob} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[showspaces=true]{demonstrations/ifelsefi1-indent-rules-global.tex}{\texttt{ifelsefi1.tex} using \cref{lst:ifelsefi-indent-rules-global}}{lst:ifelsefi1-output-indent-rules-global} + \end{minipage} + + We can further explore the treatment of \texttt{ifElseFi} code + blocks% + \announce{2018-04-27}*{updates to ifElseFi code blocks} in \cref{lst:ifelsefi2}, + and the associated default output given in \cref{lst:ifelsefi2-default}; note, in particular, + that the bodies of each of the `or statements' have been indented. + + \begin{cmhtcbraster}[raster column skip=.1\linewidth] + \cmhlistingsfromfile{demonstrations/ifelsefi2.tex}{\texttt{ifelsefi2.tex}}{lst:ifelsefi2} + \cmhlistingsfromfile{demonstrations/ifelsefi2-default.tex}{\texttt{ifelsefi2.tex} default output}{lst:ifelsefi2-default} + \end{cmhtcbraster} diff --git a/support/latexindent/documentation/subsubsec-no-add-remaining-code-blocks.tex b/support/latexindent/documentation/subsubsec-no-add-remaining-code-blocks.tex new file mode 100644 index 0000000000..a91b4b0c98 --- /dev/null +++ b/support/latexindent/documentation/subsubsec-no-add-remaining-code-blocks.tex @@ -0,0 +1,135 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\subsubsection{The remaining code blocks} + Referencing the different types of code blocks in \vref{tab:code-blocks}, we have a few + code blocks yet to cover; these are very similar to the \texttt{commands} code block + type covered comprehensively in \vref{subsubsec:commands-arguments}, but a small discussion defining + these remaining code blocks is necessary. + + \paragraph{keyEqualsValuesBracesBrackets} + \texttt{latexindent.pl} defines this type of code block by the following criteria: + \begin{itemize} + \item it must immediately follow either \lstinline!{! OR \lstinline![! OR + \lstinline!,! with comments and blank lines allowed. + \item then it has a name made up of the characters detailed in \vref{tab:code-blocks}; + \item then an $=$ symbol; + \item then at least one set of curly braces or square brackets (comments and line breaks + allowed throughout). + \end{itemize} + See the \texttt{keyEqualsValuesBracesBrackets: follow} and \texttt{keyEqualsValuesBracesBrackets: name} fields of the fine tuning + section in \vref{lst:fineTuning}% + \announce*{2019-07-13}{fine tuning: keyEqualsValuesBracesBrackets} + + An example is shown in \cref{lst:pgfkeysbefore}, with the default output given in + \cref{lst:pgfkeys1:default}. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/pgfkeys1.tex}{\texttt{pgfkeys1.tex}}{lst:pgfkeysbefore} + \end{minipage}% + \hfill + \begin{minipage}{.5\textwidth} + \cmhlistingsfromfile[showtabs=true]{demonstrations/pgfkeys1-default.tex}{\texttt{pgfkeys1.tex} default output}{lst:pgfkeys1:default} + \end{minipage}% + + In \cref{lst:pgfkeys1:default}, note that the maximum indentation is three tabs, and these + come from: + \begin{itemize} + \item the \lstinline!\pgfkeys! command's mandatory argument; + \item the \lstinline!start coordinate/.initial! key's mandatory argument; + \item the \lstinline!start coordinate/.initial! key's body, which is defined as any lines following the name + of the key that include its arguments. This is the part controlled by the + \emph{body} field for \texttt{noAdditionalIndent} and friends from + \cpageref{sec:noadd-indent-rules}. + \end{itemize} + \paragraph{namedGroupingBracesBrackets} + This type of code block is mostly motivated by tikz-based code; we define this code block + as follows: + \begin{itemize} + \item it must immediately follow either \emph{horizontal space} OR \emph{one or more line breaks} OR + \lstinline!{! OR \lstinline![! OR \lstinline!$! OR + \lstinline!)! OR \lstinline!(! + \item the name may contain the characters detailed in \vref{tab:code-blocks}; + \item then at least one set of curly braces or square brackets (comments and line breaks + allowed throughout). + \end{itemize} + See the \texttt{NamedGroupingBracesBrackets: follow} and \texttt{NamedGroupingBracesBrackets: name} fields of the fine tuning + section in \vref{lst:fineTuning}% + \announce*{2019-07-13}{fine tuning: namedGroupingBracesBrackets} + + A simple example is given in \cref{lst:child1}, with default output in + \cref{lst:child1:default}. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/child1.tex}{\texttt{child1.tex}}{lst:child1} + \end{minipage}% + \hfill + \begin{minipage}{.5\textwidth} + \cmhlistingsfromfile[showtabs=true]{demonstrations/child1-default.tex}{\texttt{child1.tex} default output}{lst:child1:default} + \end{minipage}% + + In particular, \texttt{latexindent.pl} considers \texttt{child}, + \texttt{parent} and \texttt{node} all to be + \texttt{namedGroupingBracesBrackets}\footnote{ + You may like to verify this by using the \texttt{-tt} option and checking \texttt{indent.log}! }. Referencing \cref{lst:child1:default}, note + that the maximum indentation is two tabs, and these come from: + \begin{itemize} + \item the \lstinline!child!'s mandatory argument; + \item the \lstinline!child!'s body, which is defined as any lines following the name of + the \texttt{namedGroupingBracesBrackets} that include its arguments. This is the part controlled by the + \emph{body} field for \texttt{noAdditionalIndent} and friends from + \cpageref{sec:noadd-indent-rules}. + \end{itemize} + + \paragraph{UnNamedGroupingBracesBrackets} occur in a variety of situations; specifically, we define + this type of code block as satisfying the following criteria: + \begin{itemize} + \item it must immediately follow either \lstinline!{! OR \lstinline![! OR + \lstinline!,! OR \lstinline!&! OR \lstinline!)! OR + \lstinline!(! OR \lstinline!$!; + \item then at least one set of curly braces or square brackets (comments and line breaks + allowed throughout). + \end{itemize} + See the \texttt{UnNamedGroupingBracesBrackets: follow} field of the fine tuning section in \vref{lst:fineTuning}% + \announce*{2019-07-13}{fine tuning: namedGroupingBracesBrackets} + + An example is shown in \cref{lst:psforeach1} with default output give in + \cref{lst:psforeach:default}. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/psforeach1.tex}{\texttt{psforeach1.tex}}{lst:psforeach1} + \end{minipage}% + \hfill + \begin{minipage}{.5\textwidth} + \cmhlistingsfromfile[showtabs=true]{demonstrations/psforeach1-default.tex}{\texttt{psforeach1.tex} default output}{lst:psforeach:default} + \end{minipage}% + + Referencing \cref{lst:psforeach:default}, there are \emph{three} sets of unnamed + braces. Note also that the maximum value of indentation is three tabs, and these come + from: + \begin{itemize} + \item the \lstinline!\psforeach! command's mandatory argument; + \item the \emph{first} un-named braces mandatory argument; + \item the \emph{first} un-named braces \emph{body}, which we define as any + lines following the first opening \lstinline!{! or \lstinline![! that + defined the code block. This is the part controlled by the \emph{body} field + for \texttt{noAdditionalIndent} and friends from \cpageref{sec:noadd-indent-rules}. + \end{itemize} + Users wishing to customise the mandatory and/or optional arguments on a + \emph{per-name} basis for the \texttt{UnNamedGroupingBracesBrackets} should use + \texttt{always-un-named}. + + \paragraph{filecontents} code blocks behave just as \texttt{environments}, except that + neither arguments nor items are sought. + +\subsubsection{Summary} + Having considered all of the different types of code blocks, the functions of the fields + given in \cref{lst:noAdditionalIndentGlobal,lst:indentRulesGlobal} should now make sense. + + \begin{widepage} + \begin{minipage}{.47\linewidth} + \cmhlistingsfromfile[style=noAdditionalIndentGlobal]*{../defaultSettings.yaml}[before=\centering,yaml-TCB]{\texttt{noAdditionalIndentGlobal}}{lst:noAdditionalIndentGlobal} + \end{minipage}% + \hfill + \begin{minipage}{.47\linewidth} + \cmhlistingsfromfile[style=indentRulesGlobal]*{../defaultSettings.yaml}[before=\centering,yaml-TCB]{\texttt{indentRulesGlobal}}{lst:indentRulesGlobal} + \end{minipage}% + \end{widepage} diff --git a/support/latexindent/documentation/subsubsec-special.tex b/support/latexindent/documentation/subsubsec-special.tex new file mode 100644 index 0000000000..378c4d1413 --- /dev/null +++ b/support/latexindent/documentation/subsubsec-special.tex @@ -0,0 +1,63 @@ +% arara: pdflatex: {shell: yes, files: [latexindent]} +\subsubsection{specialBeginEnd code blocks} + Let's use the example from \vref{lst:specialbefore} which has default output shown in + \vref{lst:specialafter}. + + It is recommended to specify \texttt{noAdditionalIndent} and \texttt{indentRules} in the + `scalar' form for these type of code blocks, although the `field' form would work, + assuming that \texttt{body} was specified. Examples are shown in + \cref{lst:displayMath-noAdd,lst:displayMath-indent-rules}. + + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/displayMath-noAdd.yaml}[width=.9\linewidth,before=\centering,yaml-TCB]{\texttt{displayMath-noAdd.yaml}}{lst:displayMath-noAdd} + \end{minipage} + \hfill + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/displayMath-indent-rules.yaml}[width=.9\linewidth,before=\centering,yaml-TCB]{\texttt{displayMath-indent-rules.yaml}}{lst:displayMath-indent-rules} + \end{minipage} + + After running the following commands, + \begin{commandshell} +latexindent.pl special1.tex -local displayMath-noAdd.yaml +latexindent.pl special1.tex -l displayMath-indent-rules.yaml +\end{commandshell} + we receive the respective output given in \cref{lst:special1-output-noAdd,lst:special1-output-indent-rules}; note that in + \cref{lst:special1-output-noAdd}, the \texttt{displayMath} code block has \emph{not} + received any additional indentation, while in \cref{lst:special1-output-indent-rules}, the + \texttt{displayMath} code block has received three tabs worth of indentation. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/special1-noAdd.tex}{\texttt{special1.tex} using \cref{lst:displayMath-noAdd}}{lst:special1-output-noAdd} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[showtabs=true]{demonstrations/special1-indent-rules.tex}{\texttt{special1.tex} using \cref{lst:displayMath-indent-rules}}{lst:special1-output-indent-rules} + \end{minipage} + + We may specify \texttt{noAdditionalIndentGlobal} and \texttt{indentRulesGlobal} as in + \cref{lst:special-noAdd-glob,lst:special-indent-rules-global}. + + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/special-noAdd-glob.yaml}[width=.9\linewidth,before=\centering,yaml-TCB]{\texttt{special-noAdd-glob.yaml}}{lst:special-noAdd-glob} + \end{minipage} + \hfill + \begin{minipage}{.49\textwidth} + \cmhlistingsfromfile[style=yaml-LST]*{demonstrations/special-indent-rules-global.yaml}[width=.9\linewidth,before=\centering,yaml-TCB]{\texttt{special-indent-rules-global.yaml}}{lst:special-indent-rules-global} + \end{minipage} + + Upon running the following commands + \begin{commandshell} +latexindent.pl special1.tex -local special-noAdd-glob.yaml +latexindent.pl special1.tex -l special-indent-rules-global.yaml +\end{commandshell} + we receive the outputs in \cref{lst:special1-output-noAdd-glob,lst:special1-output-indent-rules-global}; notice that in \cref{lst:special1-output-noAdd-glob} + neither of the \texttt{special} code blocks have received indentation, while in + \cref{lst:special1-output-indent-rules-global} both code blocks have received a single space of indentation. + + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile{demonstrations/special1-noAdd-glob.tex}{\texttt{special1.tex} using \cref{lst:special-noAdd-glob}}{lst:special1-output-noAdd-glob} + \end{minipage} + \hfill + \begin{minipage}{.45\textwidth} + \cmhlistingsfromfile[showspaces=true]{demonstrations/special1-indent-rules-global.tex}{\texttt{special1.tex} using \cref{lst:special-indent-rules-global}}{lst:special1-output-indent-rules-global} + \end{minipage} diff --git a/support/latexindent/documentation/title.tex b/support/latexindent/documentation/title.tex new file mode 100644 index 0000000000..4e0ae04a2b --- /dev/null +++ b/support/latexindent/documentation/title.tex @@ -0,0 +1,27 @@ +\title{% + \begin{tcolorbox}[ + width=5.2cm, + boxrule=0pt, + colframe=white!40!black, + colback=white, + rightrule=2pt, + sharp corners, + enhanced, + overlay={\node[anchor=north east,outer sep=2pt] at ([xshift=3cm,yshift=4mm]frame.north east) {\includegraphics[width=3cm]{logo}}; }] + \centering\ttfamily\bfseries latexindent.pl\\[1cm] Version 3.7 + \end{tcolorbox} +} +\author{Chris Hughes \thanks{and contributors! + See \vref{sec:contributors}. + For + all communication, please visit \cite{latexindent-home}.}} +\maketitle +\begin{adjustwidth}{1cm}{1cm} + \small + \texttt{latexindent.pl} is a \texttt{Perl} script that indents \texttt{.tex} (and other) files according to an indentation scheme that the user can modify to suit their taste. + Environments, including those with alignment delimiters (such as \texttt{tabular}), and commands, including those that can split braces and brackets across lines, are \emph{usually} handled correctly by the script. + Options for \texttt{verbatim}-like environments and commands, together with indentation after headings (such as \lstinline!chapter!, \lstinline!section!, etc) are also available. + The script also has the ability to modify line breaks, and to add comment symbols and blank lines; furthermore, it permits string or + regex-based substitutions. + All user options are customisable via the switches and the YAML interface; you can find a quick start guide in \vref{sec:quickstart}. +\end{adjustwidth} diff --git a/support/latexindent/latexindent-module-installer.pl b/support/latexindent/latexindent-module-installer.pl new file mode 100755 index 0000000000..e45fdcbe73 --- /dev/null +++ b/support/latexindent/latexindent-module-installer.pl @@ -0,0 +1,49 @@ +#!/usr/bin/env perl +# 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; + +print ("============\nlatexindent.pl module installer\n============\n"); +print ("Would you like to run the following commands?\n"); +my @modulesToInstall = ("cpanm YAML::Tiny","cpanm File::HomeDir","cpanm Unicode::GCString","cpanm Log::Log4perl","cpanm Log::Dispatch"); +foreach (@modulesToInstall) { + print $_,"\n"; +} +if (prompt_yn("Press Y to run the above commands")){ + foreach (@modulesToInstall) { + system($_); + } +} else { + print "Not installing modules\n"; +} +exit; + +# reference: https://stackoverflow.com/questions/18103501/prompting-multiple-questions-to-user-yes-no-file-name-input +sub prompt { + my ($query) = @_; # take a prompt string as argument + local $| = 1; # activate autoflush to immediately show the prompt + print $query; + chomp(my $answer = <STDIN>); + return $answer; +} + +sub prompt_yn { + my ($query) = @_; + my $answer = prompt("$query (Y/N): "); + return lc($answer) eq 'y'; +} diff --git a/support/latexindent/latexindent.pl b/support/latexindent/latexindent.pl new file mode 100755 index 0000000000..ecdf7c79e4 --- /dev/null +++ b/support/latexindent/latexindent.pl @@ -0,0 +1,81 @@ +#!/usr/bin/env perl +# latexindent.pl, version 3.7, 2019-07-13 +# +# 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 FindBin; # help find defaultSettings.yaml +use Getopt::Long; # to get the switches/options/flags + +# use lib to make sure that @INC contains the latexindent directory +use lib $FindBin::RealBin; +use LatexIndent::Document; + +# get the options +my %switches = (readLocalSettings=>0); + +GetOptions ( + "version|v"=>\$switches{version}, + "silent|s"=>\$switches{silentMode}, + "trace|t"=>\$switches{trace}, + "ttrace|tt"=>\$switches{ttrace}, + "local|l:s"=>\$switches{readLocalSettings}, + "yaml|y=s"=>\$switches{yaml}, + "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}, + "screenlog|sl"=>\$switches{screenlog}, + "replacement|r"=>\$switches{replacement}, + "onlyreplacement|rr"=>\$switches{onlyreplacement}, + "replacementrespectverb|rv"=>\$switches{replacementRespectVerb}, +); + +# check local settings doesn't interfer with reading the file; +# this can happen if the script is called as follows: +# +# latexindent.pl -l myfile.tex +# +# in which case, the GetOptions routine mistakes myfile.tex +# as the optional parameter to the l flag. +# +# 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($switches{readLocalSettings} and scalar(@ARGV) < 1) { + push(@ARGV,$switches{readLocalSettings}); + $switches{readLocalSettings} = ''; +} + +# default value of readLocalSettings +# +# latexindent -l myfile.tex +# +# means that we wish to use localSettings.yaml +if(defined($switches{readLocalSettings}) and ($switches{readLocalSettings} eq '')){ + $switches{readLocalSettings} = 'localSettings.yaml'; +} + +# allow STDIN as input, if a filename is not present +unshift( @ARGV, '-' ) unless @ARGV; + +my $document = bless ({name=>"masterDocument",modifyLineBreaksYamlName=>"masterDocument",fileName=>$ARGV[0],switches=>\%switches},"LatexIndent::Document"); +$document->latexindent; +exit(0); |