summaryrefslogtreecommitdiff
path: root/support/latexindent/LatexIndent/HiddenChildren.pm
diff options
context:
space:
mode:
Diffstat (limited to 'support/latexindent/LatexIndent/HiddenChildren.pm')
-rw-r--r--support/latexindent/LatexIndent/HiddenChildren.pm205
1 files changed, 205 insertions, 0 deletions
diff --git a/support/latexindent/LatexIndent/HiddenChildren.pm b/support/latexindent/LatexIndent/HiddenChildren.pm
new file mode 100644
index 0000000000..878939fd41
--- /dev/null
+++ b/support/latexindent/LatexIndent/HiddenChildren.pm
@@ -0,0 +1,205 @@
+package LatexIndent::HiddenChildren;
+# 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::Switches qw/$is_t_switch_active $is_tt_switch_active $is_m_switch_active /;
+use LatexIndent::Tokens qw/%tokens/;
+use LatexIndent::LogFile qw/$logger/;
+use Data::Dumper;
+use Exporter qw/import/;
+our @EXPORT_OK = qw/find_surrounding_indentation_for_children update_family_tree get_family_tree check_for_hidden_children %familyTree/;
+
+# hiddenChildren can be stored in a global array, it doesn't matter what level they're at
+our %familyTree;
+our %allChildren;
+
+
+#----------------------------------------------------------
+# Discussion surrounding hidden children
+#
+# Consider the following latex code
+#
+# \begin{one}
+# body of one
+# body of one
+# body of one
+# \begin{two}
+# body of two
+# body of two
+# body of two
+# body of two
+# \end{two}
+# \end{one}
+#
+# From the visual perspective, we might say that <one> and <two> are *nested* children;
+# from the persepective of latexindent.pl, however, they actually have *the same level*.
+#
+# Graphically, you might represent it as follows
+#
+# *
+# / \
+# / \
+# / \
+# O O
+#
+# where * represents the 'root' document object, and each 'O' is an environment object; the
+# first one, on the left, represents <two> and the second one, on the right, represents <one>.
+# (Remember that the environment regexp does not allow \begin within its body.)
+#
+# When processing the document, <one> will be processed *before* <two>. Furthermore, because
+# <one> and <two> are at the same level, they are not *natural* ancestors of each other; as such,
+# we say that <two> is a *hidden* child, and that its 'adopted' ancestor is <one>.
+#
+# We need to go to a lot of effort to make sure that <two> knows about its ancestors and its
+# surrounding indentation (<one> in this case). The subroutines in this file do that effort.
+#----------------------------------------------------------
+
+sub find_surrounding_indentation_for_children{
+ my $self = shift;
+
+ # output to logfile
+ $logger->trace("*FamilyTree before update:") if $is_tt_switch_active;
+ $logger->trace(Dumper(\%familyTree)) if($is_tt_switch_active);
+
+ # update the family tree with ancestors
+ $self->update_family_tree;
+
+ # output information to the logfile
+ $logger->trace("*FamilyTree after update:") if $is_tt_switch_active;
+ $logger->trace(Dumper(\%familyTree)) if($is_tt_switch_active);
+
+ while( my ($idToSearch,$ancestorToSearch) = each %familyTree){
+ $logger->trace("*Hidden child ID: ,$idToSearch, here are its ancestors:") if $is_t_switch_active;
+ foreach(@{${$ancestorToSearch}{ancestors}}){
+ $logger->trace("ID: ${$_}{ancestorID}") if($is_t_switch_active);
+ my $tmpIndentation = ref(${$_}{ancestorIndentation}) eq 'SCALAR'?${${$_}{ancestorIndentation}}:${$_}{ancestorIndentation};
+ $tmpIndentation = $tmpIndentation ? $tmpIndentation : q();
+ $logger->trace("indentation: '$tmpIndentation'") if($is_t_switch_active);
+ }
+ }
+
+ return;
+}
+
+sub update_family_tree{
+ my $self = shift;
+
+ # loop through the hash
+ $logger->trace("*Updating FamilyTree...") if $is_t_switch_active;
+ while( my ($idToSearch,$ancestorToSearch)= each %familyTree){
+ $logger->trace("*current ID: $idToSearch") if($is_t_switch_active);
+ foreach(@{${$ancestorToSearch}{ancestors}}){
+ my $ancestorID = ${$_}{ancestorID};
+
+ # construct the natural ancestors
+ my $naturalAncestors = q();
+ foreach(@{${$familyTree{$idToSearch}}{ancestors}}){
+ $naturalAncestors .= "---".${$_}{ancestorID} if(${$_}{type} eq "natural");
+ }
+
+ # we only need to update the family tree if the $ancestorID is *not* a natural
+ # ancestor, otherwise everything will be taken care of by the natural ancestor
+ if($naturalAncestors !~ m/$ancestorID/){
+ $logger->trace("ancestor: $ancestorID") if($is_t_switch_active);
+ if($familyTree{$ancestorID}){
+ $logger->trace("$ancestorID is a key within familyTree, grabbing its ancestors") if($is_t_switch_active);
+ foreach(@{${$familyTree{$ancestorID}}{ancestors}}){
+ $logger->trace("ancestor of *hidden* child: ${$_}{ancestorID}") if($is_t_switch_active);
+ my $newAncestorId = ${$_}{ancestorID};
+ my $type;
+ if($naturalAncestors =~ m/$ancestorID/){
+ $type = "natural";
+ } else {
+ $type = "adopted";
+ }
+ my $matched = grep { $_->{ancestorID} eq $newAncestorId } @{${$familyTree{$idToSearch}}{ancestors}};
+ push(@{${$familyTree{$idToSearch}}{ancestors}},{ancestorID=>${$_}{ancestorID},ancestorIndentation=>${$_}{ancestorIndentation},type=>$type}) unless($matched);
+ }
+ } else {
+ $logger->trace("natural ancestors of $ancestorID: $naturalAncestors") if($is_t_switch_active);
+ foreach(@{${$allChildren{$ancestorID}}{ancestors}}){
+ my $newAncestorId = ${$_}{ancestorID};
+ my $type;
+ if($naturalAncestors =~ m/$newAncestorId/){
+ $type = "natural";
+ } else {
+ $type = "adopted";
+ }
+ my $matched = grep { $_->{ancestorID} eq $newAncestorId } @{${$familyTree{$idToSearch}}{ancestors}};
+ unless($matched){
+ $logger->trace("ancestor of UNHIDDEN child: ${$_}{ancestorID}") if($is_t_switch_active);
+ push(@{${$familyTree{$idToSearch}}{ancestors}},{ancestorID=>${$_}{ancestorID},ancestorIndentation=>${$_}{ancestorIndentation},type=>$type});
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
+
+sub check_for_hidden_children{
+
+ my $self = shift;
+
+ my @matched;
+ # grab the matches
+ if($is_m_switch_active){
+ # if modifyLineBreaks is active, then the IDS can be split across lines
+ my $ifElseFiSpecialBegin = join("\\R?\\h*",split(//,$tokens{ifelsefiSpecial}));
+ my $BeginwithLineBreaks = join("\\R?\\h*",split(//,$tokens{beginOfToken}));
+ my $EndwithLineBreaks = join("\\R?\\h*",split(//,$tokens{endOfToken}));
+ my $BlankLinesWithLineBreaks = join("\\R?\\h*",split(//,$tokens{blanklines}));
+ @matched = (${$self}{body} =~ /(?!$BlankLinesWithLineBreaks)((?:$ifElseFiSpecialBegin)?$BeginwithLineBreaks[-a-z0-9\n]+?$EndwithLineBreaks)/ig);
+
+ # remove line breaks and horizontal space from matches
+ $_ =~ s/\R|\h//gs foreach (@matched);
+ } else {
+ @matched = (${$self}{body} =~ /((?:$tokens{ifelsefiSpecial})?$tokens{beginOfToken}.[-a-z0-9]+?$tokens{endOfToken})/igs);
+ }
+
+ # log file
+ $logger->trace("*Hidden children check") if $is_t_switch_active;
+ $logger->trace(join("|",@matched)) if $is_t_switch_active;
+
+ my $naturalAncestors = ${$self}{naturalAncestors};
+
+ # loop through the hidden children
+ foreach my $match (@matched){
+ # update the family tree with ancestors of self
+ if(${$self}{ancestors}){
+ foreach(@{${$self}{ancestors}}){
+ my $newAncestorId = ${$_}{ancestorID};
+ unless (grep { $_->{ancestorID} eq $newAncestorId } @{${$familyTree{$match}}{ancestors}}){
+ my $type = ($naturalAncestors =~ m/${$_}{ancestorID}/ ) ? "natural" : "adopted";
+ $logger->trace("Adding ${$_}{ancestorID} to the $type family tree of $match") if($is_t_switch_active);
+ push(@{$familyTree{$match}{ancestors}},{ancestorID=>${$_}{ancestorID},ancestorIndentation=>${$_}{ancestorIndentation},type=>$type});
+ }
+ }
+ }
+
+ # update the family tree with self
+ unless (grep { $_->{ancestorID} eq ${$self}{id}} @{${$familyTree{$match}}{ancestors}}){
+ my $type = ($naturalAncestors =~ m/${$self}{id}/ ) ? "natural" : "adopted";
+ $logger->trace("Adding ${$self}{id} to the $type family tree of hiddenChild $match") if($is_t_switch_active);
+ push(@{$familyTree{$match}{ancestors}},{ancestorID=>${$self}{id},ancestorIndentation=>${$self}{indentation},type=>$type});
+ }
+ }
+
+}
+
+1;