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 Data::Dumper; use Exporter qw/import/; use List::Util qw/max min sum/; use LatexIndent::TrailingComments qw/$trailingCommentRegExp/; use LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active %switches/; use LatexIndent::GetYamlSettings qw/%mainSettings/; use LatexIndent::Tokens qw/%tokens/; use LatexIndent::LogFile qw/$logger/; use LatexIndent::HiddenChildren qw/%familyTree/; use LatexIndent::Verbatim qw/%verbatimStorage/; our @ISA = "LatexIndent::Document"; # class inheritance, Programming Perl, pg 321 our @EXPORT_OK = qw/align_at_ampersand find_aligned_block double_back_slash_else main_formatting individual_padding multicolumn_padding multicolumn_pre_check multicolumn_post_check dont_measure hidden_child_cell_row_width hidden_child_row_width get_column_width/; our $alignmentBlockCounter; our @cellStorage; # two-dimensional storage array containing the cell information our @formattedBody; # array for the new body our @minMultiColSpan; our @maxColumnWidth; our @maxDelimiterWidth; 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( \%{ $mainSettings{lookForAlignDelims} } ) ) if ($is_tt_switch_active); while ( my ( $alignmentBlock, $yesno ) = each %{ $mainSettings{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 captured into $1 ) ( .*? # non-greedy match (body) into $3 ) \R # a line break \h* # possible horizontal spaces ( (?!<\\) %\* # % \h* # possible horizontal spaces \\end\{\2\} # \end{alignmentBlock} statement captured into $4 ) /sx; while ( ${$self}{body} =~ m/$alignmentRegExp/sx ) { ${$self}{body} =~ s/ $alignmentRegExp / # create a new Environment object my $alignmentBlockObj = LatexIndent::AlignmentAtAmpersand->new( begin=>$1, body=>$3, end=>$4, name=>$2, modifyLineBreaksYamlName=>"environments", linebreaksAtEnd=>{ begin=>1, body=>1, end=>0, }, ); # log file output $logger->trace("*Alignment block found: %*\\begin\{${$alignmentBlock}{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($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; my %input = @_; return if ( ${$self}{bodyLineBreaks} == 0 ); # some blocks may contain verbatim to be measured ${$self}{measureVerbatim} = ( ${$self}{body} =~ m/$tokens{verbatim}/ ? 1 : 0 ); my $maximumNumberOfAmpersands = 0; # clear the global arrays @formattedBody = (); @cellStorage = (); @minMultiColSpan = (); @maxColumnWidth = (); @maxDelimiterWidth = (); # maximum column widths my @maximumColumnWidths; my $rowCounter = -1; my $columnCounter = -1; $logger->trace("*dontMeasure routine, row mode") if ( ${$self}{dontMeasure} and $is_t_switch_active ); # # initial loop for column storage and measuring # foreach ( split( "\n", ${$self}{body} ) ) { $rowCounter++; # default is to measure this row, but it can be switched off by the dont_measure routine ${$self}{measureRow} = 1; # call the dont_measure routine $self->dont_measure( mode => "row", row => $_ ) if ${$self}{dontMeasure}; # remove \\ and anything following it my $endPiece = q(); if ( $_ =~ m/(\\\\.*)/ ) { if ( ${$self}{alignFinalDoubleBackSlash} ) { # for example, if we want: # # Name & \shortstack{Hi \\ Lo} \\