diff options
Diffstat (limited to 'support/latexindent/LatexIndent/Lines.pm')
-rw-r--r-- | support/latexindent/LatexIndent/Lines.pm | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/support/latexindent/LatexIndent/Lines.pm b/support/latexindent/LatexIndent/Lines.pm new file mode 100644 index 0000000000..edfe811564 --- /dev/null +++ b/support/latexindent/LatexIndent/Lines.pm @@ -0,0 +1,266 @@ +package LatexIndent::Lines; +# 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::LogFile qw/$logger/; +use LatexIndent::Switches qw/%switches/; +use LatexIndent::Verbatim qw/%verbatimStorage/; +our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321 +our @EXPORT_OK = qw/lines_body_selected_lines lines_verbatim_create_line_block/; + +sub lines_body_selected_lines { + my $self = shift; + my @lines = @{$_[0]}; + + # strip all space from lines switch + $switches{lines} =~ s/\h//sg; + + $logger->info("*-n,--lines switch is active, operating on lines $switches{lines}"); + $logger->info("number of lines in file: ".($#lines+1)); + $logger->info("*interpretting $switches{lines}"); + + my @lineRanges = split(/,/,$switches{lines}); + my @indentLineRange; + my @NOTindentLineRange; + + my %minMaxStorage; + my %negationMinMaxStorage; + + # loop through line ranges, which are separated by commas + # + # --lines 3-15,17-19 + # + foreach(@lineRanges){ + my $minLine = 0; + my $maxLine = 0; + my $negationMode = 0; + + # + # --lines !3-15 + # + if($_ =~ m/!/s){ + $negationMode = 1; + $_ =~ s/!//s; + $logger->info("negation mode active as $_"); + } + + # --lines min-max + if($_ =~ m/-/s){ + ($minLine,$maxLine) = split(/-/,$_); + } else { + $minLine = $_; + $maxLine = $_; + } + + # both minLine and maxLine need to be INTEGERS + if ($minLine !~ m/^\d+$/ or $maxLine !~ m/^\d+$/){ + $logger->warn("*$_ not a valid line specification; I'm ignoring this entry"); + next; + } + + # swap minLine and maxLine if necessary + if($minLine > $maxLine){ + ($minLine,$maxLine) = ($maxLine, $minLine); + } + + # minline > number of lines needs addressing + if ($minLine-1 > $#lines){ + $logger->warn("*--lines specified with min line $minLine which is *greater than* the number of lines in file: ".($#lines+1)); + $logger->warn("adjusting this value to be ".($#lines+1)); + $minLine = $#lines+1; + } + + # maxline > number of lines needs addressing + if ($maxLine-1 > $#lines){ + $logger->warn("*--lines specified with max line $maxLine which is *greater than* the number of lines in file: ".($#lines+1)); + $logger->warn("adjusting this value to be ".($#lines+1)); + $maxLine = $#lines+1; + } + + # either store the negation, or not + if($negationMode){ + $negationMinMaxStorage{$minLine} = $maxLine; + } else { + $minMaxStorage{$minLine} = $maxLine; + } + $logger->info("min line: $minLine, max line: $maxLine"); + } + + # only proceed if we have a valid line range + if ( (keys %minMaxStorage) < 1 and (keys %negationMinMaxStorage) < 1){ + $logger->warn("*--lines not specified with valid range: $switches{lines}"); + $logger->warn("entire body will be indented, and ignoring $switches{lines}"); + $switches{lines} = 0; + ${$self}{body} = join("",@lines); + return; + } + + # we need to perform the token check here + ${$self}{body} = join("",@lines); + $self->token_check; + ${$self}{body} = q(); + + # negated line ranges + if (keys %negationMinMaxStorage >= 1){ + @NOTindentLineRange = &lines_sort_and_combine_line_range(\%negationMinMaxStorage); + + $logger->info("*negation line range summary: "); + $logger->info("the number of NEGATION line ranges: ".(($#NOTindentLineRange+1)/2)); + $logger->info("the *sorted* NEGATION line ranges are in the form MIN-MAX: "); + for (my $index=0; $index<(($#NOTindentLineRange+1)/2); $index++){ + $logger->info(join("-",@NOTindentLineRange[2*$index..2*$index+1])); + + if($index==0 and $NOTindentLineRange[2*$index]>1){ + $minMaxStorage{1} = $NOTindentLineRange[2*$index]-1; + } elsif($index>0){ + $minMaxStorage{$NOTindentLineRange[2*$index-1]+1} = $NOTindentLineRange[2*$index]-1; + } + } + + # final range + if($NOTindentLineRange[-1]<$#lines){ + $minMaxStorage{$NOTindentLineRange[-1]+1} = $#lines+1; + } + } + + @indentLineRange = &lines_sort_and_combine_line_range(\%minMaxStorage) if (keys %minMaxStorage >= 1) ; + + $logger->info("*line range summary: "); + $logger->info("the number of indent line ranges: ".(($#indentLineRange+1)/2)); + $logger->info("the *sorted* line ranges are in the form MIN-MAX: "); + for (my $index=0; $index<(($#indentLineRange+1)/2); $index++){ + $logger->info(join("-",@indentLineRange[2*$index..2*$index+1])); + } + + my $startLine = 0; + + # now that we have the line range, we can sort arrange the body + while ($#indentLineRange>0){ + my $minLine = shift(@indentLineRange); + my $maxLine = shift(@indentLineRange); + + # perl arrays start at 0 + $minLine--; + $maxLine--; + + $self->lines_verbatim_create_line_block(\@lines,$startLine,$minLine-1) unless ($minLine == 0); + + ${$self}{body} .= join("",@lines[$minLine..$maxLine]); + + $startLine = $maxLine+1; + } + + # final line range + $self->lines_verbatim_create_line_block(\@lines,$startLine,$#lines) if($startLine<=$#lines); + return; +} + +sub lines_sort_and_combine_line_range{ + + my %minMaxStorage = %{$_[0]}; + # + # --lines 8-10,4-5,1-2 + # + # needs to be interpretted as + # + # --lines 1-2,4-5,8-10, + # + # sort the line ranges by the *minimum* value, the associated + # maximum values will be arranged after this + my @indentLineRange = sort { $a <=> $b } keys(%minMaxStorage); + + my @justMinimumValues = @indentLineRange; + for (my $index=0;$index<=$#justMinimumValues;$index++){ + splice(@indentLineRange,2*$index+1,0,$minMaxStorage{$justMinimumValues[$index]}); + } + + for (my $index=1;$index<(($#indentLineRange+1)/2);$index++){ + my $currentMin = $indentLineRange[2*$index]; + my $currentMax = $indentLineRange[2*$index+1]; + my $previousMax = $indentLineRange[2*$index-1]; + my $previousMin = $indentLineRange[2*$index-2]; + + if ( ($currentMin - 1) <= $previousMax and ( $currentMax > $previousMax ) ){ + # overlapping line ranges, for example + # + # --lines 3-5,4-10 + # + # needs to be interpretted as + # + # --lines 3-10 + # + $logger->info("overlapping line range found"); + $logger->info("line ranges (before): ".join(", ",@indentLineRange)); + splice(@indentLineRange,2*$index-1,2); + $logger->info("line ranges (after): ".join(", ",@indentLineRange)); + + # reset index so that loop starts again + $index = 0; + } elsif ( ($currentMin - 1) <= $previousMax and ( $currentMax <= $previousMax ) ){ + # overlapping line ranges, for example + # + # --lines 3-7,4-6 + # + # needs to be interpretted as + # + # --lines 3-7 + # + $logger->info("overlapping line range found"); + $logger->info("line ranges (before): ".join(", ",@indentLineRange)); + splice(@indentLineRange,2*$index,2); + $logger->info("line ranges (after): ".join(", ",@indentLineRange)); + + # reset index so that loop starts again + $index = 0; + } + } + + return @indentLineRange; +} + +sub lines_verbatim_create_line_block{ + my $self = shift; + my @lines = @{$_[0]}; + my $startLine = $_[1]; + my $finishLine = $_[2]; + + my $verbBody = join("",@lines[$startLine..$finishLine]); + $verbBody =~ s/\R?$//s; + + # create a new Verbatim object + my $noIndentBlockObj = LatexIndent::Verbatim->new( begin=>q(), + body=>$verbBody, + end=>q(), + name=>"line-switch-verbatim-protection", + type=>"linesprotect", + modifyLineBreaksYamlName=>"lines-not-to-be-indented", + ); + + # give unique id + $noIndentBlockObj->create_unique_id; + + # verbatim children go in special hash + $verbatimStorage{${$noIndentBlockObj}{id}}=$noIndentBlockObj; + + # remove the environment block, and replace with unique ID + ${$self}{body} .= ${$noIndentBlockObj}{id}."\n"; + + return; +} + +1; |