summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/latexindent/LatexIndent/GetYamlSettings.pm
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2017-02-24 22:07:42 +0000
committerKarl Berry <karl@freefriends.org>2017-02-24 22:07:42 +0000
commit990e60c6ed4437e5b632b7c9acf7f2237bbda871 (patch)
tree4dc9ea04f5fa46cc8ac59faa8dd62e4f8bc6835b /Master/texmf-dist/scripts/latexindent/LatexIndent/GetYamlSettings.pm
parent2e70942b44a972908c3180dd7a3c68be51afc466 (diff)
latexindent (23feb17)
git-svn-id: svn://tug.org/texlive/trunk@43326 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts/latexindent/LatexIndent/GetYamlSettings.pm')
-rw-r--r--Master/texmf-dist/scripts/latexindent/LatexIndent/GetYamlSettings.pm471
1 files changed, 471 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/latexindent/LatexIndent/GetYamlSettings.pm b/Master/texmf-dist/scripts/latexindent/LatexIndent/GetYamlSettings.pm
new file mode 100644
index 00000000000..4c93bc80426
--- /dev/null
+++ b/Master/texmf-dist/scripts/latexindent/LatexIndent/GetYamlSettings.pm
@@ -0,0 +1,471 @@
+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 LatexIndent::Switches qw/$is_t_switch_active $is_tt_switch_active/;
+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 Exporter qw/import/;
+our @EXPORT_OK = qw/readSettings modify_line_breaks_settings get_indentation_settings_for_this_object get_every_or_custom_value get_indentation_information get_object_attribute_for_indentation_settings alignment_at_ampersand_settings %masterSettings/;
+
+# 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;
+
+sub readSettings{
+ my $self = shift;
+
+ $defaultSettings = YAML::Tiny->new;
+ $defaultSettings = YAML::Tiny->read( "$FindBin::RealBin/defaultSettings.yaml" );
+ $self->logger("YAML settings read",'heading');
+ $self->logger("Reading 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");
+ $self->logger("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;
+
+ # 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}) {
+ $self->logger("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") {
+ $self->logger("$homeDir/.indentconfig.yaml has been found, but $indentconfig takes priority");
+ } elsif ( -e File::HomeDir->my_home . "/indentconfig.yaml" ) {
+ $self->logger("(Alternatively $homeDir/.indentconfig.yaml can be used)");
+ } elsif ( -e File::HomeDir->my_home . "/.indentconfig.yaml" ) {
+ $self->logger("(Alternatively $homeDir/indentconfig.yaml can be used)");
+ }
+
+ # read the absolute paths from indentconfig.yaml
+ $userSettings = YAML::Tiny->read( "$indentconfig" );
+
+ # output the contents of indentconfig to the log file
+ $self->logger(Dump \%{$userSettings->[0]});
+
+ # update the absolute paths
+ @absPaths = @{$userSettings->[0]->{paths}};
+ } else {
+ if($switches{onlyDefault}) {
+ $self->logger("Only default settings requested, not reading USER settings from $indentconfig");
+ $self->logger("Ignoring $switches{readLocalSettings} (you used the -d switch)") if($switches{readLocalSettings});
+ $switches{readLocalSettings}=0;
+ } else {
+ # give the user instructions on where to put indentconfig.yaml or .indentconfig.yaml
+ $self->logger("Home directory is $homeDir (didn't find either indentconfig.yaml or .indentconfig.yaml)");
+ $self->logger("To specify user settings you would put indentconfig.yaml here: $homeDir/indentconfig.yaml");
+ $self->logger("Alternatively, you can use the hidden file .indentconfig.yaml as: $homeDir/.indentconfig.yaml");
+ }
+ }
+
+ # get information about LOCAL settings, assuming that $readLocalSettings exists
+ my $directoryName = dirname (${$self}{fileName});
+
+ # local settings can be separated by ,
+ # e.g
+ # -l = myyaml1.yaml,myyaml2.yaml
+ # and in which case, we need to read them all
+ my @localSettings;
+ if($switches{readLocalSettings} =~ m/,/){
+ $self->logger("Multiple localSettings found, separated by commas:",'heading');
+ @localSettings = split(/,/,$switches{readLocalSettings});
+ } else {
+ push(@localSettings,$switches{readLocalSettings}) if($switches{readLocalSettings});
+ }
+
+ # add local settings to the paths, if appropriate
+ foreach (@localSettings) {
+ if ( (-e "$directoryName/$_") and !(-z "$directoryName/$_")) {
+ $self->logger("Adding $directoryName/$_ to YAML read paths");
+ push(@absPaths,"$directoryName/$_");
+ } elsif ( !(-e "$directoryName/$_") ) {
+ $self->logger("WARNING yaml file not found: $directoryName/$_ not found");
+ $self->logger("Proceeding without it.");
+ }
+ }
+
+ # 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)) {
+ $self->logger("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"){
+ $self->logger("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"){
+ $self->logger("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 {
+ $masterSettings{$firstLevelKey}{$secondLevelKey} = $secondLevelValue;
+ }
+ }
+ } else {
+ $masterSettings{$firstLevelKey} = $firstLevelValue;
+ }
+ }
+
+ # output settings to $logfile
+ if($masterSettings{logFilePreferences}{showEveryYamlRead}){
+ $self->logger(Dump \%{$userSettings->[0]});
+ } else {
+ $self->logger("Not showing settings in the log file (see showEveryYamlRead and showAmalgamatedSettings).");
+ }
+ } else {
+ # otherwise print a warning that we can not read userSettings.yaml
+ $self->logger("WARNING $settings contains invalid yaml format- not reading from it");
+ }
+ } else {
+ # otherwise keep going, but put a warning in the log file
+ $self->logger("WARNING: $homeDir/indentconfig.yaml");
+ if (-z $settings) {
+ $self->logger("specifies $settings but this file is EMPTY -- not reading from it");
+ } else {
+ $self->logger("specifies $settings but this file does not exist - unable to read settings from this file");
+ }
+ }
+ }
+
+ # 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}){
+ $self->logger("Amalgamated/overall settings to be used:",'heading');
+ $self->logger(Dump \%masterSettings);
+ }
+
+ return;
+}
+
+sub get_indentation_settings_for_this_object{
+ my $self = shift;
+
+ # create a name for previously found settings
+ my $storageName = ${$self}{name}.${$self}{modifyLineBreaksYamlName};
+
+ # check for storage of repeated objects
+ if ($previouslyFoundSettings{$storageName}){
+ $self->logger("Using stored settings for $storageName") if($is_t_switch_active);
+ } else {
+ my $name = ${$self}{name};
+ $self->logger("Storing settings for $storageName") if($is_t_switch_active);
+
+ # check for noAdditionalIndent and indentRules
+ # otherwise use defaultIndent
+ my $indentation = $self->get_indentation_information;
+
+ # check for alignment at ampersand settings
+ $self->alignment_at_ampersand_settings;
+
+ # check for line break settings
+ $self->modify_line_breaks_settings;
+
+ # store the settings
+ %{${previouslyFoundSettings}{$storageName}} = (
+ indentation=>$indentation,
+ BeginStartsOnOwnLine=>${$self}{BeginStartsOnOwnLine},
+ BodyStartsOnOwnLine=>${$self}{BodyStartsOnOwnLine},
+ EndStartsOnOwnLine=>${$self}{EndStartsOnOwnLine},
+ EndFinishesWithLineBreak=>${$self}{EndFinishesWithLineBreak},
+ );
+
+ # don't forget alignment settings!
+ ${${previouslyFoundSettings}{$storageName}}{lookForAlignDelims} = ${$self}{lookForAlignDelims} if(defined ${$self}{lookForAlignDelims});
+ ${${previouslyFoundSettings}{$storageName}}{alignDoubleBackSlash} = ${$self}{alignDoubleBackSlash} if(defined ${$self}{alignDoubleBackSlash});
+ ${${previouslyFoundSettings}{$storageName}}{spacesBeforeDoubleBackSlash} = ${$self}{spacesBeforeDoubleBackSlash} if(defined ${$self}{spacesBeforeDoubleBackSlash});
+
+ # 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}{$_};
+ }
+
+ }
+
+ # append indentation settings to the current object
+ while( my ($key,$value)= each %{${previouslyFoundSettings}{$storageName}}){
+ ${$self}{$key} = $value;
+ }
+
+ return;
+}
+
+sub 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};
+
+ ## check, for example,
+ ## lookForAlignDelims:
+ ## tabular:
+ ## body: 1
+ #return unless $self->get_object_attribute_for_indentation_settings;
+
+ if(ref ${$masterSettings{lookForAlignDelims}}{$name} eq "HASH"){
+ ${$self}{lookForAlignDelims} = (defined ${${$masterSettings{lookForAlignDelims}}{$name}}{delims} ) ? ${${$masterSettings{lookForAlignDelims}}{$name}}{delims} : 1;
+ ${$self}{alignDoubleBackSlash} = (defined ${${$masterSettings{lookForAlignDelims}}{$name}}{alignDoubleBackSlash} ) ? ${${$masterSettings{lookForAlignDelims}}{$name}}{alignDoubleBackSlash} : 1;
+ ${$self}{spacesBeforeDoubleBackSlash} = (defined ${${$masterSettings{lookForAlignDelims}}{$name}}{spacesBeforeDoubleBackSlash} ) ? ${${$masterSettings{lookForAlignDelims}}{$name}}{spacesBeforeDoubleBackSlash} : -1;
+ } else {
+ ${$self}{lookForAlignDelims} = 1;
+ ${$self}{alignDoubleBackSlash} = 1;
+ ${$self}{spacesBeforeDoubleBackSlash} = -1;
+ }
+ return;
+}
+
+sub modify_line_breaks_settings{
+ # return with undefined values unless the -m switch is active
+ return unless $is_m_switch_active;
+
+ my $self = shift;
+
+ # details to the log file
+ $self->logger("-m modifylinebreaks switch active, looking for settings for ${$self}{name} ",'heading') 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 effeciently loop through the following
+ foreach (@toBeAssignedTo){
+ $self->get_every_or_custom_value(
+ toBeAssignedTo=>$_,
+ toBeAssignedToAlias=> ${$self}{aliases}{$_} ? ${$self}{aliases}{$_} : $_,
+ );
+ }
+ return;
+}
+
+sub get_every_or_custom_value{
+ my $self = shift;
+ my %input = @_;
+
+ my $toBeAssignedTo = $input{toBeAssignedTo};
+ my $toBeAssignedToAlias = $input{toBeAssignedToAlias};
+
+ # alias
+ if(${$self}{aliases}{$toBeAssignedTo}){
+ $self->logger("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){
+ $self->logger("$name: $toBeAssignedToAlias=$customValue, (*custom* 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){
+ $self->logger("$name: $toBeAssignedToAlias=$everyValue, (*every* value) adjusting $toBeAssignedTo") if($is_t_switch_active);
+ ${$self}{$toBeAssignedTo} = $everyValue;
+ }
+ }
+ return;
+}
+
+sub 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->get_object_attribute_for_indentation_settings;
+
+ 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"){
+ $self->logger("$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};
+ $self->logger("$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){
+ $self->logger("Found! Using '' (see $indentationAbout)") if $is_t_switch_active;
+ return q();
+ } elsif($indentationAbout eq "indentRules" and $indentationInformation=~m/^\h*$/){
+ $self->logger("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){
+ $self->logger("$globalInformation specified for $YamlName (see $globalInformation)") if $is_t_switch_active;
+ return q();
+ } elsif($globalInformation eq "indentRulesGlobal") {
+ if(${$masterSettings{$globalInformation}}{$YamlName}=~m/^\h*$/){
+ $self->logger("$globalInformation specified for $YamlName (see $globalInformation)") if $is_t_switch_active;
+ return ${$masterSettings{$globalInformation}}{$YamlName};
+ } else {
+ $self->logger("$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
+ $self->logger("Using defaultIndent for $name") if $is_t_switch_active;
+ return $masterSettings{defaultIndent};
+}
+
+sub 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;