package LatexIndent::Blocks; # 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::GetYamlSettings qw/%mainSetting %previouslyFoundSetting $commaPolySwitchExists $equalsPolySwitchExists/; use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active $is_m_switch_active/; use LatexIndent::LogFile qw/$logger/; use LatexIndent::Tokens qw/%tokens/; use LatexIndent::ModifyLineBreaks qw/_mlb_line_break_token_adjust/; use Data::Dumper; use Exporter qw/import/; our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321 our @EXPORT_OK = qw/$mSwitchOnlyTrailing $allCodeBlocks _find_all_code_blocks _construct_code_blocks_regex $braceBracketRegExpBasic/; our $braceBracketRegExpBasic = qr/\{|\[/; our $allCodeBlocks; our $allArgumentRegEx; our $argumentBodyRegEx; our $argumentsBetween; our $environmentRegEx; our $ifElseFiRegEx; our $specialRegEx = q(); our $specialBeginRegEx = q(); our $specialEndRegEx = q(); our $specialRegExNested = q(); our $specialBeginRegExNested = q(); our $specialEndRegExNested = q(); our %specialLookUpName; our $specialAllRegEx = q(); our $codeBlockLookFor = q(); our $mSwitchOnlyTrailing = q(); our $mlbCommaSimpleArgBodyRegEx = qr(); our $mlbCommaSimpleAllArgumentRegEx = qr{}; our $mlbArgBodyWithCommasRegEx = q(); sub _construct_code_blocks_regex { $allArgumentRegEx = _construct_args_with_between(); my $commandName = qr/${${$mainSetting{fineTuning}}{commands}}{name}/; my $namedBracesBrackets = qr/${${$mainSetting{fineTuning}}{namedGroupingBracesBrackets}}{name}/; my $keyEqVal = qr/${${$mainSetting{fineTuning}}{keyEqualsValuesBracesBrackets}}{name} (?:\s|$trailingCommentRegExp|$tokens{blanklines})*=/x; my $unNamed = qr//; $mSwitchOnlyTrailing = ( $is_m_switch_active ? qr{(?\h*)?(?$trailingCommentRegExp)?(?\R\s*)?} : q{} ); # # environment regex # my $environmentName = qr/${${$mainSetting{fineTuning}}{environments}}{name}/; # for environment arguments my $mSwitchOnlyTrailingNoCapture = ( $is_m_switch_active ? qr{(?:\h*)?(?:$trailingCommentRegExp)?(?:\R\s*)?} : q{} ); # default environment regex $environmentRegEx = qr{ (? # \\begin\{(?$environmentName)\} # \begin{} ) # (? # *possible* arguments $allArgumentRegEx* # $mSwitchOnlyTrailingNoCapture ) # (? # body (?> # (??{ $environmentRegEx }) # | # (?! # (?:\\end\{) # ). # )* # ) # (? # \end{} \\end\{\g{ENVNAME}\} # ) # $mSwitchOnlyTrailing }xs; # non-default environment regex if ( defined ${ ${ $mainSetting{fineTuning} }{environments} }{begin} and defined ${ ${ $mainSetting{fineTuning} }{environments} }{end} ) { $logger->info("*environment regex changed from default using fineTuning"); $environmentRegEx = qr{ (? # ${${$mainSetting{fineTuning}}{environments}}{begin} ) # (? # *possible* arguments $allArgumentRegEx* # $mSwitchOnlyTrailingNoCapture ) # (? # body (?> # (??{ $environmentRegEx }) # | # (?! # (?:\\end\{) # ). # )* # ) # (? # \end{} ${${$mainSetting{fineTuning}}{environments}}{end} ) # $mSwitchOnlyTrailing }xs; } # # ifElseFi regex # my $ifElseFiNameRegExp = qr/${${$mainSetting{fineTuning}}{ifElseFi}}{name}/; $ifElseFiRegEx = qr{ (? # \ifnum, \ifodd (? $ifElseFiNameRegExp ) ) (? (?> (??{ $ifElseFiRegEx }) | (?! (?:\\fi(?![a-zA-Z])) ). )* ) (? # \fi statement \\fi(?![a-zA-Z]) ) $mSwitchOnlyTrailing }xs; # # specials regex, separated into 2 cases: # # 1. NOT nested # 2. YES nested # # # specials NOT nested # $specialBeginRegEx = q(); $specialEndRegEx = q(); foreach ( @{ $mainSetting{specialBeginEnd} } ) { next if !${$_}{lookForThis}; next if ${$_}{lookForThis} eq 'verbatim'; next if ${$_}{nested}; $specialBeginRegEx .= ( $specialBeginRegEx eq "" ? q() : "|" ) . ${$_}{begin}; $specialEndRegEx .= ( $specialEndRegEx eq "" ? q() : "|" ) . ${$_}{end}; } # # specials NOT nested, account for possibility of special being empty # if ( $specialBeginRegEx ne '' ) { $specialBeginRegEx = qr{$specialBeginRegEx}x; $specialEndRegEx = qr{$specialEndRegEx}x; $specialRegEx = qr{ (? $specialBeginRegEx ) (? (?> (?! (?:$specialEndRegEx) ). )* ) (? $specialEndRegEx ) $mSwitchOnlyTrailing }xs; } # # specials YES nested # $specialBeginRegExNested = q(); $specialEndRegExNested = q(); foreach ( @{ $mainSetting{specialBeginEnd} } ) { next if !${$_}{lookForThis}; next if ${$_}{lookForThis} eq 'verbatim'; next if !${$_}{nested}; $specialBeginRegExNested .= ( $specialBeginRegExNested eq "" ? q() : "|" ) . ${$_}{begin}; $specialEndRegExNested .= ( $specialEndRegExNested eq "" ? q() : "|" ) . ${$_}{end}; } # # specials YES nested # if ( $specialBeginRegExNested ne '' ) { $specialBeginRegExNested = qr{$specialBeginRegExNested}x; $specialEndRegExNested = qr{$specialEndRegExNested}x; $specialRegExNested = qr{ (? $specialBeginRegExNested ) (? (?> (??{ $specialRegExNested }) | (?! (?:$specialEndRegExNested) ). )* ) (? $specialEndRegExNested ) $mSwitchOnlyTrailing }xs; $codeBlockLookFor = qr/[{[]|$specialBeginRegEx|$specialBeginRegExNested|\\if/s; } else { $codeBlockLookFor = qr/[{[]|$specialBeginRegEx|\\if/s; } # # specialBeginEnd regex # if ( $specialRegEx ne '' and $specialRegExNested ne '' ) { # YES nested # YES not nested $specialAllRegEx = qr{ (? $specialRegEx ) | (? $specialRegExNested ) }x; } elsif ( $specialRegEx ne '' ) { # NO nested # YES not nested $specialAllRegEx = qr{ (? $specialRegEx ) }x; } elsif ( $specialRegExNested ne '' ) { # YES nested # no not nested $specialAllRegEx = qr{ (? $specialRegExNested ) }x; } # # (commands, named, key=value, unnamed) regex # if ( $specialAllRegEx ne '' ) { $logger->trace("*specialBeginEnd: at least one specialBeginEnd code block present") if $is_t_switch_active; $allCodeBlocks = qr{ (? \s* ) # $1 leading space (?: (? # $2 (?> # prevent backtracking ( # $3 name (? (?!(?:$environmentRegEx)) \\ # $4 command (? $commandName ) # $5 command name ) # | # (? $keyEqVal ) # $6 key=value name | # (? (? (? # $8 arguments (?: # $allArgumentRegEx # )+ # $mSwitchOnlyTrailing ) ) (*PRUNE) | (? $environmentRegEx ) (*PRUNE) | (? $ifElseFiRegEx ) (*PRUNE) | $specialAllRegEx ) }x; } else { $logger->trace("*specialBeginEnd empty, NO specialBeginEnd code blocks present") if $is_t_switch_active; $allCodeBlocks = qr{ (? \s* ) # $1 leading space (?: (? # $2 (?> # prevent backtracking ( # $3 name (? \\ # $4 command (? $commandName ) # $5 command name ) # | # (? $keyEqVal ) # $6 key=value name | # (? (? (? # $8 arguments (?: # $allArgumentRegEx # )+ # $mSwitchOnlyTrailing ) ) (*PRUNE) | (? $environmentRegEx ) (*PRUNE) | (? $ifElseFiRegEx ) (*PRUNE) ) }x; } # # m switch only, for the following poly-switches # # CommaStartsOnOwnLine # CommaFinishesWithLineBreak # # goal: # - use a stripped down regex for arguments # - use this stripped regex in matching commas # if ($is_m_switch_active) { $mlbCommaSimpleArgBodyRegEx = qr{ (?> # (?: \\[{}\[\]] # \{, \}, \[, \] OK ) | # [^{}\[\]] # anything except {, }, [, ] ) # }x; $mlbCommaSimpleAllArgumentRegEx = qr{}; $mlbCommaSimpleAllArgumentRegEx = qr{ (?: (?: (? (?:\s|$trailingCommentRegExp|$tokens{blanklines}|$argumentsBetween)* (? (?: (?: $mlbCommaSimpleArgBodyRegEx ) # argument body | (??{ $mlbCommaSimpleAllArgumentRegEx }) )* ) (? (? (?:\s|$trailingCommentRegExp|$tokens{blanklines}|$argumentsBetween)* (? (?: (?: $mlbCommaSimpleArgBodyRegEx ) # argument body | (??{ $mlbCommaSimpleAllArgumentRegEx }) )* ) (? (? , $mSwitchOnlyTrailing ) | (? $mlbCommaSimpleAllArgumentRegEx ) ) }xs; } } sub _construct_args_with_between { $argumentsBetween = qr/${${$mainSetting{fineTuning}}{arguments}}{between}/; my $mSwitchOnlyTrailing = qr{}; $mSwitchOnlyTrailing = qr{(\h*)?($trailingCommentRegExp)?(\R)?} if $is_m_switch_active; my $environmentName = qr/${${$mainSetting{fineTuning}}{environments}}{name}/; $argumentBodyRegEx = qr{ (?> # \\(?:begin|end)\{$environmentName\} | (?: \\\\\h*\[[^]]*\] # \\[ ... ] ) | (?: \\[{}\[\]] # \{, \}, \[, \] OK ) | # [^{}\[\]] # anything except {, }, [, ] ) # }x; my $allArgumentRegEx = qr{ (?: (?: (? (?:\s|$trailingCommentRegExp|$tokens{blanklines}|$argumentsBetween)* (? (?: (?: $argumentBodyRegEx ) # argument body | (??{ $allCodeBlocks }) )* ) (? (? (?:\s|$trailingCommentRegExp|$tokens{blanklines}|$argumentsBetween)* (? (?: (?: $argumentBodyRegEx ) # argument body | (??{ $allCodeBlocks }) )* ) (? (?trace("*found: $name \t type: environments") if $is_t_switch_active; # align at ampersand my $lookForAlignDelims = 0; if (defined $previouslyFoundSetting{$name.$modifyLineBreaksName}){ $lookForAlignDelims = (defined ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{lookForAlignDelims} ? ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{lookForAlignDelims} : 0 ); $logger->trace("alignment at ampersand ACTIVE") if ($is_t_switch_active and $lookForAlignDelims) ; } if (!$previouslyFoundSetting{$name.$modifyLineBreaksName} or $lookForAlignDelims or $is_m_switch_active){ $codeBlockObj = LatexIndent::Environment->new(name=>$name, begin=>$begin, body=>$body, end=>$end, modifyLineBreaksYamlName=>$modifyLineBreaksName, arguments=> 1, type=>"environment", horizontalTrailingSpace=>$horizontalTrailingSpace, trailingComment=>$trailingComment, linebreaksAtEnd=>{ end=>$linebreaksAtEnd, }, ); } # store settings for future use if (!$previouslyFoundSetting{$name.$modifyLineBreaksName} or $lookForAlignDelims ){ $codeBlockObj->yaml_get_indentation_settings_for_this_object; $lookForAlignDelims = ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{lookForAlignDelims} if defined ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{lookForAlignDelims}; } # argument indentation my $argBody = q(); if ($+{ENVARGS}){ $argBody = $+{ENVARGS}; $logger->trace("*found: arguments for $name environment") if $is_t_switch_active; $logger->trace("$argBody") if $is_t_switch_active; if ($argBody =~ m@^\s*$trailingCommentRegExp\s*$@s){ $body = $argBody.$body; $argBody = q(); $logger->trace("arguments for $name environment is ONLY a comment, pre-pending it to body") if $is_t_switch_active; } elsif ($argBody =~ m@^\s*$@s){ # respect trailing white space after begin statement $argBody =~ s@(^\h*)(\R\s*)?@$1@s; $begin = $begin.($1?$1:q()); $body = ($2?$2:q()).$body; $argBody = q(); ${$codeBlockObj}{begin} = $begin if $is_m_switch_active; $logger->trace("arguments for $name environment is ONLY space, pre-pending it to body") if $is_t_switch_active; } else { $argBody = _indent_all_args($name, $modifyLineBreaksName, $argBody, $currentIndentation); } } else { $logger->trace("no arguments for $name environment") if $is_t_switch_active; } # *** # find nested things # *** if (${ $mainSetting{indentAfterItems} }{ $name }){ $logger->trace("looking for environments ITEM for $name") if $is_t_switch_active; $body = _find_env_items ($body, $name, $modifyLineBreaksName, $currentIndentation); } else { $body = _find_all_code_blocks($body,$currentIndentation) if $body =~ m^$codeBlockLookFor^s; } $addedIndentation = ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{indentation}; # # m switch linebreak adjustment # if ($is_m_switch_active){ # argument trailing space goes to the *body* of the environment $argBody =~ s@(\s*)$@@s; $body = $1.$body; # appended to begin{} ${$codeBlockObj}{begin} = ${$codeBlockObj}{begin}.$argBody; # get the *updated* environment body from above nested code blocks routine ${$codeBlockObj}{body} = $body; # final argument can request a line break removal if ($argBody =~ m^$tokens{mAfterEndRemove}$^s){ ${${$codeBlockObj}{linebreaksAtEnd} }{begin} = 0; ${$codeBlockObj}{body} =~ s@^\s*@@s; } # poly-switch work $logger->trace("*-m switch poly-switch line break adjustment ($name ($modifyLineBreaksName))") if $is_t_switch_active; ${$codeBlockObj}{BeginStartsOnOwnLine} = $previouslyFoundSetting{$name.$modifyLineBreaksName}{BeginStartsOnOwnLine}; ${$codeBlockObj}{BodyStartsOnOwnLine} = $previouslyFoundSetting{$name.$modifyLineBreaksName}{BodyStartsOnOwnLine}; ${$codeBlockObj}{EndStartsOnOwnLine} = $previouslyFoundSetting{$name.$modifyLineBreaksName}{EndStartsOnOwnLine}; ${$codeBlockObj}{EndFinishesWithLineBreak} = $previouslyFoundSetting{$name.$modifyLineBreaksName}{EndFinishesWithLineBreak}; $codeBlockObj->_mlb_begin_starts_on_own_line if ${$codeBlockObj}{BeginStartsOnOwnLine} != 0; $codeBlockObj->_mlb_body_starts_on_own_line if ${$codeBlockObj}{BodyStartsOnOwnLine} != 0; $codeBlockObj->_mlb_end_starts_on_own_line if ${$codeBlockObj}{EndStartsOnOwnLine} != 0; $codeBlockObj->_mlb_end_finishes_with_line_break; # get updated begin, body, end $begin = ${$codeBlockObj}{begin}; $body = ${$codeBlockObj}{body}; $end = ${$codeBlockObj}{end}; # add indentation to BEGIN statement if ($argBody and $addedIndentation ne ''){ $argBody = $begin; # remove \begin{} from arguments $argBody =~ s@(.*?\\begin\{[^}]+\})@@s; # store \begin{} $begin = $1; # operate on argument body $argBody =~ s"^"$addedIndentation"mg; $argBody =~ s"^$addedIndentation""s; # put begin back together $begin = $begin.$argBody; } # delete arg body for future as we don't want duplication $argBody = q(); $linebreaksAtEndBegin = ${${$codeBlockObj}{linebreaksAtEnd}}{begin}; } else { $linebreaksAtEndBegin = ($body =~ m@^\h*\R@ ? 1 : 0); } ${$codeBlockObj}{body} = $body; # # align at ampersand # if ($lookForAlignDelims){ # environment argument or body needs care, see alignment SLASH environments.tex for example my $argumentsPresent = 0; if (!$is_m_switch_active and $argBody and ${$codeBlockObj}{body} !~ m@^\R@s){ ${$codeBlockObj}{body} = $argBody.${$codeBlockObj}{body}; $argBody = q(); $argumentsPresent = 1; } $codeBlockObj->align_at_ampersand; $begin = ${$codeBlockObj}{begin} if ${$codeBlockObj}{beginAdjusted}; $body = ${$codeBlockObj}{body}; $addedIndentation = ${$codeBlockObj}{indentation} if defined ${$codeBlockObj}{indentation}; $linebreaksAtEndBegin = ($body =~ m@^\h*\R@ ? 1 : 0) if $argumentsPresent; } # prepend argument body $body = $argBody.$body; # environment BODY indentation, possibly if ($addedIndentation ne ''){ # add indentation $body =~ s"^"$addedIndentation"mg if ($body !~ m@^\s*$@s); $body =~ s"^$addedIndentation""s if (!$linebreaksAtEndBegin or $argBody); } } elsif ($+{IFELSEFI}){ $begin = $1.$+{IFELSEFIBEGIN}; $body = $+{IFELSEFIBODY}; $end = $+{IFELSEFIEND}; $name = $+{IFELSEFINAME}; $modifyLineBreaksName = "ifElseFi"; my $linebreaksAtEndBegin; # storage before finding nested things my $horizontalTrailingSpace=($+{TRAILINGHSPACE}?$+{TRAILINGHSPACE}:q()); my $trailingComment=($+{TRAILINGCOMMENT}?$+{TRAILINGCOMMENT}:q()); my $linebreaksAtEnd=( $+{TRAILINGLINEBREAK} ? $+{TRAILINGLINEBREAK} : q()); my $elseExists; if (!$previouslyFoundSetting{$name.$modifyLineBreaksName} or $is_m_switch_active){ $codeBlockObj = LatexIndent::IfElseFi->new(name=>$name, begin=>$begin, body=>$body, end=>$end, modifyLineBreaksYamlName=>$modifyLineBreaksName, arguments=> 0, type=>"ifelsefi", horizontalTrailingSpace=>$horizontalTrailingSpace, trailingComment=>$trailingComment, linebreaksAtEnd=>{ end=>$linebreaksAtEnd, }, ); } $logger->trace("*found: $name \t type: $modifyLineBreaksName") if $is_t_switch_active; # store settings for future use if (!$previouslyFoundSetting{$name.$modifyLineBreaksName}){ $codeBlockObj->yaml_get_indentation_settings_for_this_object; } # *** # find nested things # *** $body = _find_all_code_blocks($body ,$currentIndentation) if $body =~ m^$codeBlockLookFor^s; # look for else, or ($body, $elseExists) = _find_ifelsefi_else_or($body, $name, $modifyLineBreaksName); # # m switch linebreak adjustment # if ($is_m_switch_active){ # middle can add line break tokens to the specialBeginEnd body $body =~ s@\R\h*$tokens{mBeforeBeginLineBreakADD}($tokens{blanklines})?@\n@sg; $body =~ s@$tokens{mBeforeBeginLineBreakADD}@\n@sg; $body =~ s@([a-zA-Z0-9])$tokens{mElseTrailingSpace}([^\\])@$1 $2@sg; $body =~ s@$tokens{mElseTrailingSpace}@@sg; # get the *updated* environment body from above nested code blocks routine ${$codeBlockObj}{body} = $body; # poly-switch work $logger->trace("*-m switch poly-switch line break adjustment ($name ($modifyLineBreaksName))") if $is_t_switch_active; ${$codeBlockObj}{BeginStartsOnOwnLine} = $previouslyFoundSetting{$name.$modifyLineBreaksName}{BeginStartsOnOwnLine}; ${$codeBlockObj}{BodyStartsOnOwnLine} = $previouslyFoundSetting{$name.$modifyLineBreaksName}{BodyStartsOnOwnLine}; ${$codeBlockObj}{EndStartsOnOwnLine} = $previouslyFoundSetting{$name.$modifyLineBreaksName}{EndStartsOnOwnLine}; ${$codeBlockObj}{EndFinishesWithLineBreak} = $previouslyFoundSetting{$name.$modifyLineBreaksName}{EndFinishesWithLineBreak}; $codeBlockObj->_mlb_begin_starts_on_own_line if ${$codeBlockObj}{BeginStartsOnOwnLine} != 0; $codeBlockObj->_mlb_body_starts_on_own_line if ${$codeBlockObj}{BodyStartsOnOwnLine} != 0; $codeBlockObj->_mlb_end_starts_on_own_line if ${$codeBlockObj}{EndStartsOnOwnLine} != 0; if (${$codeBlockObj}{BodyStartsOnOwnLine} == -1 and ${$codeBlockObj}{body} !~m@^\h@s and ${$codeBlockObj}{body} =~m@^[^\\]@s and ${$codeBlockObj}{body} !~m@^($tokens{specialBeginEndMiddle})?$tokens{mBeforeBeginLineBreakREMOVE}\\@s ){ ${$codeBlockObj}{begin} .= ' '; $logger->trace("adding single space ' ' *after* begin for $name (BodyStartsOnOwnLine == -1)") if $is_t_switch_active; } # \fi needs particular attention when FiFinishesWithLineBreak is -1, see # ifelsefi-first-mod-E -s -m -l=ifelsefi-all-on,ifelsefi-mod-lines12 -o=+-mod12 if (${$codeBlockObj}{EndFinishesWithLineBreak} == -1 and $horizontalTrailingSpace eq ''){ ${$codeBlockObj}{horizontalTrailingSpace} = ' '; $logger->trace("adding single space ' ' *after* end for $name (EndFinishesWithLineBreak == -1)") if $is_t_switch_active; } $codeBlockObj->_mlb_end_finishes_with_line_break; # \fi needs particular attention when FiFinishesWithLineBreak is 2, see # ifelsefi-one-line-mk1 -m -l=ifelsefi-add-comments-all -o=+-all-comments # in particular, this can result in # \fimSwitchComment # which then *breaks* the ifElseFi regex # # solution: *add* a space so that we get # \fi mSwitchComment # and then *remove* it in the adjust routine if (${$codeBlockObj}{EndFinishesWithLineBreak} == 2 and ${$codeBlockObj}{end} =~ m@^\\fi$tokens{mSwitchComment}@s ){ $logger->trace("adding single space ' ' temporarily *after* end for $name (EndFinishesWithLineBreak == 2)") if $is_t_switch_active; ${$codeBlockObj}{end} =~ s@^(\\fi)$tokens{mSwitchComment}@$1 $tokens{mSwitchCommentFi}@s; } # get updated begin, body, end $begin = ${$codeBlockObj}{begin}; $body = ${$codeBlockObj}{body}; $end = ${$codeBlockObj}{end}; $linebreaksAtEndBegin = ${$codeBlockObj}{linebreaksAtEnd}{begin}; } else { $linebreaksAtEndBegin = ($body =~ m@^\h*\R@ ? 1 : 0); } # ifElseFi BODY indentation, possibly if (${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{indentation} ne '' or $elseExists){ $addedIndentation = ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{indentation}; # add indentation $body =~ s"^(\h*)$tokens{mSwitchComment}\s*($tokens{specialBeginEndMiddle})"$1$2"mg if $is_m_switch_active; $body =~ s"^"$addedIndentation"mg; $body =~ s"^$addedIndentation""s if !$linebreaksAtEndBegin; # \else statement gets special treatment if ($elseExists){ $body =~ s"$addedIndentation$tokens{specialBeginEndMiddle}""mg; $body =~ s"$tokens{specialBeginEndMiddle}""sg; } # \if needs particular attention when BodyStartsOnOwnLine is -1, see # ifelsefi-nested-blank-lines -s -m -l=ifelsefi-all-on,ifelsefi-mod-lines18,removeTWS-before if ($is_m_switch_active and ${$codeBlockObj}{BodyStartsOnOwnLine} == -1 and ${$codeBlockObj}{begin} =~m@[a-zA-Z0-9]$@s and ${$codeBlockObj}{body} =~m@^[a-zA-Z0-9]@s){ ${$codeBlockObj}{body} = ' '.${$codeBlockObj}{body}; } } } elsif ($+{SPECIAL} or $+{SPECIALNESTED}){ my $lookupBegin = $+{SPECIALBEGIN}; $begin = $1.$+{SPECIALBEGIN}; $body = $+{SPECIALBODY}; $end = $+{SPECIALEND}; $modifyLineBreaksName = "specialBeginEnd"; my $linebreaksAtEndBegin; # storage before finding nested things my $horizontalTrailingSpace=($+{TRAILINGHSPACE}?$+{TRAILINGHSPACE}:q()); my $trailingComment=($+{TRAILINGCOMMENT}?$+{TRAILINGCOMMENT}:q()); my $linebreaksAtEnd=( $+{TRAILINGLINEBREAK} ? $+{TRAILINGLINEBREAK} : q()); # get the name of special if (not defined $specialLookUpName{$lookupBegin}){ foreach ( @{ $mainSetting{specialBeginEnd} } ) { if($lookupBegin =~m^${$_}{begin}^s){ $specialLookUpName{$lookupBegin} = ${$_}{name}; last; } } } $name = $specialLookUpName{$lookupBegin}; # align at ampersand my $lookForAlignDelims = 0; if (defined $previouslyFoundSetting{$name.$modifyLineBreaksName}){ $lookForAlignDelims = (defined ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{lookForAlignDelims} ? ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{lookForAlignDelims} : 0 ); $logger->trace("alignment at ampersand ACTIVE") if ($is_t_switch_active and $lookForAlignDelims) ; } if (!$previouslyFoundSetting{$name.$modifyLineBreaksName} or $lookForAlignDelims or $is_m_switch_active){ $codeBlockObj = LatexIndent::Special->new(name=>$name, begin=>$begin, body=>$body, end=>$end, modifyLineBreaksYamlName=>$modifyLineBreaksName, arguments=> 0, type=>"special", horizontalTrailingSpace=>$horizontalTrailingSpace, trailingComment=>$trailingComment, linebreaksAtEnd=>{ end=>$linebreaksAtEnd, }, ); } $logger->trace("*found: $name \t type: $modifyLineBreaksName") if $is_t_switch_active; # store settings for future use if (!$previouslyFoundSetting{$name.$modifyLineBreaksName} or $lookForAlignDelims ){ $codeBlockObj->yaml_get_indentation_settings_for_this_object; $lookForAlignDelims = ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{lookForAlignDelims} if defined ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{lookForAlignDelims}; } # *** # find nested things # *** $body = _find_all_code_blocks($body ,$currentIndentation) if $body =~ m^$codeBlockLookFor^s; # look for middle, if it is defined $body = _find_special_middle($body, $name, $modifyLineBreaksName) if defined ${$mainSetting{specialLookUpMiddle}}{$name}; $addedIndentation = ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{indentation}; # # m switch linebreak adjustment # if ($is_m_switch_active){ # middle can add line break tokens to the specialBeginEnd body $body =~ s@\R\h*$tokens{mBeforeBeginLineBreakADD}($tokens{blanklines})?@\n@sg; $body =~ s@$tokens{mBeforeBeginLineBreakADD}@\n@sg; # get the *updated* environment body from above nested code blocks routine ${$codeBlockObj}{body} = $body; # poly-switch work $logger->trace("*-m switch poly-switch line break adjustment ($name ($modifyLineBreaksName))") if $is_t_switch_active; ${$codeBlockObj}{BeginStartsOnOwnLine} = $previouslyFoundSetting{$name.$modifyLineBreaksName}{BeginStartsOnOwnLine}; ${$codeBlockObj}{BodyStartsOnOwnLine} = $previouslyFoundSetting{$name.$modifyLineBreaksName}{BodyStartsOnOwnLine}; ${$codeBlockObj}{EndStartsOnOwnLine} = $previouslyFoundSetting{$name.$modifyLineBreaksName}{EndStartsOnOwnLine}; ${$codeBlockObj}{EndFinishesWithLineBreak} = $previouslyFoundSetting{$name.$modifyLineBreaksName}{EndFinishesWithLineBreak}; $codeBlockObj->_mlb_begin_starts_on_own_line if ${$codeBlockObj}{BeginStartsOnOwnLine} != 0; $codeBlockObj->_mlb_body_starts_on_own_line if ${$codeBlockObj}{BodyStartsOnOwnLine} != 0; $codeBlockObj->_mlb_end_starts_on_own_line if ${$codeBlockObj}{EndStartsOnOwnLine} != 0; $codeBlockObj->_mlb_end_finishes_with_line_break; # get updated begin, body, end $begin = ${$codeBlockObj}{begin}; $body = ${$codeBlockObj}{body}; $end = ${$codeBlockObj}{end}; $linebreaksAtEndBegin = ${$codeBlockObj}{linebreaksAtEnd}{begin}; } else { $linebreaksAtEndBegin = ($body =~ m@^\h*\R@ ? 1 : 0); } # # align at ampersand # if ($lookForAlignDelims){ $codeBlockObj->align_at_ampersand; $begin = ${$codeBlockObj}{begin}; $body = ${$codeBlockObj}{body}; $addedIndentation = ${$codeBlockObj}{indentation} if defined ${$codeBlockObj}{indentation}; } # special BODY indentation, possibly if (${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{indentation} ne ''){ # add indentation if ($is_m_switch_active and defined ${$mainSetting{specialLookUpMiddle}}{$name}){ $body =~ s"^(\h)*$tokens{mSwitchComment}\s*($tokens{specialBeginEndMiddle})"($1?$1:q()).$2;"emg; } $body =~ s"^"$addedIndentation"mg; $body =~ s"^$addedIndentation""s if !$linebreaksAtEndBegin; if (defined ${$mainSetting{specialLookUpMiddle}}{$name}){ $body =~ s"$addedIndentation$tokens{specialBeginEndMiddle}""mg; $body =~ s"$tokens{specialBeginEndMiddle}""g; } } } else { # argument body is always in $8 my $argBody = $+{ARGUMENTS}; my $keyEqualsValue = q(); my $parentBegin = q(); # # command # if ($+{COMMAND}) { # $begin = $+{LEADINGSPACE}.q(\\); # \\ $name = $+{COMMANDNAME}; # $modifyLineBreaksName="commands"; # $parentBegin = q(\\).$name; # for alignAtAmpersand } elsif ($+{NAMEDBRACESBRACKETS}) { # # # # named braces or brackets # # # $begin = $+{LEADINGSPACE}; # $name = $+{NAMEDBRACESBRACKETS} ; # $modifyLineBreaksName="namedGroupingBracesBrackets"; # $parentBegin = $name; # for alignAtAmpersand } elsif($+{KEYEQVAL}) { # -------------------------------- # # # key = braces or brackets # # # $name = $+{KEYEQVAL}; # $name =~ s^((?:\s|$trailingCommentRegExp|$tokens{blanklines})*=)^^s; # $keyEqualsValue = $1; $begin = $begin.$name; $modifyLineBreaksName="keyEqualsValuesBracesBrackets"; # $parentBegin = $name; # for alignAtAmpersand } else { # -------------------------------- # # # unnamed braces and brackets # # # $begin = $+{LEADINGSPACE}; # $name = q(); # $modifyLineBreaksName="UnNamedGroupingBracesBrackets"; # } if (!$previouslyFoundSetting{$name.$modifyLineBreaksName} or $is_m_switch_active){ $codeBlockObj = LatexIndent::Blocks->new(name=>$name, begin=>$begin, modifyLineBreaksYamlName=>$modifyLineBreaksName, arguments=>1, type=>"something-with-braces", ); } $logger->trace("*found: $name \t type: $modifyLineBreaksName") if $is_t_switch_active; # store settings for future use if (!$previouslyFoundSetting{$name.$modifyLineBreaksName}){ ${$codeBlockObj}{BeginStartsOnOwnLine} = 0; ${$codeBlockObj}{BodyStartsOnOwnLine} = 0; $codeBlockObj->yaml_get_indentation_settings_for_this_object; } # m-switch: command name starts on own line if ($is_m_switch_active){ if ( ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{BeginStartsOnOwnLine} !=0){ ${$codeBlockObj}{BeginStartsOnOwnLine}=${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{BeginStartsOnOwnLine}; $codeBlockObj->_mlb_begin_starts_on_own_line; $begin = ${$codeBlockObj}{begin}; $begin =~ s@$tokens{mAfterEndLineBreak}$tokens{mBeforeBeginLineBreakREMOVE}@@sg; } if (${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{BodyStartsOnOwnLine} !=0){ $argBody =~ s"^\s*""s if ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{BodyStartsOnOwnLine} == -1; } # key = value poly-switches: EqualsStartsOnOwnLine or EqualsFinishesWithLineBreak if ( $modifyLineBreaksName eq "keyEqualsValuesBracesBrackets" and $equalsPolySwitchExists){ my $EqualsStartsOnOwnLine= (defined ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{EqualsStartsOnOwnLine}? ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{EqualsStartsOnOwnLine} : 0); my $EqualsFinishesWithLineBreak= (defined ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{EqualsFinishesWithLineBreak}? ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{EqualsFinishesWithLineBreak} : 0); $keyEqualsValue =~s^=^^s; $argBody =~ s@^$mSwitchOnlyTrailing@@s; my $horizontalTrailingSpace=($+{TRAILINGHSPACE}?$+{TRAILINGHSPACE}:q()); my $trailingComment=($+{TRAILINGCOMMENT}?$+{TRAILINGCOMMENT}:q()); my $linebreaksAtEnd=($+{TRAILINGCOMMENT} ? q() : ( $+{TRAILINGLINEBREAK} ? $+{TRAILINGLINEBREAK} : q())); my $codeBlockObj = LatexIndent::Special->new(name=>"equals", body=>$keyEqualsValue, end=>"=", modifyLineBreaksYamlName=>"equals", type=>"equals", EndStartsOnOwnLine=>$EqualsStartsOnOwnLine, EndFinishesWithLineBreak=>$EqualsFinishesWithLineBreak, horizontalTrailingSpace=>$horizontalTrailingSpace, trailingComment=>$trailingComment, linebreaksAtEnd=>{ end=>$linebreaksAtEnd, }, ); $logger->trace("*found: equals in key=value") if $is_t_switch_active; $codeBlockObj->_mlb_end_starts_on_own_line if ${$codeBlockObj}{EndStartsOnOwnLine} != 0; $codeBlockObj->_mlb_end_finishes_with_line_break if ${$codeBlockObj}{EndFinishesWithLineBreak} != 0; ${$codeBlockObj}{body} = ${$codeBlockObj}{body}.${$codeBlockObj}{end}; $codeBlockObj->_mlb_after_indentation_token_adjust; $keyEqualsValue = ${$codeBlockObj}{body}; } if ( $modifyLineBreaksName eq "keyEqualsValuesBracesBrackets"){ $keyEqualsValue =~ s^=(.*)$^=^s; $argBody = $1.$argBody; # EqualsFinishesWithLineBreak can add line break tokens if ($is_m_switch_active and $argBody =~ m@^($trailingCommentRegExp?$tokens{mAfterEndLineBreak})@s) { $argBody =~ s@^($trailingCommentRegExp?$tokens{mAfterEndLineBreak})@@s; $keyEqualsValue .= $1; } } } # argument indentation $argBody = _indent_all_args($name, $modifyLineBreaksName, $argBody,$currentIndentation, $parentBegin); $argBody = $keyEqualsValue.$argBody if $modifyLineBreaksName eq "keyEqualsValuesBracesBrackets"; # command BODY indentation, possibly if (${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{indentation} ne ''){ $addedIndentation = ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{indentation}; # add indentation $argBody =~ s"^"$addedIndentation"mg; $argBody =~ s"^$addedIndentation""s; } # put it all together $body = $name; # key = value is particular $body = q() if $modifyLineBreaksName eq "keyEqualsValuesBracesBrackets"; $end = $argBody; } # --------------------- # output indented block # --------------------- $begin.$body.$end;/sgex; # m switch conflicting linebreak addition or removal handled by tokens $body = _mlb_line_break_token_adjust($body) if $is_m_switch_active; return $body; } sub _indent_all_args { my ( $name, $modifyLineBreaksName, $body, $indentation, $parentBegin ) = @_; my $commandStorageName = $name . $modifyLineBreaksName; my $mandatoryArgumentsIndentation = ${ $previouslyFoundSetting{$commandStorageName} }{mandatoryArgumentsIndentation}; my $optionalArgumentsIndentation = ${ $previouslyFoundSetting{$commandStorageName} }{optionalArgumentsIndentation}; my $lookForAlignDelims = ( defined ${ $previouslyFoundSetting{$commandStorageName} }{lookForAlignDelims} ? ${ $previouslyFoundSetting{ $name . $modifyLineBreaksName } }{lookForAlignDelims} : 0 ); $body =~ s|\G$allArgumentRegEx$mSwitchOnlyTrailing| # mandatory or optional argument? my $mandatoryArgument = ($+{MANDARGBEGIN}?1:0); # begin my $begin = ($mandatoryArgument ? $+{MANDARGBEGIN}:$+{OPTARGBEGIN}); # body my $argBody = (defined $+{MANDARGBODY}?$+{MANDARGBODY}:$+{OPTARGBODY}); # end my $end = ($+{MANDARGEND}?$+{MANDARGEND}:$+{OPTARGEND}); # indentation my $currentIndentation = ($mandatoryArgument ? $mandatoryArgumentsIndentation : $optionalArgumentsIndentation).$indentation; # does arg body start on own line? my $argBodyStartsOwnLine; # storage before finding nested things my $horizontalTrailingSpace=($+{TRAILINGHSPACE}?$+{TRAILINGHSPACE}:q()); my $trailingComment=($+{TRAILINGCOMMENT}?$+{TRAILINGCOMMENT}:q()); my $linebreaksAtEnd=( $+{TRAILINGLINEBREAK} ? $+{TRAILINGLINEBREAK} : q()); # *** # find nested things # *** $argBody = _find_all_code_blocks($argBody,$indentation) if $argBody=~ m^$codeBlockLookFor^s; # # m switch linebreak adjustment # if ($is_m_switch_active or $lookForAlignDelims ){ my $argType = ($mandatoryArgument ? "mand" : "opt"); # possible comma poly-switch if ($commaPolySwitchExists){ my $CommaStartsOnOwnLine = (defined ${${$previouslyFoundSetting{$commandStorageName}}{$argType}}{CommaStartsOnOwnLine}? ${${$previouslyFoundSetting{$commandStorageName}}{$argType}}{CommaStartsOnOwnLine}: 0); my $CommaFinishesWithLineBreak = (defined ${${$previouslyFoundSetting{$commandStorageName}}{$argType}}{CommaFinishesWithLineBreak}? ${${$previouslyFoundSetting{$commandStorageName}}{$argType}}{CommaFinishesWithLineBreak}: 0); $argBody = _mlb_commas_in_arg_body($argBody,$CommaStartsOnOwnLine,$CommaFinishesWithLineBreak); $argBody =~ s@\R\h*$tokens{mBeforeBeginLineBreakADD}@\n@sg; $argBody =~ s@$tokens{mBeforeBeginLineBreakADD}@\n@sg; $argBody =~ s@$tokens{mAfterEndLineBreak}@\n@sg; } # begin statements my $BeginStartsOnOwnLine=${${$previouslyFoundSetting{$commandStorageName}}{mand}}{LCuBStartsOnOwnLine}; # body statements my $BodyStartsOnOwnLine=${${$previouslyFoundSetting{$commandStorageName}}{mand}}{MandArgBodyStartsOnOwnLine}; # end statements my $EndStartsOnOwnLine=${${$previouslyFoundSetting{$commandStorageName}}{mand}}{RCuBStartsOnOwnLine}; # after end statements my $EndFinishesWithLineBreak=${${$previouslyFoundSetting{$commandStorageName}}{mand}}{RCuBFinishesWithLineBreak}; # DBSStartsOnOwnLine, DBSFinishesWithLineBreak my $DBSStartsOnOwnLine= (defined ${${$previouslyFoundSetting{$commandStorageName}}{$argType}}{DBSStartsOnOwnLine}? ${${$previouslyFoundSetting{$commandStorageName}}{$argType}}{DBSStartsOnOwnLine}: 0); my $DBSFinishesWithLineBreak= (defined ${${$previouslyFoundSetting{$commandStorageName}}{$argType}}{DBSFinishesWithLineBreak}? ${${$previouslyFoundSetting{$commandStorageName}}{$argType}}{DBSFinishesWithLineBreak}: 0); if (!$mandatoryArgument){ # begin statements $BeginStartsOnOwnLine=${${$previouslyFoundSetting{$commandStorageName}}{opt}}{LSqBStartsOnOwnLine}; # body statements $BodyStartsOnOwnLine=${${$previouslyFoundSetting{$commandStorageName}}{opt}}{OptArgBodyStartsOnOwnLine}; # end statements $EndStartsOnOwnLine=${${$previouslyFoundSetting{$commandStorageName}}{opt}}{RSqBStartsOnOwnLine}; # after end statements $EndFinishesWithLineBreak=${${$previouslyFoundSetting{$commandStorageName}}{opt}}{RSqBFinishesWithLineBreak}; $argType = "optional"; } my $argument = LatexIndent::MandatoryArgument->new( begin=>$begin, # begin statement body=>$argBody, # body statement end=>$end, # end statement name=>$name, modifyLineBreaksName=>$modifyLineBreaksName, modifyLineBreaksYamlName=>$argType."Arguments", type=>"argument", BeginStartsOnOwnLine=>$BeginStartsOnOwnLine, # begin poly-switch BodyStartsOnOwnLine=>$BodyStartsOnOwnLine, # body poly-switch EndStartsOnOwnLine=>$EndStartsOnOwnLine, # end poly-switch EndFinishesWithLineBreak=>$EndFinishesWithLineBreak, # after end poly-switch DBSStartsOnOwnLine=>$DBSStartsOnOwnLine, DBSFinishesWithLineBreak=>$DBSFinishesWithLineBreak, horizontalTrailingSpace=>$horizontalTrailingSpace, trailingComment=>$trailingComment, parentBegin=>$parentBegin, linebreaksAtEnd=>{ end=>$linebreaksAtEnd, }, ); # poly-switch if($is_m_switch_active){ $logger->trace("*-m switch poly-switch line break adjustment ($commandStorageName, arg-type $argType)") if $is_t_switch_active; $argument->_mlb_begin_starts_on_own_line if ${$argument}{BeginStartsOnOwnLine} != 0; $argument->_mlb_body_starts_on_own_line if ${$argument}{BodyStartsOnOwnLine} != 0; $argument->_mlb_end_starts_on_own_line if ${$argument}{EndStartsOnOwnLine} != 0; $argument->_mlb_end_finishes_with_line_break; } # align at ampersand if ($lookForAlignDelims){ $argument->yaml_get_alignment_at_ampersand_from_parent($commandStorageName); $argument->align_at_ampersand; $currentIndentation = ${$argument}{indentation} if defined ${$argument}{indentation}; } # get updated begin, body, end $begin = ${$argument}{begin}; $argBody = ${$argument}{body}; $end = ${$argument}{end}; $argBodyStartsOwnLine = ${$argument}{linebreaksAtEnd}{begin}; } else { $argBodyStartsOwnLine = ($argBody =~ m@^\h*\R@ ? 1 : 0); } my $bodyHasLineBreaks = ($argBody=~m@[\n]@s ? 1 : 0); # add indentation $argBody =~ s@^@$currentIndentation@mg if ( ($argBodyStartsOwnLine or $bodyHasLineBreaks) and $argBody ne ''); # if arg body does NOT start on its own line, remove the first indentation added by previous step $argBody =~ s@^$currentIndentation@@s if (!$argBodyStartsOwnLine and $bodyHasLineBreaks); # put it all together $begin.$argBody.$end;|sgex; # m switch conflicting linebreak addition or removal handled by tokens if ($is_m_switch_active) { $body =~ s/($tokens{mBeforeBeginLineBreakREMOVE})($argumentsBetween*)\R?/$2$1/sg if ${ ${ $mainSetting{fineTuning} }{arguments} }{between} ne ''; $body = _mlb_line_break_token_adjust($body); } return $body; } sub _mlb_commas_in_arg_body { my ( $body, $CommaStartsOnOwnLine, $CommaFinishesWithLineBreak ) = @_; $body =~ s/$mlbArgBodyWithCommasRegEx/ my $begin = $1; my $commaOrArgs = q(); if ($+{comma}){ my $horizontalTrailingSpace=($+{TRAILINGHSPACE}?$+{TRAILINGHSPACE}:q()); my $trailingComment=($+{TRAILINGCOMMENT}?$+{TRAILINGCOMMENT}:q()); my $linebreaksAtEnd=($+{TRAILINGCOMMENT} ? q() : ( $+{TRAILINGLINEBREAK} ? $+{TRAILINGLINEBREAK} : q())); my $codeBlockObj = LatexIndent::Special->new(name=>"comma", begin=>$begin.",", body=>$horizontalTrailingSpace.$trailingComment.$linebreaksAtEnd, end=>q(), modifyLineBreaksYamlName=>"comma", type=>"comma", BeginStartsOnOwnLine=>$CommaStartsOnOwnLine, BodyStartsOnOwnLine=>$CommaFinishesWithLineBreak, ); $logger->trace("*found: comma within argument") if $is_t_switch_active; $codeBlockObj->_mlb_begin_starts_on_own_line if ${$codeBlockObj}{BeginStartsOnOwnLine} != 0; $codeBlockObj->_mlb_body_starts_on_own_line if ${$codeBlockObj}{BodyStartsOnOwnLine} != 0; $commaOrArgs = ${$codeBlockObj}{begin}.${$codeBlockObj}{body}.${$codeBlockObj}{end}; } else { $commaOrArgs = $+{arguments}; }; $commaOrArgs;/sgex; return $body; } # # specialBeginEnd that have middle # sub _find_special_middle { my ( $body, $name, $modifyLineBreaksName ) = @_; $logger->trace("looking for special MIDDLE for $name") if $is_t_switch_active; $logger->trace("${$mainSetting{specialLookUpMiddle}}{$name}") if $is_t_switch_active; my $specialMiddleRegEx = qr{ (\s*) (?: (? (? ${$mainSetting{specialLookUpMiddle}}{$name} ) $mSwitchOnlyTrailing ) | (? $specialAllRegEx ) ) }xs; # m-switch notes # don't usually mix poly-switch work like this, but # the middle statements need finding regardless of # whether the m-switch is active # my $commandStorageName = $name . $modifyLineBreaksName; my $SpecialMiddleStartsOnOwnLine = ( defined ${ $previouslyFoundSetting{$commandStorageName} }{SpecialMiddleStartsOnOwnLine} ? ${ $previouslyFoundSetting{$commandStorageName} }{SpecialMiddleStartsOnOwnLine} : 0 ); my $SpecialMiddleFinishesWithLineBreak = ( defined ${ $previouslyFoundSetting{$commandStorageName} }{SpecialMiddleFinishesWithLineBreak} ? ${ $previouslyFoundSetting{$commandStorageName} }{SpecialMiddleFinishesWithLineBreak} : 0 ); $body =~ s/$specialMiddleRegEx/ my $begin = $1; my $middleOrSpecial = q(); if ($+{SPECIALMIDDLE}){ my $horizontalTrailingSpace=($+{TRAILINGHSPACE}?$+{TRAILINGHSPACE}:q()); my $trailingComment=($+{TRAILINGCOMMENT}?$+{TRAILINGCOMMENT}:q()); my $linebreaksAtEnd=($+{TRAILINGCOMMENT} ? q() : ( $+{TRAILINGLINEBREAK} ? $+{TRAILINGLINEBREAK} : q())); my $codeBlockObj = LatexIndent::Special->new(name=>"specialMiddle", begin=>$begin.$+{SPECIALMIDDLESTATEMENT}, body=>$horizontalTrailingSpace.$trailingComment.$linebreaksAtEnd, end=>q(), modifyLineBreaksYamlName=>"middle", type=>"middle", BeginStartsOnOwnLine=>$SpecialMiddleStartsOnOwnLine, BodyStartsOnOwnLine=>$SpecialMiddleFinishesWithLineBreak, ); $logger->trace("*found: $+{SPECIALMIDDLESTATEMENT}\t type: specialBeginEnd MIDDLE") if $is_t_switch_active; # poly-switch work if ($is_m_switch_active){ $codeBlockObj->_mlb_begin_starts_on_own_line if ${$codeBlockObj}{BeginStartsOnOwnLine} != 0; $codeBlockObj->_mlb_body_starts_on_own_line if ${$codeBlockObj}{BodyStartsOnOwnLine} != 0; $middleOrSpecial = ${$codeBlockObj}{begin}.${$codeBlockObj}{body}.${$codeBlockObj}{end}; # poly-switch token adjustment $middleOrSpecial =~ s@^(\s*)@$1$tokens{specialBeginEndMiddle}@s; $middleOrSpecial =~ s@($tokens{specialBeginEndMiddle}\s*)($tokens{mSwitchComment}|$tokens{blanklines})(\s*)@$2$3$1@s; $middleOrSpecial =~ s@($tokens{specialBeginEndMiddle}\s*)($tokens{mBeforeBeginLineBreakADD}(?:$tokens{blanklines})?)(\s*)@$2$3$1@s; } else { $middleOrSpecial = $begin.$tokens{specialBeginEndMiddle}.$+{SPECIALMIDDLE}; } } else { $middleOrSpecial = $begin.$+{SPECIALALL}; }; $middleOrSpecial;/sgex; return $body; } # # ifelsefi find else, or # sub _find_ifelsefi_else_or { my ( $body, $name, $modifyLineBreaksName ) = @_; $logger->trace("looking for ifelsefi ELSE for $name") if $is_t_switch_active; $logger->trace( ${ ${ $mainSetting{fineTuning} }{ifElseFi} }{elseOrRegEx} ) if $is_t_switch_active; my $ifelsefiElseOrRegEx = qr{ (\s*) (?: (? (? ${${$mainSetting{fineTuning}}{ifElseFi}}{elseOrRegEx} ) $mSwitchOnlyTrailing ) | (? $ifElseFiRegEx ) ) }xs; # m-switch notes # don't usually mix poly-switch work like this, but # the ELSE/OR statements need finding regardless of # whether the m-switch is active # my $commandStorageName = $name . $modifyLineBreaksName; my $SpecialMiddleStartsOnOwnLine = ( defined ${ $previouslyFoundSetting{$commandStorageName} }{ElseStartsOnOwnLine} ? ${ $previouslyFoundSetting{$commandStorageName} }{ElseStartsOnOwnLine} : 0 ); my $SpecialMiddleFinishesWithLineBreak = ( defined ${ $previouslyFoundSetting{$commandStorageName} }{ElseFinishesWithLineBreak} ? ${ $previouslyFoundSetting{$commandStorageName} }{ElseFinishesWithLineBreak} : 0 ); my $elseExists = 0; $body =~ s/$ifelsefiElseOrRegEx/ my $begin = $1; my $middleOrSpecial = q(); if ($+{IFELSEFIELSE}){ $elseExists = 1; my $ifElseFiStatement = $+{IFELSEFIELSESTATEMENT}; my $horizontalTrailingSpace=($+{TRAILINGHSPACE}?$+{TRAILINGHSPACE}:q()); my $trailingComment=($+{TRAILINGCOMMENT}?$+{TRAILINGCOMMENT}:q()); my $linebreaksAtEnd=($+{TRAILINGCOMMENT} ? q() : ( $+{TRAILINGLINEBREAK} ? $+{TRAILINGLINEBREAK} : q())); my $codeBlockObj = LatexIndent::Special->new(name=>"ifElseFiElse", begin=>$begin.$ifElseFiStatement, body=>$horizontalTrailingSpace.$trailingComment.$linebreaksAtEnd, end=>q(), modifyLineBreaksYamlName=>"middle", type=>"middle", BeginStartsOnOwnLine=>$SpecialMiddleStartsOnOwnLine, BodyStartsOnOwnLine=>$SpecialMiddleFinishesWithLineBreak, ); $logger->trace("*found: $ifElseFiStatement in $name \t type: ifElseFi ELSE") if $is_t_switch_active; # poly-switch work if ($is_m_switch_active){ # # update for OR statement poly-switch # if ($ifElseFiStatement =~ m@\\or@s ){ ${$codeBlockObj}{BeginStartsOnOwnLine} = ( defined ${ $previouslyFoundSetting{$commandStorageName} }{OrStartsOnOwnLine} ? ${ $previouslyFoundSetting{$commandStorageName} }{OrStartsOnOwnLine} : 0 ); ${$codeBlockObj}{BodyStartsOnOwnLine} = ( defined ${ $previouslyFoundSetting{$commandStorageName} }{OrFinishesWithLineBreak} ? ${ $previouslyFoundSetting{$commandStorageName} }{OrFinishesWithLineBreak} : 0 ); } $codeBlockObj->_mlb_begin_starts_on_own_line if ${$codeBlockObj}{BeginStartsOnOwnLine} != 0; # else, OR require care when no trailing space is present my $elseRequiresTrailingSpace = 0; if (${$codeBlockObj}{BodyStartsOnOwnLine} == -1 and $horizontalTrailingSpace eq ''){ $elseRequiresTrailingSpace = 1; $logger->trace("adding trailing space ' ' *after* $ifElseFiStatement as body empty and BodyStartsOnOwnLine == -1"); } $codeBlockObj->_mlb_body_starts_on_own_line if ${$codeBlockObj}{BodyStartsOnOwnLine} != 0; $middleOrSpecial = ${$codeBlockObj}{begin} .($elseRequiresTrailingSpace ? $tokens{mElseTrailingSpace} : q() ) .${$codeBlockObj}{body} .${$codeBlockObj}{end}; # poly-switch token adjustment $middleOrSpecial =~ s@^(\s*)@$1$tokens{specialBeginEndMiddle}@s; $middleOrSpecial =~ s@($tokens{specialBeginEndMiddle}\s*)($tokens{mSwitchComment}|$tokens{blanklines})(\s*)@$2$3$1@s; $middleOrSpecial =~ s@($tokens{specialBeginEndMiddle}\s*)($tokens{mBeforeBeginLineBreakADD}(?:$tokens{blanklines})?)(\s*)@$2$3$1@s; } else { $middleOrSpecial = $begin.$tokens{specialBeginEndMiddle}.$+{IFELSEFIELSE}; } } else { $middleOrSpecial = $begin.$+{IFELSEFIALL}; }; $middleOrSpecial;/sgex; return ( $body, $elseExists ); } # # items in environment # sub _find_env_items { my ( $body, $name, $modifyLineBreaksName, $currentIndentation ) = @_; $logger->trace("looking for items for $name") if $is_t_switch_active; $logger->trace( ${ ${ $mainSetting{fineTuning} }{items} }{itemRegEx} ) if $is_t_switch_active; # # content BEFORE first item needs attention; see, for example # texexchange/350144.tex # my $itemRegExNoSpace = qr{ (?: (? ${${$mainSetting{fineTuning}}{items}}{itemRegEx} ) (? # body (?> # (??{ $environmentRegEx }) # | # (?! # (?:${${$mainSetting{fineTuning}}{items}}{itemRegEx}) ). # )* # ) ) }xs; my @envBodySplitByItem = split( /$itemRegExNoSpace/, $body ); my $beforeFirstItem = shift(@envBodySplitByItem); my $afterFirstItem = join( "", @envBodySplitByItem ); $beforeFirstItem = _find_all_code_blocks( $beforeFirstItem, $currentIndentation ); $body = $beforeFirstItem . $afterFirstItem; my $itemRegEx = qr{ (\s*) (?: (? ${${$mainSetting{fineTuning}}{items}}{itemRegEx} ) (? # body (?> # (??{ $environmentRegEx }) # | # (?! # (?:${${$mainSetting{fineTuning}}{items}}{itemRegEx}) ). # )* # ) ) }xs; # m-switch notes # don't usually mix poly-switch work like this, but # the ELSE/OR statements need finding regardless of # whether the m-switch is active # my $commandStorageName = $name . $modifyLineBreaksName; $body =~ s/$itemRegEx/ my $begin = $1.$+{ITEMBEGIN}; my $body = $+{ITEMBODY}; my $end = q(); (my $name = $begin) =~ s@[^a-zA-Z0-9]@@sg; # remove all non alphanumeric characters my $modifyLineBreaksName = "items"; my $linebreaksAtEndBegin; my $codeBlockObj; if (!$previouslyFoundSetting{$name.$modifyLineBreaksName} or $is_m_switch_active){ $codeBlockObj = LatexIndent::Special->new(name=>$name, begin=>$begin, body=>$body, end=>$end, modifyLineBreaksYamlName=>$modifyLineBreaksName, type=>"item", ); } $logger->trace("*found: $name \t type: ITEM") if $is_t_switch_active; # store settings for future use if (!$previouslyFoundSetting{$name.$modifyLineBreaksName}){ $codeBlockObj->yaml_get_indentation_settings_for_this_object; } # *** # find nested things # *** $body = _find_all_code_blocks($body,$currentIndentation) if $body =~ m^$codeBlockLookFor^s; # # poly-switch work # if ($is_m_switch_active){ # get the *updated* item body ${$codeBlockObj}{body} = $body; # poly-switch work $logger->trace("*-m switch poly-switch line break adjustment ($name ($modifyLineBreaksName))") if $is_t_switch_active; ${$codeBlockObj}{BeginStartsOnOwnLine} = $previouslyFoundSetting{$name.$modifyLineBreaksName}{BeginStartsOnOwnLine}; ${$codeBlockObj}{BodyStartsOnOwnLine} = $previouslyFoundSetting{$name.$modifyLineBreaksName}{BodyStartsOnOwnLine}; $codeBlockObj->_mlb_begin_starts_on_own_line if ${$codeBlockObj}{BeginStartsOnOwnLine} != 0; $codeBlockObj->_mlb_body_starts_on_own_line if ${$codeBlockObj}{BodyStartsOnOwnLine} != 0; # get updated begin, body, end $begin = ${$codeBlockObj}{begin}; $body = ${$codeBlockObj}{body}; $end = ${$codeBlockObj}{end}; # itemStartsOnOwnLine == 4, forced blank line if (${$codeBlockObj}{BeginStartsOnOwnLine} == 4 ){ $begin =~ s@($tokens{blanklines})\s*$tokens{mBeforeBeginLineBreakREMOVE}@$1\n@s; $begin =~ s@$tokens{mBeforeBeginLineBreakADD}@\n@s; } # need to avoid, for example, # # itemtwo # # desired: item two if (${$codeBlockObj}{BodyStartsOnOwnLine} == -1 ){ $logger->trace("adding trailing space ' ' *after* item statement ItemStartsOnOwnLine == -1"); $begin .= $tokens{mElseTrailingSpace}; } $linebreaksAtEndBegin = ${$codeBlockObj}{linebreaksAtEnd}{begin}; } else { $linebreaksAtEndBegin = ($body =~ m@^\h*\R@ ? 1 : 0); } my $addedIndentation = ${$previouslyFoundSetting{$name.$modifyLineBreaksName}}{indentation}; # item BODY indentation, possibly if ($addedIndentation ne ''){ # add indentation $body =~ s"^"$addedIndentation"mg if ($body !~ m@^\s*$@s); $body =~ s"^$addedIndentation""s if !$linebreaksAtEndBegin; } $begin.$body.$end;/sgex; # final m switch adjustment if ($is_m_switch_active) { # itemStartsOnOwnline == 2 needs care, see, for example, items1-mod5 # # mSwitchComment # \item $body =~ s/\R\h*$tokens{mSwitchComment}\R(\h*${${$mainSetting{fineTuning}}{items}}{itemRegEx})/\n$1/sg; # items5-mod5.tex $body =~ s/^$tokens{mSwitchComment}\R(\h*${${$mainSetting{fineTuning}}{items}}{itemRegEx})/$1/s; # itemStartsOnOwnline == 3 needs care, see, for example, items1-mod8 $body =~ s/\R\h*$tokens{mBeforeBeginLineBreakADD}($tokens{blanklines})?\s*($itemRegEx)/\n$2/sg; # itemFinishesWithLineBreak == -1 needs care, see, for example, items1-blanklines-unPreserveBlankLines-mod2 $body =~ s@([a-zA-Z0-9])$tokens{mElseTrailingSpace}([a-zA-Z0-9])@$1 $2@sg; $body =~ s@$tokens{mElseTrailingSpace}@@sg; # m switch conflicting linebreak addition or removal handled by tokens $body = _mlb_line_break_token_adjust($body); } return $body; } 1;