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/LatexIndent/ModifyLineBreaks.pm |
Initial commit
Diffstat (limited to 'support/latexindent/LatexIndent/ModifyLineBreaks.pm')
-rw-r--r-- | support/latexindent/LatexIndent/ModifyLineBreaks.pm | 394 |
1 files changed, 394 insertions, 0 deletions
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; |