diff options
author | Karl Berry <karl@freefriends.org> | 2018-09-15 21:42:19 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2018-09-15 21:42:19 +0000 |
commit | 21b240fd974a27aad7d7c48cb027718a9e471f5b (patch) | |
tree | 85440b7e5cc9b6ca36f33a504377851ed0ac6e19 /Master/texmf-dist/scripts/latexindent/LatexIndent/Sentence.pm | |
parent | 6646c4d916203b724973f3d5adbd597577cdb1b4 (diff) |
latexindent (15sep18)
git-svn-id: svn://tug.org/texlive/trunk@48673 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts/latexindent/LatexIndent/Sentence.pm')
-rw-r--r-- | Master/texmf-dist/scripts/latexindent/LatexIndent/Sentence.pm | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/Sentence.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/Sentence.pm new file mode 100644 index 00000000000..c7bc39c284b --- /dev/null +++ b/Master/texmf-dist/scripts/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/(?:\.\)(?!\h*[a-z]))|(?:(?<!(?:(?:e\.g)|(?:i\.e)|(?:etc))))\.(?!(?:[a-z]|[A-Z]|\-|\,|[0-9]))/; + } 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; |