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/GetYamlSettings.pm |
Initial commit
Diffstat (limited to 'support/latexindent/LatexIndent/GetYamlSettings.pm')
-rw-r--r-- | support/latexindent/LatexIndent/GetYamlSettings.pm | 929 |
1 files changed, 929 insertions, 0 deletions
diff --git a/support/latexindent/LatexIndent/GetYamlSettings.pm b/support/latexindent/LatexIndent/GetYamlSettings.pm new file mode 100644 index 0000000000..3029043f23 --- /dev/null +++ b/support/latexindent/LatexIndent/GetYamlSettings.pm @@ -0,0 +1,929 @@ +package LatexIndent::GetYamlSettings; +# 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/%switches $is_m_switch_active $is_t_switch_active $is_tt_switch_active/; +use YAML::Tiny; # interpret defaultSettings.yaml and other potential settings files +use File::Basename; # to get the filename and directory path +use File::HomeDir; +use Log::Log4perl qw(get_logger :levels); +use Exporter qw/import/; +our @EXPORT_OK = qw/yaml_read_settings yaml_modify_line_breaks_settings yaml_get_indentation_settings_for_this_object yaml_poly_switch_get_every_or_custom_value yaml_get_indentation_information yaml_get_object_attribute_for_indentation_settings yaml_alignment_at_ampersand_settings yaml_get_textwrap_removeparagraphline_breaks %masterSettings yaml_get_columns/; + +# Read in defaultSettings.YAML file +our $defaultSettings; + +# master yaml settings is a hash, global to this module +our %masterSettings; + +# previously found settings is a hash, global to this module +our %previouslyFoundSettings; + +# default values for align at ampersand routine +our @alignAtAmpersandInformation = ( {name=>"lookForAlignDelims",yamlname=>"delims",default=>1}, + {name=>"alignDoubleBackSlash",default=>1}, + {name=>"spacesBeforeDoubleBackSlash",default=>1}, + {name=>"multiColumnGrouping",default=>0}, + {name=>"alignRowsWithoutMaxDelims",default=>1}, + {name=>"spacesBeforeAmpersand",default=>1}, + {name=>"spacesAfterAmpersand",default=>1}, + {name=>"justification",default=>"left"}, + ); + +sub yaml_read_settings{ + my $self = shift; + + # read the default settings + $defaultSettings = YAML::Tiny->read( "$FindBin::RealBin/defaultSettings.yaml" ) if ( -e "$FindBin::RealBin/defaultSettings.yaml" ); + + # grab the logger object + my $logger = get_logger("Document"); + $logger->info("*YAML settings read: defaultSettings.yaml\nReading defaultSettings.yaml from $FindBin::RealBin/defaultSettings.yaml"); + + # if latexindent.exe is invoked from TeXLive, then defaultSettings.yaml won't be in + # the same directory as it; we need to navigate to it + if(!$defaultSettings) { + $defaultSettings = YAML::Tiny->read( "$FindBin::RealBin/../../texmf-dist/scripts/latexindent/defaultSettings.yaml"); + $logger->info("Reading defaultSettings.yaml (2nd attempt, TeXLive, Windows) from $FindBin::RealBin/../../texmf-dist/scripts/latexindent/defaultSettings.yaml"); + } + + # need to exit if we can't get defaultSettings.yaml + die "Could not open defaultSettings.yaml" if(!$defaultSettings); + + # master yaml settings is a hash, global to this module + our %masterSettings = %{$defaultSettings->[0]}; + + # scalar to read user settings + my $userSettings; + + # array to store the paths to user settings + my @absPaths; + + # we'll need the home directory a lot in what follows + my $homeDir = File::HomeDir->my_home; + $logger->info("*YAML settings read: indentconfig.yaml or .indentconfig.yaml") unless $switches{onlyDefault}; + + # get information about user settings- first check if indentconfig.yaml exists + my $indentconfig = "$homeDir/indentconfig.yaml"; + + # if indentconfig.yaml doesn't exist, check for the hidden file, .indentconfig.yaml + $indentconfig = "$homeDir/.indentconfig.yaml" if(! -e $indentconfig); + + # messages for indentconfig.yaml and/or .indentconfig.yaml + if ( -e $indentconfig and !$switches{onlyDefault}) { + # read the absolute paths from indentconfig.yaml + $userSettings = YAML::Tiny->read( "$indentconfig" ); + + # update the absolute paths + if($userSettings and (ref($userSettings->[0]) eq 'HASH') and $userSettings->[0]->{paths}){ + $logger->info("Reading path information from $indentconfig"); + # if both indentconfig.yaml and .indentconfig.yaml exist + if ( -e File::HomeDir->my_home . "/indentconfig.yaml" and -e File::HomeDir->my_home . "/.indentconfig.yaml") { + $logger->info("$homeDir/.indentconfig.yaml has been found, but $indentconfig takes priority"); + } elsif ( -e File::HomeDir->my_home . "/indentconfig.yaml" ) { + $logger->info("(Alternatively $homeDir/.indentconfig.yaml can be used)"); + } elsif ( -e File::HomeDir->my_home . "/.indentconfig.yaml" ) { + $logger->info("(Alternatively $homeDir/indentconfig.yaml can be used)"); + } + + # output the contents of indentconfig to the log file + $logger->info(Dump \%{$userSettings->[0]}); + + @absPaths = @{$userSettings->[0]->{paths}}; + } else { + $logger->warn("*The paths field cannot be read from $indentconfig; this means it is either empty or contains invalid YAML"); + $logger->warn("See https://latexindentpl.readthedocs.io/en/latest/sec-indent-config-and-settings.html for an example"); + } + } else { + if($switches{onlyDefault}) { + $logger->info("*-d switch active: only default settings requested"); + $logger->info("not reading USER settings from $indentconfig") if (-e $indentconfig); + $logger->info("Ignoring the -l switch: $switches{readLocalSettings} (you used the -d switch)") if($switches{readLocalSettings}); + $logger->info("Ignoring the -y switch: $switches{yaml} (you used the -d switch)") if($switches{yaml}); + $switches{readLocalSettings}=0; + $switches{yaml}=0; + } else { + # give the user instructions on where to put indentconfig.yaml or .indentconfig.yaml + $logger->info("Home directory is $homeDir (didn't find either indentconfig.yaml or .indentconfig.yaml)\nTo specify user settings you would put indentconfig.yaml here: $homeDir/indentconfig.yaml\nAlternatively, you can use the hidden file .indentconfig.yaml as: $homeDir/.indentconfig.yaml"); + } + } + + + # local settings can be called with a + symbol, for example + # -l=+myfile.yaml + # -l "+ myfile.yaml" + # -l=myfile.yaml+ + # which translates to, respectively + # -l=localSettings.yaml,myfile.yaml + # -l=myfile.yaml,localSettings.yaml + # Note: the following is *not allowed*: + # -l+myfile.yaml + # and + # -l + myfile.yaml + # will *only* load localSettings.yaml, and myfile.yaml will be ignored + my @localSettings; + + $logger->info("*YAML settings read: -l switch") if $switches{readLocalSettings}; + + # remove leading, trailing, and intermediate space + $switches{readLocalSettings} =~ s/^\h*//g; + $switches{readLocalSettings} =~ s/\h*$//g; + $switches{readLocalSettings} =~ s/\h*,\h*/,/g; + if($switches{readLocalSettings} =~ m/\+/){ + $logger->info("+ found in call for -l switch: will add localSettings.yaml"); + + # + can be either at the beginning or the end, which determines if where the comma should go + my $commaAtBeginning = ($switches{readLocalSettings} =~ m/^\h*\+/ ? q() : ","); + my $commaAtEnd = ($switches{readLocalSettings} =~ m/^\h*\+/ ? "," : q()); + $switches{readLocalSettings} =~ s/\h*\+\h*/$commaAtBeginning."localSettings.yaml".$commaAtEnd/e; + $logger->info("New value of -l switch: $switches{readLocalSettings}"); + } + + # local settings can be separated by , + # e.g + # -l = myyaml1.yaml,myyaml2.yaml + # and in which case, we need to read them all + if($switches{readLocalSettings} =~ m/,/){ + $logger->info("Multiple localSettings found, separated by commas:"); + @localSettings = split(/,/,$switches{readLocalSettings}); + $logger->info(join(', ',@localSettings)); + } else { + push(@localSettings,$switches{readLocalSettings}) if($switches{readLocalSettings}); + } + + # add local settings to the paths, if appropriate + foreach (@localSettings) { + # check for an extension (.yaml) + my ($name, $dir , $ext) = fileparse($_, "yaml"); + + # if no extension is found, append the current localSetting with .yaml + $_ = $_.($_=~m/\.\z/ ? q() : ".")."yaml" if(!$ext); + + # if the -l switch is called on its own, or else with + + # and latexindent.pl is called from a different directory, then + # we need to account for this + if($_ eq "localSettings.yaml"){ + $_ = dirname (${$self}{fileName})."/".$_; + } + + # check for existence and non-emptiness + if ( (-e $_) and !(-z $_)) { + $logger->info("Adding $_ to YAML read paths"); + push(@absPaths,"$_"); + } elsif ( !(-e $_) ) { + $logger->warn("*yaml file not found: $_ not found. Proceeding without it."); + } + } + + # heading for the log file + $logger->info("*YAML settings, reading from the following files:") if @absPaths; + + # read in the settings from each file + foreach my $settings (@absPaths) { + # check that the settings file exists and that it isn't empty + if (-e $settings and !(-z $settings)) { + $logger->info("Reading USER settings from $settings"); + $userSettings = YAML::Tiny->read( "$settings" ); + + # if we can read userSettings + if($userSettings) { + # update the MASTER setttings to include updates from the userSettings + while(my($firstLevelKey, $firstLevelValue) = each %{$userSettings->[0]}) { + # the update approach is slightly different for hashes vs scalars/arrays + if (ref($firstLevelValue) eq "HASH") { + while(my ($secondLevelKey,$secondLevelValue) = each %{$userSettings->[0]{$firstLevelKey}}) { + if (ref $secondLevelValue eq "HASH"){ + # if masterSettings already contains a *scalar* value in secondLevelKey + # then we need to delete it (test-cases/headings-first.tex with indentRules1.yaml first demonstrated this) + if(ref $masterSettings{$firstLevelKey}{$secondLevelKey} ne "HASH"){ + $logger->trace("*masterSettings{$firstLevelKey}{$secondLevelKey} currently contains a *scalar* value, but it needs to be updated with a hash (see $settings); deleting the scalar") if($is_t_switch_active); + delete $masterSettings{$firstLevelKey}{$secondLevelKey} ; + } + while(my ($thirdLevelKey,$thirdLevelValue) = each %{$secondLevelValue}) { + if (ref $thirdLevelValue eq "HASH"){ + # similarly for third level + if (ref $masterSettings{$firstLevelKey}{$secondLevelKey}{$thirdLevelKey} ne "HASH"){ + $logger->trace("*masterSettings{$firstLevelKey}{$secondLevelKey}{$thirdLevelKey} currently contains a *scalar* value, but it needs to be updated with a hash (see $settings); deleting the scalar") if($is_t_switch_active); + delete $masterSettings{$firstLevelKey}{$secondLevelKey}{$thirdLevelKey} ; + } + while(my ($fourthLevelKey,$fourthLevelValue) = each %{$thirdLevelValue}) { + $masterSettings{$firstLevelKey}{$secondLevelKey}{$thirdLevelKey}{$fourthLevelKey} = $fourthLevelValue; + } + } else { + $masterSettings{$firstLevelKey}{$secondLevelKey}{$thirdLevelKey} = $thirdLevelValue; + } + } + } else { + # settings such as commandCodeBlocks can have arrays, which may wish + # to be amalgamated, rather than overwritten + if(ref($secondLevelValue) eq "ARRAY" + and + ${${$masterSettings{$firstLevelKey}{$secondLevelKey}}[0]}{amalgamate} + and + !(ref(${$secondLevelValue}[0]) eq "HASH" and defined ${$secondLevelValue}[0]{amalgamate} and !${$secondLevelValue}[0]{amalgamate}) + ){ + $logger->trace("*$firstLevelKey -> $secondLevelKey, amalgamate: 1") if($is_t_switch_active); + foreach (@{$secondLevelValue}){ + $logger->trace("$_") if($is_t_switch_active); + push (@{$masterSettings{$firstLevelKey}{$secondLevelKey}},$_) unless(ref($_) eq "HASH"); + } + + # remove duplicated entries, https://stackoverflow.com/questions/7651/how-do-i-remove-duplicate-items-from-an-array-in-perl + my %seen = (); + my @unique = grep { ! $seen{ $_ }++ } @{$masterSettings{$firstLevelKey}{$secondLevelKey}}; + @{$masterSettings{$firstLevelKey}{$secondLevelKey}} = @unique; + + $logger->trace("*master settings for $firstLevelKey -> $secondLevelKey now look like:") if $is_t_switch_active; + foreach (@{$masterSettings{$firstLevelKey}{$secondLevelKey}}){ + $logger->trace("$_") if($is_t_switch_active); + } + } else { + $masterSettings{$firstLevelKey}{$secondLevelKey} = $secondLevelValue; + } + } + } + } elsif (ref($firstLevelValue) eq "ARRAY") { + # update amalgamate in master settings + if(ref(${$firstLevelValue}[0]) eq "HASH" and defined ${$firstLevelValue}[0]{amalgamate}){ + ${$masterSettings{$firstLevelKey}[0]}{amalgamate} = ${$firstLevelValue}[0]{amalgamate}; + shift @{$firstLevelValue} if ${$masterSettings{$firstLevelKey}[0]}{amalgamate}; + } + + # if amalgamate is set to 1, then append + if(${$masterSettings{$firstLevelKey}[0]}{amalgamate}){ + # loop through the other settings + foreach (@{$firstLevelValue}){ + push (@{$masterSettings{$firstLevelKey}},$_); + } + } else { + # otherwise overwrite + $masterSettings{$firstLevelKey} = $firstLevelValue; + } + } else { + $masterSettings{$firstLevelKey} = $firstLevelValue; + } + } + + # output settings to $logfile + if($masterSettings{logFilePreferences}{showEveryYamlRead}){ + $logger->info(Dump \%{$userSettings->[0]}); + } else { + $logger->info("Not showing settings in the log file (see showEveryYamlRead and showAmalgamatedSettings)."); + } + } else { + # otherwise print a warning that we can not read userSettings.yaml + $logger->warn("*$settings contains invalid yaml format- not reading from it"); + } + } else { + # otherwise keep going, but put a warning in the log file + $logger->warn("*$homeDir/indentconfig.yaml"); + if (-z $settings) { + $logger->info("specifies $settings but this file is EMPTY -- not reading from it"); + } else { + $logger->info("specifies $settings but this file does not exist - unable to read settings from this file"); + } + } + } + + # read settings from -y|--yaml switch + if($switches{yaml}){ + # report to log file + $logger->info("*YAML settings read: -y switch"); + + # remove any horizontal space before or after , OR : OR ; or at the beginning or end of the switch value + $switches{yaml} =~ s/\h*(,|(?<!\\):|;)\h*/$1/g; + $switches{yaml} =~ s/^\h*//g; + + # store settings, possibly multiple ones split by commas + my @yamlSettings; + if($switches{yaml} =~ m/(?<!\\),/){ + @yamlSettings = split(/(?<!\\),/,$switches{yaml}); + } else { + push(@yamlSettings,$switches{yaml}); + } + + # it is possible to specify, for example, + # + # -y=indentAfterHeadings:paragraph:indentAfterThisHeading:1;level:1 + # -y=specialBeginEnd:displayMath:begin:'\\\[';end: '\\\]';lookForThis: 1 + # + # which should be translated into + # + # indentAfterHeadings: + # paragraph: + # indentAfterThisHeading:1 + # level:1 + # + # so we need to loop through the comma separated list and search + # for semi-colons + my $settingsCounter=0; + my @originalYamlSettings = @yamlSettings; + foreach(@originalYamlSettings){ + # increment the counter + $settingsCounter++; + + # check for a match of the ; + if($_ =~ m/(?<!\\);/){ + my (@subfield) = split(/(?<!\\);/,$_); + + # the content up to the first ; is called the 'root' + my $root = shift @subfield; + + # split the root at : + my (@keysValues) = split(/:/,$root); + + # get rid of the last *two* elements, which will be + # key: value + # for example, in + # -y=indentAfterHeadings:paragraph:indentAfterThisHeading:1;level:1 + # then @keysValues holds + # indentAfterHeadings:paragraph:indentAfterThisHeading:1 + # so we need to get rid of both + # 1 + # indentAfterThisHeading + # so that we are in a position to concatenate + # indentAfterHeadings:paragraph + # with + # level:1 + # to form + # indentAfterHeadings:paragraph:level:1 + pop(@keysValues); + pop(@keysValues); + + # update the appropriate piece of the -y switch, for example: + # -y=indentAfterHeadings:paragraph:indentAfterThisHeading:1;level:1 + # needs to be changed to + # -y=indentAfterHeadings:paragraph:indentAfterThisHeading:1 + # the + # indentAfterHeadings:paragraph:level:1 + # will be added in the next part + $yamlSettings[$settingsCounter-1] = $root; + + # reform the root + $root = join(":",@keysValues); + $logger->trace("*Sub-field detected (; present) and the root is: $root") if $is_t_switch_active; + + # now we need to attach the $root back together with any subfields + foreach(@subfield){ + # splice the new field into @yamlSettings (reference: https://perlmaven.com/splice-to-slice-and-dice-arrays-in-perl) + splice @yamlSettings,$settingsCounter,0,$root.":".$_; + + # increment the counter + $settingsCounter++; + } + $logger->info("-y switch value interpreted as: ".join(',',@yamlSettings)); + } + } + + # loop through each of the settings specified in the -y switch + foreach(@yamlSettings){ + # split each value at semi-colon + my (@keysValues) = split(/(?<!(?:\\|\[)):(?!\])/,$_); + + # $value will always be the last element + my $value = $keysValues[-1]; + + # it's possible that the 'value' will contain an escaped + # semi-colon, so we replace it with just a semi-colon + $value =~ s/\\:/:/; + + # horizontal space needs special treatment + if($value =~ m/^(?:"|')(\h*)(?:"|')$/){ + # pure horizontal space + $value = $1; + } elsif($value =~ m/^(?:"|')((?:\\t)*)(?:"|')$/){ + # tabs + $value =~ s/^(?:"|')//; + $value =~ s/(?:"|')$//; + $value =~ s/\\t/\t/g; + } + + if(scalar(@keysValues) == 2){ + # for example, -y="defaultIndent: ' '" + my $key = $keysValues[0]; + $logger->info("Updating masterSettings with $key: $value"); + $masterSettings{$key} = $value; + } elsif(scalar(@keysValues) == 3){ + # for example, -y="indentRules: one: '\t\t\t\t'" + my $parent = $keysValues[0]; + my $child = $keysValues[1]; + $logger->info("Updating masterSettings with $parent: $child: $value"); + $masterSettings{$parent}{$child} = $value; + } elsif(scalar(@keysValues) == 4){ + # for example, -y='modifyLineBreaks : environments: EndStartsOnOwnLine:3' -m + my $parent = $keysValues[0]; + my $child = $keysValues[1]; + my $grandchild = $keysValues[2]; + $logger->info("Updating masterSettings with $parent: $child: $grandchild: $value"); + $masterSettings{$parent}{$child}{$grandchild} = $value; + } elsif(scalar(@keysValues) == 5){ + # for example, -y='modifyLineBreaks : environments: one: EndStartsOnOwnLine:3' -m + my $parent = $keysValues[0]; + my $child = $keysValues[1]; + my $grandchild = $keysValues[2]; + my $greatgrandchild = $keysValues[3]; + $logger->info("Updating masterSettings with $parent: $child: $grandchild: $greatgrandchild: $value"); + $masterSettings{$parent}{$child}{$grandchild}{$greatgrandchild} = $value; + } + } + } + + # some users may wish to see showAmalgamatedSettings + # which details the overall state of the settings modified + # from the default in various user files + if($masterSettings{logFilePreferences}{showAmalgamatedSettings}){ + $logger->info("Amalgamated/overall settings to be used:"); + $logger->info(Dump \%masterSettings); + } + + return; +} + +sub yaml_get_indentation_settings_for_this_object{ + my $self = shift; + + # create a name for previously found settings + my $storageName = ${$self}{name}.${$self}{modifyLineBreaksYamlName}.(defined ${$self}{storageNameAppend}?${$self}{storageNameAppend}:q()); + + # grab the logging object + my $logger = get_logger("Document"); + + # check for storage of repeated objects + if ($previouslyFoundSettings{$storageName}){ + $logger->trace("*Using stored settings for $storageName") if($is_t_switch_active); + } else { + my $name = ${$self}{name}; + $logger->trace("Storing settings for $storageName") if($is_t_switch_active); + + # check for noAdditionalIndent and indentRules + # otherwise use defaultIndent + my $indentation = $self->yaml_get_indentation_information; + + # check for alignment at ampersand settings + $self->yaml_alignment_at_ampersand_settings; + + # check for line break settings + $self->yaml_modify_line_breaks_settings if $is_m_switch_active; + + # store the settings + %{${previouslyFoundSettings}{$storageName}} = ( + indentation=>$indentation, + BeginStartsOnOwnLine=>${$self}{BeginStartsOnOwnLine}, + BodyStartsOnOwnLine=>${$self}{BodyStartsOnOwnLine}, + EndStartsOnOwnLine=>${$self}{EndStartsOnOwnLine}, + EndFinishesWithLineBreak=>${$self}{EndFinishesWithLineBreak}, + removeParagraphLineBreaks=>${$self}{removeParagraphLineBreaks}, + textWrapOptions=>${$self}{textWrapOptions}, + columns=>${$self}{columns}, + ); + + # don't forget alignment settings! + foreach (@alignAtAmpersandInformation){ + ${${previouslyFoundSettings}{$storageName}}{${$_}{name}} = ${$self}{${$_}{name}} if(defined ${$self}{${$_}{name}}); + } + + # some objects, e.g ifElseFi, can have extra assignments, e.g ElseStartsOnOwnLine + # these need to be stored as well! + foreach (@{${$self}{additionalAssignments}}){ + ${${previouslyFoundSettings}{$storageName}}{$_} = ${$self}{$_}; + } + + # log file information + $logger->trace("Settings for $name (stored for future use):") if $is_tt_switch_active; + $logger->trace(Dump \%{${previouslyFoundSettings}{$storageName}}) if $is_tt_switch_active; + + } + + # append indentation settings to the current object + while( my ($key,$value)= each %{${previouslyFoundSettings}{$storageName}}){ + ${$self}{$key} = $value; + } + + return; +} + +sub yaml_alignment_at_ampersand_settings{ + my $self = shift; + + # if the YamlName is, for example, optionalArguments, mandatoryArguments, heading, then we'll be looking for information about the *parent* + my $name = (defined ${$self}{nameForIndentationSettings}) ? ${$self}{nameForIndentationSettings} : ${$self}{name}; + + # check, for example, + # lookForAlignDelims: + # tabular: 1 + # or + # + # lookForAlignDelims: + # tabular: + # delims: 1 + # alignDoubleBackSlash: 1 + # spacesBeforeDoubleBackSlash: 2 + return unless ${$masterSettings{lookForAlignDelims}}{$name}; + + if(ref ${$masterSettings{lookForAlignDelims}}{$name} eq "HASH"){ + # specified as a hash, e.g + # + # lookForAlignDelims: + # tabular: + # delims: 1 + # alignDoubleBackSlash: 1 + # spacesBeforeDoubleBackSlash: 2 + foreach (@alignAtAmpersandInformation){ + my $yamlname = (defined ${$_}{yamlname} ? ${$_}{yamlname}: ${$_}{name}); + ${$self}{${$_}{name}} = (defined ${${$masterSettings{lookForAlignDelims}}{$name}}{$yamlname} ) ? ${${$masterSettings{lookForAlignDelims}}{$name}}{$yamlname} : ${$_}{default}; + } + } else { + # specified as a scalar, e.g + # + # lookForAlignDelims: + # tabular: 1 + foreach (@alignAtAmpersandInformation){ + ${$self}{${$_}{name}} = ${$_}{default}; + } + } + return; +} + +sub yaml_modify_line_breaks_settings{ + my $self = shift; + + # grab the logging object + my $logger = get_logger("Document"); + + # details to the log file + $logger->trace("*-m modifylinebreaks switch active") if $is_t_switch_active; + $logger->trace("looking for polyswitch, textWrapOptions, removeParagraphLineBreaks, oneSentencePerLine settings for ${$self}{name} ") if $is_t_switch_active; + + # some objects, e.g ifElseFi, can have extra assignments, e.g ElseStartsOnOwnLine + my @toBeAssignedTo = ${$self}{additionalAssignments} ? @{${$self}{additionalAssignments}} : (); + + # the following will *definitley* be in the array, so let's add them + push(@toBeAssignedTo,("BeginStartsOnOwnLine","BodyStartsOnOwnLine","EndStartsOnOwnLine","EndFinishesWithLineBreak")); + + # we can efficiently loop through the following + foreach (@toBeAssignedTo){ + $self->yaml_poly_switch_get_every_or_custom_value( + toBeAssignedTo=>$_, + toBeAssignedToAlias=> ${$self}{aliases}{$_} ? ${$self}{aliases}{$_} : $_, + ); + }; + + $self->yaml_get_textwrap_removeparagraphline_breaks; + return; +} + +sub yaml_get_textwrap_removeparagraphline_breaks{ + my $self = shift; + + # grab the logging object + my $logger = get_logger("Document"); + + # textWrap and removeParagraphLineBreaks settings + foreach ("textWrapOptions","removeParagraphLineBreaks"){ + + # first check for either + # + # textWrapOptions: + # all: 0 + # + # or + # + # removeParagraphLineBreaks: + # all: 0 + # + # *IMPORTANT* + # even if all is set to 1, then it can still be disabled on either a + # + # per-object: + # + # for example + # + # textWrapOptions: + # all: + # except: + # - environments + # + # will disable textWrapOptions for *all* environments + # + # per-name + # + # for example + # + # textWrapOptions: + # all: + # except: + # - itemize + # + # will disable textWrapOptions for itemize + + # if 'all' is set as a hash, then the default value is 1, to be turned off (possibly) later + ${$self}{$_} = ( ref ${$masterSettings{modifyLineBreaks}{$_}}{all} eq "HASH" ? 1 : ${$masterSettings{modifyLineBreaks}{$_}}{all}); + + # get the columns + if($_ eq "textWrapOptions" and ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{perCodeBlockBasis}){ + $self->yaml_get_columns; + } + + # name of the object in the modifyLineBreaks yaml (e.g environments, ifElseFi, etc) + my $YamlName = ${$self}{modifyLineBreaksYamlName}; + + # if the YamlName is either optionalArguments or mandatoryArguments, then we'll be looking for information about the *parent* + my $name = ($YamlName =~ m/Arguments/) ? ${$self}{parent} : ${$self}{name}; + + # move to the next <thing> if + # + # textWrapOptions/removeParagraphLineBreaks:: + # all: 1 + # + if(${$self}{$_} + and + ref ${$masterSettings{modifyLineBreaks}{$_}}{all} ne "HASH" + and + ${$masterSettings{modifyLineBreaks}{$_}}{all}){ + $logger->trace("$_ for $name is ${$self}{$_}") if $is_t_switch_active; + next; + }; + + # otherwise, look for exceptions, either through + # + # textWrapOptions/removeParagraphLineBreaks: + # all: + # except: + # - <*type* of thing or *name* of thing> + # + # so, for example, the following (per code-block) is acceptable + # which makes an exception for all *environments* + # + # all: + # except: + # - 'environments' + # + # the following (per-name) is acceptable + # which only makes an exception for things called itemize + # + # all: + # except: + # - 'itemize' + # + if(${$self}{$_} + and + defined ${${$masterSettings{modifyLineBreaks}{$_}}{all}}{except} + and + ref ${${$masterSettings{modifyLineBreaks}{$_}}{all}}{except} eq "ARRAY" + ){ + my %except = map { $_ => 1 } @{${${$masterSettings{modifyLineBreaks}}{$_}}{all}{except}}; + if( $except{$name} or $except{$YamlName}){ + ${$self}{$_} = 0; + my $detail = ($except{$name} ? "per-name" : "per-code-block-type"); + $logger->trace("$_ for $name is ${$self}{$_} (found as exception $detail, see $_:all:except)") if $is_t_switch_active; + next; + } + } else { + # or otherwise through, for example + # + # all: 0 + # ifElseFi: 1 + # + # the textWrapOptions/removeParagraphLineBreaks can contain fields that are hashes or scalar + # + if(ref ${$masterSettings{modifyLineBreaks}{$_}}{$YamlName} eq "HASH"){ + # textWrapOptions/removeParagraphLineBreaks: + # all: 0 + # environments: + # quotation: 0 + $logger->trace("*$YamlName specified with fields in $_, looking for $name") if $is_t_switch_active; + ${$self}{$_} = ${${$masterSettings{modifyLineBreaks}{$_}}{$YamlName}}{$name} if (defined ${${$masterSettings{modifyLineBreaks}{$_}}{$YamlName}}{$name}); + } elsif(defined ${$masterSettings{modifyLineBreaks}{$_}}{$YamlName}){ + # textWrapOptions/removeParagraphLineBreaks: + # all: 0 + # environments: 0 + $logger->trace("*$YamlName specified with just a number in $_ ${$masterSettings{modifyLineBreaks}{$_}}{$YamlName}") if $is_t_switch_active; + ${$self}{$_} = ${$masterSettings{modifyLineBreaks}{$_}}{$YamlName} if (defined ${$masterSettings{modifyLineBreaks}{$_}}{$YamlName}); + } + } + + # summary to log file + $logger->trace("$_ for $name is ${$self}{$_}") if $is_t_switch_active; + } + + return; +} + +sub yaml_get_columns{ + my $self = shift; + + my $YamlName = ${$self}{modifyLineBreaksYamlName}; + + # the columns settings can have a variety of different ways of being specified + if(ref ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns} eq "HASH"){ + # assign default value of $columns + my $columns; + if(defined ${${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}}{default}){ + $columns = ${${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}}{default}; + } else { + $columns = 80; + } + + # possibly specify object wrapping on a per-name basis + if(ref ${${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}}{$YamlName} eq "HASH"){ + # for example: + # modifyLineBreaks: + # textWrapOptions: + # columns: + # default: 80 + # environments: + # default: 80 + # something: 10 + # another: 20 + if(defined ${${${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}}{$YamlName}}{${$self}{name}}){ + $columns = ${${${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}}{$YamlName}}{${$self}{name}}; + } elsif (${${${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}}{$YamlName}}{default}){ + $columns = ${${${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}}{$YamlName}}{default}; + } + } else { + # for example: + # modifyLineBreaks: + # textWrapOptions: + # columns: + # default: 80 + # environments: 10 + $columns = ${${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}}{$YamlName}; + } + ${$self}{columns} = $columns; + } else { + ${$self}{columns} = ${$masterSettings{modifyLineBreaks}{textWrapOptions}}{columns}; + } + return; +} + +sub yaml_poly_switch_get_every_or_custom_value{ + my $self = shift; + my %input = @_; + + my $toBeAssignedTo = $input{toBeAssignedTo}; + my $toBeAssignedToAlias = $input{toBeAssignedToAlias}; + + # grab the logging object + my $logger = get_logger("Document"); + + # alias + if(${$self}{aliases}{$toBeAssignedTo}){ + $logger->trace("aliased $toBeAssignedTo using ${$self}{aliases}{$toBeAssignedTo}") if($is_t_switch_active); + } + + # name of the object in the modifyLineBreaks yaml (e.g environments, ifElseFi, etc) + my $YamlName = ${$self}{modifyLineBreaksYamlName}; + + # if the YamlName is either optionalArguments or mandatoryArguments, then we'll be looking for information about the *parent* + my $name = ($YamlName =~ m/Arguments/) ? ${$self}{parent} : ${$self}{name}; + + # these variables just ease the notation what follows + my $everyValue = ${${$masterSettings{modifyLineBreaks}}{$YamlName}}{$toBeAssignedToAlias}; + my $customValue = ${${${$masterSettings{modifyLineBreaks}}{$YamlName}}{$name}}{$toBeAssignedToAlias}; + + # check for the *custom* value + if (defined $customValue){ + $logger->trace("$name: $toBeAssignedToAlias=$customValue, (*per-name* value) adjusting $toBeAssignedTo") if($is_t_switch_active); + ${$self}{$toBeAssignedTo} = $customValue !=0 ? $customValue : undef; + } else { + # check for the *every* value + if (defined $everyValue and $everyValue != 0){ + $logger->trace("$name: $toBeAssignedToAlias=$everyValue, (*global* value) adjusting $toBeAssignedTo") if($is_t_switch_active); + ${$self}{$toBeAssignedTo} = $everyValue; + } + } + return; +} + +sub yaml_get_indentation_information{ + my $self = shift; + + #************************************** + # SEARCHING ORDER: + # noAdditionalIndent *per-name* basis + # indentRules *per-name* basis + # noAdditionalIndentGlobal + # indentRulesGlobal + #************************************** + + # noAdditionalIndent can be a scalar or a hash, e.g + # + # noAdditionalIndent: + # myexample: 1 + # + # OR + # + # noAdditionalIndent: + # myexample: + # body: 1 + # optionalArguments: 1 + # mandatoryArguments: 1 + # + # specifying as a scalar with no field (e.g myexample: 1) + # will be interpreted as noAdditionalIndent for *every* + # field, so the body, optional arguments and mandatory arguments + # will *all* receive noAdditionalIndent + # + # indentRules can also be a scalar or a hash, e.g + # indentRules: + # myexample: "\t" + # + # OR + # + # indentRules: + # myexample: + # body: " " + # optionalArguments: "\t \t" + # mandatoryArguments: "" + # + # specifying as a scalar with no field will + # mean that *every* field will receive the same treatment + + # if the YamlName is, for example, optionalArguments, mandatoryArguments, heading, then we'll be looking for information about the *parent* + my $name = (defined ${$self}{nameForIndentationSettings}) ? ${$self}{nameForIndentationSettings} : ${$self}{name}; + + # if the YamlName is not optionalArguments, mandatoryArguments, heading (possibly others) then assume we're looking for 'body' + my $YamlName = $self->yaml_get_object_attribute_for_indentation_settings; + + # grab the logging object + my $logger = get_logger("Document"); + + my $indentationInformation; + foreach my $indentationAbout ("noAdditionalIndent","indentRules"){ + # check that the 'thing' is defined + if(defined ${$masterSettings{$indentationAbout}}{$name}){ + if(ref ${$masterSettings{$indentationAbout}}{$name} eq "HASH"){ + $logger->trace("$indentationAbout indentation specified with multiple fields for $name, searching for $name: $YamlName (see $indentationAbout)") if $is_t_switch_active ; + $indentationInformation = ${${$masterSettings{$indentationAbout}}{$name}}{$YamlName}; + } else { + $indentationInformation = ${$masterSettings{$indentationAbout}}{$name}; + $logger->trace("$indentationAbout indentation specified for $name (for *all* fields, body, optionalArguments, mandatoryArguments, afterHeading), using '$indentationInformation' (see $indentationAbout)") if $is_t_switch_active ; + } + # return, after performing an integrity check + if(defined $indentationInformation){ + if($indentationAbout eq "noAdditionalIndent" and $indentationInformation == 1){ + $logger->trace("Found! Using '' (see $indentationAbout)") if $is_t_switch_active; + return q(); + } elsif($indentationAbout eq "indentRules" and $indentationInformation=~m/^\h*$/){ + $logger->trace("Found! Using '$indentationInformation' (see $indentationAbout)") if $is_t_switch_active; + return $indentationInformation ; + } + } + } + } + + # gather information + $YamlName = ${$self}{modifyLineBreaksYamlName}; + + foreach my $indentationAbout ("noAdditionalIndent","indentRules"){ + # global assignments in noAdditionalIndentGlobal and/or indentRulesGlobal + my $globalInformation = $indentationAbout."Global"; + next if(!(defined ${$masterSettings{$globalInformation}}{$YamlName})); + if( ($globalInformation eq "noAdditionalIndentGlobal") and ${$masterSettings{$globalInformation}}{$YamlName}==1){ + $logger->trace("$globalInformation specified for $YamlName (see $globalInformation)") if $is_t_switch_active; + return q(); + } elsif($globalInformation eq "indentRulesGlobal") { + if(${$masterSettings{$globalInformation}}{$YamlName}=~m/^\h*$/){ + $logger->trace("$globalInformation specified for $YamlName (see $globalInformation)") if $is_t_switch_active; + return ${$masterSettings{$globalInformation}}{$YamlName}; + } else { + $logger->trace("$globalInformation specified (${$masterSettings{$globalInformation}}{$YamlName}) for $YamlName, but it needs to only contain horizontal space -- I'm ignoring this one") if $is_t_switch_active; + } + } + } + + # return defaultIndent, by default + $logger->trace("Using defaultIndent for $name") if $is_t_switch_active; + return $masterSettings{defaultIndent}; +} + +sub yaml_get_object_attribute_for_indentation_settings{ + # when looking for noAdditionalIndent or indentRules, we may need to determine + # which thing we're looking for, e.g + # + # chapter: + # body: 0 + # optionalArguments: 1 + # mandatoryArguments: 1 + # afterHeading: 0 + # + # this method returns 'body' by default, but the other objects (optionalArgument, mandatoryArgument, afterHeading) + # return their appropriate identifier. + return "body"; +} + +1; |